1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % PPPP N N GGGG %
7 % P P NN N G %
8 % PPPP N N N G GG %
9 % P N NN G G %
10 % P N N GGG %
11 % %
12 % %
13 % Read/Write Portable Network Graphics Image Format %
14 % %
15 % Software Design %
16 % Cristy %
17 % Glenn Randers-Pehrson %
18 % November 1997 %
19 % %
20 % %
21 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 #define IM
41
42
43 /*
44 Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/constitute.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/histogram.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/layer.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/magick-private.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/memory-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/semaphore.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/static.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/timer-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/utility.h"
93 #if defined(MAGICKCORE_PNG_DELEGATE)
94
95 /* Suppress libpng pedantic warnings that were added in
96 * libpng-1.2.41 and libpng-1.4.0. If you are working on
97 * migration to libpng-1.5, remove these defines and then
98 * fix any code that generates warnings.
99 */
100 /* #define PNG_DEPRECATED Use of this function is deprecated */
101 /* #define PNG_USE_RESULT The result of this function must be checked */
102 /* #define PNG_NORETURN This function does not return */
103 /* #define PNG_ALLOCATED The result of the function is new memory */
104 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
105
106 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
107 #define PNG_PTR_NORETURN
108
109 #include <png.h>
110 #include <zlib.h>
111
112 /* ImageMagick differences */
113 #define first_scene scene
114
115 #if PNG_LIBPNG_VER > 10011
116 /*
117 Optional declarations. Define or undefine them as you like.
118 */
119 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
120
121 /*
122 Features under construction. Define these to work on them.
123 */
124 #undef MNG_OBJECT_BUFFERS
125 #undef MNG_BASI_SUPPORTED
126 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
127 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
128 #if defined(MAGICKCORE_JPEG_DELEGATE)
129 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
130 #endif
131 #if !defined(RGBColorMatchExact)
132 #define IsPNGColorEqual(color,target) \
133 (((color).red == (target).red) && \
134 ((color).green == (target).green) && \
135 ((color).blue == (target).blue))
136 #endif
137
138 /* Table of recognized sRGB ICC profiles */
139 struct sRGB_info_struct
140 {
141 png_uint_32 len;
142 png_uint_32 crc;
143 png_byte intent;
144 };
145
146 const struct sRGB_info_struct sRGB_info[] =
147 {
148 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
149 { 3048, 0x3b8772b9UL, 0},
150
151 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
152 { 3052, 0x427ebb21UL, 1},
153
154 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
155 {60988, 0x306fd8aeUL, 0},
156
157 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
158 {60960, 0xbbef7812UL, 0},
159
160 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
161 { 3024, 0x5d5129ceUL, 1},
162
163 /* HP-Microsoft sRGB v2 perceptual */
164 { 3144, 0x182ea552UL, 0},
165
166 /* HP-Microsoft sRGB v2 media-relative */
167 { 3144, 0xf29e526dUL, 1},
168
169 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
170 { 524, 0xd4938c39UL, 0},
171
172 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
173 { 3212, 0x034af5a1UL, 0},
174
175 /* Not recognized */
176 { 0, 0x00000000UL, 0},
177 };
178
179 /* Macros for left-bit-replication to ensure that pixels
180 * and PixelInfos all have the same image->depth, and for use
181 * in PNG8 quantization.
182 */
183
184 /* LBR01: Replicate top bit */
185
186 #define LBR01PacketRed(pixelpacket) \
187 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
188 0 : QuantumRange);
189
190 #define LBR01PacketGreen(pixelpacket) \
191 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
192 0 : QuantumRange);
193
194 #define LBR01PacketBlue(pixelpacket) \
195 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
196 0 : QuantumRange);
197
198 #define LBR01PacketAlpha(pixelpacket) \
199 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
200 0 : QuantumRange);
201
202 #define LBR01PacketRGB(pixelpacket) \
203 { \
204 LBR01PacketRed((pixelpacket)); \
205 LBR01PacketGreen((pixelpacket)); \
206 LBR01PacketBlue((pixelpacket)); \
207 }
208
209 #define LBR01PacketRGBA(pixelpacket) \
210 { \
211 LBR01PacketRGB((pixelpacket)); \
212 LBR01PacketAlpha((pixelpacket)); \
213 }
214
215 #define LBR01PixelRed(pixel) \
216 (SetPixelRed(image, \
217 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
218 0 : QuantumRange,(pixel)));
219
220 #define LBR01PixelGreen(pixel) \
221 (SetPixelGreen(image, \
222 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
223 0 : QuantumRange,(pixel)));
224
225 #define LBR01PixelBlue(pixel) \
226 (SetPixelBlue(image, \
227 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
228 0 : QuantumRange,(pixel)));
229
230 #define LBR01PixelAlpha(pixel) \
231 (SetPixelAlpha(image, \
232 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
233 0 : QuantumRange,(pixel)));
234
235 #define LBR01PixelRGB(pixel) \
236 { \
237 LBR01PixelRed((pixel)); \
238 LBR01PixelGreen((pixel)); \
239 LBR01PixelBlue((pixel)); \
240 }
241
242 #define LBR01PixelRGBA(pixel) \
243 { \
244 LBR01PixelRGB((pixel)); \
245 LBR01PixelAlpha((pixel)); \
246 }
247
248 /* LBR02: Replicate top 2 bits */
249
250 #define LBR02PacketRed(pixelpacket) \
251 { \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
253 (pixelpacket).red=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255 }
256 #define LBR02PacketGreen(pixelpacket) \
257 { \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
259 (pixelpacket).green=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261 }
262 #define LBR02PacketBlue(pixelpacket) \
263 { \
264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
265 (pixelpacket).blue=ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267 }
268 #define LBR02PacketAlpha(pixelpacket) \
269 { \
270 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
271 (pixelpacket).alpha=ScaleCharToQuantum( \
272 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
273 }
274
275 #define LBR02PacketRGB(pixelpacket) \
276 { \
277 LBR02PacketRed((pixelpacket)); \
278 LBR02PacketGreen((pixelpacket)); \
279 LBR02PacketBlue((pixelpacket)); \
280 }
281
282 #define LBR02PacketRGBA(pixelpacket) \
283 { \
284 LBR02PacketRGB((pixelpacket)); \
285 LBR02PacketAlpha((pixelpacket)); \
286 }
287
288 #define LBR02PixelRed(pixel) \
289 { \
290 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
291 & 0xc0; \
292 SetPixelRed(image, ScaleCharToQuantum( \
293 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
294 (pixel)); \
295 }
296 #define LBR02PixelGreen(pixel) \
297 { \
298 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
299 & 0xc0; \
300 SetPixelGreen(image, ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
302 (pixel)); \
303 }
304 #define LBR02PixelBlue(pixel) \
305 { \
306 unsigned char lbr_bits= \
307 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
308 SetPixelBlue(image, ScaleCharToQuantum( \
309 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
310 (pixel)); \
311 }
312 #define LBR02PixelAlpha(pixel) \
313 { \
314 unsigned char lbr_bits= \
315 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
316 SetPixelAlpha(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
318 (pixel) ); \
319 }
320
321 #define LBR02PixelRGB(pixel) \
322 { \
323 LBR02PixelRed((pixel)); \
324 LBR02PixelGreen((pixel)); \
325 LBR02PixelBlue((pixel)); \
326 }
327
328 #define LBR02PixelRGBA(pixel) \
329 { \
330 LBR02PixelRGB((pixel)); \
331 LBR02PixelAlpha((pixel)); \
332 }
333
334 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
335 PNG8 quantization) */
336
337 #define LBR03PacketRed(pixelpacket) \
338 { \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
340 (pixelpacket).red=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342 }
343 #define LBR03PacketGreen(pixelpacket) \
344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
346 (pixelpacket).green=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348 }
349 #define LBR03PacketBlue(pixelpacket) \
350 { \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
352 (pixelpacket).blue=ScaleCharToQuantum( \
353 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
354 }
355
356 #define LBR03PacketRGB(pixelpacket) \
357 { \
358 LBR03PacketRed((pixelpacket)); \
359 LBR03PacketGreen((pixelpacket)); \
360 LBR03PacketBlue((pixelpacket)); \
361 }
362
363 #define LBR03PixelRed(pixel) \
364 { \
365 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
366 & 0xe0; \
367 SetPixelRed(image, ScaleCharToQuantum( \
368 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
369 }
370 #define LBR03Green(pixel) \
371 { \
372 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
373 & 0xe0; \
374 SetPixelGreen(image, ScaleCharToQuantum( \
375 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
376 }
377 #define LBR03Blue(pixel) \
378 { \
379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
380 & 0xe0; \
381 SetPixelBlue(image, ScaleCharToQuantum( \
382 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
383 }
384
385 #define LBR03RGB(pixel) \
386 { \
387 LBR03PixelRed((pixel)); \
388 LBR03Green((pixel)); \
389 LBR03Blue((pixel)); \
390 }
391
392 /* LBR04: Replicate top 4 bits */
393
394 #define LBR04PacketRed(pixelpacket) \
395 { \
396 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
397 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
398 }
399 #define LBR04PacketGreen(pixelpacket) \
400 { \
401 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
402 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
403 }
404 #define LBR04PacketBlue(pixelpacket) \
405 { \
406 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
407 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
408 }
409 #define LBR04PacketAlpha(pixelpacket) \
410 { \
411 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
412 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
413 }
414
415 #define LBR04PacketRGB(pixelpacket) \
416 { \
417 LBR04PacketRed((pixelpacket)); \
418 LBR04PacketGreen((pixelpacket)); \
419 LBR04PacketBlue((pixelpacket)); \
420 }
421
422 #define LBR04PacketRGBA(pixelpacket) \
423 { \
424 LBR04PacketRGB((pixelpacket)); \
425 LBR04PacketAlpha((pixelpacket)); \
426 }
427
428 #define LBR04PixelRed(pixel) \
429 { \
430 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
431 & 0xf0; \
432 SetPixelRed(image,\
433 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
434 }
435 #define LBR04PixelGreen(pixel) \
436 { \
437 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
438 & 0xf0; \
439 SetPixelGreen(image,\
440 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
441 }
442 #define LBR04PixelBlue(pixel) \
443 { \
444 unsigned char lbr_bits= \
445 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
446 SetPixelBlue(image,\
447 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
448 }
449 #define LBR04PixelAlpha(pixel) \
450 { \
451 unsigned char lbr_bits= \
452 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
453 SetPixelAlpha(image,\
454 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
455 }
456
457 #define LBR04PixelRGB(pixel) \
458 { \
459 LBR04PixelRed((pixel)); \
460 LBR04PixelGreen((pixel)); \
461 LBR04PixelBlue((pixel)); \
462 }
463
464 #define LBR04PixelRGBA(pixel) \
465 { \
466 LBR04PixelRGB((pixel)); \
467 LBR04PixelAlpha((pixel)); \
468 }
469
470 /*
471 Establish thread safety.
472 setjmp/longjmp is claimed to be safe on these platforms:
473 setjmp/longjmp is alleged to be unsafe on these platforms:
474 */
475 #ifdef PNG_SETJMP_SUPPORTED
476 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
477 # define IMPNG_SETJMP_NOT_THREAD_SAFE
478 # endif
479
480 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
481 static SemaphoreInfo
482 *ping_semaphore = (SemaphoreInfo *) NULL;
483 # endif
484 #endif
485
486 /*
487 This temporary until I set up malloc'ed object attributes array.
488 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
489 waste more memory.
490 */
491 #define MNG_MAX_OBJECTS 256
492
493 /*
494 If this not defined, spec is interpreted strictly. If it is
495 defined, an attempt will be made to recover from some errors,
496 including
497 o global PLTE too short
498 */
499 #undef MNG_LOOSE
500
501 /*
502 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
503 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
504 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
505 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
506 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
507 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
508 will be enabled by default in libpng-1.2.0.
509 */
510 #ifdef PNG_MNG_FEATURES_SUPPORTED
511 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
512 # define PNG_READ_EMPTY_PLTE_SUPPORTED
513 # endif
514 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
515 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
516 # endif
517 #endif
518
519 /*
520 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
521 This macro is only defined in libpng-1.0.3 and later.
522 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
523 */
524 #ifndef PNG_UINT_31_MAX
525 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
526 #endif
527
528 /*
529 Constant strings for known chunk types. If you need to add a chunk,
530 add a string holding the name here. To make the code more
531 portable, we use ASCII numbers like this, not characters.
532 */
533
534 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
535 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
536 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
537 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
538 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
539 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
540 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
541 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
542 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
543 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
544 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
545 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
546 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
547 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
548 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
549 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
550 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
551 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
552 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
553 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
554 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
555 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
556 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
557 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
558 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
559 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
560 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
561 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
562 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
563 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
564 static const png_byte mng_orNT[5]={111, 114, 78, 84, (png_byte) '\0'};
565 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
566 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
567 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
568 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
569 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
570 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
571
572 #if defined(JNG_SUPPORTED)
573 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
574 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
575 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
576 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
577 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
578 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
579 #endif
580
581 #if 0
582 /* Other known chunks that are not yet supported by ImageMagick: */
583 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
584 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
585 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
586 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
587 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
588 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
589 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
590 #endif
591
592 typedef struct _MngBox
593 {
594 long
595 left,
596 right,
597 top,
598 bottom;
599 } MngBox;
600
601 typedef struct _MngPair
602 {
603 volatile long
604 a,
605 b;
606 } MngPair;
607
608 #ifdef MNG_OBJECT_BUFFERS
609 typedef struct _MngBuffer
610 {
611
612 size_t
613 height,
614 width;
615
616 Image
617 *image;
618
619 png_color
620 plte[256];
621
622 int
623 reference_count;
624
625 unsigned char
626 alpha_sample_depth,
627 compression_method,
628 color_type,
629 concrete,
630 filter_method,
631 frozen,
632 image_type,
633 interlace_method,
634 pixel_sample_depth,
635 plte_length,
636 sample_depth,
637 viewable;
638 } MngBuffer;
639 #endif
640
641 typedef struct _MngInfo
642 {
643
644 #ifdef MNG_OBJECT_BUFFERS
645 MngBuffer
646 *ob[MNG_MAX_OBJECTS];
647 #endif
648
649 Image *
650 image;
651
652 RectangleInfo
653 page;
654
655 int
656 adjoin,
657 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
658 bytes_in_read_buffer,
659 found_empty_plte,
660 #endif
661 equal_backgrounds,
662 equal_chrms,
663 equal_gammas,
664 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
665 defined(PNG_MNG_FEATURES_SUPPORTED)
666 equal_palettes,
667 #endif
668 equal_physs,
669 equal_srgbs,
670 framing_mode,
671 have_global_bkgd,
672 have_global_chrm,
673 have_global_gama,
674 have_global_phys,
675 have_global_sbit,
676 have_global_srgb,
677 have_saved_bkgd_index,
678 have_write_global_chrm,
679 have_write_global_gama,
680 have_write_global_plte,
681 have_write_global_srgb,
682 need_fram,
683 object_id,
684 old_framing_mode,
685 saved_bkgd_index;
686
687 int
688 new_number_colors;
689
690 ssize_t
691 image_found,
692 loop_count[256],
693 loop_iteration[256],
694 scenes_found,
695 x_off[MNG_MAX_OBJECTS],
696 y_off[MNG_MAX_OBJECTS];
697
698 MngBox
699 clip,
700 frame,
701 image_box,
702 object_clip[MNG_MAX_OBJECTS];
703
704 unsigned char
705 /* These flags could be combined into one byte */
706 exists[MNG_MAX_OBJECTS],
707 frozen[MNG_MAX_OBJECTS],
708 loop_active[256],
709 invisible[MNG_MAX_OBJECTS],
710 viewable[MNG_MAX_OBJECTS];
711
712 MagickOffsetType
713 loop_jump[256];
714
715 png_colorp
716 global_plte;
717
718 png_color_8
719 global_sbit;
720
721 png_byte
722 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
723 read_buffer[8],
724 #endif
725 global_trns[256];
726
727 float
728 global_gamma;
729
730 ChromaticityInfo
731 global_chrm;
732
733 RenderingIntent
734 global_srgb_intent;
735
736 unsigned long
737 delay,
738 global_plte_length,
739 global_trns_length,
740 global_x_pixels_per_unit,
741 global_y_pixels_per_unit,
742 mng_width,
743 mng_height,
744 ticks_per_second;
745
746 MagickBooleanType
747 need_blob;
748
749 unsigned int
750 IsPalette,
751 global_phys_unit_type,
752 basi_warning,
753 clon_warning,
754 dhdr_warning,
755 jhdr_warning,
756 magn_warning,
757 past_warning,
758 phyg_warning,
759 phys_warning,
760 sbit_warning,
761 show_warning,
762 mng_type,
763 write_mng,
764 write_png_colortype,
765 write_png_depth,
766 write_png_compression_level,
767 write_png_compression_strategy,
768 write_png_compression_filter,
769 write_png8,
770 write_png24,
771 write_png32,
772 write_png48,
773 write_png64;
774
775 #ifdef MNG_BASI_SUPPORTED
776 unsigned long
777 basi_width,
778 basi_height;
779
780 unsigned int
781 basi_depth,
782 basi_color_type,
783 basi_compression_method,
784 basi_filter_type,
785 basi_interlace_method,
786 basi_red,
787 basi_green,
788 basi_blue,
789 basi_alpha,
790 basi_viewable;
791 #endif
792
793 png_uint_16
794 magn_first,
795 magn_last,
796 magn_mb,
797 magn_ml,
798 magn_mr,
799 magn_mt,
800 magn_mx,
801 magn_my,
802 magn_methx,
803 magn_methy;
804
805 PixelInfo
806 mng_global_bkgd;
807
808 /* Added at version 6.6.6-7 */
809 MagickBooleanType
810 ping_exclude_bKGD,
811 ping_exclude_cHRM,
812 ping_exclude_date,
813 ping_exclude_eXIf,
814 ping_exclude_EXIF,
815 ping_exclude_gAMA,
816 ping_exclude_iCCP,
817 /* ping_exclude_iTXt, */
818 ping_exclude_oFFs,
819 ping_exclude_pHYs,
820 ping_exclude_sRGB,
821 ping_exclude_tEXt,
822 ping_exclude_tRNS,
823 ping_exclude_caNv,
824 ping_exclude_zCCP, /* hex-encoded iCCP */
825 ping_exclude_zTXt,
826 ping_preserve_colormap,
827 /* Added at version 6.8.5-7 */
828 ping_preserve_iCCP,
829 /* Added at version 6.8.9-9 */
830 ping_exclude_tIME;
831
832 } MngInfo;
833 #endif /* VER */
834
835 /*
836 Forward declarations.
837 */
838 static MagickBooleanType
839 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
840
841 static MagickBooleanType
842 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
843
844 #if defined(JNG_SUPPORTED)
845 static MagickBooleanType
846 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
847 #endif
848
849 #if PNG_LIBPNG_VER > 10011
850
851
852 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
853 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)854 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
855 {
856 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
857 *
858 * This is true if the high byte and the next highest byte of
859 * each sample of the image, the colormap, and the background color
860 * are equal to each other. We check this by seeing if the samples
861 * are unchanged when we scale them down to 8 and back up to Quantum.
862 *
863 * We don't use the method GetImageDepth() because it doesn't check
864 * background and doesn't handle PseudoClass specially.
865 */
866
867 #define QuantumToCharToQuantumEqQuantum(quantum) \
868 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
869
870 MagickBooleanType
871 ok_to_reduce=MagickFalse;
872
873 if (image->depth >= 16)
874 {
875
876 const Quantum
877 *p;
878
879 ok_to_reduce=
880 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
881 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
882 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
883 MagickTrue : MagickFalse;
884
885 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
886 {
887 int indx;
888
889 for (indx=0; indx < (ssize_t) image->colors; indx++)
890 {
891 ok_to_reduce=(
892 QuantumToCharToQuantumEqQuantum(
893 image->colormap[indx].red) &&
894 QuantumToCharToQuantumEqQuantum(
895 image->colormap[indx].green) &&
896 QuantumToCharToQuantumEqQuantum(
897 image->colormap[indx].blue)) ?
898 MagickTrue : MagickFalse;
899
900 if (ok_to_reduce == MagickFalse)
901 break;
902 }
903 }
904
905 if ((ok_to_reduce != MagickFalse) &&
906 (image->storage_class != PseudoClass))
907 {
908 ssize_t
909 y;
910
911 ssize_t
912 x;
913
914 for (y=0; y < (ssize_t) image->rows; y++)
915 {
916 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
917
918 if (p == (const Quantum *) NULL)
919 {
920 ok_to_reduce = MagickFalse;
921 break;
922 }
923
924 for (x=(ssize_t) image->columns-1; x >= 0; x--)
925 {
926 ok_to_reduce=
927 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
928 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
929 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
930 MagickTrue : MagickFalse;
931
932 if (ok_to_reduce == MagickFalse)
933 break;
934
935 p+=GetPixelChannels(image);
936 }
937 if (x >= 0)
938 break;
939 }
940 }
941
942 if (ok_to_reduce != MagickFalse)
943 {
944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
945 " OK to reduce PNG bit depth to 8 without loss of info");
946 }
947 else
948 {
949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
950 " Not OK to reduce PNG bit depth to 8 without losing info");
951 }
952 }
953
954 return ok_to_reduce;
955 }
956 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
957
PngColorTypeToString(const unsigned int color_type)958 static const char *PngColorTypeToString(const unsigned int color_type)
959 {
960 const char
961 *result = "Unknown";
962
963 switch (color_type)
964 {
965 case PNG_COLOR_TYPE_GRAY:
966 result = "Gray";
967 break;
968 case PNG_COLOR_TYPE_GRAY_ALPHA:
969 result = "Gray+Alpha";
970 break;
971 case PNG_COLOR_TYPE_PALETTE:
972 result = "Palette";
973 break;
974 case PNG_COLOR_TYPE_RGB:
975 result = "RGB";
976 break;
977 case PNG_COLOR_TYPE_RGB_ALPHA:
978 result = "RGB+Alpha";
979 break;
980 }
981
982 return result;
983 }
984
985 static int
Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)986 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
987 {
988 switch (orientation)
989 {
990 /* Convert to Exif orientations as defined in "Exchangeable image file
991 * format for digital still cameras: Exif Version 2.31",
992 * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
993 */
994
995 case TopLeftOrientation:
996 return 1;
997 case TopRightOrientation:
998 return 2;
999 case BottomRightOrientation:
1000 return 3;
1001 case BottomLeftOrientation:
1002 return 4;
1003 case LeftTopOrientation:
1004 return 5;
1005 case RightTopOrientation:
1006 return 6;
1007 case RightBottomOrientation:
1008 return 7;
1009 case LeftBottomOrientation:
1010 return 8;
1011 case UndefinedOrientation:
1012 default:
1013 return 0;
1014 }
1015 }
1016 static OrientationType
Magick_Orientation_from_Exif_Orientation(const int orientation)1017 Magick_Orientation_from_Exif_Orientation(const int orientation)
1018 {
1019 switch (orientation)
1020 {
1021 case 1:
1022 return TopLeftOrientation;
1023 case 2:
1024 return TopRightOrientation;
1025 case 3:
1026 return BottomRightOrientation;
1027 case 4:
1028 return BottomLeftOrientation;
1029 case 5:
1030 return LeftTopOrientation;
1031 case 6:
1032 return RightTopOrientation;
1033 case 7:
1034 return RightBottomOrientation;
1035 case 8:
1036 return LeftBottomOrientation;
1037 case 0:
1038 default:
1039 return UndefinedOrientation;
1040 }
1041 }
1042
1043 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)1044 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1045 {
1046 switch (intent)
1047 {
1048 case PerceptualIntent:
1049 return 0;
1050
1051 case RelativeIntent:
1052 return 1;
1053
1054 case SaturationIntent:
1055 return 2;
1056
1057 case AbsoluteIntent:
1058 return 3;
1059
1060 default:
1061 return -1;
1062 }
1063 }
1064
1065 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)1066 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1067 {
1068 switch (ping_intent)
1069 {
1070 case 0:
1071 return PerceptualIntent;
1072
1073 case 1:
1074 return RelativeIntent;
1075
1076 case 2:
1077 return SaturationIntent;
1078
1079 case 3:
1080 return AbsoluteIntent;
1081
1082 default:
1083 return UndefinedIntent;
1084 }
1085 }
1086
1087 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1088 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1089 {
1090 switch (ping_intent)
1091 {
1092 case 0:
1093 return "Perceptual Intent";
1094
1095 case 1:
1096 return "Relative Intent";
1097
1098 case 2:
1099 return "Saturation Intent";
1100
1101 case 3:
1102 return "Absolute Intent";
1103
1104 default:
1105 return "Undefined Intent";
1106 }
1107 }
1108
1109 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1110 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1111 {
1112 switch (ping_colortype)
1113 {
1114 case 0:
1115 return "Grayscale";
1116
1117 case 2:
1118 return "Truecolor";
1119
1120 case 3:
1121 return "Indexed";
1122
1123 case 4:
1124 return "GrayAlpha";
1125
1126 case 6:
1127 return "RGBA";
1128
1129 default:
1130 return "UndefinedColorType";
1131 }
1132 }
1133
1134 #endif /* PNG_LIBPNG_VER > 10011 */
1135 #endif /* MAGICKCORE_PNG_DELEGATE */
1136
1137 /*
1138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1139 % %
1140 % %
1141 % %
1142 % I s M N G %
1143 % %
1144 % %
1145 % %
1146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147 %
1148 % IsMNG() returns MagickTrue if the image format type, identified by the
1149 % magick string, is MNG.
1150 %
1151 % The format of the IsMNG method is:
1152 %
1153 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1154 %
1155 % A description of each parameter follows:
1156 %
1157 % o magick: compare image format pattern against these bytes.
1158 %
1159 % o length: Specifies the length of the magick string.
1160 %
1161 %
1162 */
IsMNG(const unsigned char * magick,const size_t length)1163 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1164 {
1165 if (length < 8)
1166 return(MagickFalse);
1167
1168 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1169 return(MagickTrue);
1170
1171 return(MagickFalse);
1172 }
1173
1174 /*
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 % %
1177 % %
1178 % %
1179 % I s J N G %
1180 % %
1181 % %
1182 % %
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 %
1185 % IsJNG() returns MagickTrue if the image format type, identified by the
1186 % magick string, is JNG.
1187 %
1188 % The format of the IsJNG method is:
1189 %
1190 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1191 %
1192 % A description of each parameter follows:
1193 %
1194 % o magick: compare image format pattern against these bytes.
1195 %
1196 % o length: Specifies the length of the magick string.
1197 %
1198 %
1199 */
IsJNG(const unsigned char * magick,const size_t length)1200 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1201 {
1202 if (length < 8)
1203 return(MagickFalse);
1204
1205 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1206 return(MagickTrue);
1207
1208 return(MagickFalse);
1209 }
1210
1211 /*
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 % %
1214 % %
1215 % %
1216 % I s P N G %
1217 % %
1218 % %
1219 % %
1220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221 %
1222 % IsPNG() returns MagickTrue if the image format type, identified by the
1223 % magick string, is PNG.
1224 %
1225 % The format of the IsPNG method is:
1226 %
1227 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1228 %
1229 % A description of each parameter follows:
1230 %
1231 % o magick: compare image format pattern against these bytes.
1232 %
1233 % o length: Specifies the length of the magick string.
1234 %
1235 */
IsPNG(const unsigned char * magick,const size_t length)1236 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1237 {
1238 if (length < 8)
1239 return(MagickFalse);
1240
1241 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1242 return(MagickTrue);
1243
1244 return(MagickFalse);
1245 }
1246
1247 #if defined(MAGICKCORE_PNG_DELEGATE)
1248 #if defined(__cplusplus) || defined(c_plusplus)
1249 extern "C" {
1250 #endif
1251
1252 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1253 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1254 {
1255 unsigned char
1256 buffer[4];
1257
1258 assert(image != (Image *) NULL);
1259 assert(image->signature == MagickCoreSignature);
1260 buffer[0]=(unsigned char) (value >> 24);
1261 buffer[1]=(unsigned char) (value >> 16);
1262 buffer[2]=(unsigned char) (value >> 8);
1263 buffer[3]=(unsigned char) value;
1264 return((size_t) WriteBlob(image,4,buffer));
1265 }
1266
PNGLong(png_bytep p,png_uint_32 value)1267 static void PNGLong(png_bytep p,png_uint_32 value)
1268 {
1269 *p++=(png_byte) ((value >> 24) & 0xff);
1270 *p++=(png_byte) ((value >> 16) & 0xff);
1271 *p++=(png_byte) ((value >> 8) & 0xff);
1272 *p++=(png_byte) (value & 0xff);
1273 }
1274
PNGsLong(png_bytep p,png_int_32 value)1275 static void PNGsLong(png_bytep p,png_int_32 value)
1276 {
1277 *p++=(png_byte) ((value >> 24) & 0xff);
1278 *p++=(png_byte) ((value >> 16) & 0xff);
1279 *p++=(png_byte) ((value >> 8) & 0xff);
1280 *p++=(png_byte) (value & 0xff);
1281 }
1282
PNGShort(png_bytep p,png_uint_16 value)1283 static void PNGShort(png_bytep p,png_uint_16 value)
1284 {
1285 *p++=(png_byte) ((value >> 8) & 0xff);
1286 *p++=(png_byte) (value & 0xff);
1287 }
1288
PNGType(png_bytep p,const png_byte * type)1289 static void PNGType(png_bytep p,const png_byte *type)
1290 {
1291 (void) memcpy(p,type,4*sizeof(png_byte));
1292 }
1293
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1294 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1295 size_t length)
1296 {
1297 if (logging != MagickFalse)
1298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1299 " Writing %c%c%c%c chunk, length: %.20g",
1300 type[0],type[1],type[2],type[3],(double) length);
1301 }
1302 #endif /* PNG_LIBPNG_VER > 10011 */
1303
1304 #if defined(__cplusplus) || defined(c_plusplus)
1305 }
1306 #endif
1307
1308 #if PNG_LIBPNG_VER > 10011
1309 /*
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 % %
1312 % %
1313 % %
1314 % R e a d P N G I m a g e %
1315 % %
1316 % %
1317 % %
1318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 %
1320 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1321 % Multiple-image Network Graphics (MNG) image file and returns it. It
1322 % allocates the memory necessary for the new Image structure and returns a
1323 % pointer to the new image or set of images.
1324 %
1325 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1326 %
1327 % The format of the ReadPNGImage method is:
1328 %
1329 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1330 %
1331 % A description of each parameter follows:
1332 %
1333 % o image_info: the image info.
1334 %
1335 % o exception: return any errors or warnings in this structure.
1336 %
1337 % To do, more or less in chronological order (as of version 5.5.2,
1338 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1339 %
1340 % Get 16-bit cheap transparency working.
1341 %
1342 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1343 %
1344 % Preserve all unknown and not-yet-handled known chunks found in input
1345 % PNG file and copy them into output PNG files according to the PNG
1346 % copying rules.
1347 %
1348 % (At this point, PNG encoding should be in full MNG compliance)
1349 %
1350 % Provide options for choice of background to use when the MNG BACK
1351 % chunk is not present or is not mandatory (i.e., leave transparent,
1352 % user specified, MNG BACK, PNG bKGD)
1353 %
1354 % Implement LOOP/ENDL [done, but could do discretionary loops more
1355 % efficiently by linking in the duplicate frames.].
1356 %
1357 % Decode and act on the MHDR simplicity profile (offer option to reject
1358 % files or attempt to process them anyway when the profile isn't LC or VLC).
1359 %
1360 % Upgrade to full MNG without Delta-PNG.
1361 %
1362 % o BACK [done a while ago except for background image ID]
1363 % o MOVE [done 15 May 1999]
1364 % o CLIP [done 15 May 1999]
1365 % o DISC [done 19 May 1999]
1366 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1367 % o SEEK [partially done 19 May 1999 (discard function only)]
1368 % o SHOW
1369 % o PAST
1370 % o BASI
1371 % o MNG-level tEXt/iTXt/zTXt
1372 % o pHYg
1373 % o pHYs
1374 % o sBIT
1375 % o bKGD
1376 % o iTXt (wait for libpng implementation).
1377 %
1378 % Use the scene signature to discover when an identical scene is
1379 % being reused, and just point to the original image->exception instead
1380 % of storing another set of pixels. This not specific to MNG
1381 % but could be applied generally.
1382 %
1383 % Upgrade to full MNG with Delta-PNG.
1384 %
1385 % JNG tEXt/iTXt/zTXt
1386 %
1387 % We will not attempt to read files containing the CgBI chunk.
1388 % They are really Xcode files meant for display on the iPhone.
1389 % These are not valid PNG files and it is impossible to recover
1390 % the original PNG from files that have been converted to Xcode-PNG,
1391 % since irretrievable loss of color data has occurred due to the
1392 % use of premultiplied alpha.
1393 */
1394
1395 #if defined(__cplusplus) || defined(c_plusplus)
1396 extern "C" {
1397 #endif
1398
1399 /*
1400 This the function that does the actual reading of data. It is
1401 the same as the one supplied in libpng, except that it receives the
1402 datastream from the ReadBlob() function instead of standard input.
1403 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1404 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1405 {
1406 Image
1407 *image;
1408
1409 image=(Image *) png_get_io_ptr(png_ptr);
1410 if (length != 0)
1411 {
1412 png_size_t
1413 check;
1414
1415 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1416 if (check != length)
1417 {
1418 char
1419 msg[MagickPathExtent];
1420
1421 (void) FormatLocaleString(msg,MagickPathExtent,
1422 "Expected %.20g bytes; found %.20g bytes",(double) length,
1423 (double) check);
1424 png_warning(png_ptr,msg);
1425 png_error(png_ptr,"Read Exception");
1426 }
1427 }
1428 }
1429
1430 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1431 !defined(PNG_MNG_FEATURES_SUPPORTED)
1432 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1433 * older than libpng-1.0.3a, which was the first to allow the empty
1434 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1435 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1436 * encountered after an empty PLTE, so we have to look ahead for bKGD
1437 * chunks and remove them from the datastream that is passed to libpng,
1438 * and store their contents for later use.
1439 */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1440 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1441 {
1442 MngInfo
1443 *mng_info;
1444
1445 Image
1446 *image;
1447
1448 png_size_t
1449 check;
1450
1451 ssize_t
1452 i;
1453
1454 i=0;
1455 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1456 image=(Image *) mng_info->image;
1457 while (mng_info->bytes_in_read_buffer && length)
1458 {
1459 data[i]=mng_info->read_buffer[i];
1460 mng_info->bytes_in_read_buffer--;
1461 length--;
1462 i++;
1463 }
1464 if (length != 0)
1465 {
1466 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1467
1468 if (check != length)
1469 png_error(png_ptr,"Read Exception");
1470
1471 if (length == 4)
1472 {
1473 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1474 (data[3] == 0))
1475 {
1476 check=(png_size_t) ReadBlob(image,(size_t) length,
1477 (char *) mng_info->read_buffer);
1478 mng_info->read_buffer[4]=0;
1479 mng_info->bytes_in_read_buffer=4;
1480 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1481 mng_info->found_empty_plte=MagickTrue;
1482 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1483 {
1484 mng_info->found_empty_plte=MagickFalse;
1485 mng_info->have_saved_bkgd_index=MagickFalse;
1486 }
1487 }
1488
1489 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1490 (data[3] == 1))
1491 {
1492 check=(png_size_t) ReadBlob(image,(size_t) length,
1493 (char *) mng_info->read_buffer);
1494 mng_info->read_buffer[4]=0;
1495 mng_info->bytes_in_read_buffer=4;
1496 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1497 if (mng_info->found_empty_plte)
1498 {
1499 /*
1500 Skip the bKGD data byte and CRC.
1501 */
1502 check=(png_size_t)
1503 ReadBlob(image,5,(char *) mng_info->read_buffer);
1504 check=(png_size_t) ReadBlob(image,(size_t) length,
1505 (char *) mng_info->read_buffer);
1506 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1507 mng_info->have_saved_bkgd_index=MagickTrue;
1508 mng_info->bytes_in_read_buffer=0;
1509 }
1510 }
1511 }
1512 }
1513 }
1514 #endif
1515
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1516 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1517 {
1518 Image
1519 *image;
1520
1521 image=(Image *) png_get_io_ptr(png_ptr);
1522 if (length != 0)
1523 {
1524 png_size_t
1525 check;
1526
1527 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1528
1529 if (check != length)
1530 png_error(png_ptr,"WriteBlob Failed");
1531 }
1532 }
1533
png_flush_data(png_structp png_ptr)1534 static void png_flush_data(png_structp png_ptr)
1535 {
1536 (void) png_ptr;
1537 }
1538
1539 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1540 static int PalettesAreEqual(Image *a,Image *b)
1541 {
1542 ssize_t
1543 i;
1544
1545 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1546 return((int) MagickFalse);
1547
1548 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1549 return((int) MagickFalse);
1550
1551 if (a->colors != b->colors)
1552 return((int) MagickFalse);
1553
1554 for (i=0; i < (ssize_t) a->colors; i++)
1555 {
1556 if ((a->colormap[i].red != b->colormap[i].red) ||
1557 (a->colormap[i].green != b->colormap[i].green) ||
1558 (a->colormap[i].blue != b->colormap[i].blue))
1559 return((int) MagickFalse);
1560 }
1561
1562 return((int) MagickTrue);
1563 }
1564 #endif
1565
MngInfoDiscardObject(MngInfo * mng_info,int i)1566 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1567 {
1568 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1569 mng_info->exists[i] && !mng_info->frozen[i])
1570 {
1571 #ifdef MNG_OBJECT_BUFFERS
1572 if (mng_info->ob[i] != (MngBuffer *) NULL)
1573 {
1574 if (mng_info->ob[i]->reference_count > 0)
1575 mng_info->ob[i]->reference_count--;
1576
1577 if (mng_info->ob[i]->reference_count == 0)
1578 {
1579 if (mng_info->ob[i]->image != (Image *) NULL)
1580 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1581
1582 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1583 }
1584 }
1585 mng_info->ob[i]=(MngBuffer *) NULL;
1586 #endif
1587 mng_info->exists[i]=MagickFalse;
1588 mng_info->invisible[i]=MagickFalse;
1589 mng_info->viewable[i]=MagickFalse;
1590 mng_info->frozen[i]=MagickFalse;
1591 mng_info->x_off[i]=0;
1592 mng_info->y_off[i]=0;
1593 mng_info->object_clip[i].left=0;
1594 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1595 mng_info->object_clip[i].top=0;
1596 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1597 }
1598 }
1599
MngInfoFreeStruct(MngInfo * mng_info)1600 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1601 {
1602 ssize_t
1603 i;
1604
1605 if (mng_info == (MngInfo *) NULL)
1606 return((MngInfo *) NULL);
1607
1608 for (i=1; i < MNG_MAX_OBJECTS; i++)
1609 MngInfoDiscardObject(mng_info,i);
1610
1611 mng_info->global_plte=(png_colorp)
1612 RelinquishMagickMemory(mng_info->global_plte);
1613
1614 return((MngInfo *) RelinquishMagickMemory(mng_info));
1615 }
1616
mng_minimum_box(MngBox box1,MngBox box2)1617 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1618 {
1619 MngBox
1620 box;
1621
1622 box=box1;
1623 if (box.left < box2.left)
1624 box.left=box2.left;
1625
1626 if (box.top < box2.top)
1627 box.top=box2.top;
1628
1629 if (box.right > box2.right)
1630 box.right=box2.right;
1631
1632 if (box.bottom > box2.bottom)
1633 box.bottom=box2.bottom;
1634
1635 return box;
1636 }
1637
mng_get_long(unsigned char * p)1638 static long mng_get_long(unsigned char *p)
1639 {
1640 return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1641 ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1642 }
1643
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1644 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1645 unsigned char *p)
1646 {
1647 MngBox
1648 box;
1649
1650 /*
1651 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1652 */
1653 box.left=mng_get_long(p);
1654 box.right=mng_get_long(&p[4]);
1655 box.top=mng_get_long(&p[8]);
1656 box.bottom=mng_get_long(&p[12]);
1657 if (delta_type != 0)
1658 {
1659 box.left+=previous_box.left;
1660 box.right+=previous_box.right;
1661 box.top+=previous_box.top;
1662 box.bottom+=previous_box.bottom;
1663 }
1664
1665 return(box);
1666 }
1667
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1668 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1669 unsigned char *p)
1670 {
1671 MngPair
1672 pair;
1673
1674 /*
1675 Read two ssize_t's from CLON, MOVE or PAST chunk
1676 */
1677 pair.a=mng_get_long(p);
1678 pair.b=mng_get_long(&p[4]);
1679 if (delta_type != 0)
1680 {
1681 pair.a+=previous_pair.a;
1682 pair.b+=previous_pair.b;
1683 }
1684
1685 return(pair);
1686 }
1687
1688 typedef struct _PNGErrorInfo
1689 {
1690 Image
1691 *image;
1692
1693 ExceptionInfo
1694 *exception;
1695 } PNGErrorInfo;
1696
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1697 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1698 {
1699 ExceptionInfo
1700 *exception;
1701
1702 Image
1703 *image;
1704
1705 PNGErrorInfo
1706 *error_info;
1707
1708 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1709 image=error_info->image;
1710 exception=error_info->exception;
1711
1712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1713 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1714
1715 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1716 "`%s'",image->filename);
1717
1718 #if (PNG_LIBPNG_VER < 10500)
1719 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1720 * are building with libpng-1.4.x and can be ignored.
1721 */
1722 longjmp(ping->jmpbuf,1);
1723 #else
1724 png_longjmp(ping,1);
1725 #endif
1726 }
1727
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1728 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1729 {
1730 ExceptionInfo
1731 *exception;
1732
1733 Image
1734 *image;
1735
1736 PNGErrorInfo
1737 *error_info;
1738
1739 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1740 png_error(ping, message);
1741
1742 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1743 image=error_info->image;
1744 exception=error_info->exception;
1745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1746 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1747
1748 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1749 message,"`%s'",image->filename);
1750 }
1751
1752 #ifdef PNG_USER_MEM_SUPPORTED
1753 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1754 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1755 #else
1756 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1757 #endif
1758 {
1759 (void) png_ptr;
1760 return((png_voidp) AcquireQuantumMemory(1,(size_t) size));
1761 }
1762
1763 /*
1764 Free a pointer. It is removed from the list at the same time.
1765 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1766 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1767 {
1768 (void) png_ptr;
1769 ptr=RelinquishMagickMemory(ptr);
1770 return((png_free_ptr) NULL);
1771 }
1772 #endif
1773
1774 #if defined(__cplusplus) || defined(c_plusplus)
1775 }
1776 #endif
1777
1778 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1779 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1780 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1781 {
1782 png_charp
1783 ep;
1784
1785 ssize_t
1786 i;
1787
1788 unsigned char
1789 *dp;
1790
1791 png_charp
1792 sp;
1793
1794 size_t
1795 extent,
1796 nibbles;
1797
1798 ssize_t
1799 length;
1800
1801 StringInfo
1802 *profile;
1803
1804 static const unsigned char
1805 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1806 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1807 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1808 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1809 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1810 13,14,15};
1811
1812 sp=text[ii].text+1;
1813 extent=text[ii].text_length;
1814 ep=text[ii].text+extent;
1815 if (ep <= sp)
1816 {
1817 png_warning(ping,"invalid profile length");
1818 return(MagickFalse);
1819 }
1820 /* look for newline */
1821 while ((*sp != '\n') && extent--)
1822 sp++;
1823
1824 /* look for length */
1825 while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1826 sp++;
1827
1828 if (extent == 0)
1829 {
1830 png_warning(ping,"invalid profile length");
1831 return(MagickFalse);
1832 }
1833
1834 length=StringToLong(sp);
1835
1836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1837 " length: %lu",(unsigned long) length);
1838
1839 while ((*sp != ' ' && *sp != '\n') && extent--)
1840 sp++;
1841
1842 if (extent == 0)
1843 {
1844 png_warning(ping,"missing profile length");
1845 return(MagickFalse);
1846 }
1847
1848 /* allocate space */
1849 if (length == 0)
1850 {
1851 png_warning(ping,"invalid profile length");
1852 return(MagickFalse);
1853 }
1854
1855 profile=BlobToStringInfo((const void *) NULL,length);
1856
1857 if (profile == (StringInfo *) NULL)
1858 {
1859 png_warning(ping, "unable to copy profile");
1860 return(MagickFalse);
1861 }
1862
1863 /* copy profile, skipping white space and column 1 "=" signs */
1864 dp=GetStringInfoDatum(profile);
1865 nibbles=length*2;
1866
1867 for (i=0; i < (ssize_t) nibbles; i++)
1868 {
1869 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1870 {
1871 if (*sp == '\0')
1872 {
1873 png_warning(ping, "ran out of profile data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
1879
1880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1882
1883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
1889 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1890 profile=DestroyStringInfo(profile);
1891
1892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1894
1895 return MagickTrue;
1896 }
1897
PNGSetExifProfile(Image * image,png_size_t size,png_byte * data,ExceptionInfo * exception)1898 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1899 ExceptionInfo *exception)
1900 {
1901 StringInfo
1902 *profile;
1903
1904 unsigned char
1905 *p;
1906
1907 png_byte
1908 *s;
1909
1910 size_t
1911 i;
1912
1913 profile=BlobToStringInfo((const void *) NULL,size+6);
1914
1915 if (profile == (StringInfo *) NULL)
1916 {
1917 (void) ThrowMagickException(exception,GetMagickModule(),
1918 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1919 image->filename);
1920 return(-1);
1921 }
1922 p=GetStringInfoDatum(profile);
1923
1924 /* Initialize profile with "Exif\0\0" */
1925 *p++ ='E';
1926 *p++ ='x';
1927 *p++ ='i';
1928 *p++ ='f';
1929 *p++ ='\0';
1930 *p++ ='\0';
1931
1932 s=data;
1933 i=0;
1934 if (size > 6)
1935 {
1936 /* Skip first 6 bytes if "Exif\0\0" is
1937 already present by accident
1938 */
1939 if (s[0] == 'E' && s[1] == 'x' && s[2] == 'i' &&
1940 s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1941 {
1942 s+=6;
1943 i=6;
1944 SetStringInfoLength(profile,size);
1945 p=GetStringInfoDatum(profile);
1946 }
1947 }
1948
1949 /* copy chunk->data to profile */
1950 for (; i<size; i++)
1951 *p++ = *s++;
1952
1953 (void) SetImageProfile(image,"exif",profile,exception);
1954
1955 profile=DestroyStringInfo(profile);
1956
1957 return(1);
1958 }
1959
1960 #if defined(PNG_READ_eXIf_SUPPORTED)
read_eXIf_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1961 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1962 ExceptionInfo *exception)
1963 {
1964 png_uint_32
1965 size;
1966
1967 png_bytep
1968 data;
1969
1970 #if PNG_LIBPNG_VER > 10631
1971 if (png_get_eXIf_1(ping,info,&size,&data))
1972 (void) PNGSetExifProfile(image,size,data,exception);
1973 #endif
1974 }
1975 #endif
1976
1977 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1978
read_user_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1979 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1980 {
1981 Image
1982 *image;
1983
1984 PNGErrorInfo
1985 *error_info;
1986
1987 /* The unknown chunk structure contains the chunk data:
1988 png_byte name[5];
1989 png_byte *data;
1990 png_size_t size;
1991
1992 Note that libpng has already taken care of the CRC handling.
1993
1994 Returns one of the following:
1995 return(-n); chunk had an error
1996 return(0); did not recognize
1997 return(n); success
1998 */
1999
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2001 " read_user_chunk: found %c%c%c%c chunk",
2002 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2003
2004 if (chunk->name[0] == 101 &&
2005 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
2006 chunk->name[2] == 73 &&
2007 chunk-> name[3] == 102)
2008 {
2009 /* process eXIf or exIf chunk */
2010
2011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2012 " recognized eXIf chunk");
2013
2014 image=(Image *) png_get_user_chunk_ptr(ping);
2015
2016 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2017
2018 return(PNGSetExifProfile(image,chunk->size,chunk->data,
2019 error_info->exception));
2020 }
2021
2022 /* orNT */
2023 if (chunk->name[0] == 111 &&
2024 chunk->name[1] == 114 &&
2025 chunk->name[2] == 78 &&
2026 chunk->name[3] == 84)
2027 {
2028 /* recognized orNT */
2029 if (chunk->size != 1)
2030 return(-1); /* Error return */
2031
2032 image=(Image *) png_get_user_chunk_ptr(ping);
2033
2034 image->orientation=
2035 Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2036
2037 return(1);
2038 }
2039
2040 /* vpAg (deprecated, replaced by caNv) */
2041 if (chunk->name[0] == 118 &&
2042 chunk->name[1] == 112 &&
2043 chunk->name[2] == 65 &&
2044 chunk->name[3] == 103)
2045 {
2046 /* recognized vpAg */
2047
2048 if (chunk->size != 9)
2049 return(-1); /* Error return */
2050
2051 if (chunk->data[8] != 0)
2052 return(0); /* ImageMagick requires pixel units */
2053
2054 image=(Image *) png_get_user_chunk_ptr(ping);
2055
2056 image->page.width=(size_t)mng_get_long(chunk->data);
2057 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2058
2059 return(1);
2060 }
2061
2062 /* caNv */
2063 if (chunk->name[0] == 99 &&
2064 chunk->name[1] == 97 &&
2065 chunk->name[2] == 78 &&
2066 chunk->name[3] == 118)
2067 {
2068 /* recognized caNv */
2069
2070 if (chunk->size != 16)
2071 return(-1); /* Error return */
2072
2073 image=(Image *) png_get_user_chunk_ptr(ping);
2074
2075 image->page.width=(size_t) mng_get_long(chunk->data);
2076 image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2077 image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2078 image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2079
2080 return(1);
2081 }
2082
2083 /* acTL */
2084 if ((chunk->name[0] == 97) && (chunk->name[1] == 99) &&
2085 (chunk->name[2] == 84) && (chunk->name[3] == 76))
2086 {
2087 image=(Image *) png_get_user_chunk_ptr(ping);
2088 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2089
2090 (void) SetImageProperty(image,"png:acTL","chunk was found",
2091 error_info->exception);
2092
2093 return(1);
2094 }
2095
2096 return(0); /* Did not recognize */
2097 }
2098 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2099
2100 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)2101 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2102 ExceptionInfo *exception)
2103 {
2104 png_timep
2105 time;
2106
2107 if (png_get_tIME(ping,info,&time))
2108 {
2109 char
2110 timestamp[21];
2111
2112 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2113 time->year,time->month,time->day,time->hour,time->minute,time->second);
2114 SetImageProperty(image,"png:tIME",timestamp,exception);
2115 }
2116 }
2117 #endif
2118
2119 /*
2120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2121 % %
2122 % %
2123 % %
2124 % R e a d O n e P N G I m a g e %
2125 % %
2126 % %
2127 % %
2128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2129 %
2130 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2131 % (minus the 8-byte signature) and returns it. It allocates the memory
2132 % necessary for the new Image structure and returns a pointer to the new
2133 % image.
2134 %
2135 % The format of the ReadOnePNGImage method is:
2136 %
2137 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2138 % ExceptionInfo *exception)
2139 %
2140 % A description of each parameter follows:
2141 %
2142 % o mng_info: Specifies a pointer to a MngInfo structure.
2143 %
2144 % o image_info: the image info.
2145 %
2146 % o exception: return any errors or warnings in this structure.
2147 %
2148 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)2149 static Image *ReadOnePNGImage(MngInfo *mng_info,
2150 const ImageInfo *image_info, ExceptionInfo *exception)
2151 {
2152 /* Read one PNG image */
2153
2154 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2155
2156 Image
2157 *image;
2158
2159 int
2160 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2161 num_raw_profiles,
2162 num_text,
2163 num_text_total,
2164 num_passes,
2165 number_colors,
2166 pass,
2167 ping_bit_depth,
2168 ping_color_type,
2169 ping_file_depth,
2170 ping_interlace_method,
2171 ping_compression_method,
2172 ping_filter_method,
2173 ping_num_trans,
2174 unit_type;
2175
2176 double
2177 file_gamma;
2178
2179 MagickBooleanType
2180 logging,
2181 ping_found_cHRM,
2182 ping_found_gAMA,
2183 ping_found_iCCP,
2184 ping_found_sRGB,
2185 ping_found_sRGB_cHRM,
2186 ping_preserve_iCCP,
2187 status;
2188
2189 MemoryInfo
2190 *volatile pixel_info;
2191
2192 PixelInfo
2193 transparent_color;
2194
2195 PNGErrorInfo
2196 error_info;
2197
2198 png_bytep
2199 ping_trans_alpha = NULL;
2200
2201 png_color_16p
2202 ping_background = (png_color_16p) NULL,
2203 ping_trans_color = (png_color_16p) NULL;
2204
2205 png_info
2206 *end_info,
2207 *ping_info;
2208
2209 png_struct
2210 *ping;
2211
2212 png_textp
2213 text;
2214
2215 png_uint_32
2216 ping_height,
2217 ping_width,
2218 x_resolution,
2219 y_resolution;
2220
2221 QuantumInfo
2222 *volatile quantum_info;
2223
2224 Quantum
2225 *volatile quantum_scanline;
2226
2227 ssize_t
2228 y;
2229
2230 unsigned char
2231 *p;
2232
2233 ssize_t
2234 i,
2235 x;
2236
2237 Quantum
2238 *q;
2239
2240 size_t
2241 length,
2242 ping_rowbytes,
2243 row_offset;
2244
2245 ssize_t
2246 j;
2247
2248 unsigned char
2249 *ping_pixels;
2250
2251 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2252 png_byte unused_chunks[]=
2253 {
2254 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2255 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2256 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2257 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2258 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2259 #if !defined(PNG_tIME_SUPPORTED)
2260 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2261 #endif
2262 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2263 /* ignore the APNG chunks */
2264 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2265 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2266 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2267 #endif
2268 };
2269 #endif
2270
2271 logging=IsEventLogging();
2272 if (logging != MagickFalse)
2273 {
2274 char
2275 im_vers[32],
2276 libpng_runv[32],
2277 libpng_vers[32],
2278 zlib_runv[32],
2279 zlib_vers[32];
2280
2281 *im_vers='\0';
2282 (void) ConcatenateMagickString(im_vers,
2283 MagickLibVersionText,32);
2284 (void) ConcatenateMagickString(im_vers,
2285 MagickLibAddendum,32);
2286
2287 *libpng_vers='\0';
2288 (void) ConcatenateMagickString(libpng_vers,
2289 PNG_LIBPNG_VER_STRING,32);
2290 *libpng_runv='\0';
2291 (void) ConcatenateMagickString(libpng_runv,
2292 png_get_libpng_ver(NULL),32);
2293
2294 *zlib_vers='\0';
2295 (void) ConcatenateMagickString(zlib_vers,
2296 ZLIB_VERSION,32);
2297 *zlib_runv='\0';
2298 (void) ConcatenateMagickString(zlib_runv,
2299 zlib_version,32);
2300
2301 LogMagickEvent(CoderEvent,GetMagickModule(),
2302 " Enter ReadOnePNGImage()\n"
2303 " IM version = %s\n"
2304 " Libpng version = %s",
2305 im_vers, libpng_vers);
2306
2307 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2309 " running with %s", libpng_runv);
2310 if (LocaleCompare(libpng_vers,zlib_vers) != 0)
2311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312 " Zlib version = %s", zlib_vers);
2313 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2315 " running with %s", zlib_runv);
2316 }
2317
2318 #if (PNG_LIBPNG_VER < 10200)
2319 if (image_info->verbose)
2320 printf("Your PNG library (libpng-%s) is rather old.\n",
2321 PNG_LIBPNG_VER_STRING);
2322 #endif
2323
2324 #if (PNG_LIBPNG_VER >= 10400)
2325 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2326 if (image_info->verbose)
2327 {
2328 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2329 PNG_LIBPNG_VER_STRING);
2330 printf("Please update it.\n");
2331 }
2332 # endif
2333 #endif
2334
2335
2336 quantum_info = (QuantumInfo *) NULL;
2337 image=mng_info->image;
2338
2339 if (logging != MagickFalse)
2340 {
2341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2342 " Before reading:\n"
2343 " image->alpha_trait=%d\n"
2344 " image->rendering_intent=%d\n"
2345 " image->colorspace=%d\n"
2346 " image->gamma=%f",
2347 (int) image->alpha_trait, (int) image->rendering_intent,
2348 (int) image->colorspace, image->gamma);
2349 }
2350 intent=
2351 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2352
2353 /* Set to an out-of-range color unless tRNS chunk is present */
2354 transparent_color.red=65537;
2355 transparent_color.green=65537;
2356 transparent_color.blue=65537;
2357 transparent_color.alpha=65537;
2358
2359 number_colors=0;
2360 num_text = 0;
2361 num_text_total = 0;
2362 num_raw_profiles = 0;
2363
2364 ping_found_cHRM = MagickFalse;
2365 ping_found_gAMA = MagickFalse;
2366 ping_found_iCCP = MagickFalse;
2367 ping_found_sRGB = MagickFalse;
2368 ping_found_sRGB_cHRM = MagickFalse;
2369 ping_preserve_iCCP = MagickFalse;
2370
2371
2372 /*
2373 Allocate the PNG structures
2374 */
2375 #ifdef PNG_USER_MEM_SUPPORTED
2376 error_info.image=image;
2377 error_info.exception=exception;
2378 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2379 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2380 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2381 #else
2382 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2383 MagickPNGErrorHandler,MagickPNGWarningHandler);
2384 #endif
2385 if (ping == (png_struct *) NULL)
2386 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2387
2388 ping_info=png_create_info_struct(ping);
2389
2390 if (ping_info == (png_info *) NULL)
2391 {
2392 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2393 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2394 }
2395
2396 end_info=png_create_info_struct(ping);
2397
2398 if (end_info == (png_info *) NULL)
2399 {
2400 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2401 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2402 }
2403
2404 pixel_info=(MemoryInfo *) NULL;
2405 quantum_scanline = (Quantum *) NULL;
2406 quantum_info = (QuantumInfo *) NULL;
2407
2408 if (setjmp(png_jmpbuf(ping)))
2409 {
2410 /*
2411 PNG image is corrupt.
2412 */
2413 png_destroy_read_struct(&ping,&ping_info,&end_info);
2414
2415 if (pixel_info != (MemoryInfo *) NULL)
2416 pixel_info=RelinquishVirtualMemory(pixel_info);
2417
2418 if (quantum_info != (QuantumInfo *) NULL)
2419 quantum_info=DestroyQuantumInfo(quantum_info);
2420
2421 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2422
2423 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2424 UnlockSemaphoreInfo(ping_semaphore);
2425 #endif
2426
2427 if (logging != MagickFalse)
2428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429 " exit ReadOnePNGImage() with error.");
2430
2431 if (image != (Image *) NULL)
2432 image=DestroyImageList(image);
2433 return(image);
2434 }
2435
2436 /* { For navigation to end of SETJMP-protected block. Within this
2437 * block, use png_error() instead of Throwing an Exception, to ensure
2438 * that libpng is able to clean up, and that the semaphore is unlocked.
2439 */
2440
2441 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2442 LockSemaphoreInfo(ping_semaphore);
2443 #endif
2444
2445 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2446 /* Allow benign errors */
2447 png_set_benign_errors(ping, 1);
2448 #endif
2449
2450 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2451 {
2452 const char
2453 *option;
2454
2455 /* Reject images with too many rows or columns */
2456 png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2457 GetMagickResourceLimit(WidthResource)),(png_uint_32)
2458 MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2459
2460 #if (PNG_LIBPNG_VER >= 10400)
2461 option=GetImageOption(image_info,"png:chunk-cache-max");
2462 if (option != (const char *) NULL)
2463 png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2464 StringToLong(option)));
2465 else
2466 png_set_chunk_cache_max(ping,32767);
2467 #endif
2468
2469 #if (PNG_LIBPNG_VER >= 10401)
2470 option=GetImageOption(image_info,"png:chunk-malloc-max");
2471 if (option != (const char *) NULL)
2472 png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2473 (size_t) StringToLong(option)));
2474 else
2475 png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2476 #endif
2477 }
2478 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2479
2480 /*
2481 Prepare PNG for reading.
2482 */
2483
2484 mng_info->image_found++;
2485 png_set_sig_bytes(ping,8);
2486
2487 if (LocaleCompare(image_info->magick,"MNG") == 0)
2488 {
2489 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2490 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2491 png_set_read_fn(ping,image,png_get_data);
2492 #else
2493 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2494 png_permit_empty_plte(ping,MagickTrue);
2495 png_set_read_fn(ping,image,png_get_data);
2496 #else
2497 mng_info->image=image;
2498 mng_info->bytes_in_read_buffer=0;
2499 mng_info->found_empty_plte=MagickFalse;
2500 mng_info->have_saved_bkgd_index=MagickFalse;
2501 png_set_read_fn(ping,mng_info,mng_get_data);
2502 #endif
2503 #endif
2504 }
2505
2506 else
2507 png_set_read_fn(ping,image,png_get_data);
2508
2509 {
2510 const char
2511 *value;
2512
2513 value=GetImageOption(image_info,"png:ignore-crc");
2514 if (IsStringTrue(value) != MagickFalse)
2515 {
2516 /* Turn off CRC checking while reading */
2517 png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2518 #ifdef PNG_IGNORE_ADLER32
2519 /* Turn off ADLER32 checking while reading */
2520 png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2521 #endif
2522 }
2523
2524 value=GetImageOption(image_info,"profile:skip");
2525
2526 if (IsOptionMember("ICC",value) == MagickFalse)
2527 {
2528
2529 value=GetImageOption(image_info,"png:preserve-iCCP");
2530
2531 if (value == NULL)
2532 value=GetImageArtifact(image,"png:preserve-iCCP");
2533
2534 if (value != NULL)
2535 ping_preserve_iCCP=MagickTrue;
2536
2537 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2538 /* Don't let libpng check for ICC/sRGB profile because we're going
2539 * to do that anyway. This feature was added at libpng-1.6.12.
2540 * If logging, go ahead and check and issue a warning as appropriate.
2541 */
2542 if (logging == MagickFalse)
2543 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2544 #endif
2545 }
2546 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2547 else
2548 {
2549 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2550 }
2551 #endif
2552 }
2553 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2554 /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2555 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2556 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2557 # else
2558 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2559 # endif
2560 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2561 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2562 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2563 (int)sizeof(unused_chunks)/5);
2564 /* Callback for other unknown chunks */
2565 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2566 #endif
2567
2568 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2569 /* Disable new libpng-1.5.10 feature */
2570 png_set_check_for_invalid_index (ping, 0);
2571 #endif
2572
2573 #if (PNG_LIBPNG_VER < 10400)
2574 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2575 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2576 /* Disable thread-unsafe features of pnggccrd */
2577 if (png_access_version_number() >= 10200)
2578 {
2579 png_uint_32 mmx_disable_mask=0;
2580 png_uint_32 asm_flags;
2581
2582 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2583 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2584 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2585 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2586 asm_flags=png_get_asm_flags(ping);
2587 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2588 }
2589 # endif
2590 #endif
2591
2592 png_read_info(ping,ping_info);
2593
2594 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2595 &ping_bit_depth,&ping_color_type,
2596 &ping_interlace_method,&ping_compression_method,
2597 &ping_filter_method);
2598
2599 ping_file_depth = ping_bit_depth;
2600
2601 /* Swap bytes if requested */
2602 if (ping_file_depth == 16)
2603 {
2604 const char
2605 *value;
2606
2607 value=GetImageOption(image_info,"png:swap-bytes");
2608
2609 if (value == NULL)
2610 value=GetImageArtifact(image,"png:swap-bytes");
2611
2612 if (value != NULL)
2613 png_set_swap(ping);
2614 }
2615
2616 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2617 {
2618 char
2619 msg[MagickPathExtent];
2620
2621 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2622 (int) ping_color_type);
2623 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2624
2625 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2626 (int) ping_bit_depth);
2627 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2628 }
2629
2630 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2631 &ping_trans_color);
2632
2633 (void) png_get_bKGD(ping, ping_info, &ping_background);
2634
2635 if (ping_bit_depth < 8)
2636 {
2637 png_set_packing(ping);
2638 ping_bit_depth = 8;
2639 }
2640
2641 image->depth=ping_bit_depth;
2642 image->depth=GetImageQuantumDepth(image,MagickFalse);
2643 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2644
2645 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2646 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2647 {
2648 image->rendering_intent=UndefinedIntent;
2649 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2650 (void) memset(&image->chromaticity,0,
2651 sizeof(image->chromaticity));
2652 }
2653
2654 if (logging != MagickFalse)
2655 {
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " PNG width: %.20g, height: %.20g\n"
2658 " PNG color_type: %d, bit_depth: %d\n"
2659 " PNG compression_method: %d\n"
2660 " PNG interlace_method: %d, filter_method: %d",
2661 (double) ping_width, (double) ping_height,
2662 ping_color_type, ping_bit_depth,
2663 ping_compression_method,
2664 ping_interlace_method,ping_filter_method);
2665
2666 }
2667
2668 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2669 {
2670 ping_found_iCCP=MagickTrue;
2671 if (logging != MagickFalse)
2672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673 " Found PNG iCCP chunk.");
2674 }
2675
2676 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2677 {
2678 ping_found_gAMA=MagickTrue;
2679 if (logging != MagickFalse)
2680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2681 " Found PNG gAMA chunk.");
2682 }
2683
2684 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2685 {
2686 ping_found_cHRM=MagickTrue;
2687 if (logging != MagickFalse)
2688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2689 " Found PNG cHRM chunk.");
2690 }
2691
2692 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2693 PNG_INFO_sRGB))
2694 {
2695 ping_found_sRGB=MagickTrue;
2696 if (logging != MagickFalse)
2697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2698 " Found PNG sRGB chunk.");
2699 }
2700
2701 #ifdef PNG_READ_iCCP_SUPPORTED
2702 if (ping_found_iCCP !=MagickTrue &&
2703 ping_found_sRGB != MagickTrue &&
2704 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2705 {
2706 ping_found_iCCP=MagickTrue;
2707 if (logging != MagickFalse)
2708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2709 " Found PNG iCCP chunk.");
2710 }
2711
2712 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2713 {
2714 int
2715 compression;
2716
2717 #if (PNG_LIBPNG_VER < 10500)
2718 png_charp
2719 info;
2720 #else
2721 png_bytep
2722 info;
2723 #endif
2724
2725 png_charp
2726 name;
2727
2728 png_uint_32
2729 profile_length;
2730
2731 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2732 &profile_length);
2733
2734 if (profile_length != 0)
2735 {
2736 StringInfo
2737 *profile;
2738
2739 if (logging != MagickFalse)
2740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2741 " Reading PNG iCCP chunk.");
2742
2743 profile=BlobToStringInfo(info,(const size_t) profile_length);
2744
2745 if (profile == (StringInfo *) NULL)
2746 {
2747 png_warning(ping, "ICC profile is NULL");
2748 profile=DestroyStringInfo(profile);
2749 }
2750 else
2751 {
2752 if (ping_preserve_iCCP == MagickFalse)
2753 {
2754 int
2755 icheck,
2756 got_crc=0;
2757
2758
2759 png_uint_32
2760 profile_crc=0;
2761
2762 unsigned char
2763 *data;
2764
2765 profile_length=(png_uint_32) GetStringInfoLength(profile);
2766
2767 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2768 {
2769 if (profile_length == sRGB_info[icheck].len)
2770 {
2771 if (got_crc == 0)
2772 {
2773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774 " Got a %lu-byte ICC profile (potentially sRGB)",
2775 (unsigned long) profile_length);
2776
2777 data=GetStringInfoDatum(profile);
2778 profile_crc=crc32(0,data,profile_length);
2779
2780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2781 " with crc=%8x",(unsigned int) profile_crc);
2782 got_crc++;
2783 }
2784
2785 if (profile_crc == sRGB_info[icheck].crc)
2786 {
2787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788 " It is sRGB with rendering intent = %s",
2789 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2790 sRGB_info[icheck].intent));
2791 if (image->rendering_intent==UndefinedIntent)
2792 {
2793 image->rendering_intent=
2794 Magick_RenderingIntent_from_PNG_RenderingIntent(
2795 sRGB_info[icheck].intent);
2796 }
2797 break;
2798 }
2799 }
2800 }
2801 if (sRGB_info[icheck].len == 0)
2802 {
2803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804 " Got %lu-byte ICC profile not recognized as sRGB",
2805 (unsigned long) profile_length);
2806 (void) SetImageProfile(image,"icc",profile,exception);
2807 }
2808 }
2809 else /* Preserve-iCCP */
2810 {
2811 (void) SetImageProfile(image,"icc",profile,exception);
2812 }
2813
2814 profile=DestroyStringInfo(profile);
2815 }
2816 }
2817 }
2818 #endif
2819
2820 #if defined(PNG_READ_sRGB_SUPPORTED)
2821 {
2822 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2823 PNG_INFO_sRGB))
2824 {
2825 if (png_get_sRGB(ping,ping_info,&intent))
2826 {
2827 if (image->rendering_intent == UndefinedIntent)
2828 image->rendering_intent=
2829 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2830
2831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2834 }
2835 }
2836
2837 else if (mng_info->have_global_srgb)
2838 {
2839 if (image->rendering_intent == UndefinedIntent)
2840 image->rendering_intent=
2841 Magick_RenderingIntent_from_PNG_RenderingIntent
2842 (mng_info->global_srgb_intent);
2843 }
2844 }
2845 #endif
2846
2847
2848 {
2849 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2850 if (mng_info->have_global_gama)
2851 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2852
2853 if (png_get_gAMA(ping,ping_info,&file_gamma))
2854 {
2855 image->gamma=file_gamma;
2856 if (logging != MagickFalse)
2857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2858 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2859 }
2860 }
2861
2862 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2863 {
2864 if (mng_info->have_global_chrm != MagickFalse)
2865 {
2866 (void) png_set_cHRM(ping,ping_info,
2867 mng_info->global_chrm.white_point.x,
2868 mng_info->global_chrm.white_point.y,
2869 mng_info->global_chrm.red_primary.x,
2870 mng_info->global_chrm.red_primary.y,
2871 mng_info->global_chrm.green_primary.x,
2872 mng_info->global_chrm.green_primary.y,
2873 mng_info->global_chrm.blue_primary.x,
2874 mng_info->global_chrm.blue_primary.y);
2875 }
2876 }
2877
2878 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2879 {
2880 (void) png_get_cHRM(ping,ping_info,
2881 &image->chromaticity.white_point.x,
2882 &image->chromaticity.white_point.y,
2883 &image->chromaticity.red_primary.x,
2884 &image->chromaticity.red_primary.y,
2885 &image->chromaticity.green_primary.x,
2886 &image->chromaticity.green_primary.y,
2887 &image->chromaticity.blue_primary.x,
2888 &image->chromaticity.blue_primary.y);
2889
2890 ping_found_cHRM=MagickTrue;
2891
2892 if (image->chromaticity.red_primary.x>0.6399f &&
2893 image->chromaticity.red_primary.x<0.6401f &&
2894 image->chromaticity.red_primary.y>0.3299f &&
2895 image->chromaticity.red_primary.y<0.3301f &&
2896 image->chromaticity.green_primary.x>0.2999f &&
2897 image->chromaticity.green_primary.x<0.3001f &&
2898 image->chromaticity.green_primary.y>0.5999f &&
2899 image->chromaticity.green_primary.y<0.6001f &&
2900 image->chromaticity.blue_primary.x>0.1499f &&
2901 image->chromaticity.blue_primary.x<0.1501f &&
2902 image->chromaticity.blue_primary.y>0.0599f &&
2903 image->chromaticity.blue_primary.y<0.0601f &&
2904 image->chromaticity.white_point.x>0.3126f &&
2905 image->chromaticity.white_point.x<0.3128f &&
2906 image->chromaticity.white_point.y>0.3289f &&
2907 image->chromaticity.white_point.y<0.3291f)
2908 ping_found_sRGB_cHRM=MagickTrue;
2909 }
2910
2911 if (image->rendering_intent != UndefinedIntent)
2912 {
2913 if (ping_found_sRGB != MagickTrue &&
2914 (ping_found_gAMA != MagickTrue ||
2915 (image->gamma > .45 && image->gamma < .46)) &&
2916 (ping_found_cHRM != MagickTrue ||
2917 ping_found_sRGB_cHRM != MagickFalse) &&
2918 ping_found_iCCP != MagickTrue)
2919 {
2920 png_set_sRGB(ping,ping_info,
2921 Magick_RenderingIntent_to_PNG_RenderingIntent
2922 (image->rendering_intent));
2923 file_gamma=0.45455f;
2924 ping_found_sRGB=MagickTrue;
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 " Setting sRGB as if in input");
2927 }
2928 }
2929
2930 #if defined(PNG_oFFs_SUPPORTED)
2931 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2932 {
2933 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2934 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2935
2936 if (logging != MagickFalse)
2937 if (image->page.x || image->page.y)
2938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2939 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2940 image->page.x,(double) image->page.y);
2941 }
2942 #endif
2943 #if defined(PNG_pHYs_SUPPORTED)
2944 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2945 {
2946 if (mng_info->have_global_phys)
2947 {
2948 png_set_pHYs(ping,ping_info,
2949 mng_info->global_x_pixels_per_unit,
2950 mng_info->global_y_pixels_per_unit,
2951 mng_info->global_phys_unit_type);
2952 }
2953 }
2954
2955 x_resolution=0;
2956 y_resolution=0;
2957 unit_type=0;
2958 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2959 {
2960 /*
2961 Set image resolution.
2962 */
2963 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2964 &unit_type);
2965 image->resolution.x=(double) x_resolution;
2966 image->resolution.y=(double) y_resolution;
2967
2968 if (unit_type == PNG_RESOLUTION_METER)
2969 {
2970 image->units=PixelsPerCentimeterResolution;
2971 image->resolution.x=(double) x_resolution/100.0;
2972 image->resolution.y=(double) y_resolution/100.0;
2973 }
2974
2975 if (logging != MagickFalse)
2976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2978 (double) x_resolution,(double) y_resolution,unit_type);
2979 }
2980 #endif
2981
2982 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2983 {
2984 png_colorp
2985 palette;
2986
2987 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2988
2989 if ((number_colors == 0) &&
2990 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2991 {
2992 if (mng_info->global_plte_length)
2993 {
2994 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2995 (int) mng_info->global_plte_length);
2996
2997 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2998 {
2999 if (mng_info->global_trns_length)
3000 {
3001 png_warning(ping,
3002 "global tRNS has more entries than global PLTE");
3003 }
3004 else
3005 {
3006 png_set_tRNS(ping,ping_info,mng_info->global_trns,
3007 (int) mng_info->global_trns_length,NULL);
3008 }
3009 }
3010 #ifdef PNG_READ_bKGD_SUPPORTED
3011 if (
3012 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3013 mng_info->have_saved_bkgd_index ||
3014 #endif
3015 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3016 {
3017 png_color_16
3018 background;
3019
3020 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3021 if (mng_info->have_saved_bkgd_index)
3022 background.index=mng_info->saved_bkgd_index;
3023 #endif
3024 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3025 background.index=ping_background->index;
3026
3027 background.red=(png_uint_16)
3028 mng_info->global_plte[background.index].red;
3029
3030 background.green=(png_uint_16)
3031 mng_info->global_plte[background.index].green;
3032
3033 background.blue=(png_uint_16)
3034 mng_info->global_plte[background.index].blue;
3035
3036 background.gray=(png_uint_16)
3037 mng_info->global_plte[background.index].green;
3038
3039 png_set_bKGD(ping,ping_info,&background);
3040 }
3041 #endif
3042 }
3043 else
3044 png_error(ping,"No global PLTE in file");
3045 }
3046 }
3047
3048 #ifdef PNG_READ_bKGD_SUPPORTED
3049 if (mng_info->have_global_bkgd &&
3050 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3051 image->background_color=mng_info->mng_global_bkgd;
3052
3053 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3054 {
3055 unsigned int
3056 bkgd_scale;
3057
3058 /* Set image background color.
3059 * Scale background components to 16-bit, then scale
3060 * to quantum depth
3061 */
3062
3063 bkgd_scale = 1;
3064
3065 if (ping_file_depth == 1)
3066 bkgd_scale = 255;
3067
3068 else if (ping_file_depth == 2)
3069 bkgd_scale = 85;
3070
3071 else if (ping_file_depth == 4)
3072 bkgd_scale = 17;
3073
3074 if (ping_file_depth <= 8)
3075 bkgd_scale *= 257;
3076
3077 ping_background->red *= bkgd_scale;
3078 ping_background->green *= bkgd_scale;
3079 ping_background->blue *= bkgd_scale;
3080
3081 if (logging != MagickFalse)
3082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3083 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3084 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3085 ping_background->red,ping_background->green,
3086 ping_background->blue,bkgd_scale,ping_background->red,
3087 ping_background->green,ping_background->blue);
3088
3089 image->background_color.red=
3090 ScaleShortToQuantum(ping_background->red);
3091
3092 image->background_color.green=
3093 ScaleShortToQuantum(ping_background->green);
3094
3095 image->background_color.blue=
3096 ScaleShortToQuantum(ping_background->blue);
3097
3098 image->background_color.alpha=OpaqueAlpha;
3099
3100 if (logging != MagickFalse)
3101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3102 " image->background_color=(%.20g,%.20g,%.20g).",
3103 (double) image->background_color.red,
3104 (double) image->background_color.green,
3105 (double) image->background_color.blue);
3106 }
3107 #endif /* PNG_READ_bKGD_SUPPORTED */
3108
3109 if ((png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
3110 (ping_trans_color != (png_color_16p) NULL))
3111 {
3112 /*
3113 Image has a tRNS chunk.
3114 */
3115 int
3116 max_sample;
3117
3118 size_t
3119 one = 1;
3120
3121 if (logging != MagickFalse)
3122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3123 " Reading PNG tRNS chunk.");
3124
3125 max_sample = (int) ((one << ping_file_depth) - 1);
3126
3127 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3128 (int)ping_trans_color->gray > max_sample) ||
3129 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3130 ((int)ping_trans_color->red > max_sample ||
3131 (int)ping_trans_color->green > max_sample ||
3132 (int)ping_trans_color->blue > max_sample)))
3133 {
3134 if (logging != MagickFalse)
3135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3136 " Ignoring PNG tRNS chunk with out-of-range sample.");
3137 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3138 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3139 image->alpha_trait=UndefinedPixelTrait;
3140 }
3141 else
3142 {
3143 int
3144 scale_to_short;
3145
3146 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3147
3148 /* Scale transparent_color to short */
3149 transparent_color.red= scale_to_short*ping_trans_color->red;
3150 transparent_color.green= scale_to_short*ping_trans_color->green;
3151 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3152 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3153
3154 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3155 {
3156 if (logging != MagickFalse)
3157 {
3158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3160 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3161
3162 }
3163 transparent_color.red=transparent_color.alpha;
3164 transparent_color.green=transparent_color.alpha;
3165 transparent_color.blue=transparent_color.alpha;
3166 }
3167 }
3168 }
3169 #if defined(PNG_READ_sBIT_SUPPORTED)
3170 if (mng_info->have_global_sbit)
3171 {
3172 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3173 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3174 }
3175 #endif
3176 num_passes=png_set_interlace_handling(ping);
3177
3178 png_read_update_info(ping,ping_info);
3179
3180 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3181
3182 /*
3183 Initialize image structure.
3184 */
3185 mng_info->image_box.left=0;
3186 mng_info->image_box.right=(ssize_t) ping_width;
3187 mng_info->image_box.top=0;
3188 mng_info->image_box.bottom=(ssize_t) ping_height;
3189 if (mng_info->mng_type == 0)
3190 {
3191 mng_info->mng_width=ping_width;
3192 mng_info->mng_height=ping_height;
3193 mng_info->frame=mng_info->image_box;
3194 mng_info->clip=mng_info->image_box;
3195 }
3196
3197 else
3198 {
3199 image->page.y=mng_info->y_off[mng_info->object_id];
3200 }
3201
3202 image->compression=ZipCompression;
3203 image->columns=ping_width;
3204 image->rows=ping_height;
3205
3206 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3207 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3208 {
3209 double
3210 image_gamma = image->gamma;
3211
3212 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3213 " image->gamma=%f",(float) image_gamma);
3214
3215 if (image_gamma > 0.75)
3216 {
3217 /* Set image->rendering_intent to Undefined,
3218 * image->colorspace to GRAY, and reset image->chromaticity.
3219 */
3220 SetImageColorspace(image,LinearGRAYColorspace,exception);
3221 }
3222 else
3223 {
3224 RenderingIntent
3225 save_rendering_intent = image->rendering_intent;
3226 ChromaticityInfo
3227 save_chromaticity = image->chromaticity;
3228
3229 SetImageColorspace(image,GRAYColorspace,exception);
3230 image->rendering_intent = save_rendering_intent;
3231 image->chromaticity = save_chromaticity;
3232 }
3233
3234 image->gamma = image_gamma;
3235 }
3236 else
3237 {
3238 double
3239 image_gamma = image->gamma;
3240
3241 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3242 " image->gamma=%f",(float) image_gamma);
3243
3244 if (image_gamma > 0.75)
3245 {
3246 /* Set image->rendering_intent to Undefined,
3247 * image->colorspace to GRAY, and reset image->chromaticity.
3248 */
3249 image->intensity = Rec709LuminancePixelIntensityMethod;
3250 SetImageColorspace(image,RGBColorspace,exception);
3251 }
3252 else
3253 {
3254 RenderingIntent
3255 save_rendering_intent = image->rendering_intent;
3256 ChromaticityInfo
3257 save_chromaticity = image->chromaticity;
3258
3259 SetImageColorspace(image,sRGBColorspace,exception);
3260 image->rendering_intent = save_rendering_intent;
3261 image->chromaticity = save_chromaticity;
3262 }
3263
3264 image->gamma = image_gamma;
3265 }
3266
3267 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3268 " image->colorspace=%d",(int) image->colorspace);
3269
3270 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3271 ((int) ping_bit_depth < 16 &&
3272 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3273 {
3274 size_t
3275 one;
3276
3277 image->storage_class=PseudoClass;
3278 one=1;
3279 image->colors=one << ping_file_depth;
3280 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3281 if (image->colors > 256)
3282 image->colors=256;
3283 #else
3284 if (image->colors > 65536L)
3285 image->colors=65536L;
3286 #endif
3287 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3288 {
3289 png_colorp
3290 palette;
3291
3292 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3293 image->colors=(size_t) number_colors;
3294
3295 if (logging != MagickFalse)
3296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3297 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3298 }
3299 }
3300
3301 if (image->storage_class == PseudoClass)
3302 {
3303 /*
3304 Initialize image colormap.
3305 */
3306 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3307 png_error(ping,"Memory allocation failed");
3308
3309 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3310 {
3311 png_colorp
3312 palette;
3313
3314 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3315
3316 for (i=0; i < (ssize_t) number_colors; i++)
3317 {
3318 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3319 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3320 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3321 }
3322
3323 for ( ; i < (ssize_t) image->colors; i++)
3324 {
3325 image->colormap[i].red=0;
3326 image->colormap[i].green=0;
3327 image->colormap[i].blue=0;
3328 }
3329 }
3330 }
3331
3332 /* Set some properties for reporting by "identify" */
3333 {
3334 char
3335 msg[MagickPathExtent];
3336
3337 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3338 ping_interlace_method in value */
3339
3340 (void) FormatLocaleString(msg,MagickPathExtent,
3341 "%d, %d",(int) ping_width, (int) ping_height);
3342 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3343
3344 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3345 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3346
3347 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3348 (int) ping_color_type,
3349 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3350 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3351
3352 if (ping_interlace_method == 0)
3353 {
3354 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3355 (int) ping_interlace_method);
3356 }
3357 else if (ping_interlace_method == 1)
3358 {
3359 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3360 (int) ping_interlace_method);
3361 }
3362 else
3363 {
3364 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3365 (int) ping_interlace_method);
3366 }
3367 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3368
3369 if (number_colors != 0)
3370 {
3371 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3372 (int) number_colors);
3373 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,exception);
3374 }
3375 }
3376 #if defined(PNG_tIME_SUPPORTED)
3377 read_tIME_chunk(image,ping,ping_info,exception);
3378 #endif
3379 #if defined(PNG_READ_eXIf_SUPPORTED)
3380 read_eXIf_chunk(image,ping,ping_info,exception);
3381 #endif
3382
3383
3384 /*
3385 Read image scanlines.
3386 */
3387 if (image->delay != 0)
3388 mng_info->scenes_found++;
3389
3390 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3391 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3392 (image_info->first_scene+image_info->number_scenes))))
3393 {
3394 /* This happens later in non-ping decodes */
3395 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3396 image->storage_class=DirectClass;
3397 image->alpha_trait=
3398 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3399 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3400 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3401 BlendPixelTrait : UndefinedPixelTrait;
3402
3403 if (logging != MagickFalse)
3404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3405 " Skipping PNG image data for scene %.20g",(double)
3406 mng_info->scenes_found-1);
3407 png_destroy_read_struct(&ping,&ping_info,&end_info);
3408
3409 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3410 UnlockSemaphoreInfo(ping_semaphore);
3411 #endif
3412
3413 if (logging != MagickFalse)
3414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415 " exit ReadOnePNGImage().");
3416
3417 return(image);
3418 }
3419
3420 if (logging != MagickFalse)
3421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3422 " Reading PNG IDAT chunk(s)");
3423
3424 status=SetImageExtent(image,image->columns,image->rows,exception);
3425 if (status != MagickFalse)
3426 status=ResetImagePixels(image,exception);
3427 if (status == MagickFalse)
3428 {
3429 png_destroy_read_struct(&ping,&ping_info,&end_info);
3430 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3431 UnlockSemaphoreInfo(ping_semaphore);
3432 #endif
3433 return(DestroyImageList(image));
3434 }
3435
3436 if (num_passes > 1)
3437 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3438 sizeof(*ping_pixels));
3439 else
3440 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3441
3442 if (pixel_info == (MemoryInfo *) NULL)
3443 png_error(ping,"Memory allocation failed");
3444 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3445
3446 if (logging != MagickFalse)
3447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3448 " Converting PNG pixels to pixel packets");
3449 /*
3450 Convert PNG pixels to pixel packets.
3451 */
3452 quantum_info=AcquireQuantumInfo(image_info,image);
3453
3454 if (quantum_info == (QuantumInfo *) NULL)
3455 png_error(ping,"Failed to allocate quantum_info");
3456
3457 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3458
3459 {
3460
3461 MagickBooleanType
3462 found_transparent_pixel;
3463
3464 found_transparent_pixel=MagickFalse;
3465
3466 if ((image->storage_class == DirectClass) ||
3467 (image_info->stream != (StreamHandler) NULL))
3468 {
3469 for (pass=0; pass < num_passes; pass++)
3470 {
3471 /*
3472 Convert image to DirectClass pixel packets.
3473 */
3474 image->alpha_trait=
3475 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3476 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3477 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3478 BlendPixelTrait : UndefinedPixelTrait;
3479
3480 for (y=0; y < (ssize_t) image->rows; y++)
3481 {
3482 if (num_passes > 1)
3483 row_offset=ping_rowbytes*y;
3484
3485 else
3486 row_offset=0;
3487
3488 png_read_row(ping,ping_pixels+row_offset,NULL);
3489
3490 if (pass < num_passes-1)
3491 continue;
3492
3493 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3494
3495 if (q == (Quantum *) NULL)
3496 break;
3497
3498 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3499 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3500 GrayQuantum,ping_pixels+row_offset,exception);
3501
3502 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3503 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3504 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3505
3506 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3507 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3508 RGBAQuantum,ping_pixels+row_offset,exception);
3509
3510 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3511 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3512 IndexQuantum,ping_pixels+row_offset,exception);
3513
3514 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3515 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3516 RGBQuantum,ping_pixels+row_offset,exception);
3517
3518 if (found_transparent_pixel == MagickFalse)
3519 {
3520 /* Is there a transparent pixel in the row? */
3521 if (y== 0 && logging != MagickFalse)
3522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523 " Looking for cheap transparent pixel");
3524
3525 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3526 {
3527 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3528 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3529 (GetPixelAlpha(image,q) != OpaqueAlpha))
3530 {
3531 if (logging != MagickFalse)
3532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3533 " ...got one.");
3534
3535 found_transparent_pixel = MagickTrue;
3536 break;
3537 }
3538 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3539 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3540 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3541 transparent_color.red &&
3542 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3543 transparent_color.green &&
3544 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3545 transparent_color.blue))
3546 {
3547 if (logging != MagickFalse)
3548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3549 " ...got one.");
3550 found_transparent_pixel = MagickTrue;
3551 break;
3552 }
3553 q+=GetPixelChannels(image);
3554 }
3555 }
3556
3557 if (num_passes == 1)
3558 {
3559 status=SetImageProgress(image,LoadImageTag,
3560 (MagickOffsetType) y, image->rows);
3561
3562 if (status == MagickFalse)
3563 break;
3564 }
3565 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3566 break;
3567 }
3568 if (y < (long) image->rows)
3569 break;
3570
3571 if (num_passes != 1)
3572 {
3573 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3574 if (status == MagickFalse)
3575 break;
3576 }
3577 }
3578 }
3579
3580 else /* image->storage_class != DirectClass */
3581
3582 for (pass=0; pass < num_passes; pass++)
3583 {
3584 Quantum
3585 *r;
3586
3587 /*
3588 Convert grayscale image to PseudoClass pixel packets.
3589 */
3590 if (logging != MagickFalse)
3591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3592 " Converting grayscale pixels to pixel packets");
3593
3594 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3595 BlendPixelTrait : UndefinedPixelTrait;
3596
3597 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3598 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3599 sizeof(*quantum_scanline));
3600
3601 if (quantum_scanline == (Quantum *) NULL)
3602 png_error(ping,"Memory allocation failed");
3603
3604 for (y=0; y < (ssize_t) image->rows; y++)
3605 {
3606 Quantum
3607 alpha;
3608
3609 if (num_passes > 1)
3610 row_offset=ping_rowbytes*y;
3611
3612 else
3613 row_offset=0;
3614
3615 png_read_row(ping,ping_pixels+row_offset,NULL);
3616
3617 if (pass < num_passes-1)
3618 continue;
3619
3620 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3621
3622 if (q == (Quantum *) NULL)
3623 break;
3624
3625 p=ping_pixels+row_offset;
3626 r=quantum_scanline;
3627
3628 switch (ping_bit_depth)
3629 {
3630 case 8:
3631 {
3632
3633 if (ping_color_type == 4)
3634 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3635 {
3636 *r++=*p++;
3637
3638 alpha=ScaleCharToQuantum((unsigned char)*p++);
3639
3640 SetPixelAlpha(image,alpha,q);
3641
3642 if (alpha != OpaqueAlpha)
3643 found_transparent_pixel = MagickTrue;
3644
3645 q+=GetPixelChannels(image);
3646 }
3647
3648 else
3649 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3650 *r++=*p++;
3651
3652 break;
3653 }
3654
3655 case 16:
3656 {
3657 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3658 {
3659 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3660 unsigned long
3661 quantum;
3662
3663 if (image->colors > 256)
3664 quantum=(((unsigned int) *p++) << 8);
3665
3666 else
3667 quantum=0;
3668
3669 quantum|=(*p++);
3670 *r=ScaleShortToQuantum(quantum);
3671 r++;
3672
3673 if (ping_color_type == 4)
3674 {
3675 if (image->colors > 256)
3676 quantum=(((unsigned int) *p++) << 8);
3677 else
3678 quantum=0;
3679
3680 quantum|=(*p++);
3681
3682 alpha=ScaleShortToQuantum(quantum);
3683 SetPixelAlpha(image,alpha,q);
3684
3685 if (alpha != OpaqueAlpha)
3686 found_transparent_pixel = MagickTrue;
3687
3688 q+=GetPixelChannels(image);
3689 }
3690
3691 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3692 *r++=(*p++);
3693 p++; /* strip low byte */
3694
3695 if (ping_color_type == 4)
3696 {
3697 SetPixelAlpha(image,*p++,q);
3698
3699 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3700 found_transparent_pixel = MagickTrue;
3701
3702 p++;
3703 q+=GetPixelChannels(image);
3704 }
3705 #endif
3706 }
3707
3708 break;
3709 }
3710
3711 default:
3712 break;
3713 }
3714
3715 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3716 break;
3717
3718 /*
3719 Transfer image scanline.
3720 */
3721 r=quantum_scanline;
3722
3723 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3724
3725 if (q == (Quantum *) NULL)
3726 break;
3727 for (x=0; x < (ssize_t) image->columns; x++)
3728 {
3729 ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3730 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3731 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3732 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3733 SetPixelIndex(image,index,q);
3734 r++;
3735 q+=GetPixelChannels(image);
3736 }
3737
3738 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3739 break;
3740
3741 if (num_passes == 1)
3742 {
3743 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3744 image->rows);
3745
3746 if (status == MagickFalse)
3747 break;
3748 }
3749 }
3750 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3751 if (y < (long) image->rows)
3752 break;
3753 if (num_passes != 1)
3754 {
3755 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3756
3757 if (status == MagickFalse)
3758 break;
3759 }
3760 }
3761
3762 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3763 UndefinedPixelTrait;
3764
3765 if (logging != MagickFalse)
3766 {
3767 if (found_transparent_pixel != MagickFalse)
3768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3769 " Found transparent pixel");
3770 else
3771 {
3772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773 " No transparent pixel was found");
3774
3775 ping_color_type&=0x03;
3776 }
3777 }
3778 }
3779 quantum_info=DestroyQuantumInfo(quantum_info);
3780
3781 png_read_end(ping,end_info);
3782
3783 if (logging != MagickFalse)
3784 {
3785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3786 " image->storage_class=%d\n",(int) image->storage_class);
3787 }
3788
3789 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3790 (ssize_t) image_info->first_scene && image->delay != 0)
3791 {
3792 png_destroy_read_struct(&ping,&ping_info,&end_info);
3793 pixel_info=RelinquishVirtualMemory(pixel_info);
3794 if (AcquireImageColormap(image,2,exception) == MagickFalse)
3795 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3796 (void) SetImageBackgroundColor(image,exception);
3797 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3798 UnlockSemaphoreInfo(ping_semaphore);
3799 #endif
3800 if (logging != MagickFalse)
3801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3802 " exit ReadOnePNGImage() early.");
3803 return(image);
3804 }
3805
3806 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3807 {
3808 ClassType
3809 storage_class;
3810
3811 /*
3812 Image has a transparent background.
3813 */
3814 storage_class=image->storage_class;
3815 image->alpha_trait=BlendPixelTrait;
3816
3817 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3818
3819 if (storage_class == PseudoClass)
3820 {
3821 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3822 {
3823 for (x=0; x < ping_num_trans; x++)
3824 {
3825 image->colormap[x].alpha_trait=BlendPixelTrait;
3826 image->colormap[x].alpha=OpaqueAlpha;
3827 if (ping_trans_alpha != (png_bytep) NULL)
3828 image->colormap[x].alpha=ScaleCharToQuantum(
3829 (unsigned char) ping_trans_alpha[x]);
3830 }
3831 }
3832
3833 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3834 {
3835 for (x=0; x < (int) image->colors; x++)
3836 {
3837 if (ScaleQuantumToShort(image->colormap[x].red) ==
3838 transparent_color.alpha)
3839 {
3840 image->colormap[x].alpha_trait=BlendPixelTrait;
3841 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3842 }
3843 }
3844 }
3845 (void) SyncImage(image,exception);
3846 }
3847
3848 #if 1 /* Should have already been done above, but glennrp problem P10
3849 * needs this.
3850 */
3851 else
3852 {
3853 for (y=0; y < (ssize_t) image->rows; y++)
3854 {
3855 image->storage_class=storage_class;
3856 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3857
3858 if (q == (Quantum *) NULL)
3859 break;
3860
3861
3862 /* Caution: on a Q8 build, this does not distinguish between
3863 * 16-bit colors that differ only in the low byte
3864 */
3865 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3866 {
3867 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3868 transparent_color.red &&
3869 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3870 transparent_color.green &&
3871 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3872 transparent_color.blue)
3873 {
3874 SetPixelAlpha(image,TransparentAlpha,q);
3875 }
3876 else
3877 {
3878 SetPixelAlpha(image,OpaqueAlpha,q);
3879 }
3880
3881 q+=GetPixelChannels(image);
3882 }
3883
3884 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3885 break;
3886 }
3887 }
3888 #endif
3889
3890 image->storage_class=DirectClass;
3891 }
3892
3893 for (j = 0; j < 2; j++)
3894 {
3895 if (j == 0)
3896 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3897 MagickTrue : MagickFalse;
3898 else
3899 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3900 MagickTrue : MagickFalse;
3901
3902 if (status != MagickFalse)
3903 for (i=0; i < (ssize_t) num_text; i++)
3904 {
3905 /* Check for a profile */
3906
3907 if (logging != MagickFalse)
3908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3909 " Reading PNG text chunk");
3910
3911 if (strlen(text[i].key) > 16 &&
3912 memcmp(text[i].key, "Raw profile type ",17) == 0)
3913 {
3914 const char
3915 *value;
3916
3917 value=GetImageOption(image_info,"profile:skip");
3918
3919 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3920 {
3921 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3922 (int) i,exception);
3923 num_raw_profiles++;
3924 if (logging != MagickFalse)
3925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3926 " Read raw profile %s",text[i].key+17);
3927 }
3928 else
3929 {
3930 if (logging != MagickFalse)
3931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3932 " Skipping raw profile %s",text[i].key+17);
3933 }
3934 }
3935
3936 else
3937 {
3938 char
3939 *value;
3940
3941 length=text[i].text_length;
3942 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3943 sizeof(*value));
3944 if (value == (char *) NULL)
3945 {
3946 png_error(ping,"Memory allocation failed");
3947 break;
3948 }
3949 *value='\0';
3950 (void) ConcatenateMagickString(value,text[i].text,length+2);
3951
3952 /* Don't save "density" or "units" property if we have a pHYs
3953 * chunk
3954 */
3955 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3956 (LocaleCompare(text[i].key,"density") != 0 &&
3957 LocaleCompare(text[i].key,"units") != 0))
3958 {
3959 char
3960 key[MagickPathExtent];
3961
3962 (void) FormatLocaleString(key,MagickPathExtent,"%s",
3963 text[i].key);
3964 if ((LocaleCompare(key,"version") == 0) ||
3965 (LocaleCompare(key,"width") == 0))
3966 (void) FormatLocaleString(key,MagickPathExtent,"png:%s",
3967 text[i].key);
3968 (void) SetImageProperty(image,key,value,exception);
3969 }
3970
3971 if (logging != MagickFalse)
3972 {
3973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3974 " length: %lu\n"
3975 " Keyword: %s",
3976 (unsigned long) length,
3977 text[i].key);
3978 }
3979
3980 value=DestroyString(value);
3981 }
3982 }
3983 num_text_total += num_text;
3984 }
3985
3986 #ifdef MNG_OBJECT_BUFFERS
3987 /*
3988 Store the object if necessary.
3989 */
3990 if (object_id && !mng_info->frozen[object_id])
3991 {
3992 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3993 {
3994 /*
3995 create a new object buffer.
3996 */
3997 mng_info->ob[object_id]=(MngBuffer *)
3998 AcquireMagickMemory(sizeof(MngBuffer));
3999
4000 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4001 {
4002 mng_info->ob[object_id]->image=(Image *) NULL;
4003 mng_info->ob[object_id]->reference_count=1;
4004 }
4005 }
4006
4007 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4008 mng_info->ob[object_id]->frozen)
4009 {
4010 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4011 png_error(ping,"Memory allocation failed");
4012
4013 if (mng_info->ob[object_id]->frozen)
4014 png_error(ping,"Cannot overwrite frozen MNG object buffer");
4015 }
4016
4017 else
4018 {
4019
4020 if (mng_info->ob[object_id]->image != (Image *) NULL)
4021 mng_info->ob[object_id]->image=DestroyImage
4022 (mng_info->ob[object_id]->image);
4023
4024 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4025 exception);
4026
4027 if (mng_info->ob[object_id]->image != (Image *) NULL)
4028 mng_info->ob[object_id]->image->file=(FILE *) NULL;
4029
4030 else
4031 png_error(ping, "Cloning image for object buffer failed");
4032
4033 if (ping_width > 250000L || ping_height > 250000L)
4034 png_error(ping,"PNG Image dimensions are too large.");
4035
4036 mng_info->ob[object_id]->width=ping_width;
4037 mng_info->ob[object_id]->height=ping_height;
4038 mng_info->ob[object_id]->color_type=ping_color_type;
4039 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4040 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4041 mng_info->ob[object_id]->compression_method=
4042 ping_compression_method;
4043 mng_info->ob[object_id]->filter_method=ping_filter_method;
4044
4045 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4046 {
4047 png_colorp
4048 plte;
4049
4050 /*
4051 Copy the PLTE to the object buffer.
4052 */
4053 png_get_PLTE(ping,ping_info,&plte,&number_colors);
4054 mng_info->ob[object_id]->plte_length=number_colors;
4055
4056 for (i=0; i < number_colors; i++)
4057 {
4058 mng_info->ob[object_id]->plte[i]=plte[i];
4059 }
4060 }
4061
4062 else
4063 mng_info->ob[object_id]->plte_length=0;
4064 }
4065 }
4066 #endif
4067
4068 /* Set image->alpha_trait to MagickTrue if the input colortype supports
4069 * alpha or if a valid tRNS chunk is present, no matter whether there
4070 * is actual transparency present.
4071 */
4072 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4073 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4074 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4075 BlendPixelTrait : UndefinedPixelTrait;
4076 if (image->alpha_trait == BlendPixelTrait)
4077 (void) SetImageStorageClass(image,DirectClass,exception);
4078
4079 #if 0 /* I'm not sure what's wrong here but it does not work. */
4080 if (image->alpha_trait != UndefinedPixelTrait)
4081 {
4082 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4083 (void) SetImageType(image,GrayscaleAlphaType,exception);
4084
4085 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4086 (void) SetImageType(image,PaletteAlphaType,exception);
4087
4088 else
4089 (void) SetImageType(image,TrueColorAlphaType,exception);
4090 }
4091
4092 else
4093 {
4094 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4095 (void) SetImageType(image,GrayscaleType,exception);
4096
4097 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4098 (void) SetImageType(image,PaletteType,exception);
4099
4100 else
4101 (void) SetImageType(image,TrueColorType,exception);
4102 }
4103 #endif
4104
4105 /* Set more properties for identify to retrieve */
4106 {
4107 char
4108 msg[MagickPathExtent];
4109
4110 if (num_text_total != 0)
4111 {
4112 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4113 (void) FormatLocaleString(msg,MagickPathExtent,
4114 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4115 (void) SetImageProperty(image,"png:text",msg,exception);
4116 }
4117
4118 if (num_raw_profiles != 0)
4119 {
4120 (void) FormatLocaleString(msg,MagickPathExtent,
4121 "%d were found", num_raw_profiles);
4122 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4123 exception);
4124 }
4125
4126 /* cHRM chunk: */
4127 if (ping_found_cHRM != MagickFalse)
4128 {
4129 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4130 "chunk was found (see Chromaticity, above)");
4131 (void) SetImageProperty(image,"png:cHRM",msg,exception);
4132 }
4133
4134 /* bKGD chunk: */
4135 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4136 {
4137 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4138 "chunk was found (see Background color, above)");
4139 (void) SetImageProperty(image,"png:bKGD",msg,exception);
4140 }
4141
4142 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4143 "chunk was found");
4144
4145 #if defined(PNG_iCCP_SUPPORTED)
4146 /* iCCP chunk: */
4147 if (ping_found_iCCP != MagickFalse)
4148 (void) SetImageProperty(image,"png:iCCP",msg,exception);
4149 #endif
4150
4151 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4152 (void) SetImageProperty(image,"png:tRNS",msg,exception);
4153
4154 #if defined(PNG_sRGB_SUPPORTED)
4155 /* sRGB chunk: */
4156 if (ping_found_sRGB != MagickFalse)
4157 {
4158 (void) FormatLocaleString(msg,MagickPathExtent,
4159 "intent=%d (%s)",
4160 (int) intent,
4161 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4162 (void) SetImageProperty(image,"png:sRGB",msg,exception);
4163 }
4164 #endif
4165
4166 /* gAMA chunk: */
4167 if (ping_found_gAMA != MagickFalse)
4168 {
4169 (void) FormatLocaleString(msg,MagickPathExtent,
4170 "gamma=%.8g (See Gamma, above)",
4171 file_gamma);
4172 (void) SetImageProperty(image,"png:gAMA",msg,exception);
4173 }
4174
4175 #if defined(PNG_pHYs_SUPPORTED)
4176 /* pHYs chunk: */
4177 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4178 {
4179 (void) FormatLocaleString(msg,MagickPathExtent,
4180 "x_res=%.10g, y_res=%.10g, units=%d",
4181 (double) x_resolution,(double) y_resolution, unit_type);
4182 (void) SetImageProperty(image,"png:pHYs",msg,exception);
4183 }
4184 #endif
4185
4186 #if defined(PNG_oFFs_SUPPORTED)
4187 /* oFFs chunk: */
4188 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4189 {
4190 (void) FormatLocaleString(msg,MagickPathExtent,
4191 "x_off=%.20g, y_off=%.20g",
4192 (double) image->page.x,(double) image->page.y);
4193 (void) SetImageProperty(image,"png:oFFs",msg,exception);
4194 }
4195 #endif
4196
4197 #if defined(PNG_tIME_SUPPORTED)
4198 read_tIME_chunk(image,ping,end_info,exception);
4199 #endif
4200 #if defined(PNG_READ_eXIf_SUPPORTED)
4201 read_eXIf_chunk(image,ping,end_info,exception);
4202 #endif
4203
4204 /* caNv chunk: */
4205 if ((image->page.width != 0 && image->page.width != image->columns) ||
4206 (image->page.height != 0 && image->page.height != image->rows) ||
4207 (image->page.x != 0 || image->page.y != 0))
4208 {
4209 (void) FormatLocaleString(msg,MagickPathExtent,
4210 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4211 (double) image->page.width,(double) image->page.height,
4212 (double) image->page.x,(double) image->page.y);
4213 (void) SetImageProperty(image,"png:caNv",msg,exception);
4214 }
4215 }
4216
4217 /*
4218 Relinquish resources.
4219 */
4220 png_destroy_read_struct(&ping,&ping_info,&end_info);
4221
4222 pixel_info=RelinquishVirtualMemory(pixel_info);
4223
4224 if (logging != MagickFalse)
4225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4226 " exit ReadOnePNGImage()");
4227
4228 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4229 UnlockSemaphoreInfo(ping_semaphore);
4230 #endif
4231
4232 /* } for navigation to beginning of SETJMP-protected block, revert to
4233 * Throwing an Exception when an error occurs.
4234 */
4235
4236 return(image);
4237
4238 /* end of reading one PNG image */
4239 }
4240
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4241 static Image *ReadPNGImage(const ImageInfo *image_info,
4242 ExceptionInfo *exception)
4243 {
4244 Image
4245 *image;
4246
4247 MagickBooleanType
4248 logging,
4249 status;
4250
4251 MngInfo
4252 *mng_info;
4253
4254 char
4255 magic_number[MagickPathExtent];
4256
4257 ssize_t
4258 count;
4259
4260 /*
4261 Open image file.
4262 */
4263 assert(image_info != (const ImageInfo *) NULL);
4264 assert(image_info->signature == MagickCoreSignature);
4265
4266 if (image_info->debug != MagickFalse)
4267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4268 image_info->filename);
4269
4270 assert(exception != (ExceptionInfo *) NULL);
4271 assert(exception->signature == MagickCoreSignature);
4272 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4273 image=AcquireImage(image_info,exception);
4274 mng_info=(MngInfo *) NULL;
4275 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4276
4277 if (status == MagickFalse)
4278 return(DestroyImageList(image));
4279
4280 /*
4281 Verify PNG signature.
4282 */
4283 count=ReadBlob(image,8,(unsigned char *) magic_number);
4284
4285 if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4286 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4287
4288 /*
4289 Verify that file size large enough to contain a PNG datastream.
4290 */
4291 if (GetBlobSize(image) < 61)
4292 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4293
4294 /*
4295 Allocate a MngInfo structure.
4296 */
4297 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4298
4299 if (mng_info == (MngInfo *) NULL)
4300 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4301
4302 /*
4303 Initialize members of the MngInfo structure.
4304 */
4305 (void) memset(mng_info,0,sizeof(MngInfo));
4306 mng_info->image=image;
4307
4308 image=ReadOnePNGImage(mng_info,image_info,exception);
4309 mng_info=MngInfoFreeStruct(mng_info);
4310
4311 if (image == (Image *) NULL)
4312 {
4313 if (logging != MagickFalse)
4314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4315 "exit ReadPNGImage() with error");
4316
4317 return((Image *) NULL);
4318 }
4319
4320 (void) CloseBlob(image);
4321
4322 if ((image->columns == 0) || (image->rows == 0))
4323 {
4324 if (logging != MagickFalse)
4325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4326 "exit ReadPNGImage() with error.");
4327
4328 ThrowReaderException(CorruptImageError,"CorruptImage");
4329 }
4330
4331 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4332 (image->gamma > .75) &&
4333 !(image->chromaticity.red_primary.x>0.6399f &&
4334 image->chromaticity.red_primary.x<0.6401f &&
4335 image->chromaticity.red_primary.y>0.3299f &&
4336 image->chromaticity.red_primary.y<0.3301f &&
4337 image->chromaticity.green_primary.x>0.2999f &&
4338 image->chromaticity.green_primary.x<0.3001f &&
4339 image->chromaticity.green_primary.y>0.5999f &&
4340 image->chromaticity.green_primary.y<0.6001f &&
4341 image->chromaticity.blue_primary.x>0.1499f &&
4342 image->chromaticity.blue_primary.x<0.1501f &&
4343 image->chromaticity.blue_primary.y>0.0599f &&
4344 image->chromaticity.blue_primary.y<0.0601f &&
4345 image->chromaticity.white_point.x>0.3126f &&
4346 image->chromaticity.white_point.x<0.3128f &&
4347 image->chromaticity.white_point.y>0.3289f &&
4348 image->chromaticity.white_point.y<0.3291f))
4349 {
4350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4351 "SetImageColorspace to RGBColorspace");
4352 SetImageColorspace(image,RGBColorspace,exception);
4353 }
4354
4355 if (logging != MagickFalse)
4356 {
4357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4358 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4359 (double) image->page.width,(double) image->page.height,
4360 (double) image->page.x,(double) image->page.y);
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362 " image->colorspace: %d", (int) image->colorspace);
4363 }
4364
4365 if (logging != MagickFalse)
4366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4367
4368 return(image);
4369 }
4370
4371
4372
4373 #if defined(JNG_SUPPORTED)
4374 /*
4375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4376 % %
4377 % %
4378 % %
4379 % R e a d O n e J N G I m a g e %
4380 % %
4381 % %
4382 % %
4383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4384 %
4385 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4386 % (minus the 8-byte signature) and returns it. It allocates the memory
4387 % necessary for the new Image structure and returns a pointer to the new
4388 % image.
4389 %
4390 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4391 %
4392 % The format of the ReadOneJNGImage method is:
4393 %
4394 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4395 % ExceptionInfo *exception)
4396 %
4397 % A description of each parameter follows:
4398 %
4399 % o mng_info: Specifies a pointer to a MngInfo structure.
4400 %
4401 % o image_info: the image info.
4402 %
4403 % o exception: return any errors or warnings in this structure.
4404 %
4405 */
4406 static void
DestroyJNG(unsigned char * chunk,Image ** color_image,ImageInfo ** color_image_info,Image ** alpha_image,ImageInfo ** alpha_image_info)4407 DestroyJNG(unsigned char *chunk,Image **color_image,
4408 ImageInfo **color_image_info,Image **alpha_image,
4409 ImageInfo **alpha_image_info)
4410 {
4411 (void) RelinquishMagickMemory(chunk);
4412 if (color_image_info && *color_image_info)
4413 {
4414 DestroyImageInfo(*color_image_info);
4415 *color_image_info = (ImageInfo *)NULL;
4416 }
4417 if (alpha_image_info && *alpha_image_info)
4418 {
4419 DestroyImageInfo(*alpha_image_info);
4420 *alpha_image_info = (ImageInfo *)NULL;
4421 }
4422 if (color_image && *color_image)
4423 {
4424 DestroyImageList(*color_image);
4425 *color_image = (Image *)NULL;
4426 }
4427 if (alpha_image && *alpha_image)
4428 {
4429 DestroyImageList(*alpha_image);
4430 *alpha_image = (Image *)NULL;
4431 }
4432 }
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4433 static Image *ReadOneJNGImage(MngInfo *mng_info,
4434 const ImageInfo *image_info, ExceptionInfo *exception)
4435 {
4436 Image
4437 *alpha_image,
4438 *color_image,
4439 *image,
4440 *jng_image;
4441
4442 ImageInfo
4443 *alpha_image_info,
4444 *color_image_info;
4445
4446 MagickBooleanType
4447 logging;
4448
4449 ssize_t
4450 y;
4451
4452 MagickBooleanType
4453 status;
4454
4455 png_uint_32
4456 jng_height,
4457 jng_width;
4458
4459 png_byte
4460 jng_color_type,
4461 jng_image_sample_depth,
4462 jng_image_compression_method,
4463 jng_image_interlace_method,
4464 jng_alpha_sample_depth,
4465 jng_alpha_compression_method,
4466 jng_alpha_filter_method,
4467 jng_alpha_interlace_method;
4468
4469 const Quantum
4470 *s;
4471
4472 ssize_t
4473 i,
4474 x;
4475
4476 Quantum
4477 *q;
4478
4479 unsigned char
4480 *p;
4481
4482 unsigned int
4483 read_JSEP,
4484 reading_idat;
4485
4486 size_t
4487 length;
4488
4489 jng_alpha_compression_method=0;
4490 jng_alpha_sample_depth=8;
4491 jng_color_type=0;
4492 jng_height=0;
4493 jng_width=0;
4494 alpha_image=(Image *) NULL;
4495 color_image=(Image *) NULL;
4496 alpha_image_info=(ImageInfo *) NULL;
4497 color_image_info=(ImageInfo *) NULL;
4498
4499 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4500 " Enter ReadOneJNGImage()");
4501
4502 image=mng_info->image;
4503
4504 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4505 {
4506 /*
4507 Allocate next image structure.
4508 */
4509 if (logging != MagickFalse)
4510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4511 " AcquireNextImage()");
4512
4513 AcquireNextImage(image_info,image,exception);
4514
4515 if (GetNextImageInList(image) == (Image *) NULL)
4516 return(DestroyImageList(image));
4517
4518 image=SyncNextImageInList(image);
4519 }
4520 mng_info->image=image;
4521
4522 /*
4523 Signature bytes have already been read.
4524 */
4525
4526 read_JSEP=MagickFalse;
4527 reading_idat=MagickFalse;
4528 for (;;)
4529 {
4530 char
4531 type[MagickPathExtent];
4532
4533 unsigned char
4534 *chunk;
4535
4536 unsigned int
4537 count;
4538
4539 /*
4540 Read a new JNG chunk.
4541 */
4542 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4543 2*GetBlobSize(image));
4544
4545 if (status == MagickFalse)
4546 break;
4547
4548 type[0]='\0';
4549 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4550 length=(size_t) ReadBlobMSBLong(image);
4551 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4552
4553 if (logging != MagickFalse)
4554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4555 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4556 type[0],type[1],type[2],type[3],(double) length);
4557
4558 if (length > PNG_UINT_31_MAX || count == 0)
4559 {
4560 DestroyJNG(NULL,&color_image,&color_image_info,
4561 &alpha_image,&alpha_image_info);
4562 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4563 }
4564 if (length > GetBlobSize(image))
4565 {
4566 DestroyJNG(NULL,&color_image,&color_image_info,
4567 &alpha_image,&alpha_image_info);
4568 ThrowReaderException(CorruptImageError,
4569 "InsufficientImageDataInFile");
4570 }
4571
4572 p=NULL;
4573 chunk=(unsigned char *) NULL;
4574
4575 if (length != 0)
4576 {
4577 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4578
4579 if (chunk == (unsigned char *) NULL)
4580 {
4581 DestroyJNG(NULL,&color_image,&color_image_info,
4582 &alpha_image,&alpha_image_info);
4583 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4584 }
4585
4586 for (i=0; i < (ssize_t) length; i++)
4587 {
4588 int
4589 c;
4590
4591 c=ReadBlobByte(image);
4592 if (c == EOF)
4593 break;
4594 chunk[i]=(unsigned char) c;
4595 }
4596 for ( ; i < (ssize_t) length; i++)
4597 chunk[i]='\0';
4598
4599 p=chunk;
4600 }
4601
4602 (void) ReadBlobMSBLong(image); /* read crc word */
4603
4604 if (memcmp(type,mng_JHDR,4) == 0)
4605 {
4606 if (length == 16)
4607 {
4608 jng_width=(png_uint_32)mng_get_long(p);
4609 jng_height=(png_uint_32)mng_get_long(&p[4]);
4610 if ((jng_width == 0) || (jng_height == 0))
4611 {
4612 DestroyJNG(chunk,&color_image,&color_image_info,
4613 &alpha_image,&alpha_image_info);
4614 ThrowReaderException(CorruptImageError,
4615 "NegativeOrZeroImageSize");
4616 }
4617 jng_color_type=p[8];
4618 jng_image_sample_depth=p[9];
4619 jng_image_compression_method=p[10];
4620 jng_image_interlace_method=p[11];
4621
4622 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4623 NoInterlace;
4624
4625 jng_alpha_sample_depth=p[12];
4626 jng_alpha_compression_method=p[13];
4627 jng_alpha_filter_method=p[14];
4628 jng_alpha_interlace_method=p[15];
4629
4630 if (logging != MagickFalse)
4631 {
4632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4633 " jng_width: %16lu, jng_height: %16lu\n"
4634 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4635 " jng_image_compression_method:%3d",
4636 (unsigned long) jng_width, (unsigned long) jng_height,
4637 jng_color_type, jng_image_sample_depth,
4638 jng_image_compression_method);
4639
4640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4641 " jng_image_interlace_method: %3d"
4642 " jng_alpha_sample_depth: %3d",
4643 jng_image_interlace_method,
4644 jng_alpha_sample_depth);
4645
4646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4647 " jng_alpha_compression_method:%3d\n"
4648 " jng_alpha_filter_method: %3d\n"
4649 " jng_alpha_interlace_method: %3d",
4650 jng_alpha_compression_method,
4651 jng_alpha_filter_method,
4652 jng_alpha_interlace_method);
4653 }
4654 }
4655
4656 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4657
4658 if ((jng_width > 65535) || (jng_height > 65535) ||
4659 (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4660 (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4661 {
4662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663 " JNG width or height too large: (%lu x %lu)",
4664 (long) jng_width, (long) jng_height);
4665 DestroyJNG(chunk,&color_image,&color_image_info,
4666 &alpha_image,&alpha_image_info);
4667 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4668 }
4669
4670 continue;
4671 }
4672
4673
4674 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4675 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4676 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4677 {
4678 /*
4679 o create color_image
4680 o open color_blob, attached to color_image
4681 o if (color type has alpha)
4682 open alpha_blob, attached to alpha_image
4683 */
4684
4685 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4686
4687 if (color_image_info == (ImageInfo *) NULL)
4688 {
4689 DestroyJNG(chunk,&color_image,&color_image_info,
4690 &alpha_image,&alpha_image_info);
4691 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4692 }
4693
4694 GetImageInfo(color_image_info);
4695 color_image=AcquireImage(color_image_info,exception);
4696
4697 if (color_image == (Image *) NULL)
4698 {
4699 DestroyJNG(chunk,&color_image,&color_image_info,
4700 &alpha_image,&alpha_image_info);
4701 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4702 }
4703
4704 if (logging != MagickFalse)
4705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4706 " Creating color_blob.");
4707
4708 (void) AcquireUniqueFilename(color_image->filename);
4709 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4710 exception);
4711
4712 if (status == MagickFalse)
4713 {
4714 DestroyJNG(chunk,&color_image,&color_image_info,
4715 &alpha_image,&alpha_image_info);
4716 return(DestroyImageList(image));
4717 }
4718
4719 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4720 {
4721 if ((jng_alpha_compression_method != 0) &&
4722 (jng_alpha_compression_method != 8))
4723 {
4724 DestroyJNG(chunk,&color_image,&color_image_info,&alpha_image,
4725 &alpha_image_info);
4726 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4727 }
4728 alpha_image_info=(ImageInfo *)
4729 AcquireMagickMemory(sizeof(ImageInfo));
4730
4731 if (alpha_image_info == (ImageInfo *) NULL)
4732 {
4733 DestroyJNG(chunk,&color_image,&color_image_info,
4734 &alpha_image,&alpha_image_info);
4735 ThrowReaderException(ResourceLimitError,
4736 "MemoryAllocationFailed");
4737 }
4738
4739 GetImageInfo(alpha_image_info);
4740 alpha_image=AcquireImage(alpha_image_info,exception);
4741
4742 if (alpha_image == (Image *) NULL)
4743 {
4744 DestroyJNG(chunk,&color_image,&color_image_info,
4745 &alpha_image,&alpha_image_info);
4746 ThrowReaderException(ResourceLimitError,
4747 "MemoryAllocationFailed");
4748 }
4749
4750 if (logging != MagickFalse)
4751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4752 " Creating alpha_blob.");
4753
4754 (void) AcquireUniqueFilename(alpha_image->filename);
4755 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4756 exception);
4757
4758 if (status == MagickFalse)
4759 {
4760 DestroyJNG(chunk,&color_image,&color_image_info,
4761 &alpha_image,&alpha_image_info);
4762 return(DestroyImageList(image));
4763 }
4764
4765 if (jng_alpha_compression_method == 0)
4766 {
4767 unsigned char
4768 data[18];
4769
4770 if (logging != MagickFalse)
4771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4772 " Writing IHDR chunk to alpha_blob.");
4773
4774 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4775 "\211PNG\r\n\032\n");
4776
4777 (void) WriteBlobMSBULong(alpha_image,13L);
4778 PNGType(data,mng_IHDR);
4779 LogPNGChunk(logging,mng_IHDR,13L);
4780 PNGLong(data+4,jng_width);
4781 PNGLong(data+8,jng_height);
4782 data[12]=jng_alpha_sample_depth;
4783 data[13]=0; /* color_type gray */
4784 data[14]=0; /* compression method 0 */
4785 data[15]=0; /* filter_method 0 */
4786 data[16]=0; /* interlace_method 0 */
4787 (void) WriteBlob(alpha_image,17,data);
4788 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4789 }
4790 }
4791 reading_idat=MagickTrue;
4792 }
4793
4794 if (memcmp(type,mng_JDAT,4) == 0)
4795 {
4796 /* Copy chunk to color_image->blob */
4797
4798 if (logging != MagickFalse)
4799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4800 " Copying JDAT chunk data to color_blob.");
4801
4802 if ((length != 0) && (color_image != (Image *) NULL))
4803 (void) WriteBlob(color_image,length,chunk);
4804 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4805 continue;
4806 }
4807
4808 if (memcmp(type,mng_IDAT,4) == 0)
4809 {
4810 png_byte
4811 data[5];
4812
4813 /* Copy IDAT header and chunk data to alpha_image->blob */
4814
4815 if (alpha_image != NULL && image_info->ping == MagickFalse)
4816 {
4817 if (logging != MagickFalse)
4818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4819 " Copying IDAT chunk data to alpha_blob.");
4820
4821 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4822 PNGType(data,mng_IDAT);
4823 LogPNGChunk(logging,mng_IDAT,length);
4824 (void) WriteBlob(alpha_image,4,data);
4825 (void) WriteBlob(alpha_image,length,chunk);
4826 (void) WriteBlobMSBULong(alpha_image,
4827 crc32(crc32(0,data,4),chunk,(uInt) length));
4828 }
4829
4830 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4831
4832 continue;
4833 }
4834
4835 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4836 {
4837 /* Copy chunk data to alpha_image->blob */
4838
4839 if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4840 (length != 0))
4841 {
4842 if (logging != MagickFalse)
4843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4844 " Copying JDAA chunk data to alpha_blob.");
4845
4846 (void) WriteBlob(alpha_image,length,chunk);
4847 }
4848
4849 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4850
4851 continue;
4852 }
4853
4854 if (memcmp(type,mng_JSEP,4) == 0)
4855 {
4856 read_JSEP=MagickTrue;
4857
4858 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4859
4860 continue;
4861 }
4862
4863 if (memcmp(type,mng_bKGD,4) == 0)
4864 {
4865 if (length == 2)
4866 {
4867 image->background_color.red=ScaleCharToQuantum(p[1]);
4868 image->background_color.green=image->background_color.red;
4869 image->background_color.blue=image->background_color.red;
4870 }
4871
4872 if (length == 6)
4873 {
4874 image->background_color.red=ScaleCharToQuantum(p[1]);
4875 image->background_color.green=ScaleCharToQuantum(p[3]);
4876 image->background_color.blue=ScaleCharToQuantum(p[5]);
4877 }
4878
4879 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4880 continue;
4881 }
4882
4883 if (memcmp(type,mng_gAMA,4) == 0)
4884 {
4885 if (length == 4)
4886 image->gamma=((float) mng_get_long(p))*0.00001;
4887
4888 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4889 continue;
4890 }
4891
4892 if (memcmp(type,mng_cHRM,4) == 0)
4893 {
4894 if (length == 32)
4895 {
4896 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4897 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4898 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4899 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4900 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4901 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4902 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4903 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4904 }
4905
4906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4907 continue;
4908 }
4909
4910 if (memcmp(type,mng_sRGB,4) == 0)
4911 {
4912 if (length == 1)
4913 {
4914 image->rendering_intent=
4915 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4916 image->gamma=0.45455f;
4917 image->chromaticity.red_primary.x=0.6400f;
4918 image->chromaticity.red_primary.y=0.3300f;
4919 image->chromaticity.green_primary.x=0.3000f;
4920 image->chromaticity.green_primary.y=0.6000f;
4921 image->chromaticity.blue_primary.x=0.1500f;
4922 image->chromaticity.blue_primary.y=0.0600f;
4923 image->chromaticity.white_point.x=0.3127f;
4924 image->chromaticity.white_point.y=0.3290f;
4925 }
4926
4927 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4928 continue;
4929 }
4930
4931 if (memcmp(type,mng_oFFs,4) == 0)
4932 {
4933 if (length > 8)
4934 {
4935 image->page.x=(ssize_t) mng_get_long(p);
4936 image->page.y=(ssize_t) mng_get_long(&p[4]);
4937
4938 if ((int) p[8] != 0)
4939 {
4940 image->page.x/=10000;
4941 image->page.y/=10000;
4942 }
4943 }
4944
4945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4946
4947 continue;
4948 }
4949
4950 if (memcmp(type,mng_pHYs,4) == 0)
4951 {
4952 if (length > 8)
4953 {
4954 image->resolution.x=(double) mng_get_long(p);
4955 image->resolution.y=(double) mng_get_long(&p[4]);
4956 if ((int) p[8] == PNG_RESOLUTION_METER)
4957 {
4958 image->units=PixelsPerCentimeterResolution;
4959 image->resolution.x=image->resolution.x/100.0f;
4960 image->resolution.y=image->resolution.y/100.0f;
4961 }
4962 }
4963
4964 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4965 continue;
4966 }
4967
4968 #if 0
4969 if (memcmp(type,mng_iCCP,4) == 0)
4970 {
4971 /* To do: */
4972 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4973
4974 continue;
4975 }
4976 #endif
4977
4978 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4979
4980 if (memcmp(type,mng_IEND,4))
4981 continue;
4982
4983 break;
4984 }
4985
4986
4987 /* IEND found */
4988
4989 /*
4990 Finish up reading image data:
4991
4992 o read main image from color_blob.
4993
4994 o close color_blob.
4995
4996 o if (color_type has alpha)
4997 if alpha_encoding is PNG
4998 read secondary image from alpha_blob via ReadPNG
4999 if alpha_encoding is JPEG
5000 read secondary image from alpha_blob via ReadJPEG
5001
5002 o close alpha_blob.
5003
5004 o copy intensity of secondary image into
5005 alpha samples of main image.
5006
5007 o destroy the secondary image.
5008 */
5009
5010 if (color_image_info == (ImageInfo *) NULL)
5011 {
5012 assert(color_image == (Image *) NULL);
5013 assert(alpha_image == (Image *) NULL);
5014 if (color_image != (Image *) NULL)
5015 color_image=DestroyImageList(color_image);
5016 return(DestroyImageList(image));
5017 }
5018
5019 if (color_image == (Image *) NULL)
5020 {
5021 assert(alpha_image == (Image *) NULL);
5022 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5023 }
5024
5025 (void) SeekBlob(color_image,0,SEEK_SET);
5026
5027 if (logging != MagickFalse)
5028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5029 " Reading jng_image from color_blob.");
5030
5031 assert(color_image_info != (ImageInfo *) NULL);
5032 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5033 "jpeg:%s",color_image->filename);
5034
5035 color_image_info->ping=MagickFalse; /* To do: avoid this */
5036 jng_image=ReadImage(color_image_info,exception);
5037
5038 (void) RelinquishUniqueFileResource(color_image->filename);
5039 color_image=DestroyImageList(color_image);
5040 color_image_info=DestroyImageInfo(color_image_info);
5041
5042 if (jng_image == (Image *) NULL)
5043 {
5044 DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5045 return(DestroyImageList(image));
5046 }
5047
5048
5049 if (logging != MagickFalse)
5050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5051 " Copying jng_image pixels to main image.");
5052
5053 image->rows=jng_height;
5054 image->columns=jng_width;
5055
5056 status=SetImageExtent(image,image->columns,image->rows,exception);
5057 if (status == MagickFalse)
5058 {
5059 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5060 &alpha_image_info);
5061 jng_image=DestroyImageList(jng_image);
5062 return(DestroyImageList(image));
5063 }
5064 if ((image->columns != jng_image->columns) ||
5065 (image->rows != jng_image->rows))
5066 {
5067 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5068 &alpha_image_info);
5069 jng_image=DestroyImageList(jng_image);
5070 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5071 }
5072 for (y=0; y < (ssize_t) image->rows; y++)
5073 {
5074 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5075 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5076 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5077 break;
5078 for (x=(ssize_t) image->columns; x != 0; x--)
5079 {
5080 SetPixelRed(image,GetPixelRed(jng_image,s),q);
5081 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5082 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5083 q+=GetPixelChannels(image);
5084 s+=GetPixelChannels(jng_image);
5085 }
5086
5087 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5088 break;
5089 }
5090
5091 jng_image=DestroyImage(jng_image);
5092
5093 if ((image_info->ping == MagickFalse) && (alpha_image != (Image *) NULL) &&
5094 (jng_color_type >= 12))
5095 {
5096 if (jng_alpha_compression_method == 0)
5097 {
5098 png_byte
5099 data[5];
5100 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5101 PNGType(data,mng_IEND);
5102 LogPNGChunk(logging,mng_IEND,0L);
5103 (void) WriteBlob(alpha_image,4,data);
5104 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5105 }
5106
5107 (void) CloseBlob(alpha_image);
5108
5109 if (logging != MagickFalse)
5110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5111 " Reading alpha from alpha_blob.");
5112
5113 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5114 "%s",alpha_image->filename);
5115
5116 jng_image=ReadImage(alpha_image_info,exception);
5117
5118 if (jng_image != (Image *) NULL)
5119 for (y=0; y < (ssize_t) image->rows; y++)
5120 {
5121 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5122 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5123 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5124 break;
5125
5126 if (image->alpha_trait != UndefinedPixelTrait)
5127 for (x=(ssize_t) image->columns; x != 0; x--)
5128 {
5129 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5130 q+=GetPixelChannels(image);
5131 s+=GetPixelChannels(jng_image);
5132 }
5133
5134 else
5135 for (x=(ssize_t) image->columns; x != 0; x--)
5136 {
5137 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5138 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5139 image->alpha_trait=BlendPixelTrait;
5140 q+=GetPixelChannels(image);
5141 s+=GetPixelChannels(jng_image);
5142 }
5143
5144 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5145 break;
5146 }
5147 (void) RelinquishUniqueFileResource(alpha_image->filename);
5148 alpha_image=DestroyImageList(alpha_image);
5149 alpha_image_info=DestroyImageInfo(alpha_image_info);
5150 if (jng_image != (Image *) NULL)
5151 jng_image=DestroyImageList(jng_image);
5152 }
5153
5154 /* Read the JNG image. */
5155
5156 if (mng_info->mng_type == 0)
5157 {
5158 mng_info->mng_width=jng_width;
5159 mng_info->mng_height=jng_height;
5160 }
5161
5162 if (image->page.width == 0 && image->page.height == 0)
5163 {
5164 image->page.width=jng_width;
5165 image->page.height=jng_height;
5166 }
5167
5168 if (image->page.x == 0 && image->page.y == 0)
5169 {
5170 image->page.x=mng_info->x_off[mng_info->object_id];
5171 image->page.y=mng_info->y_off[mng_info->object_id];
5172 }
5173
5174 else
5175 {
5176 image->page.y=mng_info->y_off[mng_info->object_id];
5177 }
5178
5179 mng_info->image_found++;
5180 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5181 2*GetBlobSize(image));
5182
5183 if (status == MagickFalse)
5184 return(DestroyImageList(image));
5185
5186 if (logging != MagickFalse)
5187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5188 " exit ReadOneJNGImage()");
5189
5190 return(image);
5191 }
5192
5193 /*
5194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5195 % %
5196 % %
5197 % %
5198 % R e a d J N G I m a g e %
5199 % %
5200 % %
5201 % %
5202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5203 %
5204 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5205 % (including the 8-byte signature) and returns it. It allocates the memory
5206 % necessary for the new Image structure and returns a pointer to the new
5207 % image.
5208 %
5209 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5210 %
5211 % The format of the ReadJNGImage method is:
5212 %
5213 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5214 % *exception)
5215 %
5216 % A description of each parameter follows:
5217 %
5218 % o image_info: the image info.
5219 %
5220 % o exception: return any errors or warnings in this structure.
5221 %
5222 */
5223
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)5224 static Image *ReadJNGImage(const ImageInfo *image_info,
5225 ExceptionInfo *exception)
5226 {
5227 Image
5228 *image;
5229
5230 MagickBooleanType
5231 logging,
5232 status;
5233
5234 MngInfo
5235 *mng_info;
5236
5237 char
5238 magic_number[MagickPathExtent];
5239
5240 size_t
5241 count;
5242
5243 /*
5244 Open image file.
5245 */
5246 assert(image_info != (const ImageInfo *) NULL);
5247 assert(image_info->signature == MagickCoreSignature);
5248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5249 image_info->filename);
5250 assert(exception != (ExceptionInfo *) NULL);
5251 assert(exception->signature == MagickCoreSignature);
5252 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5253 image=AcquireImage(image_info,exception);
5254 mng_info=(MngInfo *) NULL;
5255 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5256
5257 if (status == MagickFalse)
5258 return(DestroyImageList(image));
5259
5260 if (LocaleCompare(image_info->magick,"JNG") != 0)
5261 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5262
5263 /* Verify JNG signature. */
5264
5265 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5266
5267 if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5268 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5269
5270 /*
5271 Verify that file size large enough to contain a JNG datastream.
5272 */
5273 if (GetBlobSize(image) < 147)
5274 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5275
5276 /* Allocate a MngInfo structure. */
5277
5278 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5279
5280 if (mng_info == (MngInfo *) NULL)
5281 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5282
5283 /* Initialize members of the MngInfo structure. */
5284
5285 (void) memset(mng_info,0,sizeof(MngInfo));
5286
5287 mng_info->image=image;
5288 image=ReadOneJNGImage(mng_info,image_info,exception);
5289 mng_info=MngInfoFreeStruct(mng_info);
5290
5291 if (image == (Image *) NULL)
5292 {
5293 if (logging != MagickFalse)
5294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5295 "exit ReadJNGImage() with error");
5296
5297 return((Image *) NULL);
5298 }
5299 (void) CloseBlob(image);
5300
5301 if (image->columns == 0 || image->rows == 0)
5302 {
5303 if (logging != MagickFalse)
5304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5305 "exit ReadJNGImage() with error");
5306
5307 ThrowReaderException(CorruptImageError,"CorruptImage");
5308 }
5309
5310 if (logging != MagickFalse)
5311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5312
5313 return(image);
5314 }
5315 #endif
5316
ReadOneMNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)5317 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5318 ExceptionInfo *exception)
5319 {
5320 char
5321 page_geometry[MagickPathExtent];
5322
5323 Image
5324 *image;
5325
5326 MagickBooleanType
5327 logging;
5328
5329 volatile int
5330 first_mng_object,
5331 object_id,
5332 term_chunk_found,
5333 skip_to_iend;
5334
5335 volatile ssize_t
5336 image_count=0;
5337
5338 MagickBooleanType
5339 status;
5340
5341 MagickOffsetType
5342 offset;
5343
5344 MngBox
5345 default_fb,
5346 fb,
5347 previous_fb;
5348
5349 #if defined(MNG_INSERT_LAYERS)
5350 PixelInfo
5351 mng_background_color;
5352 #endif
5353
5354 unsigned char
5355 *p;
5356
5357 ssize_t
5358 i;
5359
5360 size_t
5361 count;
5362
5363 ssize_t
5364 loop_level;
5365
5366 volatile short
5367 skipping_loop;
5368
5369 #if defined(MNG_INSERT_LAYERS)
5370 unsigned int
5371 mandatory_back=0;
5372 #endif
5373
5374 volatile unsigned int
5375 #ifdef MNG_OBJECT_BUFFERS
5376 mng_background_object=0,
5377 #endif
5378 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5379
5380 size_t
5381 default_frame_timeout,
5382 frame_timeout,
5383 #if defined(MNG_INSERT_LAYERS)
5384 image_height,
5385 image_width,
5386 #endif
5387 length;
5388
5389 /* These delays are all measured in image ticks_per_second,
5390 * not in MNG ticks_per_second
5391 */
5392 volatile size_t
5393 default_frame_delay,
5394 final_delay,
5395 final_image_delay,
5396 frame_delay,
5397 #if defined(MNG_INSERT_LAYERS)
5398 insert_layers,
5399 #endif
5400 mng_iterations=1,
5401 simplicity=0,
5402 subframe_height=0,
5403 subframe_width=0;
5404
5405 previous_fb.top=0;
5406 previous_fb.bottom=0;
5407 previous_fb.left=0;
5408 previous_fb.right=0;
5409 default_fb.top=0;
5410 default_fb.bottom=0;
5411 default_fb.left=0;
5412 default_fb.right=0;
5413
5414 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5415 " Enter ReadOneMNGImage()");
5416
5417 image=mng_info->image;
5418
5419 if (LocaleCompare(image_info->magick,"MNG") == 0)
5420 {
5421 char
5422 magic_number[MagickPathExtent];
5423
5424 /* Verify MNG signature. */
5425 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5426 if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5427 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5428
5429 /* Initialize some nonzero members of the MngInfo structure. */
5430 for (i=0; i < MNG_MAX_OBJECTS; i++)
5431 {
5432 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5433 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5434 }
5435 mng_info->exists[0]=MagickTrue;
5436 }
5437
5438 skipping_loop=(-1);
5439 first_mng_object=MagickTrue;
5440 mng_type=0;
5441 #if defined(MNG_INSERT_LAYERS)
5442 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5443 #endif
5444 default_frame_delay=0;
5445 default_frame_timeout=0;
5446 frame_delay=0;
5447 final_delay=1;
5448 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5449 object_id=0;
5450 skip_to_iend=MagickFalse;
5451 term_chunk_found=MagickFalse;
5452 mng_info->framing_mode=1;
5453 #if defined(MNG_INSERT_LAYERS)
5454 mandatory_back=MagickFalse;
5455 #endif
5456 #if defined(MNG_INSERT_LAYERS)
5457 mng_background_color=image->background_color;
5458 #endif
5459 default_fb=mng_info->frame;
5460 previous_fb=mng_info->frame;
5461 do
5462 {
5463 char
5464 type[MagickPathExtent];
5465
5466 if (LocaleCompare(image_info->magick,"MNG") == 0)
5467 {
5468 unsigned char
5469 *chunk;
5470
5471 /*
5472 Read a new chunk.
5473 */
5474 type[0]='\0';
5475 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5476 length=(size_t) ReadBlobMSBLong(image);
5477 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5478
5479 if (logging != MagickFalse)
5480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5481 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5482 type[0],type[1],type[2],type[3],(double) length);
5483
5484 if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5485 (count < 4))
5486 ThrowReaderException(CorruptImageError,"CorruptImage");
5487
5488 p=NULL;
5489 chunk=(unsigned char *) NULL;
5490
5491 if (length != 0)
5492 {
5493 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5494
5495 if (chunk == (unsigned char *) NULL)
5496 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5497
5498 for (i=0; i < (ssize_t) length; i++)
5499 {
5500 int
5501 c;
5502
5503 c=ReadBlobByte(image);
5504 if (c == EOF)
5505 {
5506 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5507 ThrowReaderException(CorruptImageError,
5508 "InsufficientImageDataInFile");
5509 }
5510 chunk[i]=(unsigned char) c;
5511 }
5512
5513 p=chunk;
5514 }
5515
5516 (void) ReadBlobMSBLong(image); /* read crc word */
5517
5518 #if !defined(JNG_SUPPORTED)
5519 if (memcmp(type,mng_JHDR,4) == 0)
5520 {
5521 skip_to_iend=MagickTrue;
5522
5523 if (mng_info->jhdr_warning == 0)
5524 (void) ThrowMagickException(exception,GetMagickModule(),
5525 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5526
5527 mng_info->jhdr_warning++;
5528 }
5529 #endif
5530 if (memcmp(type,mng_DHDR,4) == 0)
5531 {
5532 skip_to_iend=MagickTrue;
5533
5534 if (mng_info->dhdr_warning == 0)
5535 (void) ThrowMagickException(exception,GetMagickModule(),
5536 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5537
5538 mng_info->dhdr_warning++;
5539 }
5540 if (memcmp(type,mng_MEND,4) == 0)
5541 {
5542 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5543 break;
5544 }
5545
5546 if (skip_to_iend)
5547 {
5548 if (memcmp(type,mng_IEND,4) == 0)
5549 skip_to_iend=MagickFalse;
5550
5551 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5552
5553 if (logging != MagickFalse)
5554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5555 " Skip to IEND.");
5556
5557 continue;
5558 }
5559
5560 if (memcmp(type,mng_MHDR,4) == 0)
5561 {
5562 if (length != 28)
5563 {
5564 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5565 ThrowReaderException(CorruptImageError,"CorruptImage");
5566 }
5567
5568 mng_info->mng_width=(unsigned long)mng_get_long(p);
5569 mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5570
5571 if (logging != MagickFalse)
5572 {
5573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5574 " MNG width: %.20g",(double) mng_info->mng_width);
5575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5576 " MNG height: %.20g",(double) mng_info->mng_height);
5577 }
5578
5579 p+=8;
5580 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5581
5582 if (mng_info->ticks_per_second == 0)
5583 default_frame_delay=0;
5584
5585 else
5586 default_frame_delay=1UL*image->ticks_per_second/
5587 mng_info->ticks_per_second;
5588
5589 frame_delay=default_frame_delay;
5590 simplicity=0;
5591
5592 p+=16;
5593 simplicity=(size_t) mng_get_long(p);
5594
5595 mng_type=1; /* Full MNG */
5596
5597 if ((simplicity != 0) && ((simplicity | 11) == 11))
5598 mng_type=2; /* LC */
5599
5600 if ((simplicity != 0) && ((simplicity | 9) == 9))
5601 mng_type=3; /* VLC */
5602
5603 #if defined(MNG_INSERT_LAYERS)
5604 if (mng_type != 3)
5605 insert_layers=MagickTrue;
5606 #endif
5607 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5608 {
5609 /* Allocate next image structure. */
5610 AcquireNextImage(image_info,image,exception);
5611
5612 if (GetNextImageInList(image) == (Image *) NULL)
5613 return((Image *) NULL);
5614
5615 image=SyncNextImageInList(image);
5616 mng_info->image=image;
5617 }
5618
5619 if ((mng_info->mng_width > 65535L) ||
5620 (mng_info->mng_height > 65535L))
5621 {
5622 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5623 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5624 }
5625
5626 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5627 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5628 mng_info->mng_height);
5629
5630 mng_info->frame.left=0;
5631 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5632 mng_info->frame.top=0;
5633 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5634 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5635
5636 for (i=0; i < MNG_MAX_OBJECTS; i++)
5637 mng_info->object_clip[i]=mng_info->frame;
5638
5639 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5640 continue;
5641 }
5642
5643 if (memcmp(type,mng_TERM,4) == 0)
5644 {
5645 int
5646 repeat=0;
5647
5648 if (length != 0)
5649 repeat=p[0];
5650
5651 if (repeat == 3 && length > 9)
5652 {
5653 final_delay=(png_uint_32) mng_get_long(&p[2]);
5654 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5655
5656 if (mng_iterations == PNG_UINT_31_MAX)
5657 mng_iterations=0;
5658
5659 image->iterations=mng_iterations;
5660 term_chunk_found=MagickTrue;
5661 }
5662
5663 if (logging != MagickFalse)
5664 {
5665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5666 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5667 repeat,(double) final_delay, (double) image->iterations);
5668 }
5669
5670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671 continue;
5672 }
5673 if (memcmp(type,mng_DEFI,4) == 0)
5674 {
5675 if (mng_type == 3)
5676 {
5677 (void) ThrowMagickException(exception,GetMagickModule(),
5678 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5679 image->filename);
5680 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5681 continue;
5682 }
5683
5684 if (length < 2)
5685 {
5686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5687 ThrowReaderException(CorruptImageError,"CorruptImage");
5688 }
5689
5690 object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5691
5692 if (mng_type == 2 && object_id != 0)
5693 (void) ThrowMagickException(exception,GetMagickModule(),
5694 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5695 image->filename);
5696
5697 if (object_id >= MNG_MAX_OBJECTS)
5698 {
5699 /*
5700 Instead of using a warning we should allocate a larger
5701 MngInfo structure and continue.
5702 */
5703 (void) ThrowMagickException(exception,GetMagickModule(),
5704 CoderError,"object id too large","`%s'",image->filename);
5705 object_id=MNG_MAX_OBJECTS-1;
5706 }
5707
5708 if (mng_info->exists[object_id])
5709 if (mng_info->frozen[object_id])
5710 {
5711 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5712 (void) ThrowMagickException(exception,
5713 GetMagickModule(),CoderError,
5714 "DEFI cannot redefine a frozen MNG object","`%s'",
5715 image->filename);
5716 continue;
5717 }
5718
5719 mng_info->exists[object_id]=MagickTrue;
5720
5721 if (length > 2)
5722 mng_info->invisible[object_id]=p[2];
5723
5724 /*
5725 Extract object offset info.
5726 */
5727 if (length > 11)
5728 {
5729 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5730 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5731 if (logging != MagickFalse)
5732 {
5733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5734 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5735 object_id,(double) mng_info->x_off[object_id],
5736 object_id,(double) mng_info->y_off[object_id]);
5737 }
5738 }
5739
5740 /*
5741 Extract object clipping info.
5742 */
5743 if (length > 27)
5744 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5745 &p[12]);
5746
5747 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5748 continue;
5749 }
5750 if (memcmp(type,mng_bKGD,4) == 0)
5751 {
5752 mng_info->have_global_bkgd=MagickFalse;
5753
5754 if (length > 5)
5755 {
5756 mng_info->mng_global_bkgd.red=
5757 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5758
5759 mng_info->mng_global_bkgd.green=
5760 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5761
5762 mng_info->mng_global_bkgd.blue=
5763 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5764
5765 mng_info->have_global_bkgd=MagickTrue;
5766 }
5767
5768 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5769 continue;
5770 }
5771 if (memcmp(type,mng_BACK,4) == 0)
5772 {
5773 #if defined(MNG_INSERT_LAYERS)
5774 if (length > 6)
5775 mandatory_back=p[6];
5776
5777 else
5778 mandatory_back=0;
5779
5780 if (mandatory_back && length > 5)
5781 {
5782 mng_background_color.red=
5783 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5784
5785 mng_background_color.green=
5786 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5787
5788 mng_background_color.blue=
5789 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5790
5791 mng_background_color.alpha=OpaqueAlpha;
5792 }
5793
5794 #ifdef MNG_OBJECT_BUFFERS
5795 if (length > 8)
5796 mng_background_object=(p[7] << 8) | p[8];
5797 #endif
5798 #endif
5799 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5800 continue;
5801 }
5802
5803 if (memcmp(type,mng_PLTE,4) == 0)
5804 {
5805 /* Read global PLTE. */
5806
5807 if (length && (length < 769))
5808 {
5809 /* Read global PLTE. */
5810
5811 if (mng_info->global_plte == (png_colorp) NULL)
5812 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5813 sizeof(*mng_info->global_plte));
5814
5815 if (mng_info->global_plte == (png_colorp) NULL)
5816 {
5817 mng_info->global_plte_length=0;
5818 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5819 ThrowReaderException(ResourceLimitError,
5820 "MemoryAllocationFailed");
5821 }
5822
5823 for (i=0; i < (ssize_t) (length/3); i++)
5824 {
5825 mng_info->global_plte[i].red=p[3*i];
5826 mng_info->global_plte[i].green=p[3*i+1];
5827 mng_info->global_plte[i].blue=p[3*i+2];
5828 }
5829
5830 mng_info->global_plte_length=(unsigned int) (length/3);
5831 }
5832 #ifdef MNG_LOOSE
5833 for ( ; i < 256; i++)
5834 {
5835 mng_info->global_plte[i].red=i;
5836 mng_info->global_plte[i].green=i;
5837 mng_info->global_plte[i].blue=i;
5838 }
5839
5840 if (length != 0)
5841 mng_info->global_plte_length=256;
5842 #endif
5843 else
5844 mng_info->global_plte_length=0;
5845
5846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5847 continue;
5848 }
5849
5850 if (memcmp(type,mng_tRNS,4) == 0)
5851 {
5852 /* read global tRNS */
5853
5854 if (length > 0 && length < 257)
5855 for (i=0; i < (ssize_t) length; i++)
5856 mng_info->global_trns[i]=p[i];
5857
5858 #ifdef MNG_LOOSE
5859 for ( ; i < 256; i++)
5860 mng_info->global_trns[i]=255;
5861 #endif
5862 mng_info->global_trns_length=(unsigned int) length;
5863 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864 continue;
5865 }
5866 if (memcmp(type,mng_gAMA,4) == 0)
5867 {
5868 if (length == 4)
5869 {
5870 ssize_t
5871 igamma;
5872
5873 igamma=mng_get_long(p);
5874 mng_info->global_gamma=((float) igamma)*0.00001;
5875 mng_info->have_global_gama=MagickTrue;
5876 }
5877
5878 else
5879 mng_info->have_global_gama=MagickFalse;
5880
5881 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5882 continue;
5883 }
5884
5885 if (memcmp(type,mng_cHRM,4) == 0)
5886 {
5887 /* Read global cHRM */
5888
5889 if (length == 32)
5890 {
5891 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5892 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5893 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5894 mng_info->global_chrm.red_primary.y=0.00001*
5895 mng_get_long(&p[12]);
5896 mng_info->global_chrm.green_primary.x=0.00001*
5897 mng_get_long(&p[16]);
5898 mng_info->global_chrm.green_primary.y=0.00001*
5899 mng_get_long(&p[20]);
5900 mng_info->global_chrm.blue_primary.x=0.00001*
5901 mng_get_long(&p[24]);
5902 mng_info->global_chrm.blue_primary.y=0.00001*
5903 mng_get_long(&p[28]);
5904 mng_info->have_global_chrm=MagickTrue;
5905 }
5906 else
5907 mng_info->have_global_chrm=MagickFalse;
5908
5909 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5910 continue;
5911 }
5912
5913 if (memcmp(type,mng_sRGB,4) == 0)
5914 {
5915 /*
5916 Read global sRGB.
5917 */
5918 if (length != 0)
5919 {
5920 mng_info->global_srgb_intent=
5921 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5922 mng_info->have_global_srgb=MagickTrue;
5923 }
5924 else
5925 mng_info->have_global_srgb=MagickFalse;
5926
5927 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5928 continue;
5929 }
5930
5931 if (memcmp(type,mng_iCCP,4) == 0)
5932 {
5933 /* To do: */
5934
5935 /*
5936 Read global iCCP.
5937 */
5938 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5939
5940 continue;
5941 }
5942
5943 if (memcmp(type,mng_FRAM,4) == 0)
5944 {
5945 if (mng_type == 3)
5946 (void) ThrowMagickException(exception,GetMagickModule(),
5947 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5948 image->filename);
5949
5950 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5951 image->delay=frame_delay;
5952
5953 frame_delay=default_frame_delay;
5954 frame_timeout=default_frame_timeout;
5955 fb=default_fb;
5956
5957 if (length != 0)
5958 if (p[0])
5959 mng_info->framing_mode=p[0];
5960
5961 if (logging != MagickFalse)
5962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5963 " Framing_mode=%d",mng_info->framing_mode);
5964
5965 if (length > 6)
5966 {
5967 /* Note the delay and frame clipping boundaries. */
5968
5969 p++; /* framing mode */
5970
5971 while (((p-chunk) < (long) length) && *p)
5972 p++; /* frame name */
5973
5974 p++; /* frame name terminator */
5975
5976 if ((p-chunk) < (ssize_t) (length-4))
5977 {
5978 int
5979 change_delay,
5980 change_timeout,
5981 change_clipping;
5982
5983 change_delay=(*p++);
5984 change_timeout=(*p++);
5985 change_clipping=(*p++);
5986 p++; /* change_sync */
5987
5988 if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5989 {
5990 frame_delay=1UL*image->ticks_per_second*
5991 mng_get_long(p);
5992
5993 if (mng_info->ticks_per_second != 0)
5994 frame_delay/=mng_info->ticks_per_second;
5995
5996 else
5997 frame_delay=PNG_UINT_31_MAX;
5998
5999 if (change_delay == 2)
6000 default_frame_delay=frame_delay;
6001
6002 p+=4;
6003
6004 if (logging != MagickFalse)
6005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6006 " Framing_delay=%.20g",(double) frame_delay);
6007 }
6008
6009 if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
6010 {
6011 frame_timeout=1UL*image->ticks_per_second*
6012 mng_get_long(p);
6013
6014 if (mng_info->ticks_per_second != 0)
6015 frame_timeout/=mng_info->ticks_per_second;
6016
6017 else
6018 frame_timeout=PNG_UINT_31_MAX;
6019
6020 if (change_timeout == 2)
6021 default_frame_timeout=frame_timeout;
6022
6023 p+=4;
6024
6025 if (logging != MagickFalse)
6026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6027 " Framing_timeout=%.20g",(double) frame_timeout);
6028 }
6029
6030 if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6031 {
6032 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6033 p+=16;
6034 previous_fb=fb;
6035
6036 if (logging != MagickFalse)
6037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6038 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6039 (double) fb.left,(double) fb.right,(double) fb.top,
6040 (double) fb.bottom);
6041
6042 if (change_clipping == 2)
6043 default_fb=fb;
6044 }
6045 }
6046 }
6047 mng_info->clip=fb;
6048 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6049
6050 subframe_width=(size_t) (mng_info->clip.right
6051 -mng_info->clip.left);
6052
6053 subframe_height=(size_t) (mng_info->clip.bottom
6054 -mng_info->clip.top);
6055 /*
6056 Insert a background layer behind the frame if framing_mode is 4.
6057 */
6058 #if defined(MNG_INSERT_LAYERS)
6059 if (logging != MagickFalse)
6060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6061 " subframe_width=%.20g, subframe_height=%.20g",(double)
6062 subframe_width,(double) subframe_height);
6063
6064 if (insert_layers && (mng_info->framing_mode == 4) &&
6065 (subframe_width) && (subframe_height))
6066 {
6067 /* Allocate next image structure. */
6068 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6069 {
6070 AcquireNextImage(image_info,image,exception);
6071
6072 if (GetNextImageInList(image) == (Image *) NULL)
6073 return(DestroyImageList(image));
6074
6075 image=SyncNextImageInList(image);
6076 }
6077
6078 mng_info->image=image;
6079
6080 if (term_chunk_found)
6081 {
6082 image->start_loop=MagickTrue;
6083 image->iterations=mng_iterations;
6084 term_chunk_found=MagickFalse;
6085 }
6086
6087 else
6088 image->start_loop=MagickFalse;
6089
6090 image->columns=subframe_width;
6091 image->rows=subframe_height;
6092 image->page.width=subframe_width;
6093 image->page.height=subframe_height;
6094 image->page.x=mng_info->clip.left;
6095 image->page.y=mng_info->clip.top;
6096 image->background_color=mng_background_color;
6097 image->alpha_trait=UndefinedPixelTrait;
6098 image->delay=0;
6099 if (SetImageBackgroundColor(image,exception) == MagickFalse)
6100 {
6101 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6102 return(DestroyImageList(image));
6103 }
6104 if (logging != MagickFalse)
6105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6106 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6107 (double) mng_info->clip.left,
6108 (double) mng_info->clip.right,
6109 (double) mng_info->clip.top,
6110 (double) mng_info->clip.bottom);
6111 }
6112 #endif
6113 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6114 continue;
6115 }
6116
6117 if (memcmp(type,mng_CLIP,4) == 0)
6118 {
6119 unsigned int
6120 first_object,
6121 last_object;
6122
6123 /*
6124 Read CLIP.
6125 */
6126 if (length > 3)
6127 {
6128 first_object=(p[0] << 8) | p[1];
6129 last_object=(p[2] << 8) | p[3];
6130 p+=4;
6131
6132 for (i=(int) first_object; i <= (int) last_object; i++)
6133 {
6134 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6135 continue;
6136
6137 if (mng_info->exists[i] && !mng_info->frozen[i])
6138 {
6139 MngBox
6140 box;
6141
6142 box=mng_info->object_clip[i];
6143 if ((p-chunk) < (ssize_t) (length-17))
6144 mng_info->object_clip[i]=
6145 mng_read_box(box,(char) p[0],&p[1]);
6146 }
6147 }
6148
6149 }
6150 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6151 continue;
6152 }
6153
6154 if (memcmp(type,mng_SAVE,4) == 0)
6155 {
6156 for (i=1; i < MNG_MAX_OBJECTS; i++)
6157 if (mng_info->exists[i])
6158 {
6159 mng_info->frozen[i]=MagickTrue;
6160 #ifdef MNG_OBJECT_BUFFERS
6161 if (mng_info->ob[i] != (MngBuffer *) NULL)
6162 mng_info->ob[i]->frozen=MagickTrue;
6163 #endif
6164 }
6165
6166 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6167
6168 continue;
6169 }
6170
6171 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6172 {
6173 /* Read DISC or SEEK. */
6174
6175 if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6176 {
6177 for (i=1; i < MNG_MAX_OBJECTS; i++)
6178 MngInfoDiscardObject(mng_info,i);
6179 }
6180
6181 else
6182 {
6183 ssize_t
6184 j;
6185
6186 for (j=1; j < (ssize_t) length; j+=2)
6187 {
6188 i=p[j-1] << 8 | p[j];
6189 MngInfoDiscardObject(mng_info,i);
6190 }
6191 }
6192
6193 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6194
6195 continue;
6196 }
6197
6198 if (memcmp(type,mng_MOVE,4) == 0)
6199 {
6200 size_t
6201 first_object,
6202 last_object;
6203
6204 /* read MOVE */
6205
6206 if (length > 3)
6207 {
6208 first_object=(p[0] << 8) | p[1];
6209 last_object=(p[2] << 8) | p[3];
6210 p+=4;
6211
6212 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6213 {
6214 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6215 continue;
6216
6217 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6218 (p-chunk) < (ssize_t) (length-8))
6219 {
6220 MngPair
6221 new_pair;
6222
6223 MngPair
6224 old_pair;
6225
6226 old_pair.a=mng_info->x_off[i];
6227 old_pair.b=mng_info->y_off[i];
6228 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6229 mng_info->x_off[i]=new_pair.a;
6230 mng_info->y_off[i]=new_pair.b;
6231 }
6232 }
6233 }
6234
6235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6236 continue;
6237 }
6238
6239 if (memcmp(type,mng_LOOP,4) == 0)
6240 {
6241 ssize_t loop_iters=1;
6242 if (length > 4)
6243 {
6244 loop_level=chunk[0];
6245 mng_info->loop_active[loop_level]=1; /* mark loop active */
6246
6247 /* Record starting point. */
6248 loop_iters=mng_get_long(&chunk[1]);
6249
6250 if (logging != MagickFalse)
6251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252 " LOOP level %.20g has %.20g iterations ",
6253 (double) loop_level, (double) loop_iters);
6254
6255 if (loop_iters <= 0)
6256 skipping_loop=loop_level;
6257
6258 else
6259 {
6260 if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6261 loop_iters=GetMagickResourceLimit(ListLengthResource);
6262 if (loop_iters >= 2147483647L)
6263 loop_iters=2147483647L;
6264 mng_info->loop_jump[loop_level]=TellBlob(image);
6265 mng_info->loop_count[loop_level]=loop_iters;
6266 }
6267
6268 mng_info->loop_iteration[loop_level]=0;
6269 }
6270 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6271 continue;
6272 }
6273
6274 if (memcmp(type,mng_ENDL,4) == 0)
6275 {
6276 if (length > 0)
6277 {
6278 loop_level=chunk[0];
6279
6280 if (skipping_loop > 0)
6281 {
6282 if (skipping_loop == loop_level)
6283 {
6284 /*
6285 Found end of zero-iteration loop.
6286 */
6287 skipping_loop=(-1);
6288 mng_info->loop_active[loop_level]=0;
6289 }
6290 }
6291
6292 else
6293 {
6294 if (mng_info->loop_active[loop_level] == 1)
6295 {
6296 mng_info->loop_count[loop_level]--;
6297 mng_info->loop_iteration[loop_level]++;
6298
6299 if (logging != MagickFalse)
6300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6301 " ENDL: LOOP level %.20g has %.20g remaining iters",
6302 (double) loop_level,(double)
6303 mng_info->loop_count[loop_level]);
6304
6305 if (mng_info->loop_count[loop_level] > 0)
6306 {
6307 offset=
6308 SeekBlob(image,mng_info->loop_jump[loop_level],
6309 SEEK_SET);
6310
6311 if (offset < 0)
6312 {
6313 chunk=(unsigned char *) RelinquishMagickMemory(
6314 chunk);
6315 ThrowReaderException(CorruptImageError,
6316 "ImproperImageHeader");
6317 }
6318 }
6319
6320 else
6321 {
6322 short
6323 last_level;
6324
6325 /*
6326 Finished loop.
6327 */
6328 mng_info->loop_active[loop_level]=0;
6329 last_level=(-1);
6330 for (i=0; i < loop_level; i++)
6331 if (mng_info->loop_active[i] == 1)
6332 last_level=(short) i;
6333 loop_level=last_level;
6334 }
6335 }
6336 }
6337 }
6338
6339 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6340 continue;
6341 }
6342
6343 if (memcmp(type,mng_CLON,4) == 0)
6344 {
6345 if (mng_info->clon_warning == 0)
6346 (void) ThrowMagickException(exception,GetMagickModule(),
6347 CoderError,"CLON is not implemented yet","`%s'",
6348 image->filename);
6349
6350 mng_info->clon_warning++;
6351 }
6352
6353 if (memcmp(type,mng_MAGN,4) == 0)
6354 {
6355 png_uint_16
6356 magn_first,
6357 magn_last,
6358 magn_mb,
6359 magn_ml,
6360 magn_mr,
6361 magn_mt,
6362 magn_mx,
6363 magn_my,
6364 magn_methx,
6365 magn_methy;
6366
6367 if (length > 1)
6368 magn_first=(p[0] << 8) | p[1];
6369
6370 else
6371 magn_first=0;
6372
6373 if (length > 3)
6374 magn_last=(p[2] << 8) | p[3];
6375
6376 else
6377 magn_last=magn_first;
6378 #ifndef MNG_OBJECT_BUFFERS
6379 if (magn_first || magn_last)
6380 if (mng_info->magn_warning == 0)
6381 {
6382 (void) ThrowMagickException(exception,
6383 GetMagickModule(),CoderError,
6384 "MAGN is not implemented yet for nonzero objects",
6385 "`%s'",image->filename);
6386
6387 mng_info->magn_warning++;
6388 }
6389 #endif
6390 if (length > 4)
6391 magn_methx=p[4];
6392
6393 else
6394 magn_methx=0;
6395
6396 if (length > 6)
6397 magn_mx=(p[5] << 8) | p[6];
6398
6399 else
6400 magn_mx=1;
6401
6402 if (magn_mx == 0)
6403 magn_mx=1;
6404
6405 if (length > 8)
6406 magn_my=(p[7] << 8) | p[8];
6407
6408 else
6409 magn_my=magn_mx;
6410
6411 if (magn_my == 0)
6412 magn_my=1;
6413
6414 if (length > 10)
6415 magn_ml=(p[9] << 8) | p[10];
6416
6417 else
6418 magn_ml=magn_mx;
6419
6420 if (magn_ml == 0)
6421 magn_ml=1;
6422
6423 if (length > 12)
6424 magn_mr=(p[11] << 8) | p[12];
6425
6426 else
6427 magn_mr=magn_mx;
6428
6429 if (magn_mr == 0)
6430 magn_mr=1;
6431
6432 if (length > 14)
6433 magn_mt=(p[13] << 8) | p[14];
6434
6435 else
6436 magn_mt=magn_my;
6437
6438 if (magn_mt == 0)
6439 magn_mt=1;
6440
6441 if (length > 16)
6442 magn_mb=(p[15] << 8) | p[16];
6443
6444 else
6445 magn_mb=magn_my;
6446
6447 if (magn_mb == 0)
6448 magn_mb=1;
6449
6450 if (length > 17)
6451 magn_methy=p[17];
6452
6453 else
6454 magn_methy=magn_methx;
6455
6456
6457 if (magn_methx > 5 || magn_methy > 5)
6458 if (mng_info->magn_warning == 0)
6459 {
6460 (void) ThrowMagickException(exception,
6461 GetMagickModule(),CoderError,
6462 "Unknown MAGN method in MNG datastream","`%s'",
6463 image->filename);
6464
6465 mng_info->magn_warning++;
6466 }
6467 #ifdef MNG_OBJECT_BUFFERS
6468 /* Magnify existing objects in the range magn_first to magn_last */
6469 #endif
6470 if (magn_first == 0 || magn_last == 0)
6471 {
6472 /* Save the magnification factors for object 0 */
6473 mng_info->magn_mb=magn_mb;
6474 mng_info->magn_ml=magn_ml;
6475 mng_info->magn_mr=magn_mr;
6476 mng_info->magn_mt=magn_mt;
6477 mng_info->magn_mx=magn_mx;
6478 mng_info->magn_my=magn_my;
6479 mng_info->magn_methx=magn_methx;
6480 mng_info->magn_methy=magn_methy;
6481 }
6482 }
6483
6484 if (memcmp(type,mng_PAST,4) == 0)
6485 {
6486 if (mng_info->past_warning == 0)
6487 (void) ThrowMagickException(exception,GetMagickModule(),
6488 CoderError,"PAST is not implemented yet","`%s'",
6489 image->filename);
6490
6491 mng_info->past_warning++;
6492 }
6493
6494 if (memcmp(type,mng_SHOW,4) == 0)
6495 {
6496 if (mng_info->show_warning == 0)
6497 (void) ThrowMagickException(exception,GetMagickModule(),
6498 CoderError,"SHOW is not implemented yet","`%s'",
6499 image->filename);
6500
6501 mng_info->show_warning++;
6502 }
6503
6504 if (memcmp(type,mng_sBIT,4) == 0)
6505 {
6506 if (length < 4)
6507 mng_info->have_global_sbit=MagickFalse;
6508
6509 else
6510 {
6511 mng_info->global_sbit.gray=p[0];
6512 mng_info->global_sbit.red=p[0];
6513 mng_info->global_sbit.green=p[1];
6514 mng_info->global_sbit.blue=p[2];
6515 mng_info->global_sbit.alpha=p[3];
6516 mng_info->have_global_sbit=MagickTrue;
6517 }
6518 }
6519 if (memcmp(type,mng_pHYs,4) == 0)
6520 {
6521 if (length > 8)
6522 {
6523 mng_info->global_x_pixels_per_unit=
6524 (size_t) mng_get_long(p);
6525 mng_info->global_y_pixels_per_unit=
6526 (size_t) mng_get_long(&p[4]);
6527 mng_info->global_phys_unit_type=p[8];
6528 mng_info->have_global_phys=MagickTrue;
6529 }
6530
6531 else
6532 mng_info->have_global_phys=MagickFalse;
6533 }
6534 if (memcmp(type,mng_pHYg,4) == 0)
6535 {
6536 if (mng_info->phyg_warning == 0)
6537 (void) ThrowMagickException(exception,GetMagickModule(),
6538 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6539
6540 mng_info->phyg_warning++;
6541 }
6542 if (memcmp(type,mng_BASI,4) == 0)
6543 {
6544 skip_to_iend=MagickTrue;
6545
6546 if (mng_info->basi_warning == 0)
6547 (void) ThrowMagickException(exception,GetMagickModule(),
6548 CoderError,"BASI is not implemented yet","`%s'",
6549 image->filename);
6550
6551 mng_info->basi_warning++;
6552 #ifdef MNG_BASI_SUPPORTED
6553 basi_width=(unsigned long) mng_get_long(p);
6554 basi_width=(unsigned long) mng_get_long(&p[4]);
6555 basi_color_type=p[8];
6556 basi_compression_method=p[9];
6557 basi_filter_type=p[10];
6558 basi_interlace_method=p[11];
6559 if (length > 11)
6560 basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6561
6562 else
6563 basi_red=0;
6564
6565 if (length > 13)
6566 basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6567
6568 else
6569 basi_green=0;
6570
6571 if (length > 15)
6572 basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6573
6574 else
6575 basi_blue=0;
6576
6577 if (length > 17)
6578 basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6579
6580 else
6581 {
6582 if (basi_sample_depth == 16)
6583 basi_alpha=65535L;
6584 else
6585 basi_alpha=255;
6586 }
6587
6588 if (length > 19)
6589 basi_viewable=p[20];
6590
6591 else
6592 basi_viewable=0;
6593
6594 #endif
6595 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6596 continue;
6597 }
6598
6599 if (memcmp(type,mng_IHDR,4)
6600 #if defined(JNG_SUPPORTED)
6601 && memcmp(type,mng_JHDR,4)
6602 #endif
6603 )
6604 {
6605 /* Not an IHDR or JHDR chunk */
6606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6607
6608 continue;
6609 }
6610 /* Process IHDR */
6611 if (logging != MagickFalse)
6612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6613 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6614
6615 mng_info->exists[object_id]=MagickTrue;
6616 mng_info->viewable[object_id]=MagickTrue;
6617
6618 if (mng_info->invisible[object_id])
6619 {
6620 if (logging != MagickFalse)
6621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6622 " Skipping invisible object");
6623
6624 skip_to_iend=MagickTrue;
6625 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6626 continue;
6627 }
6628 #if defined(MNG_INSERT_LAYERS)
6629 if (length < 8)
6630 {
6631 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6632 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6633 }
6634
6635 image_width=(size_t) mng_get_long(p);
6636 image_height=(size_t) mng_get_long(&p[4]);
6637 #endif
6638 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6639
6640 /*
6641 Insert a transparent background layer behind the entire animation
6642 if it is not full screen.
6643 */
6644 #if defined(MNG_INSERT_LAYERS)
6645 if (insert_layers && mng_type && first_mng_object)
6646 {
6647 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6648 (image_width < mng_info->mng_width) ||
6649 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6650 (image_height < mng_info->mng_height) ||
6651 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6652 {
6653 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6654 {
6655 /*
6656 Allocate next image structure.
6657 */
6658 AcquireNextImage(image_info,image,exception);
6659
6660 if (GetNextImageInList(image) == (Image *) NULL)
6661 return(DestroyImageList(image));
6662
6663 image=SyncNextImageInList(image);
6664 }
6665 mng_info->image=image;
6666
6667 if (term_chunk_found)
6668 {
6669 image->start_loop=MagickTrue;
6670 image->iterations=mng_iterations;
6671 term_chunk_found=MagickFalse;
6672 }
6673
6674 else
6675 image->start_loop=MagickFalse;
6676
6677 /* Make a background rectangle. */
6678
6679 image->delay=0;
6680 image->columns=mng_info->mng_width;
6681 image->rows=mng_info->mng_height;
6682 image->page.width=mng_info->mng_width;
6683 image->page.height=mng_info->mng_height;
6684 image->page.x=0;
6685 image->page.y=0;
6686 image->background_color=mng_background_color;
6687 (void) SetImageBackgroundColor(image,exception);
6688 if (logging != MagickFalse)
6689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6690 " Inserted transparent background layer, W=%.20g, H=%.20g",
6691 (double) mng_info->mng_width,(double) mng_info->mng_height);
6692 }
6693 }
6694 /*
6695 Insert a background layer behind the upcoming image if
6696 framing_mode is 3, and we haven't already inserted one.
6697 */
6698 if (insert_layers && (mng_info->framing_mode == 3) &&
6699 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6700 (simplicity & 0x08)))
6701 {
6702 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6703 {
6704 /*
6705 Allocate next image structure.
6706 */
6707 AcquireNextImage(image_info,image,exception);
6708
6709 if (GetNextImageInList(image) == (Image *) NULL)
6710 return(DestroyImageList(image));
6711
6712 image=SyncNextImageInList(image);
6713 }
6714
6715 mng_info->image=image;
6716
6717 if (term_chunk_found)
6718 {
6719 image->start_loop=MagickTrue;
6720 image->iterations=mng_iterations;
6721 term_chunk_found=MagickFalse;
6722 }
6723
6724 else
6725 image->start_loop=MagickFalse;
6726
6727 image->delay=0;
6728 image->columns=subframe_width;
6729 image->rows=subframe_height;
6730 image->page.width=subframe_width;
6731 image->page.height=subframe_height;
6732 image->page.x=mng_info->clip.left;
6733 image->page.y=mng_info->clip.top;
6734 image->background_color=mng_background_color;
6735 image->alpha_trait=UndefinedPixelTrait;
6736 (void) SetImageBackgroundColor(image,exception);
6737
6738 if (logging != MagickFalse)
6739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6740 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6741 (double) mng_info->clip.left,(double) mng_info->clip.right,
6742 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6743 }
6744 #endif /* MNG_INSERT_LAYERS */
6745 first_mng_object=MagickFalse;
6746
6747 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6748 {
6749 /*
6750 Allocate next image structure.
6751 */
6752 AcquireNextImage(image_info,image,exception);
6753
6754 if (GetNextImageInList(image) == (Image *) NULL)
6755 return(DestroyImageList(image));
6756
6757 image=SyncNextImageInList(image);
6758 }
6759 mng_info->image=image;
6760 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6761 GetBlobSize(image));
6762
6763 if (status == MagickFalse)
6764 break;
6765
6766 if (term_chunk_found)
6767 {
6768 image->start_loop=MagickTrue;
6769 term_chunk_found=MagickFalse;
6770 }
6771
6772 else
6773 image->start_loop=MagickFalse;
6774
6775 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6776 {
6777 image->delay=frame_delay;
6778 frame_delay=default_frame_delay;
6779 }
6780
6781 else
6782 image->delay=0;
6783
6784 image->page.width=mng_info->mng_width;
6785 image->page.height=mng_info->mng_height;
6786 image->page.x=mng_info->x_off[object_id];
6787 image->page.y=mng_info->y_off[object_id];
6788 image->iterations=mng_iterations;
6789
6790 /*
6791 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6792 */
6793
6794 if (logging != MagickFalse)
6795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6796 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6797 type[2],type[3]);
6798
6799 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6800
6801 if (offset < 0)
6802 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6803 }
6804
6805 mng_info->image=image;
6806 mng_info->mng_type=mng_type;
6807 mng_info->object_id=object_id;
6808
6809 if (memcmp(type,mng_IHDR,4) == 0)
6810 image=ReadOnePNGImage(mng_info,image_info,exception);
6811
6812 #if defined(JNG_SUPPORTED)
6813 else
6814 image=ReadOneJNGImage(mng_info,image_info,exception);
6815 #endif
6816
6817 if (image == (Image *) NULL)
6818 {
6819 if (logging != MagickFalse)
6820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821 "exit ReadJNGImage() with error");
6822
6823 return((Image *) NULL);
6824 }
6825
6826 if (image->columns == 0 || image->rows == 0)
6827 {
6828 (void) CloseBlob(image);
6829 return(DestroyImageList(image));
6830 }
6831
6832 mng_info->image=image;
6833
6834 if (mng_type)
6835 {
6836 MngBox
6837 crop_box;
6838
6839 if (((mng_info->magn_methx > 0) && (mng_info->magn_methx <= 5)) &&
6840 ((mng_info->magn_methy > 0) && (mng_info->magn_methy <= 5)))
6841 {
6842 png_uint_32
6843 magnified_height,
6844 magnified_width;
6845
6846 if (logging != MagickFalse)
6847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6848 " Processing MNG MAGN chunk");
6849
6850 if (image->columns == 1)
6851 mng_info->magn_methx = 1;
6852 if (image->rows == 1)
6853 mng_info->magn_methy = 1;
6854 if (mng_info->magn_methx == 1)
6855 {
6856 magnified_width=mng_info->magn_ml;
6857
6858 if (image->columns > 1)
6859 magnified_width += mng_info->magn_mr;
6860
6861 if (image->columns > 2)
6862 magnified_width += (png_uint_32)
6863 ((image->columns-2)*(mng_info->magn_mx));
6864 }
6865
6866 else
6867 {
6868 magnified_width=(png_uint_32) image->columns;
6869
6870 if (image->columns > 1)
6871 magnified_width += mng_info->magn_ml-1;
6872
6873 if (image->columns > 2)
6874 magnified_width += mng_info->magn_mr-1;
6875
6876 if (image->columns > 3)
6877 magnified_width += (png_uint_32)
6878 ((image->columns-3)*(mng_info->magn_mx-1));
6879 }
6880
6881 if (mng_info->magn_methy == 1)
6882 {
6883 magnified_height=mng_info->magn_mt;
6884
6885 if (image->rows > 1)
6886 magnified_height += mng_info->magn_mb;
6887
6888 if (image->rows > 2)
6889 magnified_height += (png_uint_32)
6890 ((image->rows-2)*(mng_info->magn_my));
6891 }
6892
6893 else
6894 {
6895 magnified_height=(png_uint_32) image->rows;
6896
6897 if (image->rows > 1)
6898 magnified_height += mng_info->magn_mt-1;
6899
6900 if (image->rows > 2)
6901 magnified_height += mng_info->magn_mb-1;
6902
6903 if (image->rows > 3)
6904 magnified_height += (png_uint_32)
6905 ((image->rows-3)*(mng_info->magn_my-1));
6906 }
6907
6908 if (magnified_height > image->rows ||
6909 magnified_width > image->columns)
6910 {
6911 Image
6912 *large_image;
6913
6914 int
6915 yy;
6916
6917 Quantum
6918 *next,
6919 *prev;
6920
6921 png_uint_16
6922 magn_methx,
6923 magn_methy;
6924
6925 ssize_t
6926 m,
6927 y;
6928
6929 Quantum
6930 *n,
6931 *q;
6932
6933 ssize_t
6934 x;
6935
6936 /* Allocate next image structure. */
6937
6938 if (logging != MagickFalse)
6939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6940 " Allocate magnified image");
6941
6942 AcquireNextImage(image_info,image,exception);
6943
6944 if (GetNextImageInList(image) == (Image *) NULL)
6945 return(DestroyImageList(image));
6946
6947 large_image=SyncNextImageInList(image);
6948
6949 large_image->columns=magnified_width;
6950 large_image->rows=magnified_height;
6951
6952 magn_methx=mng_info->magn_methx;
6953 magn_methy=mng_info->magn_methy;
6954
6955 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6956 #define QM unsigned short
6957 if (magn_methx != 1 || magn_methy != 1)
6958 {
6959 /*
6960 Scale pixels to unsigned shorts to prevent
6961 overflow of intermediate values of interpolations
6962 */
6963 for (y=0; y < (ssize_t) image->rows; y++)
6964 {
6965 q=GetAuthenticPixels(image,0,y,image->columns,1,
6966 exception);
6967 if (q == (Quantum *) NULL)
6968 break;
6969 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6970 {
6971 SetPixelRed(image,ScaleQuantumToShort(
6972 GetPixelRed(image,q)),q);
6973 SetPixelGreen(image,ScaleQuantumToShort(
6974 GetPixelGreen(image,q)),q);
6975 SetPixelBlue(image,ScaleQuantumToShort(
6976 GetPixelBlue(image,q)),q);
6977 SetPixelAlpha(image,ScaleQuantumToShort(
6978 GetPixelAlpha(image,q)),q);
6979 q+=GetPixelChannels(image);
6980 }
6981
6982 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6983 break;
6984 }
6985 }
6986 #else
6987 #define QM Quantum
6988 #endif
6989
6990 if (image->alpha_trait != UndefinedPixelTrait)
6991 (void) SetImageBackgroundColor(large_image,exception);
6992
6993 else
6994 {
6995 large_image->background_color.alpha=OpaqueAlpha;
6996 (void) SetImageBackgroundColor(large_image,exception);
6997
6998 if (magn_methx == 4)
6999 magn_methx=2;
7000
7001 if (magn_methx == 5)
7002 magn_methx=3;
7003
7004 if (magn_methy == 4)
7005 magn_methy=2;
7006
7007 if (magn_methy == 5)
7008 magn_methy=3;
7009 }
7010
7011 /* magnify the rows into the right side of the large image */
7012
7013 if (logging != MagickFalse)
7014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7015 " Magnify the rows to %.20g",
7016 (double) large_image->rows);
7017 m=(ssize_t) mng_info->magn_mt;
7018 yy=0;
7019 length=(size_t) GetPixelChannels(image)*image->columns;
7020 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7021 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7022
7023 if ((prev == (Quantum *) NULL) ||
7024 (next == (Quantum *) NULL))
7025 {
7026 if (prev != (Quantum *) NULL)
7027 prev=(Quantum *) RelinquishMagickMemory(prev);
7028 if (next != (Quantum *) NULL)
7029 next=(Quantum *) RelinquishMagickMemory(next);
7030 image=DestroyImageList(image);
7031 ThrowReaderException(ResourceLimitError,
7032 "MemoryAllocationFailed");
7033 }
7034
7035 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7036 (void) memcpy(next,n,length);
7037
7038 for (y=0; y < (ssize_t) image->rows; y++)
7039 {
7040 if (y == 0)
7041 m=(ssize_t) mng_info->magn_mt;
7042
7043 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7044 m=(ssize_t) mng_info->magn_mb;
7045
7046 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7047 m=(ssize_t) mng_info->magn_mb;
7048
7049 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7050 m=1;
7051
7052 else
7053 m=(ssize_t) mng_info->magn_my;
7054
7055 n=prev;
7056 prev=next;
7057 next=n;
7058
7059 if (y < (ssize_t) image->rows-1)
7060 {
7061 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7062 exception);
7063 (void) memcpy(next,n,length);
7064 }
7065
7066 for (i=0; i < m; i++, yy++)
7067 {
7068 Quantum
7069 *pixels;
7070
7071 assert(yy < (ssize_t) large_image->rows);
7072 pixels=prev;
7073 n=next;
7074 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7075 1,exception);
7076 if (q == (Quantum *) NULL)
7077 break;
7078 q+=(large_image->columns-image->columns)*
7079 GetPixelChannels(large_image);
7080
7081 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7082 {
7083 /* To do: get color as function of indexes[x] */
7084 /*
7085 if (image->storage_class == PseudoClass)
7086 {
7087 }
7088 */
7089
7090 if (magn_methy <= 1)
7091 {
7092 /* replicate previous */
7093 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7094 SetPixelGreen(large_image,GetPixelGreen(image,
7095 pixels),q);
7096 SetPixelBlue(large_image,GetPixelBlue(image,
7097 pixels),q);
7098 SetPixelAlpha(large_image,GetPixelAlpha(image,
7099 pixels),q);
7100 }
7101
7102 else if (magn_methy == 2 || magn_methy == 4)
7103 {
7104 if (i == 0)
7105 {
7106 SetPixelRed(large_image,GetPixelRed(image,
7107 pixels),q);
7108 SetPixelGreen(large_image,GetPixelGreen(image,
7109 pixels),q);
7110 SetPixelBlue(large_image,GetPixelBlue(image,
7111 pixels),q);
7112 SetPixelAlpha(large_image,GetPixelAlpha(image,
7113 pixels),q);
7114 }
7115
7116 else
7117 {
7118 /* Interpolate */
7119 SetPixelRed(large_image,((QM) (((ssize_t)
7120 (2*i*(GetPixelRed(image,n)
7121 -GetPixelRed(image,pixels)+m))/
7122 ((ssize_t) (m*2))
7123 +GetPixelRed(image,pixels)))),q);
7124 SetPixelGreen(large_image,((QM) (((ssize_t)
7125 (2*i*(GetPixelGreen(image,n)
7126 -GetPixelGreen(image,pixels)+m))/
7127 ((ssize_t) (m*2))
7128 +GetPixelGreen(image,pixels)))),q);
7129 SetPixelBlue(large_image,((QM) (((ssize_t)
7130 (2*i*(GetPixelBlue(image,n)
7131 -GetPixelBlue(image,pixels)+m))/
7132 ((ssize_t) (m*2))
7133 +GetPixelBlue(image,pixels)))),q);
7134
7135 if (image->alpha_trait != UndefinedPixelTrait)
7136 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7137 (2*i*(GetPixelAlpha(image,n)
7138 -GetPixelAlpha(image,pixels)+m))
7139 /((ssize_t) (m*2))+
7140 GetPixelAlpha(image,pixels)))),q);
7141 }
7142
7143 if (magn_methy == 4)
7144 {
7145 /* Replicate nearest */
7146 if (i <= ((m+1) << 1))
7147 SetPixelAlpha(large_image,GetPixelAlpha(image,
7148 pixels),q);
7149 else
7150 SetPixelAlpha(large_image,GetPixelAlpha(image,
7151 n),q);
7152 }
7153 }
7154
7155 else /* if (magn_methy == 3 || magn_methy == 5) */
7156 {
7157 /* Replicate nearest */
7158 if (i <= ((m+1) << 1))
7159 {
7160 SetPixelRed(large_image,GetPixelRed(image,
7161 pixels),q);
7162 SetPixelGreen(large_image,GetPixelGreen(image,
7163 pixels),q);
7164 SetPixelBlue(large_image,GetPixelBlue(image,
7165 pixels),q);
7166 SetPixelAlpha(large_image,GetPixelAlpha(image,
7167 pixels),q);
7168 }
7169
7170 else
7171 {
7172 SetPixelRed(large_image,GetPixelRed(image,n),q);
7173 SetPixelGreen(large_image,GetPixelGreen(image,n),
7174 q);
7175 SetPixelBlue(large_image,GetPixelBlue(image,n),
7176 q);
7177 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7178 q);
7179 }
7180
7181 if (magn_methy == 5)
7182 {
7183 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7184 (GetPixelAlpha(image,n)
7185 -GetPixelAlpha(image,pixels))
7186 +m))/((ssize_t) (m*2))
7187 +GetPixelAlpha(image,pixels)),q);
7188 }
7189 }
7190 n+=GetPixelChannels(image);
7191 q+=GetPixelChannels(large_image);
7192 pixels+=GetPixelChannels(image);
7193 } /* x */
7194
7195 if (SyncAuthenticPixels(large_image,exception) == 0)
7196 break;
7197
7198 } /* i */
7199 } /* y */
7200
7201 prev=(Quantum *) RelinquishMagickMemory(prev);
7202 next=(Quantum *) RelinquishMagickMemory(next);
7203
7204 length=image->columns;
7205
7206 if (logging != MagickFalse)
7207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7208 " Delete original image");
7209
7210 DeleteImageFromList(&image);
7211
7212 image=large_image;
7213
7214 mng_info->image=image;
7215
7216 /* magnify the columns */
7217 if (logging != MagickFalse)
7218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7219 " Magnify the columns to %.20g",
7220 (double) image->columns);
7221
7222 for (y=0; y < (ssize_t) image->rows; y++)
7223 {
7224 Quantum
7225 *pixels;
7226
7227 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7228 if (q == (Quantum *) NULL)
7229 break;
7230 pixels=q+(image->columns-length)*GetPixelChannels(image);
7231 n=pixels+GetPixelChannels(image);
7232
7233 for (x=(ssize_t) (image->columns-length);
7234 x < (ssize_t) image->columns; x++)
7235 {
7236 /* To do: Rewrite using Get/Set***PixelChannel() */
7237
7238 if (x == (ssize_t) (image->columns-length))
7239 m=(ssize_t) mng_info->magn_ml;
7240
7241 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7242 m=(ssize_t) mng_info->magn_mr;
7243
7244 else if (magn_methx <= 1 &&
7245 x == (ssize_t) image->columns-1)
7246 m=(ssize_t) mng_info->magn_mr;
7247
7248 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7249 m=1;
7250
7251 else
7252 m=(ssize_t) mng_info->magn_mx;
7253
7254 for (i=0; i < m; i++)
7255 {
7256 if (magn_methx <= 1)
7257 {
7258 /* replicate previous */
7259 SetPixelRed(image,GetPixelRed(image,pixels),q);
7260 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7261 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7262 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7263 }
7264
7265 else if (magn_methx == 2 || magn_methx == 4)
7266 {
7267 if (i == 0)
7268 {
7269 SetPixelRed(image,GetPixelRed(image,pixels),q);
7270 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7271 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7272 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7273 }
7274
7275 /* To do: Rewrite using Get/Set***PixelChannel() */
7276 else
7277 {
7278 /* Interpolate */
7279 SetPixelRed(image,(QM) ((2*i*(
7280 GetPixelRed(image,n)
7281 -GetPixelRed(image,pixels))+m)
7282 /((ssize_t) (m*2))+
7283 GetPixelRed(image,pixels)),q);
7284
7285 SetPixelGreen(image,(QM) ((2*i*(
7286 GetPixelGreen(image,n)
7287 -GetPixelGreen(image,pixels))+m)
7288 /((ssize_t) (m*2))+
7289 GetPixelGreen(image,pixels)),q);
7290
7291 SetPixelBlue(image,(QM) ((2*i*(
7292 GetPixelBlue(image,n)
7293 -GetPixelBlue(image,pixels))+m)
7294 /((ssize_t) (m*2))+
7295 GetPixelBlue(image,pixels)),q);
7296 if (image->alpha_trait != UndefinedPixelTrait)
7297 SetPixelAlpha(image,(QM) ((2*i*(
7298 GetPixelAlpha(image,n)
7299 -GetPixelAlpha(image,pixels))+m)
7300 /((ssize_t) (m*2))+
7301 GetPixelAlpha(image,pixels)),q);
7302 }
7303
7304 if (magn_methx == 4)
7305 {
7306 /* Replicate nearest */
7307 if (i <= ((m+1) << 1))
7308 {
7309 SetPixelAlpha(image,
7310 GetPixelAlpha(image,pixels)+0,q);
7311 }
7312 else
7313 {
7314 SetPixelAlpha(image,
7315 GetPixelAlpha(image,n)+0,q);
7316 }
7317 }
7318 }
7319
7320 else /* if (magn_methx == 3 || magn_methx == 5) */
7321 {
7322 /* Replicate nearest */
7323 if (i <= ((m+1) << 1))
7324 {
7325 SetPixelRed(image,GetPixelRed(image,pixels),q);
7326 SetPixelGreen(image,GetPixelGreen(image,
7327 pixels),q);
7328 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7329 SetPixelAlpha(image,GetPixelAlpha(image,
7330 pixels),q);
7331 }
7332
7333 else
7334 {
7335 SetPixelRed(image,GetPixelRed(image,n),q);
7336 SetPixelGreen(image,GetPixelGreen(image,n),q);
7337 SetPixelBlue(image,GetPixelBlue(image,n),q);
7338 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7339 }
7340
7341 if (magn_methx == 5)
7342 {
7343 /* Interpolate */
7344 SetPixelAlpha(image,
7345 (QM) ((2*i*( GetPixelAlpha(image,n)
7346 -GetPixelAlpha(image,pixels))+m)/
7347 ((ssize_t) (m*2))
7348 +GetPixelAlpha(image,pixels)),q);
7349 }
7350 }
7351 q+=GetPixelChannels(image);
7352 }
7353 n+=GetPixelChannels(image);
7354 }
7355
7356 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7357 break;
7358 }
7359 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7360 if (magn_methx != 1 || magn_methy != 1)
7361 {
7362 /*
7363 Rescale pixels to Quantum
7364 */
7365 for (y=0; y < (ssize_t) image->rows; y++)
7366 {
7367 q=GetAuthenticPixels(image,0,y,image->columns,1,
7368 exception);
7369 if (q == (Quantum *) NULL)
7370 break;
7371
7372 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7373 {
7374 SetPixelRed(image,ScaleShortToQuantum(
7375 GetPixelRed(image,q)),q);
7376 SetPixelGreen(image,ScaleShortToQuantum(
7377 GetPixelGreen(image,q)),q);
7378 SetPixelBlue(image,ScaleShortToQuantum(
7379 GetPixelBlue(image,q)),q);
7380 SetPixelAlpha(image,ScaleShortToQuantum(
7381 GetPixelAlpha(image,q)),q);
7382 q+=GetPixelChannels(image);
7383 }
7384
7385 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7386 break;
7387 }
7388 }
7389 #endif
7390 if (logging != MagickFalse)
7391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7392 " Finished MAGN processing");
7393 }
7394 }
7395
7396 /*
7397 Crop_box is with respect to the upper left corner of the MNG.
7398 */
7399 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7400 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7401 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7402 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7403 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7404 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7405 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7406 if ((crop_box.left != (mng_info->image_box.left
7407 +mng_info->x_off[object_id])) ||
7408 (crop_box.right != (mng_info->image_box.right
7409 +mng_info->x_off[object_id])) ||
7410 (crop_box.top != (mng_info->image_box.top
7411 +mng_info->y_off[object_id])) ||
7412 (crop_box.bottom != (mng_info->image_box.bottom
7413 +mng_info->y_off[object_id])))
7414 {
7415 if (logging != MagickFalse)
7416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7417 " Crop the PNG image");
7418
7419 if ((crop_box.left < crop_box.right) &&
7420 (crop_box.top < crop_box.bottom))
7421 {
7422 Image
7423 *im;
7424
7425 RectangleInfo
7426 crop_info;
7427
7428 /*
7429 Crop_info is with respect to the upper left corner of
7430 the image.
7431 */
7432 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7433 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7434 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7435 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7436 image->page.width=image->columns;
7437 image->page.height=image->rows;
7438 image->page.x=0;
7439 image->page.y=0;
7440 im=CropImage(image,&crop_info,exception);
7441
7442 if (im != (Image *) NULL)
7443 {
7444 image->columns=im->columns;
7445 image->rows=im->rows;
7446 im=DestroyImage(im);
7447 image->page.width=image->columns;
7448 image->page.height=image->rows;
7449 image->page.x=crop_box.left;
7450 image->page.y=crop_box.top;
7451 }
7452 }
7453
7454 else
7455 {
7456 /*
7457 No pixels in crop area. The MNG spec still requires
7458 a layer, though, so make a single transparent pixel in
7459 the top left corner.
7460 */
7461 image->columns=1;
7462 image->rows=1;
7463 image->colors=2;
7464 (void) SetImageBackgroundColor(image,exception);
7465 image->page.width=1;
7466 image->page.height=1;
7467 image->page.x=0;
7468 image->page.y=0;
7469 }
7470 }
7471 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7472 image=mng_info->image;
7473 #endif
7474 }
7475
7476 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7477 /* PNG does not handle depths greater than 16 so reduce it even
7478 * if lossy.
7479 */
7480 if (image->depth > 16)
7481 image->depth=16;
7482 #endif
7483
7484 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7485 if (image->depth > 8)
7486 {
7487 /* To do: fill low byte properly */
7488 image->depth=16;
7489 }
7490
7491 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7492 image->depth = 8;
7493 #endif
7494
7495 if (image_info->number_scenes != 0)
7496 {
7497 if (mng_info->scenes_found >
7498 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7499 break;
7500 }
7501
7502 if (logging != MagickFalse)
7503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504 " Finished reading image datastream.");
7505
7506 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7507
7508 (void) CloseBlob(image);
7509
7510 if (logging != MagickFalse)
7511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512 " Finished reading all image datastreams.");
7513
7514 #if defined(MNG_INSERT_LAYERS)
7515 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7516 (mng_info->mng_height))
7517 {
7518 /*
7519 Insert a background layer if nothing else was found.
7520 */
7521 if (logging != MagickFalse)
7522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7523 " No images found. Inserting a background layer.");
7524
7525 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7526 {
7527 /*
7528 Allocate next image structure.
7529 */
7530 AcquireNextImage(image_info,image,exception);
7531 if (GetNextImageInList(image) == (Image *) NULL)
7532 {
7533 if (logging != MagickFalse)
7534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7535 " Allocation failed, returning NULL.");
7536
7537 return(DestroyImageList(image));;
7538 }
7539 image=SyncNextImageInList(image);
7540 }
7541 image->columns=mng_info->mng_width;
7542 image->rows=mng_info->mng_height;
7543 image->page.width=mng_info->mng_width;
7544 image->page.height=mng_info->mng_height;
7545 image->page.x=0;
7546 image->page.y=0;
7547 image->background_color=mng_background_color;
7548 image->alpha_trait=UndefinedPixelTrait;
7549
7550 if (image_info->ping == MagickFalse)
7551 (void) SetImageBackgroundColor(image,exception);
7552
7553 mng_info->image_found++;
7554 }
7555 #endif
7556 image->iterations=mng_iterations;
7557
7558 if (mng_iterations == 1)
7559 image->start_loop=MagickTrue;
7560
7561 while (GetPreviousImageInList(image) != (Image *) NULL)
7562 {
7563 image_count++;
7564 if (image_count > 10*mng_info->image_found)
7565 {
7566 if (logging != MagickFalse)
7567 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7568
7569 (void) ThrowMagickException(exception,GetMagickModule(),
7570 CoderError,"Linked list is corrupted, beginning of list not found",
7571 "`%s'",image_info->filename);
7572
7573 return(DestroyImageList(image));
7574 }
7575
7576 image=GetPreviousImageInList(image);
7577
7578 if (GetNextImageInList(image) == (Image *) NULL)
7579 {
7580 if (logging != MagickFalse)
7581 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7582
7583 (void) ThrowMagickException(exception,GetMagickModule(),
7584 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7585 image_info->filename);
7586 }
7587 }
7588
7589 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7590 GetNextImageInList(image) ==
7591 (Image *) NULL)
7592 {
7593 if (logging != MagickFalse)
7594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " First image null");
7596
7597 (void) ThrowMagickException(exception,GetMagickModule(),
7598 CoderError,"image->next for first image is NULL but shouldn't be.",
7599 "`%s'",image_info->filename);
7600 }
7601
7602 if (mng_info->image_found == 0)
7603 {
7604 if (logging != MagickFalse)
7605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606 " No visible images found.");
7607
7608 (void) ThrowMagickException(exception,GetMagickModule(),
7609 CoderError,"No visible images in file","`%s'",image_info->filename);
7610
7611 return(DestroyImageList(image));
7612 }
7613
7614 if (mng_info->ticks_per_second)
7615 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7616 final_delay/mng_info->ticks_per_second;
7617
7618 else
7619 image->start_loop=MagickTrue;
7620
7621 /* Find final nonzero image delay */
7622 final_image_delay=0;
7623
7624 while (GetNextImageInList(image) != (Image *) NULL)
7625 {
7626 if (image->delay)
7627 final_image_delay=image->delay;
7628
7629 image=GetNextImageInList(image);
7630 }
7631
7632 if (final_delay < final_image_delay)
7633 final_delay=final_image_delay;
7634
7635 image->delay=final_delay;
7636
7637 if (logging != MagickFalse)
7638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7639 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7640 (double) final_delay);
7641
7642 if (logging != MagickFalse)
7643 {
7644 int
7645 scene;
7646
7647 scene=0;
7648 image=GetFirstImageInList(image);
7649
7650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7651 " Before coalesce:");
7652
7653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654 " scene 0 delay=%.20g",(double) image->delay);
7655
7656 while (GetNextImageInList(image) != (Image *) NULL)
7657 {
7658 image=GetNextImageInList(image);
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " scene %.20g delay=%.20g",(double) scene++,
7661 (double) image->delay);
7662 }
7663 }
7664
7665 image=GetFirstImageInList(image);
7666 #ifdef MNG_COALESCE_LAYERS
7667 if (insert_layers && image->next)
7668 {
7669 Image
7670 *next_image,
7671 *next;
7672
7673 size_t
7674 scene;
7675
7676 if (logging != MagickFalse)
7677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7678 " Coalesce Images");
7679
7680 scene=image->scene;
7681 next_image=CoalesceImages(image,exception);
7682 image=DestroyImageList(image);
7683 if (next_image == (Image *) NULL)
7684 return((Image *) NULL);
7685 image=next_image;
7686
7687 for (next=image; next != (Image *) NULL; next=next_image)
7688 {
7689 next->page.width=mng_info->mng_width;
7690 next->page.height=mng_info->mng_height;
7691 next->page.x=0;
7692 next->page.y=0;
7693 next->scene=scene++;
7694 next_image=GetNextImageInList(next);
7695
7696 if (next_image == (Image *) NULL)
7697 break;
7698
7699 if (next->delay == 0)
7700 {
7701 scene--;
7702 next_image->previous=GetPreviousImageInList(next);
7703 if (GetPreviousImageInList(next) == (Image *) NULL)
7704 image=next_image;
7705 else
7706 next->previous->next=next_image;
7707 next=DestroyImage(next);
7708 }
7709 }
7710 }
7711 #endif
7712
7713 while (GetNextImageInList(image) != (Image *) NULL)
7714 image=GetNextImageInList(image);
7715
7716 image->dispose=BackgroundDispose;
7717
7718 if (logging != MagickFalse)
7719 {
7720 int
7721 scene;
7722
7723 scene=0;
7724 image=GetFirstImageInList(image);
7725
7726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7727 " After coalesce:");
7728
7729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7730 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7731 (double) image->dispose);
7732
7733 while (GetNextImageInList(image) != (Image *) NULL)
7734 {
7735 image=GetNextImageInList(image);
7736
7737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7738 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7739 (double) image->delay,(double) image->dispose);
7740 }
7741 }
7742
7743 if (logging != MagickFalse)
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " exit ReadOneMNGImage();");
7746
7747 return(image);
7748 }
7749
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7750 static Image *ReadMNGImage(const ImageInfo *image_info,
7751 ExceptionInfo *exception)
7752 {
7753 Image
7754 *image;
7755
7756 MagickBooleanType
7757 logging,
7758 status;
7759
7760 MngInfo
7761 *mng_info;
7762
7763 /* Open image file. */
7764
7765 assert(image_info != (const ImageInfo *) NULL);
7766 assert(image_info->signature == MagickCoreSignature);
7767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7768 image_info->filename);
7769 assert(exception != (ExceptionInfo *) NULL);
7770 assert(exception->signature == MagickCoreSignature);
7771 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7772 image=AcquireImage(image_info,exception);
7773 mng_info=(MngInfo *) NULL;
7774 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7775
7776 if (status == MagickFalse)
7777 return(DestroyImageList(image));
7778
7779 /* Allocate a MngInfo structure. */
7780
7781 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7782
7783 if (mng_info == (MngInfo *) NULL)
7784 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7785
7786 /* Initialize members of the MngInfo structure. */
7787
7788 (void) memset(mng_info,0,sizeof(MngInfo));
7789 mng_info->image=image;
7790 image=ReadOneMNGImage(mng_info,image_info,exception);
7791 mng_info=MngInfoFreeStruct(mng_info);
7792
7793 if (image == (Image *) NULL)
7794 {
7795 if (logging != MagickFalse)
7796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7797 "exit ReadMNGImage() with error");
7798
7799 return((Image *) NULL);
7800 }
7801 (void) CloseBlob(image);
7802
7803 if (logging != MagickFalse)
7804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7805
7806 return(GetFirstImageInList(image));
7807 }
7808 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7809 static Image *ReadPNGImage(const ImageInfo *image_info,
7810 ExceptionInfo *exception)
7811 {
7812 printf("Your PNG library is too old: You have libpng-%s\n",
7813 PNG_LIBPNG_VER_STRING);
7814
7815 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7816 "PNG library is too old","`%s'",image_info->filename);
7817
7818 return(Image *) NULL;
7819 }
7820
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7821 static Image *ReadMNGImage(const ImageInfo *image_info,
7822 ExceptionInfo *exception)
7823 {
7824 return(ReadPNGImage(image_info,exception));
7825 }
7826 #endif /* PNG_LIBPNG_VER > 10011 */
7827 #endif
7828
7829 /*
7830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7831 % %
7832 % %
7833 % %
7834 % R e g i s t e r P N G I m a g e %
7835 % %
7836 % %
7837 % %
7838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7839 %
7840 % RegisterPNGImage() adds properties for the PNG image format to
7841 % the list of supported formats. The properties include the image format
7842 % tag, a method to read and/or write the format, whether the format
7843 % supports the saving of more than one frame to the same file or blob,
7844 % whether the format supports native in-memory I/O, and a brief
7845 % description of the format.
7846 %
7847 % The format of the RegisterPNGImage method is:
7848 %
7849 % size_t RegisterPNGImage(void)
7850 %
7851 */
RegisterPNGImage(void)7852 ModuleExport size_t RegisterPNGImage(void)
7853 {
7854 char
7855 version[MagickPathExtent];
7856
7857 MagickInfo
7858 *entry;
7859
7860 static const char
7861 PNGNote[] =
7862 {
7863 "See http://www.libpng.org/ for details about the PNG format."
7864 },
7865
7866 JNGNote[] =
7867 {
7868 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7869 "format."
7870 },
7871
7872 MNGNote[] =
7873 {
7874 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7875 "format."
7876 };
7877
7878 *version='\0';
7879
7880 #if defined(PNG_LIBPNG_VER_STRING)
7881 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7882 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7883 MagickPathExtent);
7884
7885 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7886 {
7887 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7888 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7889 MagickPathExtent);
7890 }
7891 #endif
7892
7893 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7894 entry->flags|=CoderDecoderSeekableStreamFlag;
7895
7896 #if defined(MAGICKCORE_PNG_DELEGATE)
7897 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7898 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7899 #endif
7900
7901 entry->magick=(IsImageFormatHandler *) IsMNG;
7902
7903 if (*version != '\0')
7904 entry->version=ConstantString(version);
7905
7906 entry->mime_type=ConstantString("video/x-mng");
7907 entry->note=ConstantString(MNGNote);
7908 (void) RegisterMagickInfo(entry);
7909
7910 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7911
7912 #if defined(MAGICKCORE_PNG_DELEGATE)
7913 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7914 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7915 #endif
7916
7917 entry->magick=(IsImageFormatHandler *) IsPNG;
7918 entry->flags|=CoderDecoderSeekableStreamFlag;
7919 entry->flags^=CoderAdjoinFlag;
7920 entry->mime_type=ConstantString("image/png");
7921
7922 if (*version != '\0')
7923 entry->version=ConstantString(version);
7924
7925 entry->note=ConstantString(PNGNote);
7926 (void) RegisterMagickInfo(entry);
7927
7928 entry=AcquireMagickInfo("PNG","PNG8",
7929 "8-bit indexed with optional binary transparency");
7930
7931 #if defined(MAGICKCORE_PNG_DELEGATE)
7932 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7933 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7934 #endif
7935
7936 entry->magick=(IsImageFormatHandler *) IsPNG;
7937 entry->flags|=CoderDecoderSeekableStreamFlag;
7938 entry->flags^=CoderAdjoinFlag;
7939 entry->mime_type=ConstantString("image/png");
7940 (void) RegisterMagickInfo(entry);
7941
7942 entry=AcquireMagickInfo("PNG","PNG24",
7943 "opaque or binary transparent 24-bit RGB");
7944 *version='\0';
7945
7946 #if defined(ZLIB_VERSION)
7947 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7948 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7949
7950 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7951 {
7952 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7953 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7954 }
7955 #endif
7956
7957 if (*version != '\0')
7958 entry->version=ConstantString(version);
7959
7960 #if defined(MAGICKCORE_PNG_DELEGATE)
7961 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7962 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7963 #endif
7964
7965 entry->magick=(IsImageFormatHandler *) IsPNG;
7966 entry->flags|=CoderDecoderSeekableStreamFlag;
7967 entry->flags^=CoderAdjoinFlag;
7968 entry->mime_type=ConstantString("image/png");
7969 (void) RegisterMagickInfo(entry);
7970
7971 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7972
7973 #if defined(MAGICKCORE_PNG_DELEGATE)
7974 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7975 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7976 #endif
7977
7978 entry->magick=(IsImageFormatHandler *) IsPNG;
7979 entry->flags|=CoderDecoderSeekableStreamFlag;
7980 entry->flags^=CoderAdjoinFlag;
7981 entry->mime_type=ConstantString("image/png");
7982 (void) RegisterMagickInfo(entry);
7983
7984 entry=AcquireMagickInfo("PNG","PNG48",
7985 "opaque or binary transparent 48-bit RGB");
7986
7987 #if defined(MAGICKCORE_PNG_DELEGATE)
7988 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7989 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7990 #endif
7991
7992 entry->magick=(IsImageFormatHandler *) IsPNG;
7993 entry->flags|=CoderDecoderSeekableStreamFlag;
7994 entry->flags^=CoderAdjoinFlag;
7995 entry->mime_type=ConstantString("image/png");
7996 (void) RegisterMagickInfo(entry);
7997
7998 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7999
8000 #if defined(MAGICKCORE_PNG_DELEGATE)
8001 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8002 entry->encoder=(EncodeImageHandler *) WritePNGImage;
8003 #endif
8004
8005 entry->magick=(IsImageFormatHandler *) IsPNG;
8006 entry->flags|=CoderDecoderSeekableStreamFlag;
8007 entry->flags^=CoderAdjoinFlag;
8008 entry->mime_type=ConstantString("image/png");
8009 (void) RegisterMagickInfo(entry);
8010
8011 entry=AcquireMagickInfo("PNG","PNG00",
8012 "PNG inheriting bit-depth, color-type from original, if possible");
8013
8014 #if defined(MAGICKCORE_PNG_DELEGATE)
8015 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8016 entry->encoder=(EncodeImageHandler *) WritePNGImage;
8017 #endif
8018
8019 entry->magick=(IsImageFormatHandler *) IsPNG;
8020 entry->flags|=CoderDecoderSeekableStreamFlag;
8021 entry->flags^=CoderAdjoinFlag;
8022 entry->mime_type=ConstantString("image/png");
8023 (void) RegisterMagickInfo(entry);
8024
8025 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
8026
8027 #if defined(JNG_SUPPORTED)
8028 #if defined(MAGICKCORE_PNG_DELEGATE)
8029 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
8030 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8031 #endif
8032 #endif
8033
8034 entry->magick=(IsImageFormatHandler *) IsJNG;
8035 entry->flags|=CoderDecoderSeekableStreamFlag;
8036 entry->flags^=CoderAdjoinFlag;
8037 entry->mime_type=ConstantString("image/x-jng");
8038 entry->note=ConstantString(JNGNote);
8039 (void) RegisterMagickInfo(entry);
8040
8041 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8042 ping_semaphore=AcquireSemaphoreInfo();
8043 #endif
8044
8045 return(MagickImageCoderSignature);
8046 }
8047
8048 /*
8049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8050 % %
8051 % %
8052 % %
8053 % U n r e g i s t e r P N G I m a g e %
8054 % %
8055 % %
8056 % %
8057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8058 %
8059 % UnregisterPNGImage() removes format registrations made by the
8060 % PNG module from the list of supported formats.
8061 %
8062 % The format of the UnregisterPNGImage method is:
8063 %
8064 % UnregisterPNGImage(void)
8065 %
8066 */
UnregisterPNGImage(void)8067 ModuleExport void UnregisterPNGImage(void)
8068 {
8069 (void) UnregisterMagickInfo("MNG");
8070 (void) UnregisterMagickInfo("PNG");
8071 (void) UnregisterMagickInfo("PNG8");
8072 (void) UnregisterMagickInfo("PNG24");
8073 (void) UnregisterMagickInfo("PNG32");
8074 (void) UnregisterMagickInfo("PNG48");
8075 (void) UnregisterMagickInfo("PNG64");
8076 (void) UnregisterMagickInfo("PNG00");
8077 (void) UnregisterMagickInfo("JNG");
8078
8079 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8080 if (ping_semaphore != (SemaphoreInfo *) NULL)
8081 RelinquishSemaphoreInfo(&ping_semaphore);
8082 #endif
8083 }
8084
8085 #if defined(MAGICKCORE_PNG_DELEGATE)
8086 #if PNG_LIBPNG_VER > 10011
8087 /*
8088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8089 % %
8090 % %
8091 % %
8092 % W r i t e M N G I m a g e %
8093 % %
8094 % %
8095 % %
8096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8097 %
8098 % WriteMNGImage() writes an image in the Portable Network Graphics
8099 % Group's "Multiple-image Network Graphics" encoded image format.
8100 %
8101 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8102 %
8103 % The format of the WriteMNGImage method is:
8104 %
8105 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8106 % Image *image,ExceptionInfo *exception)
8107 %
8108 % A description of each parameter follows.
8109 %
8110 % o image_info: the image info.
8111 %
8112 % o image: The image.
8113 %
8114 % o exception: return any errors or warnings in this structure.
8115 %
8116 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8117 % "To do" under ReadPNGImage):
8118 %
8119 % Preserve all unknown and not-yet-handled known chunks found in input
8120 % PNG file and copy them into output PNG files according to the PNG
8121 % copying rules.
8122 %
8123 % Write the iCCP chunk at MNG level when (icc profile length > 0)
8124 %
8125 % Improve selection of color type (use indexed-color or indexed-color
8126 % with tRNS when 256 or fewer unique RGBA values are present).
8127 %
8128 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8129 % This will be complicated if we limit ourselves to generating MNG-LC
8130 % files. For now we ignore disposal method 3 and simply overlay the next
8131 % image on it.
8132 %
8133 % Check for identical PLTE's or PLTE/tRNS combinations and use a
8134 % global MNG PLTE or PLTE/tRNS combination when appropriate.
8135 % [mostly done 15 June 1999 but still need to take care of tRNS]
8136 %
8137 % Check for identical sRGB and replace with a global sRGB (and remove
8138 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8139 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8140 % local gAMA/cHRM with local sRGB if appropriate).
8141 %
8142 % Check for identical sBIT chunks and write global ones.
8143 %
8144 % Provide option to skip writing the signature tEXt chunks.
8145 %
8146 % Use signatures to detect identical objects and reuse the first
8147 % instance of such objects instead of writing duplicate objects.
8148 %
8149 % Use a smaller-than-32k value of compression window size when
8150 % appropriate.
8151 %
8152 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8153 % ancillary text chunks and save profiles.
8154 %
8155 % Provide an option to force LC files (to ensure exact framing rate)
8156 % instead of VLC.
8157 %
8158 % Provide an option to force VLC files instead of LC, even when offsets
8159 % are present. This will involve expanding the embedded images with a
8160 % transparent region at the top and/or left.
8161 */
8162
8163 static void
Magick_png_write_raw_profile(const ImageInfo * image_info,png_struct * ping,png_info * ping_info,unsigned char * profile_type,unsigned char * profile_description,unsigned char * profile_data,png_uint_32 length,ExceptionInfo * exception)8164 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8165 png_info *ping_info, unsigned char *profile_type, unsigned char
8166 *profile_description, unsigned char *profile_data, png_uint_32 length,
8167 ExceptionInfo *exception)
8168 {
8169 png_charp
8170 dp;
8171
8172 png_textp
8173 text;
8174
8175 png_uint_32
8176 allocated_length,
8177 description_length;
8178
8179 ssize_t
8180 i;
8181
8182 unsigned char
8183 hex[16] =
8184 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
8185 *sp;
8186
8187 if (length > 1)
8188 {
8189 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8190 return;
8191 }
8192 if (image_info->verbose != MagickFalse)
8193 {
8194 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8195 (char *) profile_type, (double) length);
8196 }
8197 description_length=(png_uint_32) strlen((const char *) profile_description);
8198 allocated_length=(png_uint_32) (2*length+(length >> 5)+description_length+
8199 20);
8200 if (allocated_length < length)
8201 {
8202 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8203 "maximum profile length exceeded","`%s'",image_info->filename);
8204 return;
8205 }
8206 #if PNG_LIBPNG_VER >= 10400
8207 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8208 #else
8209 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8210 #endif
8211 #if PNG_LIBPNG_VER >= 10400
8212 text[0].text=(png_charp) png_malloc(ping,(png_alloc_size_t)
8213 allocated_length);
8214 text[0].key=(png_charp) png_malloc(ping,(png_alloc_size_t) 80);
8215 #else
8216 text[0].text=(png_charp) png_malloc(ping,(png_size_t) allocated_length);
8217 text[0].key=(png_charp) png_malloc(ping,(png_size_t) 80);
8218 #endif
8219 text[0].key[0]='\0';
8220 (void) ConcatenateMagickString(text[0].key,"Raw profile type ",
8221 MagickPathExtent);
8222 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8223 sp=profile_data;
8224 dp=text[0].text;
8225 *dp++='\n';
8226 (void) CopyMagickString(dp,(const char *) profile_description,
8227 allocated_length);
8228 dp+=description_length;
8229 *dp++='\n';
8230 (void) FormatLocaleString(dp,allocated_length-
8231 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8232 dp+=8;
8233
8234 for (i=0; i < (ssize_t) length; i++)
8235 {
8236 if (i%36 == 0)
8237 *dp++='\n';
8238 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8239 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8240 }
8241
8242 *dp++='\n';
8243 *dp='\0';
8244 text[0].text_length=(png_size_t) (dp-text[0].text);
8245 text[0].compression=image_info->compression == NoCompression ||
8246 (image_info->compression == UndefinedCompression &&
8247 text[0].text_length < 128) ? -1 : 0;
8248
8249 if (text[0].text_length <= allocated_length)
8250 png_set_text(ping,ping_info,text,1);
8251
8252 png_free(ping,text[0].text);
8253 png_free(ping,text[0].key);
8254 png_free(ping,text);
8255 }
8256
IsColorEqual(const Image * image,const Quantum * p,const PixelInfo * q)8257 static inline MagickBooleanType IsColorEqual(const Image *image,
8258 const Quantum *p, const PixelInfo *q)
8259 {
8260 MagickRealType
8261 blue,
8262 green,
8263 red;
8264
8265 red=(MagickRealType) GetPixelRed(image,p);
8266 green=(MagickRealType) GetPixelGreen(image,p);
8267 blue=(MagickRealType) GetPixelBlue(image,p);
8268 if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8269 (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8270 (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8271 return(MagickTrue);
8272 return(MagickFalse);
8273 }
8274
8275 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * timestamp,ExceptionInfo * exception)8276 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8277 const char *timestamp,ExceptionInfo *exception)
8278 {
8279 int
8280 ret;
8281
8282 int
8283 day,
8284 hour,
8285 minute,
8286 month,
8287 second,
8288 year;
8289
8290 int
8291 addhours=0,
8292 addminutes=0;
8293
8294 png_time
8295 ptime;
8296
8297 assert(timestamp != (const char *) NULL);
8298 LogMagickEvent(CoderEvent,GetMagickModule(),
8299 " Writing tIME chunk: timestamp property is %30s\n",timestamp);
8300 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8301 &minute, &second);
8302 addhours=0;
8303 addminutes=0;
8304 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8305 &minute, &second, &addhours, &addminutes);
8306 LogMagickEvent(CoderEvent,GetMagickModule(),
8307 " Date format specified for png:tIME=%s" ,timestamp);
8308 LogMagickEvent(CoderEvent,GetMagickModule(),
8309 " ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8310 ret,year,month,day,hour,minute,second,addhours,addminutes);
8311 if (ret < 6)
8312 {
8313 LogMagickEvent(CoderEvent,GetMagickModule(),
8314 " Invalid date, ret=%d",ret);
8315 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8316 "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8317 image->filename,ret);
8318 return;
8319 }
8320 if (addhours < 0)
8321 {
8322 addhours+=24;
8323 addminutes=-addminutes;
8324 day--;
8325 }
8326 hour+=addhours;
8327 minute+=addminutes;
8328 if (day == 0)
8329 {
8330 month--;
8331 day=31;
8332 if(month == 2)
8333 day=28;
8334 else
8335 {
8336 if(month == 4 || month == 6 || month == 9 || month == 11)
8337 day=30;
8338 else
8339 day=31;
8340 }
8341 }
8342 if (month == 0)
8343 {
8344 month++;
8345 year--;
8346 }
8347 if (minute > 59)
8348 {
8349 hour++;
8350 minute-=60;
8351 }
8352 if (hour > 23)
8353 {
8354 day ++;
8355 hour -=24;
8356 }
8357 if (hour < 0)
8358 {
8359 day --;
8360 hour +=24;
8361 }
8362 /* To do: fix this for leap years */
8363 if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8364 month == 9 || month == 11) && day > 30))
8365 {
8366 month++;
8367 day = 1;
8368 }
8369 if (month > 12)
8370 {
8371 year++;
8372 month=1;
8373 }
8374
8375 ptime.year = year;
8376 ptime.month = month;
8377 ptime.day = day;
8378 ptime.hour = hour;
8379 ptime.minute = minute;
8380 ptime.second = second;
8381 png_convert_from_time_t(&ptime,GetMagickTime());
8382 LogMagickEvent(CoderEvent,GetMagickModule(),
8383 " png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8384 ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8385 ptime.second, addhours, addminutes);
8386 png_set_tIME(ping,info,&ptime);
8387 }
8388 #endif
8389
8390 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)8391 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8392 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8393 {
8394 char
8395 im_vers[32],
8396 libpng_runv[32],
8397 libpng_vers[32],
8398 zlib_runv[32],
8399 zlib_vers[32];
8400
8401 Image
8402 *image;
8403
8404 ImageInfo
8405 *image_info;
8406
8407 char
8408 *name,
8409 s[2];
8410
8411 const char
8412 *property,
8413 *value;
8414
8415 const StringInfo
8416 *profile;
8417
8418 int
8419 num_passes,
8420 pass,
8421 ping_wrote_caNv;
8422
8423 png_byte
8424 ping_trans_alpha[256];
8425
8426 png_color
8427 palette[257];
8428
8429 png_color_16
8430 ping_background,
8431 ping_trans_color;
8432
8433 png_info
8434 *ping_info;
8435
8436 png_struct
8437 *ping;
8438
8439 png_uint_32
8440 ping_height,
8441 ping_width;
8442
8443 ssize_t
8444 y;
8445
8446 MagickBooleanType
8447 image_matte,
8448 logging,
8449 matte,
8450
8451 ping_have_blob,
8452 ping_have_cheap_transparency,
8453 ping_have_color,
8454 ping_have_non_bw,
8455 ping_have_PLTE,
8456 ping_have_bKGD,
8457 ping_have_eXIf,
8458 ping_have_iCCP,
8459 ping_have_pHYs,
8460 ping_have_sRGB,
8461 ping_have_tRNS,
8462
8463 ping_exclude_bKGD,
8464 ping_exclude_cHRM,
8465 ping_exclude_date,
8466 /* ping_exclude_EXIF, */
8467 ping_exclude_eXIf,
8468 ping_exclude_gAMA,
8469 ping_exclude_iCCP,
8470 /* ping_exclude_iTXt, */
8471 ping_exclude_oFFs,
8472 ping_exclude_pHYs,
8473 ping_exclude_sRGB,
8474 ping_exclude_tEXt,
8475 ping_exclude_tIME,
8476 /* ping_exclude_tRNS, */
8477 ping_exclude_caNv,
8478 ping_exclude_zCCP, /* hex-encoded iCCP */
8479 ping_exclude_zTXt,
8480
8481 ping_preserve_colormap,
8482 ping_preserve_iCCP,
8483 ping_need_colortype_warning,
8484
8485 status,
8486 tried_332,
8487 tried_333,
8488 tried_444;
8489
8490 MemoryInfo
8491 *volatile pixel_info;
8492
8493 QuantumInfo
8494 *quantum_info;
8495
8496 PNGErrorInfo
8497 error_info;
8498
8499 ssize_t
8500 i,
8501 x;
8502
8503 unsigned char
8504 *ping_pixels;
8505
8506 volatile int
8507 image_colors,
8508 ping_bit_depth,
8509 ping_color_type,
8510 ping_interlace_method,
8511 ping_compression_method,
8512 ping_filter_method,
8513 ping_num_trans;
8514
8515 volatile size_t
8516 image_depth,
8517 old_bit_depth;
8518
8519 size_t
8520 quality,
8521 rowbytes,
8522 save_image_depth;
8523
8524 int
8525 j,
8526 number_colors,
8527 number_opaque,
8528 number_semitransparent,
8529 number_transparent,
8530 ping_pHYs_unit_type;
8531
8532 png_uint_32
8533 ping_pHYs_x_resolution,
8534 ping_pHYs_y_resolution;
8535
8536 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8537 " Enter WriteOnePNGImage()");
8538
8539 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8540 if (image == (Image *) NULL)
8541 return(MagickFalse);
8542 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8543
8544 /* Define these outside of the following "if logging()" block so they will
8545 * show in debuggers.
8546 */
8547 *im_vers='\0';
8548 (void) ConcatenateMagickString(im_vers,
8549 MagickLibVersionText,MagickPathExtent);
8550 (void) ConcatenateMagickString(im_vers,
8551 MagickLibAddendum,MagickPathExtent);
8552
8553 *libpng_vers='\0';
8554 (void) ConcatenateMagickString(libpng_vers,
8555 PNG_LIBPNG_VER_STRING,32);
8556 *libpng_runv='\0';
8557 (void) ConcatenateMagickString(libpng_runv,
8558 png_get_libpng_ver(NULL),32);
8559
8560 *zlib_vers='\0';
8561 (void) ConcatenateMagickString(zlib_vers,
8562 ZLIB_VERSION,32);
8563 *zlib_runv='\0';
8564 (void) ConcatenateMagickString(zlib_runv,
8565 zlib_version,32);
8566
8567 if (logging != MagickFalse)
8568 {
8569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570 " IM version = %s", im_vers);
8571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572 " Libpng version = %s", libpng_vers);
8573 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8574 {
8575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8576 " running with %s", libpng_runv);
8577 }
8578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579 " Zlib version = %s", zlib_vers);
8580 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8581 {
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " running with %s", zlib_runv);
8584 }
8585 }
8586
8587 /* Initialize some stuff */
8588 ping_bit_depth=0,
8589 ping_color_type=0,
8590 ping_interlace_method=0,
8591 ping_compression_method=0,
8592 ping_filter_method=0,
8593 ping_num_trans = 0;
8594
8595 ping_background.red = 0;
8596 ping_background.green = 0;
8597 ping_background.blue = 0;
8598 ping_background.gray = 0;
8599 ping_background.index = 0;
8600
8601 ping_trans_color.red=0;
8602 ping_trans_color.green=0;
8603 ping_trans_color.blue=0;
8604 ping_trans_color.gray=0;
8605
8606 ping_pHYs_unit_type = 0;
8607 ping_pHYs_x_resolution = 0;
8608 ping_pHYs_y_resolution = 0;
8609
8610 ping_have_blob=MagickFalse;
8611 ping_have_cheap_transparency=MagickFalse;
8612 ping_have_color=MagickTrue;
8613 ping_have_non_bw=MagickTrue;
8614 ping_have_PLTE=MagickFalse;
8615 ping_have_bKGD=MagickFalse;
8616 ping_have_eXIf=MagickTrue;
8617 ping_have_iCCP=MagickFalse;
8618 ping_have_pHYs=MagickFalse;
8619 ping_have_sRGB=MagickFalse;
8620 ping_have_tRNS=MagickFalse;
8621
8622 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8623 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8624 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8625 ping_exclude_date=mng_info->ping_exclude_date;
8626 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8627 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8628 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8629 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8630 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8631 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8632 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8633 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8634 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8635 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8636 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8637 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8638
8639 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8640 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8641 ping_need_colortype_warning = MagickFalse;
8642
8643 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8644 * i.e., eliminate the ICC profile and set image->rendering_intent.
8645 * Note that this will not involve any changes to the actual pixels
8646 * but merely passes information to applications that read the resulting
8647 * PNG image.
8648 *
8649 * To do: recognize other variants of the sRGB profile, using the CRC to
8650 * verify all recognized variants including the 7 already known.
8651 *
8652 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8653 *
8654 * Use something other than image->rendering_intent to record the fact
8655 * that the sRGB profile was found.
8656 *
8657 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8658 * profile. Record the Blackpoint Compensation, if any.
8659 */
8660 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8661 {
8662 ResetImageProfileIterator(image);
8663 for (name=GetNextImageProfile(image); name != (char *) NULL; )
8664 {
8665 profile=GetImageProfile(image,name);
8666
8667 if (profile != (StringInfo *) NULL)
8668 {
8669 if ((LocaleCompare(name,"ICC") == 0) ||
8670 (LocaleCompare(name,"ICM") == 0))
8671
8672 {
8673 int
8674 icheck,
8675 got_crc=0;
8676
8677
8678 png_uint_32
8679 length,
8680 profile_crc=0;
8681
8682 unsigned char
8683 *data;
8684
8685 length=(png_uint_32) GetStringInfoLength(profile);
8686
8687 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8688 {
8689 if (length == sRGB_info[icheck].len)
8690 {
8691 if (got_crc == 0)
8692 {
8693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8694 " Got a %lu-byte ICC profile (potentially sRGB)",
8695 (unsigned long) length);
8696
8697 data=GetStringInfoDatum(profile);
8698 profile_crc=crc32(0,data,length);
8699
8700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8701 " with crc=%8x",(unsigned int) profile_crc);
8702 got_crc++;
8703 }
8704
8705 if (profile_crc == sRGB_info[icheck].crc)
8706 {
8707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8708 " It is sRGB with rendering intent = %s",
8709 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8710 sRGB_info[icheck].intent));
8711 if (image->rendering_intent==UndefinedIntent)
8712 {
8713 image->rendering_intent=
8714 Magick_RenderingIntent_from_PNG_RenderingIntent(
8715 sRGB_info[icheck].intent);
8716 }
8717 ping_exclude_iCCP = MagickTrue;
8718 ping_exclude_zCCP = MagickTrue;
8719 ping_have_sRGB = MagickTrue;
8720 break;
8721 }
8722 }
8723 }
8724 if (sRGB_info[icheck].len == 0)
8725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726 " Got %lu-byte ICC profile not recognized as sRGB",
8727 (unsigned long) length);
8728 }
8729 }
8730 name=GetNextImageProfile(image);
8731 }
8732 }
8733
8734 number_opaque = 0;
8735 number_semitransparent = 0;
8736 number_transparent = 0;
8737
8738 if (logging != MagickFalse)
8739 {
8740 if (image->storage_class == UndefinedClass)
8741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8742 " image->storage_class=UndefinedClass");
8743 if (image->storage_class == DirectClass)
8744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8745 " image->storage_class=DirectClass");
8746 if (image->storage_class == PseudoClass)
8747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8748 " image->storage_class=PseudoClass");
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8750 " image->taint=MagickTrue":
8751 " image->taint=MagickFalse");
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " image->gamma=%g", image->gamma);
8754 }
8755
8756 if (image->storage_class == PseudoClass &&
8757 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8758 mng_info->write_png48 || mng_info->write_png64 ||
8759 (mng_info->write_png_colortype != 1 &&
8760 mng_info->write_png_colortype != 5)))
8761 {
8762 (void) SyncImage(image,exception);
8763 image->storage_class = DirectClass;
8764 }
8765
8766 if (ping_preserve_colormap == MagickFalse)
8767 {
8768 if ((image->storage_class != PseudoClass) &&
8769 (image->colormap != (PixelInfo *) NULL))
8770 {
8771 /* Free the bogus colormap; it can cause trouble later */
8772 if (logging != MagickFalse)
8773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774 " Freeing bogus colormap");
8775 image->colormap=(PixelInfo *) RelinquishMagickMemory(
8776 image->colormap);
8777 }
8778 }
8779
8780 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8781 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8782
8783 /*
8784 Sometimes we get PseudoClass images whose RGB values don't match
8785 the colors in the colormap. This code syncs the RGB values.
8786 */
8787 image->depth=GetImageQuantumDepth(image,MagickFalse);
8788 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8789 (void) SyncImage(image,exception);
8790
8791 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8792 if (image->depth > 8)
8793 {
8794 if (logging != MagickFalse)
8795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8796 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8797
8798 image->depth=8;
8799 }
8800 #endif
8801
8802 /* Respect the -depth option */
8803 if (image->depth < 4)
8804 {
8805 Quantum
8806 *r;
8807
8808 if (image->depth > 2)
8809 {
8810 /* Scale to 4-bit */
8811 LBR04PacketRGBA(image->background_color);
8812
8813 for (y=0; y < (ssize_t) image->rows; y++)
8814 {
8815 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8816
8817 if (r == (Quantum *) NULL)
8818 break;
8819
8820 for (x=0; x < (ssize_t) image->columns; x++)
8821 {
8822 LBR04PixelRGBA(r);
8823 r+=GetPixelChannels(image);
8824 }
8825
8826 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8827 break;
8828 }
8829
8830 if (image->storage_class == PseudoClass && image->colormap != NULL)
8831 {
8832 for (i=0; i < (ssize_t) image->colors; i++)
8833 {
8834 LBR04PacketRGBA(image->colormap[i]);
8835 }
8836 }
8837 }
8838 else if (image->depth > 1)
8839 {
8840 /* Scale to 2-bit */
8841 LBR02PacketRGBA(image->background_color);
8842
8843 for (y=0; y < (ssize_t) image->rows; y++)
8844 {
8845 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8846
8847 if (r == (Quantum *) NULL)
8848 break;
8849
8850 for (x=0; x < (ssize_t) image->columns; x++)
8851 {
8852 LBR02PixelRGBA(r);
8853 r+=GetPixelChannels(image);
8854 }
8855
8856 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8857 break;
8858 }
8859
8860 if (image->storage_class == PseudoClass && image->colormap != NULL)
8861 {
8862 for (i=0; i < (ssize_t) image->colors; i++)
8863 {
8864 LBR02PacketRGBA(image->colormap[i]);
8865 }
8866 }
8867 }
8868 else
8869 {
8870 /* Scale to 1-bit */
8871 LBR01PacketRGBA(image->background_color);
8872
8873 for (y=0; y < (ssize_t) image->rows; y++)
8874 {
8875 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8876
8877 if (r == (Quantum *) NULL)
8878 break;
8879
8880 for (x=0; x < (ssize_t) image->columns; x++)
8881 {
8882 LBR01PixelRGBA(r);
8883 r+=GetPixelChannels(image);
8884 }
8885
8886 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8887 break;
8888 }
8889
8890 if (image->storage_class == PseudoClass && image->colormap != NULL)
8891 {
8892 for (i=0; i < (ssize_t) image->colors; i++)
8893 {
8894 LBR01PacketRGBA(image->colormap[i]);
8895 }
8896 }
8897 }
8898 }
8899
8900 /* To do: set to next higher multiple of 8 */
8901 if (image->depth < 8)
8902 image->depth=8;
8903
8904 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8905 /* PNG does not handle depths greater than 16 so reduce it even
8906 * if lossy
8907 */
8908 if (image->depth > 8)
8909 image->depth=16;
8910 #endif
8911
8912 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8913 if (image->depth > 8)
8914 {
8915 /* To do: fill low byte properly */
8916 image->depth=16;
8917 }
8918
8919 if (image->depth == 16 && mng_info->write_png_depth != 16)
8920 if (mng_info->write_png8 ||
8921 LosslessReduceDepthOK(image,exception) != MagickFalse)
8922 image->depth = 8;
8923 #endif
8924
8925 image_colors = (int) image->colors;
8926 number_opaque = (int) image->colors;
8927 number_transparent = 0;
8928 number_semitransparent = 0;
8929
8930 if (mng_info->write_png_colortype &&
8931 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8932 mng_info->write_png_colortype < 4 &&
8933 image->alpha_trait == UndefinedPixelTrait)))
8934 {
8935 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8936 * are not going to need the result.
8937 */
8938 if (mng_info->write_png_colortype == 1 ||
8939 mng_info->write_png_colortype == 5)
8940 ping_have_color=MagickFalse;
8941
8942 if (image->alpha_trait != UndefinedPixelTrait)
8943 {
8944 number_transparent = 2;
8945 number_semitransparent = 1;
8946 }
8947 }
8948
8949 if (mng_info->write_png_colortype < 7)
8950 {
8951 /* BUILD_PALETTE
8952 *
8953 * Normally we run this just once, but in the case of writing PNG8
8954 * we reduce the transparency to binary and run again, then if there
8955 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8956 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8957 * palette. Then (To do) we take care of a final reduction that is only
8958 * needed if there are still 256 colors present and one of them has both
8959 * transparent and opaque instances.
8960 */
8961
8962 tried_332 = MagickFalse;
8963 tried_333 = MagickFalse;
8964 tried_444 = MagickFalse;
8965
8966 if (image->depth != GetImageDepth(image,exception))
8967 (void) SetImageDepth(image,image->depth,exception);
8968 for (j=0; j<6; j++)
8969 {
8970 /*
8971 * Sometimes we get DirectClass images that have 256 colors or fewer.
8972 * This code will build a colormap.
8973 *
8974 * Also, sometimes we get PseudoClass images with an out-of-date
8975 * colormap. This code will replace the colormap with a new one.
8976 * Sometimes we get PseudoClass images that have more than 256 colors.
8977 * This code will delete the colormap and change the image to
8978 * DirectClass.
8979 *
8980 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8981 * even though it sometimes contains left-over non-opaque values.
8982 *
8983 * Also we gather some information (number of opaque, transparent,
8984 * and semitransparent pixels, and whether the image has any non-gray
8985 * pixels or only black-and-white pixels) that we might need later.
8986 *
8987 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8988 * we need to check for bogus non-opaque values, at least.
8989 */
8990
8991 int
8992 n;
8993
8994 PixelInfo
8995 opaque[260],
8996 semitransparent[260],
8997 transparent[260];
8998
8999 const Quantum
9000 *r;
9001
9002 Quantum
9003 *q;
9004
9005 if (logging != MagickFalse)
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " Enter BUILD_PALETTE:");
9008
9009 if (logging != MagickFalse)
9010 {
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012 " image->columns=%.20g",(double) image->columns);
9013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014 " image->rows=%.20g",(double) image->rows);
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " image->alpha_trait=%.20g",(double) image->alpha_trait);
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " image->depth=%.20g",(double) image->depth);
9019
9020 if (image->storage_class == PseudoClass && image->colormap != NULL)
9021 {
9022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9023 " Original colormap:");
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " i (red,green,blue,alpha)");
9026
9027 for (i=0; i < (ssize_t) MagickMin(image->colors,256); i++)
9028 {
9029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030 " %d (%d,%d,%d,%d)",
9031 (int) i,
9032 (int) image->colormap[i].red,
9033 (int) image->colormap[i].green,
9034 (int) image->colormap[i].blue,
9035 (int) image->colormap[i].alpha);
9036 }
9037
9038 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
9039 {
9040 if (i > 255)
9041 {
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " %d (%d,%d,%d,%d)",
9044 (int) i,
9045 (int) image->colormap[i].red,
9046 (int) image->colormap[i].green,
9047 (int) image->colormap[i].blue,
9048 (int) image->colormap[i].alpha);
9049 }
9050 }
9051 }
9052
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " image->colors=%d",(int) image->colors);
9055
9056 if (image->colors == 0)
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058 " (zero means unknown)");
9059
9060 if (ping_preserve_colormap == MagickFalse)
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 " Regenerate the colormap");
9063 }
9064
9065 image_colors=0;
9066 number_opaque = 0;
9067 number_semitransparent = 0;
9068 number_transparent = 0;
9069
9070 for (y=0; y < (ssize_t) image->rows; y++)
9071 {
9072 r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9073
9074 if (r == (const Quantum *) NULL)
9075 break;
9076
9077 for (x=0; x < (ssize_t) image->columns; x++)
9078 {
9079 if (image->alpha_trait == UndefinedPixelTrait ||
9080 GetPixelAlpha(image,r) == OpaqueAlpha)
9081 {
9082 if (number_opaque < 259)
9083 {
9084 if (number_opaque == 0)
9085 {
9086 GetPixelInfoPixel(image,r,opaque);
9087 opaque[0].alpha=OpaqueAlpha;
9088 number_opaque=1;
9089 }
9090
9091 for (i=0; i< (ssize_t) number_opaque; i++)
9092 {
9093 if (IsColorEqual(image,r,opaque+i))
9094 break;
9095 }
9096
9097 if (i == (ssize_t) number_opaque && number_opaque < 259)
9098 {
9099 number_opaque++;
9100 GetPixelInfoPixel(image,r,opaque+i);
9101 opaque[i].alpha=OpaqueAlpha;
9102 }
9103 }
9104 }
9105 else if (GetPixelAlpha(image,r) == TransparentAlpha)
9106 {
9107 if (number_transparent < 259)
9108 {
9109 if (number_transparent == 0)
9110 {
9111 GetPixelInfoPixel(image,r,transparent);
9112 ping_trans_color.red=(unsigned short)
9113 GetPixelRed(image,r);
9114 ping_trans_color.green=(unsigned short)
9115 GetPixelGreen(image,r);
9116 ping_trans_color.blue=(unsigned short)
9117 GetPixelBlue(image,r);
9118 ping_trans_color.gray=(unsigned short)
9119 GetPixelGray(image,r);
9120 number_transparent = 1;
9121 }
9122
9123 for (i=0; i< (ssize_t) number_transparent; i++)
9124 {
9125 if (IsColorEqual(image,r,transparent+i))
9126 break;
9127 }
9128
9129 if (i == (ssize_t) number_transparent &&
9130 number_transparent < 259)
9131 {
9132 number_transparent++;
9133 GetPixelInfoPixel(image,r,transparent+i);
9134 }
9135 }
9136 }
9137 else
9138 {
9139 if (number_semitransparent < 259)
9140 {
9141 if (number_semitransparent == 0)
9142 {
9143 GetPixelInfoPixel(image,r,semitransparent);
9144 number_semitransparent = 1;
9145 }
9146
9147 for (i=0; i< (ssize_t) number_semitransparent; i++)
9148 {
9149 if (IsColorEqual(image,r,semitransparent+i)
9150 && GetPixelAlpha(image,r) ==
9151 semitransparent[i].alpha)
9152 break;
9153 }
9154
9155 if (i == (ssize_t) number_semitransparent &&
9156 number_semitransparent < 259)
9157 {
9158 number_semitransparent++;
9159 GetPixelInfoPixel(image,r,semitransparent+i);
9160 }
9161 }
9162 }
9163 r+=GetPixelChannels(image);
9164 }
9165 }
9166
9167 if (mng_info->write_png8 == MagickFalse &&
9168 ping_exclude_bKGD == MagickFalse)
9169 {
9170 /* Add the background color to the palette, if it
9171 * isn't already there.
9172 */
9173 if (logging != MagickFalse)
9174 {
9175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176 " Check colormap for background (%d,%d,%d)",
9177 (int) image->background_color.red,
9178 (int) image->background_color.green,
9179 (int) image->background_color.blue);
9180 }
9181 if (number_opaque < 259)
9182 {
9183 for (i=0; i<number_opaque; i++)
9184 {
9185 if (opaque[i].red == image->background_color.red &&
9186 opaque[i].green == image->background_color.green &&
9187 opaque[i].blue == image->background_color.blue)
9188 break;
9189 }
9190 if (i == number_opaque)
9191 {
9192 opaque[i] = image->background_color;
9193 ping_background.index = i;
9194 number_opaque++;
9195 if (logging != MagickFalse)
9196 {
9197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198 " background_color index is %d",(int) i);
9199 }
9200
9201 }
9202 }
9203 else if (logging != MagickFalse)
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205 " No room in the colormap to add background color");
9206 }
9207
9208 image_colors=number_opaque+number_transparent+number_semitransparent;
9209
9210 if (logging != MagickFalse)
9211 {
9212 if (image_colors > 256)
9213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214 " image has more than 256 colors");
9215
9216 else
9217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9218 " image has %d colors",image_colors);
9219 }
9220
9221 if (ping_preserve_colormap != MagickFalse)
9222 break;
9223
9224 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9225 {
9226 ping_have_color=MagickFalse;
9227 ping_have_non_bw=MagickFalse;
9228
9229 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9230 {
9231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9232 "incompatible colorspace");
9233 ping_have_color=MagickTrue;
9234 ping_have_non_bw=MagickTrue;
9235 }
9236
9237 if(image_colors > 256)
9238 {
9239 for (y=0; y < (ssize_t) image->rows; y++)
9240 {
9241 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9242
9243 if (q == (Quantum *) NULL)
9244 break;
9245
9246 r=q;
9247 for (x=0; x < (ssize_t) image->columns; x++)
9248 {
9249 if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9250 GetPixelRed(image,r) != GetPixelBlue(image,r))
9251 {
9252 ping_have_color=MagickTrue;
9253 ping_have_non_bw=MagickTrue;
9254 break;
9255 }
9256 r+=GetPixelChannels(image);
9257 }
9258
9259 if (ping_have_color != MagickFalse)
9260 break;
9261
9262 /* Worst case is black-and-white; we are looking at every
9263 * pixel twice.
9264 */
9265
9266 if (ping_have_non_bw == MagickFalse)
9267 {
9268 r=q;
9269 for (x=0; x < (ssize_t) image->columns; x++)
9270 {
9271 if (GetPixelRed(image,r) != 0 &&
9272 GetPixelRed(image,r) != QuantumRange)
9273 {
9274 ping_have_non_bw=MagickTrue;
9275 break;
9276 }
9277 r+=GetPixelChannels(image);
9278 }
9279 }
9280 }
9281 }
9282 }
9283
9284 if (image_colors < 257)
9285 {
9286 PixelInfo
9287 colormap[260];
9288
9289 /*
9290 * Initialize image colormap.
9291 */
9292
9293 if (logging != MagickFalse)
9294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295 " Sort the new colormap");
9296
9297 /* Sort palette, transparent first */;
9298
9299 n = 0;
9300
9301 for (i=0; i<number_transparent; i++)
9302 colormap[n++] = transparent[i];
9303
9304 for (i=0; i<number_semitransparent; i++)
9305 colormap[n++] = semitransparent[i];
9306
9307 for (i=0; i<number_opaque; i++)
9308 colormap[n++] = opaque[i];
9309
9310 ping_background.index +=
9311 (number_transparent + number_semitransparent);
9312
9313 /* image_colors < 257; search the colormap instead of the pixels
9314 * to get ping_have_color and ping_have_non_bw
9315 */
9316 for (i=0; i<n; i++)
9317 {
9318 if (ping_have_color == MagickFalse)
9319 {
9320 if (colormap[i].red != colormap[i].green ||
9321 colormap[i].red != colormap[i].blue)
9322 {
9323 ping_have_color=MagickTrue;
9324 ping_have_non_bw=MagickTrue;
9325 break;
9326 }
9327 }
9328
9329 if (ping_have_non_bw == MagickFalse)
9330 {
9331 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9332 ping_have_non_bw=MagickTrue;
9333 }
9334 }
9335
9336 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9337 (number_transparent == 0 && number_semitransparent == 0)) &&
9338 (((mng_info->write_png_colortype-1) ==
9339 PNG_COLOR_TYPE_PALETTE) ||
9340 (mng_info->write_png_colortype == 0)))
9341 {
9342 if (logging != MagickFalse)
9343 {
9344 if (n != (ssize_t) image_colors)
9345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9346 " image_colors (%d) and n (%d) don't match",
9347 image_colors, n);
9348
9349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350 " AcquireImageColormap");
9351 }
9352
9353 image->colors = image_colors;
9354
9355 if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9356 {
9357 (void) ThrowMagickException(exception,GetMagickModule(),
9358 ResourceLimitError,"MemoryAllocationFailed","`%s'",
9359 image->filename);
9360 break;
9361 }
9362
9363 for (i=0; i< (ssize_t) image_colors; i++)
9364 image->colormap[i] = colormap[i];
9365
9366 if (logging != MagickFalse)
9367 {
9368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9369 " image->colors=%d (%d)",
9370 (int) image->colors, image_colors);
9371
9372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373 " Update the pixel indexes");
9374 }
9375
9376 /* Sync the pixel indices with the new colormap */
9377
9378 for (y=0; y < (ssize_t) image->rows; y++)
9379 {
9380 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9381
9382 if (q == (Quantum *) NULL)
9383 break;
9384
9385 for (x=0; x < (ssize_t) image->columns; x++)
9386 {
9387 for (i=0; i< (ssize_t) image_colors; i++)
9388 {
9389 if ((image->alpha_trait == UndefinedPixelTrait ||
9390 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9391 image->colormap[i].red == GetPixelRed(image,q) &&
9392 image->colormap[i].green == GetPixelGreen(image,q) &&
9393 image->colormap[i].blue == GetPixelBlue(image,q))
9394 {
9395 SetPixelIndex(image,i,q);
9396 break;
9397 }
9398 }
9399 q+=GetPixelChannels(image);
9400 }
9401
9402 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9403 break;
9404 }
9405 }
9406 }
9407
9408 if (logging != MagickFalse)
9409 {
9410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9411 " image->colors=%d", (int) image->colors);
9412
9413 if (image->colormap != NULL)
9414 {
9415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9416 " i (red,green,blue,alpha)");
9417
9418 for (i=0; i < (ssize_t) image->colors; i++)
9419 {
9420 if (i < 300 || i >= (ssize_t) image->colors - 10)
9421 {
9422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423 " %d (%d,%d,%d,%d)",
9424 (int) i,
9425 (int) image->colormap[i].red,
9426 (int) image->colormap[i].green,
9427 (int) image->colormap[i].blue,
9428 (int) image->colormap[i].alpha);
9429 }
9430 }
9431 }
9432
9433 if (number_transparent < 257)
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " number_transparent = %d",
9436 number_transparent);
9437 else
9438
9439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9440 " number_transparent > 256");
9441
9442 if (number_opaque < 257)
9443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9444 " number_opaque = %d",
9445 number_opaque);
9446
9447 else
9448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449 " number_opaque > 256");
9450
9451 if (number_semitransparent < 257)
9452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453 " number_semitransparent = %d",
9454 number_semitransparent);
9455
9456 else
9457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9458 " number_semitransparent > 256");
9459
9460 if (ping_have_non_bw == MagickFalse)
9461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462 " All pixels and the background are black or white");
9463
9464 else if (ping_have_color == MagickFalse)
9465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9466 " All pixels and the background are gray");
9467
9468 else
9469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9470 " At least one pixel or the background is non-gray");
9471
9472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9473 " Exit BUILD_PALETTE:");
9474 }
9475
9476 if (mng_info->write_png8 == MagickFalse)
9477 break;
9478
9479 /* Make any reductions necessary for the PNG8 format */
9480 if (image_colors <= 256 &&
9481 image_colors != 0 && image->colormap != NULL &&
9482 number_semitransparent == 0 &&
9483 number_transparent <= 1)
9484 break;
9485
9486 /* PNG8 can't have semitransparent colors so we threshold the
9487 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9488 * transparent color so if more than one is transparent we merge
9489 * them into image->background_color.
9490 */
9491 if (number_semitransparent != 0 || number_transparent > 1)
9492 {
9493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494 " Thresholding the alpha channel to binary");
9495
9496 for (y=0; y < (ssize_t) image->rows; y++)
9497 {
9498 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9499
9500 if (q == (Quantum *) NULL)
9501 break;
9502
9503 for (x=0; x < (ssize_t) image->columns; x++)
9504 {
9505 if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9506 {
9507 SetPixelViaPixelInfo(image,&image->background_color,q);
9508 SetPixelAlpha(image,TransparentAlpha,q);
9509 }
9510 else
9511 SetPixelAlpha(image,OpaqueAlpha,q);
9512 q+=GetPixelChannels(image);
9513 }
9514
9515 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9516 break;
9517
9518 if (image_colors != 0 && image_colors <= 256 &&
9519 image->colormap != NULL)
9520 for (i=0; i<image_colors; i++)
9521 image->colormap[i].alpha =
9522 (image->colormap[i].alpha > TransparentAlpha/2 ?
9523 TransparentAlpha : OpaqueAlpha);
9524 }
9525 continue;
9526 }
9527
9528 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9529 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9530 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9531 * colors or less.
9532 */
9533 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9534 {
9535 if (logging != MagickFalse)
9536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537 " Quantizing the background color to 4-4-4");
9538
9539 tried_444 = MagickTrue;
9540
9541 LBR04PacketRGB(image->background_color);
9542
9543 if (logging != MagickFalse)
9544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9545 " Quantizing the pixel colors to 4-4-4");
9546
9547 if (image->colormap == NULL)
9548 {
9549 for (y=0; y < (ssize_t) image->rows; y++)
9550 {
9551 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9552
9553 if (q == (Quantum *) NULL)
9554 break;
9555
9556 for (x=0; x < (ssize_t) image->columns; x++)
9557 {
9558 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9559 LBR04PixelRGB(q);
9560 q+=GetPixelChannels(image);
9561 }
9562
9563 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9564 break;
9565 }
9566 }
9567
9568 else /* Should not reach this; colormap already exists and
9569 must be <= 256 */
9570 {
9571 if (logging != MagickFalse)
9572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573 " Quantizing the colormap to 4-4-4");
9574
9575 for (i=0; i<image_colors; i++)
9576 {
9577 LBR04PacketRGB(image->colormap[i]);
9578 }
9579 }
9580 continue;
9581 }
9582
9583 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9584 {
9585 if (logging != MagickFalse)
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Quantizing the background color to 3-3-3");
9588
9589 tried_333 = MagickTrue;
9590
9591 LBR03PacketRGB(image->background_color);
9592
9593 if (logging != MagickFalse)
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595 " Quantizing the pixel colors to 3-3-3-1");
9596
9597 if (image->colormap == NULL)
9598 {
9599 for (y=0; y < (ssize_t) image->rows; y++)
9600 {
9601 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9602
9603 if (q == (Quantum *) NULL)
9604 break;
9605
9606 for (x=0; x < (ssize_t) image->columns; x++)
9607 {
9608 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9609 LBR03RGB(q);
9610 q+=GetPixelChannels(image);
9611 }
9612
9613 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9614 break;
9615 }
9616 }
9617
9618 else /* Should not reach this; colormap already exists and
9619 must be <= 256 */
9620 {
9621 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Quantizing the colormap to 3-3-3-1");
9624 for (i=0; i<image_colors; i++)
9625 {
9626 LBR03PacketRGB(image->colormap[i]);
9627 }
9628 }
9629 continue;
9630 }
9631
9632 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9633 {
9634 if (logging != MagickFalse)
9635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9636 " Quantizing the background color to 3-3-2");
9637
9638 tried_332 = MagickTrue;
9639
9640 /* Red and green were already done so we only quantize the blue
9641 * channel
9642 */
9643
9644 LBR02PacketBlue(image->background_color);
9645
9646 if (logging != MagickFalse)
9647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9648 " Quantizing the pixel colors to 3-3-2-1");
9649
9650 if (image->colormap == NULL)
9651 {
9652 for (y=0; y < (ssize_t) image->rows; y++)
9653 {
9654 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9655
9656 if (q == (Quantum *) NULL)
9657 break;
9658
9659 for (x=0; x < (ssize_t) image->columns; x++)
9660 {
9661 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9662 LBR02PixelBlue(q);
9663 q+=GetPixelChannels(image);
9664 }
9665
9666 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9667 break;
9668 }
9669 }
9670
9671 else /* Should not reach this; colormap already exists and
9672 must be <= 256 */
9673 {
9674 if (logging != MagickFalse)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Quantizing the colormap to 3-3-2-1");
9677 for (i=0; i<image_colors; i++)
9678 {
9679 LBR02PacketBlue(image->colormap[i]);
9680 }
9681 }
9682 continue;
9683 }
9684
9685 if (image_colors == 0 || image_colors > 256)
9686 {
9687 /* Take care of special case with 256 opaque colors + 1 transparent
9688 * color. We don't need to quantize to 2-3-2-1; we only need to
9689 * eliminate one color, so we'll merge the two darkest red
9690 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9691 */
9692 if (logging != MagickFalse)
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " Merging two dark red background colors to 3-3-2-1");
9695
9696 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9697 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9698 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9699 {
9700 image->background_color.red=ScaleCharToQuantum(0x24);
9701 }
9702
9703 if (logging != MagickFalse)
9704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9705 " Merging two dark red pixel colors to 3-3-2-1");
9706
9707 if (image->colormap == NULL)
9708 {
9709 for (y=0; y < (ssize_t) image->rows; y++)
9710 {
9711 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9712
9713 if (q == (Quantum *) NULL)
9714 break;
9715
9716 for (x=0; x < (ssize_t) image->columns; x++)
9717 {
9718 if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9719 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9720 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9721 GetPixelAlpha(image,q) == OpaqueAlpha)
9722 {
9723 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9724 }
9725 q+=GetPixelChannels(image);
9726 }
9727
9728 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9729 break;
9730
9731 }
9732 }
9733
9734 else
9735 {
9736 for (i=0; i<image_colors; i++)
9737 {
9738 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9739 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9740 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9741 {
9742 image->colormap[i].red=ScaleCharToQuantum(0x24);
9743 }
9744 }
9745 }
9746 }
9747 }
9748 }
9749 /* END OF BUILD_PALETTE */
9750
9751 /* If we are excluding the tRNS chunk and there is transparency,
9752 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9753 * PNG.
9754 */
9755 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9756 (number_transparent != 0 || number_semitransparent != 0))
9757 {
9758 unsigned int colortype=mng_info->write_png_colortype;
9759
9760 if (ping_have_color == MagickFalse)
9761 mng_info->write_png_colortype = 5;
9762
9763 else
9764 mng_info->write_png_colortype = 7;
9765
9766 if (colortype != 0 &&
9767 mng_info->write_png_colortype != colortype)
9768 ping_need_colortype_warning=MagickTrue;
9769
9770 }
9771
9772 /* See if cheap transparency is possible. It is only possible
9773 * when there is a single transparent color, no semitransparent
9774 * color, and no opaque color that has the same RGB components
9775 * as the transparent color. We only need this information if
9776 * we are writing a PNG with colortype 0 or 2, and we have not
9777 * excluded the tRNS chunk.
9778 */
9779 if (number_transparent == 1 &&
9780 mng_info->write_png_colortype < 4)
9781 {
9782 ping_have_cheap_transparency = MagickTrue;
9783
9784 if (number_semitransparent != 0)
9785 ping_have_cheap_transparency = MagickFalse;
9786
9787 else if (image_colors == 0 || image_colors > 256 ||
9788 image->colormap == NULL)
9789 {
9790 const Quantum
9791 *q;
9792
9793 for (y=0; y < (ssize_t) image->rows; y++)
9794 {
9795 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9796
9797 if (q == (Quantum *) NULL)
9798 break;
9799
9800 for (x=0; x < (ssize_t) image->columns; x++)
9801 {
9802 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9803 (unsigned short) GetPixelRed(image,q) ==
9804 ping_trans_color.red &&
9805 (unsigned short) GetPixelGreen(image,q) ==
9806 ping_trans_color.green &&
9807 (unsigned short) GetPixelBlue(image,q) ==
9808 ping_trans_color.blue)
9809 {
9810 ping_have_cheap_transparency = MagickFalse;
9811 break;
9812 }
9813
9814 q+=GetPixelChannels(image);
9815 }
9816
9817 if (ping_have_cheap_transparency == MagickFalse)
9818 break;
9819 }
9820 }
9821 else
9822 {
9823 /* Assuming that image->colormap[0] is the one transparent color
9824 * and that all others are opaque.
9825 */
9826 if (image_colors > 1)
9827 for (i=1; i<image_colors; i++)
9828 if (image->colormap[i].red == image->colormap[0].red &&
9829 image->colormap[i].green == image->colormap[0].green &&
9830 image->colormap[i].blue == image->colormap[0].blue)
9831 {
9832 ping_have_cheap_transparency = MagickFalse;
9833 break;
9834 }
9835 }
9836
9837 if (logging != MagickFalse)
9838 {
9839 if (ping_have_cheap_transparency == MagickFalse)
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Cheap transparency is not possible.");
9842
9843 else
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Cheap transparency is possible.");
9846 }
9847 }
9848 else
9849 ping_have_cheap_transparency = MagickFalse;
9850
9851 image_depth=image->depth;
9852
9853 quantum_info = (QuantumInfo *) NULL;
9854 number_colors=0;
9855 image_colors=(int) image->colors;
9856 image_matte=image->alpha_trait !=
9857 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9858
9859 if (mng_info->write_png_colortype < 5)
9860 mng_info->IsPalette=image->storage_class == PseudoClass &&
9861 image_colors <= 256 && image->colormap != NULL;
9862 else
9863 mng_info->IsPalette = MagickFalse;
9864
9865 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9866 (image->colors == 0 || image->colormap == NULL))
9867 {
9868 image_info=DestroyImageInfo(image_info);
9869 image=DestroyImage(image);
9870 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9871 "Cannot write PNG8 or color-type 3; colormap is NULL",
9872 "`%s'",IMimage->filename);
9873 return(MagickFalse);
9874 }
9875
9876 /*
9877 Allocate the PNG structures
9878 */
9879 #ifdef PNG_USER_MEM_SUPPORTED
9880 error_info.image=image;
9881 error_info.exception=exception;
9882 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9883 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9884 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9885
9886 #else
9887 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9888 MagickPNGErrorHandler,MagickPNGWarningHandler);
9889
9890 #endif
9891 if (ping == (png_struct *) NULL)
9892 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9893
9894 ping_info=png_create_info_struct(ping);
9895
9896 if (ping_info == (png_info *) NULL)
9897 {
9898 png_destroy_write_struct(&ping,(png_info **) NULL);
9899 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9900 }
9901
9902 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9903 pixel_info=(MemoryInfo *) NULL;
9904
9905 if (setjmp(png_jmpbuf(ping)))
9906 {
9907 /*
9908 PNG write failed.
9909 */
9910 #ifdef PNG_DEBUG
9911 if (image_info->verbose)
9912 (void) printf("PNG write has failed.\n");
9913 #endif
9914 png_destroy_write_struct(&ping,&ping_info);
9915 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9916 UnlockSemaphoreInfo(ping_semaphore);
9917 #endif
9918
9919 if (pixel_info != (MemoryInfo *) NULL)
9920 pixel_info=RelinquishVirtualMemory(pixel_info);
9921
9922 if (quantum_info != (QuantumInfo *) NULL)
9923 quantum_info=DestroyQuantumInfo(quantum_info);
9924
9925 if (ping_have_blob != MagickFalse)
9926 (void) CloseBlob(image);
9927 image_info=DestroyImageInfo(image_info);
9928 image=DestroyImage(image);
9929 return(MagickFalse);
9930 }
9931
9932 /* { For navigation to end of SETJMP-protected block. Within this
9933 * block, use png_error() instead of Throwing an Exception, to ensure
9934 * that libpng is able to clean up, and that the semaphore is unlocked.
9935 */
9936
9937 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9938 LockSemaphoreInfo(ping_semaphore);
9939 #endif
9940
9941 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9942 /* Allow benign errors */
9943 png_set_benign_errors(ping, 1);
9944 #endif
9945
9946 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9947 /* Reject images with too many rows or columns */
9948 png_set_user_limits(ping,
9949 (png_uint_32) MagickMin(0x7fffffffL,
9950 GetMagickResourceLimit(WidthResource)),
9951 (png_uint_32) MagickMin(0x7fffffffL,
9952 GetMagickResourceLimit(HeightResource)));
9953 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9954
9955 /*
9956 Prepare PNG for writing.
9957 */
9958
9959 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9960 if (mng_info->write_mng)
9961 {
9962 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9963 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9964 /* Disable new libpng-1.5.10 feature when writing a MNG because
9965 * zero-length PLTE is OK
9966 */
9967 png_set_check_for_invalid_index (ping, 0);
9968 # endif
9969 }
9970
9971 #else
9972 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9973 if (mng_info->write_mng)
9974 png_permit_empty_plte(ping,MagickTrue);
9975
9976 # endif
9977 #endif
9978
9979 x=0;
9980
9981 ping_width=(png_uint_32) image->columns;
9982 ping_height=(png_uint_32) image->rows;
9983
9984 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9985 image_depth=8;
9986
9987 if (mng_info->write_png48 || mng_info->write_png64)
9988 image_depth=16;
9989
9990 if (mng_info->write_png_depth != 0)
9991 image_depth=mng_info->write_png_depth;
9992
9993 /* Adjust requested depth to next higher valid depth if necessary */
9994 if (image_depth > 8)
9995 image_depth=16;
9996
9997 if ((image_depth > 4) && (image_depth < 8))
9998 image_depth=8;
9999
10000 if (image_depth == 3)
10001 image_depth=4;
10002
10003 if (logging != MagickFalse)
10004 {
10005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10006 " width=%.20g",(double) ping_width);
10007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10008 " height=%.20g",(double) ping_height);
10009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10010 " image_matte=%.20g",(double) image->alpha_trait);
10011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012 " image->depth=%.20g",(double) image->depth);
10013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10014 " Tentative ping_bit_depth=%.20g",(double) image_depth);
10015 }
10016
10017 save_image_depth=image_depth;
10018 ping_bit_depth=(png_byte) save_image_depth;
10019
10020
10021 #if defined(PNG_pHYs_SUPPORTED)
10022 if (ping_exclude_pHYs == MagickFalse)
10023 {
10024 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
10025 (!mng_info->write_mng || !mng_info->equal_physs))
10026 {
10027 if (logging != MagickFalse)
10028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029 " Setting up pHYs chunk");
10030
10031 if (image->units == PixelsPerInchResolution)
10032 {
10033 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10034 ping_pHYs_x_resolution=
10035 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
10036 ping_pHYs_y_resolution=
10037 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
10038 }
10039
10040 else if (image->units == PixelsPerCentimeterResolution)
10041 {
10042 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10043 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10044 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10045 }
10046
10047 else
10048 {
10049 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10050 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10051 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10052 }
10053
10054 if (logging != MagickFalse)
10055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10056 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10057 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10058 (int) ping_pHYs_unit_type);
10059 ping_have_pHYs = MagickTrue;
10060 }
10061 }
10062 #endif
10063
10064 if (ping_exclude_bKGD == MagickFalse)
10065 {
10066 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10067 {
10068 unsigned int
10069 mask;
10070
10071 mask=0xffff;
10072 if (ping_bit_depth == 8)
10073 mask=0x00ff;
10074
10075 if (ping_bit_depth == 4)
10076 mask=0x000f;
10077
10078 if (ping_bit_depth == 2)
10079 mask=0x0003;
10080
10081 if (ping_bit_depth == 1)
10082 mask=0x0001;
10083
10084 ping_background.red=(png_uint_16)
10085 (ScaleQuantumToShort(image->background_color.red) & mask);
10086
10087 ping_background.green=(png_uint_16)
10088 (ScaleQuantumToShort(image->background_color.green) & mask);
10089
10090 ping_background.blue=(png_uint_16)
10091 (ScaleQuantumToShort(image->background_color.blue) & mask);
10092
10093 ping_background.gray=(png_uint_16) ping_background.green;
10094 }
10095
10096 if (logging != MagickFalse)
10097 {
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting up bKGD chunk (1)");
10100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101 " background_color index is %d",
10102 (int) ping_background.index);
10103
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " ping_bit_depth=%d",ping_bit_depth);
10106 }
10107
10108 ping_have_bKGD = MagickTrue;
10109 }
10110
10111 /*
10112 Select the color type.
10113 */
10114 matte=image_matte;
10115 old_bit_depth=0;
10116
10117 if (mng_info->IsPalette && mng_info->write_png8)
10118 {
10119 /* To do: make this a function cause it's used twice, except
10120 for reducing the sample depth from 8. */
10121
10122 number_colors=image_colors;
10123
10124 ping_have_tRNS=MagickFalse;
10125
10126 /*
10127 Set image palette.
10128 */
10129 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10130
10131 if (logging != MagickFalse)
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " Setting up PLTE chunk with %d colors (%d)",
10134 number_colors, image_colors);
10135
10136 for (i=0; i < (ssize_t) number_colors; i++)
10137 {
10138 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10139 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10140 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10141 if (logging != MagickFalse)
10142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10143 #if MAGICKCORE_QUANTUM_DEPTH == 8
10144 " %3ld (%3d,%3d,%3d)",
10145 #else
10146 " %5ld (%5d,%5d,%5d)",
10147 #endif
10148 (long) i,palette[i].red,palette[i].green,palette[i].blue);
10149
10150 }
10151
10152 ping_have_PLTE=MagickTrue;
10153 image_depth=ping_bit_depth;
10154 ping_num_trans=0;
10155
10156 if (matte != MagickFalse)
10157 {
10158 /*
10159 Identify which colormap entry is transparent.
10160 */
10161 assert(number_colors <= 256);
10162 assert(image->colormap != NULL);
10163
10164 for (i=0; i < (ssize_t) number_transparent; i++)
10165 ping_trans_alpha[i]=0;
10166
10167
10168 ping_num_trans=(unsigned short) (number_transparent +
10169 number_semitransparent);
10170
10171 if (ping_num_trans == 0)
10172 ping_have_tRNS=MagickFalse;
10173
10174 else
10175 ping_have_tRNS=MagickTrue;
10176 }
10177
10178 if (ping_exclude_bKGD == MagickFalse)
10179 {
10180 /*
10181 * Identify which colormap entry is the background color.
10182 */
10183
10184 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10185 if (IsPNGColorEqual(ping_background,image->colormap[i]))
10186 break;
10187
10188 ping_background.index=(png_byte) i;
10189
10190 if (logging != MagickFalse)
10191 {
10192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10193 " background_color index is %d",
10194 (int) ping_background.index);
10195 }
10196 }
10197 } /* end of write_png8 */
10198
10199 else if (mng_info->write_png_colortype == 1)
10200 {
10201 image_matte=MagickFalse;
10202 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10203 }
10204
10205 else if (mng_info->write_png24 || mng_info->write_png48 ||
10206 mng_info->write_png_colortype == 3)
10207 {
10208 image_matte=MagickFalse;
10209 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10210 }
10211
10212 else if (mng_info->write_png32 || mng_info->write_png64 ||
10213 mng_info->write_png_colortype == 7)
10214 {
10215 image_matte=MagickTrue;
10216 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10217 }
10218
10219 else /* mng_info->write_pngNN not specified */
10220 {
10221 image_depth=ping_bit_depth;
10222
10223 if (mng_info->write_png_colortype != 0)
10224 {
10225 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10226
10227 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10228 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10229 image_matte=MagickTrue;
10230
10231 else
10232 image_matte=MagickFalse;
10233
10234 if (logging != MagickFalse)
10235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10236 " PNG colortype %d was specified:",(int) ping_color_type);
10237 }
10238
10239 else /* write_png_colortype not specified */
10240 {
10241 if (logging != MagickFalse)
10242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10243 " Selecting PNG colortype:");
10244
10245 if (image_info->type == TrueColorType)
10246 {
10247 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10248 image_matte=MagickFalse;
10249 }
10250 else if (image_info->type == TrueColorAlphaType)
10251 {
10252 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10253 image_matte=MagickTrue;
10254 }
10255 else if (image_info->type == PaletteType ||
10256 image_info->type == PaletteAlphaType)
10257 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10258 else
10259 {
10260 if (ping_have_color == MagickFalse)
10261 {
10262 if (image_matte == MagickFalse)
10263 {
10264 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10265 image_matte=MagickFalse;
10266 }
10267
10268 else
10269 {
10270 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10271 image_matte=MagickTrue;
10272 }
10273 }
10274 else
10275 {
10276 if (image_matte == MagickFalse)
10277 {
10278 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10279 image_matte=MagickFalse;
10280 }
10281
10282 else
10283 {
10284 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10285 image_matte=MagickTrue;
10286 }
10287 }
10288 }
10289
10290 }
10291
10292 if (logging != MagickFalse)
10293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10294 " Selected PNG colortype=%d",ping_color_type);
10295
10296 if (ping_bit_depth < 8)
10297 {
10298 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10299 ping_color_type == PNG_COLOR_TYPE_RGB ||
10300 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10301 ping_bit_depth=8;
10302 }
10303
10304 old_bit_depth=ping_bit_depth;
10305
10306 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10307 {
10308 if (image->alpha_trait == UndefinedPixelTrait &&
10309 ping_have_non_bw == MagickFalse)
10310 ping_bit_depth=1;
10311 }
10312
10313 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10314 {
10315 size_t one = 1;
10316 ping_bit_depth=1;
10317
10318 if (image->colors == 0)
10319 {
10320 /* DO SOMETHING */
10321 png_error(ping,"image has 0 colors");
10322 }
10323
10324 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10325 ping_bit_depth <<= 1;
10326 }
10327
10328 if (logging != MagickFalse)
10329 {
10330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10331 " Number of colors: %.20g",(double) image_colors);
10332
10333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10334 " Tentative PNG bit depth: %d",ping_bit_depth);
10335 }
10336
10337 if (ping_bit_depth < (int) mng_info->write_png_depth)
10338 ping_bit_depth = mng_info->write_png_depth;
10339 }
10340 (void) old_bit_depth;
10341 image_depth=ping_bit_depth;
10342
10343 if (logging != MagickFalse)
10344 {
10345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346 " Tentative PNG color type: %s (%.20g)",
10347 PngColorTypeToString(ping_color_type),
10348 (double) ping_color_type);
10349
10350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10351 " image_info->type: %.20g",(double) image_info->type);
10352
10353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354 " image_depth: %.20g",(double) image_depth);
10355
10356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10357
10358 " image->depth: %.20g",(double) image->depth);
10359
10360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10361 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10362 }
10363
10364 if (matte != MagickFalse)
10365 {
10366 if (mng_info->IsPalette)
10367 {
10368 if (mng_info->write_png_colortype == 0)
10369 {
10370 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10371
10372 if (ping_have_color != MagickFalse)
10373 ping_color_type=PNG_COLOR_TYPE_RGBA;
10374 }
10375
10376 /*
10377 * Determine if there is any transparent color.
10378 */
10379 if (number_transparent + number_semitransparent == 0)
10380 {
10381 /*
10382 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10383 */
10384
10385 image_matte=MagickFalse;
10386
10387 if (mng_info->write_png_colortype == 0)
10388 ping_color_type&=0x03;
10389 }
10390
10391 else
10392 {
10393 unsigned int
10394 mask;
10395
10396 mask=0xffff;
10397
10398 if (ping_bit_depth == 8)
10399 mask=0x00ff;
10400
10401 if (ping_bit_depth == 4)
10402 mask=0x000f;
10403
10404 if (ping_bit_depth == 2)
10405 mask=0x0003;
10406
10407 if (ping_bit_depth == 1)
10408 mask=0x0001;
10409
10410 ping_trans_color.red=(png_uint_16)
10411 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10412
10413 ping_trans_color.green=(png_uint_16)
10414 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10415
10416 ping_trans_color.blue=(png_uint_16)
10417 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10418
10419 ping_trans_color.gray=(png_uint_16)
10420 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10421 image->colormap)) & mask);
10422
10423 ping_trans_color.index=(png_byte) 0;
10424
10425 ping_have_tRNS=MagickTrue;
10426 }
10427
10428 if (ping_have_tRNS != MagickFalse)
10429 {
10430 /*
10431 * Determine if there is one and only one transparent color
10432 * and if so if it is fully transparent.
10433 */
10434 if (ping_have_cheap_transparency == MagickFalse)
10435 ping_have_tRNS=MagickFalse;
10436 }
10437
10438 if (ping_have_tRNS != MagickFalse)
10439 {
10440 if (mng_info->write_png_colortype == 0)
10441 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10442
10443 if (image_depth == 8)
10444 {
10445 ping_trans_color.red&=0xff;
10446 ping_trans_color.green&=0xff;
10447 ping_trans_color.blue&=0xff;
10448 ping_trans_color.gray&=0xff;
10449 }
10450 }
10451 }
10452 else
10453 {
10454 if (image_depth == 8)
10455 {
10456 ping_trans_color.red&=0xff;
10457 ping_trans_color.green&=0xff;
10458 ping_trans_color.blue&=0xff;
10459 ping_trans_color.gray&=0xff;
10460 }
10461 }
10462 }
10463
10464 matte=image_matte;
10465
10466 if (ping_have_tRNS != MagickFalse)
10467 image_matte=MagickFalse;
10468
10469 if ((mng_info->IsPalette) &&
10470 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10471 ping_have_color == MagickFalse &&
10472 (image_matte == MagickFalse || image_depth >= 8))
10473 {
10474 size_t one=1;
10475
10476 if (image_matte != MagickFalse)
10477 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10478
10479 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10480 {
10481 ping_color_type=PNG_COLOR_TYPE_GRAY;
10482
10483 if (save_image_depth == 16 && image_depth == 8)
10484 {
10485 if (logging != MagickFalse)
10486 {
10487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10488 " Scaling ping_trans_color (0)");
10489 }
10490 ping_trans_color.gray*=0x0101;
10491 }
10492 }
10493
10494 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10495 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10496
10497 if ((image_colors == 0) ||
10498 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10499 image_colors=(int) (one << image_depth);
10500
10501 if (image_depth > 8)
10502 ping_bit_depth=16;
10503
10504 else
10505 {
10506 ping_bit_depth=8;
10507 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10508 {
10509 if(!mng_info->write_png_depth)
10510 {
10511 ping_bit_depth=1;
10512
10513 while ((int) (one << ping_bit_depth)
10514 < (ssize_t) image_colors)
10515 ping_bit_depth <<= 1;
10516 }
10517 }
10518
10519 else if (ping_color_type ==
10520 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10521 mng_info->IsPalette)
10522 {
10523 /* Check if grayscale is reducible */
10524
10525 int
10526 depth_4_ok=MagickTrue,
10527 depth_2_ok=MagickTrue,
10528 depth_1_ok=MagickTrue;
10529
10530 for (i=0; i < (ssize_t) image_colors; i++)
10531 {
10532 unsigned char
10533 intensity;
10534
10535 intensity=ScaleQuantumToChar(image->colormap[i].red);
10536
10537 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10538 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10539 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10540 depth_2_ok=depth_1_ok=MagickFalse;
10541 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10542 depth_1_ok=MagickFalse;
10543 }
10544
10545 if (depth_1_ok && mng_info->write_png_depth <= 1)
10546 ping_bit_depth=1;
10547
10548 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10549 ping_bit_depth=2;
10550
10551 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10552 ping_bit_depth=4;
10553 }
10554 }
10555
10556 image_depth=ping_bit_depth;
10557 }
10558
10559 else
10560
10561 if (mng_info->IsPalette)
10562 {
10563 number_colors=image_colors;
10564
10565 if (image_depth <= 8)
10566 {
10567 /*
10568 Set image palette.
10569 */
10570 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10571
10572 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10573 {
10574 for (i=0; i < (ssize_t) number_colors; i++)
10575 {
10576 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10577 palette[i].green=
10578 ScaleQuantumToChar(image->colormap[i].green);
10579 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10580 }
10581
10582 if (logging != MagickFalse)
10583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10584 " Setting up PLTE chunk with %d colors",
10585 number_colors);
10586
10587 ping_have_PLTE=MagickTrue;
10588 }
10589
10590 /* color_type is PNG_COLOR_TYPE_PALETTE */
10591 if (mng_info->write_png_depth == 0)
10592 {
10593 size_t
10594 one;
10595
10596 ping_bit_depth=1;
10597 one=1;
10598
10599 while ((one << ping_bit_depth) < (size_t) number_colors)
10600 ping_bit_depth <<= 1;
10601 }
10602
10603 ping_num_trans=0;
10604
10605 if (matte != MagickFalse)
10606 {
10607 /*
10608 * Set up trans_colors array.
10609 */
10610 assert(number_colors <= 256);
10611
10612 ping_num_trans=(unsigned short) (number_transparent +
10613 number_semitransparent);
10614
10615 if (ping_num_trans == 0)
10616 ping_have_tRNS=MagickFalse;
10617
10618 else
10619 {
10620 if (logging != MagickFalse)
10621 {
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " Scaling ping_trans_color (1)");
10624 }
10625 ping_have_tRNS=MagickTrue;
10626
10627 for (i=0; i < ping_num_trans; i++)
10628 {
10629 ping_trans_alpha[i]= (png_byte)
10630 ScaleQuantumToChar(image->colormap[i].alpha);
10631 }
10632 }
10633 }
10634 }
10635 }
10636
10637 else
10638 {
10639
10640 if (image_depth < 8)
10641 image_depth=8;
10642
10643 if ((save_image_depth == 16) && (image_depth == 8))
10644 {
10645 if (logging != MagickFalse)
10646 {
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " Scaling ping_trans_color from (%d,%d,%d)",
10649 (int) ping_trans_color.red,
10650 (int) ping_trans_color.green,
10651 (int) ping_trans_color.blue);
10652 }
10653
10654 ping_trans_color.red*=0x0101;
10655 ping_trans_color.green*=0x0101;
10656 ping_trans_color.blue*=0x0101;
10657 ping_trans_color.gray*=0x0101;
10658
10659 if (logging != MagickFalse)
10660 {
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " to (%d,%d,%d)",
10663 (int) ping_trans_color.red,
10664 (int) ping_trans_color.green,
10665 (int) ping_trans_color.blue);
10666 }
10667 }
10668 }
10669
10670 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10671 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10672
10673 /*
10674 Adjust background and transparency samples in sub-8-bit grayscale files.
10675 */
10676 if (ping_bit_depth < 8 && ping_color_type ==
10677 PNG_COLOR_TYPE_GRAY)
10678 {
10679 png_uint_16
10680 maxval;
10681
10682 size_t
10683 one=1;
10684
10685 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10686
10687 if (ping_exclude_bKGD == MagickFalse)
10688 {
10689
10690 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10691 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10692 &image->background_color))) +.5)));
10693
10694 if (logging != MagickFalse)
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " Setting up bKGD chunk (2)");
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " background_color index is %d",
10699 (int) ping_background.index);
10700
10701 ping_have_bKGD = MagickTrue;
10702 }
10703
10704 if (logging != MagickFalse)
10705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10706 " Scaling ping_trans_color.gray from %d",
10707 (int)ping_trans_color.gray);
10708
10709 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10710 ping_trans_color.gray)+.5);
10711
10712 if (logging != MagickFalse)
10713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10714 " to %d", (int)ping_trans_color.gray);
10715 }
10716
10717 if (ping_exclude_bKGD == MagickFalse)
10718 {
10719 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10720 {
10721 /*
10722 Identify which colormap entry is the background color.
10723 */
10724
10725 number_colors=image_colors;
10726
10727 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10728 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10729 break;
10730
10731 ping_background.index=(png_byte) i;
10732
10733 if (logging != MagickFalse)
10734 {
10735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736 " Setting up bKGD chunk with index=%d",(int) i);
10737 }
10738
10739 if (i < (ssize_t) number_colors)
10740 {
10741 ping_have_bKGD = MagickTrue;
10742
10743 if (logging != MagickFalse)
10744 {
10745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10746 " background =(%d,%d,%d)",
10747 (int) ping_background.red,
10748 (int) ping_background.green,
10749 (int) ping_background.blue);
10750 }
10751 }
10752
10753 else /* Can't happen */
10754 {
10755 if (logging != MagickFalse)
10756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10757 " No room in PLTE to add bKGD color");
10758 ping_have_bKGD = MagickFalse;
10759 }
10760 }
10761 }
10762
10763 if (logging != MagickFalse)
10764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10765 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10766 ping_color_type);
10767 /*
10768 Initialize compression level and filtering.
10769 */
10770 if (logging != MagickFalse)
10771 {
10772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773 " Setting up deflate compression");
10774
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776 " Compression buffer size: 32768");
10777 }
10778
10779 png_set_compression_buffer_size(ping,32768L);
10780
10781 if (logging != MagickFalse)
10782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10783 " Compression mem level: 9");
10784
10785 png_set_compression_mem_level(ping, 9);
10786
10787 /* Untangle the "-quality" setting:
10788
10789 Undefined is 0; the default is used.
10790 Default is 75
10791
10792 10's digit:
10793
10794 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10795 zlib default compression level
10796
10797 1-9: the zlib compression level
10798
10799 1's digit:
10800
10801 0-4: the PNG filter method
10802
10803 5: libpng adaptive filtering if compression level > 5
10804 libpng filter type "none" if compression level <= 5
10805 or if image is grayscale or palette
10806
10807 6: libpng adaptive filtering
10808
10809 7: "LOCO" filtering (intrapixel differing) if writing
10810 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10811 and earlier because of a missing "else".
10812
10813 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10814 filtering. Unused prior to IM-6.7.0-10, was same as 6
10815
10816 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10817 Unused prior to IM-6.7.0-10, was same as 6
10818
10819 Note that using the -quality option, not all combinations of
10820 PNG filter type, zlib compression level, and zlib compression
10821 strategy are possible. This will be addressed soon in a
10822 release that accomodates "-define png:compression-strategy", etc.
10823
10824 */
10825
10826 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10827 image_info->quality;
10828
10829 if (quality <= 9)
10830 {
10831 if (mng_info->write_png_compression_strategy == 0)
10832 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10833 }
10834
10835 else if (mng_info->write_png_compression_level == 0)
10836 {
10837 int
10838 level;
10839
10840 level=(int) MagickMin((ssize_t) quality/10,9);
10841
10842 mng_info->write_png_compression_level = level+1;
10843 }
10844
10845 if (mng_info->write_png_compression_strategy == 0)
10846 {
10847 if ((quality %10) == 8 || (quality %10) == 9)
10848 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10849 mng_info->write_png_compression_strategy=Z_RLE+1;
10850 #else
10851 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10852 #endif
10853 }
10854
10855 if (mng_info->write_png_compression_filter == 0)
10856 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10857
10858 if (logging != MagickFalse)
10859 {
10860 if (mng_info->write_png_compression_level)
10861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862 " Compression level: %d",
10863 (int) mng_info->write_png_compression_level-1);
10864
10865 if (mng_info->write_png_compression_strategy)
10866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867 " Compression strategy: %d",
10868 (int) mng_info->write_png_compression_strategy-1);
10869
10870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871 " Setting up filtering");
10872
10873 if (mng_info->write_png_compression_filter == 6)
10874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10875 " Base filter method: ADAPTIVE");
10876 else if (mng_info->write_png_compression_filter == 0 ||
10877 mng_info->write_png_compression_filter == 1)
10878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879 " Base filter method: NONE");
10880 else
10881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882 " Base filter method: %d",
10883 (int) mng_info->write_png_compression_filter-1);
10884 }
10885
10886 if (mng_info->write_png_compression_level != 0)
10887 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10888
10889 if (mng_info->write_png_compression_filter == 6)
10890 {
10891 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10892 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10893 (quality < 50))
10894 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10895 else
10896 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10897 }
10898 else if (mng_info->write_png_compression_filter == 7 ||
10899 mng_info->write_png_compression_filter == 10)
10900 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10901
10902 else if (mng_info->write_png_compression_filter == 8)
10903 {
10904 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10905 if (mng_info->write_mng)
10906 {
10907 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10908 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10909 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10910 }
10911 #endif
10912 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10913 }
10914
10915 else if (mng_info->write_png_compression_filter == 9)
10916 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10917
10918 else if (mng_info->write_png_compression_filter != 0)
10919 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10920 mng_info->write_png_compression_filter-1);
10921
10922 if (mng_info->write_png_compression_strategy != 0)
10923 png_set_compression_strategy(ping,
10924 mng_info->write_png_compression_strategy-1);
10925
10926 ping_interlace_method=image_info->interlace != NoInterlace;
10927
10928 if (mng_info->write_mng)
10929 png_set_sig_bytes(ping,8);
10930
10931 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10932
10933 if (mng_info->write_png_colortype != 0)
10934 {
10935 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10936 if (ping_have_color != MagickFalse)
10937 {
10938 ping_color_type = PNG_COLOR_TYPE_RGB;
10939
10940 if (ping_bit_depth < 8)
10941 ping_bit_depth=8;
10942 }
10943
10944 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10945 if (ping_have_color != MagickFalse)
10946 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10947 }
10948
10949 if (ping_need_colortype_warning != MagickFalse ||
10950 ((mng_info->write_png_depth &&
10951 (int) mng_info->write_png_depth != ping_bit_depth) ||
10952 (mng_info->write_png_colortype &&
10953 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10954 mng_info->write_png_colortype != 7 &&
10955 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10956 {
10957 if (logging != MagickFalse)
10958 {
10959 if (ping_need_colortype_warning != MagickFalse)
10960 {
10961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10962 " Image has transparency but tRNS chunk was excluded");
10963 }
10964
10965 if (mng_info->write_png_depth)
10966 {
10967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10968 " Defined png:bit-depth=%u, Computed depth=%u",
10969 mng_info->write_png_depth,
10970 ping_bit_depth);
10971 }
10972
10973 if (mng_info->write_png_colortype)
10974 {
10975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10976 " Defined png:color-type=%u, Computed color type=%u",
10977 mng_info->write_png_colortype-1,
10978 ping_color_type);
10979 }
10980 }
10981
10982 png_warning(ping,
10983 "Cannot write image with defined png:bit-depth or png:color-type.");
10984 }
10985
10986 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10987 {
10988 /* Add an opaque matte channel */
10989 image->alpha_trait = BlendPixelTrait;
10990 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10991
10992 if (logging != MagickFalse)
10993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10994 " Added an opaque matte channel");
10995 }
10996
10997 if (number_transparent != 0 || number_semitransparent != 0)
10998 {
10999 if (ping_color_type < 4)
11000 {
11001 ping_have_tRNS=MagickTrue;
11002 if (logging != MagickFalse)
11003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11004 " Setting ping_have_tRNS=MagickTrue.");
11005 }
11006 }
11007
11008 if (logging != MagickFalse)
11009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010 " Writing PNG header chunks");
11011
11012 png_set_IHDR(ping,ping_info,ping_width,ping_height,
11013 ping_bit_depth,ping_color_type,
11014 ping_interlace_method,ping_compression_method,
11015 ping_filter_method);
11016
11017 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
11018 {
11019 png_set_PLTE(ping,ping_info,palette,number_colors);
11020
11021 if (logging != MagickFalse)
11022 {
11023 for (i=0; i< (ssize_t) number_colors; i++)
11024 {
11025 if (i < ping_num_trans)
11026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11027 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
11028 (int) i,
11029 (int) palette[i].red,
11030 (int) palette[i].green,
11031 (int) palette[i].blue,
11032 (int) i,
11033 (int) ping_trans_alpha[i]);
11034 else
11035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11036 " PLTE[%d] = (%d,%d,%d)",
11037 (int) i,
11038 (int) palette[i].red,
11039 (int) palette[i].green,
11040 (int) palette[i].blue);
11041 }
11042 }
11043 }
11044
11045 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11046 if (ping_exclude_sRGB != MagickFalse ||
11047 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11048 {
11049 if ((ping_exclude_tEXt == MagickFalse ||
11050 ping_exclude_zTXt == MagickFalse) &&
11051 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11052 {
11053 ResetImageProfileIterator(image);
11054 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11055 {
11056 profile=GetImageProfile(image,name);
11057
11058 if (profile != (StringInfo *) NULL)
11059 {
11060 #ifdef PNG_WRITE_iCCP_SUPPORTED
11061 if ((LocaleCompare(name,"ICC") == 0) ||
11062 (LocaleCompare(name,"ICM") == 0))
11063 {
11064 ping_have_iCCP = MagickTrue;
11065 if (ping_exclude_iCCP == MagickFalse)
11066 {
11067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068 " Setting up iCCP chunk");
11069
11070 png_set_iCCP(ping,ping_info,(png_charp) name,0,
11071 #if (PNG_LIBPNG_VER < 10500)
11072 (png_charp) GetStringInfoDatum(profile),
11073 #else
11074 (const png_byte *) GetStringInfoDatum(profile),
11075 #endif
11076 (png_uint_32) GetStringInfoLength(profile));
11077 }
11078 else
11079 {
11080 /* Do not write hex-encoded ICC chunk */
11081 name=GetNextImageProfile(image);
11082 continue;
11083 }
11084 }
11085 #endif /* WRITE_iCCP */
11086
11087 if (LocaleCompare(name,"exif") == 0)
11088 {
11089 /* Do not write hex-encoded ICC chunk; we will
11090 write it later as an eXIf chunk */
11091 name=GetNextImageProfile(image);
11092 continue;
11093 }
11094
11095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11096 " Setting up zTXt chunk with uuencoded %s profile",
11097 name);
11098 Magick_png_write_raw_profile(image_info,ping,ping_info,
11099 (unsigned char *) name,(unsigned char *) name,
11100 GetStringInfoDatum(profile),(png_uint_32)
11101 GetStringInfoLength(profile),exception);
11102 }
11103 name=GetNextImageProfile(image);
11104 }
11105 }
11106 }
11107
11108 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11109 if ((mng_info->have_write_global_srgb == 0) &&
11110 ping_have_iCCP != MagickTrue &&
11111 (ping_have_sRGB != MagickFalse ||
11112 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11113 {
11114 if (ping_exclude_sRGB == MagickFalse)
11115 {
11116 /*
11117 Note image rendering intent.
11118 */
11119 if (logging != MagickFalse)
11120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11121 " Setting up sRGB chunk");
11122
11123 (void) png_set_sRGB(ping,ping_info,(
11124 Magick_RenderingIntent_to_PNG_RenderingIntent(
11125 image->rendering_intent)));
11126
11127 ping_have_sRGB = MagickTrue;
11128 }
11129 }
11130
11131 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11132 #endif
11133 {
11134 if (ping_exclude_gAMA == MagickFalse &&
11135 ping_have_iCCP == MagickFalse &&
11136 ping_have_sRGB == MagickFalse &&
11137 (ping_exclude_sRGB == MagickFalse ||
11138 (image->gamma < .45 || image->gamma > .46)))
11139 {
11140 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11141 {
11142 /*
11143 Note image gamma.
11144 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11145 */
11146 if (logging != MagickFalse)
11147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148 " Setting up gAMA chunk");
11149
11150 png_set_gAMA(ping,ping_info,image->gamma);
11151 }
11152 }
11153
11154 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11155 {
11156 if ((mng_info->have_write_global_chrm == 0) &&
11157 (image->chromaticity.red_primary.x != 0.0))
11158 {
11159 /*
11160 Note image chromaticity.
11161 Note: if cHRM+gAMA == sRGB write sRGB instead.
11162 */
11163 PrimaryInfo
11164 bp,
11165 gp,
11166 rp,
11167 wp;
11168
11169 wp=image->chromaticity.white_point;
11170 rp=image->chromaticity.red_primary;
11171 gp=image->chromaticity.green_primary;
11172 bp=image->chromaticity.blue_primary;
11173
11174 if (logging != MagickFalse)
11175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176 " Setting up cHRM chunk");
11177
11178 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11179 bp.x,bp.y);
11180 }
11181 }
11182 }
11183
11184 if (ping_exclude_bKGD == MagickFalse)
11185 {
11186 if (ping_have_bKGD != MagickFalse)
11187 {
11188 png_set_bKGD(ping,ping_info,&ping_background);
11189 if (logging != MagickFalse)
11190 {
11191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192 " Setting up bKGD chunk");
11193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194 " background color = (%d,%d,%d)",
11195 (int) ping_background.red,
11196 (int) ping_background.green,
11197 (int) ping_background.blue);
11198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11199 " index = %d, gray=%d",
11200 (int) ping_background.index,
11201 (int) ping_background.gray);
11202 }
11203 }
11204 }
11205
11206 if (ping_exclude_pHYs == MagickFalse)
11207 {
11208 if (ping_have_pHYs != MagickFalse)
11209 {
11210 png_set_pHYs(ping,ping_info,
11211 ping_pHYs_x_resolution,
11212 ping_pHYs_y_resolution,
11213 ping_pHYs_unit_type);
11214
11215 if (logging != MagickFalse)
11216 {
11217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11218 " Setting up pHYs chunk");
11219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11220 " x_resolution=%lu",
11221 (unsigned long) ping_pHYs_x_resolution);
11222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223 " y_resolution=%lu",
11224 (unsigned long) ping_pHYs_y_resolution);
11225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11226 " unit_type=%lu",
11227 (unsigned long) ping_pHYs_unit_type);
11228 }
11229 }
11230 }
11231
11232 #if defined(PNG_tIME_SUPPORTED)
11233 if (ping_exclude_tIME == MagickFalse)
11234 {
11235 const char
11236 *timestamp;
11237
11238 if (image->taint == MagickFalse)
11239 {
11240 timestamp=GetImageOption(image_info,"png:tIME");
11241
11242 if (timestamp == (const char *) NULL)
11243 timestamp=GetImageProperty(image,"png:tIME",exception);
11244 }
11245
11246 else
11247 {
11248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11249 " Reset tIME in tainted image");
11250
11251 timestamp=GetImageProperty(image,"date:modify",exception);
11252 }
11253
11254 if (timestamp != (const char *) NULL)
11255 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11256 }
11257 #endif
11258
11259 if (mng_info->need_blob != MagickFalse)
11260 {
11261 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11262 MagickFalse)
11263 png_error(ping,"WriteBlob Failed");
11264
11265 ping_have_blob=MagickTrue;
11266 }
11267
11268 png_write_info_before_PLTE(ping, ping_info);
11269
11270 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11271 {
11272 if (logging != MagickFalse)
11273 {
11274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11275 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11276 }
11277
11278 if (ping_color_type == 3)
11279 (void) png_set_tRNS(ping, ping_info,
11280 ping_trans_alpha,
11281 ping_num_trans,
11282 NULL);
11283
11284 else
11285 {
11286 (void) png_set_tRNS(ping, ping_info,
11287 NULL,
11288 0,
11289 &ping_trans_color);
11290
11291 if (logging != MagickFalse)
11292 {
11293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11294 " tRNS color =(%d,%d,%d)",
11295 (int) ping_trans_color.red,
11296 (int) ping_trans_color.green,
11297 (int) ping_trans_color.blue);
11298 }
11299 }
11300 }
11301
11302 png_write_info(ping,ping_info);
11303
11304 /* write orNT if image->orientation is defined */
11305 if (image->orientation != UndefinedOrientation)
11306 {
11307 unsigned char
11308 chunk[6];
11309 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11310 PNGType(chunk,mng_orNT);
11311 LogPNGChunk(logging,mng_orNT,1L);
11312 /* PNG uses Exif orientation values */
11313 chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11314 (void) WriteBlob(image,5,chunk);
11315 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11316 }
11317
11318 ping_wrote_caNv = MagickFalse;
11319
11320 /* write caNv chunk */
11321 if (ping_exclude_caNv == MagickFalse)
11322 {
11323 if ((image->page.width != 0 && image->page.width != image->columns) ||
11324 (image->page.height != 0 && image->page.height != image->rows) ||
11325 image->page.x != 0 || image->page.y != 0)
11326 {
11327 unsigned char
11328 chunk[20];
11329
11330 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11331 PNGType(chunk,mng_caNv);
11332 LogPNGChunk(logging,mng_caNv,16L);
11333 PNGLong(chunk+4,(png_uint_32) image->page.width);
11334 PNGLong(chunk+8,(png_uint_32) image->page.height);
11335 PNGsLong(chunk+12,(png_int_32) image->page.x);
11336 PNGsLong(chunk+16,(png_int_32) image->page.y);
11337 (void) WriteBlob(image,20,chunk);
11338 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11339 ping_wrote_caNv = MagickTrue;
11340 }
11341 }
11342
11343 #if defined(PNG_oFFs_SUPPORTED)
11344 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11345 {
11346 if (image->page.x || image->page.y)
11347 {
11348 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11349 (png_int_32) image->page.y, 0);
11350
11351 if (logging != MagickFalse)
11352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11353 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11354 (int) image->page.x, (int) image->page.y);
11355 }
11356 }
11357 #endif
11358
11359 #if (PNG_LIBPNG_VER == 10206)
11360 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11361 #define PNG_HAVE_IDAT 0x04
11362 ping->mode |= PNG_HAVE_IDAT;
11363 #undef PNG_HAVE_IDAT
11364 #endif
11365
11366 png_set_packing(ping);
11367 /*
11368 Allocate memory.
11369 */
11370 rowbytes=image->columns;
11371 if (image_depth > 8)
11372 rowbytes*=2;
11373 switch (ping_color_type)
11374 {
11375 case PNG_COLOR_TYPE_RGB:
11376 rowbytes*=3;
11377 break;
11378
11379 case PNG_COLOR_TYPE_GRAY_ALPHA:
11380 rowbytes*=2;
11381 break;
11382
11383 case PNG_COLOR_TYPE_RGBA:
11384 rowbytes*=4;
11385 break;
11386
11387 default:
11388 break;
11389 }
11390
11391 if (logging != MagickFalse)
11392 {
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394 " Writing PNG image data");
11395
11396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11398 }
11399 pixel_info=AcquireVirtualMemory(rowbytes,GetPixelChannels(image)*
11400 sizeof(*ping_pixels));
11401 if (pixel_info == (MemoryInfo *) NULL)
11402 png_error(ping,"Allocation of memory for pixels failed");
11403 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11404 (void) memset(ping_pixels,0,rowbytes*GetPixelChannels(image)*
11405 sizeof(*ping_pixels));
11406 /*
11407 Initialize image scanlines.
11408 */
11409 quantum_info=AcquireQuantumInfo(image_info,image);
11410 if (quantum_info == (QuantumInfo *) NULL)
11411 png_error(ping,"Memory allocation for quantum_info failed");
11412 quantum_info->format=UndefinedQuantumFormat;
11413 SetQuantumDepth(image,quantum_info,image_depth);
11414 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11415 num_passes=png_set_interlace_handling(ping);
11416
11417 if ((mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE) ||
11418 ((!mng_info->write_png8 && !mng_info->write_png24 &&
11419 !mng_info->write_png48 && !mng_info->write_png64 &&
11420 !mng_info->write_png32) &&
11421 (mng_info->IsPalette ||
11422 (image_info->type == BilevelType)) &&
11423 image_matte == MagickFalse &&
11424 ping_have_non_bw == MagickFalse))
11425 {
11426 /* Palette, Bilevel, or Opaque Monochrome */
11427 QuantumType
11428 quantum_type;
11429
11430 const Quantum
11431 *p;
11432
11433 quantum_type=RedQuantum;
11434 if (mng_info->IsPalette)
11435 {
11436 quantum_type=GrayQuantum;
11437 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE)
11438 quantum_type=IndexQuantum;
11439 }
11440 SetQuantumDepth(image,quantum_info,8);
11441 for (pass=0; pass < num_passes; pass++)
11442 {
11443 /*
11444 Convert PseudoClass image to a PNG monochrome image.
11445 */
11446 for (y=0; y < (ssize_t) image->rows; y++)
11447 {
11448 if (logging != MagickFalse && y == 0)
11449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11450 " Writing row of pixels (0)");
11451
11452 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11453
11454 if (p == (const Quantum *) NULL)
11455 break;
11456
11457 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11458 quantum_info,quantum_type,ping_pixels,exception);
11459 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11460 for (i=0; i < (ssize_t) image->columns; i++)
11461 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11462 255 : 0);
11463
11464 if (logging != MagickFalse && y == 0)
11465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 " Writing row of pixels (1)");
11467
11468 png_write_row(ping,ping_pixels);
11469
11470 status=SetImageProgress(image,SaveImageTag,
11471 (MagickOffsetType) (pass * image->rows + y),
11472 num_passes * image->rows);
11473
11474 if (status == MagickFalse)
11475 break;
11476 }
11477 }
11478 }
11479
11480 else /* Not Palette, Bilevel, or Opaque Monochrome */
11481 {
11482 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11483 !mng_info->write_png48 && !mng_info->write_png64 &&
11484 !mng_info->write_png32) && (image_matte != MagickFalse ||
11485 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11486 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11487 {
11488 const Quantum
11489 *p;
11490
11491 for (pass=0; pass < num_passes; pass++)
11492 {
11493
11494 for (y=0; y < (ssize_t) image->rows; y++)
11495 {
11496 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11497
11498 if (p == (const Quantum *) NULL)
11499 break;
11500
11501 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11502 {
11503 if (mng_info->IsPalette)
11504 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11505 quantum_info,GrayQuantum,ping_pixels,exception);
11506
11507 else
11508 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11509 quantum_info,RedQuantum,ping_pixels,exception);
11510
11511 if (logging != MagickFalse && y == 0)
11512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11513 " Writing GRAY PNG pixels (2)");
11514 }
11515
11516 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11517 {
11518 if (logging != MagickFalse && y == 0)
11519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11520 " Writing GRAY_ALPHA PNG pixels (2)");
11521
11522 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11523 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11524 }
11525
11526 if (logging != MagickFalse && y == 0)
11527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 " Writing row of pixels (2)");
11529
11530 png_write_row(ping,ping_pixels);
11531
11532 status=SetImageProgress(image,SaveImageTag,
11533 (MagickOffsetType) (pass * image->rows + y),
11534 num_passes * image->rows);
11535
11536 if (status == MagickFalse)
11537 break;
11538 }
11539 }
11540 }
11541
11542 else
11543 {
11544 const Quantum
11545 *p;
11546
11547 for (pass=0; pass < num_passes; pass++)
11548 {
11549 if ((image_depth > 8) ||
11550 mng_info->write_png24 ||
11551 mng_info->write_png32 ||
11552 mng_info->write_png48 ||
11553 mng_info->write_png64 ||
11554 (!mng_info->write_png8 && !mng_info->IsPalette))
11555 {
11556 for (y=0; y < (ssize_t) image->rows; y++)
11557 {
11558 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11559
11560 if (p == (const Quantum *) NULL)
11561 break;
11562
11563 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11564 {
11565 if (image->storage_class == DirectClass)
11566 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567 quantum_info,RedQuantum,ping_pixels,exception);
11568
11569 else
11570 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11571 quantum_info,GrayQuantum,ping_pixels,exception);
11572 }
11573
11574 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11575 {
11576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577 quantum_info,GrayAlphaQuantum,ping_pixels,
11578 exception);
11579
11580 if (logging != MagickFalse && y == 0)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " Writing GRAY_ALPHA PNG pixels (3)");
11583 }
11584
11585 else if (image_matte != MagickFalse)
11586 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11587 quantum_info,RGBAQuantum,ping_pixels,exception);
11588
11589 else
11590 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11591 quantum_info,RGBQuantum,ping_pixels,exception);
11592
11593 if (logging != MagickFalse && y == 0)
11594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11595 " Writing row of pixels (3)");
11596
11597 png_write_row(ping,ping_pixels);
11598
11599 status=SetImageProgress(image,SaveImageTag,
11600 (MagickOffsetType) (pass * image->rows + y),
11601 num_passes * image->rows);
11602
11603 if (status == MagickFalse)
11604 break;
11605 }
11606 }
11607
11608 else
11609 /* not ((image_depth > 8) ||
11610 mng_info->write_png24 || mng_info->write_png32 ||
11611 mng_info->write_png48 || mng_info->write_png64 ||
11612 (!mng_info->write_png8 && !mng_info->IsPalette))
11613 */
11614 {
11615 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11616 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11617 {
11618 if (logging != MagickFalse)
11619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11620 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11621
11622 SetQuantumDepth(image,quantum_info,8);
11623 image_depth=8;
11624 }
11625
11626 for (y=0; y < (ssize_t) image->rows; y++)
11627 {
11628 if (logging != MagickFalse && y == 0)
11629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11631 pass);
11632
11633 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11634
11635 if (p == (const Quantum *) NULL)
11636 break;
11637
11638 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11639 {
11640 SetQuantumDepth(image,quantum_info,image->depth);
11641
11642 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11643 quantum_info,GrayQuantum,ping_pixels,exception);
11644 }
11645
11646 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11647 {
11648 if (logging != MagickFalse && y == 0)
11649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11650 " Writing GRAY_ALPHA PNG pixels (4)");
11651
11652 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11653 quantum_info,GrayAlphaQuantum,ping_pixels,
11654 exception);
11655 }
11656
11657 else
11658 {
11659 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11660 quantum_info,IndexQuantum,ping_pixels,exception);
11661
11662 if (logging != MagickFalse && y <= 2)
11663 {
11664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11665 " Writing row of non-gray pixels (4)");
11666
11667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11668 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11669 (int)ping_pixels[0],(int)ping_pixels[1]);
11670 }
11671 }
11672 png_write_row(ping,ping_pixels);
11673
11674 status=SetImageProgress(image,SaveImageTag,
11675 (MagickOffsetType) (pass * image->rows + y),
11676 num_passes * image->rows);
11677
11678 if (status == MagickFalse)
11679 break;
11680 }
11681 }
11682 }
11683 }
11684 }
11685
11686 if (quantum_info != (QuantumInfo *) NULL)
11687 quantum_info=DestroyQuantumInfo(quantum_info);
11688
11689 if (logging != MagickFalse)
11690 {
11691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692 " Wrote PNG image data");
11693
11694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11695 " Width: %.20g",(double) ping_width);
11696
11697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11698 " Height: %.20g",(double) ping_height);
11699
11700 if (mng_info->write_png_depth)
11701 {
11702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11704 }
11705
11706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11707 " PNG bit-depth written: %d",ping_bit_depth);
11708
11709 if (mng_info->write_png_colortype)
11710 {
11711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11713 }
11714
11715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11716 " PNG color-type written: %d",ping_color_type);
11717
11718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719 " PNG Interlace method: %d",ping_interlace_method);
11720 }
11721 /*
11722 Generate text chunks after IDAT.
11723 */
11724 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11725 {
11726 ResetImagePropertyIterator(image);
11727 property=GetNextImageProperty(image);
11728 while (property != (const char *) NULL)
11729 {
11730 png_textp
11731 text;
11732
11733 value=GetImageProperty(image,property,exception);
11734
11735 /* Don't write any "png:" or "jpeg:" properties; those are just for
11736 * "identify" or for passing through to another JPEG
11737 */
11738 if ((LocaleNCompare(property,"png:",4) != 0 &&
11739 LocaleNCompare(property,"jpeg:",5) != 0) &&
11740
11741
11742 /* Suppress density and units if we wrote a pHYs chunk */
11743 (ping_exclude_pHYs != MagickFalse ||
11744 LocaleCompare(property,"density") != 0 ||
11745 LocaleCompare(property,"units") != 0) &&
11746
11747 /* Suppress the IM-generated Date:create and Date:modify */
11748 (ping_exclude_date == MagickFalse ||
11749 LocaleNCompare(property, "Date:",5) != 0))
11750 {
11751 if (value != (const char *) NULL)
11752 {
11753
11754 #if PNG_LIBPNG_VER >= 10400
11755 text=(png_textp) png_malloc(ping,
11756 (png_alloc_size_t) sizeof(png_text));
11757 #else
11758 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11759 #endif
11760 text[0].key=(char *) property;
11761 text[0].text=(char *) value;
11762 text[0].text_length=strlen(value);
11763
11764 if (ping_exclude_tEXt != MagickFalse)
11765 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11766
11767 else if (ping_exclude_zTXt != MagickFalse)
11768 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11769
11770 else
11771 {
11772 text[0].compression=image_info->compression == NoCompression ||
11773 (image_info->compression == UndefinedCompression &&
11774 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11775 PNG_TEXT_COMPRESSION_zTXt ;
11776 }
11777
11778 if (logging != MagickFalse)
11779 {
11780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781 " Setting up text chunk");
11782
11783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784 " keyword: '%s'",text[0].key);
11785 }
11786
11787 png_set_text(ping,ping_info,text,1);
11788 png_free(ping,text);
11789 }
11790 }
11791 property=GetNextImageProperty(image);
11792 }
11793 }
11794
11795 /* write eXIf profile */
11796 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11797 {
11798 ResetImageProfileIterator(image);
11799
11800 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11801 {
11802 if (LocaleCompare(name,"exif") == 0)
11803 {
11804 profile=GetImageProfile(image,name);
11805
11806 if (profile != (StringInfo *) NULL)
11807 {
11808 png_uint_32
11809 length;
11810
11811 unsigned char
11812 chunk[4],
11813 *data;
11814
11815 StringInfo
11816 *ping_profile;
11817
11818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " Have eXIf profile");
11820
11821 ping_profile=CloneStringInfo(profile);
11822 data=GetStringInfoDatum(ping_profile),
11823 length=(png_uint_32) GetStringInfoLength(ping_profile);
11824
11825 PNGType(chunk,mng_eXIf);
11826 if (length < 7)
11827 {
11828 ping_profile=DestroyStringInfo(ping_profile);
11829 break; /* otherwise crashes */
11830 }
11831
11832 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11833 *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11834 {
11835 /* skip the "Exif\0\0" JFIF Exif Header ID */
11836 length -= 6;
11837 data += 6;
11838 }
11839
11840 LogPNGChunk(logging,chunk,length);
11841 (void) WriteBlobMSBULong(image,length);
11842 (void) WriteBlob(image,4,chunk);
11843 (void) WriteBlob(image,length,data);
11844 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11845 (uInt) length));
11846 ping_profile=DestroyStringInfo(ping_profile);
11847 break;
11848 }
11849 }
11850 name=GetNextImageProfile(image);
11851 }
11852 }
11853
11854 if (logging != MagickFalse)
11855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11856 " Writing PNG end info");
11857
11858 png_write_end(ping,ping_info);
11859
11860 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11861 {
11862 if (mng_info->page.x || mng_info->page.y ||
11863 (ping_width != mng_info->page.width) ||
11864 (ping_height != mng_info->page.height))
11865 {
11866 unsigned char
11867 chunk[32];
11868
11869 /*
11870 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11871 */
11872 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11873 PNGType(chunk,mng_FRAM);
11874 LogPNGChunk(logging,mng_FRAM,27L);
11875 chunk[4]=4;
11876 chunk[5]=0; /* frame name separator (no name) */
11877 chunk[6]=1; /* flag for changing delay, for next frame only */
11878 chunk[7]=0; /* flag for changing frame timeout */
11879 chunk[8]=1; /* flag for changing frame clipping for next frame */
11880 chunk[9]=0; /* flag for changing frame sync_id */
11881 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11882 chunk[14]=0; /* clipping boundaries delta type */
11883 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11884 PNGLong(chunk+19,
11885 (png_uint_32) (mng_info->page.x + ping_width));
11886 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11887 PNGLong(chunk+27,
11888 (png_uint_32) (mng_info->page.y + ping_height));
11889 (void) WriteBlob(image,31,chunk);
11890 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11891 mng_info->old_framing_mode=4;
11892 mng_info->framing_mode=1;
11893 }
11894
11895 else
11896 mng_info->framing_mode=3;
11897 }
11898 if (mng_info->write_mng && !mng_info->need_fram &&
11899 ((int) image->dispose == 3))
11900 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11901
11902 /*
11903 Free PNG resources.
11904 */
11905
11906 png_destroy_write_struct(&ping,&ping_info);
11907
11908 pixel_info=RelinquishVirtualMemory(pixel_info);
11909
11910 if (ping_have_blob != MagickFalse)
11911 (void) CloseBlob(image);
11912
11913 image_info=DestroyImageInfo(image_info);
11914 image=DestroyImage(image);
11915
11916 /* Store bit depth actually written */
11917 s[0]=(char) ping_bit_depth;
11918 s[1]='\0';
11919
11920 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11921
11922 if (logging != MagickFalse)
11923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11924 " exit WriteOnePNGImage()");
11925
11926 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11927 UnlockSemaphoreInfo(ping_semaphore);
11928 #endif
11929
11930 /* } for navigation to beginning of SETJMP-protected block. Revert to
11931 * Throwing an Exception when an error occurs.
11932 */
11933
11934 return(MagickTrue);
11935 /* End write one PNG image */
11936
11937 }
11938
11939 /*
11940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11941 % %
11942 % %
11943 % %
11944 % W r i t e P N G I m a g e %
11945 % %
11946 % %
11947 % %
11948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11949 %
11950 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11951 % Multiple-image Network Graphics (MNG) image file.
11952 %
11953 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11954 %
11955 % The format of the WritePNGImage method is:
11956 %
11957 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11958 % Image *image,ExceptionInfo *exception)
11959 %
11960 % A description of each parameter follows:
11961 %
11962 % o image_info: the image info.
11963 %
11964 % o image: The image.
11965 %
11966 % o exception: return any errors or warnings in this structure.
11967 %
11968 % Returns MagickTrue on success, MagickFalse on failure.
11969 %
11970 % Communicating with the PNG encoder:
11971 %
11972 % While the datastream written is always in PNG format and normally would
11973 % be given the "png" file extension, this method also writes the following
11974 % pseudo-formats which are subsets of png:
11975 %
11976 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11977 % a depth greater than 8, the depth is reduced. If transparency
11978 % is present, the tRNS chunk must only have values 0 and 255
11979 % (i.e., transparency is binary: fully opaque or fully
11980 % transparent). If other values are present they will be
11981 % 50%-thresholded to binary transparency. If more than 256
11982 % colors are present, they will be quantized to the 4-4-4-1,
11983 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11984 % of any resulting fully-transparent pixels is changed to
11985 % the image's background color.
11986 %
11987 % If you want better quantization or dithering of the colors
11988 % or alpha than that, you need to do it before calling the
11989 % PNG encoder. The pixels contain 8-bit indices even if
11990 % they could be represented with 1, 2, or 4 bits. Grayscale
11991 % images will be written as indexed PNG files even though the
11992 % PNG grayscale type might be slightly more efficient. Please
11993 % note that writing to the PNG8 format may result in loss
11994 % of color and alpha data.
11995 %
11996 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11997 % chunk can be present to convey binary transparency by naming
11998 % one of the colors as transparent. The only loss incurred
11999 % is reduction of sample depth to 8. If the image has more
12000 % than one transparent color, has semitransparent pixels, or
12001 % has an opaque pixel with the same RGB components as the
12002 % transparent color, an image is not written.
12003 %
12004 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
12005 % transparency is permitted, i.e., the alpha sample for
12006 % each pixel can have any value from 0 to 255. The alpha
12007 % channel is present even if the image is fully opaque.
12008 % The only loss in data is the reduction of the sample depth
12009 % to 8.
12010 %
12011 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
12012 % chunk can be present to convey binary transparency by naming
12013 % one of the colors as transparent. If the image has more
12014 % than one transparent color, has semitransparent pixels, or
12015 % has an opaque pixel with the same RGB components as the
12016 % transparent color, an image is not written.
12017 %
12018 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
12019 % transparency is permitted, i.e., the alpha sample for
12020 % each pixel can have any value from 0 to 65535. The alpha
12021 % channel is present even if the image is fully opaque.
12022 %
12023 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
12024 % image, if the input was a PNG, is written. If these values
12025 % cannot be found, or if the pixels have been changed in a way
12026 % that makes this impossible, then "PNG00" falls back to the
12027 % regular "PNG" format.
12028 %
12029 % o -define: For more precise control of the PNG output, you can use the
12030 % Image options "png:bit-depth" and "png:color-type". These
12031 % can be set from the commandline with "-define" and also
12032 % from the application programming interfaces. The options
12033 % are case-independent and are converted to lowercase before
12034 % being passed to this encoder.
12035 %
12036 % png:color-type can be 0, 2, 3, 4, or 6.
12037 %
12038 % When png:color-type is 0 (Grayscale), png:bit-depth can
12039 % be 1, 2, 4, 8, or 16.
12040 %
12041 % When png:color-type is 2 (RGB), png:bit-depth can
12042 % be 8 or 16.
12043 %
12044 % When png:color-type is 3 (Indexed), png:bit-depth can
12045 % be 1, 2, 4, or 8. This refers to the number of bits
12046 % used to store the index. The color samples always have
12047 % bit-depth 8 in indexed PNG files.
12048 %
12049 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12050 % png:bit-depth can be 8 or 16.
12051 %
12052 % If the image cannot be written without loss with the
12053 % requested bit-depth and color-type, a PNG file will not
12054 % be written, a warning will be issued, and the encoder will
12055 % return MagickFalse.
12056 %
12057 % Since image encoders should not be responsible for the "heavy lifting",
12058 % the user should make sure that ImageMagick has already reduced the
12059 % image depth and number of colors and limit transparency to binary
12060 % transparency prior to attempting to write the image with depth, color,
12061 % or transparency limitations.
12062 %
12063 % Note that another definition, "png:bit-depth-written" exists, but it
12064 % is not intended for external use. It is only used internally by the
12065 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12066 %
12067 % As of version 6.6.6 the following optimizations are always done:
12068 %
12069 % o 32-bit depth is reduced to 16.
12070 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
12071 % high byte and low byte are identical.
12072 % o Palette is sorted to remove unused entries and to put a
12073 % transparent color first, if BUILD_PNG_PALETTE is defined.
12074 % o Opaque matte channel is removed when writing an indexed PNG.
12075 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
12076 % this can be done without loss and a larger bit depth N was not
12077 % requested via the "-define png:bit-depth=N" option.
12078 % o If matte channel is present but only one transparent color is
12079 % present, RGB+tRNS is written instead of RGBA
12080 % o Opaque matte channel is removed (or added, if color-type 4 or 6
12081 % was requested when converting an opaque image).
12082 %
12083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12084 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12085 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12086 Image *image,ExceptionInfo *exception)
12087 {
12088 MagickBooleanType
12089 excluding,
12090 logging,
12091 status;
12092
12093 MngInfo
12094 *mng_info;
12095
12096 const char
12097 *value;
12098
12099 int
12100 source;
12101
12102 /*
12103 Open image file.
12104 */
12105 assert(image_info != (const ImageInfo *) NULL);
12106 assert(image_info->signature == MagickCoreSignature);
12107 assert(image != (Image *) NULL);
12108 assert(image->signature == MagickCoreSignature);
12109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12110 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12111 /*
12112 Allocate a MngInfo structure.
12113 */
12114 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12115
12116 if (mng_info == (MngInfo *) NULL)
12117 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12118
12119 /*
12120 Initialize members of the MngInfo structure.
12121 */
12122 (void) memset(mng_info,0,sizeof(MngInfo));
12123 mng_info->image=image;
12124 mng_info->equal_backgrounds=MagickTrue;
12125
12126 /* See if user has requested a specific PNG subformat */
12127
12128 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12129 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12130 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12131 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12132 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12133
12134 value=GetImageOption(image_info,"png:format");
12135
12136 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12137 {
12138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12139 " Format=%s",value);
12140
12141 mng_info->write_png8 = MagickFalse;
12142 mng_info->write_png24 = MagickFalse;
12143 mng_info->write_png32 = MagickFalse;
12144 mng_info->write_png48 = MagickFalse;
12145 mng_info->write_png64 = MagickFalse;
12146
12147 if (LocaleCompare(value,"png8") == 0)
12148 mng_info->write_png8 = MagickTrue;
12149
12150 else if (LocaleCompare(value,"png24") == 0)
12151 mng_info->write_png24 = MagickTrue;
12152
12153 else if (LocaleCompare(value,"png32") == 0)
12154 mng_info->write_png32 = MagickTrue;
12155
12156 else if (LocaleCompare(value,"png48") == 0)
12157 mng_info->write_png48 = MagickTrue;
12158
12159 else if (LocaleCompare(value,"png64") == 0)
12160 mng_info->write_png64 = MagickTrue;
12161
12162 else if ((LocaleCompare(value,"png00") == 0) ||
12163 LocaleCompare(image_info->magick,"PNG00") == 0)
12164 {
12165 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12166 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12167
12168 if (value != (char *) NULL)
12169 {
12170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12171 " png00 inherited bit depth=%s",value);
12172
12173 if (LocaleCompare(value,"1") == 0)
12174 mng_info->write_png_depth = 1;
12175
12176 else if (LocaleCompare(value,"2") == 0)
12177 mng_info->write_png_depth = 2;
12178
12179 else if (LocaleCompare(value,"4") == 0)
12180 mng_info->write_png_depth = 4;
12181
12182 else if (LocaleCompare(value,"8") == 0)
12183 mng_info->write_png_depth = 8;
12184
12185 else if (LocaleCompare(value,"16") == 0)
12186 mng_info->write_png_depth = 16;
12187 }
12188
12189 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12190
12191 if (value != (char *) NULL)
12192 {
12193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12194 " png00 inherited color type=%s",value);
12195
12196 if (LocaleCompare(value,"0") == 0)
12197 mng_info->write_png_colortype = 1;
12198
12199 else if (LocaleCompare(value,"2") == 0)
12200 mng_info->write_png_colortype = 3;
12201
12202 else if (LocaleCompare(value,"3") == 0)
12203 mng_info->write_png_colortype = 4;
12204
12205 else if (LocaleCompare(value,"4") == 0)
12206 mng_info->write_png_colortype = 5;
12207
12208 else if (LocaleCompare(value,"6") == 0)
12209 mng_info->write_png_colortype = 7;
12210 }
12211 }
12212 }
12213
12214 if (mng_info->write_png8)
12215 {
12216 mng_info->write_png_colortype = /* 3 */ 4;
12217 mng_info->write_png_depth = 8;
12218 image->depth = 8;
12219 }
12220
12221 if (mng_info->write_png24)
12222 {
12223 mng_info->write_png_colortype = /* 2 */ 3;
12224 mng_info->write_png_depth = 8;
12225 image->depth = 8;
12226
12227 if (image->alpha_trait != UndefinedPixelTrait)
12228 (void) SetImageType(image,TrueColorAlphaType,exception);
12229
12230 else
12231 (void) SetImageType(image,TrueColorType,exception);
12232
12233 (void) SyncImage(image,exception);
12234 }
12235
12236 if (mng_info->write_png32)
12237 {
12238 mng_info->write_png_colortype = /* 6 */ 7;
12239 mng_info->write_png_depth = 8;
12240 image->depth = 8;
12241 image->alpha_trait = BlendPixelTrait;
12242
12243 (void) SetImageType(image,TrueColorAlphaType,exception);
12244 (void) SyncImage(image,exception);
12245 }
12246
12247 if (mng_info->write_png48)
12248 {
12249 mng_info->write_png_colortype = /* 2 */ 3;
12250 mng_info->write_png_depth = 16;
12251 image->depth = 16;
12252
12253 if (image->alpha_trait != UndefinedPixelTrait)
12254 (void) SetImageType(image,TrueColorAlphaType,exception);
12255
12256 else
12257 (void) SetImageType(image,TrueColorType,exception);
12258
12259 (void) SyncImage(image,exception);
12260 }
12261
12262 if (mng_info->write_png64)
12263 {
12264 mng_info->write_png_colortype = /* 6 */ 7;
12265 mng_info->write_png_depth = 16;
12266 image->depth = 16;
12267 image->alpha_trait = BlendPixelTrait;
12268
12269 (void) SetImageType(image,TrueColorAlphaType,exception);
12270 (void) SyncImage(image,exception);
12271 }
12272
12273 value=GetImageOption(image_info,"png:bit-depth");
12274
12275 if (value != (char *) NULL)
12276 {
12277 if (LocaleCompare(value,"1") == 0)
12278 mng_info->write_png_depth = 1;
12279
12280 else if (LocaleCompare(value,"2") == 0)
12281 mng_info->write_png_depth = 2;
12282
12283 else if (LocaleCompare(value,"4") == 0)
12284 mng_info->write_png_depth = 4;
12285
12286 else if (LocaleCompare(value,"8") == 0)
12287 mng_info->write_png_depth = 8;
12288
12289 else if (LocaleCompare(value,"16") == 0)
12290 mng_info->write_png_depth = 16;
12291
12292 else
12293 (void) ThrowMagickException(exception,
12294 GetMagickModule(),CoderWarning,
12295 "ignoring invalid defined png:bit-depth",
12296 "=%s",value);
12297
12298 if (logging != MagickFalse)
12299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12300 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12301 }
12302
12303 value=GetImageOption(image_info,"png:color-type");
12304
12305 if (value != (char *) NULL)
12306 {
12307 /* We must store colortype+1 because 0 is a valid colortype */
12308 if (LocaleCompare(value,"0") == 0)
12309 mng_info->write_png_colortype = 1;
12310
12311 else if (LocaleCompare(value,"1") == 0)
12312 mng_info->write_png_colortype = 2;
12313
12314 else if (LocaleCompare(value,"2") == 0)
12315 mng_info->write_png_colortype = 3;
12316
12317 else if (LocaleCompare(value,"3") == 0)
12318 mng_info->write_png_colortype = 4;
12319
12320 else if (LocaleCompare(value,"4") == 0)
12321 mng_info->write_png_colortype = 5;
12322
12323 else if (LocaleCompare(value,"6") == 0)
12324 mng_info->write_png_colortype = 7;
12325
12326 else
12327 (void) ThrowMagickException(exception,
12328 GetMagickModule(),CoderWarning,
12329 "ignoring invalid defined png:color-type",
12330 "=%s",value);
12331
12332 if (logging != MagickFalse)
12333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12334 " png:color-type=%d was defined.\n",
12335 mng_info->write_png_colortype-1);
12336 }
12337
12338 /* Check for chunks to be excluded:
12339 *
12340 * The default is to not exclude any known chunks except for any
12341 * listed in the "unused_chunks" array, above.
12342 *
12343 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12344 * define (in the image properties or in the image artifacts)
12345 * or via a mng_info member. For convenience, in addition
12346 * to or instead of a comma-separated list of chunks, the
12347 * "exclude-chunk" string can be simply "all" or "none".
12348 *
12349 * Note that the "-strip" option provides a convenient way of
12350 * doing the equivalent of
12351 *
12352 * -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12353 * iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12354 *
12355 * The exclude-chunk define takes priority over the mng_info.
12356 *
12357 * A "png:include-chunk" define takes priority over both the
12358 * mng_info and the "png:exclude-chunk" define. Like the
12359 * "exclude-chunk" string, it can define "all" or "none" as
12360 * well as a comma-separated list. Chunks that are unknown to
12361 * ImageMagick are always excluded, regardless of their "copy-safe"
12362 * status according to the PNG specification, and even if they
12363 * appear in the "include-chunk" list. Such defines appearing among
12364 * the image options take priority over those found among the image
12365 * artifacts.
12366 *
12367 * Finally, all chunks listed in the "unused_chunks" array are
12368 * automatically excluded, regardless of the other instructions
12369 * or lack thereof.
12370 *
12371 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12372 * will not be written and the gAMA chunk will only be written if it
12373 * is not between .45 and .46, or approximately (1.0/2.2).
12374 *
12375 * If you exclude tRNS and the image has transparency, the colortype
12376 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12377 *
12378 * The -strip option causes StripImage() to set the png:include-chunk
12379 * artifact to "none,trns,gama".
12380 */
12381
12382 mng_info->ping_exclude_bKGD=MagickFalse;
12383 mng_info->ping_exclude_caNv=MagickFalse;
12384 mng_info->ping_exclude_cHRM=MagickFalse;
12385 mng_info->ping_exclude_date=MagickFalse;
12386 mng_info->ping_exclude_eXIf=MagickFalse;
12387 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12388 mng_info->ping_exclude_gAMA=MagickFalse;
12389 mng_info->ping_exclude_iCCP=MagickFalse;
12390 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12391 mng_info->ping_exclude_oFFs=MagickFalse;
12392 mng_info->ping_exclude_pHYs=MagickFalse;
12393 mng_info->ping_exclude_sRGB=MagickFalse;
12394 mng_info->ping_exclude_tEXt=MagickFalse;
12395 mng_info->ping_exclude_tIME=MagickFalse;
12396 mng_info->ping_exclude_tRNS=MagickFalse;
12397 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12398 mng_info->ping_exclude_zTXt=MagickFalse;
12399
12400 mng_info->ping_preserve_colormap=MagickFalse;
12401
12402 value=GetImageOption(image_info,"png:preserve-colormap");
12403 if (value == NULL)
12404 value=GetImageArtifact(image,"png:preserve-colormap");
12405 if (value != NULL)
12406 mng_info->ping_preserve_colormap=MagickTrue;
12407
12408 mng_info->ping_preserve_iCCP=MagickFalse;
12409
12410 value=GetImageOption(image_info,"png:preserve-iCCP");
12411 if (value == NULL)
12412 value=GetImageArtifact(image,"png:preserve-iCCP");
12413 if (value != NULL)
12414 mng_info->ping_preserve_iCCP=MagickTrue;
12415
12416 /* These compression-level, compression-strategy, and compression-filter
12417 * defines take precedence over values from the -quality option.
12418 */
12419 value=GetImageOption(image_info,"png:compression-level");
12420 if (value == NULL)
12421 value=GetImageArtifact(image,"png:compression-level");
12422 if (value != NULL)
12423 {
12424 /* We have to add 1 to everything because 0 is a valid input,
12425 * and we want to use 0 (the default) to mean undefined.
12426 */
12427 if (LocaleCompare(value,"0") == 0)
12428 mng_info->write_png_compression_level = 1;
12429
12430 else if (LocaleCompare(value,"1") == 0)
12431 mng_info->write_png_compression_level = 2;
12432
12433 else if (LocaleCompare(value,"2") == 0)
12434 mng_info->write_png_compression_level = 3;
12435
12436 else if (LocaleCompare(value,"3") == 0)
12437 mng_info->write_png_compression_level = 4;
12438
12439 else if (LocaleCompare(value,"4") == 0)
12440 mng_info->write_png_compression_level = 5;
12441
12442 else if (LocaleCompare(value,"5") == 0)
12443 mng_info->write_png_compression_level = 6;
12444
12445 else if (LocaleCompare(value,"6") == 0)
12446 mng_info->write_png_compression_level = 7;
12447
12448 else if (LocaleCompare(value,"7") == 0)
12449 mng_info->write_png_compression_level = 8;
12450
12451 else if (LocaleCompare(value,"8") == 0)
12452 mng_info->write_png_compression_level = 9;
12453
12454 else if (LocaleCompare(value,"9") == 0)
12455 mng_info->write_png_compression_level = 10;
12456
12457 else
12458 (void) ThrowMagickException(exception,
12459 GetMagickModule(),CoderWarning,
12460 "ignoring invalid defined png:compression-level",
12461 "=%s",value);
12462 }
12463
12464 value=GetImageOption(image_info,"png:compression-strategy");
12465 if (value == NULL)
12466 value=GetImageArtifact(image,"png:compression-strategy");
12467 if (value != NULL)
12468 {
12469 if (LocaleCompare(value,"0") == 0)
12470 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12471
12472 else if (LocaleCompare(value,"1") == 0)
12473 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12474
12475 else if (LocaleCompare(value,"2") == 0)
12476 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12477
12478 else if (LocaleCompare(value,"3") == 0)
12479 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12480 mng_info->write_png_compression_strategy = Z_RLE+1;
12481 #else
12482 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12483 #endif
12484
12485 else if (LocaleCompare(value,"4") == 0)
12486 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12487 mng_info->write_png_compression_strategy = Z_FIXED+1;
12488 #else
12489 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12490 #endif
12491
12492 else
12493 (void) ThrowMagickException(exception,
12494 GetMagickModule(),CoderWarning,
12495 "ignoring invalid defined png:compression-strategy",
12496 "=%s",value);
12497 }
12498
12499 value=GetImageOption(image_info,"png:compression-filter");
12500 if (value == NULL)
12501 value=GetImageArtifact(image,"png:compression-filter");
12502 if (value != NULL)
12503 {
12504 /* To do: combinations of filters allowed by libpng
12505 * masks 0x08 through 0xf8
12506 *
12507 * Implement this as a comma-separated list of 0,1,2,3,4,5
12508 * where 5 is a special case meaning PNG_ALL_FILTERS.
12509 */
12510
12511 if (LocaleCompare(value,"0") == 0)
12512 mng_info->write_png_compression_filter = 1;
12513
12514 else if (LocaleCompare(value,"1") == 0)
12515 mng_info->write_png_compression_filter = 2;
12516
12517 else if (LocaleCompare(value,"2") == 0)
12518 mng_info->write_png_compression_filter = 3;
12519
12520 else if (LocaleCompare(value,"3") == 0)
12521 mng_info->write_png_compression_filter = 4;
12522
12523 else if (LocaleCompare(value,"4") == 0)
12524 mng_info->write_png_compression_filter = 5;
12525
12526 else if (LocaleCompare(value,"5") == 0)
12527 mng_info->write_png_compression_filter = 6;
12528
12529 else
12530 (void) ThrowMagickException(exception,
12531 GetMagickModule(),CoderWarning,
12532 "ignoring invalid defined png:compression-filter",
12533 "=%s",value);
12534 }
12535
12536 for (source=0; source<8; source++)
12537 {
12538 value = (const char *) NULL;
12539
12540 switch(source)
12541 {
12542 case 0:
12543 value=GetImageOption(image_info,"png:exclude-chunks");
12544 break;
12545 case 1:
12546 value=GetImageArtifact(image,"png:exclude-chunks");
12547 break;
12548 case 2:
12549 value=GetImageOption(image_info,"png:exclude-chunk");
12550 break;
12551 case 3:
12552 value=GetImageArtifact(image,"png:exclude-chunk");
12553 break;
12554 case 4:
12555 value=GetImageOption(image_info,"png:include-chunks");
12556 break;
12557 case 5:
12558 value=GetImageArtifact(image,"png:include-chunks");
12559 break;
12560 case 6:
12561 value=GetImageOption(image_info,"png:include-chunk");
12562 break;
12563 case 7:
12564 value=GetImageArtifact(image,"png:include-chunk");
12565 break;
12566 }
12567
12568 if (value == NULL)
12569 continue;
12570
12571 if (source < 4)
12572 excluding = MagickTrue;
12573 else
12574 excluding = MagickFalse;
12575
12576 if (logging != MagickFalse)
12577 {
12578 if (source == 0 || source == 2)
12579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12580 " png:exclude-chunk=%s found in image options.\n", value);
12581 else if (source == 1 || source == 3)
12582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12583 " png:exclude-chunk=%s found in image artifacts.\n", value);
12584 else if (source == 4 || source == 6)
12585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12586 " png:include-chunk=%s found in image options.\n", value);
12587 else /* if (source == 5 || source == 7) */
12588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12589 " png:include-chunk=%s found in image artifacts.\n", value);
12590 }
12591
12592 if (IsOptionMember("all",value) != MagickFalse)
12593 {
12594 mng_info->ping_exclude_bKGD=excluding;
12595 mng_info->ping_exclude_caNv=excluding;
12596 mng_info->ping_exclude_cHRM=excluding;
12597 mng_info->ping_exclude_date=excluding;
12598 mng_info->ping_exclude_EXIF=excluding;
12599 mng_info->ping_exclude_eXIf=excluding;
12600 mng_info->ping_exclude_gAMA=excluding;
12601 mng_info->ping_exclude_iCCP=excluding;
12602 /* mng_info->ping_exclude_iTXt=excluding; */
12603 mng_info->ping_exclude_oFFs=excluding;
12604 mng_info->ping_exclude_pHYs=excluding;
12605 mng_info->ping_exclude_sRGB=excluding;
12606 mng_info->ping_exclude_tEXt=excluding;
12607 mng_info->ping_exclude_tIME=excluding;
12608 mng_info->ping_exclude_tRNS=excluding;
12609 mng_info->ping_exclude_zCCP=excluding;
12610 mng_info->ping_exclude_zTXt=excluding;
12611 }
12612
12613 if (IsOptionMember("none",value) != MagickFalse)
12614 {
12615 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12616 MagickTrue;
12617 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12618 MagickTrue;
12619 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12620 MagickTrue;
12621 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12622 MagickTrue;
12623 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12624 MagickTrue;
12625 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12626 MagickTrue;
12627 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12628 MagickTrue;
12629 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12630 MagickTrue;
12631 /* mng_info->ping_exclude_iTXt=!excluding; */
12632 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12633 MagickTrue;
12634 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12635 MagickTrue;
12636 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12637 MagickTrue;
12638 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12639 MagickTrue;
12640 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12641 MagickTrue;
12642 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12643 MagickTrue;
12644 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12645 MagickTrue;
12646 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12647 MagickTrue;
12648 }
12649
12650 if (IsOptionMember("bkgd",value) != MagickFalse)
12651 mng_info->ping_exclude_bKGD=excluding;
12652
12653 if (IsOptionMember("caNv",value) != MagickFalse)
12654 mng_info->ping_exclude_caNv=excluding;
12655
12656 if (IsOptionMember("chrm",value) != MagickFalse)
12657 mng_info->ping_exclude_cHRM=excluding;
12658
12659 if (IsOptionMember("date",value) != MagickFalse)
12660 mng_info->ping_exclude_date=excluding;
12661
12662 if (IsOptionMember("exif",value) != MagickFalse)
12663 {
12664 mng_info->ping_exclude_EXIF=excluding;
12665 mng_info->ping_exclude_eXIf=excluding;
12666 }
12667
12668 if (IsOptionMember("gama",value) != MagickFalse)
12669 mng_info->ping_exclude_gAMA=excluding;
12670
12671 if (IsOptionMember("iccp",value) != MagickFalse)
12672 mng_info->ping_exclude_iCCP=excluding;
12673
12674 #if 0
12675 if (IsOptionMember("itxt",value) != MagickFalse)
12676 mng_info->ping_exclude_iTXt=excluding;
12677 #endif
12678
12679 if (IsOptionMember("offs",value) != MagickFalse)
12680 mng_info->ping_exclude_oFFs=excluding;
12681
12682 if (IsOptionMember("phys",value) != MagickFalse)
12683 mng_info->ping_exclude_pHYs=excluding;
12684
12685 if (IsOptionMember("srgb",value) != MagickFalse)
12686 mng_info->ping_exclude_sRGB=excluding;
12687
12688 if (IsOptionMember("text",value) != MagickFalse)
12689 mng_info->ping_exclude_tEXt=excluding;
12690
12691 if (IsOptionMember("time",value) != MagickFalse)
12692 mng_info->ping_exclude_tIME=excluding;
12693
12694 if (IsOptionMember("trns",value) != MagickFalse)
12695 mng_info->ping_exclude_tRNS=excluding;
12696
12697 if (IsOptionMember("zccp",value) != MagickFalse)
12698 mng_info->ping_exclude_zCCP=excluding;
12699
12700 if (IsOptionMember("ztxt",value) != MagickFalse)
12701 mng_info->ping_exclude_zTXt=excluding;
12702 }
12703
12704 if (logging != MagickFalse)
12705 {
12706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707 " Chunks to be excluded from the output png:");
12708 if (mng_info->ping_exclude_bKGD != MagickFalse)
12709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710 " bKGD");
12711 if (mng_info->ping_exclude_caNv != MagickFalse)
12712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12713 " caNv");
12714 if (mng_info->ping_exclude_cHRM != MagickFalse)
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 " cHRM");
12717 if (mng_info->ping_exclude_date != MagickFalse)
12718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719 " date");
12720 if (mng_info->ping_exclude_EXIF != MagickFalse)
12721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12722 " EXIF");
12723 if (mng_info->ping_exclude_eXIf != MagickFalse)
12724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12725 " eXIf");
12726 if (mng_info->ping_exclude_gAMA != MagickFalse)
12727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728 " gAMA");
12729 if (mng_info->ping_exclude_iCCP != MagickFalse)
12730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12731 " iCCP");
12732 #if 0
12733 if (mng_info->ping_exclude_iTXt != MagickFalse)
12734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12735 " iTXt");
12736 #endif
12737
12738 if (mng_info->ping_exclude_oFFs != MagickFalse)
12739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12740 " oFFs");
12741 if (mng_info->ping_exclude_pHYs != MagickFalse)
12742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12743 " pHYs");
12744 if (mng_info->ping_exclude_sRGB != MagickFalse)
12745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12746 " sRGB");
12747 if (mng_info->ping_exclude_tEXt != MagickFalse)
12748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12749 " tEXt");
12750 if (mng_info->ping_exclude_tIME != MagickFalse)
12751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12752 " tIME");
12753 if (mng_info->ping_exclude_tRNS != MagickFalse)
12754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12755 " tRNS");
12756 if (mng_info->ping_exclude_zCCP != MagickFalse)
12757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12758 " zCCP");
12759 if (mng_info->ping_exclude_zTXt != MagickFalse)
12760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12761 " zTXt");
12762 }
12763
12764 mng_info->need_blob = MagickTrue;
12765
12766 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12767
12768 mng_info=MngInfoFreeStruct(mng_info);
12769
12770 if (logging != MagickFalse)
12771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12772
12773 return(status);
12774 }
12775
12776 #if defined(JNG_SUPPORTED)
12777
12778 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12779 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12780 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12781 {
12782 Image
12783 *jpeg_image;
12784
12785 ImageInfo
12786 *jpeg_image_info;
12787
12788 MagickBooleanType
12789 logging,
12790 status;
12791
12792 size_t
12793 length;
12794
12795 unsigned char
12796 *blob,
12797 chunk[80],
12798 *p;
12799
12800 unsigned int
12801 jng_alpha_compression_method,
12802 jng_alpha_sample_depth,
12803 jng_color_type,
12804 transparent;
12805
12806 size_t
12807 jng_alpha_quality,
12808 jng_quality;
12809
12810 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12811 " Enter WriteOneJNGImage()");
12812
12813 if ((image->columns > 65500U) || (image->rows > 65500U))
12814 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12815
12816 blob=(unsigned char *) NULL;
12817 jpeg_image=(Image *) NULL;
12818 jpeg_image_info=(ImageInfo *) NULL;
12819 length=0;
12820
12821 status=MagickTrue;
12822 transparent=image_info->type==GrayscaleAlphaType ||
12823 image_info->type==TrueColorAlphaType ||
12824 image->alpha_trait != UndefinedPixelTrait;
12825
12826 jng_alpha_sample_depth = 0;
12827
12828 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12829
12830 jng_alpha_compression_method=(image->compression==JPEGCompression ||
12831 image_info->compression==JPEGCompression) ? 8 : 0;
12832
12833 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12834 image_info->quality;
12835
12836 if (jng_alpha_quality >= 1000)
12837 jng_alpha_quality /= 1000;
12838
12839 length=0;
12840
12841 if (transparent != 0)
12842 {
12843 jng_color_type=14;
12844
12845 /* Create JPEG blob, image, and image_info */
12846 if (logging != MagickFalse)
12847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12848 " Creating jpeg_image_info for alpha.");
12849
12850 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12851
12852 if (jpeg_image_info == (ImageInfo *) NULL)
12853 {
12854 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12855 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12856 }
12857
12858 if (logging != MagickFalse)
12859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12860 " Creating jpeg_image.");
12861
12862 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12863 if (jpeg_image == (Image *) NULL)
12864 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12865 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12866 jpeg_image->alpha_trait=UndefinedPixelTrait;
12867 jpeg_image->quality=jng_alpha_quality;
12868 jpeg_image_info->type=GrayscaleType;
12869 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12870 (void) AcquireUniqueFilename(jpeg_image->filename);
12871 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12872 "%s",jpeg_image->filename);
12873 }
12874 else
12875 {
12876 jng_alpha_compression_method=0;
12877 jng_color_type=10;
12878 jng_alpha_sample_depth=0;
12879 }
12880
12881 /* To do: check bit depth of PNG alpha channel */
12882
12883 /* Check if image is grayscale. */
12884 if (image_info->type != TrueColorAlphaType && image_info->type !=
12885 TrueColorType && SetImageGray(image,exception))
12886 jng_color_type-=2;
12887
12888 if (logging != MagickFalse)
12889 {
12890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12891 " JNG Quality = %d",(int) jng_quality);
12892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12893 " JNG Color Type = %d",jng_color_type);
12894 if (transparent != 0)
12895 {
12896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12897 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12899 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12901 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12902 }
12903 }
12904
12905 if (transparent != 0)
12906 {
12907 if (jng_alpha_compression_method==0)
12908 {
12909 const char
12910 *value;
12911
12912 /* Encode alpha as a grayscale PNG blob */
12913 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12914 exception);
12915 if (status == MagickFalse)
12916 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12917
12918 if (logging != MagickFalse)
12919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12920 " Creating PNG blob.");
12921
12922 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12923 MagickPathExtent);
12924 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12925 jpeg_image_info->interlace=NoInterlace;
12926
12927 /* Exclude all ancillary chunks */
12928 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12929
12930 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12931 &length,exception);
12932
12933 /* Retrieve sample depth used */
12934 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12935 if (value != (char *) NULL)
12936 jng_alpha_sample_depth= (unsigned int) value[0];
12937 }
12938 else
12939 {
12940 /* Encode alpha as a grayscale JPEG blob */
12941
12942 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12943 exception);
12944 if (status == MagickFalse)
12945 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12946
12947 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12948 MagickPathExtent);
12949 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12950 jpeg_image_info->interlace=NoInterlace;
12951 if (logging != MagickFalse)
12952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12953 " Creating blob.");
12954 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12955 &length,exception);
12956 if (blob == (unsigned char *) NULL)
12957 {
12958 if (jpeg_image != (Image *)NULL)
12959 jpeg_image=DestroyImage(jpeg_image);
12960 if (jpeg_image_info != (ImageInfo *)NULL)
12961 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12962 return(MagickFalse);
12963 }
12964 jng_alpha_sample_depth=8;
12965
12966 if (logging != MagickFalse)
12967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968 " Successfully read jpeg_image into a blob, length=%.20g.",
12969 (double) length);
12970
12971 }
12972 /* Destroy JPEG image and image_info */
12973 jpeg_image=DestroyImage(jpeg_image);
12974 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12975 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12976 }
12977
12978 /* Write JHDR chunk */
12979 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12980 PNGType(chunk,mng_JHDR);
12981 LogPNGChunk(logging,mng_JHDR,16L);
12982 PNGLong(chunk+4,(png_uint_32) image->columns);
12983 PNGLong(chunk+8,(png_uint_32) image->rows);
12984 chunk[12]=jng_color_type;
12985 chunk[13]=8; /* sample depth */
12986 chunk[14]=8; /*jng_image_compression_method */
12987 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12988 chunk[16]=jng_alpha_sample_depth;
12989 chunk[17]=jng_alpha_compression_method;
12990 chunk[18]=0; /*jng_alpha_filter_method */
12991 chunk[19]=0; /*jng_alpha_interlace_method */
12992 (void) WriteBlob(image,20,chunk);
12993 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12994 if (logging != MagickFalse)
12995 {
12996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12997 " JNG width:%15lu",(unsigned long) image->columns);
12998
12999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13000 " JNG height:%14lu",(unsigned long) image->rows);
13001
13002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13003 " JNG color type:%10d",jng_color_type);
13004
13005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13006 " JNG sample depth:%8d",8);
13007
13008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13009 " JNG compression:%9d",8);
13010
13011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13012 " JNG interlace:%11d",0);
13013
13014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13015 " JNG alpha depth:%9d",jng_alpha_sample_depth);
13016
13017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13018 " JNG alpha compression:%3d",jng_alpha_compression_method);
13019
13020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13021 " JNG alpha filter:%8d",0);
13022
13023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13024 " JNG alpha interlace:%5d",0);
13025 }
13026
13027 /*
13028 Write leading ancillary chunks
13029 */
13030
13031 if (transparent != 0)
13032 {
13033 /*
13034 Write JNG bKGD chunk
13035 */
13036
13037 unsigned char
13038 blue,
13039 green,
13040 red;
13041
13042 ssize_t
13043 num_bytes;
13044
13045 if (jng_color_type == 8 || jng_color_type == 12)
13046 num_bytes=6L;
13047 else
13048 num_bytes=10L;
13049 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13050 PNGType(chunk,mng_bKGD);
13051 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13052 red=ScaleQuantumToChar(image->background_color.red);
13053 green=ScaleQuantumToChar(image->background_color.green);
13054 blue=ScaleQuantumToChar(image->background_color.blue);
13055 *(chunk+4)=0;
13056 *(chunk+5)=red;
13057 *(chunk+6)=0;
13058 *(chunk+7)=green;
13059 *(chunk+8)=0;
13060 *(chunk+9)=blue;
13061 (void) WriteBlob(image,(size_t) num_bytes,chunk);
13062 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13063 }
13064
13065 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13066 {
13067 /*
13068 Write JNG sRGB chunk
13069 */
13070 (void) WriteBlobMSBULong(image,1L);
13071 PNGType(chunk,mng_sRGB);
13072 LogPNGChunk(logging,mng_sRGB,1L);
13073
13074 if (image->rendering_intent != UndefinedIntent)
13075 chunk[4]=(unsigned char)
13076 Magick_RenderingIntent_to_PNG_RenderingIntent(
13077 (image->rendering_intent));
13078
13079 else
13080 chunk[4]=(unsigned char)
13081 Magick_RenderingIntent_to_PNG_RenderingIntent(
13082 (PerceptualIntent));
13083
13084 (void) WriteBlob(image,5,chunk);
13085 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13086 }
13087 else
13088 {
13089 if (image->gamma != 0.0)
13090 {
13091 /*
13092 Write JNG gAMA chunk
13093 */
13094 (void) WriteBlobMSBULong(image,4L);
13095 PNGType(chunk,mng_gAMA);
13096 LogPNGChunk(logging,mng_gAMA,4L);
13097 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13098 (void) WriteBlob(image,8,chunk);
13099 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13100 }
13101
13102 if ((mng_info->equal_chrms == MagickFalse) &&
13103 (image->chromaticity.red_primary.x != 0.0))
13104 {
13105 PrimaryInfo
13106 primary;
13107
13108 /*
13109 Write JNG cHRM chunk
13110 */
13111 (void) WriteBlobMSBULong(image,32L);
13112 PNGType(chunk,mng_cHRM);
13113 LogPNGChunk(logging,mng_cHRM,32L);
13114 primary=image->chromaticity.white_point;
13115 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13116 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13117 primary=image->chromaticity.red_primary;
13118 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13119 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13120 primary=image->chromaticity.green_primary;
13121 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13122 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13123 primary=image->chromaticity.blue_primary;
13124 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13125 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13126 (void) WriteBlob(image,36,chunk);
13127 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13128 }
13129 }
13130
13131 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13132 {
13133 /*
13134 Write JNG pHYs chunk
13135 */
13136 (void) WriteBlobMSBULong(image,9L);
13137 PNGType(chunk,mng_pHYs);
13138 LogPNGChunk(logging,mng_pHYs,9L);
13139 if (image->units == PixelsPerInchResolution)
13140 {
13141 PNGLong(chunk+4,(png_uint_32)
13142 (image->resolution.x*100.0/2.54+0.5));
13143
13144 PNGLong(chunk+8,(png_uint_32)
13145 (image->resolution.y*100.0/2.54+0.5));
13146
13147 chunk[12]=1;
13148 }
13149
13150 else
13151 {
13152 if (image->units == PixelsPerCentimeterResolution)
13153 {
13154 PNGLong(chunk+4,(png_uint_32)
13155 (image->resolution.x*100.0+0.5));
13156
13157 PNGLong(chunk+8,(png_uint_32)
13158 (image->resolution.y*100.0+0.5));
13159
13160 chunk[12]=1;
13161 }
13162
13163 else
13164 {
13165 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13166 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13167 chunk[12]=0;
13168 }
13169 }
13170 (void) WriteBlob(image,13,chunk);
13171 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13172 }
13173
13174 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13175 {
13176 /*
13177 Write JNG oFFs chunk
13178 */
13179 (void) WriteBlobMSBULong(image,9L);
13180 PNGType(chunk,mng_oFFs);
13181 LogPNGChunk(logging,mng_oFFs,9L);
13182 PNGsLong(chunk+4,(ssize_t) (image->page.x));
13183 PNGsLong(chunk+8,(ssize_t) (image->page.y));
13184 chunk[12]=0;
13185 (void) WriteBlob(image,13,chunk);
13186 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13187 }
13188
13189 if (transparent != 0)
13190 {
13191 if (jng_alpha_compression_method==0)
13192 {
13193 ssize_t
13194 i;
13195
13196 size_t
13197 len;
13198
13199 /* Write IDAT chunk header */
13200 if (logging != MagickFalse)
13201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13202 " Write IDAT chunks from blob, length=%.20g.",(double)
13203 length);
13204
13205 /* Copy IDAT chunks */
13206 len=0;
13207 p=blob+8;
13208 for (i=8; i<(ssize_t) length; i+=len+12)
13209 {
13210 len=(((unsigned int) *(p ) & 0xff) << 24) +
13211 (((unsigned int) *(p + 1) & 0xff) << 16) +
13212 (((unsigned int) *(p + 2) & 0xff) << 8) +
13213 (((unsigned int) *(p + 3) & 0xff) ) ;
13214 p+=4;
13215
13216 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13217 {
13218 /* Found an IDAT chunk. */
13219 (void) WriteBlobMSBULong(image,len);
13220 LogPNGChunk(logging,mng_IDAT,len);
13221 (void) WriteBlob(image,len+4,p);
13222 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13223 }
13224
13225 else
13226 {
13227 if (logging != MagickFalse)
13228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229 " Skipping %c%c%c%c chunk, length=%.20g.",
13230 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13231 }
13232 p+=(8+len);
13233 }
13234 }
13235 else if (length != 0)
13236 {
13237 /* Write JDAA chunk header */
13238 if (logging != MagickFalse)
13239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13240 " Write JDAA chunk, length=%.20g.",(double) length);
13241 (void) WriteBlobMSBULong(image,(size_t) length);
13242 PNGType(chunk,mng_JDAA);
13243 LogPNGChunk(logging,mng_JDAA,length);
13244 /* Write JDAT chunk(s) data */
13245 (void) WriteBlob(image,4,chunk);
13246 (void) WriteBlob(image,length,blob);
13247 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13248 (uInt) length));
13249 }
13250 blob=(unsigned char *) RelinquishMagickMemory(blob);
13251 }
13252
13253 /* Encode image as a JPEG blob */
13254 if (logging != MagickFalse)
13255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13256 " Creating jpeg_image_info.");
13257 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13258 if (jpeg_image_info == (ImageInfo *) NULL)
13259 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13260
13261 if (logging != MagickFalse)
13262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13263 " Creating jpeg_image.");
13264
13265 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13266 if (jpeg_image == (Image *) NULL)
13267 {
13268 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13269 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13270 }
13271 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13272
13273 (void) AcquireUniqueFilename(jpeg_image->filename);
13274 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13275 jpeg_image->filename);
13276
13277 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13278 exception);
13279
13280 if (logging != MagickFalse)
13281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13282 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13283 (double) jpeg_image->rows);
13284
13285 if (status == MagickFalse)
13286 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13287
13288 if (jng_color_type == 8 || jng_color_type == 12)
13289 jpeg_image_info->type=GrayscaleType;
13290
13291 jpeg_image_info->quality=jng_quality;
13292 jpeg_image->quality=jng_quality;
13293 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13294 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13295
13296 if (logging != MagickFalse)
13297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13298 " Creating blob.");
13299
13300 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13301 exception);
13302
13303 if (logging != MagickFalse)
13304 {
13305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306 " Successfully read jpeg_image into a blob, length=%.20g.",
13307 (double) length);
13308
13309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13310 " Write JDAT chunk, length=%.20g.",(double) length);
13311 }
13312
13313 /* Write JDAT chunk(s) */
13314 (void) WriteBlobMSBULong(image,(size_t) length);
13315 PNGType(chunk,mng_JDAT);
13316 LogPNGChunk(logging,mng_JDAT,length);
13317 (void) WriteBlob(image,4,chunk);
13318 (void) WriteBlob(image,length,blob);
13319 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13320
13321 jpeg_image=DestroyImage(jpeg_image);
13322 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13323 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13324 blob=(unsigned char *) RelinquishMagickMemory(blob);
13325
13326 /* Write IEND chunk */
13327 (void) WriteBlobMSBULong(image,0L);
13328 PNGType(chunk,mng_IEND);
13329 LogPNGChunk(logging,mng_IEND,0);
13330 (void) WriteBlob(image,4,chunk);
13331 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13332
13333 if (logging != MagickFalse)
13334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13335 " exit WriteOneJNGImage()");
13336
13337 return(status);
13338 }
13339
13340 /*
13341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13342 % %
13343 % %
13344 % %
13345 % W r i t e J N G I m a g e %
13346 % %
13347 % %
13348 % %
13349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13350 %
13351 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13352 %
13353 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13354 %
13355 % The format of the WriteJNGImage method is:
13356 %
13357 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13358 % Image *image,ExceptionInfo *exception)
13359 %
13360 % A description of each parameter follows:
13361 %
13362 % o image_info: the image info.
13363 %
13364 % o image: The image.
13365 %
13366 % o exception: return any errors or warnings in this structure.
13367 %
13368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13369 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13370 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13371 Image *image, ExceptionInfo *exception)
13372 {
13373 MagickBooleanType
13374 logging,
13375 status;
13376
13377 MngInfo
13378 *mng_info;
13379
13380 /*
13381 Open image file.
13382 */
13383 assert(image_info != (const ImageInfo *) NULL);
13384 assert(image_info->signature == MagickCoreSignature);
13385 assert(image != (Image *) NULL);
13386 assert(image->signature == MagickCoreSignature);
13387 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13388 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13389 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13390 if (status == MagickFalse)
13391 return(status);
13392 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13393 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13394
13395 /*
13396 Allocate a MngInfo structure.
13397 */
13398 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13399 if (mng_info == (MngInfo *) NULL)
13400 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13401 /*
13402 Initialize members of the MngInfo structure.
13403 */
13404 (void) memset(mng_info,0,sizeof(MngInfo));
13405 mng_info->image=image;
13406
13407 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13408
13409 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13410 mng_info=MngInfoFreeStruct(mng_info);
13411 (void) CloseBlob(image);
13412
13413 (void) CatchImageException(image);
13414 if (logging != MagickFalse)
13415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13416 return(status);
13417 }
13418 #endif
13419
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13420 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13421 Image *image, ExceptionInfo *exception)
13422 {
13423 Image
13424 *next_image;
13425
13426 MagickBooleanType
13427 status;
13428
13429 volatile MagickBooleanType
13430 logging;
13431
13432 MngInfo
13433 *mng_info;
13434
13435 int
13436 image_count,
13437 need_iterations,
13438 need_matte;
13439
13440 volatile int
13441 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13442 defined(PNG_MNG_FEATURES_SUPPORTED)
13443 need_local_plte,
13444 #endif
13445 all_images_are_gray,
13446 need_defi,
13447 use_global_plte;
13448
13449 ssize_t
13450 i;
13451
13452 unsigned char
13453 chunk[800];
13454
13455 volatile unsigned int
13456 write_jng,
13457 write_mng;
13458
13459 volatile size_t
13460 scene;
13461
13462 size_t
13463 final_delay=0,
13464 imageListLength,
13465 initial_delay;
13466
13467 #if (PNG_LIBPNG_VER < 10200)
13468 if (image_info->verbose)
13469 printf("Your PNG library (libpng-%s) is rather old.\n",
13470 PNG_LIBPNG_VER_STRING);
13471 #endif
13472
13473 /*
13474 Open image file.
13475 */
13476 assert(image_info != (const ImageInfo *) NULL);
13477 assert(image_info->signature == MagickCoreSignature);
13478 assert(image != (Image *) NULL);
13479 assert(image->signature == MagickCoreSignature);
13480 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13481 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13482 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13483 if (status == MagickFalse)
13484 return(status);
13485
13486 /*
13487 Allocate a MngInfo structure.
13488 */
13489 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13490 if (mng_info == (MngInfo *) NULL)
13491 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13492 /*
13493 Initialize members of the MngInfo structure.
13494 */
13495 (void) memset(mng_info,0,sizeof(MngInfo));
13496 mng_info->image=image;
13497 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13498
13499 /*
13500 * See if user has requested a specific PNG subformat to be used
13501 * for all of the PNGs in the MNG being written, e.g.,
13502 *
13503 * convert *.png png8:animation.mng
13504 *
13505 * To do: check -define png:bit_depth and png:color_type as well,
13506 * or perhaps use mng:bit_depth and mng:color_type instead for
13507 * global settings.
13508 */
13509
13510 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13511 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13512 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13513
13514 write_jng=MagickFalse;
13515 if (image_info->compression == JPEGCompression)
13516 write_jng=MagickTrue;
13517
13518 mng_info->adjoin=image_info->adjoin &&
13519 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13520
13521 if (logging != MagickFalse)
13522 {
13523 /* Log some info about the input */
13524 Image
13525 *p;
13526
13527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13528 " Checking input image(s)\n"
13529 " Image_info depth: %.20g, Type: %d",
13530 (double) image_info->depth, image_info->type);
13531
13532 scene=0;
13533 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13534 {
13535
13536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13537 " Scene: %.20g\n, Image depth: %.20g",
13538 (double) scene++, (double) p->depth);
13539
13540 if (p->alpha_trait != UndefinedPixelTrait)
13541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13542 " Matte: True");
13543
13544 else
13545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13546 " Matte: False");
13547
13548 if (p->storage_class == PseudoClass)
13549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13550 " Storage class: PseudoClass");
13551
13552 else
13553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13554 " Storage class: DirectClass");
13555
13556 if (p->colors)
13557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13558 " Number of colors: %.20g",(double) p->colors);
13559
13560 else
13561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13562 " Number of colors: unspecified");
13563
13564 if (mng_info->adjoin == MagickFalse)
13565 break;
13566 }
13567 }
13568
13569 use_global_plte=MagickFalse;
13570 all_images_are_gray=MagickFalse;
13571 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13572 need_local_plte=MagickTrue;
13573 #endif
13574 need_defi=MagickFalse;
13575 need_matte=MagickFalse;
13576 mng_info->framing_mode=1;
13577 mng_info->old_framing_mode=1;
13578
13579 if (write_mng)
13580 if (image_info->page != (char *) NULL)
13581 {
13582 /*
13583 Determine image bounding box.
13584 */
13585 SetGeometry(image,&mng_info->page);
13586 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13587 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13588 }
13589 if (write_mng)
13590 {
13591 unsigned int
13592 need_geom;
13593
13594 unsigned short
13595 red,
13596 green,
13597 blue;
13598
13599 const char *
13600 option;
13601
13602 mng_info->page=image->page;
13603 need_geom=MagickTrue;
13604 if (mng_info->page.width || mng_info->page.height)
13605 need_geom=MagickFalse;
13606 /*
13607 Check all the scenes.
13608 */
13609 initial_delay=image->delay;
13610 need_iterations=MagickFalse;
13611 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13612 mng_info->equal_physs=MagickTrue,
13613 mng_info->equal_gammas=MagickTrue;
13614 mng_info->equal_srgbs=MagickTrue;
13615 mng_info->equal_backgrounds=MagickTrue;
13616 image_count=0;
13617 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13618 defined(PNG_MNG_FEATURES_SUPPORTED)
13619 all_images_are_gray=MagickTrue;
13620 mng_info->equal_palettes=MagickFalse;
13621 need_local_plte=MagickFalse;
13622 #endif
13623 for (next_image=image; next_image != (Image *) NULL; )
13624 {
13625 if (need_geom)
13626 {
13627 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13628 mng_info->page.width=next_image->columns+next_image->page.x;
13629
13630 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13631 mng_info->page.height=next_image->rows+next_image->page.y;
13632 }
13633
13634 if (next_image->page.x || next_image->page.y)
13635 need_defi=MagickTrue;
13636
13637 if (next_image->alpha_trait != UndefinedPixelTrait)
13638 need_matte=MagickTrue;
13639
13640 if ((int) next_image->dispose >= BackgroundDispose)
13641 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13642 next_image->page.x || next_image->page.y ||
13643 ((next_image->columns < mng_info->page.width) &&
13644 (next_image->rows < mng_info->page.height)))
13645 mng_info->need_fram=MagickTrue;
13646
13647 if (next_image->iterations)
13648 need_iterations=MagickTrue;
13649
13650 final_delay=next_image->delay;
13651
13652 if (final_delay != initial_delay || final_delay > 1UL*
13653 next_image->ticks_per_second)
13654 mng_info->need_fram=1;
13655
13656 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13657 defined(PNG_MNG_FEATURES_SUPPORTED)
13658 /*
13659 check for global palette possibility.
13660 */
13661 if (image->alpha_trait != UndefinedPixelTrait)
13662 need_local_plte=MagickTrue;
13663
13664 if (need_local_plte == 0)
13665 {
13666 if (SetImageGray(image,exception) == MagickFalse)
13667 all_images_are_gray=MagickFalse;
13668 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13669 if (use_global_plte == 0)
13670 use_global_plte=mng_info->equal_palettes;
13671 need_local_plte=!mng_info->equal_palettes;
13672 }
13673 #endif
13674 if (GetNextImageInList(next_image) != (Image *) NULL)
13675 {
13676 if (next_image->background_color.red !=
13677 next_image->next->background_color.red ||
13678 next_image->background_color.green !=
13679 next_image->next->background_color.green ||
13680 next_image->background_color.blue !=
13681 next_image->next->background_color.blue)
13682 mng_info->equal_backgrounds=MagickFalse;
13683
13684 if (next_image->gamma != next_image->next->gamma)
13685 mng_info->equal_gammas=MagickFalse;
13686
13687 if (next_image->rendering_intent !=
13688 next_image->next->rendering_intent)
13689 mng_info->equal_srgbs=MagickFalse;
13690
13691 if ((next_image->units != next_image->next->units) ||
13692 (next_image->resolution.x != next_image->next->resolution.x) ||
13693 (next_image->resolution.y != next_image->next->resolution.y))
13694 mng_info->equal_physs=MagickFalse;
13695
13696 if (mng_info->equal_chrms)
13697 {
13698 if (next_image->chromaticity.red_primary.x !=
13699 next_image->next->chromaticity.red_primary.x ||
13700 next_image->chromaticity.red_primary.y !=
13701 next_image->next->chromaticity.red_primary.y ||
13702 next_image->chromaticity.green_primary.x !=
13703 next_image->next->chromaticity.green_primary.x ||
13704 next_image->chromaticity.green_primary.y !=
13705 next_image->next->chromaticity.green_primary.y ||
13706 next_image->chromaticity.blue_primary.x !=
13707 next_image->next->chromaticity.blue_primary.x ||
13708 next_image->chromaticity.blue_primary.y !=
13709 next_image->next->chromaticity.blue_primary.y ||
13710 next_image->chromaticity.white_point.x !=
13711 next_image->next->chromaticity.white_point.x ||
13712 next_image->chromaticity.white_point.y !=
13713 next_image->next->chromaticity.white_point.y)
13714 mng_info->equal_chrms=MagickFalse;
13715 }
13716 }
13717 image_count++;
13718 next_image=GetNextImageInList(next_image);
13719 }
13720 if (image_count < 2)
13721 {
13722 mng_info->equal_backgrounds=MagickFalse;
13723 mng_info->equal_chrms=MagickFalse;
13724 mng_info->equal_gammas=MagickFalse;
13725 mng_info->equal_srgbs=MagickFalse;
13726 mng_info->equal_physs=MagickFalse;
13727 use_global_plte=MagickFalse;
13728 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13729 need_local_plte=MagickTrue;
13730 #endif
13731 need_iterations=MagickFalse;
13732 }
13733
13734 if (mng_info->need_fram == MagickFalse)
13735 {
13736 /*
13737 Only certain framing rates 100/n are exactly representable without
13738 the FRAM chunk but we'll allow some slop in VLC files
13739 */
13740 if (final_delay == 0)
13741 {
13742 if (need_iterations != MagickFalse)
13743 {
13744 /*
13745 It's probably a GIF with loop; don't run it *too* fast.
13746 */
13747 if (mng_info->adjoin)
13748 {
13749 final_delay=10;
13750 (void) ThrowMagickException(exception,GetMagickModule(),
13751 CoderWarning,
13752 "input has zero delay between all frames; assuming",
13753 " 10 cs `%s'","");
13754 }
13755 }
13756 else
13757 mng_info->ticks_per_second=0;
13758 }
13759 if (final_delay != 0)
13760 mng_info->ticks_per_second=(png_uint_32)
13761 (image->ticks_per_second/final_delay);
13762 if (final_delay > 50)
13763 mng_info->ticks_per_second=2;
13764
13765 if (final_delay > 75)
13766 mng_info->ticks_per_second=1;
13767
13768 if (final_delay > 125)
13769 mng_info->need_fram=MagickTrue;
13770
13771 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13772 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13773 (final_delay != 25) && (final_delay != 50) &&
13774 (final_delay != (size_t) image->ticks_per_second))
13775 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13776 }
13777
13778 if (mng_info->need_fram != MagickFalse)
13779 mng_info->ticks_per_second=image->ticks_per_second;
13780 /*
13781 If pseudocolor, we should also check to see if all the
13782 palettes are identical and write a global PLTE if they are.
13783 ../glennrp Feb 99.
13784 */
13785 /*
13786 Write the MNG version 1.0 signature and MHDR chunk.
13787 */
13788 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13789 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13790 PNGType(chunk,mng_MHDR);
13791 LogPNGChunk(logging,mng_MHDR,28L);
13792 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13793 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13794 PNGLong(chunk+12,mng_info->ticks_per_second);
13795 PNGLong(chunk+16,0L); /* layer count=unknown */
13796 PNGLong(chunk+20,0L); /* frame count=unknown */
13797 PNGLong(chunk+24,0L); /* play time=unknown */
13798 if (write_jng)
13799 {
13800 if (need_matte)
13801 {
13802 if (need_defi || mng_info->need_fram || use_global_plte)
13803 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13804
13805 else
13806 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13807 }
13808
13809 else
13810 {
13811 if (need_defi || mng_info->need_fram || use_global_plte)
13812 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13813
13814 else
13815 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13816 }
13817 }
13818
13819 else
13820 {
13821 if (need_matte)
13822 {
13823 if (need_defi || mng_info->need_fram || use_global_plte)
13824 PNGLong(chunk+28,11L); /* simplicity=LC */
13825
13826 else
13827 PNGLong(chunk+28,9L); /* simplicity=VLC */
13828 }
13829
13830 else
13831 {
13832 if (need_defi || mng_info->need_fram || use_global_plte)
13833 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13834
13835 else
13836 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13837 }
13838 }
13839 (void) WriteBlob(image,32,chunk);
13840 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13841 option=GetImageOption(image_info,"mng:need-cacheoff");
13842 if (option != (const char *) NULL)
13843 {
13844 size_t
13845 length;
13846 /*
13847 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13848 */
13849 PNGType(chunk,mng_nEED);
13850 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13851 (void) WriteBlobMSBULong(image,(size_t) length);
13852 LogPNGChunk(logging,mng_nEED,(size_t) length);
13853 length+=4;
13854 (void) WriteBlob(image,length,chunk);
13855 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13856 }
13857 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13858 (GetNextImageInList(image) != (Image *) NULL) &&
13859 (image->iterations != 1))
13860 {
13861 /*
13862 Write MNG TERM chunk
13863 */
13864 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13865 PNGType(chunk,mng_TERM);
13866 LogPNGChunk(logging,mng_TERM,10L);
13867 chunk[4]=3; /* repeat animation */
13868 chunk[5]=0; /* show last frame when done */
13869 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13870 final_delay/MagickMax(image->ticks_per_second,1)));
13871
13872 if (image->iterations == 0)
13873 PNGLong(chunk+10,PNG_UINT_31_MAX);
13874
13875 else
13876 PNGLong(chunk+10,(png_uint_32) image->iterations);
13877
13878 if (logging != MagickFalse)
13879 {
13880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13881 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13882 final_delay/MagickMax(image->ticks_per_second,1)));
13883
13884 if (image->iterations == 0)
13885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13886 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13887
13888 else
13889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13890 " Image iterations: %.20g",(double) image->iterations);
13891 }
13892 (void) WriteBlob(image,14,chunk);
13893 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13894 }
13895 /*
13896 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13897 */
13898 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13899 mng_info->equal_srgbs)
13900 {
13901 /*
13902 Write MNG sRGB chunk
13903 */
13904 (void) WriteBlobMSBULong(image,1L);
13905 PNGType(chunk,mng_sRGB);
13906 LogPNGChunk(logging,mng_sRGB,1L);
13907
13908 if (image->rendering_intent != UndefinedIntent)
13909 chunk[4]=(unsigned char)
13910 Magick_RenderingIntent_to_PNG_RenderingIntent(
13911 (image->rendering_intent));
13912
13913 else
13914 chunk[4]=(unsigned char)
13915 Magick_RenderingIntent_to_PNG_RenderingIntent(
13916 (PerceptualIntent));
13917
13918 (void) WriteBlob(image,5,chunk);
13919 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13920 mng_info->have_write_global_srgb=MagickTrue;
13921 }
13922
13923 else
13924 {
13925 if (image->gamma && mng_info->equal_gammas)
13926 {
13927 /*
13928 Write MNG gAMA chunk
13929 */
13930 (void) WriteBlobMSBULong(image,4L);
13931 PNGType(chunk,mng_gAMA);
13932 LogPNGChunk(logging,mng_gAMA,4L);
13933 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13934 (void) WriteBlob(image,8,chunk);
13935 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13936 mng_info->have_write_global_gama=MagickTrue;
13937 }
13938 if (mng_info->equal_chrms)
13939 {
13940 PrimaryInfo
13941 primary;
13942
13943 /*
13944 Write MNG cHRM chunk
13945 */
13946 (void) WriteBlobMSBULong(image,32L);
13947 PNGType(chunk,mng_cHRM);
13948 LogPNGChunk(logging,mng_cHRM,32L);
13949 primary=image->chromaticity.white_point;
13950 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13951 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13952 primary=image->chromaticity.red_primary;
13953 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13954 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13955 primary=image->chromaticity.green_primary;
13956 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13957 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13958 primary=image->chromaticity.blue_primary;
13959 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13960 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13961 (void) WriteBlob(image,36,chunk);
13962 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13963 mng_info->have_write_global_chrm=MagickTrue;
13964 }
13965 }
13966 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13967 {
13968 /*
13969 Write MNG pHYs chunk
13970 */
13971 (void) WriteBlobMSBULong(image,9L);
13972 PNGType(chunk,mng_pHYs);
13973 LogPNGChunk(logging,mng_pHYs,9L);
13974
13975 if (image->units == PixelsPerInchResolution)
13976 {
13977 PNGLong(chunk+4,(png_uint_32)
13978 (image->resolution.x*100.0/2.54+0.5));
13979
13980 PNGLong(chunk+8,(png_uint_32)
13981 (image->resolution.y*100.0/2.54+0.5));
13982
13983 chunk[12]=1;
13984 }
13985
13986 else
13987 {
13988 if (image->units == PixelsPerCentimeterResolution)
13989 {
13990 PNGLong(chunk+4,(png_uint_32)
13991 (image->resolution.x*100.0+0.5));
13992
13993 PNGLong(chunk+8,(png_uint_32)
13994 (image->resolution.y*100.0+0.5));
13995
13996 chunk[12]=1;
13997 }
13998
13999 else
14000 {
14001 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
14002 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
14003 chunk[12]=0;
14004 }
14005 }
14006 (void) WriteBlob(image,13,chunk);
14007 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
14008 }
14009 /*
14010 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
14011 or does not cover the entire frame.
14012 */
14013 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
14014 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14015 (image->page.width+image->page.x < mng_info->page.width))
14016 || (image->page.height && (image->page.height+image->page.y
14017 < mng_info->page.height))))
14018 {
14019 (void) WriteBlobMSBULong(image,6L);
14020 PNGType(chunk,mng_BACK);
14021 LogPNGChunk(logging,mng_BACK,6L);
14022 red=ScaleQuantumToShort(image->background_color.red);
14023 green=ScaleQuantumToShort(image->background_color.green);
14024 blue=ScaleQuantumToShort(image->background_color.blue);
14025 PNGShort(chunk+4,red);
14026 PNGShort(chunk+6,green);
14027 PNGShort(chunk+8,blue);
14028 (void) WriteBlob(image,10,chunk);
14029 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14030 if (mng_info->equal_backgrounds)
14031 {
14032 (void) WriteBlobMSBULong(image,6L);
14033 PNGType(chunk,mng_bKGD);
14034 LogPNGChunk(logging,mng_bKGD,6L);
14035 (void) WriteBlob(image,10,chunk);
14036 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14037 }
14038 }
14039
14040 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14041 if ((need_local_plte == MagickFalse) &&
14042 (image->storage_class == PseudoClass) &&
14043 (all_images_are_gray == MagickFalse))
14044 {
14045 size_t
14046 data_length;
14047
14048 /*
14049 Write MNG PLTE chunk
14050 */
14051 data_length=3*image->colors;
14052 (void) WriteBlobMSBULong(image,data_length);
14053 PNGType(chunk,mng_PLTE);
14054 LogPNGChunk(logging,mng_PLTE,data_length);
14055
14056 for (i=0; i < (ssize_t) image->colors; i++)
14057 {
14058 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14059 image->colormap[i].red) & 0xff);
14060 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14061 image->colormap[i].green) & 0xff);
14062 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14063 image->colormap[i].blue) & 0xff);
14064 }
14065
14066 (void) WriteBlob(image,data_length+4,chunk);
14067 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14068 mng_info->have_write_global_plte=MagickTrue;
14069 }
14070 #endif
14071 }
14072 scene=0;
14073 mng_info->delay=0;
14074 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14075 defined(PNG_MNG_FEATURES_SUPPORTED)
14076 mng_info->equal_palettes=MagickFalse;
14077 #endif
14078 imageListLength=GetImageListLength(image);
14079 do
14080 {
14081 if (mng_info->adjoin)
14082 {
14083 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14084 defined(PNG_MNG_FEATURES_SUPPORTED)
14085 /*
14086 If we aren't using a global palette for the entire MNG, check to
14087 see if we can use one for two or more consecutive images.
14088 */
14089 if (need_local_plte && use_global_plte && !all_images_are_gray)
14090 {
14091 if (mng_info->IsPalette)
14092 {
14093 /*
14094 When equal_palettes is true, this image has the same palette
14095 as the previous PseudoClass image
14096 */
14097 mng_info->have_write_global_plte=mng_info->equal_palettes;
14098 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14099 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14100 {
14101 /*
14102 Write MNG PLTE chunk
14103 */
14104 size_t
14105 data_length;
14106
14107 data_length=3*image->colors;
14108 (void) WriteBlobMSBULong(image,data_length);
14109 PNGType(chunk,mng_PLTE);
14110 LogPNGChunk(logging,mng_PLTE,data_length);
14111
14112 for (i=0; i < (ssize_t) image->colors; i++)
14113 {
14114 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14115 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14116 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14117 }
14118
14119 (void) WriteBlob(image,data_length+4,chunk);
14120 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14121 (uInt) (data_length+4)));
14122 mng_info->have_write_global_plte=MagickTrue;
14123 }
14124 }
14125 else
14126 mng_info->have_write_global_plte=MagickFalse;
14127 }
14128 #endif
14129 if (need_defi)
14130 {
14131 ssize_t
14132 previous_x,
14133 previous_y;
14134
14135 if (scene != 0)
14136 {
14137 previous_x=mng_info->page.x;
14138 previous_y=mng_info->page.y;
14139 }
14140 else
14141 {
14142 previous_x=0;
14143 previous_y=0;
14144 }
14145 mng_info->page=image->page;
14146 if ((mng_info->page.x != previous_x) ||
14147 (mng_info->page.y != previous_y))
14148 {
14149 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
14150 PNGType(chunk,mng_DEFI);
14151 LogPNGChunk(logging,mng_DEFI,12L);
14152 chunk[4]=0; /* object 0 MSB */
14153 chunk[5]=0; /* object 0 LSB */
14154 chunk[6]=0; /* visible */
14155 chunk[7]=0; /* abstract */
14156 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14157 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14158 (void) WriteBlob(image,16,chunk);
14159 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14160 }
14161 }
14162 }
14163
14164 mng_info->write_mng=write_mng;
14165
14166 if ((int) image->dispose >= 3)
14167 mng_info->framing_mode=3;
14168
14169 if (mng_info->need_fram && mng_info->adjoin &&
14170 ((image->delay != mng_info->delay) ||
14171 (mng_info->framing_mode != mng_info->old_framing_mode)))
14172 {
14173 if (image->delay == mng_info->delay)
14174 {
14175 /*
14176 Write a MNG FRAM chunk with the new framing mode.
14177 */
14178 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
14179 PNGType(chunk,mng_FRAM);
14180 LogPNGChunk(logging,mng_FRAM,1L);
14181 chunk[4]=(unsigned char) mng_info->framing_mode;
14182 (void) WriteBlob(image,5,chunk);
14183 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14184 }
14185 else
14186 {
14187 /*
14188 Write a MNG FRAM chunk with the delay.
14189 */
14190 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
14191 PNGType(chunk,mng_FRAM);
14192 LogPNGChunk(logging,mng_FRAM,10L);
14193 chunk[4]=(unsigned char) mng_info->framing_mode;
14194 chunk[5]=0; /* frame name separator (no name) */
14195 chunk[6]=2; /* flag for changing default delay */
14196 chunk[7]=0; /* flag for changing frame timeout */
14197 chunk[8]=0; /* flag for changing frame clipping */
14198 chunk[9]=0; /* flag for changing frame sync_id */
14199 PNGLong(chunk+10,(png_uint_32)
14200 ((mng_info->ticks_per_second*
14201 image->delay)/MagickMax(image->ticks_per_second,1)));
14202 (void) WriteBlob(image,14,chunk);
14203 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14204 mng_info->delay=(png_uint_32) image->delay;
14205 }
14206 mng_info->old_framing_mode=mng_info->framing_mode;
14207 }
14208
14209 #if defined(JNG_SUPPORTED)
14210 if (image_info->compression == JPEGCompression)
14211 {
14212 ImageInfo
14213 *write_info;
14214
14215 if (logging != MagickFalse)
14216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14217 " Writing JNG object.");
14218 /* To do: specify the desired alpha compression method. */
14219 write_info=CloneImageInfo(image_info);
14220 write_info->compression=UndefinedCompression;
14221 status=WriteOneJNGImage(mng_info,write_info,image,exception);
14222 write_info=DestroyImageInfo(write_info);
14223 }
14224 else
14225 #endif
14226 {
14227 if (logging != MagickFalse)
14228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14229 " Writing PNG object.");
14230
14231 mng_info->need_blob = MagickFalse;
14232 mng_info->ping_preserve_colormap = MagickFalse;
14233
14234 /* We don't want any ancillary chunks written */
14235 mng_info->ping_exclude_bKGD=MagickTrue;
14236 mng_info->ping_exclude_caNv=MagickTrue;
14237 mng_info->ping_exclude_cHRM=MagickTrue;
14238 mng_info->ping_exclude_date=MagickTrue;
14239 mng_info->ping_exclude_EXIF=MagickTrue;
14240 mng_info->ping_exclude_gAMA=MagickTrue;
14241 mng_info->ping_exclude_iCCP=MagickTrue;
14242 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14243 mng_info->ping_exclude_oFFs=MagickTrue;
14244 mng_info->ping_exclude_pHYs=MagickTrue;
14245 mng_info->ping_exclude_sRGB=MagickTrue;
14246 mng_info->ping_exclude_tEXt=MagickTrue;
14247 mng_info->ping_exclude_tRNS=MagickTrue;
14248 mng_info->ping_exclude_zCCP=MagickTrue;
14249 mng_info->ping_exclude_zTXt=MagickTrue;
14250
14251 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14252 }
14253
14254 if (status == MagickFalse)
14255 {
14256 mng_info=MngInfoFreeStruct(mng_info);
14257 (void) CloseBlob(image);
14258 return(MagickFalse);
14259 }
14260 (void) CatchImageException(image);
14261 if (GetNextImageInList(image) == (Image *) NULL)
14262 break;
14263 image=SyncNextImageInList(image);
14264 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14265
14266 if (status == MagickFalse)
14267 break;
14268
14269 } while (mng_info->adjoin);
14270
14271 if (write_mng)
14272 {
14273 while (GetPreviousImageInList(image) != (Image *) NULL)
14274 image=GetPreviousImageInList(image);
14275 /*
14276 Write the MEND chunk.
14277 */
14278 (void) WriteBlobMSBULong(image,0x00000000L);
14279 PNGType(chunk,mng_MEND);
14280 LogPNGChunk(logging,mng_MEND,0L);
14281 (void) WriteBlob(image,4,chunk);
14282 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14283 }
14284 /*
14285 Relinquish resources.
14286 */
14287 (void) CloseBlob(image);
14288 mng_info=MngInfoFreeStruct(mng_info);
14289
14290 if (logging != MagickFalse)
14291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14292
14293 return(MagickTrue);
14294 }
14295 #else /* PNG_LIBPNG_VER > 10011 */
14296
WritePNGImage(const ImageInfo * image_info,Image * image)14297 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14298 Image *image)
14299 {
14300 (void) image;
14301 printf("Your PNG library is too old: You have libpng-%s\n",
14302 PNG_LIBPNG_VER_STRING);
14303
14304 ThrowBinaryException(CoderError,"PNG library is too old",
14305 image_info->filename);
14306 }
14307
WriteMNGImage(const ImageInfo * image_info,Image * image)14308 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14309 Image *image)
14310 {
14311 return(WritePNGImage(image_info,image));
14312 }
14313 #endif /* PNG_LIBPNG_VER > 10011 */
14314 #endif
14315