void SavePixForCrash(int resolution, Pix* pix) { #ifdef __linux__ #ifndef ANDROID int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; #else int thread_id = gettid() % kMaxNumThreadPixes; #endif pixDestroy(&global_crash_pixes[thread_id]); if (pix != nullptr) { Pix* clone = pixClone(pix); pixSetXRes(clone, resolution); pixSetYRes(clone, resolution); global_crash_pixes[thread_id] = clone; } #endif }
/*! * pixaReadStream() * * Input: stream * Return: pixa, or null on error */ PIXA * pixaReadStream(FILE *fp) { l_int32 n, i, xres, yres, version; l_int32 ignore; BOXA *boxa; PIX *pix; PIXA *pixa; PROCNAME("pixaReadStream"); #if !HAVE_LIBPNG /* defined in environ.h */ return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); #else if (!fp) return (PIXA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nPixa Version %d\n", &version) != 1) return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); if (version != PIXA_VERSION_NUMBER) return (PIXA *)ERROR_PTR("invalid pixa version", procName, NULL); if (fscanf(fp, "Number of pix = %d\n", &n) != 1) return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); if ((pixa = pixaCreate(n)) == NULL) return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); if ((boxa = boxaReadStream(fp)) == NULL) return (PIXA *)ERROR_PTR("boxa not made", procName, NULL); boxaDestroy(&pixa->boxa); pixa->boxa = boxa; for (i = 0; i < n; i++) { if ((fscanf(fp, " pix[%d]: xres = %d, yres = %d\n", &ignore, &xres, &yres)) != 3) return (PIXA *)ERROR_PTR("res reading", procName, NULL); if ((pix = pixReadStreamPng(fp)) == NULL) return (PIXA *)ERROR_PTR("pix not read", procName, NULL); pixSetXRes(pix, xres); pixSetYRes(pix, yres); pixaAddPix(pixa, pix, L_INSERT); } return pixa; #endif /* !HAVE_LIBPNG */ }
l_int32 pixCopyResolution(PIX *pixd, PIX *pixs) { PROCNAME("pixCopyResolution"); 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 */ pixSetXRes(pixd, pixGetXRes(pixs)); pixSetYRes(pixd, pixGetYRes(pixs)); return 0; }
/*! * pixReadStreamPng() * * Input: stream * Return: pix, or null on error * * Notes: * (1) If called from pixReadStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential reads of png format images from a stream, * use pixReadStreamPng() */ PIX * pixReadStreamPng(FILE *fp) { l_uint8 rval, gval, bval; l_int32 i, j, k; l_int32 wpl, d, spp, cindex; l_uint32 png_transforms; l_uint32 *data, *line, *ppixel; int num_palette, num_text; png_byte bit_depth, color_type, channels; png_uint_32 w, h, rowbytes; png_uint_32 xres, yres; png_bytep rowptr; png_bytep *row_pointers; png_structp png_ptr; png_infop info_ptr, end_info; png_colorp palette; png_textp text_ptr; /* ptr to text_chunk */ PIX *pix; PIXCMAP *cmap; PROCNAME("pixReadStreamPng"); if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); pix = NULL; /* Allocate the 3 data structures */ if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); } if ((end_info = png_create_info_struct(png_ptr)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return (PIX *)ERROR_PTR("end_info not made", procName, NULL); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("internal png error", procName, NULL); } png_init_io(png_ptr, fp); /* ---------------------------------------------------------- * * Set the transforms flags. Whatever happens here, * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. * ---------------------------------------------------------- */ /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ if (var_PNG_STRIP_16_TO_8 == 1) /* our default */ png_transforms = PNG_TRANSFORM_STRIP_16; else png_transforms = PNG_TRANSFORM_IDENTITY; /* To remove alpha channel, use PNG_TRANSFORM_STRIP_ALPHA */ if (var_PNG_STRIP_ALPHA == 1) /* our default */ png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; /* Read it */ png_read_png(png_ptr, info_ptr, png_transforms, NULL); row_pointers = png_get_rows(png_ptr, info_ptr); w = png_get_image_width(png_ptr, info_ptr); h = png_get_image_height(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); channels = png_get_channels(png_ptr, info_ptr); spp = channels; if (spp == 1) d = bit_depth; else if (spp == 2) { d = 2 * bit_depth; L_WARNING("there shouldn't be 2 spp!", procName); } else /* spp == 3 (rgb), spp == 4 (rgba) */ d = 4 * bit_depth; /* Remove if/when this is implemented for all bit_depths */ if (spp == 3 && bit_depth != 8) { fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("not implemented for this depth", procName, NULL); } if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); cmap = pixcmapCreate(d); /* spp == 1 */ for (cindex = 0; cindex < num_palette; cindex++) { rval = palette[cindex].red; gval = palette[cindex].green; bval = palette[cindex].blue; pixcmapAddColor(cmap, rval, gval, bval); } } else cmap = NULL; if ((pix = pixCreate(w, h, d)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("pix not made", procName, NULL); } wpl = pixGetWpl(pix); data = pixGetData(pix); pixSetColormap(pix, cmap); if (spp == 1) { /* copy straight from buffer to pix */ for (i = 0; i < h; i++) { line = data + i * wpl; rowptr = row_pointers[i]; for (j = 0; j < rowbytes; j++) { SET_DATA_BYTE(line, j, rowptr[j]); } } } else { /* spp == 3 or spp == 4 */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; rowptr = row_pointers[i]; for (j = k = 0; j < w; j++) { SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); if (spp == 4) SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); ppixel++; } } } #if DEBUG if (cmap) { for (i = 0; i < 16; i++) { fprintf(stderr, "[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); } } #endif /* DEBUG */ /* If there is no colormap, PNG defines black = 0 and * white = 1 by default for binary monochrome. Therefore, * since we use the opposite definition, we must invert * the image colors in either of these cases: * (i) there is no colormap (default) * (ii) there is a colormap which defines black to * be 0 and white to be 1. * We cannot use the PNG_TRANSFORM_INVERT_MONO flag * because that flag (since version 1.0.9) inverts 8 bpp * grayscale as well, which we don't want to do. * (It also doesn't work if there is a colormap.) * If there is a colormap that defines black = 1 and * white = 0, we don't need to do anything. * * How do we check the polarity of the colormap? * The colormap determines the values of black and * white pixels in the following way: * if black = 1 (255), white = 0 * 255, 255, 255, 0, 0, 0, 0, 0, 0 * if black = 0, white = 1 (255) * 0, 0, 0, 0, 255, 255, 255, 0 * So we test the first byte to see if it is 0; * if so, invert the colors. * * If there is a colormap, after inverting the pixels it is * necessary to destroy the colormap. Otherwise, if someone were * to call pixRemoveColormap(), this would cause the pixel * values to be inverted again! */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { /* fprintf(stderr, "Inverting binary data on png read\n"); */ pixInvert(pix, pix); pixDestroyColormap(pix); } xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); 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 */ /* Get the text if there is any */ png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); if (num_text && text_ptr) pixSetText(pix, text_ptr->text); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return pix; }
/*! * \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; }
/*! * \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; }
/*! * pixReadStreamJpeg() * * Input: stream * colormap flag (0 means return RGB image if color; * 1 means create colormap and return 8 bpp * palette image if color) * reduction (scaling factor: 1, 2, 4 or 8) * &pnwarn (<optional return> number of warnings) * hint: (a bitwise OR of L_HINT_* values); use 0 for no hints * Return: pix, or null on error * * Usage: see pixReadJpeg() */ PIX * pixReadStreamJpeg(FILE *fp, l_int32 cmflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint) { l_uint8 cyan, yellow, magenta, black, white; l_int32 rval, gval, bval; l_int32 i, j, k; 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; l_uint8 *comment = NULL; PROCNAME("pixReadStreamJpeg"); if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); if (pnwarn) *pnwarn = 0; /* init */ if (cmflag != 0 && cmflag != 1) cmflag = 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; /* init */ if (setjmp(jpeg_jmpbuf)) { pixDestroy(&pix); FREE(rowbuffer); return (PIX *)ERROR_PTR("internal jpeg error", procName, NULL); } rowbuffer = NULL; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_do_not_exit; /* catch error; do not exit! */ jpeg_create_decompress(&cinfo); cinfo.client_data = &comment; jpeg_set_marker_processor(&cinfo, JPEG_COM, jpeg_comment_callback); jpeg_stdio_src(&cinfo, fp); jpeg_read_header(&cinfo, TRUE); cinfo.scale_denom = reduction; cinfo.scale_num = 1; if (hint & L_HINT_GRAY) cinfo.out_color_space = JCS_GRAYSCALE; jpeg_calc_output_dimensions(&cinfo); /* Allocate the image and a row buffer */ spp = cinfo.out_color_components; w = cinfo.output_width; h = cinfo.output_height; ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmflag == 0); cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmflag == 0); if (spp != 1 && spp != 3 && !ycck && !cmyk) { if (comment) FREE(comment); return (PIX *)ERROR_PTR("spp must be 1 or 3, or YCCK or CMYK", procName, NULL); } if ((spp == 3 && cmflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), spp * w); pix = pixCreate(w, h, 32); } else { /* 8 bpp gray or colormapped */ rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), w); pix = pixCreate(w, h, 8); } if (!rowbuffer || !pix) { if (comment) FREE(comment); if (rowbuffer) FREE(rowbuffer); pixDestroy(&pix); return (PIX *)ERROR_PTR("rowbuffer or pix not made", procName, NULL); } if (comment) { pixSetText(pix, (char *)comment); FREE(comment); } if (spp == 1) /* Grayscale or colormapped */ jpeg_start_decompress(&cinfo); else { /* Color; spp == 3 or YCCK or CMYK */ if (cmflag == 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 */ if ((spp == 3 && cmflag == 0) || ycck || cmyk) { /* -- 24 bit color -- */ for (i = 0; i < h; i++) { if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) != 1) return (PIX *)ERROR_PTR("bad read scanline", procName, NULL); 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 are inverted on read-in, see the "Special color spaces" section of "Using the IJG JPEG Library" by Thomas G. Lane. */ for (j = k = 0; j < w; j++) { cyan = 255 - rowbuffer[k++]; magenta = 255 - rowbuffer[k++]; yellow = 255 - rowbuffer[k++]; white = rowbuffer[k++]; black = 255 - white; rval = 255 - (cyan * white) / 255 - black; gval = 255 - (magenta * white) / 255 - black; bval = 255 - (yellow * white) / 255 - black; 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 */ for (i = 0; i < h; i++) { if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) != 1) return (PIX *)ERROR_PTR("bad read scanline", procName, NULL); line = data + i * wpl; for (j = 0; j < w; j++) SET_DATA_BYTE(line, j, rowbuffer[j]); } } if (pnwarn) *pnwarn = cinfo.err->num_warnings; switch (cinfo.density_unit) { case 1: /* pixels per inch */ pixSetXRes(pix, cinfo.X_density); pixSetYRes(pix, cinfo.Y_density); break; case 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)); break; default: /* the pixel density may not be defined; ignore */ break; } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); FREE(rowbuffer); return pix; }