Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/pngio.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 - Copyright (C) 2017 Milner Technologies, Inc. | |
| 4 - | |
| 5 - Redistribution and use in source and binary forms, with or without | |
| 6 - modification, are permitted provided that the following conditions | |
| 7 - are met: | |
| 8 - 1. Redistributions of source code must retain the above copyright | |
| 9 - notice, this list of conditions and the following disclaimer. | |
| 10 - 2. Redistributions in binary form must reproduce the above | |
| 11 - copyright notice, this list of conditions and the following | |
| 12 - disclaimer in the documentation and/or other materials | |
| 13 - provided with the distribution. | |
| 14 - | |
| 15 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 16 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 17 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 18 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY | |
| 19 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 20 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 22 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 23 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 24 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 25 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 *====================================================================*/ | |
| 27 | |
| 28 /*! | |
| 29 * \file pngio.c | |
| 30 * <pre> | |
| 31 * | |
| 32 * Reading png through stream | |
| 33 * PIX *pixReadStreamPng() | |
| 34 * | |
| 35 * Reading png header | |
| 36 * l_int32 readHeaderPng() | |
| 37 * l_int32 freadHeaderPng() | |
| 38 * l_int32 readHeaderMemPng() | |
| 39 * | |
| 40 * Reading png metadata | |
| 41 * l_int32 fgetPngResolution() | |
| 42 * l_int32 isPngInterlaced() | |
| 43 * l_int32 fgetPngColormapInfo() | |
| 44 * | |
| 45 * Writing png through stream | |
| 46 * l_int32 pixWritePng() [ special top level ] | |
| 47 * l_int32 pixWriteStreamPng() | |
| 48 * l_int32 pixSetZlibCompression() | |
| 49 * | |
| 50 * Set flag for special read mode | |
| 51 * void l_pngSetReadStrip16To8() | |
| 52 * | |
| 53 * Low-level memio utility (thanks to T. D. Hintz) | |
| 54 * static void memio_png_write_data() | |
| 55 * static void memio_png_flush() | |
| 56 * static void memio_png_read_data() | |
| 57 * static void memio_free() | |
| 58 * | |
| 59 * Reading png from memory | |
| 60 * PIX *pixReadMemPng() | |
| 61 * | |
| 62 * Writing png to memory | |
| 63 * l_int32 pixWriteMemPng() | |
| 64 * | |
| 65 * Documentation: libpng.txt and example.c | |
| 66 * | |
| 67 * On input (decompression from file), palette color images | |
| 68 * are read into an 8 bpp Pix with a colormap, and 24 bpp | |
| 69 * 3 component color images are read into a 32 bpp Pix with | |
| 70 * rgb samples. On output (compression to file), palette color | |
| 71 * images are written as 8 bpp with the colormap, and 32 bpp | |
| 72 * full color images are written compressed as a 24 bpp, | |
| 73 * 3 component color image. | |
| 74 * | |
| 75 * In the following, we use these abbreviations: | |
| 76 * bps == bit/sample | |
| 77 * spp == samples/pixel | |
| 78 * bpp == bits/pixel of image in Pix (memory) | |
| 79 * where each component is referred to as a "sample". | |
| 80 * | |
| 81 * For reading and writing rgb and rgba images, we read and write | |
| 82 * alpha if it exists (spp == 4) and do not read or write if | |
| 83 * it doesn't (spp == 3). The alpha component can be 'removed' | |
| 84 * simply by setting spp to 3. In leptonica, we make relatively | |
| 85 * little explicit use of the alpha sample. Note that the alpha | |
| 86 * sample in the image is also called "alpha transparency", | |
| 87 * "alpha component" and "alpha layer." | |
| 88 * | |
| 89 * To change the zlib compression level, use pixSetZlibCompression() | |
| 90 * before writing the file. The default is for standard png compression. | |
| 91 * The zlib compression value can be set [0 ... 9], with | |
| 92 * 0 no compression (huge files) | |
| 93 * 1 fastest compression | |
| 94 * -1 default compression (equivalent to 6 in latest version) | |
| 95 * 9 best compression | |
| 96 * Note that if you are using the defined constants in zlib instead | |
| 97 * of the compression integers given above, you must include zlib.h. | |
| 98 * | |
| 99 * There is global for determining the size of retained samples: | |
| 100 * var_PNG_STRIP_16_to_8 | |
| 101 * and a function l_pngSetReadStrip16To8() for setting it. | |
| 102 * The default is TRUE, which causes pixRead() to strip each 16 bit | |
| 103 * sample down to 8 bps: | |
| 104 * ~ For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix | |
| 105 * ~ For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix | |
| 106 * If the variable is set to FALSE, the 16 bit gray samples | |
| 107 * are saved when read; the 16 bit rgb samples return an error. | |
| 108 * Note: results can be non-deterministic if used with | |
| 109 * multi-threaded applications. | |
| 110 * | |
| 111 * Thanks to a memory buffering utility contributed by T. D. Hintz, | |
| 112 * encoding png directly into memory (and decoding from memory) | |
| 113 * is now enabled without the use of any temp files. Unlike with webp, | |
| 114 * it is necessary to preserve the stream interface to enable writing | |
| 115 * pixa to memory. So there are two independent but very similar | |
| 116 * implementations of png reading and writing. | |
| 117 * </pre> | |
| 118 */ | |
| 119 | |
| 120 #ifdef HAVE_CONFIG_H | |
| 121 #include <config_auto.h> | |
| 122 #endif /* HAVE_CONFIG_H */ | |
| 123 | |
| 124 #include <string.h> | |
| 125 #include "allheaders.h" | |
| 126 #include "pix_internal.h" | |
| 127 | |
| 128 /* --------------------------------------------*/ | |
| 129 #if HAVE_LIBPNG /* defined in environ.h */ | |
| 130 /* --------------------------------------------*/ | |
| 131 | |
| 132 #include "png.h" | |
| 133 | |
| 134 #if HAVE_LIBZ | |
| 135 #include "zlib.h" | |
| 136 #else | |
| 137 #define Z_DEFAULT_COMPRESSION (-1) | |
| 138 #endif /* HAVE_LIBZ */ | |
| 139 | |
| 140 /* ------------------ Set default for read option -------------------- */ | |
| 141 /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping. | |
| 142 * If you don't strip, you can't read the gray-alpha spp = 2 images. */ | |
| 143 static l_int32 var_PNG_STRIP_16_TO_8 = 1; | |
| 144 | |
| 145 #ifndef NO_CONSOLE_IO | |
| 146 #define DEBUG_READ 0 | |
| 147 #define DEBUG_WRITE 0 | |
| 148 #endif /* ~NO_CONSOLE_IO */ | |
| 149 | |
| 150 | |
| 151 /*---------------------------------------------------------------------* | |
| 152 * Reading png through stream * | |
| 153 *---------------------------------------------------------------------*/ | |
| 154 /*! | |
| 155 * \brief pixReadStreamPng() | |
| 156 * | |
| 157 * \param[in] fp file stream | |
| 158 * \return pix, or NULL on error | |
| 159 * | |
| 160 * <pre> | |
| 161 * Notes: | |
| 162 * (1) If called from pixReadStream(), the stream is positioned | |
| 163 * at the beginning of the file. | |
| 164 * (2) To do sequential reads of png format images from a stream, | |
| 165 * use pixReadStreamPng() | |
| 166 * (3) All images with alpha is converted to RGBA (spp = 4, with | |
| 167 * equal red, green and blue channels) on reading. | |
| 168 * There are three cases with alpha: | |
| 169 * (a) RGBA: spp = 4. The alpha value is the fourth byte | |
| 170 * (aka "channel, "component") in each 4-byte pixel. | |
| 171 * (b) grayscale-with-alpha (spp = 2), where bpp = 8, and each | |
| 172 * pixel has an associated alpha (transparency) value | |
| 173 * in the second component of the image data. | |
| 174 * (c) colormap (spp = 1) with alpha in the trans palette. | |
| 175 * d = 1, 2, 4, 8. The trans palette in writing is derived | |
| 176 * from the alpha components in the cmap. Transparency is | |
| 177 * often associated with a white background. | |
| 178 * (4) We use the high level png interface, where the transforms are set | |
| 179 * up in advance and the header and image are read with a single | |
| 180 * call. The more complicated interface, where the header is | |
| 181 * read first and the buffers for the raster image are user- | |
| 182 * allocated before reading the image, works for single images, | |
| 183 * but I could not get it to work properly for the successive | |
| 184 * png reads that are required by pixaReadStream(). | |
| 185 * </pre> | |
| 186 */ | |
| 187 PIX * | |
| 188 pixReadStreamPng(FILE *fp) | |
| 189 { | |
| 190 l_uint8 byte; | |
| 191 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid; | |
| 192 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS; | |
| 193 l_uint32 png_transforms; | |
| 194 l_uint32 *data, *line, *ppixel; | |
| 195 int num_palette, num_text, num_trans; | |
| 196 png_byte bit_depth, color_type, channels; | |
| 197 png_uint_32 w, h, rowbytes, xres, yres; | |
| 198 png_bytep rowptr, trans; | |
| 199 png_bytep *row_pointers; | |
| 200 png_structp png_ptr; | |
| 201 png_infop info_ptr, end_info; | |
| 202 png_colorp palette; | |
| 203 png_textp text_ptr; /* ptr to text_chunk */ | |
| 204 PIX *pix, *pix1; | |
| 205 PIXCMAP *cmap; | |
| 206 | |
| 207 if (!fp) | |
| 208 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); | |
| 209 pix = NULL; | |
| 210 | |
| 211 /* Allocate the 3 data structures */ | |
| 212 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, | |
| 213 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 214 return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL); | |
| 215 | |
| 216 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 217 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
| 218 return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL); | |
| 219 } | |
| 220 | |
| 221 if ((end_info = png_create_info_struct(png_ptr)) == NULL) { | |
| 222 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
| 223 return (PIX *)ERROR_PTR("end_info not made", __func__, NULL); | |
| 224 } | |
| 225 | |
| 226 /* Set up png setjmp error handling */ | |
| 227 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 228 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 229 return (PIX *)ERROR_PTR("internal png error", __func__, NULL); | |
| 230 } | |
| 231 | |
| 232 png_init_io(png_ptr, fp); | |
| 233 | |
| 234 /* ---------------------------------------------------------- * | |
| 235 * - Set the transforms flags. Whatever happens here, | |
| 236 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. | |
| 237 * - Do not use PNG_TRANSFORM_EXPAND, which would | |
| 238 * expand all images with bpp < 8 to 8 bpp. | |
| 239 * - Strip 16 --> 8 if reading 16-bit gray+alpha | |
| 240 * ---------------------------------------------------------- */ | |
| 241 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ | |
| 242 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ | |
| 243 png_transforms = PNG_TRANSFORM_STRIP_16; | |
| 244 } else { | |
| 245 png_transforms = PNG_TRANSFORM_IDENTITY; | |
| 246 L_INFO("not stripping 16 --> 8 in png reading\n", __func__); | |
| 247 } | |
| 248 | |
| 249 /* Read it */ | |
| 250 png_read_png(png_ptr, info_ptr, png_transforms, NULL); | |
| 251 | |
| 252 row_pointers = png_get_rows(png_ptr, info_ptr); | |
| 253 w = png_get_image_width(png_ptr, info_ptr); | |
| 254 h = png_get_image_height(png_ptr, info_ptr); | |
| 255 bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
| 256 rowbytes = png_get_rowbytes(png_ptr, info_ptr); | |
| 257 color_type = png_get_color_type(png_ptr, info_ptr); | |
| 258 channels = png_get_channels(png_ptr, info_ptr); | |
| 259 spp = channels; | |
| 260 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; | |
| 261 | |
| 262 if (spp == 1) { | |
| 263 d = bit_depth; | |
| 264 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ | |
| 265 d = 4 * bit_depth; | |
| 266 } | |
| 267 | |
| 268 /* Remove if/when this is implemented for all bit_depths */ | |
| 269 if (spp != 1 && bit_depth != 8) { | |
| 270 L_ERROR("spp = %d and bps = %d != 8\n" | |
| 271 "turn on 16 --> 8 stripping\n", __func__, spp, bit_depth); | |
| 272 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 273 return (PIX *)ERROR_PTR("not implemented for this image", | |
| 274 __func__, NULL); | |
| 275 } | |
| 276 | |
| 277 cmap = NULL; | |
| 278 if (color_type == PNG_COLOR_TYPE_PALETTE || | |
| 279 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ | |
| 280 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); | |
| 281 cmap = pixcmapCreate(d); /* spp == 1 */ | |
| 282 for (cindex = 0; cindex < num_palette; cindex++) { | |
| 283 rval = palette[cindex].red; | |
| 284 gval = palette[cindex].green; | |
| 285 bval = palette[cindex].blue; | |
| 286 pixcmapAddColor(cmap, rval, gval, bval); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 if ((pix = pixCreate(w, h, d)) == NULL) { | |
| 291 pixcmapDestroy(&cmap); | |
| 292 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 293 return (PIX *)ERROR_PTR("pix not made", __func__, NULL); | |
| 294 } | |
| 295 pixSetInputFormat(pix, IFF_PNG); | |
| 296 wpl = pixGetWpl(pix); | |
| 297 data = pixGetData(pix); | |
| 298 pixSetSpp(pix, spp); | |
| 299 if (pixSetColormap(pix, cmap)) { | |
| 300 pixDestroy(&pix); | |
| 301 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); | |
| 302 } | |
| 303 | |
| 304 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ | |
| 305 for (i = 0; i < h; i++) { | |
| 306 line = data + i * wpl; | |
| 307 rowptr = row_pointers[i]; | |
| 308 for (j = 0; j < rowbytes; j++) { | |
| 309 SET_DATA_BYTE(line, j, rowptr[j]); | |
| 310 } | |
| 311 } | |
| 312 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ | |
| 313 L_INFO("converting (gray + alpha) ==> RGBA\n", __func__); | |
| 314 for (i = 0; i < h; i++) { | |
| 315 ppixel = data + i * wpl; | |
| 316 rowptr = row_pointers[i]; | |
| 317 for (j = k = 0; j < w; j++) { | |
| 318 /* Copy gray value into r, g and b */ | |
| 319 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); | |
| 320 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); | |
| 321 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); | |
| 322 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); | |
| 323 ppixel++; | |
| 324 } | |
| 325 } | |
| 326 pixSetSpp(pix, 4); /* we do not support 2 spp pix */ | |
| 327 } else if (spp == 3 || spp == 4) { | |
| 328 for (i = 0; i < h; i++) { | |
| 329 ppixel = data + i * wpl; | |
| 330 rowptr = row_pointers[i]; | |
| 331 for (j = k = 0; j < w; j++) { | |
| 332 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); | |
| 333 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); | |
| 334 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); | |
| 335 if (spp == 3) /* set to opaque; some readers are buggy */ | |
| 336 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255); | |
| 337 else /* spp == 4 */ | |
| 338 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); | |
| 339 ppixel++; | |
| 340 } | |
| 341 } | |
| 342 } | |
| 343 | |
| 344 /* Special spp == 1 cases with transparency: | |
| 345 * (1) 8 bpp without colormap; assume full transparency | |
| 346 * (2) 1 bpp with colormap + trans array (for alpha) | |
| 347 * (3) 2 bpp with colormap + trans array (for alpha) | |
| 348 * (4) 4 bpp with colormap + trans array (for alpha) | |
| 349 * (5) 8 bpp with colormap + trans array (for alpha) | |
| 350 * These all require converting to RGBA */ | |
| 351 if (spp == 1 && tRNS) { | |
| 352 if (!cmap) { | |
| 353 /* Case 1: make fully transparent RGBA image */ | |
| 354 L_INFO("transparency, 1 spp, no colormap, no transparency array: " | |
| 355 "convention is fully transparent image\n", __func__); | |
| 356 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__); | |
| 357 pixDestroy(&pix); | |
| 358 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ | |
| 359 pixSetSpp(pix, 4); | |
| 360 } else { | |
| 361 L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__); | |
| 362 | |
| 363 /* Grab the transparency array */ | |
| 364 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); | |
| 365 if (!trans) { /* invalid png file */ | |
| 366 pixDestroy(&pix); | |
| 367 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 368 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", | |
| 369 __func__, NULL); | |
| 370 } | |
| 371 | |
| 372 /* Save the cmap and destroy the pix */ | |
| 373 cmap = pixcmapCopy(pixGetColormap(pix)); | |
| 374 ncolors = pixcmapGetCount(cmap); | |
| 375 pixDestroy(&pix); | |
| 376 | |
| 377 /* Start over with 32 bit RGBA */ | |
| 378 pix = pixCreate(w, h, 32); | |
| 379 wpl = pixGetWpl(pix); | |
| 380 data = pixGetData(pix); | |
| 381 pixSetSpp(pix, 4); | |
| 382 | |
| 383 #if DEBUG_READ | |
| 384 lept_stderr("ncolors = %d, num_trans = %d\n", | |
| 385 ncolors, num_trans); | |
| 386 for (i = 0; i < ncolors; i++) { | |
| 387 pixcmapGetColor(cmap, i, &rval, &gval, &bval); | |
| 388 if (i < num_trans) { | |
| 389 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n", | |
| 390 rval, gval, bval, trans[i]); | |
| 391 } else { | |
| 392 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n", | |
| 393 rval, gval, bval); | |
| 394 } | |
| 395 } | |
| 396 #endif /* DEBUG_READ */ | |
| 397 | |
| 398 /* Extract the data and convert to RGBA */ | |
| 399 if (d == 1) { | |
| 400 /* Case 2: 1 bpp with transparency (usually) behind white */ | |
| 401 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 402 if (num_trans == 1) | |
| 403 L_INFO("num_trans = 1; second color opaque by default\n", | |
| 404 __func__); | |
| 405 for (i = 0; i < h; i++) { | |
| 406 ppixel = data + i * wpl; | |
| 407 rowptr = row_pointers[i]; | |
| 408 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 409 byte = rowptr[j]; | |
| 410 for (k = 0; k < 8 && index < w; k++, index++) { | |
| 411 bitval = (byte >> (7 - k)) & 1; | |
| 412 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); | |
| 413 composeRGBPixel(rval, gval, bval, ppixel); | |
| 414 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 415 bitval < num_trans ? trans[bitval] : 255); | |
| 416 ppixel++; | |
| 417 } | |
| 418 } | |
| 419 } | |
| 420 } else if (d == 2) { | |
| 421 /* Case 3: 2 bpp with cmap and associated transparency */ | |
| 422 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 423 for (i = 0; i < h; i++) { | |
| 424 ppixel = data + i * wpl; | |
| 425 rowptr = row_pointers[i]; | |
| 426 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 427 byte = rowptr[j]; | |
| 428 for (k = 0; k < 4 && index < w; k++, index++) { | |
| 429 bival = (byte >> 2 * (3 - k)) & 3; | |
| 430 pixcmapGetColor(cmap, bival, &rval, &gval, &bval); | |
| 431 composeRGBPixel(rval, gval, bval, ppixel); | |
| 432 /* Assume missing entries to be 255 (opaque) | |
| 433 * according to the spec: | |
| 434 * http://www.w3.org/TR/PNG/#11tRNS */ | |
| 435 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 436 bival < num_trans ? trans[bival] : 255); | |
| 437 ppixel++; | |
| 438 } | |
| 439 } | |
| 440 } | |
| 441 } else if (d == 4) { | |
| 442 /* Case 4: 4 bpp with cmap and associated transparency */ | |
| 443 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 444 for (i = 0; i < h; i++) { | |
| 445 ppixel = data + i * wpl; | |
| 446 rowptr = row_pointers[i]; | |
| 447 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 448 byte = rowptr[j]; | |
| 449 for (k = 0; k < 2 && index < w; k++, index++) { | |
| 450 quadval = (byte >> 4 * (1 - k)) & 0xf; | |
| 451 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval); | |
| 452 composeRGBPixel(rval, gval, bval, ppixel); | |
| 453 /* Assume missing entries to be 255 (opaque) */ | |
| 454 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 455 quadval < num_trans ? trans[quadval] : 255); | |
| 456 ppixel++; | |
| 457 } | |
| 458 } | |
| 459 } | |
| 460 } else if (d == 8) { | |
| 461 /* Case 5: 8 bpp with cmap and associated transparency */ | |
| 462 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 463 for (i = 0; i < h; i++) { | |
| 464 ppixel = data + i * wpl; | |
| 465 rowptr = row_pointers[i]; | |
| 466 for (j = 0; j < w; j++) { | |
| 467 index = rowptr[j]; | |
| 468 pixcmapGetColor(cmap, index, &rval, &gval, &bval); | |
| 469 composeRGBPixel(rval, gval, bval, ppixel); | |
| 470 /* Assume missing entries to be 255 (opaque) */ | |
| 471 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 472 index < num_trans ? trans[index] : 255); | |
| 473 ppixel++; | |
| 474 } | |
| 475 } | |
| 476 } else { | |
| 477 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", | |
| 478 __func__, d); | |
| 479 } | |
| 480 pixcmapDestroy(&cmap); | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 #if DEBUG_READ | |
| 485 if (cmap) { | |
| 486 for (i = 0; i < 16; i++) { | |
| 487 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); | |
| 488 } | |
| 489 } | |
| 490 #endif /* DEBUG_READ */ | |
| 491 | |
| 492 /* Final adjustments for bpp = 1. | |
| 493 * + If there is no colormap, the image must be inverted because | |
| 494 * png stores black pixels as 0. | |
| 495 * + We have already handled the case of cmapped, 1 bpp pix | |
| 496 * with transparency, where the output pix is 32 bpp RGBA. | |
| 497 * If there is no transparency but the pix has a colormap, | |
| 498 * we remove the colormap, because functions operating on | |
| 499 * 1 bpp images in leptonica assume no colormap. | |
| 500 * + The colormap must be removed in such a way that the pixel | |
| 501 * values are not changed. If the values are only black and | |
| 502 * white, we return a 1 bpp image; if gray, return an 8 bpp pix; | |
| 503 * otherwise, return a 32 bpp rgb pix. | |
| 504 * | |
| 505 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag | |
| 506 * to do the inversion, because that flag (since version 1.0.9) | |
| 507 * inverts 8 bpp grayscale as well, which we don't want to do. | |
| 508 * (It also doesn't work if there is a colormap.) | |
| 509 * | |
| 510 * Note that if the input png is a 1-bit with colormap and | |
| 511 * transparency, it has already been rendered as a 32 bpp, | |
| 512 * spp = 4 rgba pix. | |
| 513 */ | |
| 514 if (pixGetDepth(pix) == 1) { | |
| 515 if (!cmap) { | |
| 516 pixInvert(pix, pix); | |
| 517 } else { | |
| 518 L_INFO("removing opaque cmap from 1 bpp\n", __func__); | |
| 519 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); | |
| 520 pixDestroy(&pix); | |
| 521 pix = pix1; | |
| 522 } | |
| 523 } | |
| 524 | |
| 525 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); | |
| 526 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); | |
| 527 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ | |
| 528 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ | |
| 529 | |
| 530 /* Get the text if there is any */ | |
| 531 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); | |
| 532 if (num_text && text_ptr) | |
| 533 pixSetText(pix, text_ptr->text); | |
| 534 | |
| 535 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 536 | |
| 537 /* Final validity check on the colormap */ | |
| 538 if ((cmap = pixGetColormap(pix)) != NULL) { | |
| 539 pixcmapIsValid(cmap, pix, &valid); | |
| 540 if (!valid) { | |
| 541 pixDestroy(&pix); | |
| 542 return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL); | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 pixSetPadBits(pix, 0); | |
| 547 return pix; | |
| 548 } | |
| 549 | |
| 550 | |
| 551 /*---------------------------------------------------------------------* | |
| 552 * Reading png header * | |
| 553 *---------------------------------------------------------------------*/ | |
| 554 /*! | |
| 555 * \brief readHeaderPng() | |
| 556 * | |
| 557 * \param[in] filename | |
| 558 * \param[out] pw [optional] | |
| 559 * \param[out] ph [optional] | |
| 560 * \param[out] pbps [optional] bits/sample | |
| 561 * \param[out] pspp [optional] samples/pixel | |
| 562 * \param[out] piscmap [optional] | |
| 563 * \return 0 if OK, 1 on error | |
| 564 * | |
| 565 * <pre> | |
| 566 * Notes: | |
| 567 * (1) If there is a colormap, iscmap is returned as 1; else 0. | |
| 568 * (2) For gray+alpha, although the png records bps = 16, we | |
| 569 * consider this as two 8 bpp samples (gray and alpha). | |
| 570 * When a gray+alpha is read, it is converted to 32 bpp RGBA. | |
| 571 * </pre> | |
| 572 */ | |
| 573 l_ok | |
| 574 readHeaderPng(const char *filename, | |
| 575 l_int32 *pw, | |
| 576 l_int32 *ph, | |
| 577 l_int32 *pbps, | |
| 578 l_int32 *pspp, | |
| 579 l_int32 *piscmap) | |
| 580 { | |
| 581 l_int32 ret; | |
| 582 FILE *fp; | |
| 583 | |
| 584 if (pw) *pw = 0; | |
| 585 if (ph) *ph = 0; | |
| 586 if (pbps) *pbps = 0; | |
| 587 if (pspp) *pspp = 0; | |
| 588 if (piscmap) *piscmap = 0; | |
| 589 if (!filename) | |
| 590 return ERROR_INT("filename not defined", __func__, 1); | |
| 591 if ((fp = fopenReadStream(filename)) == NULL) | |
| 592 return ERROR_INT_1("image file not found", filename, __func__, 1); | |
| 593 ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap); | |
| 594 fclose(fp); | |
| 595 return ret; | |
| 596 } | |
| 597 | |
| 598 | |
| 599 /*! | |
| 600 * \brief freadHeaderPng() | |
| 601 * | |
| 602 * \param[in] fp file stream | |
| 603 * \param[out] pw [optional] | |
| 604 * \param[out] ph [optional] | |
| 605 * \param[out] pbps [optional] bits/sample | |
| 606 * \param[out] pspp [optional] samples/pixel | |
| 607 * \param[out] piscmap [optional] | |
| 608 * \return 0 if OK, 1 on error | |
| 609 * | |
| 610 * <pre> | |
| 611 * Notes: | |
| 612 * (1) See readHeaderPng(). We only need the first 40 bytes in the file. | |
| 613 * </pre> | |
| 614 */ | |
| 615 l_ok | |
| 616 freadHeaderPng(FILE *fp, | |
| 617 l_int32 *pw, | |
| 618 l_int32 *ph, | |
| 619 l_int32 *pbps, | |
| 620 l_int32 *pspp, | |
| 621 l_int32 *piscmap) | |
| 622 { | |
| 623 l_int32 nbytes, ret; | |
| 624 l_uint8 data[40]; | |
| 625 | |
| 626 if (pw) *pw = 0; | |
| 627 if (ph) *ph = 0; | |
| 628 if (pbps) *pbps = 0; | |
| 629 if (pspp) *pspp = 0; | |
| 630 if (piscmap) *piscmap = 0; | |
| 631 if (!fp) | |
| 632 return ERROR_INT("stream not defined", __func__, 1); | |
| 633 | |
| 634 nbytes = fnbytesInFile(fp); | |
| 635 if (nbytes < 40) | |
| 636 return ERROR_INT("file too small to be png", __func__, 1); | |
| 637 if (fread(data, 1, 40, fp) != 40) | |
| 638 return ERROR_INT("error reading data", __func__, 1); | |
| 639 ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap); | |
| 640 return ret; | |
| 641 } | |
| 642 | |
| 643 | |
| 644 /*! | |
| 645 * \brief readHeaderMemPng() | |
| 646 * | |
| 647 * \param[in] data | |
| 648 * \param[in] size 40 bytes is sufficient | |
| 649 * \param[out] pw [optional] | |
| 650 * \param[out] ph [optional] | |
| 651 * \param[out] pbps [optional] bits/sample | |
| 652 * \param[out] pspp [optional] samples/pixel | |
| 653 * \param[out] piscmap [optional] input NULL to ignore | |
| 654 * \return 0 if OK, 1 on error | |
| 655 * | |
| 656 * <pre> | |
| 657 * Notes: | |
| 658 * (1) See readHeaderPng(). | |
| 659 * (2) png colortypes (see png.h: PNG_COLOR_TYPE_*): | |
| 660 * 0: gray; fully transparent (with tRNS) (1 spp) | |
| 661 * 2: RGB (3 spp) | |
| 662 * 3: colormap; colormap+alpha (with tRNS) (1 spp) | |
| 663 * 4: gray + alpha (2 spp) | |
| 664 * 6: RGBA (4 spp) | |
| 665 * Note: | |
| 666 * 0 and 3 have the alpha information in a tRNS chunk | |
| 667 * 4 and 6 have separate alpha samples with each pixel. | |
| 668 * </pre> | |
| 669 */ | |
| 670 l_ok | |
| 671 readHeaderMemPng(const l_uint8 *data, | |
| 672 size_t size, | |
| 673 l_int32 *pw, | |
| 674 l_int32 *ph, | |
| 675 l_int32 *pbps, | |
| 676 l_int32 *pspp, | |
| 677 l_int32 *piscmap) | |
| 678 { | |
| 679 l_uint16 twobytes; | |
| 680 l_uint16 *pshort; | |
| 681 l_int32 colortype, w, h, bps, spp; | |
| 682 l_uint32 *pword; | |
| 683 | |
| 684 if (pw) *pw = 0; | |
| 685 if (ph) *ph = 0; | |
| 686 if (pbps) *pbps = 0; | |
| 687 if (pspp) *pspp = 0; | |
| 688 if (piscmap) *piscmap = 0; | |
| 689 if (!data) | |
| 690 return ERROR_INT("data not defined", __func__, 1); | |
| 691 if (size < 40) | |
| 692 return ERROR_INT("size < 40", __func__, 1); | |
| 693 | |
| 694 /* Check password */ | |
| 695 if (data[0] != 137 || data[1] != 80 || data[2] != 78 || | |
| 696 data[3] != 71 || data[4] != 13 || data[5] != 10 || | |
| 697 data[6] != 26 || data[7] != 10) | |
| 698 return ERROR_INT("not a valid png file", __func__, 1); | |
| 699 | |
| 700 pword = (l_uint32 *)data; | |
| 701 pshort = (l_uint16 *)data; | |
| 702 w = convertOnLittleEnd32(pword[4]); | |
| 703 h = convertOnLittleEnd32(pword[5]); | |
| 704 if (w < 1 || h < 1) | |
| 705 return ERROR_INT("invalid w or h", __func__, 1); | |
| 706 twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */ | |
| 707 /* and the color type */ | |
| 708 colortype = twobytes & 0xff; /* color type */ | |
| 709 bps = twobytes >> 8; /* bits/sample */ | |
| 710 | |
| 711 /* Special case with alpha that is extracted as RGBA. | |
| 712 * Note that the cmap+alpha is also extracted as RGBA, | |
| 713 * but only if the tRNS chunk exists, which we can't tell | |
| 714 * by this simple parser.*/ | |
| 715 if (colortype == 4) | |
| 716 L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", __func__); | |
| 717 | |
| 718 if (colortype == 2) { /* RGB */ | |
| 719 spp = 3; | |
| 720 } else if (colortype == 6) { /* RGBA */ | |
| 721 spp = 4; | |
| 722 } else if (colortype == 4) { /* gray + alpha */ | |
| 723 spp = 2; | |
| 724 bps = 8; /* both the gray and alpha are 8-bit samples */ | |
| 725 } else { /* gray (0) or cmap (3) or cmap+alpha (3) */ | |
| 726 spp = 1; | |
| 727 } | |
| 728 if (bps < 1 || bps > 16) { | |
| 729 L_ERROR("invalid bps = %d\n", __func__, bps); | |
| 730 return 1; | |
| 731 } | |
| 732 if (pw) *pw = w; | |
| 733 if (ph) *ph = h; | |
| 734 if (pbps) *pbps = bps; | |
| 735 if (pspp) *pspp = spp; | |
| 736 if (piscmap) { | |
| 737 if (colortype & 1) /* palette */ | |
| 738 *piscmap = 1; | |
| 739 else | |
| 740 *piscmap = 0; | |
| 741 } | |
| 742 | |
| 743 return 0; | |
| 744 } | |
| 745 | |
| 746 | |
| 747 /*---------------------------------------------------------------------* | |
| 748 * Reading png metadata * | |
| 749 *---------------------------------------------------------------------*/ | |
| 750 /* | |
| 751 * fgetPngResolution() | |
| 752 * | |
| 753 * Input: fp (file stream opened for read) | |
| 754 * &xres, &yres (<return> resolution in ppi) | |
| 755 * Return: 0 if OK; 1 on error | |
| 756 * | |
| 757 * Notes: | |
| 758 * (1) If neither resolution field is set, this is not an error; | |
| 759 * the returned resolution values are 0 (designating 'unknown'). | |
| 760 * (2) Side-effect: this rewinds the stream. | |
| 761 */ | |
| 762 l_int32 | |
| 763 fgetPngResolution(FILE *fp, | |
| 764 l_int32 *pxres, | |
| 765 l_int32 *pyres) | |
| 766 { | |
| 767 png_uint_32 xres, yres; | |
| 768 png_structp png_ptr; | |
| 769 png_infop info_ptr; | |
| 770 | |
| 771 if (pxres) *pxres = 0; | |
| 772 if (pyres) *pyres = 0; | |
| 773 if (!fp) | |
| 774 return ERROR_INT("stream not opened", __func__, 1); | |
| 775 if (!pxres || !pyres) | |
| 776 return ERROR_INT("&xres and &yres not both defined", __func__, 1); | |
| 777 | |
| 778 /* Make the two required structs */ | |
| 779 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, | |
| 780 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 781 return ERROR_INT("png_ptr not made", __func__, 1); | |
| 782 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 783 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
| 784 return ERROR_INT("info_ptr not made", __func__, 1); | |
| 785 } | |
| 786 | |
| 787 /* Set up png setjmp error handling. | |
| 788 * Without this, an error calls exit. */ | |
| 789 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 790 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
| 791 return ERROR_INT("internal png error", __func__, 1); | |
| 792 } | |
| 793 | |
| 794 /* Read the metadata */ | |
| 795 rewind(fp); | |
| 796 png_init_io(png_ptr, fp); | |
| 797 png_read_info(png_ptr, info_ptr); | |
| 798 | |
| 799 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); | |
| 800 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); | |
| 801 *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */ | |
| 802 *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5); | |
| 803 | |
| 804 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | |
| 805 rewind(fp); | |
| 806 return 0; | |
| 807 } | |
| 808 | |
| 809 | |
| 810 /*! | |
| 811 * \brief isPngInterlaced() | |
| 812 * | |
| 813 * \param[in] filename | |
| 814 * \param[out] pinterlaced 1 if interlaced png; 0 otherwise | |
| 815 * \return 0 if OK, 1 on error | |
| 816 */ | |
| 817 l_ok | |
| 818 isPngInterlaced(const char *filename, | |
| 819 l_int32 *pinterlaced) | |
| 820 { | |
| 821 l_uint8 buf[32]; | |
| 822 FILE *fp; | |
| 823 | |
| 824 if (!pinterlaced) | |
| 825 return ERROR_INT("&interlaced not defined", __func__, 1); | |
| 826 *pinterlaced = 0; | |
| 827 if (!filename) | |
| 828 return ERROR_INT("filename not defined", __func__, 1); | |
| 829 | |
| 830 if ((fp = fopenReadStream(filename)) == NULL) | |
| 831 return ERROR_INT_1("stream not opened", filename, __func__, 1); | |
| 832 if (fread(buf, 1, 32, fp) != 32) { | |
| 833 fclose(fp); | |
| 834 return ERROR_INT_1("data not read", filename, __func__, 1); | |
| 835 } | |
| 836 fclose(fp); | |
| 837 | |
| 838 *pinterlaced = (buf[28] == 0) ? 0 : 1; | |
| 839 return 0; | |
| 840 } | |
| 841 | |
| 842 | |
| 843 /* | |
| 844 * \brief fgetPngColormapInfo() | |
| 845 * | |
| 846 * \param[in] fp file stream opened for read | |
| 847 * \param[out] pcmap optional; use NULL to skip | |
| 848 * \param[out] ptransparency optional; 1 if colormapped with | |
| 849 * transparency, 0 otherwise; use NULL to skip | |
| 850 * \return 0 if OK, 1 on error | |
| 851 * | |
| 852 * Notes: | |
| 853 * (1) The transparency information in a png is in the tRNA array, | |
| 854 * which is separate from the colormap. If this array exists | |
| 855 * and if any element is less than 255, there exists some | |
| 856 * transparency. | |
| 857 * (2) Side-effect: this rewinds the stream. | |
| 858 */ | |
| 859 l_ok | |
| 860 fgetPngColormapInfo(FILE *fp, | |
| 861 PIXCMAP **pcmap, | |
| 862 l_int32 *ptransparency) | |
| 863 { | |
| 864 l_int32 i, cindex, rval, gval, bval, num_palette, num_trans; | |
| 865 png_byte bit_depth, color_type; | |
| 866 png_bytep trans; | |
| 867 png_colorp palette; | |
| 868 png_structp png_ptr; | |
| 869 png_infop info_ptr; | |
| 870 | |
| 871 if (pcmap) *pcmap = NULL; | |
| 872 if (ptransparency) *ptransparency = 0; | |
| 873 if (!pcmap && !ptransparency) | |
| 874 return ERROR_INT("no output defined", __func__, 1); | |
| 875 if (!fp) | |
| 876 return ERROR_INT("stream not opened", __func__, 1); | |
| 877 | |
| 878 /* Make the two required structs */ | |
| 879 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, | |
| 880 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 881 return ERROR_INT("png_ptr not made", __func__, 1); | |
| 882 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 883 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
| 884 return ERROR_INT("info_ptr not made", __func__, 1); | |
| 885 } | |
| 886 | |
| 887 /* Set up png setjmp error handling. | |
| 888 * Without this, an error calls exit. */ | |
| 889 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 890 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | |
| 891 if (pcmap && *pcmap) pixcmapDestroy(pcmap); | |
| 892 return ERROR_INT("internal png error", __func__, 1); | |
| 893 } | |
| 894 | |
| 895 /* Read the metadata and check if there is a colormap */ | |
| 896 rewind(fp); | |
| 897 png_init_io(png_ptr, fp); | |
| 898 png_read_info(png_ptr, info_ptr); | |
| 899 color_type = png_get_color_type(png_ptr, info_ptr); | |
| 900 if (color_type != PNG_COLOR_TYPE_PALETTE && | |
| 901 color_type != PNG_COLOR_MASK_PALETTE) { | |
| 902 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | |
| 903 return 0; | |
| 904 } | |
| 905 | |
| 906 /* Optionally, read the colormap */ | |
| 907 if (pcmap) { | |
| 908 bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
| 909 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); | |
| 910 *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */ | |
| 911 for (cindex = 0; cindex < num_palette; cindex++) { | |
| 912 rval = palette[cindex].red; | |
| 913 gval = palette[cindex].green; | |
| 914 bval = palette[cindex].blue; | |
| 915 pixcmapAddColor(*pcmap, rval, gval, bval); | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 /* Optionally, look for transparency. Note that the colormap | |
| 920 * has been initialized to fully opaque. */ | |
| 921 if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
| 922 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); | |
| 923 if (trans) { | |
| 924 for (i = 0; i < num_trans; i++) { | |
| 925 if (trans[i] < 255) { /* not fully opaque */ | |
| 926 *ptransparency = 1; | |
| 927 if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]); | |
| 928 } | |
| 929 } | |
| 930 } else { | |
| 931 L_ERROR("transparency array not returned\n", __func__); | |
| 932 } | |
| 933 } | |
| 934 | |
| 935 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | |
| 936 rewind(fp); | |
| 937 return 0; | |
| 938 } | |
| 939 | |
| 940 | |
| 941 /*---------------------------------------------------------------------* | |
| 942 * Writing png through stream * | |
| 943 *---------------------------------------------------------------------*/ | |
| 944 /*! | |
| 945 * \brief pixWritePng() | |
| 946 * | |
| 947 * \param[in] filename | |
| 948 * \param[in] pix | |
| 949 * \param[in] gamma | |
| 950 * \return 0 if OK; 1 on error | |
| 951 * | |
| 952 * <pre> | |
| 953 * Notes: | |
| 954 * (1) Special version for writing png with a specified gamma. | |
| 955 * When using pixWrite(), no field is given for gamma. | |
| 956 * </pre> | |
| 957 */ | |
| 958 l_ok | |
| 959 pixWritePng(const char *filename, | |
| 960 PIX *pix, | |
| 961 l_float32 gamma) | |
| 962 { | |
| 963 FILE *fp; | |
| 964 | |
| 965 if (!pix) | |
| 966 return ERROR_INT("pix not defined", __func__, 1); | |
| 967 if (!filename) | |
| 968 return ERROR_INT("filename not defined", __func__, 1); | |
| 969 | |
| 970 if ((fp = fopenWriteStream(filename, "wb+")) == NULL) | |
| 971 return ERROR_INT_1("stream not opened", filename, __func__, 1); | |
| 972 | |
| 973 if (pixWriteStreamPng(fp, pix, gamma)) { | |
| 974 fclose(fp); | |
| 975 return ERROR_INT_1("pix not written to stream", filename, __func__, 1); | |
| 976 } | |
| 977 | |
| 978 fclose(fp); | |
| 979 return 0; | |
| 980 } | |
| 981 | |
| 982 | |
| 983 /*! | |
| 984 * \brief pixWriteStreamPng() | |
| 985 * | |
| 986 * \param[in] fp file stream | |
| 987 * \param[in] pix | |
| 988 * \param[in] gamma use 0.0 if gamma is not defined | |
| 989 * \return 0 if OK; 1 on error | |
| 990 * | |
| 991 * <pre> | |
| 992 * Notes: | |
| 993 * (1) If called from pixWriteStream(), the stream is positioned | |
| 994 * at the beginning of the file. | |
| 995 * (2) To do sequential writes of png format images to a stream, | |
| 996 * use pixWriteStreamPng() directly. | |
| 997 * (3) gamma is an optional png chunk. If no gamma value is to be | |
| 998 * placed into the file, use gamma = 0.0. Otherwise, if | |
| 999 * gamma > 0.0, its value is written into the header. | |
| 1000 * (4) The use of gamma in png is highly problematic. For an illuminating | |
| 1001 * discussion, see: http://hsivonen.iki.fi/png-gamma/ | |
| 1002 * (5) What is the effect/meaning of gamma in the png file? This | |
| 1003 * gamma, which we can call the 'source' gamma, is the | |
| 1004 * inverse of the gamma that was used in enhance.c to brighten | |
| 1005 * or darken images. The 'source' gamma is supposed to indicate | |
| 1006 * the intensity mapping that was done at the time the | |
| 1007 * image was captured. Display programs typically apply a | |
| 1008 * 'display' gamma of 2.2 to the output, which is intended | |
| 1009 * to linearize the intensity based on the response of | |
| 1010 * thermionic tubes (CRTs). Flat panel LCDs have typically | |
| 1011 * been designed to give a similar response as CRTs (call it | |
| 1012 * "backward compatibility"). The 'display' gamma is | |
| 1013 * in some sense the inverse of the 'source' gamma. | |
| 1014 * jpeg encoders attached to scanners and cameras will lighten | |
| 1015 * the pixels, applying a gamma corresponding to approximately | |
| 1016 * a square-root relation of output vs input: | |
| 1017 * output = input^(gamma) | |
| 1018 * where gamma is often set near 0.4545 (1/gamma is 2.2). | |
| 1019 * This is stored in the image file. Then if the display | |
| 1020 * program reads the gamma, it will apply a display gamma, | |
| 1021 * typically about 2.2; the product is 1.0, and the | |
| 1022 * display program produces a linear output. This works because | |
| 1023 * the dark colors were appropriately boosted by the scanner, | |
| 1024 * as described by the 'source' gamma, so they should not | |
| 1025 * be further boosted by the display program. | |
| 1026 * (6) As an example, with xv and display, if no gamma is stored, | |
| 1027 * the program acts as if gamma were 0.4545, multiplies this by 2.2, | |
| 1028 * and does a linear rendering. Taking this as a baseline | |
| 1029 * brightness, if the stored gamma is: | |
| 1030 * > 0.4545, the image is rendered lighter than baseline | |
| 1031 * < 0.4545, the image is rendered darker than baseline | |
| 1032 * In contrast, gqview seems to ignore the gamma chunk in png. | |
| 1033 * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 | |
| 1034 * and 32. However, it is possible, and in some cases desirable, | |
| 1035 * to write out a png file using an rgb pix that has 24 bpp. | |
| 1036 * For example, the open source xpdf SplashBitmap class generates | |
| 1037 * 24 bpp rgb images. Consequently, we enable writing 24 bpp pix | |
| 1038 * without converting it to 32 bpp first. Caution: do not call | |
| 1039 * pixSetPadBits(), because the alignment is wrong and you may | |
| 1040 * erase part of the last pixel on each line. | |
| 1041 * (8) If the pix has a colormap, it is written to file. In most | |
| 1042 * situations, the alpha component is 255 for each colormap entry, | |
| 1043 * which is opaque and indicates that it should be ignored. | |
| 1044 * However, if any alpha component is not 255, it is assumed that | |
| 1045 * the alpha values are valid, and they are written to the png | |
| 1046 * file in a tRNS segment. On readback, the tRNS segment is | |
| 1047 * identified, and the colormapped image with alpha is converted | |
| 1048 * to a 4 spp rgba image. | |
| 1049 * </pre> | |
| 1050 */ | |
| 1051 l_ok | |
| 1052 pixWriteStreamPng(FILE *fp, | |
| 1053 PIX *pix, | |
| 1054 l_float32 gamma) | |
| 1055 { | |
| 1056 char commentstring[] = "Comment"; | |
| 1057 l_int32 i, j, k, wpl, d, spp, compval, valid; | |
| 1058 l_int32 cmflag, opaque, max_trans, ncolors; | |
| 1059 l_int32 *rmap, *gmap, *bmap, *amap; | |
| 1060 l_uint32 *data, *ppixel; | |
| 1061 png_byte bit_depth, color_type; | |
| 1062 png_byte alpha[256]; | |
| 1063 png_uint_32 w, h; | |
| 1064 png_uint_32 xres, yres; | |
| 1065 png_bytep *row_pointers; | |
| 1066 png_bytep rowbuffer; | |
| 1067 png_structp png_ptr; | |
| 1068 png_infop info_ptr; | |
| 1069 png_colorp palette; | |
| 1070 PIX *pix1; | |
| 1071 PIXCMAP *cmap; | |
| 1072 char *text; | |
| 1073 | |
| 1074 if (!fp) | |
| 1075 return ERROR_INT("stream not open", __func__, 1); | |
| 1076 if (!pix) | |
| 1077 return ERROR_INT("pix not defined", __func__, 1); | |
| 1078 | |
| 1079 w = pixGetWidth(pix); | |
| 1080 h = pixGetHeight(pix); | |
| 1081 d = pixGetDepth(pix); | |
| 1082 spp = pixGetSpp(pix); | |
| 1083 | |
| 1084 /* A cmap validity check should prevent low-level colormap errors. */ | |
| 1085 if ((cmap = pixGetColormap(pix))) { | |
| 1086 cmflag = 1; | |
| 1087 pixcmapIsValid(cmap, pix, &valid); | |
| 1088 if (!valid) | |
| 1089 return ERROR_INT("colormap is not valid", __func__, 1); | |
| 1090 } else { | |
| 1091 cmflag = 0; | |
| 1092 } | |
| 1093 | |
| 1094 /* Do not set pad bits for d = 24 ! */ | |
| 1095 if (d != 24) pixSetPadBits(pix, 0); | |
| 1096 | |
| 1097 /* Set the color type and bit depth. */ | |
| 1098 if (d == 32 && spp == 4) { | |
| 1099 bit_depth = 8; | |
| 1100 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ | |
| 1101 cmflag = 0; /* ignore if it exists */ | |
| 1102 } else if (d == 24 || d == 32) { | |
| 1103 bit_depth = 8; | |
| 1104 color_type = PNG_COLOR_TYPE_RGB; /* 2 */ | |
| 1105 cmflag = 0; /* ignore if it exists */ | |
| 1106 } else { | |
| 1107 bit_depth = d; | |
| 1108 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ | |
| 1109 } | |
| 1110 if (cmflag) | |
| 1111 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ | |
| 1112 | |
| 1113 #if DEBUG_WRITE | |
| 1114 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n", | |
| 1115 cmflag, bit_depth, color_type); | |
| 1116 #endif /* DEBUG_WRITE */ | |
| 1117 | |
| 1118 /* Allocate the 2 png data structures */ | |
| 1119 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | |
| 1120 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 1121 return ERROR_INT("png_ptr not made", __func__, 1); | |
| 1122 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 1123 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |
| 1124 return ERROR_INT("info_ptr not made", __func__, 1); | |
| 1125 } | |
| 1126 | |
| 1127 /* Set up png setjmp error handling */ | |
| 1128 pix1 = NULL; | |
| 1129 row_pointers = NULL; | |
| 1130 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 1131 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 1132 LEPT_FREE(row_pointers); | |
| 1133 pixDestroy(&pix1); | |
| 1134 return ERROR_INT("internal png error", __func__, 1); | |
| 1135 } | |
| 1136 | |
| 1137 png_init_io(png_ptr, fp); | |
| 1138 | |
| 1139 /* With best zlib compression (9), get between 1 and 10% improvement | |
| 1140 * over default (6), but the compression is 3 to 10 times slower. | |
| 1141 * Use the zlib default (6) as our default compression unless | |
| 1142 * pix->special falls in the range [10 ... 19]; then subtract 10 | |
| 1143 * to get the compression value. */ | |
| 1144 compval = Z_DEFAULT_COMPRESSION; | |
| 1145 if (pix->special >= 10 && pix->special < 20) | |
| 1146 compval = pix->special - 10; | |
| 1147 png_set_compression_level(png_ptr, compval); | |
| 1148 | |
| 1149 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, | |
| 1150 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, | |
| 1151 PNG_FILTER_TYPE_BASE); | |
| 1152 | |
| 1153 /* Store resolution in ppm, if known */ | |
| 1154 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); | |
| 1155 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); | |
| 1156 if ((xres == 0) || (yres == 0)) | |
| 1157 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); | |
| 1158 else | |
| 1159 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); | |
| 1160 | |
| 1161 if (cmflag) { | |
| 1162 /* Make and save the palette */ | |
| 1163 ncolors = pixcmapGetCount(cmap); | |
| 1164 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); | |
| 1165 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); | |
| 1166 for (i = 0; i < ncolors; i++) { | |
| 1167 palette[i].red = (png_byte)rmap[i]; | |
| 1168 palette[i].green = (png_byte)gmap[i]; | |
| 1169 palette[i].blue = (png_byte)bmap[i]; | |
| 1170 alpha[i] = (png_byte)amap[i]; | |
| 1171 } | |
| 1172 LEPT_FREE(rmap); | |
| 1173 LEPT_FREE(gmap); | |
| 1174 LEPT_FREE(bmap); | |
| 1175 LEPT_FREE(amap); | |
| 1176 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); | |
| 1177 LEPT_FREE(palette); | |
| 1178 | |
| 1179 /* Add the tRNS chunk. If the non-opaque colors are listed | |
| 1180 * first in the colormap, as in the spec, we can use that in | |
| 1181 * the 4th arg of png_set_tRNS. Otherwise, transparency will | |
| 1182 * be lost for some colors. To prevent that, see the comments | |
| 1183 * in pixcmapNonOpaqueColorsInfo(). */ | |
| 1184 pixcmapIsOpaque(cmap, &opaque); | |
| 1185 if (!opaque) { /* alpha channel has some transparency; assume valid */ | |
| 1186 pixcmapNonOpaqueColorsInfo(cmap, NULL, &max_trans, NULL); | |
| 1187 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, | |
| 1188 max_trans + 1, NULL); | |
| 1189 } | |
| 1190 } | |
| 1191 | |
| 1192 /* 0.4545 is treated as the default by some image | |
| 1193 * display programs (not gqview). A value > 0.4545 will | |
| 1194 * lighten an image as displayed by xv, display, etc. */ | |
| 1195 if (gamma > 0.0) | |
| 1196 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); | |
| 1197 | |
| 1198 if ((text = pixGetText(pix))) { | |
| 1199 png_text text_chunk; | |
| 1200 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; | |
| 1201 text_chunk.key = commentstring; | |
| 1202 text_chunk.text = text; | |
| 1203 text_chunk.text_length = strlen(text); | |
| 1204 #ifdef PNG_ITXT_SUPPORTED | |
| 1205 text_chunk.itxt_length = 0; | |
| 1206 text_chunk.lang = NULL; | |
| 1207 text_chunk.lang_key = NULL; | |
| 1208 #endif | |
| 1209 png_set_text(png_ptr, info_ptr, &text_chunk, 1); | |
| 1210 } | |
| 1211 | |
| 1212 /* Write header and palette info */ | |
| 1213 png_write_info(png_ptr, info_ptr); | |
| 1214 | |
| 1215 if ((d != 32) && (d != 24)) { /* not rgb color */ | |
| 1216 /* Generate a temporary pix with bytes swapped. | |
| 1217 * For writing a 1 bpp image as png: | |
| 1218 * ~ if no colormap, invert the data, because png writes | |
| 1219 * black as 0 | |
| 1220 * ~ if colormapped, do not invert the data; the two RGBA | |
| 1221 * colors can have any value. */ | |
| 1222 if (d == 1 && !cmap) { | |
| 1223 pix1 = pixInvert(NULL, pix); | |
| 1224 pixEndianByteSwap(pix1); | |
| 1225 } else { | |
| 1226 pix1 = pixEndianByteSwapNew(pix); | |
| 1227 } | |
| 1228 if (!pix1) { | |
| 1229 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 1230 return ERROR_INT("pix1 not made", __func__, 1); | |
| 1231 } | |
| 1232 | |
| 1233 /* Make and assign array of image row pointers */ | |
| 1234 row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); | |
| 1235 wpl = pixGetWpl(pix1); | |
| 1236 data = pixGetData(pix1); | |
| 1237 for (i = 0; i < h; i++) | |
| 1238 row_pointers[i] = (png_bytep)(data + i * wpl); | |
| 1239 png_set_rows(png_ptr, info_ptr, row_pointers); | |
| 1240 | |
| 1241 /* Transfer the data */ | |
| 1242 png_write_image(png_ptr, row_pointers); | |
| 1243 png_write_end(png_ptr, info_ptr); | |
| 1244 LEPT_FREE(row_pointers); | |
| 1245 pixDestroy(&pix1); | |
| 1246 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 1247 return 0; | |
| 1248 } | |
| 1249 | |
| 1250 /* For rgb and rgba, compose and write a row at a time */ | |
| 1251 data = pixGetData(pix); | |
| 1252 wpl = pixGetWpl(pix); | |
| 1253 if (d == 24) { /* See note 7 above */ | |
| 1254 for (i = 0; i < h; i++) { | |
| 1255 ppixel = data + i * wpl; | |
| 1256 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); | |
| 1257 } | |
| 1258 } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */ | |
| 1259 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); | |
| 1260 for (i = 0; i < h; i++) { | |
| 1261 ppixel = data + i * wpl; | |
| 1262 for (j = k = 0; j < w; j++) { | |
| 1263 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); | |
| 1264 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); | |
| 1265 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); | |
| 1266 if (spp == 4) | |
| 1267 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); | |
| 1268 ppixel++; | |
| 1269 } | |
| 1270 | |
| 1271 png_write_rows(png_ptr, &rowbuffer, 1); | |
| 1272 } | |
| 1273 LEPT_FREE(rowbuffer); | |
| 1274 } | |
| 1275 | |
| 1276 png_write_end(png_ptr, info_ptr); | |
| 1277 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 1278 return 0; | |
| 1279 } | |
| 1280 | |
| 1281 | |
| 1282 /*! | |
| 1283 * \brief pixSetZlibCompression() | |
| 1284 * | |
| 1285 * \param[in] pix | |
| 1286 * \param[in] compval zlib compression value | |
| 1287 * \return 0 if OK, 1 on error | |
| 1288 * | |
| 1289 * <pre> | |
| 1290 * Notes: | |
| 1291 * (1) Valid zlib compression values are in the interval [0 ... 9], | |
| 1292 * where, as defined in zlib.h: | |
| 1293 * 0 Z_NO_COMPRESSION | |
| 1294 * 1 Z_BEST_SPEED (poorest compression) | |
| 1295 * 9 Z_BEST_COMPRESSION | |
| 1296 * For the default value, use either of these: | |
| 1297 * 6 Z_DEFAULT_COMPRESSION | |
| 1298 * -1 (resolves to Z_DEFAULT_COMPRESSION) | |
| 1299 * (2) If you use the defined constants in zlib.h instead of the | |
| 1300 * compression integers given above, you must include zlib.h. | |
| 1301 * </pre> | |
| 1302 */ | |
| 1303 l_ok | |
| 1304 pixSetZlibCompression(PIX *pix, | |
| 1305 l_int32 compval) | |
| 1306 { | |
| 1307 if (!pix) | |
| 1308 return ERROR_INT("pix not defined", __func__, 1); | |
| 1309 if (compval < 0 || compval > 9) { | |
| 1310 L_ERROR("Invalid zlib comp val; using default\n", __func__); | |
| 1311 compval = Z_DEFAULT_COMPRESSION; | |
| 1312 } | |
| 1313 pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */ | |
| 1314 return 0; | |
| 1315 } | |
| 1316 | |
| 1317 | |
| 1318 /*---------------------------------------------------------------------* | |
| 1319 * Set flag for stripping 16 bits on reading * | |
| 1320 *---------------------------------------------------------------------*/ | |
| 1321 /*! | |
| 1322 * \brief l_pngSetReadStrip16To8() | |
| 1323 * | |
| 1324 * \param[in] flag 1 for stripping 16 bpp to 8 bpp on reading; | |
| 1325 * 0 for leaving 16 bpp | |
| 1326 * \return void | |
| 1327 */ | |
| 1328 void | |
| 1329 l_pngSetReadStrip16To8(l_int32 flag) | |
| 1330 { | |
| 1331 var_PNG_STRIP_16_TO_8 = flag; | |
| 1332 } | |
| 1333 | |
| 1334 | |
| 1335 /*-------------------------------------------------------------------------* | |
| 1336 * Memio utility * | |
| 1337 * libpng read/write callback replacements for performing memory I/O * | |
| 1338 * * | |
| 1339 * Copyright (C) 2017 Milner Technologies, Inc. This content is a * | |
| 1340 * component of leptonica and is provided under the terms of the * | |
| 1341 * Leptonica license. * | |
| 1342 *-------------------------------------------------------------------------*/ | |
| 1343 | |
| 1344 /*! A node in a linked list of memory buffers that hold I/O content */ | |
| 1345 struct MemIOData | |
| 1346 { | |
| 1347 char* m_Buffer; /*!< pointer to this node's I/O content */ | |
| 1348 l_int32 m_Count; /*!< number of I/O content bytes read or written */ | |
| 1349 l_int32 m_Size; /*!< allocated size of m_buffer */ | |
| 1350 struct MemIOData *m_Next; /*!< pointer to the next node in the list; */ | |
| 1351 /*!< zero if this is the last node */ | |
| 1352 struct MemIOData *m_Last; /*!< pointer to the last node in the linked */ | |
| 1353 /*!< list. The last node is where new */ | |
| 1354 /*!< content is written. */ | |
| 1355 }; | |
| 1356 typedef struct MemIOData MEMIODATA; | |
| 1357 | |
| 1358 static void memio_png_write_data(png_structp png_ptr, png_bytep data, | |
| 1359 png_size_t length); | |
| 1360 static void memio_png_flush(MEMIODATA* pthing); | |
| 1361 static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes, | |
| 1362 png_size_t byteCountToRead); | |
| 1363 static void memio_free(MEMIODATA* pthing); | |
| 1364 | |
| 1365 static const l_int32 MEMIO_BUFFER_SIZE = 8192; /*! buffer alloc size */ | |
| 1366 | |
| 1367 /* | |
| 1368 * \brief memio_png_write_data() | |
| 1369 * | |
| 1370 * \param[in] png_ptr | |
| 1371 * \param[in] data | |
| 1372 * \param[in] len size of array data in bytes | |
| 1373 * | |
| 1374 * <pre> | |
| 1375 * Notes: | |
| 1376 * (1) This is a libpng callback for writing an image into a | |
| 1377 * linked list of memory buffers. | |
| 1378 * </pre> | |
| 1379 */ | |
| 1380 static void | |
| 1381 memio_png_write_data(png_structp png_ptr, | |
| 1382 png_bytep data, | |
| 1383 png_size_t len) | |
| 1384 { | |
| 1385 MEMIODATA *thing, *last; | |
| 1386 l_int32 written = 0; | |
| 1387 l_int32 remainingSpace, remainingToWrite; | |
| 1388 | |
| 1389 thing = (struct MemIOData*)png_get_io_ptr(png_ptr); | |
| 1390 last = (struct MemIOData*)thing->m_Last; | |
| 1391 if (last->m_Buffer == NULL) { | |
| 1392 if (len > MEMIO_BUFFER_SIZE) { | |
| 1393 last->m_Buffer = (char *)LEPT_MALLOC(len); | |
| 1394 memcpy(last->m_Buffer, data, len); | |
| 1395 last->m_Size = last->m_Count = len; | |
| 1396 return; | |
| 1397 } | |
| 1398 | |
| 1399 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); | |
| 1400 last->m_Size = MEMIO_BUFFER_SIZE; | |
| 1401 } | |
| 1402 | |
| 1403 while (written < len) { | |
| 1404 if (last->m_Count == last->m_Size) { | |
| 1405 MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA)); | |
| 1406 next->m_Next = NULL; | |
| 1407 next->m_Count = 0; | |
| 1408 next->m_Last = next; | |
| 1409 | |
| 1410 last->m_Next = next; | |
| 1411 last = thing->m_Last = next; | |
| 1412 | |
| 1413 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); | |
| 1414 last->m_Size = MEMIO_BUFFER_SIZE; | |
| 1415 } | |
| 1416 | |
| 1417 remainingSpace = last->m_Size - last->m_Count; | |
| 1418 remainingToWrite = len - written; | |
| 1419 if (remainingSpace < remainingToWrite) { | |
| 1420 memcpy(last->m_Buffer + last->m_Count, data + written, | |
| 1421 remainingSpace); | |
| 1422 written += remainingSpace; | |
| 1423 last->m_Count += remainingSpace; | |
| 1424 } else { | |
| 1425 memcpy(last->m_Buffer + last->m_Count, data + written, | |
| 1426 remainingToWrite); | |
| 1427 written += remainingToWrite; | |
| 1428 last->m_Count += remainingToWrite; | |
| 1429 } | |
| 1430 } | |
| 1431 } | |
| 1432 | |
| 1433 | |
| 1434 /* | |
| 1435 * \brief memio_png_flush() | |
| 1436 * | |
| 1437 * \param[in] pthing | |
| 1438 * | |
| 1439 * <pre> | |
| 1440 * Notes: | |
| 1441 * (1) This consolidates write buffers into a single buffer at the | |
| 1442 * haed of the link list of buffers. | |
| 1443 * </pre> | |
| 1444 */ | |
| 1445 static void | |
| 1446 memio_png_flush(MEMIODATA *pthing) | |
| 1447 { | |
| 1448 l_int32 amount = 0; | |
| 1449 l_int32 copied = 0; | |
| 1450 MEMIODATA *buffer = 0; | |
| 1451 char *data = 0; | |
| 1452 | |
| 1453 /* If the data is in one buffer, give the buffer to the user. */ | |
| 1454 if (pthing->m_Next == NULL) return; | |
| 1455 | |
| 1456 /* Consolidate multiple buffers into one new one; add the buffer | |
| 1457 * sizes together. */ | |
| 1458 amount = pthing->m_Count; | |
| 1459 buffer = pthing->m_Next; | |
| 1460 while (buffer != NULL) { | |
| 1461 amount += buffer->m_Count; | |
| 1462 buffer = buffer->m_Next; | |
| 1463 } | |
| 1464 | |
| 1465 /* Copy data to a new buffer. */ | |
| 1466 data = (char *)LEPT_MALLOC(amount); | |
| 1467 memcpy(data, pthing->m_Buffer, pthing->m_Count); | |
| 1468 copied = pthing->m_Count; | |
| 1469 | |
| 1470 LEPT_FREE(pthing->m_Buffer); | |
| 1471 pthing->m_Buffer = NULL; | |
| 1472 | |
| 1473 /* Don't delete original "thing" because we don't control it. */ | |
| 1474 buffer = pthing->m_Next; | |
| 1475 pthing->m_Next = NULL; | |
| 1476 while (buffer != NULL && copied < amount) { | |
| 1477 MEMIODATA* old; | |
| 1478 memcpy(data + copied, buffer->m_Buffer, buffer->m_Count); | |
| 1479 copied += buffer->m_Count; | |
| 1480 | |
| 1481 old = buffer; | |
| 1482 buffer = buffer->m_Next; | |
| 1483 | |
| 1484 LEPT_FREE(old->m_Buffer); | |
| 1485 LEPT_FREE(old); | |
| 1486 } | |
| 1487 | |
| 1488 pthing->m_Buffer = data; | |
| 1489 pthing->m_Count = copied; | |
| 1490 pthing->m_Size = amount; | |
| 1491 return; | |
| 1492 } | |
| 1493 | |
| 1494 | |
| 1495 /* | |
| 1496 * \brief memio_png_read_data() | |
| 1497 * | |
| 1498 * \param[in] png_ptr | |
| 1499 * \param[in] outBytes | |
| 1500 * \param[in] byteCountToRead | |
| 1501 * | |
| 1502 * <pre> | |
| 1503 * Notes: | |
| 1504 * (1) This is a libpng callback that reads an image from a single | |
| 1505 * memory buffer. | |
| 1506 * </pre> | |
| 1507 */ | |
| 1508 static void | |
| 1509 memio_png_read_data(png_structp png_ptr, | |
| 1510 png_bytep outBytes, | |
| 1511 png_size_t byteCountToRead) | |
| 1512 { | |
| 1513 MEMIODATA *thing; | |
| 1514 | |
| 1515 thing = (MEMIODATA *)png_get_io_ptr(png_ptr); | |
| 1516 if (byteCountToRead > (thing->m_Size - thing->m_Count)) { | |
| 1517 png_error(png_ptr, "read error in memio_png_read_data"); | |
| 1518 } | |
| 1519 memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead); | |
| 1520 thing->m_Count += byteCountToRead; | |
| 1521 } | |
| 1522 | |
| 1523 | |
| 1524 /* | |
| 1525 * \brief memio_free() | |
| 1526 * | |
| 1527 * \param[in] pthing | |
| 1528 * | |
| 1529 * <pre> | |
| 1530 * Notes: | |
| 1531 * (1) This frees all the write buffers in the linked list. It must | |
| 1532 * be done before exiting the pixWriteMemPng(). | |
| 1533 * </pre> | |
| 1534 */ | |
| 1535 static void | |
| 1536 memio_free(MEMIODATA* pthing) | |
| 1537 { | |
| 1538 MEMIODATA *buffer, *old; | |
| 1539 | |
| 1540 if (pthing->m_Buffer != NULL) | |
| 1541 LEPT_FREE(pthing->m_Buffer); | |
| 1542 | |
| 1543 pthing->m_Buffer = NULL; | |
| 1544 buffer = pthing->m_Next; | |
| 1545 while (buffer != NULL) { | |
| 1546 old = buffer; | |
| 1547 buffer = buffer->m_Next; | |
| 1548 | |
| 1549 if (old->m_Buffer != NULL) | |
| 1550 LEPT_FREE(old->m_Buffer); | |
| 1551 LEPT_FREE(old); | |
| 1552 } | |
| 1553 } | |
| 1554 | |
| 1555 | |
| 1556 /*---------------------------------------------------------------------* | |
| 1557 * Reading png from memory * | |
| 1558 *---------------------------------------------------------------------*/ | |
| 1559 /*! | |
| 1560 * \brief pixReadMemPng() | |
| 1561 * | |
| 1562 * \param[in] filedata png compressed data in memory | |
| 1563 * \param[in] filesize number of bytes in data | |
| 1564 * \return pix, or NULL on error | |
| 1565 * | |
| 1566 * <pre> | |
| 1567 * Notes: | |
| 1568 * (1) See pixReastreamPng(). | |
| 1569 * </pre> | |
| 1570 */ | |
| 1571 PIX * | |
| 1572 pixReadMemPng(const l_uint8 *filedata, | |
| 1573 size_t filesize) | |
| 1574 { | |
| 1575 l_uint8 byte; | |
| 1576 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid; | |
| 1577 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS; | |
| 1578 l_uint32 png_transforms; | |
| 1579 l_uint32 *data, *line, *ppixel; | |
| 1580 int num_palette, num_text, num_trans; | |
| 1581 png_byte bit_depth, color_type, channels; | |
| 1582 png_uint_32 w, h, rowbytes, xres, yres; | |
| 1583 png_bytep rowptr, trans; | |
| 1584 png_bytep *row_pointers; | |
| 1585 png_structp png_ptr; | |
| 1586 png_infop info_ptr, end_info; | |
| 1587 png_colorp palette; | |
| 1588 png_textp text_ptr; /* ptr to text_chunk */ | |
| 1589 MEMIODATA state; | |
| 1590 PIX *pix, *pix1; | |
| 1591 PIXCMAP *cmap; | |
| 1592 | |
| 1593 if (!filedata) | |
| 1594 return (PIX *)ERROR_PTR("filedata not defined", __func__, NULL); | |
| 1595 if (filesize < 1) | |
| 1596 return (PIX *)ERROR_PTR("invalid filesize", __func__, NULL); | |
| 1597 | |
| 1598 state.m_Next = 0; | |
| 1599 state.m_Count = 0; | |
| 1600 state.m_Last = &state; | |
| 1601 state.m_Buffer = (char*)filedata; | |
| 1602 state.m_Size = filesize; | |
| 1603 pix = NULL; | |
| 1604 | |
| 1605 /* Allocate the 3 data structures */ | |
| 1606 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, | |
| 1607 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 1608 return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL); | |
| 1609 | |
| 1610 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 1611 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
| 1612 return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL); | |
| 1613 } | |
| 1614 | |
| 1615 if ((end_info = png_create_info_struct(png_ptr)) == NULL) { | |
| 1616 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
| 1617 return (PIX *)ERROR_PTR("end_info not made", __func__, NULL); | |
| 1618 } | |
| 1619 | |
| 1620 /* Set up png setjmp error handling */ | |
| 1621 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 1622 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 1623 return (PIX *)ERROR_PTR("internal png error", __func__, NULL); | |
| 1624 } | |
| 1625 | |
| 1626 png_set_read_fn(png_ptr, &state, memio_png_read_data); | |
| 1627 | |
| 1628 /* ---------------------------------------------------------- * | |
| 1629 * Set the transforms flags. Whatever happens here, | |
| 1630 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. | |
| 1631 * Also, do not use PNG_TRANSFORM_EXPAND, which would | |
| 1632 * expand all images with bpp < 8 to 8 bpp. | |
| 1633 * ---------------------------------------------------------- */ | |
| 1634 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ | |
| 1635 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ | |
| 1636 png_transforms = PNG_TRANSFORM_STRIP_16; | |
| 1637 } else { | |
| 1638 png_transforms = PNG_TRANSFORM_IDENTITY; | |
| 1639 L_INFO("not stripping 16 --> 8 in png reading\n", __func__); | |
| 1640 } | |
| 1641 | |
| 1642 /* Read it */ | |
| 1643 png_read_png(png_ptr, info_ptr, png_transforms, NULL); | |
| 1644 | |
| 1645 row_pointers = png_get_rows(png_ptr, info_ptr); | |
| 1646 w = png_get_image_width(png_ptr, info_ptr); | |
| 1647 h = png_get_image_height(png_ptr, info_ptr); | |
| 1648 bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
| 1649 rowbytes = png_get_rowbytes(png_ptr, info_ptr); | |
| 1650 color_type = png_get_color_type(png_ptr, info_ptr); | |
| 1651 channels = png_get_channels(png_ptr, info_ptr); | |
| 1652 spp = channels; | |
| 1653 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; | |
| 1654 | |
| 1655 if (spp == 1) { | |
| 1656 d = bit_depth; | |
| 1657 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ | |
| 1658 d = 4 * bit_depth; | |
| 1659 } | |
| 1660 | |
| 1661 /* Remove if/when this is implemented for all bit_depths */ | |
| 1662 if (spp == 3 && bit_depth != 8) { | |
| 1663 lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth); | |
| 1664 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 1665 return (PIX *)ERROR_PTR("not implemented for this depth", | |
| 1666 __func__, NULL); | |
| 1667 } | |
| 1668 | |
| 1669 cmap = NULL; | |
| 1670 if (color_type == PNG_COLOR_TYPE_PALETTE || | |
| 1671 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ | |
| 1672 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); | |
| 1673 cmap = pixcmapCreate(d); /* spp == 1 */ | |
| 1674 for (cindex = 0; cindex < num_palette; cindex++) { | |
| 1675 rval = palette[cindex].red; | |
| 1676 gval = palette[cindex].green; | |
| 1677 bval = palette[cindex].blue; | |
| 1678 pixcmapAddColor(cmap, rval, gval, bval); | |
| 1679 } | |
| 1680 } | |
| 1681 | |
| 1682 if ((pix = pixCreate(w, h, d)) == NULL) { | |
| 1683 pixcmapDestroy(&cmap); | |
| 1684 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 1685 pixcmapDestroy(&cmap); | |
| 1686 return (PIX *)ERROR_PTR("pix not made", __func__, NULL); | |
| 1687 } | |
| 1688 pixSetInputFormat(pix, IFF_PNG); | |
| 1689 wpl = pixGetWpl(pix); | |
| 1690 data = pixGetData(pix); | |
| 1691 pixSetSpp(pix, spp); | |
| 1692 if (pixSetColormap(pix, cmap)) { | |
| 1693 pixDestroy(&pix); | |
| 1694 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); | |
| 1695 } | |
| 1696 | |
| 1697 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ | |
| 1698 for (i = 0; i < h; i++) { | |
| 1699 line = data + i * wpl; | |
| 1700 rowptr = row_pointers[i]; | |
| 1701 for (j = 0; j < rowbytes; j++) { | |
| 1702 SET_DATA_BYTE(line, j, rowptr[j]); | |
| 1703 } | |
| 1704 } | |
| 1705 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ | |
| 1706 L_INFO("converting (gray + alpha) ==> RGBA\n", __func__); | |
| 1707 for (i = 0; i < h; i++) { | |
| 1708 ppixel = data + i * wpl; | |
| 1709 rowptr = row_pointers[i]; | |
| 1710 for (j = k = 0; j < w; j++) { | |
| 1711 /* Copy gray value into r, g and b */ | |
| 1712 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); | |
| 1713 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); | |
| 1714 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); | |
| 1715 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); | |
| 1716 ppixel++; | |
| 1717 } | |
| 1718 } | |
| 1719 pixSetSpp(pix, 4); /* we do not support 2 spp pix */ | |
| 1720 } else if (spp == 3 || spp == 4) { | |
| 1721 for (i = 0; i < h; i++) { | |
| 1722 ppixel = data + i * wpl; | |
| 1723 rowptr = row_pointers[i]; | |
| 1724 for (j = k = 0; j < w; j++) { | |
| 1725 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); | |
| 1726 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); | |
| 1727 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); | |
| 1728 if (spp == 4) | |
| 1729 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); | |
| 1730 ppixel++; | |
| 1731 } | |
| 1732 } | |
| 1733 } | |
| 1734 | |
| 1735 /* Special spp == 1 cases with transparency: | |
| 1736 * (1) 8 bpp without colormap; assume full transparency | |
| 1737 * (2) 1 bpp with colormap + trans array (for alpha) | |
| 1738 * (3) 2 bpp with colormap + trans array (for alpha) | |
| 1739 * (4) 4 bpp with colormap + trans array (for alpha) | |
| 1740 * (5) 8 bpp with colormap + trans array (for alpha) | |
| 1741 * These all require converting to RGBA */ | |
| 1742 if (spp == 1 && tRNS) { | |
| 1743 if (!cmap) { | |
| 1744 /* Case 1: make fully transparent RGBA image */ | |
| 1745 L_INFO("transparency, 1 spp, no colormap, no transparency array: " | |
| 1746 "convention is fully transparent image\n", __func__); | |
| 1747 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__); | |
| 1748 pixDestroy(&pix); | |
| 1749 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ | |
| 1750 pixSetSpp(pix, 4); | |
| 1751 } else { | |
| 1752 L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__); | |
| 1753 | |
| 1754 /* Grab the transparency array */ | |
| 1755 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); | |
| 1756 if (!trans) { /* invalid png file */ | |
| 1757 pixDestroy(&pix); | |
| 1758 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 1759 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", | |
| 1760 __func__, NULL); | |
| 1761 } | |
| 1762 | |
| 1763 /* Save the cmap and destroy the pix */ | |
| 1764 cmap = pixcmapCopy(pixGetColormap(pix)); | |
| 1765 ncolors = pixcmapGetCount(cmap); | |
| 1766 pixDestroy(&pix); | |
| 1767 | |
| 1768 /* Start over with 32 bit RGBA */ | |
| 1769 pix = pixCreate(w, h, 32); | |
| 1770 wpl = pixGetWpl(pix); | |
| 1771 data = pixGetData(pix); | |
| 1772 pixSetSpp(pix, 4); | |
| 1773 | |
| 1774 #if DEBUG_READ | |
| 1775 lept_stderr("ncolors = %d, num_trans = %d\n", | |
| 1776 ncolors, num_trans); | |
| 1777 for (i = 0; i < ncolors; i++) { | |
| 1778 pixcmapGetColor(cmap, i, &rval, &gval, &bval); | |
| 1779 if (i < num_trans) { | |
| 1780 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n", | |
| 1781 rval, gval, bval, trans[i]); | |
| 1782 } else { | |
| 1783 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n", | |
| 1784 rval, gval, bval); | |
| 1785 } | |
| 1786 } | |
| 1787 #endif /* DEBUG_READ */ | |
| 1788 | |
| 1789 /* Extract the data and convert to RGBA */ | |
| 1790 if (d == 1) { | |
| 1791 /* Case 2: 1 bpp with transparency (usually) behind white */ | |
| 1792 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 1793 if (num_trans == 1) | |
| 1794 L_INFO("num_trans = 1; second color opaque by default\n", | |
| 1795 __func__); | |
| 1796 for (i = 0; i < h; i++) { | |
| 1797 ppixel = data + i * wpl; | |
| 1798 rowptr = row_pointers[i]; | |
| 1799 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 1800 byte = rowptr[j]; | |
| 1801 for (k = 0; k < 8 && index < w; k++, index++) { | |
| 1802 bitval = (byte >> (7 - k)) & 1; | |
| 1803 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); | |
| 1804 composeRGBPixel(rval, gval, bval, ppixel); | |
| 1805 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 1806 bitval < num_trans ? trans[bitval] : 255); | |
| 1807 ppixel++; | |
| 1808 } | |
| 1809 } | |
| 1810 } | |
| 1811 } else if (d == 2) { | |
| 1812 /* Case 3: 2 bpp with cmap and associated transparency */ | |
| 1813 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 1814 for (i = 0; i < h; i++) { | |
| 1815 ppixel = data + i * wpl; | |
| 1816 rowptr = row_pointers[i]; | |
| 1817 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 1818 byte = rowptr[j]; | |
| 1819 for (k = 0; k < 4 && index < w; k++, index++) { | |
| 1820 bival = (byte >> 2 * (3 - k)) & 3; | |
| 1821 pixcmapGetColor(cmap, bival, &rval, &gval, &bval); | |
| 1822 composeRGBPixel(rval, gval, bval, ppixel); | |
| 1823 /* Assume missing entries to be 255 (opaque) | |
| 1824 * according to the spec: | |
| 1825 * http://www.w3.org/TR/PNG/#11tRNS */ | |
| 1826 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 1827 bival < num_trans ? trans[bival] : 255); | |
| 1828 ppixel++; | |
| 1829 } | |
| 1830 } | |
| 1831 } | |
| 1832 } else if (d == 4) { | |
| 1833 /* Case 4: 4 bpp with cmap and associated transparency */ | |
| 1834 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 1835 for (i = 0; i < h; i++) { | |
| 1836 ppixel = data + i * wpl; | |
| 1837 rowptr = row_pointers[i]; | |
| 1838 for (j = 0, index = 0; j < rowbytes; j++) { | |
| 1839 byte = rowptr[j]; | |
| 1840 for (k = 0; k < 2 && index < w; k++, index++) { | |
| 1841 quadval = (byte >> 4 * (1 - k)) & 0xf; | |
| 1842 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval); | |
| 1843 composeRGBPixel(rval, gval, bval, ppixel); | |
| 1844 /* Assume missing entries to be 255 (opaque) */ | |
| 1845 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 1846 quadval < num_trans ? trans[quadval] : 255); | |
| 1847 ppixel++; | |
| 1848 } | |
| 1849 } | |
| 1850 } | |
| 1851 } else if (d == 8) { | |
| 1852 /* Case 5: 8 bpp with cmap and associated transparency */ | |
| 1853 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__); | |
| 1854 for (i = 0; i < h; i++) { | |
| 1855 ppixel = data + i * wpl; | |
| 1856 rowptr = row_pointers[i]; | |
| 1857 for (j = 0; j < w; j++) { | |
| 1858 index = rowptr[j]; | |
| 1859 pixcmapGetColor(cmap, index, &rval, &gval, &bval); | |
| 1860 composeRGBPixel(rval, gval, bval, ppixel); | |
| 1861 /* Assume missing entries to be 255 (opaque) | |
| 1862 * according to the spec: | |
| 1863 * http://www.w3.org/TR/PNG/#11tRNS */ | |
| 1864 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, | |
| 1865 index < num_trans ? trans[index] : 255); | |
| 1866 ppixel++; | |
| 1867 } | |
| 1868 } | |
| 1869 } else { | |
| 1870 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", | |
| 1871 __func__, d); | |
| 1872 } | |
| 1873 pixcmapDestroy(&cmap); | |
| 1874 } | |
| 1875 } | |
| 1876 | |
| 1877 #if DEBUG_READ | |
| 1878 if (cmap) { | |
| 1879 for (i = 0; i < 16; i++) { | |
| 1880 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); | |
| 1881 } | |
| 1882 } | |
| 1883 #endif /* DEBUG_READ */ | |
| 1884 | |
| 1885 /* Final adjustments for bpp = 1. | |
| 1886 * + If there is no colormap, the image must be inverted because | |
| 1887 * png stores black pixels as 0. | |
| 1888 * + We have already handled the case of cmapped, 1 bpp pix | |
| 1889 * with transparency, where the output pix is 32 bpp RGBA. | |
| 1890 * If there is no transparency but the pix has a colormap, | |
| 1891 * we remove the colormap, because functions operating on | |
| 1892 * 1 bpp images in leptonica assume no colormap. | |
| 1893 * + The colormap must be removed in such a way that the pixel | |
| 1894 * values are not changed. If the values are only black and | |
| 1895 * white, we return a 1 bpp image; if gray, return an 8 bpp pix; | |
| 1896 * otherwise, return a 32 bpp rgb pix. | |
| 1897 * | |
| 1898 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag | |
| 1899 * to do the inversion, because that flag (since version 1.0.9) | |
| 1900 * inverts 8 bpp grayscale as well, which we don't want to do. | |
| 1901 * (It also doesn't work if there is a colormap.) | |
| 1902 * | |
| 1903 * Note that if the input png is a 1-bit with colormap and | |
| 1904 * transparency, it has already been rendered as a 32 bpp, | |
| 1905 * spp = 4 rgba pix. | |
| 1906 */ | |
| 1907 if (pixGetDepth(pix) == 1) { | |
| 1908 if (!cmap) { | |
| 1909 pixInvert(pix, pix); | |
| 1910 } else { | |
| 1911 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); | |
| 1912 pixDestroy(&pix); | |
| 1913 pix = pix1; | |
| 1914 } | |
| 1915 } | |
| 1916 | |
| 1917 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); | |
| 1918 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); | |
| 1919 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ | |
| 1920 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ | |
| 1921 | |
| 1922 /* Get the text if there is any */ | |
| 1923 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); | |
| 1924 if (num_text && text_ptr) | |
| 1925 pixSetText(pix, text_ptr->text); | |
| 1926 | |
| 1927 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | |
| 1928 | |
| 1929 /* Final validity check on the colormap */ | |
| 1930 if ((cmap = pixGetColormap(pix)) != NULL) { | |
| 1931 pixcmapIsValid(cmap, pix, &valid); | |
| 1932 if (!valid) { | |
| 1933 pixDestroy(&pix); | |
| 1934 return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL); | |
| 1935 } | |
| 1936 } | |
| 1937 | |
| 1938 pixSetPadBits(pix, 0); | |
| 1939 return pix; | |
| 1940 } | |
| 1941 | |
| 1942 | |
| 1943 /*---------------------------------------------------------------------* | |
| 1944 * Writing png to memory * | |
| 1945 *---------------------------------------------------------------------*/ | |
| 1946 /*! | |
| 1947 * \brief pixWriteMemPng() | |
| 1948 * | |
| 1949 * \param[out] pfiledata png encoded data of pix | |
| 1950 * \param[out] pfilesize size of png encoded data | |
| 1951 * \param[in] pix | |
| 1952 * \param[in] gamma use 0.0 if gamma is not defined | |
| 1953 * \return 0 if OK; 1 on error | |
| 1954 * | |
| 1955 * <pre> | |
| 1956 * Notes: | |
| 1957 * (1) See pixWriteStreamPng() | |
| 1958 * </pre> | |
| 1959 */ | |
| 1960 l_ok | |
| 1961 pixWriteMemPng(l_uint8 **pfiledata, | |
| 1962 size_t *pfilesize, | |
| 1963 PIX *pix, | |
| 1964 l_float32 gamma) | |
| 1965 { | |
| 1966 char commentstring[] = "Comment"; | |
| 1967 l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid; | |
| 1968 l_int32 *rmap, *gmap, *bmap, *amap; | |
| 1969 l_uint32 *data, *ppixel; | |
| 1970 png_byte bit_depth, color_type; | |
| 1971 png_byte alpha[256]; | |
| 1972 png_uint_32 w, h, xres, yres; | |
| 1973 png_bytep rowbuffer; | |
| 1974 png_structp png_ptr; | |
| 1975 png_infop info_ptr; | |
| 1976 png_colorp palette; | |
| 1977 PIX *pix1; | |
| 1978 PIXCMAP *cmap; | |
| 1979 char *text; | |
| 1980 MEMIODATA state; | |
| 1981 | |
| 1982 if (pfiledata) *pfiledata = NULL; | |
| 1983 if (pfilesize) *pfilesize = 0; | |
| 1984 if (!pfiledata) | |
| 1985 return ERROR_INT("&filedata not defined", __func__, 1); | |
| 1986 if (!pfilesize) | |
| 1987 return ERROR_INT("&filesize not defined", __func__, 1); | |
| 1988 if (!pix) | |
| 1989 return ERROR_INT("pix not defined", __func__, 1); | |
| 1990 | |
| 1991 state.m_Buffer = 0; | |
| 1992 state.m_Size = 0; | |
| 1993 state.m_Next = 0; | |
| 1994 state.m_Count = 0; | |
| 1995 state.m_Last = &state; | |
| 1996 | |
| 1997 w = pixGetWidth(pix); | |
| 1998 h = pixGetHeight(pix); | |
| 1999 d = pixGetDepth(pix); | |
| 2000 spp = pixGetSpp(pix); | |
| 2001 | |
| 2002 /* A cmap validity check should prevent low-level colormap errors. */ | |
| 2003 if ((cmap = pixGetColormap(pix))) { | |
| 2004 cmflag = 1; | |
| 2005 pixcmapIsValid(cmap, pix, &valid); | |
| 2006 if (!valid) | |
| 2007 return ERROR_INT("colormap is not valid", __func__, 1); | |
| 2008 } else { | |
| 2009 cmflag = 0; | |
| 2010 } | |
| 2011 | |
| 2012 /* Do not set pad bits for d = 24 ! */ | |
| 2013 if (d != 24) pixSetPadBits(pix, 0); | |
| 2014 | |
| 2015 /* Set the color type and bit depth. */ | |
| 2016 if (d == 32 && spp == 4) { | |
| 2017 bit_depth = 8; | |
| 2018 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ | |
| 2019 cmflag = 0; /* ignore if it exists */ | |
| 2020 } else if (d == 24 || d == 32) { | |
| 2021 bit_depth = 8; | |
| 2022 color_type = PNG_COLOR_TYPE_RGB; /* 2 */ | |
| 2023 cmflag = 0; /* ignore if it exists */ | |
| 2024 } else { | |
| 2025 bit_depth = d; | |
| 2026 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ | |
| 2027 } | |
| 2028 if (cmflag) | |
| 2029 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ | |
| 2030 | |
| 2031 #if DEBUG_WRITE | |
| 2032 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n", | |
| 2033 cmflag, bit_depth, color_type); | |
| 2034 #endif /* DEBUG_WRITE */ | |
| 2035 | |
| 2036 /* Allocate the 2 data structures */ | |
| 2037 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | |
| 2038 (png_voidp)NULL, NULL, NULL)) == NULL) | |
| 2039 return ERROR_INT("png_ptr not made", __func__, 1); | |
| 2040 | |
| 2041 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { | |
| 2042 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |
| 2043 return ERROR_INT("info_ptr not made", __func__, 1); | |
| 2044 } | |
| 2045 | |
| 2046 /* Set up png setjmp error handling */ | |
| 2047 pix1 = NULL; | |
| 2048 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 2049 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 2050 pixDestroy(&pix1); | |
| 2051 return ERROR_INT("internal png error", __func__, 1); | |
| 2052 } | |
| 2053 | |
| 2054 png_set_write_fn(png_ptr, &state, memio_png_write_data, | |
| 2055 (png_flush_ptr)NULL); | |
| 2056 | |
| 2057 /* With best zlib compression (9), get between 1 and 10% improvement | |
| 2058 * over default (6), but the compression is 3 to 10 times slower. | |
| 2059 * Use the zlib default (6) as our default compression unless | |
| 2060 * pix->special falls in the range [10 ... 19]; then subtract 10 | |
| 2061 * to get the compression value. */ | |
| 2062 compval = Z_DEFAULT_COMPRESSION; | |
| 2063 if (pix->special >= 10 && pix->special < 20) | |
| 2064 compval = pix->special - 10; | |
| 2065 png_set_compression_level(png_ptr, compval); | |
| 2066 | |
| 2067 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, | |
| 2068 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, | |
| 2069 PNG_FILTER_TYPE_BASE); | |
| 2070 | |
| 2071 /* Store resolution in ppm, if known */ | |
| 2072 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); | |
| 2073 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); | |
| 2074 if ((xres == 0) || (yres == 0)) | |
| 2075 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); | |
| 2076 else | |
| 2077 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); | |
| 2078 | |
| 2079 if (cmflag) { | |
| 2080 /* Make and save the palette */ | |
| 2081 ncolors = pixcmapGetCount(cmap); | |
| 2082 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); | |
| 2083 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); | |
| 2084 for (i = 0; i < ncolors; i++) { | |
| 2085 palette[i].red = (png_byte)rmap[i]; | |
| 2086 palette[i].green = (png_byte)gmap[i]; | |
| 2087 palette[i].blue = (png_byte)bmap[i]; | |
| 2088 alpha[i] = (png_byte)amap[i]; | |
| 2089 } | |
| 2090 LEPT_FREE(rmap); | |
| 2091 LEPT_FREE(gmap); | |
| 2092 LEPT_FREE(bmap); | |
| 2093 LEPT_FREE(amap); | |
| 2094 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); | |
| 2095 LEPT_FREE(palette); | |
| 2096 | |
| 2097 pixcmapIsOpaque(cmap, &opaque); | |
| 2098 if (!opaque) /* alpha channel has some transparency; assume valid */ | |
| 2099 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, | |
| 2100 (int)ncolors, NULL); | |
| 2101 } | |
| 2102 | |
| 2103 /* 0.4545 is treated as the default by some image | |
| 2104 * display programs (not gqview). A value > 0.4545 will | |
| 2105 * lighten an image as displayed by xv, display, etc. */ | |
| 2106 if (gamma > 0.0) | |
| 2107 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); | |
| 2108 | |
| 2109 if ((text = pixGetText(pix))) { | |
| 2110 png_text text_chunk; | |
| 2111 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; | |
| 2112 text_chunk.key = commentstring; | |
| 2113 text_chunk.text = text; | |
| 2114 text_chunk.text_length = strlen(text); | |
| 2115 #ifdef PNG_ITXT_SUPPORTED | |
| 2116 text_chunk.itxt_length = 0; | |
| 2117 text_chunk.lang = NULL; | |
| 2118 text_chunk.lang_key = NULL; | |
| 2119 #endif | |
| 2120 png_set_text(png_ptr, info_ptr, &text_chunk, 1); | |
| 2121 } | |
| 2122 | |
| 2123 /* Write header and palette info */ | |
| 2124 png_write_info(png_ptr, info_ptr); | |
| 2125 | |
| 2126 if ((d != 32) && (d != 24)) { /* not rgb color */ | |
| 2127 /* Generate a temporary pix with bytes swapped. | |
| 2128 * For writing a 1 bpp image as png: | |
| 2129 * ~ if no colormap, invert the data, because png writes | |
| 2130 * black as 0 | |
| 2131 * ~ if colormapped, do not invert the data; the two RGBA | |
| 2132 * colors can have any value. */ | |
| 2133 if (d == 1 && !cmap) { | |
| 2134 pix1 = pixInvert(NULL, pix); | |
| 2135 pixEndianByteSwap(pix1); | |
| 2136 } else { | |
| 2137 pix1 = pixEndianByteSwapNew(pix); | |
| 2138 } | |
| 2139 if (!pix1) { | |
| 2140 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 2141 memio_free(&state); | |
| 2142 return ERROR_INT("pix1 not made", __func__, 1); | |
| 2143 } | |
| 2144 | |
| 2145 /* Transfer the data */ | |
| 2146 wpl = pixGetWpl(pix1); | |
| 2147 data = pixGetData(pix1); | |
| 2148 for (i = 0; i < h; i++) | |
| 2149 png_write_row(png_ptr, (png_bytep)(data + i * wpl)); | |
| 2150 png_write_end(png_ptr, info_ptr); | |
| 2151 | |
| 2152 pixDestroy(&pix1); | |
| 2153 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 2154 memio_png_flush(&state); | |
| 2155 *pfiledata = (l_uint8 *)state.m_Buffer; | |
| 2156 state.m_Buffer = 0; | |
| 2157 *pfilesize = state.m_Count; | |
| 2158 memio_free(&state); | |
| 2159 return 0; | |
| 2160 } | |
| 2161 | |
| 2162 /* For rgb and rgba, compose and write a row at a time */ | |
| 2163 data = pixGetData(pix); | |
| 2164 wpl = pixGetWpl(pix); | |
| 2165 if (d == 24) { /* See note 7 in pixWriteStreamPng() */ | |
| 2166 for (i = 0; i < h; i++) { | |
| 2167 ppixel = data + i * wpl; | |
| 2168 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); | |
| 2169 } | |
| 2170 } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */ | |
| 2171 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); | |
| 2172 for (i = 0; i < h; i++) { | |
| 2173 ppixel = data + i * wpl; | |
| 2174 for (j = k = 0; j < w; j++) { | |
| 2175 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); | |
| 2176 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); | |
| 2177 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); | |
| 2178 if (spp == 4) | |
| 2179 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); | |
| 2180 ppixel++; | |
| 2181 } | |
| 2182 | |
| 2183 png_write_rows(png_ptr, &rowbuffer, 1); | |
| 2184 } | |
| 2185 LEPT_FREE(rowbuffer); | |
| 2186 } | |
| 2187 png_write_end(png_ptr, info_ptr); | |
| 2188 | |
| 2189 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 2190 memio_png_flush(&state); | |
| 2191 *pfiledata = (l_uint8 *)state.m_Buffer; | |
| 2192 state.m_Buffer = 0; | |
| 2193 *pfilesize = state.m_Count; | |
| 2194 memio_free(&state); | |
| 2195 return 0; | |
| 2196 } | |
| 2197 | |
| 2198 /* --------------------------------------------*/ | |
| 2199 #endif /* HAVE_LIBPNG */ | |
| 2200 /* --------------------------------------------*/ |
