/*! * \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 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; }
/*! * findFileFormatBuffer() * * Input: byte buffer (at least 12 bytes in size; we can't check) * &format (<return>) * Return: 0 if OK, 1 on error or if format is not recognized * * Notes: * (1) This determines the file format from the first 12 bytes in * the compressed data stream, which are stored in memory. * (2) For tiff files, this returns IFF_TIFF. The specific tiff * compression is then determined using findTiffCompression(). */ l_int32 findFileFormatBuffer(const l_uint8 *buf, l_int32 *pformat) { l_uint16 twobytepw; PROCNAME("findFileFormatBuffer"); if (!pformat) return ERROR_INT("&format not defined", procName, 1); *pformat = IFF_UNKNOWN; if (!buf) return ERROR_INT("byte buffer not defined", procName, 0); /* Check the bmp and tiff 2-byte header ids */ ((char *)(&twobytepw))[0] = buf[0]; ((char *)(&twobytepw))[1] = buf[1]; if (convertOnBigEnd16(twobytepw) == BMP_ID) { *pformat = IFF_BMP; return 0; } if (twobytepw == TIFF_BIGEND_ID || twobytepw == TIFF_LITTLEEND_ID) { *pformat = IFF_TIFF; return 0; } /* Check for the p*m 2-byte header ids */ if ((buf[0] == 'P' && buf[1] == '4') || /* newer packed */ (buf[0] == 'P' && buf[1] == '1')) { /* old format */ *pformat = IFF_PNM; return 0; } if ((buf[0] == 'P' && buf[1] == '5') || /* newer */ (buf[0] == 'P' && buf[1] == '2')) { /* old */ *pformat = IFF_PNM; return 0; } if ((buf[0] == 'P' && buf[1] == '6') || /* newer */ (buf[0] == 'P' && buf[1] == '3')) { /* old */ *pformat = IFF_PNM; return 0; } /* Consider the first 11 bytes of the standard JFIF JPEG header: * - The first two bytes are the most important: 0xffd8. * - The next two bytes are the jfif marker: 0xffe0. * Not all jpeg files have this marker. * - The next two bytes are the header length. * - The next 5 bytes are a null-terminated string. * For JFIF, the string is "JFIF", naturally. For others it * can be "Exif" or just about anything else. * - Because of all this variability, we only check the first * two byte marker. All jpeg files are identified as * IFF_JFIF_JPEG. */ if (buf[0] == 0xff && buf[1] == 0xd8) { *pformat = IFF_JFIF_JPEG; return 0; } /* Check for the 8 byte PNG signature (png_signature in png.c): * {137, 80, 78, 71, 13, 10, 26, 10} */ if (buf[0] == 137 && buf[1] == 80 && buf[2] == 78 && buf[3] == 71 && buf[4] == 13 && buf[5] == 10 && buf[6] == 26 && buf[7] == 10) { *pformat = IFF_PNG; return 0; } /* Look for "GIF87a" or "GIF89a" */ if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == '8' && (buf[4] == '7' || buf[4] == '9') && buf[5] == 'a') { *pformat = IFF_GIF; return 0; } /* Check for both types of jp2k file */ if (strncmp((const char *)buf, (char *)JP2K_CODESTREAM, 4) == 0 || strncmp((const char *)buf, (char *)JP2K_IMAGE_DATA, 12) == 0) { *pformat = IFF_JP2; return 0; } /* Check for webp */ if (buf[0] == 'R' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == 'F' && buf[8] == 'W' && buf[9] == 'E' && buf[10] == 'B' && buf[11] == 'P') { *pformat = IFF_WEBP; return 0; } /* Check for "spix" serialized pix */ if (buf[0] == 's' && buf[1] == 'p' && buf[2] == 'i' && buf[3] == 'x') { *pformat = IFF_SPIX; return 0; } /* File format identifier not found; unknown */ return 1; }