Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/bmpio.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*====================================================================* | |
| 2 - Copyright (C) 2001 Leptonica. All rights reserved. | |
| 3 - | |
| 4 - Redistribution and use in source and binary forms, with or without | |
| 5 - modification, are permitted provided that the following conditions | |
| 6 - are met: | |
| 7 - 1. Redistributions of source code must retain the above copyright | |
| 8 - notice, this list of conditions and the following disclaimer. | |
| 9 - 2. Redistributions in binary form must reproduce the above | |
| 10 - copyright notice, this list of conditions and the following | |
| 11 - disclaimer in the documentation and/or other materials | |
| 12 - provided with the distribution. | |
| 13 - | |
| 14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY | |
| 18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 *====================================================================*/ | |
| 26 | |
| 27 /*! | |
| 28 * \file bmpio.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Read bmp | |
| 32 * PIX *pixReadStreamBmp() | |
| 33 * PIX *pixReadMemBmp() | |
| 34 * | |
| 35 * Write bmp | |
| 36 * l_int32 pixWriteStreamBmp() | |
| 37 * l_int32 pixWriteMemBmp() | |
| 38 * | |
| 39 * </pre> | |
| 40 */ | |
| 41 | |
| 42 #ifdef HAVE_CONFIG_H | |
| 43 #include <config_auto.h> | |
| 44 #endif /* HAVE_CONFIG_H */ | |
| 45 | |
| 46 #include <string.h> | |
| 47 #include "allheaders.h" | |
| 48 #include "pix_internal.h" | |
| 49 #include "bmp.h" | |
| 50 | |
| 51 /* --------------------------------------------*/ | |
| 52 #if USE_BMPIO /* defined in environ.h */ | |
| 53 /* --------------------------------------------*/ | |
| 54 | |
| 55 /* Here we're setting the pixel value 0 to white (255) and the | |
| 56 * value 1 to black (0). This is the convention for grayscale, but | |
| 57 * the opposite of the convention for 1 bpp, where 0 is white | |
| 58 * and 1 is black. Both colormap entries are opaque (alpha = 255) */ | |
| 59 RGBA_QUAD bwmap[2] = { {255,255,255,255}, {0,0,0,255} }; | |
| 60 | |
| 61 /* Image dimension limits */ | |
| 62 static const l_int32 L_MAX_ALLOWED_WIDTH = 1000000; | |
| 63 static const l_int32 L_MAX_ALLOWED_HEIGHT = 1000000; | |
| 64 static const l_int64 L_MAX_ALLOWED_PIXELS = 400000000LL; | |
| 65 static const l_int32 L_MAX_ALLOWED_RES = 10000000; /* pixels/meter */ | |
| 66 | |
| 67 #ifndef NO_CONSOLE_IO | |
| 68 #define DEBUG 0 | |
| 69 #endif /* ~NO_CONSOLE_IO */ | |
| 70 | |
| 71 /*--------------------------------------------------------------* | |
| 72 * Read bmp * | |
| 73 *--------------------------------------------------------------*/ | |
| 74 /*! | |
| 75 * \brief pixReadStreamBmp() | |
| 76 * | |
| 77 * \param[in] fp file stream opened for read | |
| 78 * \return pix, or NULL on error | |
| 79 * | |
| 80 * <pre> | |
| 81 * Notes: | |
| 82 * (1) Here are references on the bmp file format: | |
| 83 * http://en.wikipedia.org/wiki/BMP_file_format | |
| 84 * http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html | |
| 85 * </pre> | |
| 86 */ | |
| 87 PIX * | |
| 88 pixReadStreamBmp(FILE *fp) | |
| 89 { | |
| 90 l_uint8 *data; | |
| 91 size_t size; | |
| 92 PIX *pix; | |
| 93 | |
| 94 if (!fp) | |
| 95 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); | |
| 96 | |
| 97 /* Read data from file and decode into Y,U,V arrays */ | |
| 98 rewind(fp); | |
| 99 if ((data = l_binaryReadStream(fp, &size)) == NULL) | |
| 100 return (PIX *)ERROR_PTR("data not read", __func__, NULL); | |
| 101 | |
| 102 pix = pixReadMemBmp(data, size); | |
| 103 LEPT_FREE(data); | |
| 104 return pix; | |
| 105 } | |
| 106 | |
| 107 | |
| 108 /*! | |
| 109 * \brief pixReadMemBmp() | |
| 110 * | |
| 111 * \param[in] cdata bmp data | |
| 112 * \param[in] size number of bytes of bmp-formatted data | |
| 113 * \return pix, or NULL on error | |
| 114 * | |
| 115 * <pre> | |
| 116 * Notes: | |
| 117 * (1) The BMP file is organized as follows: | |
| 118 * * 14 byte fileheader | |
| 119 * * Variable size infoheader: 40, 108 or 124 bytes. | |
| 120 * We only use data in he first 40 bytes. | |
| 121 * * Optional colormap, with size 4 * ncolors (in bytes) | |
| 122 * * Image data | |
| 123 * (2) 2 bpp bmp files are not valid in the original spec, but they | |
| 124 * are valid in later versions. | |
| 125 * (3) We support reading rgb files with bpp = 24 and rgba files | |
| 126 * with 32 bpp. For the latter, the transparency component of | |
| 127 * the generated pix is saved; however, for rgba images with | |
| 128 * non-opaque transparent components, png provides more flexibility. | |
| 129 * </pre> | |
| 130 */ | |
| 131 PIX * | |
| 132 pixReadMemBmp(const l_uint8 *cdata, | |
| 133 size_t size) | |
| 134 { | |
| 135 l_uint8 pel[4]; | |
| 136 l_uint8 *cmapBuf, *fdata, *data, *bmpih_b; | |
| 137 l_int16 bftype, depth, d; | |
| 138 l_int32 offset, width, height, height_neg, xres, yres, spp; | |
| 139 l_int32 compression, imagebytes, fdatabytes, cmapbytes, ncolors, maxcolors; | |
| 140 l_int32 fdatabpl, extrabytes, filebpp, pixWpl, pixBpl, i, j, k; | |
| 141 l_uint32 ihbytes; | |
| 142 l_uint32 *line, *pixdata, *pword; | |
| 143 l_int64 npixels; | |
| 144 BMP_FH *bmpfh; | |
| 145 BMP_IH bmpih; | |
| 146 PIX *pix, *pix1; | |
| 147 PIXCMAP *cmap; | |
| 148 | |
| 149 if (!cdata) | |
| 150 return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL); | |
| 151 if (size < sizeof(BMP_FH) + sizeof(BMP_IH)) | |
| 152 return (PIX *)ERROR_PTR("bmf size error", __func__, NULL); | |
| 153 | |
| 154 /* Verify this is an uncompressed bmp */ | |
| 155 bmpfh = (BMP_FH *)cdata; | |
| 156 bftype = bmpfh->bfType[0] + ((l_int32)bmpfh->bfType[1] << 8); | |
| 157 if (bftype != BMP_ID) | |
| 158 return (PIX *)ERROR_PTR("not bmf format", __func__, NULL); | |
| 159 memcpy(&bmpih, cdata + BMP_FHBYTES, BMP_IHBYTES); | |
| 160 compression = convertOnBigEnd32(bmpih.biCompression); | |
| 161 if (compression != 0) | |
| 162 return (PIX *)ERROR_PTR("cannot read compressed BMP files", | |
| 163 __func__, NULL); | |
| 164 | |
| 165 /* Find the offset from the beginning of the file to the image data */ | |
| 166 offset = bmpfh->bfOffBits[0]; | |
| 167 offset += (l_int32)bmpfh->bfOffBits[1] << 8; | |
| 168 offset += (l_int32)bmpfh->bfOffBits[2] << 16; | |
| 169 offset += (l_uint32)bmpfh->bfOffBits[3] << 24; | |
| 170 | |
| 171 /* Read the remaining useful data in the infoheader. | |
| 172 * Note that the first 4 bytes give the infoheader size. | |
| 173 * The infoheader pointer on sparc64 is not 32-bit aligned. */ | |
| 174 bmpih_b = (l_uint8 *)&bmpih; | |
| 175 ihbytes = bmpih_b[0] | ((l_int32)bmpih_b[1] << 8) | | |
| 176 ((l_int32)bmpih_b[2] << 16) | ((l_uint32)bmpih_b[3] << 24); | |
| 177 width = convertOnBigEnd32(bmpih.biWidth); | |
| 178 height = convertOnBigEnd32(bmpih.biHeight); | |
| 179 depth = convertOnBigEnd16(bmpih.biBitCount); | |
| 180 imagebytes = convertOnBigEnd32(bmpih.biSizeImage); | |
| 181 xres = convertOnBigEnd32(bmpih.biXPelsPerMeter); | |
| 182 yres = convertOnBigEnd32(bmpih.biYPelsPerMeter); | |
| 183 | |
| 184 /* Some sanity checking. We impose limits on the image | |
| 185 * dimensions, resolution and number of pixels. We make sure the | |
| 186 * file is the correct size to hold the amount of uncompressed data | |
| 187 * that is specified in the header. The number of colormap | |
| 188 * entries is checked: it can be either 0 (no cmap) or some | |
| 189 * number between 2 and 256. | |
| 190 * Note that the imagebytes for uncompressed images is either | |
| 191 * 0 or the size of the file data. (The fact that it can | |
| 192 * be 0 is perhaps some legacy glitch). */ | |
| 193 if (width < 1) | |
| 194 return (PIX *)ERROR_PTR("width < 1", __func__, NULL); | |
| 195 if (width > L_MAX_ALLOWED_WIDTH) | |
| 196 return (PIX *)ERROR_PTR("width too large", __func__, NULL); | |
| 197 if (height == 0 || height < -L_MAX_ALLOWED_HEIGHT || | |
| 198 height > L_MAX_ALLOWED_HEIGHT) | |
| 199 return (PIX *)ERROR_PTR("invalid height", __func__, NULL); | |
| 200 if (xres < 0 || xres > L_MAX_ALLOWED_RES || | |
| 201 yres < 0 || yres > L_MAX_ALLOWED_RES) | |
| 202 return (PIX *)ERROR_PTR("invalid resolution", __func__, NULL); | |
| 203 height_neg = 0; | |
| 204 if (height < 0) { | |
| 205 height_neg = 1; | |
| 206 height = -height; | |
| 207 } | |
| 208 if (ihbytes != 40 && ihbytes != 108 && ihbytes != 124) { | |
| 209 L_ERROR("invalid ihbytes = %d; not in {40, 108, 124}\n", | |
| 210 __func__, ihbytes); | |
| 211 return NULL; | |
| 212 } | |
| 213 npixels = 1LL * width * height; | |
| 214 if (npixels > L_MAX_ALLOWED_PIXELS) | |
| 215 return (PIX *)ERROR_PTR("npixels too large", __func__, NULL); | |
| 216 if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && | |
| 217 depth != 16 && depth != 24 && depth != 32) { | |
| 218 L_ERROR("invalid depth = %d; not in {1, 2, 4, 8, 16, 24, 32}\n", | |
| 219 __func__, depth); | |
| 220 return NULL; | |
| 221 } | |
| 222 fdatabpl = 4 * ((1LL * width * depth + 31)/32); | |
| 223 fdatabytes = fdatabpl * height; | |
| 224 if (imagebytes != 0 && imagebytes != fdatabytes) { | |
| 225 L_ERROR("invalid imagebytes = %d; not equal to fdatabytes = %d\n", | |
| 226 __func__, imagebytes, fdatabytes); | |
| 227 return NULL; | |
| 228 } | |
| 229 | |
| 230 /* In the original spec, BITMAPINFOHEADER is 40 bytes. | |
| 231 * There have been a number of revisions, to capture more information. | |
| 232 * For example, the fifth version, BITMAPV5HEADER, adds 84 bytes | |
| 233 * of ICC color profiles. We use the size of the infoheader | |
| 234 * to accommodate these newer formats. Knowing the size of the | |
| 235 * infoheader gives more opportunity to sanity check input params. */ | |
| 236 cmapbytes = offset - BMP_FHBYTES - ihbytes; | |
| 237 ncolors = cmapbytes / sizeof(RGBA_QUAD); | |
| 238 if (ncolors < 0 || ncolors == 1) | |
| 239 return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", __func__, NULL); | |
| 240 if (ncolors > 0 && depth > 8) | |
| 241 return (PIX *)ERROR_PTR("can't have cmap for d > 8", __func__, NULL); | |
| 242 maxcolors = (depth <= 8) ? 1 << depth : 0; | |
| 243 if (ncolors > maxcolors) { | |
| 244 L_ERROR("cmap too large for depth %d: ncolors = %d > maxcolors = %d\n", | |
| 245 __func__, depth, ncolors, maxcolors); | |
| 246 return NULL; | |
| 247 } | |
| 248 if (size != 1LL * offset + 1LL * fdatabytes) | |
| 249 return (PIX *)ERROR_PTR("size incommensurate with image data", | |
| 250 __func__,NULL); | |
| 251 | |
| 252 /* Handle the colormap */ | |
| 253 cmapBuf = NULL; | |
| 254 if (ncolors > 0) { | |
| 255 if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(ncolors, sizeof(RGBA_QUAD))) | |
| 256 == NULL) | |
| 257 return (PIX *)ERROR_PTR("cmapBuf alloc fail", __func__, NULL ); | |
| 258 | |
| 259 /* Read the colormap entry data from bmp. The RGBA_QUAD colormap | |
| 260 * entries are used for both bmp and leptonica colormaps. */ | |
| 261 memcpy(cmapBuf, cdata + BMP_FHBYTES + ihbytes, | |
| 262 ncolors * sizeof(RGBA_QUAD)); | |
| 263 } | |
| 264 | |
| 265 /* Make a 32 bpp pix if file depth is 24 bpp */ | |
| 266 d = (depth == 24) ? 32 : depth; | |
| 267 if ((pix = pixCreate(width, height, d)) == NULL) { | |
| 268 LEPT_FREE(cmapBuf); | |
| 269 return (PIX *)ERROR_PTR( "pix not made", __func__, NULL); | |
| 270 } | |
| 271 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ | |
| 272 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ | |
| 273 pixSetInputFormat(pix, IFF_BMP); | |
| 274 pixWpl = pixGetWpl(pix); | |
| 275 pixBpl = 4 * pixWpl; | |
| 276 if (depth <= 16) | |
| 277 spp = 1; | |
| 278 else if (depth == 24) | |
| 279 spp = 3; | |
| 280 else /* depth == 32 */ | |
| 281 spp = 4; | |
| 282 pixSetSpp(pix, spp); | |
| 283 | |
| 284 /* Convert the bmp colormap to a pixcmap */ | |
| 285 cmap = NULL; | |
| 286 if (ncolors > 0) { /* import the colormap to the pix cmap */ | |
| 287 cmap = pixcmapCreate(L_MIN(d, 8)); | |
| 288 LEPT_FREE(cmap->array); /* remove generated cmap array */ | |
| 289 cmap->array = (void *)cmapBuf; /* and replace */ | |
| 290 cmap->n = L_MIN(ncolors, 256); | |
| 291 for (i = 0; i < cmap->n; i++) /* set all colors opaque */ | |
| 292 pixcmapSetAlpha (cmap, i, 255); | |
| 293 } | |
| 294 if (pixSetColormap(pix, cmap)) { | |
| 295 pixDestroy(&pix); | |
| 296 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); | |
| 297 } | |
| 298 | |
| 299 /* Acquire the image data. Image origin for bmp is at lower right. */ | |
| 300 fdata = (l_uint8 *)cdata + offset; /* start of the bmp image data */ | |
| 301 pixdata = pixGetData(pix); | |
| 302 if (depth != 24 && depth != 32) { /* typ. 1 or 8 bpp */ | |
| 303 data = (l_uint8 *)pixdata + pixBpl * (height - 1); | |
| 304 for (i = 0; i < height; i++) { | |
| 305 memcpy(data, fdata, fdatabpl); | |
| 306 fdata += fdatabpl; | |
| 307 data -= pixBpl; | |
| 308 } | |
| 309 } else { /* 24 or 32 bpp file; 32 bpp pix | |
| 310 * Note: for rgb bmp files, pel[0] is blue, pel[1] is green, | |
| 311 * and pel[2] is red. This is opposite to the storage | |
| 312 * in the pix, which puts the red pixel in the 0 byte, | |
| 313 * the green in the 1 byte and the blue in the 2 byte. | |
| 314 * Note also that all words are endian flipped after | |
| 315 * assignment on L_LITTLE_ENDIAN platforms. | |
| 316 * | |
| 317 * We can then make these assignments for little endians: | |
| 318 * SET_DATA_BYTE(pword, 1, pel[0]); blue | |
| 319 * SET_DATA_BYTE(pword, 2, pel[1]); green | |
| 320 * SET_DATA_BYTE(pword, 3, pel[2]); red | |
| 321 * This looks like: | |
| 322 * 3 (R) 2 (G) 1 (B) 0 | |
| 323 * |-----------|------------|-----------|-----------| | |
| 324 * and after byte flipping: | |
| 325 * 3 2 (B) 1 (G) 0 (R) | |
| 326 * |-----------|------------|-----------|-----------| | |
| 327 * | |
| 328 * For big endians we set: | |
| 329 * SET_DATA_BYTE(pword, 2, pel[0]); blue | |
| 330 * SET_DATA_BYTE(pword, 1, pel[1]); green | |
| 331 * SET_DATA_BYTE(pword, 0, pel[2]); red | |
| 332 * This looks like: | |
| 333 * 0 (R) 1 (G) 2 (B) 3 | |
| 334 * |-----------|------------|-----------|-----------| | |
| 335 * so in both cases we get the correct assignment in the PIX. | |
| 336 * | |
| 337 * Can we do a platform-independent assignment? | |
| 338 * Yes, set the bytes without using macros: | |
| 339 * *((l_uint8 *)pword) = pel[2]; red | |
| 340 * *((l_uint8 *)pword + 1) = pel[1]; green | |
| 341 * *((l_uint8 *)pword + 2) = pel[0]; blue | |
| 342 * For little endians, before flipping, this looks again like: | |
| 343 * 3 (R) 2 (G) 1 (B) 0 | |
| 344 * |-----------|------------|-----------|-----------| | |
| 345 * | |
| 346 * For reading an spp == 4 file with a transparency component, | |
| 347 * the code below shows where the alpha component is located | |
| 348 * in each pixel. | |
| 349 */ | |
| 350 filebpp = (depth == 24) ? 3 : 4; | |
| 351 extrabytes = fdatabpl - filebpp * width; | |
| 352 line = pixdata + pixWpl * (height - 1); | |
| 353 for (i = 0; i < height; i++) { | |
| 354 for (j = 0; j < width; j++) { | |
| 355 pword = line + j; | |
| 356 memcpy(&pel, fdata, filebpp); | |
| 357 fdata += filebpp; | |
| 358 *((l_uint8 *)pword + COLOR_RED) = pel[2]; | |
| 359 *((l_uint8 *)pword + COLOR_GREEN) = pel[1]; | |
| 360 *((l_uint8 *)pword + COLOR_BLUE) = pel[0]; | |
| 361 /* Set the alpha byte to opaque for rgb */ | |
| 362 if (depth == 24) | |
| 363 *((l_uint8 *)pword + L_ALPHA_CHANNEL) = 255; | |
| 364 else | |
| 365 *((l_uint8 *)pword + L_ALPHA_CHANNEL) = pel[3]; | |
| 366 } | |
| 367 if (extrabytes) { | |
| 368 for (k = 0; k < extrabytes; k++) { | |
| 369 memcpy(&pel, fdata, 1); | |
| 370 fdata++; | |
| 371 } | |
| 372 } | |
| 373 line -= pixWpl; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 pixEndianByteSwap(pix); | |
| 378 if (height_neg) | |
| 379 pixFlipTB(pix, pix); | |
| 380 | |
| 381 /* ---------------------------------------------- | |
| 382 * We do not use 1 bpp pix with colormaps in leptonica. | |
| 383 * The colormap must be removed in such a way that the pixel | |
| 384 * values are not changed. If the values are only black and | |
| 385 * white, return a 1 bpp image; if gray, return an 8 bpp pix; | |
| 386 * otherwise, return a 32 bpp rgb pix. | |
| 387 * ---------------------------------------------- */ | |
| 388 if (depth == 1 && cmap) { | |
| 389 L_INFO("removing opaque cmap from 1 bpp\n", __func__); | |
| 390 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); | |
| 391 pixDestroy(&pix); | |
| 392 pix = pix1; /* rename */ | |
| 393 } | |
| 394 | |
| 395 return pix; | |
| 396 } | |
| 397 | |
| 398 | |
| 399 /*--------------------------------------------------------------* | |
| 400 * Write bmp * | |
| 401 *--------------------------------------------------------------*/ | |
| 402 /*! | |
| 403 * \brief pixWriteStreamBmp() | |
| 404 * | |
| 405 * \param[in] fp file stream | |
| 406 * \param[in] pix all depths | |
| 407 * \return 0 if OK, 1 on error | |
| 408 */ | |
| 409 l_ok | |
| 410 pixWriteStreamBmp(FILE *fp, | |
| 411 PIX *pix) | |
| 412 { | |
| 413 l_uint8 *data; | |
| 414 size_t size, nbytes; | |
| 415 | |
| 416 if (!fp) | |
| 417 return ERROR_INT("stream not defined", __func__, 1); | |
| 418 if (!pix) | |
| 419 return ERROR_INT("pix not defined", __func__, 1); | |
| 420 | |
| 421 pixWriteMemBmp(&data, &size, pix); | |
| 422 rewind(fp); | |
| 423 nbytes = fwrite(data, 1, size, fp); | |
| 424 free(data); | |
| 425 if (nbytes != size) { | |
| 426 L_ERROR("Truncation: nbytes = %zu, size = %zu\n", | |
| 427 __func__, nbytes, size); | |
| 428 return ERROR_INT("Write error", __func__, 1); | |
| 429 } | |
| 430 return 0; | |
| 431 } | |
| 432 | |
| 433 | |
| 434 /*! | |
| 435 * \brief pixWriteMemBmp() | |
| 436 * | |
| 437 * \param[out] pfdata data of bmp formatted image | |
| 438 * \param[out] pfsize size of returned data | |
| 439 * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp | |
| 440 * \return 0 if OK, 1 on error | |
| 441 * | |
| 442 * <pre> | |
| 443 * Notes: | |
| 444 * (1) 2 bpp bmp files are not valid in the original spec, and are | |
| 445 * written as 8 bpp. | |
| 446 * (2) pix with depth <= 8 bpp are written with a colormap. | |
| 447 * 16 bpp gray and 32 bpp rgb pix are written without a colormap. | |
| 448 * (3) The transparency component in an rgba (spp = 4) pix is written. | |
| 449 * (4) The bmp colormap entries, RGBA_QUAD, are the same as | |
| 450 * the ones used for colormaps in leptonica. This allows | |
| 451 * a simple memcpy for bmp output. | |
| 452 * </pre> | |
| 453 */ | |
| 454 l_ok | |
| 455 pixWriteMemBmp(l_uint8 **pfdata, | |
| 456 size_t *pfsize, | |
| 457 PIX *pixs) | |
| 458 { | |
| 459 l_uint8 pel[4]; | |
| 460 l_uint8 *cta = NULL; /* address of the bmp color table array */ | |
| 461 l_uint8 *fdata, *data, *fmdata; | |
| 462 l_int32 cmaplen; /* number of bytes in the bmp colormap */ | |
| 463 l_int32 ncolors, val, stepsize, w, h, d, fdepth, xres, yres, valid; | |
| 464 l_int32 pixWpl, pixBpl, extrabytes, spp, fBpl, fWpl, i, j, k; | |
| 465 l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ | |
| 466 l_uint32 offbytes, fimagebytes; | |
| 467 l_uint32 *line, *pword; | |
| 468 size_t fsize; | |
| 469 BMP_FH *bmpfh; | |
| 470 BMP_IH bmpih; | |
| 471 PIX *pix; | |
| 472 PIXCMAP *cmap; | |
| 473 RGBA_QUAD *pquad; | |
| 474 | |
| 475 if (pfdata) *pfdata = NULL; | |
| 476 if (pfsize) *pfsize = 0; | |
| 477 if (!pfdata) | |
| 478 return ERROR_INT("&fdata not defined", __func__, 1 ); | |
| 479 if (!pfsize) | |
| 480 return ERROR_INT("&fsize not defined", __func__, 1 ); | |
| 481 if (!pixs) | |
| 482 return ERROR_INT("pixs not defined", __func__, 1); | |
| 483 | |
| 484 /* Verify validity of colormap */ | |
| 485 if ((cmap = pixGetColormap(pixs)) != NULL) { | |
| 486 pixcmapIsValid(cmap, pixs, &valid); | |
| 487 if (!valid) | |
| 488 return ERROR_INT("colormap is not valid", __func__, 1); | |
| 489 } | |
| 490 | |
| 491 pixGetDimensions(pixs, &w, &h, &d); | |
| 492 spp = pixGetSpp(pixs); | |
| 493 if (spp != 1 && spp != 3 && spp != 4) { | |
| 494 L_ERROR("unsupported spp = %d\n", __func__, spp); | |
| 495 return 1; | |
| 496 } | |
| 497 if (d == 2) { | |
| 498 L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", | |
| 499 __func__); | |
| 500 pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); | |
| 501 d = 8; | |
| 502 } else if (d == 24) { | |
| 503 pix = pixConvert24To32(pixs); | |
| 504 d = 32; | |
| 505 } else { | |
| 506 pix = pixCopy(NULL, pixs); | |
| 507 } | |
| 508 | |
| 509 /* Find the bits/pixel to be written to file */ | |
| 510 if (spp == 1) | |
| 511 fdepth = d; | |
| 512 else if (spp == 3) | |
| 513 fdepth = 24; | |
| 514 else /* spp == 4 */ | |
| 515 fdepth = 32; | |
| 516 | |
| 517 /* Resolution is given in pixels/meter */ | |
| 518 xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); | |
| 519 yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); | |
| 520 | |
| 521 pixWpl = pixGetWpl(pix); | |
| 522 pixBpl = 4 * pixWpl; | |
| 523 fWpl = (w * fdepth + 31) / 32; | |
| 524 fBpl = 4 * fWpl; | |
| 525 fimagebytes = h * fBpl; | |
| 526 if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { | |
| 527 pixDestroy(&pix); | |
| 528 return ERROR_INT("image data is too large", __func__, 1); | |
| 529 } | |
| 530 | |
| 531 /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ | |
| 532 heapcm = 0; | |
| 533 if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ | |
| 534 ncolors = 0; | |
| 535 cmaplen = 0; | |
| 536 } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ | |
| 537 ncolors = pixcmapGetCount(cmap); | |
| 538 cmaplen = ncolors * sizeof(RGBA_QUAD); | |
| 539 cta = (l_uint8 *)cmap->array; | |
| 540 } else { /* no existing colormap; d <= 8; make a binary or gray one */ | |
| 541 if (d == 1) { | |
| 542 cmaplen = sizeof(bwmap); | |
| 543 ncolors = 2; | |
| 544 cta = (l_uint8 *)bwmap; | |
| 545 } else { /* d = 2,4,8; use a grayscale output colormap */ | |
| 546 ncolors = 1 << d; | |
| 547 cmaplen = ncolors * sizeof(RGBA_QUAD); | |
| 548 heapcm = 1; | |
| 549 cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); | |
| 550 stepsize = 255 / (ncolors - 1); | |
| 551 for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; | |
| 552 i < ncolors; | |
| 553 i++, val += stepsize, pquad++) { | |
| 554 pquad->blue = pquad->green = pquad->red = val; | |
| 555 pquad->alpha = 255; /* opaque */ | |
| 556 } | |
| 557 } | |
| 558 } | |
| 559 | |
| 560 #if DEBUG | |
| 561 if (pixGetColormap(pix) != NULL) { | |
| 562 l_uint8 *pcmptr; | |
| 563 pcmptr = (l_uint8 *)pixGetColormap(pix)->array; | |
| 564 lept_stderr("Pix colormap[0] = %c%c%c%d\n", | |
| 565 pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); | |
| 566 lept_stderr("Pix colormap[1] = %c%c%c%d\n", | |
| 567 pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); | |
| 568 } | |
| 569 #endif /* DEBUG */ | |
| 570 | |
| 571 offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; | |
| 572 fsize = offbytes + fimagebytes; | |
| 573 fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); | |
| 574 *pfdata = fdata; | |
| 575 *pfsize = fsize; | |
| 576 | |
| 577 /* Write little-endian file header data */ | |
| 578 bmpfh = (BMP_FH *)fdata; | |
| 579 bmpfh->bfType[0] = (l_uint8)(BMP_ID >> 0); | |
| 580 bmpfh->bfType[1] = (l_uint8)(BMP_ID >> 8); | |
| 581 bmpfh->bfSize[0] = (l_uint8)(fsize >> 0); | |
| 582 bmpfh->bfSize[1] = (l_uint8)(fsize >> 8); | |
| 583 bmpfh->bfSize[2] = (l_uint8)(fsize >> 16); | |
| 584 bmpfh->bfSize[3] = (l_uint8)(fsize >> 24); | |
| 585 bmpfh->bfOffBits[0] = (l_uint8)(offbytes >> 0); | |
| 586 bmpfh->bfOffBits[1] = (l_uint8)(offbytes >> 8); | |
| 587 bmpfh->bfOffBits[2] = (l_uint8)(offbytes >> 16); | |
| 588 bmpfh->bfOffBits[3] = (l_uint8)(offbytes >> 24); | |
| 589 | |
| 590 /* Convert to little-endian and write the info header data */ | |
| 591 bmpih.biSize = convertOnBigEnd32(BMP_IHBYTES); | |
| 592 bmpih.biWidth = convertOnBigEnd32(w); | |
| 593 bmpih.biHeight = convertOnBigEnd32(h); | |
| 594 bmpih.biPlanes = convertOnBigEnd16(1); | |
| 595 bmpih.biBitCount = convertOnBigEnd16(fdepth); | |
| 596 bmpih.biCompression = 0; | |
| 597 bmpih.biSizeImage = convertOnBigEnd32(fimagebytes); | |
| 598 bmpih.biXPelsPerMeter = convertOnBigEnd32(xres); | |
| 599 bmpih.biYPelsPerMeter = convertOnBigEnd32(yres); | |
| 600 bmpih.biClrUsed = convertOnBigEnd32(ncolors); | |
| 601 bmpih.biClrImportant = convertOnBigEnd32(ncolors); | |
| 602 memcpy(fdata + BMP_FHBYTES, &bmpih, BMP_IHBYTES); | |
| 603 | |
| 604 /* Copy the colormap data and free the cta if necessary */ | |
| 605 if (ncolors > 0) { | |
| 606 memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); | |
| 607 if (heapcm) LEPT_FREE(cta); | |
| 608 } | |
| 609 | |
| 610 /* When you write a binary image with a colormap | |
| 611 * that sets BLACK to 0, you must invert the data */ | |
| 612 if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { | |
| 613 pixInvert(pix, pix); | |
| 614 } | |
| 615 | |
| 616 /* An endian byte swap is also required */ | |
| 617 pixEndianByteSwap(pix); | |
| 618 | |
| 619 /* Transfer the image data. Image origin for bmp is at lower right. */ | |
| 620 fmdata = fdata + offbytes; | |
| 621 if (fdepth != 24 && fdepth != 32) { /* typ 1 or 8 bpp */ | |
| 622 data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); | |
| 623 for (i = 0; i < h; i++) { | |
| 624 memcpy(fmdata, data, fBpl); | |
| 625 data -= pixBpl; | |
| 626 fmdata += fBpl; | |
| 627 } | |
| 628 } else { /* 32 bpp pix; 24 bpp or 32 bpp file | |
| 629 * See the comments in pixReadStreamBmp() to | |
| 630 * understand the logic behind the pixel ordering below. | |
| 631 * Note that we have again done an endian swap on | |
| 632 * little endian machines before arriving here, so that | |
| 633 * the bytes are ordered on both platforms as: | |
| 634 * Red Green Blue -- | |
| 635 * |-----------|------------|-----------|-----------| | |
| 636 * | |
| 637 * For writing an spp == 4 file, the code below shows where | |
| 638 * the alpha component is written to file in each pixel. | |
| 639 */ | |
| 640 extrabytes = fBpl - spp * w; | |
| 641 line = pixGetData(pix) + pixWpl * (h - 1); | |
| 642 for (i = 0; i < h; i++) { | |
| 643 for (j = 0; j < w; j++) { | |
| 644 pword = line + j; | |
| 645 pel[2] = *((l_uint8 *)pword + COLOR_RED); | |
| 646 pel[1] = *((l_uint8 *)pword + COLOR_GREEN); | |
| 647 pel[0] = *((l_uint8 *)pword + COLOR_BLUE); | |
| 648 if (spp == 4) | |
| 649 pel[3] = *((l_uint8 *)pword + L_ALPHA_CHANNEL); | |
| 650 memcpy(fmdata, &pel, spp); | |
| 651 fmdata += spp; | |
| 652 } | |
| 653 if (extrabytes) { | |
| 654 for (k = 0; k < extrabytes; k++) { | |
| 655 memcpy(fmdata, &pel, 1); | |
| 656 fmdata++; | |
| 657 } | |
| 658 } | |
| 659 line -= pixWpl; | |
| 660 } | |
| 661 } | |
| 662 | |
| 663 pixDestroy(&pix); | |
| 664 return 0; | |
| 665 } | |
| 666 | |
| 667 /* --------------------------------------------*/ | |
| 668 #endif /* USE_BMPIO */ |
