/*! * \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; }
jlong Java_com_googlecode_leptonica_android_ReadFile_nativeReadBitmap(JNIEnv *env, jclass clazz, jobject bitmap) { LOGV(__FUNCTION__); l_int32 w, h, d; AndroidBitmapInfo info; void* pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return JNI_FALSE; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888 !"); return JNI_FALSE; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); return JNI_FALSE; } PIX *pixd = pixCreate(info.width, info.height, 32); l_uint8 *src = (l_uint8 *) pixels; l_uint8 *dst = (l_uint8 *) pixGetData(pixd); l_int32 srcBpl = (info.stride); l_int32 dstBpl = pixGetWpl(pixd)*4; for (int dy = 0; dy < info.height; dy++) { memcpy(dst, src, 4 * info.width); dst += dstBpl; src += srcBpl; } pixEndianByteSwap(pixd); AndroidBitmap_unlockPixels(env, bitmap); return (jlong) pixd; }
/*! * pixReadMemWebP() * * Input: filedata (webp compressed data in memory) * filesize (number of bytes in data) * Return: pix (32 bpp), or null on error * * 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(). */ 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); 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); } /* WebP decoder emits opposite byte order for RGBA components */ pixEndianByteSwap(pix); return pix; }
/*! * pixWriteMemWebP() * * Input: &encdata (<return> webp encoded data of pixs) * &encsize (<return> size of webp encoded data) * pixs (any depth, cmapped OK) * quality (0 - 100; default ~80) * lossless (use 1 for lossless; 0 for lossy) * Return: 0 if OK, 1 on error * * Notes: * (1) Lossless and lossy encoding are entirely different in webp. * @quality applies to lossy, and is ignored for lossless. * (2) The input image is converted to RGB if necessary. If spp == 3, * we set the alpha channel to fully opaque (255), and * WebPEncodeRGBA() then removes the alpha chunk when encoding, * setting the internal header field has_alpha to 0. */ l_int32 pixWriteMemWebP(l_uint8 **pencdata, size_t *pencsize, PIX *pixs, l_int32 quality, l_int32 lossless) { l_int32 w, h, d, wpl, stride; l_uint32 *data; PIX *pix1, *pix2; PROCNAME("pixWriteMemWebP"); if (!pencdata) return ERROR_INT("&encdata not defined", procName, 1); *pencdata = NULL; if (!pencsize) return ERROR_INT("&encsize not defined", procName, 1); *pencsize = 0; if (!pixs) return ERROR_INT("&pixs not defined", procName, 1); if (lossless == 0 && (quality < 0 || quality > 100)) return ERROR_INT("quality not in [0 ... 100]", procName, 1); if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) return ERROR_INT("failure to remove color map", procName, 1); /* Convert to rgb if not 32 bpp; pix2 must not be a clone of pixs. */ if (pixGetDepth(pix1) != 32) pix2 = pixConvertTo32(pix1); else pix2 = pixCopy(NULL, pix1); pixDestroy(&pix1); pixGetDimensions(pix2, &w, &h, &d); if (w <= 0 || h <= 0 || d != 32) { pixDestroy(&pix2); return ERROR_INT("pix2 not 32 bpp or of 0 size", procName, 1); } /* If spp == 3, need to set alpha layer to opaque (all 1s). */ if (pixGetSpp(pix2) == 3) pixSetComponentArbitrary(pix2, L_ALPHA_CHANNEL, 255); /* Webp encoder assumes big-endian byte order for RGBA components */ pixEndianByteSwap(pix2); wpl = pixGetWpl(pix2); data = pixGetData(pix2); stride = wpl * 4; if (lossless) { *pencsize = WebPEncodeLosslessRGBA((uint8_t *)data, w, h, stride, pencdata); } else { *pencsize = WebPEncodeRGBA((uint8_t *)data, w, h, stride, quality, pencdata); } pixDestroy(&pix2); if (*pencsize == 0) { free(pencdata); *pencdata = NULL; return ERROR_INT("webp encoding failed", procName, 1); } return 0; }
/*! * pixWriteStreamPng() * * Input: stream * pix * gamma (use 0.0 if gamma is not defined) * Return: 0 if OK; 1 on error * * Notes: * (1) If called from pixWriteStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential writes of png format images to a stream, * use pixWriteStreamPng() directly. * (3) gamma is an optional png chunk. If no gamma value is to be * placed into the file, use gamma = 0.0. Otherwise, if * gamma > 0.0, its value is written into the header. * (4) The use of gamma in png is highly problematic. For an illuminating * discussion, see: http://hsivonen.iki.fi/png-gamma/ * (5) What is the effect/meaning of gamma in the png file? This * gamma, which we can call the 'source' gamma, is the * inverse of the gamma that was used in enhance.c to brighten * or darken images. The 'source' gamma is supposed to indicate * the intensity mapping that was done at the time the * image was captured. Display programs typically apply a * 'display' gamma of 2.2 to the output, which is intended * to linearize the intensity based on the response of * thermionic tubes (CRTs). Flat panel LCDs have typically * been designed to give a similar response as CRTs (call it * "backward compatibility"). The 'display' gamma is * in some sense the inverse of the 'source' gamma. * jpeg encoders attached to scanners and cameras will lighten * the pixels, applying a gamma corresponding to approximately * a square-root relation of output vs input: * output = input^(gamma) * where gamma is often set near 0.4545 (1/gamma is 2.2). * This is stored in the image file. Then if the display * program reads the gamma, it will apply a display gamma, * typically about 2.2; the product is 1.0, and the * display program produces a linear output. This works because * the dark colors were appropriately boosted by the scanner, * as described by the 'source' gamma, so they should not * be further boosted by the display program. * (6) As an example, with xv and display, if no gamma is stored, * the program acts as if gamma were 0.4545, multiplies this by 2.2, * and does a linear rendering. Taking this as a baseline * brightness, if the stored gamma is: * > 0.4545, the image is rendered lighter than baseline * < 0.4545, the image is rendered darker than baseline * In contrast, gqview seems to ignore the gamma chunk in png. * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32. However, it is possible, and in some cases desirable, * to write out a png file using an rgb pix that has 24 bpp. * For example, the open source xpdf SplashBitmap class generates * 24 bpp rgb images. Consequently, we anble writing 24 bpp pix. * To generate such a pix, you can make a 24 bpp pix without data * and assign the data array to the pix; e.g., * pix = pixCreateHeader(w, h, 24); * pixSetData(pix, rgbdata); * See pixConvert32To24() for an example, where we get rgbdata * from the 32 bpp pix. Caution: do not call pixSetPadBits(), * because the alignment is wrong and you may erase part of the * last pixel on each line. */ l_int32 pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) { char commentstring[] = "Comment"; l_int32 i, j, k; l_int32 wpl, d, cmflag; l_int32 ncolors; l_int32 *rmap, *gmap, *bmap; l_uint32 *data, *ppixel; png_byte bit_depth, color_type; png_uint_32 w, h; png_uint_32 xres, yres; png_bytep *row_pointers; png_bytep rowbuffer; png_structp png_ptr; png_infop info_ptr; png_colorp palette; PIX *pixt; PIXCMAP *cmap; char *text; PROCNAME("pixWriteStreamPng"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); /* Allocate the 2 data structures */ if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return ERROR_INT("png_ptr not made", procName, 1); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return ERROR_INT("info_ptr not made", procName, 1); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("internal png error", procName, 1); } png_init_io(png_ptr, fp); /* With best zlib compression (9), get between 1 and 10% improvement * over default (5), but the compression is 3 to 10 times slower. * Our default compression is the zlib default (5). */ png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION); w = pixGetWidth(pix); h = pixGetHeight(pix); d = pixGetDepth(pix); if ((cmap = pixGetColormap(pix))) cmflag = 1; else cmflag = 0; /* Set the color type and bit depth. */ if (d == 32 && var_PNG_WRITE_ALPHA == 1) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ cmflag = 0; /* ignore if it exists */ } else if (d == 24 || d == 32) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; /* 2 */ cmflag = 0; /* ignore if it exists */ } else { bit_depth = d; color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ } if (cmflag) color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ #if DEBUG fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", cmflag, bit_depth, color_type); #endif /* DEBUG */ png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Store resolution in ppm, if known */ xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); if ((xres == 0) || (yres == 0)) png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); else png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); if (cmflag) { pixcmapToArrays(cmap, &rmap, &gmap, &bmap); ncolors = pixcmapGetCount(cmap); /* Make and save the palette */ if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color)))) == NULL) return ERROR_INT("palette not made", procName, 1); for (i = 0; i < ncolors; i++) { palette[i].red = (png_byte)rmap[i]; palette[i].green = (png_byte)gmap[i]; palette[i].blue = (png_byte)bmap[i]; } png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); FREE(rmap); FREE(gmap); FREE(bmap); } /* 0.4545 is treated as the default by some image * display programs (not gqview). A value > 0.4545 will * lighten an image as displayed by xv, display, etc. */ if (gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); if ((text = pixGetText(pix))) { png_text text_chunk; text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; text_chunk.key = commentstring; text_chunk.text = text; text_chunk.text_length = strlen(text); #ifdef PNG_ITXT_SUPPORTED text_chunk.itxt_length = 0; text_chunk.lang = NULL; text_chunk.lang_key = NULL; #endif png_set_text(png_ptr, info_ptr, &text_chunk, 1); } /* Write header and palette info */ png_write_info(png_ptr, info_ptr); if ((d != 32) && (d != 24)) { /* not rgb color */ /* Generate a temporary pix with bytes swapped. * For a binary image, there are two conditions in * which you must first invert the data for writing png: * (a) no colormap * (b) colormap with BLACK set to 0 * png writes binary with BLACK = 0, unless contradicted * by a colormap. If the colormap has BLACK = "1" * (typ. about 255), do not invert the data. If there * is no colormap, you must invert the data to store * in default BLACK = 0 state. */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { pixt = pixInvert(NULL, pix); pixEndianByteSwap(pixt); } else pixt = pixEndianByteSwapNew(pix); if (!pixt) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("pixt not made", procName, 1); } /* Make and assign array of image row pointers */ if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL) return ERROR_INT("row-pointers not made", procName, 1); wpl = pixGetWpl(pixt); data = pixGetData(pixt); for (i = 0; i < h; i++) row_pointers[i] = (png_bytep)(data + i * wpl); png_set_rows(png_ptr, info_ptr, row_pointers); /* Transfer the data */ png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); FREE(row_pointers); pixDestroy(&pixt); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } /* For rgb, compose and write a row at a time */ data = pixGetData(pix); wpl = pixGetWpl(pix); if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); } } else { /* 32 bpp rgb and rgba */ if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL) return ERROR_INT("rowbuffer not made", procName, 1); for (i = 0; i < h; i++) { ppixel = data + i * wpl; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); if (var_PNG_WRITE_ALPHA == 1) rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); ppixel++; } png_write_rows(png_ptr, &rowbuffer, 1); } FREE(rowbuffer); } png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; }
/*! * \brief pixWriteMemBmp() * * \param[out] pfdata data of bmp formatted image * \param[out] pfsize size of returned data * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) 2 bpp bmp files are not valid in the spec, and are * written as 8 bpp. * (2) pix with depth <= 8 bpp are written with a colormap. * 16 bpp gray and 32 bpp rgb pix are written without a colormap. * (3) The transparency component in an rgb pix is ignored. * All 32 bpp pix have the bmp alpha component set to 255 (opaque). * (4) The bmp colormap entries, RGBA_QUAD, are the same as * the ones used for colormaps in leptonica. This allows * a simple memcpy for bmp output. * </pre> */ l_int32 pixWriteMemBmp(l_uint8 **pfdata, size_t *pfsize, PIX *pixs) { l_uint8 pel[4]; l_uint8 *cta = NULL; /* address of the bmp color table array */ l_uint8 *fdata, *data, *fmdata; l_int32 cmaplen; /* number of bytes in the bmp colormap */ l_int32 ncolors, val, stepsize; l_int32 w, h, d, fdepth, xres, yres; l_int32 pixWpl, pixBpl, extrabytes, fBpl, fWpl, i, j, k; l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ l_uint32 offbytes, fimagebytes; l_uint32 *line, *pword; size_t fsize; BMP_FH *bmpfh; BMP_IH *bmpih; PIX *pix; PIXCMAP *cmap; RGBA_QUAD *pquad; PROCNAME("pixWriteMemBmp"); if (pfdata) *pfdata = NULL; if (pfsize) *pfsize = 0; if (!pfdata) return ERROR_INT("&fdata not defined", procName, 1 ); if (!pfsize) return ERROR_INT("&fsize not defined", procName, 1 ); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixGetDimensions(pixs, &w, &h, &d); if (d == 2) { L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", procName); pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); d = 8; } else { pix = pixCopy(NULL, pixs); } fdepth = (d == 32) ? 24 : d; /* Resolution is given in pixels/meter */ xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); pixWpl = pixGetWpl(pix); pixBpl = 4 * pixWpl; fWpl = (w * fdepth + 31) / 32; fBpl = 4 * fWpl; fimagebytes = h * fBpl; if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { pixDestroy(&pix); return ERROR_INT("image data is too large", procName, 1); } /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ heapcm = 0; if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ ncolors = 0; cmaplen = 0; } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ ncolors = pixcmapGetCount(cmap); cmaplen = ncolors * sizeof(RGBA_QUAD); cta = (l_uint8 *)cmap->array; } else { /* no existing colormap; d <= 8; make a binary or gray one */ if (d == 1) { cmaplen = sizeof(bwmap); ncolors = 2; cta = (l_uint8 *)bwmap; } else { /* d = 2,4,8; use a grayscale output colormap */ ncolors = 1 << fdepth; cmaplen = ncolors * sizeof(RGBA_QUAD); heapcm = 1; cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); stepsize = 255 / (ncolors - 1); for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; i < ncolors; i++, val += stepsize, pquad++) { pquad->blue = pquad->green = pquad->red = val; pquad->alpha = 255; /* opaque */ } } } #if DEBUG {l_uint8 *pcmptr; pcmptr = (l_uint8 *)pixGetColormap(pix)->array; fprintf(stderr, "Pix colormap[0] = %c%c%c%d\n", pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); fprintf(stderr, "Pix colormap[1] = %c%c%c%d\n", pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); } #endif /* DEBUG */ offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; fsize = offbytes + fimagebytes; fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); *pfdata = fdata; *pfsize = fsize; /* Convert to little-endian and write the file header data */ bmpfh = (BMP_FH *)fdata; bmpfh->bfType = convertOnBigEnd16(BMP_ID); bmpfh->bfSize = convertOnBigEnd16(fsize & 0x0000ffff); bmpfh->bfFill1 = convertOnBigEnd16((fsize >> 16) & 0x0000ffff); bmpfh->bfOffBits = convertOnBigEnd16(offbytes & 0x0000ffff); bmpfh->bfFill2 = convertOnBigEnd16((offbytes >> 16) & 0x0000ffff); /* Convert to little-endian and write the info header data */ bmpih = (BMP_IH *)(fdata + BMP_FHBYTES); bmpih->biSize = convertOnBigEnd32(BMP_IHBYTES); bmpih->biWidth = convertOnBigEnd32(w); bmpih->biHeight = convertOnBigEnd32(h); bmpih->biPlanes = convertOnBigEnd16(1); bmpih->biBitCount = convertOnBigEnd16(fdepth); bmpih->biSizeImage = convertOnBigEnd32(fimagebytes); bmpih->biXPelsPerMeter = convertOnBigEnd32(xres); bmpih->biYPelsPerMeter = convertOnBigEnd32(yres); bmpih->biClrUsed = convertOnBigEnd32(ncolors); bmpih->biClrImportant = convertOnBigEnd32(ncolors); /* Copy the colormap data and free the cta if necessary */ if (ncolors > 0) { memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); if (heapcm) LEPT_FREE(cta); } /* When you write a binary image with a colormap * that sets BLACK to 0, you must invert the data */ if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { pixInvert(pix, pix); } /* An endian byte swap is also required */ pixEndianByteSwap(pix); /* Transfer the image data. Image origin for bmp is at lower right. */ fmdata = fdata + offbytes; if (fdepth != 24) { /* typ 1 or 8 bpp */ data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); for (i = 0; i < h; i++) { memcpy(fmdata, data, fBpl); data -= pixBpl; fmdata += fBpl; } } else { /* 32 bpp pix; 24 bpp file * See the comments in pixReadStreamBmp() to * understand the logic behind the pixel ordering below. * Note that we have again done an endian swap on * little endian machines before arriving here, so that * the bytes are ordered on both platforms as: Red Green Blue -- |-----------|------------|-----------|-----------| */ extrabytes = fBpl - 3 * w; line = pixGetData(pix) + pixWpl * (h - 1); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pword = line + j; pel[2] = *((l_uint8 *)pword + COLOR_RED); pel[1] = *((l_uint8 *)pword + COLOR_GREEN); pel[0] = *((l_uint8 *)pword + COLOR_BLUE); memcpy(fmdata, &pel, 3); fmdata += 3; } if (extrabytes) { for (k = 0; k < extrabytes; k++) { memcpy(fmdata, &pel, 1); fmdata++; } } line -= pixWpl; } } pixDestroy(&pix); 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; }
jboolean Java_com_googlecode_leptonica_android_WriteFile_nativeWriteBitmap(JNIEnv *env, jclass clazz, jlong nativePix, jobject bitmap) { PIX *pixs = (PIX *) nativePix; l_int32 w, h, d; AndroidBitmapInfo info; void* pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return JNI_FALSE; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888 !"); return JNI_FALSE; } pixGetDimensions(pixs, &w, &h, &d); if (w != info.width || h != info.height) { LOGE("Bitmap width and height do not match Pix dimensions!"); return JNI_FALSE; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); return JNI_FALSE; } pixEndianByteSwap(pixs); l_uint8 *dst = (l_uint8 *) pixels; l_uint8 *src = (l_uint8 *) pixGetData(pixs); l_int32 dstBpl = info.stride; l_int32 srcBpl = 4 * pixGetWpl(pixs); LOGE("Writing 32bpp RGBA bitmap (w=%d, h=%d, stride=%d) from %dbpp Pix (wpl=%d)", info.width, info.height, info.stride, d, pixGetWpl(pixs)); for (int dy = 0; dy < info.height; dy++) { l_uint8 *dstx = dst; l_uint8 *srcx = src; if (d == 32) { memcpy(dst, src, 4 * info.width); } else if (d == 8) { for (int dw = 0; dw < info.width; dw++) { dstx[0] = dstx[1] = dstx[2] = srcx[0]; dstx[3] = 0xFF; dstx += 4; srcx += 1; } } else if (d == 1) { for (int dw = 0; dw < info.width; dw++) { dstx[0] = dstx[1] = dstx[2] = (1 << (7 - (dw & 7)) & srcx[0]) ? 0x00 : 0xFF; dstx[3] = 0xFF; dstx += 4; srcx += ((dw % 8) == 7) ? 1 : 0; } } dst += dstBpl; src += srcBpl; } AndroidBitmap_unlockPixels(env, bitmap); return JNI_TRUE; }
/*! * \brief pixWriteMemWebP() * * \param[out] pencdata webp encoded data of pixs * \param[out] pencsize size of webp encoded data * \param[in] pixs any depth, cmapped OK * \param[in] quality 0 - 100; default ~80 * \param[in] lossless use 1 for lossless; 0 for lossy * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) Lossless and lossy encoding are entirely different in webp. * %quality applies to lossy, and is ignored for lossless. * (2) The input image is converted to RGB if necessary. If spp == 3, * we set the alpha channel to fully opaque (255), and * WebPEncodeRGBA() then removes the alpha chunk when encoding, * setting the internal header field has_alpha to 0. * </pre> */ l_ok pixWriteMemWebP(l_uint8 **pencdata, size_t *pencsize, PIX *pixs, l_int32 quality, l_int32 lossless) { l_int32 w, h, d, wpl, stride; l_uint32 *data; PIX *pix1, *pix2; PROCNAME("pixWriteMemWebP"); if (!pencdata) return ERROR_INT("&encdata not defined", procName, 1); *pencdata = NULL; if (!pencsize) return ERROR_INT("&encsize not defined", procName, 1); *pencsize = 0; if (!pixs) return ERROR_INT("&pixs not defined", procName, 1); if (lossless == 0 && (quality < 0 || quality > 100)) return ERROR_INT("quality not in [0 ... 100]", procName, 1); if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) return ERROR_INT("failure to remove color map", procName, 1); /* Convert to rgb if not 32 bpp; pix2 must not be a clone of pixs. */ if (pixGetDepth(pix1) != 32) pix2 = pixConvertTo32(pix1); else pix2 = pixCopy(NULL, pix1); pixDestroy(&pix1); pixGetDimensions(pix2, &w, &h, &d); if (w <= 0 || h <= 0 || d != 32) { pixDestroy(&pix2); return ERROR_INT("pix2 not 32 bpp or of 0 size", procName, 1); } /* If spp == 3, need to set alpha layer to opaque (all 1s). */ if (pixGetSpp(pix2) == 3) pixSetComponentArbitrary(pix2, L_ALPHA_CHANNEL, 255); /* 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(pix2); wpl = pixGetWpl(pix2); data = pixGetData(pix2); stride = wpl * 4; if (lossless) { *pencsize = WebPEncodeLosslessRGBA((uint8_t *)data, w, h, stride, pencdata); } else { *pencsize = WebPEncodeRGBA((uint8_t *)data, w, h, stride, quality, pencdata); } pixDestroy(&pix2); if (*pencsize == 0) { free(*pencdata); *pencdata = NULL; return ERROR_INT("webp encoding failed", procName, 1); } return 0; }
/*! * pixGetWindowsHBITMAP() * * Input: pix * Return: Windows hBitmap, or null on error * * Notes: * (1) It's the responsibility of the caller to destroy the * returned hBitmap with a call to DeleteObject (or with * something that eventually calls DeleteObject). */ HBITMAP pixGetWindowsHBITMAP(PIX *pix) { l_int32 width, height, depth; l_uint32 *data; HBITMAP hBitmap = NULL; BITMAP bm; DWORD imageBitsSize; PIX *pixt = NULL; PIXCMAP *cmap; PROCNAME("pixGetWindowsHBITMAP"); if (!pix) return (HBITMAP)ERROR_PTR("pix not defined", procName, NULL); pixGetDimensions(pix, &width, &height, &depth); cmap = pixGetColormap(pix); if (depth == 24) depth = 32; if (depth == 2) { pixt = pixConvert2To8(pix, 0, 85, 170, 255, TRUE); if (!pixt) return (HBITMAP)ERROR_PTR("unable to convert pix from 2bpp to 8bpp", procName, NULL); depth = pixGetDepth(pixt); cmap = pixGetColormap(pixt); } if (depth < 16) { if (!cmap) cmap = pixcmapCreateLinear(depth, 1<<depth); } hBitmap = DSCreateDIBSection(width, height, depth, cmap); if (!hBitmap) return (HBITMAP)ERROR_PTR("Unable to create HBITMAP", procName, NULL); /* By default, Windows assumes bottom up images */ if (pixt) pixt = pixFlipTB(pixt, pixt); else pixt = pixFlipTB(NULL, pix); /* "standard" color table assumes bit off=black */ if (depth == 1) { pixInvert(pixt, pixt); } /* Don't byte swap until done manipulating pix! */ if (depth <= 16) pixEndianByteSwap(pixt); GetObject (hBitmap, sizeof(BITMAP), &bm); imageBitsSize = ImageBitsSize(hBitmap); data = pixGetData(pixt); if (data) { memcpy (bm.bmBits, data, imageBitsSize); } else { DeleteObject (hBitmap); hBitmap = NULL; } pixDestroy(&pixt); return hBitmap; }