l_int32 pixCopyInputFormat(PIX *pixd, PIX *pixs) { PROCNAME("pixCopyInputFormat"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetInputFormat(pixd, pixGetInputFormat(pixs)); return 0; }
/*! * \brief pixReadMemWebP() * * \param[in] filedata webp compressed data in memory * \param[in] filesize number of bytes in data * \return pix 32 bpp, or NULL on error * * <pre> * Notes: * (1) When the encoded data only has 3 channels (no alpha), * WebPDecodeRGBAInto() generates a raster of 32-bit pixels, with * the alpha channel set to opaque (255). * (2) We don't need to use the gnu runtime functions like fmemopen() * for redirecting data from a stream to memory, because * the webp library has been written with memory-to-memory * functions at the lowest level (which is good!). And, in * any event, fmemopen() doesn't work with l_binaryReadStream(). * </pre> */ PIX * pixReadMemWebP(const l_uint8 *filedata, size_t filesize) { l_uint8 *out = NULL; l_int32 w, h, has_alpha, wpl, stride; l_uint32 *data; size_t size; PIX *pix; WebPBitstreamFeatures features; PROCNAME("pixReadMemWebP"); if (!filedata) return (PIX *)ERROR_PTR("filedata not defined", procName, NULL); if (WebPGetFeatures(filedata, filesize, &features)) return (PIX *)ERROR_PTR("Invalid WebP file", procName, NULL); w = features.width; h = features.height; has_alpha = features.has_alpha; /* Write from compressed Y,U,V arrays to pix raster data */ pix = pixCreate(w, h, 32); pixSetInputFormat(pix, IFF_WEBP); if (has_alpha) pixSetSpp(pix, 4); data = pixGetData(pix); wpl = pixGetWpl(pix); stride = wpl * 4; size = stride * h; out = WebPDecodeRGBAInto(filedata, filesize, (uint8_t *)data, size, stride); if (out == NULL) { /* error: out should also point to data */ pixDestroy(&pix); return (PIX *)ERROR_PTR("WebP decode failed", procName, NULL); } /* The WebP API expects data in RGBA order. The pix stores * in host-dependent order with R as the MSB and A as the LSB. * On little-endian machines, the bytes in the word must * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). * No swapping is necessary for big-endians. */ pixEndianByteSwap(pix); return pix; }
/*! * \brief gifToPix() * * \param[in] gif opened gif stream * \return pix, or NULL on error * * <pre> * Notes: * (1) This decodes the pix from the compressed gif stream and * closes the stream. * (2) It is static so that the stream is not exposed to clients. * </pre> */ static PIX * gifToPix(GifFileType *gif) { l_int32 wpl, i, j, w, h, d, cindex, ncolors; l_int32 rval, gval, bval; l_uint32 *data, *line; PIX *pixd, *pixdi; PIXCMAP *cmap; ColorMapObject *gif_cmap; SavedImage si; #if (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5 int giferr; #endif /* 5.1 and beyond */ PROCNAME("gifToPix"); /* Read all the data, but use only the first image found */ if (DGifSlurp(gif) != GIF_OK) { DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL); } if (gif->SavedImages == NULL) { DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL); } si = gif->SavedImages[0]; w = si.ImageDesc.Width; h = si.ImageDesc.Height; if (w <= 0 || h <= 0) { DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL); } if (si.RasterBits == NULL) { DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL); } if (si.ImageDesc.ColorMap) { /* private cmap for this image */ gif_cmap = si.ImageDesc.ColorMap; } else if (gif->SColorMap) { /* global cmap for whole picture */ gif_cmap = gif->SColorMap; } else { /* don't know where to take cmap from */ DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("color map is missing", procName, NULL); } ncolors = gif_cmap->ColorCount; if (ncolors <= 2) d = 1; else if (ncolors <= 4) d = 2; else if (ncolors <= 16) d = 4; else d = 8; if ((cmap = pixcmapCreate(d)) == NULL) { DGifCloseFile(gif, &giferr); return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL); } for (cindex = 0; cindex < ncolors; cindex++) { rval = gif_cmap->Colors[cindex].Red; gval = gif_cmap->Colors[cindex].Green; bval = gif_cmap->Colors[cindex].Blue; pixcmapAddColor(cmap, rval, gval, bval); } if ((pixd = pixCreate(w, h, d)) == NULL) { DGifCloseFile(gif, &giferr); pixcmapDestroy(&cmap); return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL); } pixSetInputFormat(pixd, IFF_GIF); pixSetColormap(pixd, cmap); wpl = pixGetWpl(pixd); data = pixGetData(pixd); for (i = 0; i < h; i++) { line = data + i * wpl; if (d == 1) { for (j = 0; j < w; j++) { if (si.RasterBits[i * w + j]) SET_DATA_BIT(line, j); } } else if (d == 2) { for (j = 0; j < w; j++) SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]); } else if (d == 4) { for (j = 0; j < w; j++) SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]); } else { /* d == 8 */ for (j = 0; j < w; j++) SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]); } } /* If the image has been interlaced (for viewing in a browser), * this restores the raster lines to normal order. */ if (gif->Image.Interlace) { pixdi = pixUninterlaceGIF(pixd); pixTransferAllData(pixd, &pixdi, 0, 0); } DGifCloseFile(gif, &giferr); return pixd; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * scalefactor (0.0 to disable; otherwise this is a scale factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @scalefactor scales the input image. After scaling and * possible depth conversion, the image is saved in the input * pixa, along with a box that specifies the location to * place it when tiled later. Disable saving the pix by * setting @scalefactor == 0.0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; BOX *box; PIX *pix1, *pix2, *pix3, *pix4; PROCNAME("pixSaveTiledOutline"); if (scalefactor == 0.0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32\n", procName); depth = 32; } else { depth = dp; } } else { /* extract the depth and bottom params from the first pix */ pix1 = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix1); bottom = pixGetInputFormat(pix1); /* not typical usage! */ pixDestroy(&pix1); } /* Remove colormap if it exists; otherwise a copy. This * guarantees that pix4 is not a clone of pixs. */ pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); /* Scale and convert to output depth */ if (scalefactor == 1.0) { pix2 = pixClone(pix1); } else if (scalefactor > 1.0) { pix2 = pixScale(pix1, scalefactor, scalefactor); } else if (scalefactor < 1.0) { if (pixGetDepth(pix1) == 1) pix2 = pixScaleToGray(pix1, scalefactor); else pix2 = pixScale(pix1, scalefactor, scalefactor); } pixDestroy(&pix1); if (depth == 8) pix3 = pixConvertTo8(pix2, 0); else pix3 = pixConvertTo32(pix2); pixDestroy(&pix2); /* Add black outline */ if (linewidth > 0) pix4 = pixAddBorder(pix3, linewidth, 0); else pix4 = pixClone(pix3); pixDestroy(&pix3); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pix4, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pix4, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix1 = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix1, bottom); /* not typical usage! */ pixDestroy(&pix1); return 0; }
/*! * \brief pixReadMemBmp() * * \param[in] cdata bmp data * \param[in] size number of bytes of bmp-formatted data * \return pix, or NULL on error */ PIX * pixReadMemBmp(const l_uint8 *cdata, size_t size) { l_uint8 pel[4]; l_uint8 *cmapBuf, *fdata, *data; l_int16 bftype, offset, depth, d; l_int32 width, height, xres, yres, compression, imagebytes; l_int32 cmapbytes, cmapEntries; l_int32 fdatabpl, extrabytes, pixWpl, pixBpl, i, j, k; l_uint32 *line, *pixdata, *pword; l_int64 npixels; BMP_FH *bmpfh; BMP_IH *bmpih; PIX *pix, *pix1; PIXCMAP *cmap; PROCNAME("pixReadMemBmp"); if (!cdata) return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); if (size < sizeof(BMP_FH) + sizeof(BMP_IH)) return (PIX *)ERROR_PTR("bmf size error", procName, NULL); /* Verify this is an uncompressed bmp */ bmpfh = (BMP_FH *)cdata; bftype = convertOnBigEnd16(bmpfh->bfType); if (bftype != BMP_ID) return (PIX *)ERROR_PTR("not bmf format", procName, NULL); bmpih = (BMP_IH *)(cdata + BMP_FHBYTES); if (!bmpih) return (PIX *)ERROR_PTR("bmpih not defined", procName, NULL); compression = convertOnBigEnd32(bmpih->biCompression); if (compression != 0) return (PIX *)ERROR_PTR("cannot read compressed BMP files", procName, NULL); /* Read the rest of the useful header information */ offset = convertOnBigEnd16(bmpfh->bfOffBits); width = convertOnBigEnd32(bmpih->biWidth); height = convertOnBigEnd32(bmpih->biHeight); depth = convertOnBigEnd16(bmpih->biBitCount); imagebytes = convertOnBigEnd32(bmpih->biSizeImage); xres = convertOnBigEnd32(bmpih->biXPelsPerMeter); yres = convertOnBigEnd32(bmpih->biYPelsPerMeter); /* Some sanity checking. We impose limits on the image * dimensions and number of pixels. We make sure the file * is the correct size to hold the amount of uncompressed data * that is specified in the header. The number of colormap * entries is checked: it can be either 0 (no cmap) or some * number between 2 and 256. * Note that the imagebytes for uncompressed images is either * 0 or the size of the file data. (The fact that it can * be 0 is perhaps some legacy glitch). */ if (width < 1) return (PIX *)ERROR_PTR("width < 1", procName, NULL); if (width > L_MAX_ALLOWED_WIDTH) return (PIX *)ERROR_PTR("width too large", procName, NULL); if (height < 1) return (PIX *)ERROR_PTR("height < 1", procName, NULL); if (height > L_MAX_ALLOWED_HEIGHT) return (PIX *)ERROR_PTR("height too large", procName, NULL); npixels = 1LL * width * height; if (npixels > L_MAX_ALLOWED_PIXELS) return (PIX *)ERROR_PTR("npixels too large", procName, NULL); if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 24 && depth != 32) return (PIX *)ERROR_PTR("depth not in {1, 2, 4, 8, 16, 24, 32}", procName,NULL); fdatabpl = 4 * ((1LL * width * depth + 31)/32); if (imagebytes != 0 && imagebytes != fdatabpl * height) return (PIX *)ERROR_PTR("invalid imagebytes", procName, NULL); cmapbytes = offset - BMP_FHBYTES - BMP_IHBYTES; cmapEntries = cmapbytes / sizeof(RGBA_QUAD); if (cmapEntries < 0 || cmapEntries == 1) return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", procName, NULL); if (cmapEntries > L_MAX_ALLOWED_NUM_COLORS) return (PIX *)ERROR_PTR("invalid cmap: too large", procName,NULL); if (size != 1LL * offset + 1LL * fdatabpl * height) return (PIX *)ERROR_PTR("size incommensurate with image data", procName,NULL); /* Handle the colormap */ cmapBuf = NULL; if (cmapEntries > 0) { if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(cmapEntries, sizeof(RGBA_QUAD))) == NULL) return (PIX *)ERROR_PTR("cmapBuf alloc fail", procName, NULL ); /* Read the colormap entry data from bmp. The RGBA_QUAD colormap * entries are used for both bmp and leptonica colormaps. */ memcpy(cmapBuf, cdata + BMP_FHBYTES + BMP_IHBYTES, sizeof(RGBA_QUAD) * cmapEntries); } /* Make a 32 bpp pix if depth is 24 bpp */ d = (depth == 24) ? 32 : depth; if ((pix = pixCreate(width, height, d)) == NULL) { LEPT_FREE(cmapBuf); return (PIX *)ERROR_PTR( "pix not made", procName, NULL); } pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ pixSetInputFormat(pix, IFF_BMP); pixWpl = pixGetWpl(pix); pixBpl = 4 * pixWpl; /* Convert the bmp colormap to a pixcmap */ cmap = NULL; if (cmapEntries > 0) { /* import the colormap to the pix cmap */ cmap = pixcmapCreate(L_MIN(d, 8)); LEPT_FREE(cmap->array); /* remove generated cmap array */ cmap->array = (void *)cmapBuf; /* and replace */ cmap->n = L_MIN(cmapEntries, 256); for (i = 0; i < cmap->n; i++) /* set all colors opaque */ pixcmapSetAlpha (cmap, i, 255); } pixSetColormap(pix, cmap); /* Acquire the image data. Image origin for bmp is at lower right. */ fdata = (l_uint8 *)cdata + offset; /* start of the bmp image data */ pixdata = pixGetData(pix); if (depth != 24) { /* typ. 1 or 8 bpp */ data = (l_uint8 *)pixdata + pixBpl * (height - 1); for (i = 0; i < height; i++) { memcpy(data, fdata, fdatabpl); fdata += fdatabpl; data -= pixBpl; } } else { /* 24 bpp file; 32 bpp pix * Note: for bmp files, pel[0] is blue, pel[1] is green, * and pel[2] is red. This is opposite to the storage * in the pix, which puts the red pixel in the 0 byte, * the green in the 1 byte and the blue in the 2 byte. * Note also that all words are endian flipped after * assignment on L_LITTLE_ENDIAN platforms. * * We can then make these assignments for little endians: * SET_DATA_BYTE(pword, 1, pel[0]); blue * SET_DATA_BYTE(pword, 2, pel[1]); green * SET_DATA_BYTE(pword, 3, pel[2]); red * This looks like: * 3 (R) 2 (G) 1 (B) 0 * |-----------|------------|-----------|-----------| * and after byte flipping: * 3 2 (B) 1 (G) 0 (R) * |-----------|------------|-----------|-----------| * * For big endians we set: * SET_DATA_BYTE(pword, 2, pel[0]); blue * SET_DATA_BYTE(pword, 1, pel[1]); green * SET_DATA_BYTE(pword, 0, pel[2]); red * This looks like: * 0 (R) 1 (G) 2 (B) 3 * |-----------|------------|-----------|-----------| * so in both cases we get the correct assignment in the PIX. * * Can we do a platform-independent assignment? * Yes, set the bytes without using macros: * *((l_uint8 *)pword) = pel[2]; red * *((l_uint8 *)pword + 1) = pel[1]; green * *((l_uint8 *)pword + 2) = pel[0]; blue * For little endians, before flipping, this looks again like: * 3 (R) 2 (G) 1 (B) 0 * |-----------|------------|-----------|-----------| */ extrabytes = fdatabpl - 3 * width; line = pixdata + pixWpl * (height - 1); for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { pword = line + j; memcpy(&pel, fdata, 3); fdata += 3; *((l_uint8 *)pword + COLOR_RED) = pel[2]; *((l_uint8 *)pword + COLOR_GREEN) = pel[1]; *((l_uint8 *)pword + COLOR_BLUE) = pel[0]; } if (extrabytes) { for (k = 0; k < extrabytes; k++) { memcpy(&pel, fdata, 1); fdata++; } } line -= pixWpl; } } pixEndianByteSwap(pix); /* ---------------------------------------------- * The bmp colormap determines the values of black * and white pixels for binary in the following way: * (a) white = 0 [255], black = 1 [0] * 255, 255, 255, 255, 0, 0, 0, 255 * (b) black = 0 [0], white = 1 [255] * 0, 0, 0, 255, 255, 255, 255, 255 * We have no need for a 1 bpp pix with a colormap! * Note: the alpha component here is 255 (opaque) * ---------------------------------------------- */ if (depth == 1 && cmap) { pix1 = pixRemoveColormap(pix, REMOVE_CMAP_TO_BINARY); pixDestroy(&pix); pix = pix1; /* rename */ } return pix; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * reduction (0 to disable; otherwise this is a reduction factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @reduction is the integer reduction factor for the input * image. After reduction and possible depth conversion, * the image is saved in the input pixa, along with a box * that specifies the location to place it when tiled later. * Disable saving the pix by setting reduction == 0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_int32 reduction, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; l_float32 scale; BOX *box; PIX *pix, *pixt1, *pixt2, *pixt3; PROCNAME("pixSaveTiledOutline"); if (reduction == 0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32", procName); depth = 32; } else depth = dp; } else { /* extract the depth and bottom params from the first pix */ pix = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix); bottom = pixGetInputFormat(pix); /* not typical usage! */ pixDestroy(&pix); } /* Scale and convert to output depth */ if (reduction == 1) pixt1 = pixClone(pixs); else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pixt1 = pixScaleToGray(pixs, scale); else pixt1 = pixScale(pixs, scale, scale); } if (depth == 8) pixt2 = pixConvertTo8(pixt1, 0); else pixt2 = pixConvertTo32(pixt1); pixDestroy(&pixt1); /* Add black outline */ if (linewidth > 0) pixt3 = pixAddBorder(pixt2, linewidth, 0); else pixt3 = pixClone(pixt2); pixDestroy(&pixt2); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pixt3, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pixt3, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix, bottom); /* not typical usage! */ pixDestroy(&pix); return 0; }
/*! * pixReadMem() * * Input: data (const; encoded) * datasize (size of data) * Return: pix, or null on error * * Notes: * (1) This is a variation of pixReadStream(), where the data is read * from a memory buffer rather than a file. * (2) On windows, this only reads tiff formatted files directly from * memory. For other formats, it write to a temp file and * decompress from file. * (3) findFileFormatBuffer() requires up to 8 bytes to decide on * the format. That determines the constraint here. But in * fact the data must contain the entire compressed string for * the image. */ PIX * pixReadMem(const l_uint8 *data, size_t size) { l_int32 format; PIX *pix; PROCNAME("pixReadMem"); if (!data) return (PIX *)ERROR_PTR("data not defined", procName, NULL); if (size < 8) return (PIX *)ERROR_PTR("size < 8", procName, NULL); pix = NULL; findFileFormatBuffer(data, &format); switch (format) { case IFF_BMP: if ((pix = pixReadMemBmp(data, size)) == NULL ) return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL); break; case IFF_JFIF_JPEG: if ((pix = pixReadMemJpeg(data, size, READ_24_BIT_COLOR, 1, NULL, 0)) == NULL) return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL); break; case IFF_PNG: if ((pix = pixReadMemPng(data, size)) == NULL) return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL); break; case IFF_TIFF: case IFF_TIFF_PACKBITS: case IFF_TIFF_RLE: case IFF_TIFF_G3: case IFF_TIFF_G4: case IFF_TIFF_LZW: case IFF_TIFF_ZIP: /* Reading page 0 by default */ if ((pix = pixReadMemTiff(data, size, 0)) == NULL) return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL); break; case IFF_PNM: if ((pix = pixReadMemPnm(data, size)) == NULL) return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL); break; case IFF_GIF: if ((pix = pixReadMemGif(data, size)) == NULL) return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL); break; case IFF_JP2: if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0)) == NULL) return (PIX *)ERROR_PTR("jp2k: no pix returned", procName, NULL); break; case IFF_WEBP: if ((pix = pixReadMemWebP(data, size)) == NULL) return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL); break; case IFF_SPIX: if ((pix = pixReadMemSpix(data, size)) == NULL) return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL); break; case IFF_UNKNOWN: return (PIX *)ERROR_PTR("Unknown format: no pix returned", procName, NULL); break; } /* Set the input format. For tiff reading from memory we lose * the actual input format; for 1 bpp, default to G4. */ if (pix) { if (format == IFF_TIFF && pixGetDepth(pix) == 1) format = IFF_TIFF_G4; pixSetInputFormat(pix, format); } return pix; }
/*! * pixReadStream() * * Input: fp (file stream) * hint (bitwise OR of L_HINT_* values for jpeg; use 0 for no hint) * Return: pix if OK; null on error * * Notes: * (1) The hint only applies to jpeg. */ PIX * pixReadStream(FILE *fp, l_int32 hint) { l_int32 format, ret; l_uint8 *comment; PIX *pix; PROCNAME("pixReadStream"); if (!fp) return (PIX *)ERROR_PTR("stream not defined", procName, NULL); pix = NULL; findFileFormatStream(fp, &format); switch (format) { case IFF_BMP: if ((pix = pixReadStreamBmp(fp)) == NULL ) return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL); break; // case IFF_JFIF_JPEG: // if ((pix = pixReadStreamJpeg(fp, READ_24_BIT_COLOR, 1, NULL, hint)) // == NULL) // return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL); // ret = fgetJpegComment(fp, &comment); // if (!ret && comment) // pixSetText(pix, (char *)comment); // FREE(comment); // break; case IFF_PNG: if ((pix = pixReadStreamPng(fp)) == NULL) return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL); break; case IFF_TIFF: case IFF_TIFF_PACKBITS: case IFF_TIFF_RLE: case IFF_TIFF_G3: case IFF_TIFF_G4: case IFF_TIFF_LZW: case IFF_TIFF_ZIP: if ((pix = pixReadStreamTiff(fp, 0)) == NULL) /* page 0 by default */ return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL); break; case IFF_PNM: if ((pix = pixReadStreamPnm(fp)) == NULL) return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL); break; case IFF_GIF: if ((pix = pixReadStreamGif(fp)) == NULL) return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL); break; case IFF_JP2: if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0)) == NULL) return (PIX *)ERROR_PTR("jp2: no pix returned", procName, NULL); break; case IFF_WEBP: if ((pix = pixReadStreamWebP(fp)) == NULL) return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL); break; case IFF_SPIX: if ((pix = pixReadStreamSpix(fp)) == NULL) return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL); break; case IFF_UNKNOWN: return (PIX *)ERROR_PTR( "Unknown format: no pix returned", procName, NULL); break; } if (pix) pixSetInputFormat(pix, format); return pix; }
/*! * \brief pixReadStreamJpeg() * * \param[in] fp file stream * \param[in] cmapflag 0 for no colormap in returned pix; * 1 to return an 8 bpp cmapped pix if spp = 3 or 4 * \param[in] reduction scaling factor: 1, 2, 4 or 8 * \param[out] pnwarn [optional] number of warnings * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default * \return pix, or NULL on error * * Usage: see pixReadJpeg * <pre> * Notes: * (1) The jpeg comment, if it exists, is not stored in the pix. * </pre> */ PIX * pixReadStreamJpeg(FILE *fp, l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint) { l_int32 cyan, yellow, magenta, black, nwarn; l_int32 i, j, k, rval, gval, bval; l_int32 w, h, wpl, spp, ncolors, cindex, ycck, cmyk; l_uint32 *data; l_uint32 *line, *ppixel; JSAMPROW rowbuffer; PIX *pix; PIXCMAP *cmap; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; jmp_buf jmpbuf; /* must be local to the function */ PROCNAME("pixReadStreamJpeg"); if (pnwarn) *pnwarn = 0; if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); if (cmapflag != 0 && cmapflag != 1) cmapflag = 0; /* default */ if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL); if (BITS_IN_JSAMPLE != 8) /* set in jmorecfg.h */ return (PIX *)ERROR_PTR("BITS_IN_JSAMPLE != 8", procName, NULL); rewind(fp); pix = NULL; rowbuffer = NULL; /* Modify the jpeg error handling to catch fatal errors */ cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_catch_all_1; cinfo.client_data = (void *)&jmpbuf; if (setjmp(jmpbuf)) { pixDestroy(&pix); LEPT_FREE(rowbuffer); return (PIX *)ERROR_PTR("internal jpeg error", procName, NULL); } /* Initialize jpeg structs for decompression */ jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, fp); jpeg_read_header(&cinfo, TRUE); cinfo.scale_denom = reduction; cinfo.scale_num = 1; jpeg_calc_output_dimensions(&cinfo); if (hint & L_JPEG_READ_LUMINANCE) { cinfo.out_color_space = JCS_GRAYSCALE; spp = 1; L_INFO("reading luminance channel only\n", procName); } else { spp = cinfo.out_color_components; } /* Allocate the image and a row buffer */ w = cinfo.output_width; h = cinfo.output_height; ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmapflag == 0); cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmapflag == 0); if (spp != 1 && spp != 3 && !ycck && !cmyk) { return (PIX *)ERROR_PTR("spp must be 1 or 3, or YCCK or CMYK", procName, NULL); } if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), spp * w); pix = pixCreate(w, h, 32); } else { /* 8 bpp gray or colormapped */ rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), w); pix = pixCreate(w, h, 8); } pixSetInputFormat(pix, IFF_JFIF_JPEG); if (!rowbuffer || !pix) { LEPT_FREE(rowbuffer); pixDestroy(&pix); return (PIX *)ERROR_PTR("rowbuffer or pix not made", procName, NULL); } /* Initialize decompression. Set up a colormap for color * quantization if requested. */ if (spp == 1) { /* Grayscale or colormapped */ jpeg_start_decompress(&cinfo); } else { /* Color; spp == 3 or YCCK or CMYK */ if (cmapflag == 0) { /* 24 bit color in 32 bit pix or YCCK/CMYK */ cinfo.quantize_colors = FALSE; jpeg_start_decompress(&cinfo); } else { /* Color quantize to 8 bits */ cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = 256; jpeg_start_decompress(&cinfo); /* Construct a pix cmap */ cmap = pixcmapCreate(8); ncolors = cinfo.actual_number_of_colors; for (cindex = 0; cindex < ncolors; cindex++) { rval = cinfo.colormap[0][cindex]; gval = cinfo.colormap[1][cindex]; bval = cinfo.colormap[2][cindex]; pixcmapAddColor(cmap, rval, gval, bval); } pixSetColormap(pix, cmap); } } wpl = pixGetWpl(pix); data = pixGetData(pix); /* Decompress. Unfortunately, we cannot use the return value * from jpeg_read_scanlines() to determine if there was a problem * with the data; it always appears to return 1. We can only * tell from the warnings during decoding, such as "premature * end of data segment". The default behavior is to return an * image even if there are warnings. However, by setting the * hint to have the same bit flag as L_JPEG_FAIL_ON_BAD_DATA, * no image will be returned if there are any warnings. */ for (i = 0; i < h; i++) { if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) == 0) { L_ERROR("read error at scanline %d\n", procName, i); pixDestroy(&pix); jpeg_destroy_decompress(&cinfo); LEPT_FREE(rowbuffer); return (PIX *)ERROR_PTR("bad data", procName, NULL); } /* -- 24 bit color -- */ if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { ppixel = data + i * wpl; if (spp == 3) { for (j = k = 0; j < w; j++) { SET_DATA_BYTE(ppixel, COLOR_RED, rowbuffer[k++]); SET_DATA_BYTE(ppixel, COLOR_GREEN, rowbuffer[k++]); SET_DATA_BYTE(ppixel, COLOR_BLUE, rowbuffer[k++]); ppixel++; } } else { /* This is a conversion from CMYK -> RGB that ignores color profiles, and is invoked when the image header claims to be in CMYK or YCCK colorspace. If in YCCK, libjpeg may be doing YCCK -> CMYK under the hood. To understand why the colors need to be inverted on read-in for the Adobe marker, see the "Special color spaces" section of "Using the IJG JPEG Library" by Thomas G. Lane: http://www.jpegcameras.com/libjpeg/libjpeg-3.html#ss3.1 The non-Adobe conversion is equivalent to: rval = black - black * cyan / 255 ... The Adobe conversion is equivalent to: rval = black - black * (255 - cyan) / 255 ... Note that cyan is the complement to red, and we are subtracting the complement color (weighted by black) from black. For Adobe conversions, where they've already inverted the CMY but not the K, we have to invert again. The results must be clipped to [0 ... 255]. */ for (j = k = 0; j < w; j++) { cyan = rowbuffer[k++]; magenta = rowbuffer[k++]; yellow = rowbuffer[k++]; black = rowbuffer[k++]; if (cinfo.saw_Adobe_marker) { rval = (black * cyan) / 255; gval = (black * magenta) / 255; bval = (black * yellow) / 255; } else { rval = black * (255 - cyan) / 255; gval = black * (255 - magenta) / 255; bval = black * (255 - yellow) / 255; } rval = L_MIN(L_MAX(rval, 0), 255); gval = L_MIN(L_MAX(gval, 0), 255); bval = L_MIN(L_MAX(bval, 0), 255); composeRGBPixel(rval, gval, bval, ppixel); ppixel++; } } } else { /* 8 bpp grayscale or colormapped pix */ line = data + i * wpl; for (j = 0; j < w; j++) SET_DATA_BYTE(line, j, rowbuffer[j]); } } nwarn = cinfo.err->num_warnings; if (pnwarn) *pnwarn = nwarn; /* If the pixel density is neither 1 nor 2, it may not be defined. * In that case, don't set the resolution. */ if (cinfo.density_unit == 1) { /* pixels per inch */ pixSetXRes(pix, cinfo.X_density); pixSetYRes(pix, cinfo.Y_density); } else if (cinfo.density_unit == 2) { /* pixels per centimeter */ pixSetXRes(pix, (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5)); pixSetYRes(pix, (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5)); } if (cinfo.output_components != spp) fprintf(stderr, "output spp = %d, spp = %d\n", cinfo.output_components, spp); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); LEPT_FREE(rowbuffer); if (nwarn > 0) { if (hint & L_JPEG_FAIL_ON_BAD_DATA) { L_ERROR("fail with %d warning(s) of bad data\n", procName, nwarn); pixDestroy(&pix); } else { L_WARNING("%d warning(s) of bad data\n", procName, nwarn); } } return pix; }
/*! * \brief pixReadStreamPnm() * * \param[in] fp file stream opened for read * \return pix, or NULL on error */ PIX * pixReadStreamPnm(FILE *fp) { l_uint8 val8, rval8, gval8, bval8; l_uint16 val16; l_int32 w, h, d, bpl, wpl, i, j, type; l_int32 val, rval, gval, bval; l_uint32 rgbval; l_uint32 *line, *data; PIX *pix; PROCNAME("pixReadStreamPnm"); if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); if (freadHeaderPnm(fp, &w, &h, &d, &type, NULL, NULL)) return (PIX *)ERROR_PTR( "header read failed", procName, NULL); if ((pix = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR( "pix not made", procName, NULL); pixSetInputFormat(pix, IFF_PNM); data = pixGetData(pix); wpl = pixGetWpl(pix); /* Old "ascii" format */ if (type <= 3) { for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { if (type == 1 || type == 2) { if (pnmReadNextAsciiValue(fp, &val)) return (PIX *)ERROR_PTR( "read abend", procName, pix); pixSetPixel(pix, j, i, val); } else { /* type == 3 */ if (pnmReadNextAsciiValue(fp, &rval)) return (PIX *)ERROR_PTR( "read abend", procName, pix); if (pnmReadNextAsciiValue(fp, &gval)) return (PIX *)ERROR_PTR( "read abend", procName, pix); if (pnmReadNextAsciiValue(fp, &bval)) return (PIX *)ERROR_PTR( "read abend", procName, pix); composeRGBPixel(rval, gval, bval, &rgbval); pixSetPixel(pix, j, i, rgbval); } } } return pix; } /* "raw" format for 1 bpp */ if (type == 4) { bpl = (d * w + 7) / 8; for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < bpl; j++) { if (fread(&val8, 1, 1, fp) != 1) return (PIX *)ERROR_PTR( "read error in 4", procName, pix); SET_DATA_BYTE(line, j, val8); } } return pix; } /* "raw" format for grayscale */ if (type == 5) { bpl = (d * w + 7) / 8; for (i = 0; i < h; i++) { line = data + i * wpl; if (d != 16) { for (j = 0; j < w; j++) { if (fread(&val8, 1, 1, fp) != 1) return (PIX *)ERROR_PTR( "error in 5", procName, pix); if (d == 2) SET_DATA_DIBIT(line, j, val8); else if (d == 4) SET_DATA_QBIT(line, j, val8); else /* d == 8 */ SET_DATA_BYTE(line, j, val8); } } else { /* d == 16 */ for (j = 0; j < w; j++) { if (fread(&val16, 2, 1, fp) != 1) return (PIX *)ERROR_PTR( "16 bpp error", procName, pix); SET_DATA_TWO_BYTES(line, j, val16); } } } return pix; } /* "raw" format, type == 6; rgb */ for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < wpl; j++) { if (fread(&rval8, 1, 1, fp) != 1) return (PIX *)ERROR_PTR( "read error type 6", procName, pix); if (fread(&gval8, 1, 1, fp) != 1) return (PIX *)ERROR_PTR( "read error type 6", procName, pix); if (fread(&bval8, 1, 1, fp) != 1) return (PIX *)ERROR_PTR( "read error type 6", procName, pix); composeRGBPixel(rval8, gval8, bval8, &rgbval); line[j] = rgbval; } } return pix; }