/src/leptonica/src/pngio.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
190 | 0 | l_uint8 byte; |
191 | 0 | l_int32 i, j, k, index, ncolors, rval, gval, bval, valid; |
192 | 0 | l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS; |
193 | 0 | l_uint32 png_transforms; |
194 | 0 | l_uint32 *data, *line, *ppixel; |
195 | 0 | int num_palette, num_text, num_trans; |
196 | 0 | png_byte bit_depth, color_type, channels; |
197 | 0 | png_uint_32 w, h, rowbytes, xres, yres; |
198 | 0 | png_bytep rowptr, trans; |
199 | 0 | png_bytep *row_pointers; |
200 | 0 | png_structp png_ptr; |
201 | 0 | png_infop info_ptr, end_info; |
202 | 0 | png_colorp palette; |
203 | 0 | png_textp text_ptr; /* ptr to text_chunk */ |
204 | 0 | PIX *pix, *pix1; |
205 | 0 | PIXCMAP *cmap; |
206 | |
|
207 | 0 | if (!fp) |
208 | 0 | return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); |
209 | 0 | pix = NULL; |
210 | | |
211 | | /* Allocate the 3 data structures */ |
212 | 0 | if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
213 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
214 | 0 | return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL); |
215 | | |
216 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
217 | 0 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); |
218 | 0 | return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL); |
219 | 0 | } |
220 | | |
221 | 0 | if ((end_info = png_create_info_struct(png_ptr)) == NULL) { |
222 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
223 | 0 | return (PIX *)ERROR_PTR("end_info not made", __func__, NULL); |
224 | 0 | } |
225 | | |
226 | | /* Set up png setjmp error handling */ |
227 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
228 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
229 | 0 | return (PIX *)ERROR_PTR("internal png error", __func__, NULL); |
230 | 0 | } |
231 | | |
232 | 0 | 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 | 0 | if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ |
243 | 0 | png_transforms = PNG_TRANSFORM_STRIP_16; |
244 | 0 | } else { |
245 | 0 | png_transforms = PNG_TRANSFORM_IDENTITY; |
246 | 0 | L_INFO("not stripping 16 --> 8 in png reading\n", __func__); |
247 | 0 | } |
248 | | |
249 | | /* Read it */ |
250 | 0 | png_read_png(png_ptr, info_ptr, png_transforms, NULL); |
251 | |
|
252 | 0 | row_pointers = png_get_rows(png_ptr, info_ptr); |
253 | 0 | w = png_get_image_width(png_ptr, info_ptr); |
254 | 0 | h = png_get_image_height(png_ptr, info_ptr); |
255 | 0 | bit_depth = png_get_bit_depth(png_ptr, info_ptr); |
256 | 0 | rowbytes = png_get_rowbytes(png_ptr, info_ptr); |
257 | 0 | color_type = png_get_color_type(png_ptr, info_ptr); |
258 | 0 | channels = png_get_channels(png_ptr, info_ptr); |
259 | 0 | spp = channels; |
260 | 0 | tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; |
261 | |
|
262 | 0 | if (spp == 1) { |
263 | 0 | d = bit_depth; |
264 | 0 | } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ |
265 | 0 | d = 4 * bit_depth; |
266 | 0 | } |
267 | | |
268 | | /* Remove if/when this is implemented for all bit_depths */ |
269 | 0 | if (spp != 1 && bit_depth != 8) { |
270 | 0 | L_ERROR("spp = %d and bps = %d != 8\n" |
271 | 0 | "turn on 16 --> 8 stripping\n", __func__, spp, bit_depth); |
272 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
273 | 0 | return (PIX *)ERROR_PTR("not implemented for this image", |
274 | 0 | __func__, NULL); |
275 | 0 | } |
276 | | |
277 | 0 | cmap = NULL; |
278 | 0 | if (color_type == PNG_COLOR_TYPE_PALETTE || |
279 | 0 | color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ |
280 | 0 | png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
281 | 0 | cmap = pixcmapCreate(d); /* spp == 1 */ |
282 | 0 | for (cindex = 0; cindex < num_palette; cindex++) { |
283 | 0 | rval = palette[cindex].red; |
284 | 0 | gval = palette[cindex].green; |
285 | 0 | bval = palette[cindex].blue; |
286 | 0 | pixcmapAddColor(cmap, rval, gval, bval); |
287 | 0 | } |
288 | 0 | } |
289 | |
|
290 | 0 | if ((pix = pixCreate(w, h, d)) == NULL) { |
291 | 0 | pixcmapDestroy(&cmap); |
292 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
293 | 0 | return (PIX *)ERROR_PTR("pix not made", __func__, NULL); |
294 | 0 | } |
295 | 0 | pixSetInputFormat(pix, IFF_PNG); |
296 | 0 | wpl = pixGetWpl(pix); |
297 | 0 | data = pixGetData(pix); |
298 | 0 | pixSetSpp(pix, spp); |
299 | 0 | if (pixSetColormap(pix, cmap)) { |
300 | 0 | pixDestroy(&pix); |
301 | 0 | return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); |
302 | 0 | } |
303 | | |
304 | 0 | if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ |
305 | 0 | for (i = 0; i < h; i++) { |
306 | 0 | line = data + i * wpl; |
307 | 0 | rowptr = row_pointers[i]; |
308 | 0 | for (j = 0; j < rowbytes; j++) { |
309 | 0 | SET_DATA_BYTE(line, j, rowptr[j]); |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ |
313 | 0 | L_INFO("converting (gray + alpha) ==> RGBA\n", __func__); |
314 | 0 | for (i = 0; i < h; i++) { |
315 | 0 | ppixel = data + i * wpl; |
316 | 0 | rowptr = row_pointers[i]; |
317 | 0 | for (j = k = 0; j < w; j++) { |
318 | | /* Copy gray value into r, g and b */ |
319 | 0 | SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); |
320 | 0 | SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); |
321 | 0 | SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); |
322 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); |
323 | 0 | ppixel++; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | pixSetSpp(pix, 4); /* we do not support 2 spp pix */ |
327 | 0 | } else if (spp == 3 || spp == 4) { |
328 | 0 | for (i = 0; i < h; i++) { |
329 | 0 | ppixel = data + i * wpl; |
330 | 0 | rowptr = row_pointers[i]; |
331 | 0 | for (j = k = 0; j < w; j++) { |
332 | 0 | SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); |
333 | 0 | SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); |
334 | 0 | SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); |
335 | 0 | if (spp == 3) /* set to opaque; some readers are buggy */ |
336 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255); |
337 | 0 | else /* spp == 4 */ |
338 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); |
339 | 0 | ppixel++; |
340 | 0 | } |
341 | 0 | } |
342 | 0 | } |
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 | 0 | if (spp == 1 && tRNS) { |
352 | 0 | if (!cmap) { |
353 | | /* Case 1: make fully transparent RGBA image */ |
354 | 0 | L_INFO("transparency, 1 spp, no colormap, no transparency array: " |
355 | 0 | "convention is fully transparent image\n", __func__); |
356 | 0 | L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__); |
357 | 0 | pixDestroy(&pix); |
358 | 0 | pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ |
359 | 0 | pixSetSpp(pix, 4); |
360 | 0 | } else { |
361 | 0 | L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__); |
362 | | |
363 | | /* Grab the transparency array */ |
364 | 0 | png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
365 | 0 | if (!trans) { /* invalid png file */ |
366 | 0 | pixDestroy(&pix); |
367 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
368 | 0 | return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", |
369 | 0 | __func__, NULL); |
370 | 0 | } |
371 | | |
372 | | /* Save the cmap and destroy the pix */ |
373 | 0 | cmap = pixcmapCopy(pixGetColormap(pix)); |
374 | 0 | ncolors = pixcmapGetCount(cmap); |
375 | 0 | pixDestroy(&pix); |
376 | | |
377 | | /* Start over with 32 bit RGBA */ |
378 | 0 | pix = pixCreate(w, h, 32); |
379 | 0 | wpl = pixGetWpl(pix); |
380 | 0 | data = pixGetData(pix); |
381 | 0 | 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 | 0 | if (d == 1) { |
400 | | /* Case 2: 1 bpp with transparency (usually) behind white */ |
401 | 0 | L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__); |
402 | 0 | if (num_trans == 1) |
403 | 0 | L_INFO("num_trans = 1; second color opaque by default\n", |
404 | 0 | __func__); |
405 | 0 | for (i = 0; i < h; i++) { |
406 | 0 | ppixel = data + i * wpl; |
407 | 0 | rowptr = row_pointers[i]; |
408 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
409 | 0 | byte = rowptr[j]; |
410 | 0 | for (k = 0; k < 8 && index < w; k++, index++) { |
411 | 0 | bitval = (byte >> (7 - k)) & 1; |
412 | 0 | pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); |
413 | 0 | composeRGBPixel(rval, gval, bval, ppixel); |
414 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
415 | 0 | bitval < num_trans ? trans[bitval] : 255); |
416 | 0 | ppixel++; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | 0 | } else if (d == 2) { |
421 | | /* Case 3: 2 bpp with cmap and associated transparency */ |
422 | 0 | L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__); |
423 | 0 | for (i = 0; i < h; i++) { |
424 | 0 | ppixel = data + i * wpl; |
425 | 0 | rowptr = row_pointers[i]; |
426 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
427 | 0 | byte = rowptr[j]; |
428 | 0 | for (k = 0; k < 4 && index < w; k++, index++) { |
429 | 0 | bival = (byte >> 2 * (3 - k)) & 3; |
430 | 0 | pixcmapGetColor(cmap, bival, &rval, &gval, &bval); |
431 | 0 | 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 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
436 | 0 | bival < num_trans ? trans[bival] : 255); |
437 | 0 | ppixel++; |
438 | 0 | } |
439 | 0 | } |
440 | 0 | } |
441 | 0 | } else if (d == 4) { |
442 | | /* Case 4: 4 bpp with cmap and associated transparency */ |
443 | 0 | L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__); |
444 | 0 | for (i = 0; i < h; i++) { |
445 | 0 | ppixel = data + i * wpl; |
446 | 0 | rowptr = row_pointers[i]; |
447 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
448 | 0 | byte = rowptr[j]; |
449 | 0 | for (k = 0; k < 2 && index < w; k++, index++) { |
450 | 0 | quadval = (byte >> 4 * (1 - k)) & 0xf; |
451 | 0 | pixcmapGetColor(cmap, quadval, &rval, &gval, &bval); |
452 | 0 | composeRGBPixel(rval, gval, bval, ppixel); |
453 | | /* Assume missing entries to be 255 (opaque) */ |
454 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
455 | 0 | quadval < num_trans ? trans[quadval] : 255); |
456 | 0 | ppixel++; |
457 | 0 | } |
458 | 0 | } |
459 | 0 | } |
460 | 0 | } else if (d == 8) { |
461 | | /* Case 5: 8 bpp with cmap and associated transparency */ |
462 | 0 | L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__); |
463 | 0 | for (i = 0; i < h; i++) { |
464 | 0 | ppixel = data + i * wpl; |
465 | 0 | rowptr = row_pointers[i]; |
466 | 0 | for (j = 0; j < w; j++) { |
467 | 0 | index = rowptr[j]; |
468 | 0 | pixcmapGetColor(cmap, index, &rval, &gval, &bval); |
469 | 0 | composeRGBPixel(rval, gval, bval, ppixel); |
470 | | /* Assume missing entries to be 255 (opaque) */ |
471 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
472 | 0 | index < num_trans ? trans[index] : 255); |
473 | 0 | ppixel++; |
474 | 0 | } |
475 | 0 | } |
476 | 0 | } else { |
477 | 0 | L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", |
478 | 0 | __func__, d); |
479 | 0 | } |
480 | 0 | pixcmapDestroy(&cmap); |
481 | 0 | } |
482 | 0 | } |
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 | 0 | if (pixGetDepth(pix) == 1) { |
515 | 0 | if (!cmap) { |
516 | 0 | pixInvert(pix, pix); |
517 | 0 | } else { |
518 | 0 | L_INFO("removing opaque cmap from 1 bpp\n", __func__); |
519 | 0 | pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
520 | 0 | pixDestroy(&pix); |
521 | 0 | pix = pix1; |
522 | 0 | } |
523 | 0 | } |
524 | |
|
525 | 0 | xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); |
526 | 0 | yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); |
527 | 0 | pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ |
528 | 0 | pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ |
529 | | |
530 | | /* Get the text if there is any */ |
531 | 0 | png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); |
532 | 0 | if (num_text && text_ptr) |
533 | 0 | pixSetText(pix, text_ptr->text); |
534 | |
|
535 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
536 | | |
537 | | /* Final validity check on the colormap */ |
538 | 0 | if ((cmap = pixGetColormap(pix)) != NULL) { |
539 | 0 | pixcmapIsValid(cmap, pix, &valid); |
540 | 0 | if (!valid) { |
541 | 0 | pixDestroy(&pix); |
542 | 0 | return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL); |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | 0 | pixSetPadBits(pix, 0); |
547 | 0 | return pix; |
548 | 0 | } |
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 | 0 | { |
581 | 0 | l_int32 ret; |
582 | 0 | FILE *fp; |
583 | |
|
584 | 0 | if (pw) *pw = 0; |
585 | 0 | if (ph) *ph = 0; |
586 | 0 | if (pbps) *pbps = 0; |
587 | 0 | if (pspp) *pspp = 0; |
588 | 0 | if (piscmap) *piscmap = 0; |
589 | 0 | if (!filename) |
590 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
591 | 0 | if ((fp = fopenReadStream(filename)) == NULL) |
592 | 0 | return ERROR_INT_1("image file not found", filename, __func__, 1); |
593 | 0 | ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap); |
594 | 0 | fclose(fp); |
595 | 0 | return ret; |
596 | 0 | } |
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 | 0 | { |
623 | 0 | l_int32 nbytes, ret; |
624 | 0 | l_uint8 data[40]; |
625 | |
|
626 | 0 | if (pw) *pw = 0; |
627 | 0 | if (ph) *ph = 0; |
628 | 0 | if (pbps) *pbps = 0; |
629 | 0 | if (pspp) *pspp = 0; |
630 | 0 | if (piscmap) *piscmap = 0; |
631 | 0 | if (!fp) |
632 | 0 | return ERROR_INT("stream not defined", __func__, 1); |
633 | | |
634 | 0 | nbytes = fnbytesInFile(fp); |
635 | 0 | if (nbytes < 40) |
636 | 0 | return ERROR_INT("file too small to be png", __func__, 1); |
637 | 0 | if (fread(data, 1, 40, fp) != 40) |
638 | 0 | return ERROR_INT("error reading data", __func__, 1); |
639 | 0 | ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap); |
640 | 0 | return ret; |
641 | 0 | } |
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 | 0 | { |
679 | 0 | l_uint16 twobytes; |
680 | 0 | l_uint16 *pshort; |
681 | 0 | l_int32 colortype, w, h, bps, spp; |
682 | 0 | l_uint32 *pword; |
683 | |
|
684 | 0 | if (pw) *pw = 0; |
685 | 0 | if (ph) *ph = 0; |
686 | 0 | if (pbps) *pbps = 0; |
687 | 0 | if (pspp) *pspp = 0; |
688 | 0 | if (piscmap) *piscmap = 0; |
689 | 0 | if (!data) |
690 | 0 | return ERROR_INT("data not defined", __func__, 1); |
691 | 0 | if (size < 40) |
692 | 0 | return ERROR_INT("size < 40", __func__, 1); |
693 | | |
694 | | /* Check password */ |
695 | 0 | if (data[0] != 137 || data[1] != 80 || data[2] != 78 || |
696 | 0 | data[3] != 71 || data[4] != 13 || data[5] != 10 || |
697 | 0 | data[6] != 26 || data[7] != 10) |
698 | 0 | return ERROR_INT("not a valid png file", __func__, 1); |
699 | | |
700 | 0 | pword = (l_uint32 *)data; |
701 | 0 | pshort = (l_uint16 *)data; |
702 | 0 | w = convertOnLittleEnd32(pword[4]); |
703 | 0 | h = convertOnLittleEnd32(pword[5]); |
704 | 0 | if (w < 1 || h < 1) |
705 | 0 | return ERROR_INT("invalid w or h", __func__, 1); |
706 | 0 | twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */ |
707 | | /* and the color type */ |
708 | 0 | colortype = twobytes & 0xff; /* color type */ |
709 | 0 | 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 | 0 | if (colortype == 4) |
716 | 0 | L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", __func__); |
717 | |
|
718 | 0 | if (colortype == 2) { /* RGB */ |
719 | 0 | spp = 3; |
720 | 0 | } else if (colortype == 6) { /* RGBA */ |
721 | 0 | spp = 4; |
722 | 0 | } else if (colortype == 4) { /* gray + alpha */ |
723 | 0 | spp = 2; |
724 | 0 | bps = 8; /* both the gray and alpha are 8-bit samples */ |
725 | 0 | } else { /* gray (0) or cmap (3) or cmap+alpha (3) */ |
726 | 0 | spp = 1; |
727 | 0 | } |
728 | 0 | if (bps < 1 || bps > 16) { |
729 | 0 | L_ERROR("invalid bps = %d\n", __func__, bps); |
730 | 0 | return 1; |
731 | 0 | } |
732 | 0 | if (pw) *pw = w; |
733 | 0 | if (ph) *ph = h; |
734 | 0 | if (pbps) *pbps = bps; |
735 | 0 | if (pspp) *pspp = spp; |
736 | 0 | if (piscmap) { |
737 | 0 | if (colortype & 1) /* palette */ |
738 | 0 | *piscmap = 1; |
739 | 0 | else |
740 | 0 | *piscmap = 0; |
741 | 0 | } |
742 | |
|
743 | 0 | return 0; |
744 | 0 | } |
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 | 0 | { |
767 | 0 | png_uint_32 xres, yres; |
768 | 0 | png_structp png_ptr; |
769 | 0 | png_infop info_ptr; |
770 | |
|
771 | 0 | if (pxres) *pxres = 0; |
772 | 0 | if (pyres) *pyres = 0; |
773 | 0 | if (!fp) |
774 | 0 | return ERROR_INT("stream not opened", __func__, 1); |
775 | 0 | if (!pxres || !pyres) |
776 | 0 | return ERROR_INT("&xres and &yres not both defined", __func__, 1); |
777 | | |
778 | | /* Make the two required structs */ |
779 | 0 | if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
780 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
781 | 0 | return ERROR_INT("png_ptr not made", __func__, 1); |
782 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
783 | 0 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); |
784 | 0 | return ERROR_INT("info_ptr not made", __func__, 1); |
785 | 0 | } |
786 | | |
787 | | /* Set up png setjmp error handling. |
788 | | * Without this, an error calls exit. */ |
789 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
790 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
791 | 0 | return ERROR_INT("internal png error", __func__, 1); |
792 | 0 | } |
793 | | |
794 | | /* Read the metadata */ |
795 | 0 | rewind(fp); |
796 | 0 | png_init_io(png_ptr, fp); |
797 | 0 | png_read_info(png_ptr, info_ptr); |
798 | |
|
799 | 0 | xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); |
800 | 0 | yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); |
801 | 0 | *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */ |
802 | 0 | *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5); |
803 | |
|
804 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
805 | 0 | rewind(fp); |
806 | 0 | return 0; |
807 | 0 | } |
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 | 0 | { |
821 | 0 | l_uint8 buf[32]; |
822 | 0 | FILE *fp; |
823 | |
|
824 | 0 | if (!pinterlaced) |
825 | 0 | return ERROR_INT("&interlaced not defined", __func__, 1); |
826 | 0 | *pinterlaced = 0; |
827 | 0 | if (!filename) |
828 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
829 | | |
830 | 0 | if ((fp = fopenReadStream(filename)) == NULL) |
831 | 0 | return ERROR_INT_1("stream not opened", filename, __func__, 1); |
832 | 0 | if (fread(buf, 1, 32, fp) != 32) { |
833 | 0 | fclose(fp); |
834 | 0 | return ERROR_INT_1("data not read", filename, __func__, 1); |
835 | 0 | } |
836 | 0 | fclose(fp); |
837 | |
|
838 | 0 | *pinterlaced = (buf[28] == 0) ? 0 : 1; |
839 | 0 | return 0; |
840 | 0 | } |
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 | 0 | { |
864 | 0 | l_int32 i, cindex, rval, gval, bval, num_palette, num_trans; |
865 | 0 | png_byte bit_depth, color_type; |
866 | 0 | png_bytep trans; |
867 | 0 | png_colorp palette; |
868 | 0 | png_structp png_ptr; |
869 | 0 | png_infop info_ptr; |
870 | |
|
871 | 0 | if (pcmap) *pcmap = NULL; |
872 | 0 | if (ptransparency) *ptransparency = 0; |
873 | 0 | if (!pcmap && !ptransparency) |
874 | 0 | return ERROR_INT("no output defined", __func__, 1); |
875 | 0 | if (!fp) |
876 | 0 | return ERROR_INT("stream not opened", __func__, 1); |
877 | | |
878 | | /* Make the two required structs */ |
879 | 0 | if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
880 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
881 | 0 | return ERROR_INT("png_ptr not made", __func__, 1); |
882 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
883 | 0 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); |
884 | 0 | return ERROR_INT("info_ptr not made", __func__, 1); |
885 | 0 | } |
886 | | |
887 | | /* Set up png setjmp error handling. |
888 | | * Without this, an error calls exit. */ |
889 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
890 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
891 | 0 | if (pcmap && *pcmap) pixcmapDestroy(pcmap); |
892 | 0 | return ERROR_INT("internal png error", __func__, 1); |
893 | 0 | } |
894 | | |
895 | | /* Read the metadata and check if there is a colormap */ |
896 | 0 | rewind(fp); |
897 | 0 | png_init_io(png_ptr, fp); |
898 | 0 | png_read_info(png_ptr, info_ptr); |
899 | 0 | color_type = png_get_color_type(png_ptr, info_ptr); |
900 | 0 | if (color_type != PNG_COLOR_TYPE_PALETTE && |
901 | 0 | color_type != PNG_COLOR_MASK_PALETTE) { |
902 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
903 | 0 | return 0; |
904 | 0 | } |
905 | | |
906 | | /* Optionally, read the colormap */ |
907 | 0 | if (pcmap) { |
908 | 0 | bit_depth = png_get_bit_depth(png_ptr, info_ptr); |
909 | 0 | png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
910 | 0 | *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */ |
911 | 0 | for (cindex = 0; cindex < num_palette; cindex++) { |
912 | 0 | rval = palette[cindex].red; |
913 | 0 | gval = palette[cindex].green; |
914 | 0 | bval = palette[cindex].blue; |
915 | 0 | pixcmapAddColor(*pcmap, rval, gval, bval); |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | | /* Optionally, look for transparency. Note that the colormap |
920 | | * has been initialized to fully opaque. */ |
921 | 0 | if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
922 | 0 | png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
923 | 0 | if (trans) { |
924 | 0 | for (i = 0; i < num_trans; i++) { |
925 | 0 | if (trans[i] < 255) { /* not fully opaque */ |
926 | 0 | *ptransparency = 1; |
927 | 0 | if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]); |
928 | 0 | } |
929 | 0 | } |
930 | 0 | } else { |
931 | 0 | L_ERROR("transparency array not returned\n", __func__); |
932 | 0 | } |
933 | 0 | } |
934 | |
|
935 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
936 | 0 | rewind(fp); |
937 | 0 | return 0; |
938 | 0 | } |
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 | 0 | { |
963 | 0 | FILE *fp; |
964 | |
|
965 | 0 | if (!pix) |
966 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
967 | 0 | if (!filename) |
968 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
969 | | |
970 | 0 | if ((fp = fopenWriteStream(filename, "wb+")) == NULL) |
971 | 0 | return ERROR_INT_1("stream not opened", filename, __func__, 1); |
972 | | |
973 | 0 | if (pixWriteStreamPng(fp, pix, gamma)) { |
974 | 0 | fclose(fp); |
975 | 0 | return ERROR_INT_1("pix not written to stream", filename, __func__, 1); |
976 | 0 | } |
977 | | |
978 | 0 | fclose(fp); |
979 | 0 | return 0; |
980 | 0 | } |
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 | 0 | { |
1056 | 0 | char commentstring[] = "Comment"; |
1057 | 0 | l_int32 i, j, k, wpl, d, spp, compval, valid; |
1058 | 0 | l_int32 cmflag, opaque, max_trans, ncolors; |
1059 | 0 | l_int32 *rmap, *gmap, *bmap, *amap; |
1060 | 0 | l_uint32 *data, *ppixel; |
1061 | 0 | png_byte bit_depth, color_type; |
1062 | 0 | png_byte alpha[256]; |
1063 | 0 | png_uint_32 w, h; |
1064 | 0 | png_uint_32 xres, yres; |
1065 | 0 | png_bytep *row_pointers; |
1066 | 0 | png_bytep rowbuffer; |
1067 | 0 | png_structp png_ptr; |
1068 | 0 | png_infop info_ptr; |
1069 | 0 | png_colorp palette; |
1070 | 0 | PIX *pix1; |
1071 | 0 | PIXCMAP *cmap; |
1072 | 0 | char *text; |
1073 | |
|
1074 | 0 | if (!fp) |
1075 | 0 | return ERROR_INT("stream not open", __func__, 1); |
1076 | 0 | if (!pix) |
1077 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
1078 | | |
1079 | 0 | w = pixGetWidth(pix); |
1080 | 0 | h = pixGetHeight(pix); |
1081 | 0 | d = pixGetDepth(pix); |
1082 | 0 | spp = pixGetSpp(pix); |
1083 | | |
1084 | | /* A cmap validity check should prevent low-level colormap errors. */ |
1085 | 0 | if ((cmap = pixGetColormap(pix))) { |
1086 | 0 | cmflag = 1; |
1087 | 0 | pixcmapIsValid(cmap, pix, &valid); |
1088 | 0 | if (!valid) |
1089 | 0 | return ERROR_INT("colormap is not valid", __func__, 1); |
1090 | 0 | } else { |
1091 | 0 | cmflag = 0; |
1092 | 0 | } |
1093 | | |
1094 | | /* Do not set pad bits for d = 24 ! */ |
1095 | 0 | if (d != 24) pixSetPadBits(pix, 0); |
1096 | | |
1097 | | /* Set the color type and bit depth. */ |
1098 | 0 | if (d == 32 && spp == 4) { |
1099 | 0 | bit_depth = 8; |
1100 | 0 | color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ |
1101 | 0 | cmflag = 0; /* ignore if it exists */ |
1102 | 0 | } else if (d == 24 || d == 32) { |
1103 | 0 | bit_depth = 8; |
1104 | 0 | color_type = PNG_COLOR_TYPE_RGB; /* 2 */ |
1105 | 0 | cmflag = 0; /* ignore if it exists */ |
1106 | 0 | } else { |
1107 | 0 | bit_depth = d; |
1108 | 0 | color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ |
1109 | 0 | } |
1110 | 0 | if (cmflag) |
1111 | 0 | 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 | 0 | if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
1120 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
1121 | 0 | return ERROR_INT("png_ptr not made", __func__, 1); |
1122 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
1123 | 0 | png_destroy_write_struct(&png_ptr, (png_infopp)NULL); |
1124 | 0 | return ERROR_INT("info_ptr not made", __func__, 1); |
1125 | 0 | } |
1126 | | |
1127 | | /* Set up png setjmp error handling */ |
1128 | 0 | pix1 = NULL; |
1129 | 0 | row_pointers = NULL; |
1130 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
1131 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
1132 | 0 | LEPT_FREE(row_pointers); |
1133 | 0 | pixDestroy(&pix1); |
1134 | 0 | return ERROR_INT("internal png error", __func__, 1); |
1135 | 0 | } |
1136 | | |
1137 | 0 | 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 | 0 | compval = Z_DEFAULT_COMPRESSION; |
1145 | 0 | if (pix->special >= 10 && pix->special < 20) |
1146 | 0 | compval = pix->special - 10; |
1147 | 0 | png_set_compression_level(png_ptr, compval); |
1148 | |
|
1149 | 0 | png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, |
1150 | 0 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, |
1151 | 0 | PNG_FILTER_TYPE_BASE); |
1152 | | |
1153 | | /* Store resolution in ppm, if known */ |
1154 | 0 | xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); |
1155 | 0 | yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); |
1156 | 0 | if ((xres == 0) || (yres == 0)) |
1157 | 0 | png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); |
1158 | 0 | else |
1159 | 0 | png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); |
1160 | |
|
1161 | 0 | if (cmflag) { |
1162 | | /* Make and save the palette */ |
1163 | 0 | ncolors = pixcmapGetCount(cmap); |
1164 | 0 | palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); |
1165 | 0 | pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); |
1166 | 0 | for (i = 0; i < ncolors; i++) { |
1167 | 0 | palette[i].red = (png_byte)rmap[i]; |
1168 | 0 | palette[i].green = (png_byte)gmap[i]; |
1169 | 0 | palette[i].blue = (png_byte)bmap[i]; |
1170 | 0 | alpha[i] = (png_byte)amap[i]; |
1171 | 0 | } |
1172 | 0 | LEPT_FREE(rmap); |
1173 | 0 | LEPT_FREE(gmap); |
1174 | 0 | LEPT_FREE(bmap); |
1175 | 0 | LEPT_FREE(amap); |
1176 | 0 | png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); |
1177 | 0 | 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 | 0 | pixcmapIsOpaque(cmap, &opaque); |
1185 | 0 | if (!opaque) { /* alpha channel has some transparency; assume valid */ |
1186 | 0 | pixcmapNonOpaqueColorsInfo(cmap, NULL, &max_trans, NULL); |
1187 | 0 | png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, |
1188 | 0 | max_trans + 1, NULL); |
1189 | 0 | } |
1190 | 0 | } |
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 | 0 | if (gamma > 0.0) |
1196 | 0 | png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); |
1197 | |
|
1198 | 0 | if ((text = pixGetText(pix))) { |
1199 | 0 | png_text text_chunk; |
1200 | 0 | text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; |
1201 | 0 | text_chunk.key = commentstring; |
1202 | 0 | text_chunk.text = text; |
1203 | 0 | 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 | 0 | png_set_text(png_ptr, info_ptr, &text_chunk, 1); |
1210 | 0 | } |
1211 | | |
1212 | | /* Write header and palette info */ |
1213 | 0 | png_write_info(png_ptr, info_ptr); |
1214 | |
|
1215 | 0 | 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 | 0 | if (d == 1 && !cmap) { |
1223 | 0 | pix1 = pixInvert(NULL, pix); |
1224 | 0 | pixEndianByteSwap(pix1); |
1225 | 0 | } else { |
1226 | 0 | pix1 = pixEndianByteSwapNew(pix); |
1227 | 0 | } |
1228 | 0 | if (!pix1) { |
1229 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
1230 | 0 | return ERROR_INT("pix1 not made", __func__, 1); |
1231 | 0 | } |
1232 | | |
1233 | | /* Make and assign array of image row pointers */ |
1234 | 0 | row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); |
1235 | 0 | wpl = pixGetWpl(pix1); |
1236 | 0 | data = pixGetData(pix1); |
1237 | 0 | for (i = 0; i < h; i++) |
1238 | 0 | row_pointers[i] = (png_bytep)(data + i * wpl); |
1239 | 0 | png_set_rows(png_ptr, info_ptr, row_pointers); |
1240 | | |
1241 | | /* Transfer the data */ |
1242 | 0 | png_write_image(png_ptr, row_pointers); |
1243 | 0 | png_write_end(png_ptr, info_ptr); |
1244 | 0 | LEPT_FREE(row_pointers); |
1245 | 0 | pixDestroy(&pix1); |
1246 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
1247 | 0 | return 0; |
1248 | 0 | } |
1249 | | |
1250 | | /* For rgb and rgba, compose and write a row at a time */ |
1251 | 0 | data = pixGetData(pix); |
1252 | 0 | wpl = pixGetWpl(pix); |
1253 | 0 | if (d == 24) { /* See note 7 above */ |
1254 | 0 | for (i = 0; i < h; i++) { |
1255 | 0 | ppixel = data + i * wpl; |
1256 | 0 | png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); |
1257 | 0 | } |
1258 | 0 | } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */ |
1259 | 0 | rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); |
1260 | 0 | for (i = 0; i < h; i++) { |
1261 | 0 | ppixel = data + i * wpl; |
1262 | 0 | for (j = k = 0; j < w; j++) { |
1263 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); |
1264 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); |
1265 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); |
1266 | 0 | if (spp == 4) |
1267 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); |
1268 | 0 | ppixel++; |
1269 | 0 | } |
1270 | |
|
1271 | 0 | png_write_rows(png_ptr, &rowbuffer, 1); |
1272 | 0 | } |
1273 | 0 | LEPT_FREE(rowbuffer); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | png_write_end(png_ptr, info_ptr); |
1277 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
1278 | 0 | return 0; |
1279 | 0 | } |
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 | 0 | { |
1307 | 0 | if (!pix) |
1308 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
1309 | 0 | if (compval < 0 || compval > 9) { |
1310 | 0 | L_ERROR("Invalid zlib comp val; using default\n", __func__); |
1311 | 0 | compval = Z_DEFAULT_COMPRESSION; |
1312 | 0 | } |
1313 | 0 | pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */ |
1314 | 0 | return 0; |
1315 | 0 | } |
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 | 0 | { |
1331 | 0 | var_PNG_STRIP_16_TO_8 = flag; |
1332 | 0 | } |
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 | 0 | { |
1385 | 0 | MEMIODATA *thing, *last; |
1386 | 0 | l_int32 written = 0; |
1387 | 0 | l_int32 remainingSpace, remainingToWrite; |
1388 | |
|
1389 | 0 | thing = (struct MemIOData*)png_get_io_ptr(png_ptr); |
1390 | 0 | last = (struct MemIOData*)thing->m_Last; |
1391 | 0 | if (last->m_Buffer == NULL) { |
1392 | 0 | if (len > MEMIO_BUFFER_SIZE) { |
1393 | 0 | last->m_Buffer = (char *)LEPT_MALLOC(len); |
1394 | 0 | memcpy(last->m_Buffer, data, len); |
1395 | 0 | last->m_Size = last->m_Count = len; |
1396 | 0 | return; |
1397 | 0 | } |
1398 | | |
1399 | 0 | last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); |
1400 | 0 | last->m_Size = MEMIO_BUFFER_SIZE; |
1401 | 0 | } |
1402 | | |
1403 | 0 | while (written < len) { |
1404 | 0 | if (last->m_Count == last->m_Size) { |
1405 | 0 | MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA)); |
1406 | 0 | next->m_Next = NULL; |
1407 | 0 | next->m_Count = 0; |
1408 | 0 | next->m_Last = next; |
1409 | |
|
1410 | 0 | last->m_Next = next; |
1411 | 0 | last = thing->m_Last = next; |
1412 | |
|
1413 | 0 | last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); |
1414 | 0 | last->m_Size = MEMIO_BUFFER_SIZE; |
1415 | 0 | } |
1416 | |
|
1417 | 0 | remainingSpace = last->m_Size - last->m_Count; |
1418 | 0 | remainingToWrite = len - written; |
1419 | 0 | if (remainingSpace < remainingToWrite) { |
1420 | 0 | memcpy(last->m_Buffer + last->m_Count, data + written, |
1421 | 0 | remainingSpace); |
1422 | 0 | written += remainingSpace; |
1423 | 0 | last->m_Count += remainingSpace; |
1424 | 0 | } else { |
1425 | 0 | memcpy(last->m_Buffer + last->m_Count, data + written, |
1426 | 0 | remainingToWrite); |
1427 | 0 | written += remainingToWrite; |
1428 | 0 | last->m_Count += remainingToWrite; |
1429 | 0 | } |
1430 | 0 | } |
1431 | 0 | } |
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 | 0 | { |
1448 | 0 | l_int32 amount = 0; |
1449 | 0 | l_int32 copied = 0; |
1450 | 0 | MEMIODATA *buffer = 0; |
1451 | 0 | char *data = 0; |
1452 | | |
1453 | | /* If the data is in one buffer, give the buffer to the user. */ |
1454 | 0 | if (pthing->m_Next == NULL) return; |
1455 | | |
1456 | | /* Consolidate multiple buffers into one new one; add the buffer |
1457 | | * sizes together. */ |
1458 | 0 | amount = pthing->m_Count; |
1459 | 0 | buffer = pthing->m_Next; |
1460 | 0 | while (buffer != NULL) { |
1461 | 0 | amount += buffer->m_Count; |
1462 | 0 | buffer = buffer->m_Next; |
1463 | 0 | } |
1464 | | |
1465 | | /* Copy data to a new buffer. */ |
1466 | 0 | data = (char *)LEPT_MALLOC(amount); |
1467 | 0 | memcpy(data, pthing->m_Buffer, pthing->m_Count); |
1468 | 0 | copied = pthing->m_Count; |
1469 | |
|
1470 | 0 | LEPT_FREE(pthing->m_Buffer); |
1471 | 0 | pthing->m_Buffer = NULL; |
1472 | | |
1473 | | /* Don't delete original "thing" because we don't control it. */ |
1474 | 0 | buffer = pthing->m_Next; |
1475 | 0 | pthing->m_Next = NULL; |
1476 | 0 | while (buffer != NULL && copied < amount) { |
1477 | 0 | MEMIODATA* old; |
1478 | 0 | memcpy(data + copied, buffer->m_Buffer, buffer->m_Count); |
1479 | 0 | copied += buffer->m_Count; |
1480 | |
|
1481 | 0 | old = buffer; |
1482 | 0 | buffer = buffer->m_Next; |
1483 | |
|
1484 | 0 | LEPT_FREE(old->m_Buffer); |
1485 | 0 | LEPT_FREE(old); |
1486 | 0 | } |
1487 | |
|
1488 | 0 | pthing->m_Buffer = data; |
1489 | 0 | pthing->m_Count = copied; |
1490 | 0 | pthing->m_Size = amount; |
1491 | 0 | return; |
1492 | 0 | } |
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 | 0 | { |
1513 | 0 | MEMIODATA *thing; |
1514 | |
|
1515 | 0 | thing = (MEMIODATA *)png_get_io_ptr(png_ptr); |
1516 | 0 | if (byteCountToRead > (thing->m_Size - thing->m_Count)) { |
1517 | 0 | png_error(png_ptr, "read error in memio_png_read_data"); |
1518 | 0 | } |
1519 | 0 | memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead); |
1520 | 0 | thing->m_Count += byteCountToRead; |
1521 | 0 | } |
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 | 0 | { |
1538 | 0 | MEMIODATA *buffer, *old; |
1539 | |
|
1540 | 0 | if (pthing->m_Buffer != NULL) |
1541 | 0 | LEPT_FREE(pthing->m_Buffer); |
1542 | |
|
1543 | 0 | pthing->m_Buffer = NULL; |
1544 | 0 | buffer = pthing->m_Next; |
1545 | 0 | while (buffer != NULL) { |
1546 | 0 | old = buffer; |
1547 | 0 | buffer = buffer->m_Next; |
1548 | |
|
1549 | 0 | if (old->m_Buffer != NULL) |
1550 | 0 | LEPT_FREE(old->m_Buffer); |
1551 | 0 | LEPT_FREE(old); |
1552 | 0 | } |
1553 | 0 | } |
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 | 0 | { |
1575 | 0 | l_uint8 byte; |
1576 | 0 | l_int32 i, j, k, index, ncolors, rval, gval, bval, valid; |
1577 | 0 | l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS; |
1578 | 0 | l_uint32 png_transforms; |
1579 | 0 | l_uint32 *data, *line, *ppixel; |
1580 | 0 | int num_palette, num_text, num_trans; |
1581 | 0 | png_byte bit_depth, color_type, channels; |
1582 | 0 | png_uint_32 w, h, rowbytes, xres, yres; |
1583 | 0 | png_bytep rowptr, trans; |
1584 | 0 | png_bytep *row_pointers; |
1585 | 0 | png_structp png_ptr; |
1586 | 0 | png_infop info_ptr, end_info; |
1587 | 0 | png_colorp palette; |
1588 | 0 | png_textp text_ptr; /* ptr to text_chunk */ |
1589 | 0 | MEMIODATA state; |
1590 | 0 | PIX *pix, *pix1; |
1591 | 0 | PIXCMAP *cmap; |
1592 | |
|
1593 | 0 | if (!filedata) |
1594 | 0 | return (PIX *)ERROR_PTR("filedata not defined", __func__, NULL); |
1595 | 0 | if (filesize < 1) |
1596 | 0 | return (PIX *)ERROR_PTR("invalid filesize", __func__, NULL); |
1597 | | |
1598 | 0 | state.m_Next = 0; |
1599 | 0 | state.m_Count = 0; |
1600 | 0 | state.m_Last = &state; |
1601 | 0 | state.m_Buffer = (char*)filedata; |
1602 | 0 | state.m_Size = filesize; |
1603 | 0 | pix = NULL; |
1604 | | |
1605 | | /* Allocate the 3 data structures */ |
1606 | 0 | if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
1607 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
1608 | 0 | return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL); |
1609 | | |
1610 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
1611 | 0 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); |
1612 | 0 | return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL); |
1613 | 0 | } |
1614 | | |
1615 | 0 | if ((end_info = png_create_info_struct(png_ptr)) == NULL) { |
1616 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
1617 | 0 | return (PIX *)ERROR_PTR("end_info not made", __func__, NULL); |
1618 | 0 | } |
1619 | | |
1620 | | /* Set up png setjmp error handling */ |
1621 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
1622 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
1623 | 0 | return (PIX *)ERROR_PTR("internal png error", __func__, NULL); |
1624 | 0 | } |
1625 | | |
1626 | 0 | 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 | 0 | if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ |
1636 | 0 | png_transforms = PNG_TRANSFORM_STRIP_16; |
1637 | 0 | } else { |
1638 | 0 | png_transforms = PNG_TRANSFORM_IDENTITY; |
1639 | 0 | L_INFO("not stripping 16 --> 8 in png reading\n", __func__); |
1640 | 0 | } |
1641 | | |
1642 | | /* Read it */ |
1643 | 0 | png_read_png(png_ptr, info_ptr, png_transforms, NULL); |
1644 | |
|
1645 | 0 | row_pointers = png_get_rows(png_ptr, info_ptr); |
1646 | 0 | w = png_get_image_width(png_ptr, info_ptr); |
1647 | 0 | h = png_get_image_height(png_ptr, info_ptr); |
1648 | 0 | bit_depth = png_get_bit_depth(png_ptr, info_ptr); |
1649 | 0 | rowbytes = png_get_rowbytes(png_ptr, info_ptr); |
1650 | 0 | color_type = png_get_color_type(png_ptr, info_ptr); |
1651 | 0 | channels = png_get_channels(png_ptr, info_ptr); |
1652 | 0 | spp = channels; |
1653 | 0 | tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; |
1654 | |
|
1655 | 0 | if (spp == 1) { |
1656 | 0 | d = bit_depth; |
1657 | 0 | } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ |
1658 | 0 | d = 4 * bit_depth; |
1659 | 0 | } |
1660 | | |
1661 | | /* Remove if/when this is implemented for all bit_depths */ |
1662 | 0 | if (spp == 3 && bit_depth != 8) { |
1663 | 0 | lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth); |
1664 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
1665 | 0 | return (PIX *)ERROR_PTR("not implemented for this depth", |
1666 | 0 | __func__, NULL); |
1667 | 0 | } |
1668 | | |
1669 | 0 | cmap = NULL; |
1670 | 0 | if (color_type == PNG_COLOR_TYPE_PALETTE || |
1671 | 0 | color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ |
1672 | 0 | png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
1673 | 0 | cmap = pixcmapCreate(d); /* spp == 1 */ |
1674 | 0 | for (cindex = 0; cindex < num_palette; cindex++) { |
1675 | 0 | rval = palette[cindex].red; |
1676 | 0 | gval = palette[cindex].green; |
1677 | 0 | bval = palette[cindex].blue; |
1678 | 0 | pixcmapAddColor(cmap, rval, gval, bval); |
1679 | 0 | } |
1680 | 0 | } |
1681 | |
|
1682 | 0 | if ((pix = pixCreate(w, h, d)) == NULL) { |
1683 | 0 | pixcmapDestroy(&cmap); |
1684 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
1685 | 0 | pixcmapDestroy(&cmap); |
1686 | 0 | return (PIX *)ERROR_PTR("pix not made", __func__, NULL); |
1687 | 0 | } |
1688 | 0 | pixSetInputFormat(pix, IFF_PNG); |
1689 | 0 | wpl = pixGetWpl(pix); |
1690 | 0 | data = pixGetData(pix); |
1691 | 0 | pixSetSpp(pix, spp); |
1692 | 0 | if (pixSetColormap(pix, cmap)) { |
1693 | 0 | pixDestroy(&pix); |
1694 | 0 | return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); |
1695 | 0 | } |
1696 | | |
1697 | 0 | if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ |
1698 | 0 | for (i = 0; i < h; i++) { |
1699 | 0 | line = data + i * wpl; |
1700 | 0 | rowptr = row_pointers[i]; |
1701 | 0 | for (j = 0; j < rowbytes; j++) { |
1702 | 0 | SET_DATA_BYTE(line, j, rowptr[j]); |
1703 | 0 | } |
1704 | 0 | } |
1705 | 0 | } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ |
1706 | 0 | L_INFO("converting (gray + alpha) ==> RGBA\n", __func__); |
1707 | 0 | for (i = 0; i < h; i++) { |
1708 | 0 | ppixel = data + i * wpl; |
1709 | 0 | rowptr = row_pointers[i]; |
1710 | 0 | for (j = k = 0; j < w; j++) { |
1711 | | /* Copy gray value into r, g and b */ |
1712 | 0 | SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); |
1713 | 0 | SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); |
1714 | 0 | SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); |
1715 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); |
1716 | 0 | ppixel++; |
1717 | 0 | } |
1718 | 0 | } |
1719 | 0 | pixSetSpp(pix, 4); /* we do not support 2 spp pix */ |
1720 | 0 | } else if (spp == 3 || spp == 4) { |
1721 | 0 | for (i = 0; i < h; i++) { |
1722 | 0 | ppixel = data + i * wpl; |
1723 | 0 | rowptr = row_pointers[i]; |
1724 | 0 | for (j = k = 0; j < w; j++) { |
1725 | 0 | SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); |
1726 | 0 | SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); |
1727 | 0 | SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); |
1728 | 0 | if (spp == 4) |
1729 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); |
1730 | 0 | ppixel++; |
1731 | 0 | } |
1732 | 0 | } |
1733 | 0 | } |
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 | 0 | if (spp == 1 && tRNS) { |
1743 | 0 | if (!cmap) { |
1744 | | /* Case 1: make fully transparent RGBA image */ |
1745 | 0 | L_INFO("transparency, 1 spp, no colormap, no transparency array: " |
1746 | 0 | "convention is fully transparent image\n", __func__); |
1747 | 0 | L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__); |
1748 | 0 | pixDestroy(&pix); |
1749 | 0 | pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ |
1750 | 0 | pixSetSpp(pix, 4); |
1751 | 0 | } else { |
1752 | 0 | L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__); |
1753 | | |
1754 | | /* Grab the transparency array */ |
1755 | 0 | png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); |
1756 | 0 | if (!trans) { /* invalid png file */ |
1757 | 0 | pixDestroy(&pix); |
1758 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
1759 | 0 | return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", |
1760 | 0 | __func__, NULL); |
1761 | 0 | } |
1762 | | |
1763 | | /* Save the cmap and destroy the pix */ |
1764 | 0 | cmap = pixcmapCopy(pixGetColormap(pix)); |
1765 | 0 | ncolors = pixcmapGetCount(cmap); |
1766 | 0 | pixDestroy(&pix); |
1767 | | |
1768 | | /* Start over with 32 bit RGBA */ |
1769 | 0 | pix = pixCreate(w, h, 32); |
1770 | 0 | wpl = pixGetWpl(pix); |
1771 | 0 | data = pixGetData(pix); |
1772 | 0 | 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 | 0 | if (d == 1) { |
1791 | | /* Case 2: 1 bpp with transparency (usually) behind white */ |
1792 | 0 | L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__); |
1793 | 0 | if (num_trans == 1) |
1794 | 0 | L_INFO("num_trans = 1; second color opaque by default\n", |
1795 | 0 | __func__); |
1796 | 0 | for (i = 0; i < h; i++) { |
1797 | 0 | ppixel = data + i * wpl; |
1798 | 0 | rowptr = row_pointers[i]; |
1799 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
1800 | 0 | byte = rowptr[j]; |
1801 | 0 | for (k = 0; k < 8 && index < w; k++, index++) { |
1802 | 0 | bitval = (byte >> (7 - k)) & 1; |
1803 | 0 | pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); |
1804 | 0 | composeRGBPixel(rval, gval, bval, ppixel); |
1805 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
1806 | 0 | bitval < num_trans ? trans[bitval] : 255); |
1807 | 0 | ppixel++; |
1808 | 0 | } |
1809 | 0 | } |
1810 | 0 | } |
1811 | 0 | } else if (d == 2) { |
1812 | | /* Case 3: 2 bpp with cmap and associated transparency */ |
1813 | 0 | L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__); |
1814 | 0 | for (i = 0; i < h; i++) { |
1815 | 0 | ppixel = data + i * wpl; |
1816 | 0 | rowptr = row_pointers[i]; |
1817 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
1818 | 0 | byte = rowptr[j]; |
1819 | 0 | for (k = 0; k < 4 && index < w; k++, index++) { |
1820 | 0 | bival = (byte >> 2 * (3 - k)) & 3; |
1821 | 0 | pixcmapGetColor(cmap, bival, &rval, &gval, &bval); |
1822 | 0 | 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 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
1827 | 0 | bival < num_trans ? trans[bival] : 255); |
1828 | 0 | ppixel++; |
1829 | 0 | } |
1830 | 0 | } |
1831 | 0 | } |
1832 | 0 | } else if (d == 4) { |
1833 | | /* Case 4: 4 bpp with cmap and associated transparency */ |
1834 | 0 | L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__); |
1835 | 0 | for (i = 0; i < h; i++) { |
1836 | 0 | ppixel = data + i * wpl; |
1837 | 0 | rowptr = row_pointers[i]; |
1838 | 0 | for (j = 0, index = 0; j < rowbytes; j++) { |
1839 | 0 | byte = rowptr[j]; |
1840 | 0 | for (k = 0; k < 2 && index < w; k++, index++) { |
1841 | 0 | quadval = (byte >> 4 * (1 - k)) & 0xf; |
1842 | 0 | pixcmapGetColor(cmap, quadval, &rval, &gval, &bval); |
1843 | 0 | composeRGBPixel(rval, gval, bval, ppixel); |
1844 | | /* Assume missing entries to be 255 (opaque) */ |
1845 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
1846 | 0 | quadval < num_trans ? trans[quadval] : 255); |
1847 | 0 | ppixel++; |
1848 | 0 | } |
1849 | 0 | } |
1850 | 0 | } |
1851 | 0 | } else if (d == 8) { |
1852 | | /* Case 5: 8 bpp with cmap and associated transparency */ |
1853 | 0 | L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__); |
1854 | 0 | for (i = 0; i < h; i++) { |
1855 | 0 | ppixel = data + i * wpl; |
1856 | 0 | rowptr = row_pointers[i]; |
1857 | 0 | for (j = 0; j < w; j++) { |
1858 | 0 | index = rowptr[j]; |
1859 | 0 | pixcmapGetColor(cmap, index, &rval, &gval, &bval); |
1860 | 0 | 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 | 0 | SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, |
1865 | 0 | index < num_trans ? trans[index] : 255); |
1866 | 0 | ppixel++; |
1867 | 0 | } |
1868 | 0 | } |
1869 | 0 | } else { |
1870 | 0 | L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", |
1871 | 0 | __func__, d); |
1872 | 0 | } |
1873 | 0 | pixcmapDestroy(&cmap); |
1874 | 0 | } |
1875 | 0 | } |
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 | 0 | if (pixGetDepth(pix) == 1) { |
1908 | 0 | if (!cmap) { |
1909 | 0 | pixInvert(pix, pix); |
1910 | 0 | } else { |
1911 | 0 | pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
1912 | 0 | pixDestroy(&pix); |
1913 | 0 | pix = pix1; |
1914 | 0 | } |
1915 | 0 | } |
1916 | |
|
1917 | 0 | xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); |
1918 | 0 | yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); |
1919 | 0 | pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ |
1920 | 0 | pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ |
1921 | | |
1922 | | /* Get the text if there is any */ |
1923 | 0 | png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); |
1924 | 0 | if (num_text && text_ptr) |
1925 | 0 | pixSetText(pix, text_ptr->text); |
1926 | |
|
1927 | 0 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
1928 | | |
1929 | | /* Final validity check on the colormap */ |
1930 | 0 | if ((cmap = pixGetColormap(pix)) != NULL) { |
1931 | 0 | pixcmapIsValid(cmap, pix, &valid); |
1932 | 0 | if (!valid) { |
1933 | 0 | pixDestroy(&pix); |
1934 | 0 | return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL); |
1935 | 0 | } |
1936 | 0 | } |
1937 | | |
1938 | 0 | pixSetPadBits(pix, 0); |
1939 | 0 | return pix; |
1940 | 0 | } |
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 | 0 | { |
1966 | 0 | char commentstring[] = "Comment"; |
1967 | 0 | l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid; |
1968 | 0 | l_int32 *rmap, *gmap, *bmap, *amap; |
1969 | 0 | l_uint32 *data, *ppixel; |
1970 | 0 | png_byte bit_depth, color_type; |
1971 | 0 | png_byte alpha[256]; |
1972 | 0 | png_uint_32 w, h, xres, yres; |
1973 | 0 | png_bytep rowbuffer; |
1974 | 0 | png_structp png_ptr; |
1975 | 0 | png_infop info_ptr; |
1976 | 0 | png_colorp palette; |
1977 | 0 | PIX *pix1; |
1978 | 0 | PIXCMAP *cmap; |
1979 | 0 | char *text; |
1980 | 0 | MEMIODATA state; |
1981 | |
|
1982 | 0 | if (pfiledata) *pfiledata = NULL; |
1983 | 0 | if (pfilesize) *pfilesize = 0; |
1984 | 0 | if (!pfiledata) |
1985 | 0 | return ERROR_INT("&filedata not defined", __func__, 1); |
1986 | 0 | if (!pfilesize) |
1987 | 0 | return ERROR_INT("&filesize not defined", __func__, 1); |
1988 | 0 | if (!pix) |
1989 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
1990 | | |
1991 | 0 | state.m_Buffer = 0; |
1992 | 0 | state.m_Size = 0; |
1993 | 0 | state.m_Next = 0; |
1994 | 0 | state.m_Count = 0; |
1995 | 0 | state.m_Last = &state; |
1996 | |
|
1997 | 0 | w = pixGetWidth(pix); |
1998 | 0 | h = pixGetHeight(pix); |
1999 | 0 | d = pixGetDepth(pix); |
2000 | 0 | spp = pixGetSpp(pix); |
2001 | | |
2002 | | /* A cmap validity check should prevent low-level colormap errors. */ |
2003 | 0 | if ((cmap = pixGetColormap(pix))) { |
2004 | 0 | cmflag = 1; |
2005 | 0 | pixcmapIsValid(cmap, pix, &valid); |
2006 | 0 | if (!valid) |
2007 | 0 | return ERROR_INT("colormap is not valid", __func__, 1); |
2008 | 0 | } else { |
2009 | 0 | cmflag = 0; |
2010 | 0 | } |
2011 | | |
2012 | | /* Do not set pad bits for d = 24 ! */ |
2013 | 0 | if (d != 24) pixSetPadBits(pix, 0); |
2014 | | |
2015 | | /* Set the color type and bit depth. */ |
2016 | 0 | if (d == 32 && spp == 4) { |
2017 | 0 | bit_depth = 8; |
2018 | 0 | color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ |
2019 | 0 | cmflag = 0; /* ignore if it exists */ |
2020 | 0 | } else if (d == 24 || d == 32) { |
2021 | 0 | bit_depth = 8; |
2022 | 0 | color_type = PNG_COLOR_TYPE_RGB; /* 2 */ |
2023 | 0 | cmflag = 0; /* ignore if it exists */ |
2024 | 0 | } else { |
2025 | 0 | bit_depth = d; |
2026 | 0 | color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ |
2027 | 0 | } |
2028 | 0 | if (cmflag) |
2029 | 0 | 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 | 0 | if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
2038 | 0 | (png_voidp)NULL, NULL, NULL)) == NULL) |
2039 | 0 | return ERROR_INT("png_ptr not made", __func__, 1); |
2040 | | |
2041 | 0 | if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { |
2042 | 0 | png_destroy_write_struct(&png_ptr, (png_infopp)NULL); |
2043 | 0 | return ERROR_INT("info_ptr not made", __func__, 1); |
2044 | 0 | } |
2045 | | |
2046 | | /* Set up png setjmp error handling */ |
2047 | 0 | pix1 = NULL; |
2048 | 0 | if (setjmp(png_jmpbuf(png_ptr))) { |
2049 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
2050 | 0 | pixDestroy(&pix1); |
2051 | 0 | return ERROR_INT("internal png error", __func__, 1); |
2052 | 0 | } |
2053 | | |
2054 | 0 | png_set_write_fn(png_ptr, &state, memio_png_write_data, |
2055 | 0 | (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 | 0 | compval = Z_DEFAULT_COMPRESSION; |
2063 | 0 | if (pix->special >= 10 && pix->special < 20) |
2064 | 0 | compval = pix->special - 10; |
2065 | 0 | png_set_compression_level(png_ptr, compval); |
2066 | |
|
2067 | 0 | png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, |
2068 | 0 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, |
2069 | 0 | PNG_FILTER_TYPE_BASE); |
2070 | | |
2071 | | /* Store resolution in ppm, if known */ |
2072 | 0 | xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); |
2073 | 0 | yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); |
2074 | 0 | if ((xres == 0) || (yres == 0)) |
2075 | 0 | png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); |
2076 | 0 | else |
2077 | 0 | png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); |
2078 | |
|
2079 | 0 | if (cmflag) { |
2080 | | /* Make and save the palette */ |
2081 | 0 | ncolors = pixcmapGetCount(cmap); |
2082 | 0 | palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); |
2083 | 0 | pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); |
2084 | 0 | for (i = 0; i < ncolors; i++) { |
2085 | 0 | palette[i].red = (png_byte)rmap[i]; |
2086 | 0 | palette[i].green = (png_byte)gmap[i]; |
2087 | 0 | palette[i].blue = (png_byte)bmap[i]; |
2088 | 0 | alpha[i] = (png_byte)amap[i]; |
2089 | 0 | } |
2090 | 0 | LEPT_FREE(rmap); |
2091 | 0 | LEPT_FREE(gmap); |
2092 | 0 | LEPT_FREE(bmap); |
2093 | 0 | LEPT_FREE(amap); |
2094 | 0 | png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); |
2095 | 0 | LEPT_FREE(palette); |
2096 | |
|
2097 | 0 | pixcmapIsOpaque(cmap, &opaque); |
2098 | 0 | if (!opaque) /* alpha channel has some transparency; assume valid */ |
2099 | 0 | png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, |
2100 | 0 | (int)ncolors, NULL); |
2101 | 0 | } |
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 | 0 | if (gamma > 0.0) |
2107 | 0 | png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); |
2108 | |
|
2109 | 0 | if ((text = pixGetText(pix))) { |
2110 | 0 | png_text text_chunk; |
2111 | 0 | text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; |
2112 | 0 | text_chunk.key = commentstring; |
2113 | 0 | text_chunk.text = text; |
2114 | 0 | 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 | 0 | png_set_text(png_ptr, info_ptr, &text_chunk, 1); |
2121 | 0 | } |
2122 | | |
2123 | | /* Write header and palette info */ |
2124 | 0 | png_write_info(png_ptr, info_ptr); |
2125 | |
|
2126 | 0 | 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 | 0 | if (d == 1 && !cmap) { |
2134 | 0 | pix1 = pixInvert(NULL, pix); |
2135 | 0 | pixEndianByteSwap(pix1); |
2136 | 0 | } else { |
2137 | 0 | pix1 = pixEndianByteSwapNew(pix); |
2138 | 0 | } |
2139 | 0 | if (!pix1) { |
2140 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
2141 | 0 | memio_free(&state); |
2142 | 0 | return ERROR_INT("pix1 not made", __func__, 1); |
2143 | 0 | } |
2144 | | |
2145 | | /* Transfer the data */ |
2146 | 0 | wpl = pixGetWpl(pix1); |
2147 | 0 | data = pixGetData(pix1); |
2148 | 0 | for (i = 0; i < h; i++) |
2149 | 0 | png_write_row(png_ptr, (png_bytep)(data + i * wpl)); |
2150 | 0 | png_write_end(png_ptr, info_ptr); |
2151 | |
|
2152 | 0 | pixDestroy(&pix1); |
2153 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
2154 | 0 | memio_png_flush(&state); |
2155 | 0 | *pfiledata = (l_uint8 *)state.m_Buffer; |
2156 | 0 | state.m_Buffer = 0; |
2157 | 0 | *pfilesize = state.m_Count; |
2158 | 0 | memio_free(&state); |
2159 | 0 | return 0; |
2160 | 0 | } |
2161 | | |
2162 | | /* For rgb and rgba, compose and write a row at a time */ |
2163 | 0 | data = pixGetData(pix); |
2164 | 0 | wpl = pixGetWpl(pix); |
2165 | 0 | if (d == 24) { /* See note 7 in pixWriteStreamPng() */ |
2166 | 0 | for (i = 0; i < h; i++) { |
2167 | 0 | ppixel = data + i * wpl; |
2168 | 0 | png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); |
2169 | 0 | } |
2170 | 0 | } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */ |
2171 | 0 | rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); |
2172 | 0 | for (i = 0; i < h; i++) { |
2173 | 0 | ppixel = data + i * wpl; |
2174 | 0 | for (j = k = 0; j < w; j++) { |
2175 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); |
2176 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); |
2177 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); |
2178 | 0 | if (spp == 4) |
2179 | 0 | rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); |
2180 | 0 | ppixel++; |
2181 | 0 | } |
2182 | |
|
2183 | 0 | png_write_rows(png_ptr, &rowbuffer, 1); |
2184 | 0 | } |
2185 | 0 | LEPT_FREE(rowbuffer); |
2186 | 0 | } |
2187 | 0 | png_write_end(png_ptr, info_ptr); |
2188 | |
|
2189 | 0 | png_destroy_write_struct(&png_ptr, &info_ptr); |
2190 | 0 | memio_png_flush(&state); |
2191 | 0 | *pfiledata = (l_uint8 *)state.m_Buffer; |
2192 | 0 | state.m_Buffer = 0; |
2193 | 0 | *pfilesize = state.m_Count; |
2194 | 0 | memio_free(&state); |
2195 | 0 | return 0; |
2196 | 0 | } |
2197 | | |
2198 | | /* --------------------------------------------*/ |
2199 | | #endif /* HAVE_LIBPNG */ |
2200 | | /* --------------------------------------------*/ |