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-2019 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/transform.h"
91 #include "MagickCore/utility.h"
92 #if defined(MAGICKCORE_PNG_DELEGATE)
93
94 /* Suppress libpng pedantic warnings that were added in
95 * libpng-1.2.41 and libpng-1.4.0. If you are working on
96 * migration to libpng-1.5, remove these defines and then
97 * fix any code that generates warnings.
98 */
99 /* #define PNG_DEPRECATED Use of this function is deprecated */
100 /* #define PNG_USE_RESULT The result of this function must be checked */
101 /* #define PNG_NORETURN This function does not return */
102 /* #define PNG_ALLOCATED The result of the function is new memory */
103 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
104
105 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
106 #define PNG_PTR_NORETURN
107
108 #include <png.h>
109 #include <zlib.h>
110
111 /* ImageMagick differences */
112 #define first_scene scene
113
114 #if PNG_LIBPNG_VER > 10011
115 /*
116 Optional declarations. Define or undefine them as you like.
117 */
118 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
119
120 /*
121 Features under construction. Define these to work on them.
122 */
123 #undef MNG_OBJECT_BUFFERS
124 #undef MNG_BASI_SUPPORTED
125 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
126 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
127 #if defined(MAGICKCORE_JPEG_DELEGATE)
128 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
129 #endif
130 #if !defined(RGBColorMatchExact)
131 #define IsPNGColorEqual(color,target) \
132 (((color).red == (target).red) && \
133 ((color).green == (target).green) && \
134 ((color).blue == (target).blue))
135 #endif
136
137 /* Table of recognized sRGB ICC profiles */
138 struct sRGB_info_struct
139 {
140 png_uint_32 len;
141 png_uint_32 crc;
142 png_byte intent;
143 };
144
145 const struct sRGB_info_struct sRGB_info[] =
146 {
147 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
148 { 3048, 0x3b8772b9UL, 0},
149
150 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
151 { 3052, 0x427ebb21UL, 1},
152
153 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
154 {60988, 0x306fd8aeUL, 0},
155
156 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
157 {60960, 0xbbef7812UL, 0},
158
159 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
160 { 3024, 0x5d5129ceUL, 1},
161
162 /* HP-Microsoft sRGB v2 perceptual */
163 { 3144, 0x182ea552UL, 0},
164
165 /* HP-Microsoft sRGB v2 media-relative */
166 { 3144, 0xf29e526dUL, 1},
167
168 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
169 { 524, 0xd4938c39UL, 0},
170
171 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
172 { 3212, 0x034af5a1UL, 0},
173
174 /* Not recognized */
175 { 0, 0x00000000UL, 0},
176 };
177
178 /* Macros for left-bit-replication to ensure that pixels
179 * and PixelInfos all have the same image->depth, and for use
180 * in PNG8 quantization.
181 */
182
183 /* LBR01: Replicate top bit */
184
185 #define LBR01PacketRed(pixelpacket) \
186 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
187 0 : QuantumRange);
188
189 #define LBR01PacketGreen(pixelpacket) \
190 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
191 0 : QuantumRange);
192
193 #define LBR01PacketBlue(pixelpacket) \
194 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
195 0 : QuantumRange);
196
197 #define LBR01PacketAlpha(pixelpacket) \
198 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
199 0 : QuantumRange);
200
201 #define LBR01PacketRGB(pixelpacket) \
202 { \
203 LBR01PacketRed((pixelpacket)); \
204 LBR01PacketGreen((pixelpacket)); \
205 LBR01PacketBlue((pixelpacket)); \
206 }
207
208 #define LBR01PacketRGBA(pixelpacket) \
209 { \
210 LBR01PacketRGB((pixelpacket)); \
211 LBR01PacketAlpha((pixelpacket)); \
212 }
213
214 #define LBR01PixelRed(pixel) \
215 (SetPixelRed(image, \
216 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
217 0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelGreen(pixel) \
220 (SetPixelGreen(image, \
221 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
222 0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelBlue(pixel) \
225 (SetPixelBlue(image, \
226 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
227 0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelAlpha(pixel) \
230 (SetPixelAlpha(image, \
231 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
232 0 : QuantumRange,(pixel)));
233
234 #define LBR01PixelRGB(pixel) \
235 { \
236 LBR01PixelRed((pixel)); \
237 LBR01PixelGreen((pixel)); \
238 LBR01PixelBlue((pixel)); \
239 }
240
241 #define LBR01PixelRGBA(pixel) \
242 { \
243 LBR01PixelRGB((pixel)); \
244 LBR01PixelAlpha((pixel)); \
245 }
246
247 /* LBR02: Replicate top 2 bits */
248
249 #define LBR02PacketRed(pixelpacket) \
250 { \
251 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
252 (pixelpacket).red=ScaleCharToQuantum( \
253 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
254 }
255 #define LBR02PacketGreen(pixelpacket) \
256 { \
257 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
258 (pixelpacket).green=ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
260 }
261 #define LBR02PacketBlue(pixelpacket) \
262 { \
263 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
264 (pixelpacket).blue=ScaleCharToQuantum( \
265 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
266 }
267 #define LBR02PacketAlpha(pixelpacket) \
268 { \
269 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
270 (pixelpacket).alpha=ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
272 }
273
274 #define LBR02PacketRGB(pixelpacket) \
275 { \
276 LBR02PacketRed((pixelpacket)); \
277 LBR02PacketGreen((pixelpacket)); \
278 LBR02PacketBlue((pixelpacket)); \
279 }
280
281 #define LBR02PacketRGBA(pixelpacket) \
282 { \
283 LBR02PacketRGB((pixelpacket)); \
284 LBR02PacketAlpha((pixelpacket)); \
285 }
286
287 #define LBR02PixelRed(pixel) \
288 { \
289 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
290 & 0xc0; \
291 SetPixelRed(image, ScaleCharToQuantum( \
292 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
293 (pixel)); \
294 }
295 #define LBR02PixelGreen(pixel) \
296 { \
297 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
298 & 0xc0; \
299 SetPixelGreen(image, ScaleCharToQuantum( \
300 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
301 (pixel)); \
302 }
303 #define LBR02PixelBlue(pixel) \
304 { \
305 unsigned char lbr_bits= \
306 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
307 SetPixelBlue(image, ScaleCharToQuantum( \
308 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
309 (pixel)); \
310 }
311 #define LBR02PixelAlpha(pixel) \
312 { \
313 unsigned char lbr_bits= \
314 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
315 SetPixelAlpha(image, ScaleCharToQuantum( \
316 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
317 (pixel) ); \
318 }
319
320 #define LBR02PixelRGB(pixel) \
321 { \
322 LBR02PixelRed((pixel)); \
323 LBR02PixelGreen((pixel)); \
324 LBR02PixelBlue((pixel)); \
325 }
326
327 #define LBR02PixelRGBA(pixel) \
328 { \
329 LBR02PixelRGB((pixel)); \
330 LBR02PixelAlpha((pixel)); \
331 }
332
333 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
334 PNG8 quantization) */
335
336 #define LBR03PacketRed(pixelpacket) \
337 { \
338 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
339 (pixelpacket).red=ScaleCharToQuantum( \
340 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
341 }
342 #define LBR03PacketGreen(pixelpacket) \
343 { \
344 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
345 (pixelpacket).green=ScaleCharToQuantum( \
346 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
347 }
348 #define LBR03PacketBlue(pixelpacket) \
349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
351 (pixelpacket).blue=ScaleCharToQuantum( \
352 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
353 }
354
355 #define LBR03PacketRGB(pixelpacket) \
356 { \
357 LBR03PacketRed((pixelpacket)); \
358 LBR03PacketGreen((pixelpacket)); \
359 LBR03PacketBlue((pixelpacket)); \
360 }
361
362 #define LBR03PixelRed(pixel) \
363 { \
364 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
365 & 0xe0; \
366 SetPixelRed(image, ScaleCharToQuantum( \
367 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
368 }
369 #define LBR03Green(pixel) \
370 { \
371 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
372 & 0xe0; \
373 SetPixelGreen(image, ScaleCharToQuantum( \
374 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
375 }
376 #define LBR03Blue(pixel) \
377 { \
378 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
379 & 0xe0; \
380 SetPixelBlue(image, ScaleCharToQuantum( \
381 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
382 }
383
384 #define LBR03RGB(pixel) \
385 { \
386 LBR03PixelRed((pixel)); \
387 LBR03Green((pixel)); \
388 LBR03Blue((pixel)); \
389 }
390
391 /* LBR04: Replicate top 4 bits */
392
393 #define LBR04PacketRed(pixelpacket) \
394 { \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
396 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397 }
398 #define LBR04PacketGreen(pixelpacket) \
399 { \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
401 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402 }
403 #define LBR04PacketBlue(pixelpacket) \
404 { \
405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
406 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407 }
408 #define LBR04PacketAlpha(pixelpacket) \
409 { \
410 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
411 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
412 }
413
414 #define LBR04PacketRGB(pixelpacket) \
415 { \
416 LBR04PacketRed((pixelpacket)); \
417 LBR04PacketGreen((pixelpacket)); \
418 LBR04PacketBlue((pixelpacket)); \
419 }
420
421 #define LBR04PacketRGBA(pixelpacket) \
422 { \
423 LBR04PacketRGB((pixelpacket)); \
424 LBR04PacketAlpha((pixelpacket)); \
425 }
426
427 #define LBR04PixelRed(pixel) \
428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
430 & 0xf0; \
431 SetPixelRed(image,\
432 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
433 }
434 #define LBR04PixelGreen(pixel) \
435 { \
436 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
437 & 0xf0; \
438 SetPixelGreen(image,\
439 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
440 }
441 #define LBR04PixelBlue(pixel) \
442 { \
443 unsigned char lbr_bits= \
444 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
445 SetPixelBlue(image,\
446 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
447 }
448 #define LBR04PixelAlpha(pixel) \
449 { \
450 unsigned char lbr_bits= \
451 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
452 SetPixelAlpha(image,\
453 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
454 }
455
456 #define LBR04PixelRGB(pixel) \
457 { \
458 LBR04PixelRed((pixel)); \
459 LBR04PixelGreen((pixel)); \
460 LBR04PixelBlue((pixel)); \
461 }
462
463 #define LBR04PixelRGBA(pixel) \
464 { \
465 LBR04PixelRGB((pixel)); \
466 LBR04PixelAlpha((pixel)); \
467 }
468
469 /*
470 Establish thread safety.
471 setjmp/longjmp is claimed to be safe on these platforms:
472 setjmp/longjmp is alleged to be unsafe on these platforms:
473 */
474 #ifdef PNG_SETJMP_SUPPORTED
475 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
476 # define IMPNG_SETJMP_NOT_THREAD_SAFE
477 # endif
478
479 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
480 static SemaphoreInfo
481 *ping_semaphore = (SemaphoreInfo *) NULL;
482 # endif
483 #endif
484
485 /*
486 This temporary until I set up malloc'ed object attributes array.
487 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
488 waste more memory.
489 */
490 #define MNG_MAX_OBJECTS 256
491
492 /*
493 If this not defined, spec is interpreted strictly. If it is
494 defined, an attempt will be made to recover from some errors,
495 including
496 o global PLTE too short
497 */
498 #undef MNG_LOOSE
499
500 /*
501 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
502 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
503 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
504 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
505 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
506 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
507 will be enabled by default in libpng-1.2.0.
508 */
509 #ifdef PNG_MNG_FEATURES_SUPPORTED
510 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
511 # define PNG_READ_EMPTY_PLTE_SUPPORTED
512 # endif
513 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
514 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
515 # endif
516 #endif
517
518 /*
519 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
520 This macro is only defined in libpng-1.0.3 and later.
521 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
522 */
523 #ifndef PNG_UINT_31_MAX
524 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
525 #endif
526
527 /*
528 Constant strings for known chunk types. If you need to add a chunk,
529 add a string holding the name here. To make the code more
530 portable, we use ASCII numbers like this, not characters.
531 */
532
533 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
534 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
535 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
536 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
537 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
538 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
539 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
540 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
541 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
542 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
543 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
544 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
545 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
546 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
547 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
548 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
549 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
550 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
551 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
552 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
553 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
554 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
555 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
556 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
557 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
558 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
559 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
560 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
561 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
562 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
563 static const png_byte mng_orNT[5]={111, 114, 78, 84, (png_byte) '\0'};
564 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
565 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
566 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
567 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
568 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
569 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
570
571 #if defined(JNG_SUPPORTED)
572 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
573 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
574 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
575 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
576 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
577 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
578 #endif
579
580 #if 0
581 /* Other known chunks that are not yet supported by ImageMagick: */
582 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
583 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
584 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
585 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
586 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
587 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
588 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
589 #endif
590
591 typedef struct _MngBox
592 {
593 long
594 left,
595 right,
596 top,
597 bottom;
598 } MngBox;
599
600 typedef struct _MngPair
601 {
602 volatile long
603 a,
604 b;
605 } MngPair;
606
607 #ifdef MNG_OBJECT_BUFFERS
608 typedef struct _MngBuffer
609 {
610
611 size_t
612 height,
613 width;
614
615 Image
616 *image;
617
618 png_color
619 plte[256];
620
621 int
622 reference_count;
623
624 unsigned char
625 alpha_sample_depth,
626 compression_method,
627 color_type,
628 concrete,
629 filter_method,
630 frozen,
631 image_type,
632 interlace_method,
633 pixel_sample_depth,
634 plte_length,
635 sample_depth,
636 viewable;
637 } MngBuffer;
638 #endif
639
640 typedef struct _MngInfo
641 {
642
643 #ifdef MNG_OBJECT_BUFFERS
644 MngBuffer
645 *ob[MNG_MAX_OBJECTS];
646 #endif
647
648 Image *
649 image;
650
651 RectangleInfo
652 page;
653
654 int
655 adjoin,
656 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
657 bytes_in_read_buffer,
658 found_empty_plte,
659 #endif
660 equal_backgrounds,
661 equal_chrms,
662 equal_gammas,
663 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
664 defined(PNG_MNG_FEATURES_SUPPORTED)
665 equal_palettes,
666 #endif
667 equal_physs,
668 equal_srgbs,
669 framing_mode,
670 have_global_bkgd,
671 have_global_chrm,
672 have_global_gama,
673 have_global_phys,
674 have_global_sbit,
675 have_global_srgb,
676 have_saved_bkgd_index,
677 have_write_global_chrm,
678 have_write_global_gama,
679 have_write_global_plte,
680 have_write_global_srgb,
681 need_fram,
682 object_id,
683 old_framing_mode,
684 saved_bkgd_index;
685
686 int
687 new_number_colors;
688
689 ssize_t
690 image_found,
691 loop_count[256],
692 loop_iteration[256],
693 scenes_found,
694 x_off[MNG_MAX_OBJECTS],
695 y_off[MNG_MAX_OBJECTS];
696
697 MngBox
698 clip,
699 frame,
700 image_box,
701 object_clip[MNG_MAX_OBJECTS];
702
703 unsigned char
704 /* These flags could be combined into one byte */
705 exists[MNG_MAX_OBJECTS],
706 frozen[MNG_MAX_OBJECTS],
707 loop_active[256],
708 invisible[MNG_MAX_OBJECTS],
709 viewable[MNG_MAX_OBJECTS];
710
711 MagickOffsetType
712 loop_jump[256];
713
714 png_colorp
715 global_plte;
716
717 png_color_8
718 global_sbit;
719
720 png_byte
721 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
722 read_buffer[8],
723 #endif
724 global_trns[256];
725
726 float
727 global_gamma;
728
729 ChromaticityInfo
730 global_chrm;
731
732 RenderingIntent
733 global_srgb_intent;
734
735 unsigned long
736 delay,
737 global_plte_length,
738 global_trns_length,
739 global_x_pixels_per_unit,
740 global_y_pixels_per_unit,
741 mng_width,
742 mng_height,
743 ticks_per_second;
744
745 MagickBooleanType
746 need_blob;
747
748 unsigned int
749 IsPalette,
750 global_phys_unit_type,
751 basi_warning,
752 clon_warning,
753 dhdr_warning,
754 jhdr_warning,
755 magn_warning,
756 past_warning,
757 phyg_warning,
758 phys_warning,
759 sbit_warning,
760 show_warning,
761 mng_type,
762 write_mng,
763 write_png_colortype,
764 write_png_depth,
765 write_png_compression_level,
766 write_png_compression_strategy,
767 write_png_compression_filter,
768 write_png8,
769 write_png24,
770 write_png32,
771 write_png48,
772 write_png64;
773
774 #ifdef MNG_BASI_SUPPORTED
775 unsigned long
776 basi_width,
777 basi_height;
778
779 unsigned int
780 basi_depth,
781 basi_color_type,
782 basi_compression_method,
783 basi_filter_type,
784 basi_interlace_method,
785 basi_red,
786 basi_green,
787 basi_blue,
788 basi_alpha,
789 basi_viewable;
790 #endif
791
792 png_uint_16
793 magn_first,
794 magn_last,
795 magn_mb,
796 magn_ml,
797 magn_mr,
798 magn_mt,
799 magn_mx,
800 magn_my,
801 magn_methx,
802 magn_methy;
803
804 PixelInfo
805 mng_global_bkgd;
806
807 /* Added at version 6.6.6-7 */
808 MagickBooleanType
809 ping_exclude_bKGD,
810 ping_exclude_cHRM,
811 ping_exclude_date,
812 ping_exclude_eXIf,
813 ping_exclude_EXIF,
814 ping_exclude_gAMA,
815 ping_exclude_iCCP,
816 /* ping_exclude_iTXt, */
817 ping_exclude_oFFs,
818 ping_exclude_pHYs,
819 ping_exclude_sRGB,
820 ping_exclude_tEXt,
821 ping_exclude_tRNS,
822 ping_exclude_caNv,
823 ping_exclude_zCCP, /* hex-encoded iCCP */
824 ping_exclude_zTXt,
825 ping_preserve_colormap,
826 /* Added at version 6.8.5-7 */
827 ping_preserve_iCCP,
828 /* Added at version 6.8.9-9 */
829 ping_exclude_tIME;
830
831 } MngInfo;
832 #endif /* VER */
833
834 /*
835 Forward declarations.
836 */
837 static MagickBooleanType
838 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839
840 static MagickBooleanType
841 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
842
843 #if defined(JNG_SUPPORTED)
844 static MagickBooleanType
845 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
846 #endif
847
848 #if PNG_LIBPNG_VER > 10011
849
850
851 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
852 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)853 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
854 {
855 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
856 *
857 * This is true if the high byte and the next highest byte of
858 * each sample of the image, the colormap, and the background color
859 * are equal to each other. We check this by seeing if the samples
860 * are unchanged when we scale them down to 8 and back up to Quantum.
861 *
862 * We don't use the method GetImageDepth() because it doesn't check
863 * background and doesn't handle PseudoClass specially.
864 */
865
866 #define QuantumToCharToQuantumEqQuantum(quantum) \
867 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
868
869 MagickBooleanType
870 ok_to_reduce=MagickFalse;
871
872 if (image->depth >= 16)
873 {
874
875 const Quantum
876 *p;
877
878 ok_to_reduce=
879 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
880 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
881 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
882 MagickTrue : MagickFalse;
883
884 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
885 {
886 int indx;
887
888 for (indx=0; indx < (ssize_t) image->colors; indx++)
889 {
890 ok_to_reduce=(
891 QuantumToCharToQuantumEqQuantum(
892 image->colormap[indx].red) &&
893 QuantumToCharToQuantumEqQuantum(
894 image->colormap[indx].green) &&
895 QuantumToCharToQuantumEqQuantum(
896 image->colormap[indx].blue)) ?
897 MagickTrue : MagickFalse;
898
899 if (ok_to_reduce == MagickFalse)
900 break;
901 }
902 }
903
904 if ((ok_to_reduce != MagickFalse) &&
905 (image->storage_class != PseudoClass))
906 {
907 ssize_t
908 y;
909
910 register ssize_t
911 x;
912
913 for (y=0; y < (ssize_t) image->rows; y++)
914 {
915 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
916
917 if (p == (const Quantum *) NULL)
918 {
919 ok_to_reduce = MagickFalse;
920 break;
921 }
922
923 for (x=(ssize_t) image->columns-1; x >= 0; x--)
924 {
925 ok_to_reduce=
926 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
927 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
928 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
929 MagickTrue : MagickFalse;
930
931 if (ok_to_reduce == MagickFalse)
932 break;
933
934 p+=GetPixelChannels(image);
935 }
936 if (x >= 0)
937 break;
938 }
939 }
940
941 if (ok_to_reduce != MagickFalse)
942 {
943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
944 " OK to reduce PNG bit depth to 8 without loss of info");
945 }
946 else
947 {
948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
949 " Not OK to reduce PNG bit depth to 8 without losing info");
950 }
951 }
952
953 return ok_to_reduce;
954 }
955 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
956
PngColorTypeToString(const unsigned int color_type)957 static const char* PngColorTypeToString(const unsigned int color_type)
958 {
959 const char
960 *result = "Unknown";
961
962 switch (color_type)
963 {
964 case PNG_COLOR_TYPE_GRAY:
965 result = "Gray";
966 break;
967 case PNG_COLOR_TYPE_GRAY_ALPHA:
968 result = "Gray+Alpha";
969 break;
970 case PNG_COLOR_TYPE_PALETTE:
971 result = "Palette";
972 break;
973 case PNG_COLOR_TYPE_RGB:
974 result = "RGB";
975 break;
976 case PNG_COLOR_TYPE_RGB_ALPHA:
977 result = "RGB+Alpha";
978 break;
979 }
980
981 return result;
982 }
983
984 static int
Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)985 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
986 {
987 switch (orientation)
988 {
989 /* Convert to Exif orientations as defined in "Exchangeable image file
990 * format for digital still cameras: Exif Version 2.31",
991 * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
992 */
993
994 case TopLeftOrientation:
995 return 1;
996 case TopRightOrientation:
997 return 2;
998 case BottomRightOrientation:
999 return 3;
1000 case BottomLeftOrientation:
1001 return 4;
1002 case LeftTopOrientation:
1003 return 5;
1004 case RightTopOrientation:
1005 return 6;
1006 case RightBottomOrientation:
1007 return 7;
1008 case LeftBottomOrientation:
1009 return 8;
1010 case UndefinedOrientation:
1011 default:
1012 return 0;
1013 }
1014 }
1015 static OrientationType
Magick_Orientation_from_Exif_Orientation(const int orientation)1016 Magick_Orientation_from_Exif_Orientation(const int orientation)
1017 {
1018 switch (orientation)
1019 {
1020 case 1:
1021 return TopLeftOrientation;
1022 case 2:
1023 return TopRightOrientation;
1024 case 3:
1025 return BottomRightOrientation;
1026 case 4:
1027 return BottomLeftOrientation;
1028 case 5:
1029 return LeftTopOrientation;
1030 case 6:
1031 return RightTopOrientation;
1032 case 7:
1033 return RightBottomOrientation;
1034 case 8:
1035 return LeftBottomOrientation;
1036 case 0:
1037 default:
1038 return UndefinedOrientation;
1039 }
1040 }
1041
1042 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)1043 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1044 {
1045 switch (intent)
1046 {
1047 case PerceptualIntent:
1048 return 0;
1049
1050 case RelativeIntent:
1051 return 1;
1052
1053 case SaturationIntent:
1054 return 2;
1055
1056 case AbsoluteIntent:
1057 return 3;
1058
1059 default:
1060 return -1;
1061 }
1062 }
1063
1064 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)1065 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1066 {
1067 switch (ping_intent)
1068 {
1069 case 0:
1070 return PerceptualIntent;
1071
1072 case 1:
1073 return RelativeIntent;
1074
1075 case 2:
1076 return SaturationIntent;
1077
1078 case 3:
1079 return AbsoluteIntent;
1080
1081 default:
1082 return UndefinedIntent;
1083 }
1084 }
1085
1086 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1087 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1088 {
1089 switch (ping_intent)
1090 {
1091 case 0:
1092 return "Perceptual Intent";
1093
1094 case 1:
1095 return "Relative Intent";
1096
1097 case 2:
1098 return "Saturation Intent";
1099
1100 case 3:
1101 return "Absolute Intent";
1102
1103 default:
1104 return "Undefined Intent";
1105 }
1106 }
1107
1108 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1109 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1110 {
1111 switch (ping_colortype)
1112 {
1113 case 0:
1114 return "Grayscale";
1115
1116 case 2:
1117 return "Truecolor";
1118
1119 case 3:
1120 return "Indexed";
1121
1122 case 4:
1123 return "GrayAlpha";
1124
1125 case 6:
1126 return "RGBA";
1127
1128 default:
1129 return "UndefinedColorType";
1130 }
1131 }
1132
1133 #endif /* PNG_LIBPNG_VER > 10011 */
1134 #endif /* MAGICKCORE_PNG_DELEGATE */
1135
1136 /*
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 % %
1139 % %
1140 % %
1141 % I s M N G %
1142 % %
1143 % %
1144 % %
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 %
1147 % IsMNG() returns MagickTrue if the image format type, identified by the
1148 % magick string, is MNG.
1149 %
1150 % The format of the IsMNG method is:
1151 %
1152 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1153 %
1154 % A description of each parameter follows:
1155 %
1156 % o magick: compare image format pattern against these bytes.
1157 %
1158 % o length: Specifies the length of the magick string.
1159 %
1160 %
1161 */
IsMNG(const unsigned char * magick,const size_t length)1162 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1163 {
1164 if (length < 8)
1165 return(MagickFalse);
1166
1167 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1168 return(MagickTrue);
1169
1170 return(MagickFalse);
1171 }
1172
1173 /*
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 % %
1176 % %
1177 % %
1178 % I s J N G %
1179 % %
1180 % %
1181 % %
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 %
1184 % IsJNG() returns MagickTrue if the image format type, identified by the
1185 % magick string, is JNG.
1186 %
1187 % The format of the IsJNG method is:
1188 %
1189 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1190 %
1191 % A description of each parameter follows:
1192 %
1193 % o magick: compare image format pattern against these bytes.
1194 %
1195 % o length: Specifies the length of the magick string.
1196 %
1197 %
1198 */
IsJNG(const unsigned char * magick,const size_t length)1199 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1200 {
1201 if (length < 8)
1202 return(MagickFalse);
1203
1204 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1205 return(MagickTrue);
1206
1207 return(MagickFalse);
1208 }
1209
1210 /*
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 % %
1213 % %
1214 % %
1215 % I s P N G %
1216 % %
1217 % %
1218 % %
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 %
1221 % IsPNG() returns MagickTrue if the image format type, identified by the
1222 % magick string, is PNG.
1223 %
1224 % The format of the IsPNG method is:
1225 %
1226 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1227 %
1228 % A description of each parameter follows:
1229 %
1230 % o magick: compare image format pattern against these bytes.
1231 %
1232 % o length: Specifies the length of the magick string.
1233 %
1234 */
IsPNG(const unsigned char * magick,const size_t length)1235 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1236 {
1237 if (length < 8)
1238 return(MagickFalse);
1239
1240 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1241 return(MagickTrue);
1242
1243 return(MagickFalse);
1244 }
1245
1246 #if defined(MAGICKCORE_PNG_DELEGATE)
1247 #if defined(__cplusplus) || defined(c_plusplus)
1248 extern "C" {
1249 #endif
1250
1251 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1252 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1253 {
1254 unsigned char
1255 buffer[4];
1256
1257 assert(image != (Image *) NULL);
1258 assert(image->signature == MagickCoreSignature);
1259 buffer[0]=(unsigned char) (value >> 24);
1260 buffer[1]=(unsigned char) (value >> 16);
1261 buffer[2]=(unsigned char) (value >> 8);
1262 buffer[3]=(unsigned char) value;
1263 return((size_t) WriteBlob(image,4,buffer));
1264 }
1265
PNGLong(png_bytep p,png_uint_32 value)1266 static void PNGLong(png_bytep p,png_uint_32 value)
1267 {
1268 *p++=(png_byte) ((value >> 24) & 0xff);
1269 *p++=(png_byte) ((value >> 16) & 0xff);
1270 *p++=(png_byte) ((value >> 8) & 0xff);
1271 *p++=(png_byte) (value & 0xff);
1272 }
1273
PNGsLong(png_bytep p,png_int_32 value)1274 static void PNGsLong(png_bytep p,png_int_32 value)
1275 {
1276 *p++=(png_byte) ((value >> 24) & 0xff);
1277 *p++=(png_byte) ((value >> 16) & 0xff);
1278 *p++=(png_byte) ((value >> 8) & 0xff);
1279 *p++=(png_byte) (value & 0xff);
1280 }
1281
PNGShort(png_bytep p,png_uint_16 value)1282 static void PNGShort(png_bytep p,png_uint_16 value)
1283 {
1284 *p++=(png_byte) ((value >> 8) & 0xff);
1285 *p++=(png_byte) (value & 0xff);
1286 }
1287
PNGType(png_bytep p,const png_byte * type)1288 static void PNGType(png_bytep p,const png_byte *type)
1289 {
1290 (void) memcpy(p,type,4*sizeof(png_byte));
1291 }
1292
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1293 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1294 size_t length)
1295 {
1296 if (logging != MagickFalse)
1297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1298 " Writing %c%c%c%c chunk, length: %.20g",
1299 type[0],type[1],type[2],type[3],(double) length);
1300 }
1301 #endif /* PNG_LIBPNG_VER > 10011 */
1302
1303 #if defined(__cplusplus) || defined(c_plusplus)
1304 }
1305 #endif
1306
1307 #if PNG_LIBPNG_VER > 10011
1308 /*
1309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310 % %
1311 % %
1312 % %
1313 % R e a d P N G I m a g e %
1314 % %
1315 % %
1316 % %
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318 %
1319 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1320 % Multiple-image Network Graphics (MNG) image file and returns it. It
1321 % allocates the memory necessary for the new Image structure and returns a
1322 % pointer to the new image or set of images.
1323 %
1324 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1325 %
1326 % The format of the ReadPNGImage method is:
1327 %
1328 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1329 %
1330 % A description of each parameter follows:
1331 %
1332 % o image_info: the image info.
1333 %
1334 % o exception: return any errors or warnings in this structure.
1335 %
1336 % To do, more or less in chronological order (as of version 5.5.2,
1337 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1338 %
1339 % Get 16-bit cheap transparency working.
1340 %
1341 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1342 %
1343 % Preserve all unknown and not-yet-handled known chunks found in input
1344 % PNG file and copy them into output PNG files according to the PNG
1345 % copying rules.
1346 %
1347 % (At this point, PNG encoding should be in full MNG compliance)
1348 %
1349 % Provide options for choice of background to use when the MNG BACK
1350 % chunk is not present or is not mandatory (i.e., leave transparent,
1351 % user specified, MNG BACK, PNG bKGD)
1352 %
1353 % Implement LOOP/ENDL [done, but could do discretionary loops more
1354 % efficiently by linking in the duplicate frames.].
1355 %
1356 % Decode and act on the MHDR simplicity profile (offer option to reject
1357 % files or attempt to process them anyway when the profile isn't LC or VLC).
1358 %
1359 % Upgrade to full MNG without Delta-PNG.
1360 %
1361 % o BACK [done a while ago except for background image ID]
1362 % o MOVE [done 15 May 1999]
1363 % o CLIP [done 15 May 1999]
1364 % o DISC [done 19 May 1999]
1365 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1366 % o SEEK [partially done 19 May 1999 (discard function only)]
1367 % o SHOW
1368 % o PAST
1369 % o BASI
1370 % o MNG-level tEXt/iTXt/zTXt
1371 % o pHYg
1372 % o pHYs
1373 % o sBIT
1374 % o bKGD
1375 % o iTXt (wait for libpng implementation).
1376 %
1377 % Use the scene signature to discover when an identical scene is
1378 % being reused, and just point to the original image->exception instead
1379 % of storing another set of pixels. This not specific to MNG
1380 % but could be applied generally.
1381 %
1382 % Upgrade to full MNG with Delta-PNG.
1383 %
1384 % JNG tEXt/iTXt/zTXt
1385 %
1386 % We will not attempt to read files containing the CgBI chunk.
1387 % They are really Xcode files meant for display on the iPhone.
1388 % These are not valid PNG files and it is impossible to recover
1389 % the original PNG from files that have been converted to Xcode-PNG,
1390 % since irretrievable loss of color data has occurred due to the
1391 % use of premultiplied alpha.
1392 */
1393
1394 #if defined(__cplusplus) || defined(c_plusplus)
1395 extern "C" {
1396 #endif
1397
1398 /*
1399 This the function that does the actual reading of data. It is
1400 the same as the one supplied in libpng, except that it receives the
1401 datastream from the ReadBlob() function instead of standard input.
1402 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1403 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1404 {
1405 Image
1406 *image;
1407
1408 image=(Image *) png_get_io_ptr(png_ptr);
1409 if (length != 0)
1410 {
1411 png_size_t
1412 check;
1413
1414 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1415 if (check != length)
1416 {
1417 char
1418 msg[MagickPathExtent];
1419
1420 (void) FormatLocaleString(msg,MagickPathExtent,
1421 "Expected %.20g bytes; found %.20g bytes",(double) length,
1422 (double) check);
1423 png_warning(png_ptr,msg);
1424 png_error(png_ptr,"Read Exception");
1425 }
1426 }
1427 }
1428
1429 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1430 !defined(PNG_MNG_FEATURES_SUPPORTED)
1431 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1432 * older than libpng-1.0.3a, which was the first to allow the empty
1433 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1434 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1435 * encountered after an empty PLTE, so we have to look ahead for bKGD
1436 * chunks and remove them from the datastream that is passed to libpng,
1437 * and store their contents for later use.
1438 */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1439 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1440 {
1441 MngInfo
1442 *mng_info;
1443
1444 Image
1445 *image;
1446
1447 png_size_t
1448 check;
1449
1450 register ssize_t
1451 i;
1452
1453 i=0;
1454 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1455 image=(Image *) mng_info->image;
1456 while (mng_info->bytes_in_read_buffer && length)
1457 {
1458 data[i]=mng_info->read_buffer[i];
1459 mng_info->bytes_in_read_buffer--;
1460 length--;
1461 i++;
1462 }
1463 if (length != 0)
1464 {
1465 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1466
1467 if (check != length)
1468 png_error(png_ptr,"Read Exception");
1469
1470 if (length == 4)
1471 {
1472 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1473 (data[3] == 0))
1474 {
1475 check=(png_size_t) ReadBlob(image,(size_t) length,
1476 (char *) mng_info->read_buffer);
1477 mng_info->read_buffer[4]=0;
1478 mng_info->bytes_in_read_buffer=4;
1479 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1480 mng_info->found_empty_plte=MagickTrue;
1481 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1482 {
1483 mng_info->found_empty_plte=MagickFalse;
1484 mng_info->have_saved_bkgd_index=MagickFalse;
1485 }
1486 }
1487
1488 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1489 (data[3] == 1))
1490 {
1491 check=(png_size_t) ReadBlob(image,(size_t) length,
1492 (char *) mng_info->read_buffer);
1493 mng_info->read_buffer[4]=0;
1494 mng_info->bytes_in_read_buffer=4;
1495 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1496 if (mng_info->found_empty_plte)
1497 {
1498 /*
1499 Skip the bKGD data byte and CRC.
1500 */
1501 check=(png_size_t)
1502 ReadBlob(image,5,(char *) mng_info->read_buffer);
1503 check=(png_size_t) ReadBlob(image,(size_t) length,
1504 (char *) mng_info->read_buffer);
1505 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1506 mng_info->have_saved_bkgd_index=MagickTrue;
1507 mng_info->bytes_in_read_buffer=0;
1508 }
1509 }
1510 }
1511 }
1512 }
1513 #endif
1514
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1515 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1516 {
1517 Image
1518 *image;
1519
1520 image=(Image *) png_get_io_ptr(png_ptr);
1521 if (length != 0)
1522 {
1523 png_size_t
1524 check;
1525
1526 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1527
1528 if (check != length)
1529 png_error(png_ptr,"WriteBlob Failed");
1530 }
1531 }
1532
png_flush_data(png_structp png_ptr)1533 static void png_flush_data(png_structp png_ptr)
1534 {
1535 (void) png_ptr;
1536 }
1537
1538 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1539 static int PalettesAreEqual(Image *a,Image *b)
1540 {
1541 ssize_t
1542 i;
1543
1544 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1545 return((int) MagickFalse);
1546
1547 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1548 return((int) MagickFalse);
1549
1550 if (a->colors != b->colors)
1551 return((int) MagickFalse);
1552
1553 for (i=0; i < (ssize_t) a->colors; i++)
1554 {
1555 if ((a->colormap[i].red != b->colormap[i].red) ||
1556 (a->colormap[i].green != b->colormap[i].green) ||
1557 (a->colormap[i].blue != b->colormap[i].blue))
1558 return((int) MagickFalse);
1559 }
1560
1561 return((int) MagickTrue);
1562 }
1563 #endif
1564
MngInfoDiscardObject(MngInfo * mng_info,int i)1565 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1566 {
1567 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1568 mng_info->exists[i] && !mng_info->frozen[i])
1569 {
1570 #ifdef MNG_OBJECT_BUFFERS
1571 if (mng_info->ob[i] != (MngBuffer *) NULL)
1572 {
1573 if (mng_info->ob[i]->reference_count > 0)
1574 mng_info->ob[i]->reference_count--;
1575
1576 if (mng_info->ob[i]->reference_count == 0)
1577 {
1578 if (mng_info->ob[i]->image != (Image *) NULL)
1579 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1580
1581 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1582 }
1583 }
1584 mng_info->ob[i]=(MngBuffer *) NULL;
1585 #endif
1586 mng_info->exists[i]=MagickFalse;
1587 mng_info->invisible[i]=MagickFalse;
1588 mng_info->viewable[i]=MagickFalse;
1589 mng_info->frozen[i]=MagickFalse;
1590 mng_info->x_off[i]=0;
1591 mng_info->y_off[i]=0;
1592 mng_info->object_clip[i].left=0;
1593 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1594 mng_info->object_clip[i].top=0;
1595 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1596 }
1597 }
1598
MngInfoFreeStruct(MngInfo * mng_info)1599 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1600 {
1601 register ssize_t
1602 i;
1603
1604 if (mng_info == (MngInfo *) NULL)
1605 return((MngInfo *) NULL);
1606
1607 for (i=1; i < MNG_MAX_OBJECTS; i++)
1608 MngInfoDiscardObject(mng_info,i);
1609
1610 mng_info->global_plte=(png_colorp)
1611 RelinquishMagickMemory(mng_info->global_plte);
1612
1613 return((MngInfo *) RelinquishMagickMemory(mng_info));
1614 }
1615
mng_minimum_box(MngBox box1,MngBox box2)1616 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1617 {
1618 MngBox
1619 box;
1620
1621 box=box1;
1622 if (box.left < box2.left)
1623 box.left=box2.left;
1624
1625 if (box.top < box2.top)
1626 box.top=box2.top;
1627
1628 if (box.right > box2.right)
1629 box.right=box2.right;
1630
1631 if (box.bottom > box2.bottom)
1632 box.bottom=box2.bottom;
1633
1634 return box;
1635 }
1636
mng_get_long(unsigned char * p)1637 static long mng_get_long(unsigned char *p)
1638 {
1639 return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1640 ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1641 }
1642
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1643 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1644 unsigned char *p)
1645 {
1646 MngBox
1647 box;
1648
1649 /*
1650 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1651 */
1652 box.left=mng_get_long(p);
1653 box.right=mng_get_long(&p[4]);
1654 box.top=mng_get_long(&p[8]);
1655 box.bottom=mng_get_long(&p[12]);
1656 if (delta_type != 0)
1657 {
1658 box.left+=previous_box.left;
1659 box.right+=previous_box.right;
1660 box.top+=previous_box.top;
1661 box.bottom+=previous_box.bottom;
1662 }
1663
1664 return(box);
1665 }
1666
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1667 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1668 unsigned char *p)
1669 {
1670 MngPair
1671 pair;
1672
1673 /*
1674 Read two ssize_t's from CLON, MOVE or PAST chunk
1675 */
1676 pair.a=mng_get_long(p);
1677 pair.b=mng_get_long(&p[4]);
1678 if (delta_type != 0)
1679 {
1680 pair.a+=previous_pair.a;
1681 pair.b+=previous_pair.b;
1682 }
1683
1684 return(pair);
1685 }
1686
1687 typedef struct _PNGErrorInfo
1688 {
1689 Image
1690 *image;
1691
1692 ExceptionInfo
1693 *exception;
1694 } PNGErrorInfo;
1695
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1696 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1697 {
1698 ExceptionInfo
1699 *exception;
1700
1701 Image
1702 *image;
1703
1704 PNGErrorInfo
1705 *error_info;
1706
1707 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1708 image=error_info->image;
1709 exception=error_info->exception;
1710
1711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1712 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1713
1714 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1715 "`%s'",image->filename);
1716
1717 #if (PNG_LIBPNG_VER < 10500)
1718 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1719 * are building with libpng-1.4.x and can be ignored.
1720 */
1721 longjmp(ping->jmpbuf,1);
1722 #else
1723 png_longjmp(ping,1);
1724 #endif
1725 }
1726
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1727 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1728 {
1729 ExceptionInfo
1730 *exception;
1731
1732 Image
1733 *image;
1734
1735 PNGErrorInfo
1736 *error_info;
1737
1738 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1739 png_error(ping, message);
1740
1741 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1742 image=error_info->image;
1743 exception=error_info->exception;
1744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1745 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1746
1747 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1748 message,"`%s'",image->filename);
1749 }
1750
1751 #ifdef PNG_USER_MEM_SUPPORTED
1752 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1753 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1754 #else
1755 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1756 #endif
1757 {
1758 (void) png_ptr;
1759 return((png_voidp) AcquireMagickMemory((size_t) size));
1760 }
1761
1762 /*
1763 Free a pointer. It is removed from the list at the same time.
1764 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1765 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1766 {
1767 (void) png_ptr;
1768 ptr=RelinquishMagickMemory(ptr);
1769 return((png_free_ptr) NULL);
1770 }
1771 #endif
1772
1773 #if defined(__cplusplus) || defined(c_plusplus)
1774 }
1775 #endif
1776
1777 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1778 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1779 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1780 {
1781 register ssize_t
1782 i;
1783
1784 register unsigned char
1785 *dp;
1786
1787 register png_charp
1788 sp;
1789
1790 size_t
1791 extent,
1792 length,
1793 nibbles;
1794
1795 StringInfo
1796 *profile;
1797
1798 static const unsigned char
1799 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1800 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1801 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1802 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1803 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1804 13,14,15};
1805
1806 sp=text[ii].text+1;
1807 extent=text[ii].text_length;
1808 /* look for newline */
1809 while ((*sp != '\n') && extent--)
1810 sp++;
1811
1812 /* look for length */
1813 while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1814 sp++;
1815
1816 if (extent == 0)
1817 {
1818 png_warning(ping,"invalid profile length");
1819 return(MagickFalse);
1820 }
1821
1822 length=StringToLong(sp);
1823
1824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825 " length: %lu",(unsigned long) length);
1826
1827 while ((*sp != ' ' && *sp != '\n') && extent--)
1828 sp++;
1829
1830 if (extent == 0)
1831 {
1832 png_warning(ping,"missing profile length");
1833 return(MagickFalse);
1834 }
1835
1836 /* allocate space */
1837 if (length == 0)
1838 {
1839 png_warning(ping,"invalid profile length");
1840 return(MagickFalse);
1841 }
1842
1843 profile=BlobToStringInfo((const void *) NULL,length);
1844
1845 if (profile == (StringInfo *) NULL)
1846 {
1847 png_warning(ping, "unable to copy profile");
1848 return(MagickFalse);
1849 }
1850
1851 /* copy profile, skipping white space and column 1 "=" signs */
1852 dp=GetStringInfoDatum(profile);
1853 nibbles=length*2;
1854
1855 for (i=0; i < (ssize_t) nibbles; i++)
1856 {
1857 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1858 {
1859 if (*sp == '\0')
1860 {
1861 png_warning(ping, "ran out of profile data");
1862 profile=DestroyStringInfo(profile);
1863 return(MagickFalse);
1864 }
1865 sp++;
1866 }
1867
1868 if (i%2 == 0)
1869 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1870
1871 else
1872 (*dp++)+=unhex[(int) *sp++];
1873 }
1874 /*
1875 We have already read "Raw profile type.
1876 */
1877 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1878 profile=DestroyStringInfo(profile);
1879
1880 if (image_info->verbose)
1881 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1882
1883 return MagickTrue;
1884 }
1885
PNGSetExifProfile(Image * image,png_size_t size,png_byte * data,ExceptionInfo * exception)1886 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1887 ExceptionInfo *exception)
1888 {
1889 StringInfo
1890 *profile;
1891
1892 unsigned char
1893 *p;
1894
1895 png_byte
1896 *s;
1897
1898 size_t
1899 i;
1900
1901 profile=BlobToStringInfo((const void *) NULL,size+6);
1902
1903 if (profile == (StringInfo *) NULL)
1904 {
1905 (void) ThrowMagickException(exception,GetMagickModule(),
1906 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1907 image->filename);
1908 return(-1);
1909 }
1910 p=GetStringInfoDatum(profile);
1911
1912 /* Initialize profile with "Exif\0\0" */
1913 *p++ ='E';
1914 *p++ ='x';
1915 *p++ ='i';
1916 *p++ ='f';
1917 *p++ ='\0';
1918 *p++ ='\0';
1919
1920 s=data;
1921 i=0;
1922 if (size > 6)
1923 {
1924 /* Skip first 6 bytes if "Exif\0\0" is
1925 already present by accident
1926 */
1927 if (s[0] == 'E' && s[1] == 'x' && s[2] == 'i' &&
1928 s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1929 {
1930 s+=6;
1931 i=6;
1932 SetStringInfoLength(profile,size);
1933 p=GetStringInfoDatum(profile);
1934 }
1935 }
1936
1937 /* copy chunk->data to profile */
1938 for (; i<size; i++)
1939 *p++ = *s++;
1940
1941 (void) SetImageProfile(image,"exif",profile,exception);
1942
1943 profile=DestroyStringInfo(profile);
1944
1945 return(1);
1946 }
1947
1948 #if defined(PNG_READ_eXIf_SUPPORTED)
read_eXIf_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1949 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1950 ExceptionInfo *exception)
1951 {
1952 png_uint_32
1953 size;
1954
1955 png_bytep
1956 data;
1957
1958 #if PNG_LIBPNG_VER > 10631
1959 if (png_get_eXIf_1(ping,info,&size,&data))
1960 (void) PNGSetExifProfile(image,size,data,exception);
1961 #endif
1962 }
1963 #endif
1964
1965 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1966
read_user_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1967 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1968 {
1969 Image
1970 *image;
1971
1972 PNGErrorInfo
1973 *error_info;
1974
1975 /* The unknown chunk structure contains the chunk data:
1976 png_byte name[5];
1977 png_byte *data;
1978 png_size_t size;
1979
1980 Note that libpng has already taken care of the CRC handling.
1981
1982 Returns one of the following:
1983 return(-n); chunk had an error
1984 return(0); did not recognize
1985 return(n); success
1986 */
1987
1988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1989 " read_user_chunk: found %c%c%c%c chunk",
1990 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1991
1992 if (chunk->name[0] == 101 &&
1993 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1994 chunk->name[2] == 73 &&
1995 chunk-> name[3] == 102)
1996 {
1997 /* process eXIf or exIf chunk */
1998
1999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2000 " recognized eXIf chunk");
2001
2002 image=(Image *) png_get_user_chunk_ptr(ping);
2003
2004 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2005
2006 return(PNGSetExifProfile(image,chunk->size,chunk->data,
2007 error_info->exception));
2008 }
2009
2010 /* orNT */
2011 if (chunk->name[0] == 111 &&
2012 chunk->name[1] == 114 &&
2013 chunk->name[2] == 78 &&
2014 chunk->name[3] == 84)
2015 {
2016 /* recognized orNT */
2017 if (chunk->size != 1)
2018 return(-1); /* Error return */
2019
2020 image=(Image *) png_get_user_chunk_ptr(ping);
2021
2022 image->orientation=
2023 Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2024
2025 return(1);
2026 }
2027
2028 /* vpAg (deprecated, replaced by caNv) */
2029 if (chunk->name[0] == 118 &&
2030 chunk->name[1] == 112 &&
2031 chunk->name[2] == 65 &&
2032 chunk->name[3] == 103)
2033 {
2034 /* recognized vpAg */
2035
2036 if (chunk->size != 9)
2037 return(-1); /* Error return */
2038
2039 if (chunk->data[8] != 0)
2040 return(0); /* ImageMagick requires pixel units */
2041
2042 image=(Image *) png_get_user_chunk_ptr(ping);
2043
2044 image->page.width=(size_t)mng_get_long(chunk->data);
2045 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2046
2047 return(1);
2048 }
2049
2050 /* caNv */
2051 if (chunk->name[0] == 99 &&
2052 chunk->name[1] == 97 &&
2053 chunk->name[2] == 78 &&
2054 chunk->name[3] == 118)
2055 {
2056 /* recognized caNv */
2057
2058 if (chunk->size != 16)
2059 return(-1); /* Error return */
2060
2061 image=(Image *) png_get_user_chunk_ptr(ping);
2062
2063 image->page.width=(size_t) mng_get_long(chunk->data);
2064 image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2065 image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2066 image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2067
2068 return(1);
2069 }
2070
2071 /* acTL */
2072 if ((chunk->name[0] == 97) && (chunk->name[1] == 99) &&
2073 (chunk->name[2] == 84) && (chunk->name[3] == 76))
2074 {
2075 image=(Image *) png_get_user_chunk_ptr(ping);
2076 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2077
2078 (void) SetImageProperty(image,"png:acTL","chunk was found",
2079 error_info->exception);
2080
2081 return(1);
2082 }
2083
2084 return(0); /* Did not recognize */
2085 }
2086 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2087
2088 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)2089 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2090 ExceptionInfo *exception)
2091 {
2092 png_timep
2093 time;
2094
2095 if (png_get_tIME(ping,info,&time))
2096 {
2097 char
2098 timestamp[21];
2099
2100 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2101 time->year,time->month,time->day,time->hour,time->minute,time->second);
2102 SetImageProperty(image,"png:tIME",timestamp,exception);
2103 }
2104 }
2105 #endif
2106
2107 /*
2108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2109 % %
2110 % %
2111 % %
2112 % R e a d O n e P N G I m a g e %
2113 % %
2114 % %
2115 % %
2116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2117 %
2118 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2119 % (minus the 8-byte signature) and returns it. It allocates the memory
2120 % necessary for the new Image structure and returns a pointer to the new
2121 % image.
2122 %
2123 % The format of the ReadOnePNGImage method is:
2124 %
2125 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2126 % ExceptionInfo *exception)
2127 %
2128 % A description of each parameter follows:
2129 %
2130 % o mng_info: Specifies a pointer to a MngInfo structure.
2131 %
2132 % o image_info: the image info.
2133 %
2134 % o exception: return any errors or warnings in this structure.
2135 %
2136 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)2137 static Image *ReadOnePNGImage(MngInfo *mng_info,
2138 const ImageInfo *image_info, ExceptionInfo *exception)
2139 {
2140 /* Read one PNG image */
2141
2142 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2143
2144 Image
2145 *image;
2146
2147 char
2148 im_vers[32],
2149 libpng_runv[32],
2150 libpng_vers[32],
2151 zlib_runv[32],
2152 zlib_vers[32];
2153
2154 int
2155 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2156 num_raw_profiles,
2157 num_text,
2158 num_text_total,
2159 num_passes,
2160 number_colors,
2161 pass,
2162 ping_bit_depth,
2163 ping_color_type,
2164 ping_file_depth,
2165 ping_interlace_method,
2166 ping_compression_method,
2167 ping_filter_method,
2168 ping_num_trans,
2169 unit_type;
2170
2171 double
2172 file_gamma;
2173
2174 MagickBooleanType
2175 logging,
2176 ping_found_cHRM,
2177 ping_found_gAMA,
2178 ping_found_iCCP,
2179 ping_found_sRGB,
2180 ping_found_sRGB_cHRM,
2181 ping_preserve_iCCP,
2182 status;
2183
2184 MemoryInfo
2185 *volatile pixel_info;
2186
2187 PixelInfo
2188 transparent_color;
2189
2190 PNGErrorInfo
2191 error_info;
2192
2193 png_bytep
2194 ping_trans_alpha;
2195
2196 png_color_16p
2197 ping_background,
2198 ping_trans_color;
2199
2200 png_info
2201 *end_info,
2202 *ping_info;
2203
2204 png_struct
2205 *ping;
2206
2207 png_textp
2208 text;
2209
2210 png_uint_32
2211 ping_height,
2212 ping_width,
2213 x_resolution,
2214 y_resolution;
2215
2216 QuantumInfo
2217 *volatile quantum_info;
2218
2219 Quantum
2220 *volatile quantum_scanline;
2221
2222 ssize_t
2223 ping_rowbytes,
2224 y;
2225
2226 register unsigned char
2227 *p;
2228
2229 register ssize_t
2230 i,
2231 x;
2232
2233 register Quantum
2234 *q;
2235
2236 size_t
2237 length,
2238 row_offset;
2239
2240 ssize_t
2241 j;
2242
2243 unsigned char
2244 *ping_pixels;
2245
2246 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2247 png_byte unused_chunks[]=
2248 {
2249 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2250 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2251 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2252 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2253 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2254 #if !defined(PNG_tIME_SUPPORTED)
2255 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2256 #endif
2257 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2258 /* ignore the APNG chunks */
2259 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2260 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2261 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2262 #endif
2263 };
2264 #endif
2265
2266 /* Define these outside of the following "if logging()" block so they will
2267 * show in debuggers.
2268 */
2269 *im_vers='\0';
2270 (void) ConcatenateMagickString(im_vers,
2271 MagickLibVersionText,32);
2272 (void) ConcatenateMagickString(im_vers,
2273 MagickLibAddendum,32);
2274
2275 *libpng_vers='\0';
2276 (void) ConcatenateMagickString(libpng_vers,
2277 PNG_LIBPNG_VER_STRING,32);
2278 *libpng_runv='\0';
2279 (void) ConcatenateMagickString(libpng_runv,
2280 png_get_libpng_ver(NULL),32);
2281
2282 *zlib_vers='\0';
2283 (void) ConcatenateMagickString(zlib_vers,
2284 ZLIB_VERSION,32);
2285 *zlib_runv='\0';
2286 (void) ConcatenateMagickString(zlib_runv,
2287 zlib_version,32);
2288
2289 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2290 " Enter ReadOnePNGImage()\n"
2291 " IM version = %s\n"
2292 " Libpng version = %s",
2293 im_vers, libpng_vers);
2294
2295 if (logging != MagickFalse)
2296 {
2297 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2298 {
2299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2300 " running with %s", libpng_runv);
2301 }
2302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2303 " Zlib version = %s", zlib_vers);
2304 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2305 {
2306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2307 " running with %s", zlib_runv);
2308 }
2309 }
2310
2311 #if (PNG_LIBPNG_VER < 10200)
2312 if (image_info->verbose)
2313 printf("Your PNG library (libpng-%s) is rather old.\n",
2314 PNG_LIBPNG_VER_STRING);
2315 #endif
2316
2317 #if (PNG_LIBPNG_VER >= 10400)
2318 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2319 if (image_info->verbose)
2320 {
2321 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2322 PNG_LIBPNG_VER_STRING);
2323 printf("Please update it.\n");
2324 }
2325 # endif
2326 #endif
2327
2328
2329 quantum_info = (QuantumInfo *) NULL;
2330 image=mng_info->image;
2331
2332 if (logging != MagickFalse)
2333 {
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2335 " Before reading:\n"
2336 " image->alpha_trait=%d\n"
2337 " image->rendering_intent=%d\n"
2338 " image->colorspace=%d\n"
2339 " image->gamma=%f",
2340 (int) image->alpha_trait, (int) image->rendering_intent,
2341 (int) image->colorspace, image->gamma);
2342 }
2343 intent=
2344 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2345
2346 /* Set to an out-of-range color unless tRNS chunk is present */
2347 transparent_color.red=65537;
2348 transparent_color.green=65537;
2349 transparent_color.blue=65537;
2350 transparent_color.alpha=65537;
2351
2352 number_colors=0;
2353 num_text = 0;
2354 num_text_total = 0;
2355 num_raw_profiles = 0;
2356
2357 ping_found_cHRM = MagickFalse;
2358 ping_found_gAMA = MagickFalse;
2359 ping_found_iCCP = MagickFalse;
2360 ping_found_sRGB = MagickFalse;
2361 ping_found_sRGB_cHRM = MagickFalse;
2362 ping_preserve_iCCP = MagickFalse;
2363
2364
2365 /*
2366 Allocate the PNG structures
2367 */
2368 #ifdef PNG_USER_MEM_SUPPORTED
2369 error_info.image=image;
2370 error_info.exception=exception;
2371 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2372 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2373 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2374 #else
2375 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2376 MagickPNGErrorHandler,MagickPNGWarningHandler);
2377 #endif
2378 if (ping == (png_struct *) NULL)
2379 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2380
2381 ping_info=png_create_info_struct(ping);
2382
2383 if (ping_info == (png_info *) NULL)
2384 {
2385 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2386 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2387 }
2388
2389 end_info=png_create_info_struct(ping);
2390
2391 if (end_info == (png_info *) NULL)
2392 {
2393 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2394 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2395 }
2396
2397 pixel_info=(MemoryInfo *) NULL;
2398 quantum_scanline = (Quantum *) NULL;
2399 quantum_info = (QuantumInfo *) NULL;
2400
2401 if (setjmp(png_jmpbuf(ping)))
2402 {
2403 /*
2404 PNG image is corrupt.
2405 */
2406 png_destroy_read_struct(&ping,&ping_info,&end_info);
2407
2408 if (pixel_info != (MemoryInfo *) NULL)
2409 pixel_info=RelinquishVirtualMemory(pixel_info);
2410
2411 if (quantum_info != (QuantumInfo *) NULL)
2412 quantum_info=DestroyQuantumInfo(quantum_info);
2413
2414 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2415
2416 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2417 UnlockSemaphoreInfo(ping_semaphore);
2418 #endif
2419
2420 if (logging != MagickFalse)
2421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422 " exit ReadOnePNGImage() with error.");
2423
2424 if (image != (Image *) NULL)
2425 image=DestroyImageList(image);
2426 return(image);
2427 }
2428
2429 /* { For navigation to end of SETJMP-protected block. Within this
2430 * block, use png_error() instead of Throwing an Exception, to ensure
2431 * that libpng is able to clean up, and that the semaphore is unlocked.
2432 */
2433
2434 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2435 LockSemaphoreInfo(ping_semaphore);
2436 #endif
2437
2438 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2439 /* Allow benign errors */
2440 png_set_benign_errors(ping, 1);
2441 #endif
2442
2443 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2444 {
2445 const char
2446 *option;
2447
2448 /* Reject images with too many rows or columns */
2449 png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2450 GetMagickResourceLimit(WidthResource)),(png_uint_32)
2451 MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2452
2453 #if (PNG_LIBPNG_VER >= 10400)
2454 option=GetImageOption(image_info,"png:chunk-cache-max");
2455 if (option != (const char *) NULL)
2456 png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2457 StringToLong(option)));
2458 else
2459 png_set_chunk_cache_max(ping,32767);
2460 #endif
2461
2462 #if (PNG_LIBPNG_VER >= 10401)
2463 option=GetImageOption(image_info,"png:chunk-malloc-max");
2464 if (option != (const char *) NULL)
2465 png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2466 (size_t) StringToLong(option)));
2467 else
2468 png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2469 #endif
2470 }
2471 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2472
2473 /*
2474 Prepare PNG for reading.
2475 */
2476
2477 mng_info->image_found++;
2478 png_set_sig_bytes(ping,8);
2479
2480 if (LocaleCompare(image_info->magick,"MNG") == 0)
2481 {
2482 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2483 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2484 png_set_read_fn(ping,image,png_get_data);
2485 #else
2486 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2487 png_permit_empty_plte(ping,MagickTrue);
2488 png_set_read_fn(ping,image,png_get_data);
2489 #else
2490 mng_info->image=image;
2491 mng_info->bytes_in_read_buffer=0;
2492 mng_info->found_empty_plte=MagickFalse;
2493 mng_info->have_saved_bkgd_index=MagickFalse;
2494 png_set_read_fn(ping,mng_info,mng_get_data);
2495 #endif
2496 #endif
2497 }
2498
2499 else
2500 png_set_read_fn(ping,image,png_get_data);
2501
2502 {
2503 const char
2504 *value;
2505
2506 value=GetImageOption(image_info,"png:ignore-crc");
2507 if (value != NULL)
2508 {
2509 /* Turn off CRC checking while reading */
2510 png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2511 #ifdef PNG_IGNORE_ADLER32
2512 /* Turn off ADLER32 checking while reading */
2513 png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2514 #endif
2515 }
2516
2517 value=GetImageOption(image_info,"profile:skip");
2518
2519 if (IsOptionMember("ICC",value) == MagickFalse)
2520 {
2521
2522 value=GetImageOption(image_info,"png:preserve-iCCP");
2523
2524 if (value == NULL)
2525 value=GetImageArtifact(image,"png:preserve-iCCP");
2526
2527 if (value != NULL)
2528 ping_preserve_iCCP=MagickTrue;
2529
2530 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2531 /* Don't let libpng check for ICC/sRGB profile because we're going
2532 * to do that anyway. This feature was added at libpng-1.6.12.
2533 * If logging, go ahead and check and issue a warning as appropriate.
2534 */
2535 if (logging == MagickFalse)
2536 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2537 #endif
2538 }
2539 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2540 else
2541 {
2542 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2543 }
2544 #endif
2545 }
2546 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2547 /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2548 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2549 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2550 # else
2551 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2552 # endif
2553 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2554 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2555 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2556 (int)sizeof(unused_chunks)/5);
2557 /* Callback for other unknown chunks */
2558 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2559 #endif
2560
2561 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2562 /* Disable new libpng-1.5.10 feature */
2563 png_set_check_for_invalid_index (ping, 0);
2564 #endif
2565
2566 #if (PNG_LIBPNG_VER < 10400)
2567 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2568 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2569 /* Disable thread-unsafe features of pnggccrd */
2570 if (png_access_version_number() >= 10200)
2571 {
2572 png_uint_32 mmx_disable_mask=0;
2573 png_uint_32 asm_flags;
2574
2575 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2576 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2577 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2578 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2579 asm_flags=png_get_asm_flags(ping);
2580 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2581 }
2582 # endif
2583 #endif
2584
2585 png_read_info(ping,ping_info);
2586
2587 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2588 &ping_bit_depth,&ping_color_type,
2589 &ping_interlace_method,&ping_compression_method,
2590 &ping_filter_method);
2591
2592 ping_file_depth = ping_bit_depth;
2593
2594 /* Swap bytes if requested */
2595 if (ping_file_depth == 16)
2596 {
2597 const char
2598 *value;
2599
2600 value=GetImageOption(image_info,"png:swap-bytes");
2601
2602 if (value == NULL)
2603 value=GetImageArtifact(image,"png:swap-bytes");
2604
2605 if (value != NULL)
2606 png_set_swap(ping);
2607 }
2608
2609 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2610 {
2611 char
2612 msg[MagickPathExtent];
2613
2614 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2615 (int) ping_color_type);
2616 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2617
2618 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2619 (int) ping_bit_depth);
2620 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2621 }
2622
2623 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2624 &ping_trans_color);
2625
2626 (void) png_get_bKGD(ping, ping_info, &ping_background);
2627
2628 if (ping_bit_depth < 8)
2629 {
2630 png_set_packing(ping);
2631 ping_bit_depth = 8;
2632 }
2633
2634 image->depth=ping_bit_depth;
2635 image->depth=GetImageQuantumDepth(image,MagickFalse);
2636 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2637
2638 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2639 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2640 {
2641 image->rendering_intent=UndefinedIntent;
2642 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2643 (void) memset(&image->chromaticity,0,
2644 sizeof(image->chromaticity));
2645 }
2646
2647 if (logging != MagickFalse)
2648 {
2649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2650 " PNG width: %.20g, height: %.20g\n"
2651 " PNG color_type: %d, bit_depth: %d\n"
2652 " PNG compression_method: %d\n"
2653 " PNG interlace_method: %d, filter_method: %d",
2654 (double) ping_width, (double) ping_height,
2655 ping_color_type, ping_bit_depth,
2656 ping_compression_method,
2657 ping_interlace_method,ping_filter_method);
2658
2659 }
2660
2661 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2662 {
2663 ping_found_iCCP=MagickTrue;
2664 if (logging != MagickFalse)
2665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2666 " Found PNG iCCP chunk.");
2667 }
2668
2669 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2670 {
2671 ping_found_gAMA=MagickTrue;
2672 if (logging != MagickFalse)
2673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674 " Found PNG gAMA chunk.");
2675 }
2676
2677 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2678 {
2679 ping_found_cHRM=MagickTrue;
2680 if (logging != MagickFalse)
2681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682 " Found PNG cHRM chunk.");
2683 }
2684
2685 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2686 PNG_INFO_sRGB))
2687 {
2688 ping_found_sRGB=MagickTrue;
2689 if (logging != MagickFalse)
2690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691 " Found PNG sRGB chunk.");
2692 }
2693
2694 #ifdef PNG_READ_iCCP_SUPPORTED
2695 if (ping_found_iCCP !=MagickTrue &&
2696 ping_found_sRGB != MagickTrue &&
2697 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2698 {
2699 ping_found_iCCP=MagickTrue;
2700 if (logging != MagickFalse)
2701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2702 " Found PNG iCCP chunk.");
2703 }
2704
2705 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2706 {
2707 int
2708 compression;
2709
2710 #if (PNG_LIBPNG_VER < 10500)
2711 png_charp
2712 info;
2713 #else
2714 png_bytep
2715 info;
2716 #endif
2717
2718 png_charp
2719 name;
2720
2721 png_uint_32
2722 profile_length;
2723
2724 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2725 &profile_length);
2726
2727 if (profile_length != 0)
2728 {
2729 StringInfo
2730 *profile;
2731
2732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG iCCP chunk.");
2735
2736 profile=BlobToStringInfo(info,(const size_t) profile_length);
2737
2738 if (profile == (StringInfo *) NULL)
2739 {
2740 png_warning(ping, "ICC profile is NULL");
2741 profile=DestroyStringInfo(profile);
2742 }
2743 else
2744 {
2745 if (ping_preserve_iCCP == MagickFalse)
2746 {
2747 int
2748 icheck,
2749 got_crc=0;
2750
2751
2752 png_uint_32
2753 profile_crc=0;
2754
2755 unsigned char
2756 *data;
2757
2758 profile_length=(png_uint_32) GetStringInfoLength(profile);
2759
2760 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2761 {
2762 if (profile_length == sRGB_info[icheck].len)
2763 {
2764 if (got_crc == 0)
2765 {
2766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2767 " Got a %lu-byte ICC profile (potentially sRGB)",
2768 (unsigned long) profile_length);
2769
2770 data=GetStringInfoDatum(profile);
2771 profile_crc=crc32(0,data,profile_length);
2772
2773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774 " with crc=%8x",(unsigned int) profile_crc);
2775 got_crc++;
2776 }
2777
2778 if (profile_crc == sRGB_info[icheck].crc)
2779 {
2780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2781 " It is sRGB with rendering intent = %s",
2782 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2783 sRGB_info[icheck].intent));
2784 if (image->rendering_intent==UndefinedIntent)
2785 {
2786 image->rendering_intent=
2787 Magick_RenderingIntent_from_PNG_RenderingIntent(
2788 sRGB_info[icheck].intent);
2789 }
2790 break;
2791 }
2792 }
2793 }
2794 if (sRGB_info[icheck].len == 0)
2795 {
2796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2797 " Got %lu-byte ICC profile not recognized as sRGB",
2798 (unsigned long) profile_length);
2799 (void) SetImageProfile(image,"icc",profile,exception);
2800 }
2801 }
2802 else /* Preserve-iCCP */
2803 {
2804 (void) SetImageProfile(image,"icc",profile,exception);
2805 }
2806
2807 profile=DestroyStringInfo(profile);
2808 }
2809 }
2810 }
2811 #endif
2812
2813 #if defined(PNG_READ_sRGB_SUPPORTED)
2814 {
2815 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2816 PNG_INFO_sRGB))
2817 {
2818 if (png_get_sRGB(ping,ping_info,&intent))
2819 {
2820 if (image->rendering_intent == UndefinedIntent)
2821 image->rendering_intent=
2822 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2823
2824 if (logging != MagickFalse)
2825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2826 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2827 }
2828 }
2829
2830 else if (mng_info->have_global_srgb)
2831 {
2832 if (image->rendering_intent == UndefinedIntent)
2833 image->rendering_intent=
2834 Magick_RenderingIntent_from_PNG_RenderingIntent
2835 (mng_info->global_srgb_intent);
2836 }
2837 }
2838 #endif
2839
2840
2841 {
2842 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2843 if (mng_info->have_global_gama)
2844 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2845
2846 if (png_get_gAMA(ping,ping_info,&file_gamma))
2847 {
2848 image->gamma=(float) file_gamma;
2849 if (logging != MagickFalse)
2850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2851 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2852 }
2853 }
2854
2855 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2856 {
2857 if (mng_info->have_global_chrm != MagickFalse)
2858 {
2859 (void) png_set_cHRM(ping,ping_info,
2860 mng_info->global_chrm.white_point.x,
2861 mng_info->global_chrm.white_point.y,
2862 mng_info->global_chrm.red_primary.x,
2863 mng_info->global_chrm.red_primary.y,
2864 mng_info->global_chrm.green_primary.x,
2865 mng_info->global_chrm.green_primary.y,
2866 mng_info->global_chrm.blue_primary.x,
2867 mng_info->global_chrm.blue_primary.y);
2868 }
2869 }
2870
2871 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2872 {
2873 (void) png_get_cHRM(ping,ping_info,
2874 &image->chromaticity.white_point.x,
2875 &image->chromaticity.white_point.y,
2876 &image->chromaticity.red_primary.x,
2877 &image->chromaticity.red_primary.y,
2878 &image->chromaticity.green_primary.x,
2879 &image->chromaticity.green_primary.y,
2880 &image->chromaticity.blue_primary.x,
2881 &image->chromaticity.blue_primary.y);
2882
2883 ping_found_cHRM=MagickTrue;
2884
2885 if (image->chromaticity.red_primary.x>0.6399f &&
2886 image->chromaticity.red_primary.x<0.6401f &&
2887 image->chromaticity.red_primary.y>0.3299f &&
2888 image->chromaticity.red_primary.y<0.3301f &&
2889 image->chromaticity.green_primary.x>0.2999f &&
2890 image->chromaticity.green_primary.x<0.3001f &&
2891 image->chromaticity.green_primary.y>0.5999f &&
2892 image->chromaticity.green_primary.y<0.6001f &&
2893 image->chromaticity.blue_primary.x>0.1499f &&
2894 image->chromaticity.blue_primary.x<0.1501f &&
2895 image->chromaticity.blue_primary.y>0.0599f &&
2896 image->chromaticity.blue_primary.y<0.0601f &&
2897 image->chromaticity.white_point.x>0.3126f &&
2898 image->chromaticity.white_point.x<0.3128f &&
2899 image->chromaticity.white_point.y>0.3289f &&
2900 image->chromaticity.white_point.y<0.3291f)
2901 ping_found_sRGB_cHRM=MagickTrue;
2902 }
2903
2904 if (image->rendering_intent != UndefinedIntent)
2905 {
2906 if (ping_found_sRGB != MagickTrue &&
2907 (ping_found_gAMA != MagickTrue ||
2908 (image->gamma > .45 && image->gamma < .46)) &&
2909 (ping_found_cHRM != MagickTrue ||
2910 ping_found_sRGB_cHRM != MagickFalse) &&
2911 ping_found_iCCP != MagickTrue)
2912 {
2913 png_set_sRGB(ping,ping_info,
2914 Magick_RenderingIntent_to_PNG_RenderingIntent
2915 (image->rendering_intent));
2916 file_gamma=0.45455f;
2917 ping_found_sRGB=MagickTrue;
2918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2919 " Setting sRGB as if in input");
2920 }
2921 }
2922
2923 #if defined(PNG_oFFs_SUPPORTED)
2924 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2925 {
2926 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2927 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2928
2929 if (logging != MagickFalse)
2930 if (image->page.x || image->page.y)
2931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2932 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2933 image->page.x,(double) image->page.y);
2934 }
2935 #endif
2936 #if defined(PNG_pHYs_SUPPORTED)
2937 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2938 {
2939 if (mng_info->have_global_phys)
2940 {
2941 png_set_pHYs(ping,ping_info,
2942 mng_info->global_x_pixels_per_unit,
2943 mng_info->global_y_pixels_per_unit,
2944 mng_info->global_phys_unit_type);
2945 }
2946 }
2947
2948 x_resolution=0;
2949 y_resolution=0;
2950 unit_type=0;
2951 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2952 {
2953 /*
2954 Set image resolution.
2955 */
2956 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2957 &unit_type);
2958 image->resolution.x=(double) x_resolution;
2959 image->resolution.y=(double) y_resolution;
2960
2961 if (unit_type == PNG_RESOLUTION_METER)
2962 {
2963 image->units=PixelsPerCentimeterResolution;
2964 image->resolution.x=(double) x_resolution/100.0;
2965 image->resolution.y=(double) y_resolution/100.0;
2966 }
2967
2968 if (logging != MagickFalse)
2969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2970 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2971 (double) x_resolution,(double) y_resolution,unit_type);
2972 }
2973 #endif
2974
2975 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2976 {
2977 png_colorp
2978 palette;
2979
2980 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2981
2982 if ((number_colors == 0) &&
2983 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2984 {
2985 if (mng_info->global_plte_length)
2986 {
2987 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2988 (int) mng_info->global_plte_length);
2989
2990 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2991 {
2992 if (mng_info->global_trns_length)
2993 {
2994 png_warning(ping,
2995 "global tRNS has more entries than global PLTE");
2996 }
2997 else
2998 {
2999 png_set_tRNS(ping,ping_info,mng_info->global_trns,
3000 (int) mng_info->global_trns_length,NULL);
3001 }
3002 }
3003 #ifdef PNG_READ_bKGD_SUPPORTED
3004 if (
3005 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3006 mng_info->have_saved_bkgd_index ||
3007 #endif
3008 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3009 {
3010 png_color_16
3011 background;
3012
3013 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3014 if (mng_info->have_saved_bkgd_index)
3015 background.index=mng_info->saved_bkgd_index;
3016 #endif
3017 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3018 background.index=ping_background->index;
3019
3020 background.red=(png_uint_16)
3021 mng_info->global_plte[background.index].red;
3022
3023 background.green=(png_uint_16)
3024 mng_info->global_plte[background.index].green;
3025
3026 background.blue=(png_uint_16)
3027 mng_info->global_plte[background.index].blue;
3028
3029 background.gray=(png_uint_16)
3030 mng_info->global_plte[background.index].green;
3031
3032 png_set_bKGD(ping,ping_info,&background);
3033 }
3034 #endif
3035 }
3036 else
3037 png_error(ping,"No global PLTE in file");
3038 }
3039 }
3040
3041 #ifdef PNG_READ_bKGD_SUPPORTED
3042 if (mng_info->have_global_bkgd &&
3043 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3044 image->background_color=mng_info->mng_global_bkgd;
3045
3046 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3047 {
3048 unsigned int
3049 bkgd_scale;
3050
3051 /* Set image background color.
3052 * Scale background components to 16-bit, then scale
3053 * to quantum depth
3054 */
3055
3056 bkgd_scale = 1;
3057
3058 if (ping_file_depth == 1)
3059 bkgd_scale = 255;
3060
3061 else if (ping_file_depth == 2)
3062 bkgd_scale = 85;
3063
3064 else if (ping_file_depth == 4)
3065 bkgd_scale = 17;
3066
3067 if (ping_file_depth <= 8)
3068 bkgd_scale *= 257;
3069
3070 ping_background->red *= bkgd_scale;
3071 ping_background->green *= bkgd_scale;
3072 ping_background->blue *= bkgd_scale;
3073
3074 if (logging != MagickFalse)
3075 {
3076 if (logging != MagickFalse)
3077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3078 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3079 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3080 ping_background->red,ping_background->green,
3081 ping_background->blue,
3082 bkgd_scale,ping_background->red,
3083 ping_background->green,ping_background->blue);
3084 }
3085
3086 image->background_color.red=
3087 ScaleShortToQuantum(ping_background->red);
3088
3089 image->background_color.green=
3090 ScaleShortToQuantum(ping_background->green);
3091
3092 image->background_color.blue=
3093 ScaleShortToQuantum(ping_background->blue);
3094
3095 image->background_color.alpha=OpaqueAlpha;
3096
3097 if (logging != MagickFalse)
3098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3099 " image->background_color=(%.20g,%.20g,%.20g).",
3100 (double) image->background_color.red,
3101 (double) image->background_color.green,
3102 (double) image->background_color.blue);
3103 }
3104 #endif /* PNG_READ_bKGD_SUPPORTED */
3105
3106 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3107 {
3108 /*
3109 Image has a tRNS chunk.
3110 */
3111 int
3112 max_sample;
3113
3114 size_t
3115 one = 1;
3116
3117 if (logging != MagickFalse)
3118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3119 " Reading PNG tRNS chunk.");
3120
3121 max_sample = (int) ((one << ping_file_depth) - 1);
3122
3123 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3124 (int)ping_trans_color->gray > max_sample) ||
3125 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3126 ((int)ping_trans_color->red > max_sample ||
3127 (int)ping_trans_color->green > max_sample ||
3128 (int)ping_trans_color->blue > max_sample)))
3129 {
3130 if (logging != MagickFalse)
3131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3132 " Ignoring PNG tRNS chunk with out-of-range sample.");
3133 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3134 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3135 image->alpha_trait=UndefinedPixelTrait;
3136 }
3137 else
3138 {
3139 int
3140 scale_to_short;
3141
3142 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3143
3144 /* Scale transparent_color to short */
3145 transparent_color.red= scale_to_short*ping_trans_color->red;
3146 transparent_color.green= scale_to_short*ping_trans_color->green;
3147 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3148 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3149
3150 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3151 {
3152 if (logging != MagickFalse)
3153 {
3154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3155 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3156 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3157
3158 }
3159 transparent_color.red=transparent_color.alpha;
3160 transparent_color.green=transparent_color.alpha;
3161 transparent_color.blue=transparent_color.alpha;
3162 }
3163 }
3164 }
3165 #if defined(PNG_READ_sBIT_SUPPORTED)
3166 if (mng_info->have_global_sbit)
3167 {
3168 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3169 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3170 }
3171 #endif
3172 num_passes=png_set_interlace_handling(ping);
3173
3174 png_read_update_info(ping,ping_info);
3175
3176 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3177
3178 /*
3179 Initialize image structure.
3180 */
3181 mng_info->image_box.left=0;
3182 mng_info->image_box.right=(ssize_t) ping_width;
3183 mng_info->image_box.top=0;
3184 mng_info->image_box.bottom=(ssize_t) ping_height;
3185 if (mng_info->mng_type == 0)
3186 {
3187 mng_info->mng_width=ping_width;
3188 mng_info->mng_height=ping_height;
3189 mng_info->frame=mng_info->image_box;
3190 mng_info->clip=mng_info->image_box;
3191 }
3192
3193 else
3194 {
3195 image->page.y=mng_info->y_off[mng_info->object_id];
3196 }
3197
3198 image->compression=ZipCompression;
3199 image->columns=ping_width;
3200 image->rows=ping_height;
3201
3202 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3203 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3204 {
3205 double
3206 image_gamma = image->gamma;
3207
3208 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3209 " image->gamma=%f",(float) image_gamma);
3210
3211 if (image_gamma > 0.75)
3212 {
3213 /* Set image->rendering_intent to Undefined,
3214 * image->colorspace to GRAY, and reset image->chromaticity.
3215 */
3216 image->intensity = Rec709LuminancePixelIntensityMethod;
3217 SetImageColorspace(image,LinearGRAYColorspace,exception);
3218 }
3219 else
3220 {
3221 RenderingIntent
3222 save_rendering_intent = image->rendering_intent;
3223 ChromaticityInfo
3224 save_chromaticity = image->chromaticity;
3225
3226 SetImageColorspace(image,GRAYColorspace,exception);
3227 image->rendering_intent = save_rendering_intent;
3228 image->chromaticity = save_chromaticity;
3229 }
3230
3231 image->gamma = image_gamma;
3232 }
3233 else
3234 {
3235 double
3236 image_gamma = image->gamma;
3237
3238 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " image->gamma=%f",(float) image_gamma);
3240
3241 if (image_gamma > 0.75)
3242 {
3243 /* Set image->rendering_intent to Undefined,
3244 * image->colorspace to GRAY, and reset image->chromaticity.
3245 */
3246 image->intensity = Rec709LuminancePixelIntensityMethod;
3247 SetImageColorspace(image,RGBColorspace,exception);
3248 }
3249 else
3250 {
3251 RenderingIntent
3252 save_rendering_intent = image->rendering_intent;
3253 ChromaticityInfo
3254 save_chromaticity = image->chromaticity;
3255
3256 SetImageColorspace(image,sRGBColorspace,exception);
3257 image->rendering_intent = save_rendering_intent;
3258 image->chromaticity = save_chromaticity;
3259 }
3260
3261 image->gamma = image_gamma;
3262 }
3263
3264 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3265 " image->colorspace=%d",(int) image->colorspace);
3266
3267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3268 ((int) ping_bit_depth < 16 &&
3269 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3270 {
3271 size_t
3272 one;
3273
3274 image->storage_class=PseudoClass;
3275 one=1;
3276 image->colors=one << ping_file_depth;
3277 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3278 if (image->colors > 256)
3279 image->colors=256;
3280 #else
3281 if (image->colors > 65536L)
3282 image->colors=65536L;
3283 #endif
3284 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3285 {
3286 png_colorp
3287 palette;
3288
3289 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3290 image->colors=(size_t) number_colors;
3291
3292 if (logging != MagickFalse)
3293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3295 }
3296 }
3297
3298 if (image->storage_class == PseudoClass)
3299 {
3300 /*
3301 Initialize image colormap.
3302 */
3303 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3304 png_error(ping,"Memory allocation failed");
3305
3306 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3307 {
3308 png_colorp
3309 palette;
3310
3311 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3312
3313 for (i=0; i < (ssize_t) number_colors; i++)
3314 {
3315 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3316 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3317 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3318 }
3319
3320 for ( ; i < (ssize_t) image->colors; i++)
3321 {
3322 image->colormap[i].red=0;
3323 image->colormap[i].green=0;
3324 image->colormap[i].blue=0;
3325 }
3326 }
3327 }
3328
3329 /* Set some properties for reporting by "identify" */
3330 {
3331 char
3332 msg[MagickPathExtent];
3333
3334 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3335 ping_interlace_method in value */
3336
3337 (void) FormatLocaleString(msg,MagickPathExtent,
3338 "%d, %d",(int) ping_width, (int) ping_height);
3339 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3340
3341 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3342 (int) ping_file_depth);
3343 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3344
3345 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3346 (int) ping_color_type,
3347 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3348 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3349
3350 if (ping_interlace_method == 0)
3351 {
3352 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3353 (int) ping_interlace_method);
3354 }
3355 else if (ping_interlace_method == 1)
3356 {
3357 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3358 (int) ping_interlace_method);
3359 }
3360 else
3361 {
3362 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3363 (int) ping_interlace_method);
3364 }
3365 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3366 msg,exception);
3367
3368 if (number_colors != 0)
3369 {
3370 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3371 (int) number_colors);
3372 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3373 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 {
3468 for (pass=0; pass < num_passes; pass++)
3469 {
3470 /*
3471 Convert image to DirectClass pixel packets.
3472 */
3473 image->alpha_trait=
3474 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3475 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3476 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3477 BlendPixelTrait : UndefinedPixelTrait;
3478
3479 for (y=0; y < (ssize_t) image->rows; y++)
3480 {
3481 if (num_passes > 1)
3482 row_offset=ping_rowbytes*y;
3483
3484 else
3485 row_offset=0;
3486
3487 png_read_row(ping,ping_pixels+row_offset,NULL);
3488
3489 if (pass < num_passes-1)
3490 continue;
3491
3492 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3493
3494 if (q == (Quantum *) NULL)
3495 break;
3496
3497 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3498 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3499 GrayQuantum,ping_pixels+row_offset,exception);
3500
3501 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3502 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3503 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3504
3505 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3506 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3507 RGBAQuantum,ping_pixels+row_offset,exception);
3508
3509 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3510 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3511 IndexQuantum,ping_pixels+row_offset,exception);
3512
3513 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3514 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3515 RGBQuantum,ping_pixels+row_offset,exception);
3516
3517 if (found_transparent_pixel == MagickFalse)
3518 {
3519 /* Is there a transparent pixel in the row? */
3520 if (y== 0 && logging != MagickFalse)
3521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522 " Looking for cheap transparent pixel");
3523
3524 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3525 {
3526 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3527 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3528 (GetPixelAlpha(image,q) != OpaqueAlpha))
3529 {
3530 if (logging != MagickFalse)
3531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " ...got one.");
3533
3534 found_transparent_pixel = MagickTrue;
3535 break;
3536 }
3537 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3538 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3539 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3540 transparent_color.red &&
3541 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3542 transparent_color.green &&
3543 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3544 transparent_color.blue))
3545 {
3546 if (logging != MagickFalse)
3547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3548 " ...got one.");
3549 found_transparent_pixel = MagickTrue;
3550 break;
3551 }
3552 q+=GetPixelChannels(image);
3553 }
3554 }
3555
3556 if (num_passes == 1)
3557 {
3558 status=SetImageProgress(image,LoadImageTag,
3559 (MagickOffsetType) y, image->rows);
3560
3561 if (status == MagickFalse)
3562 break;
3563 }
3564 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3565 break;
3566 }
3567 if (y < (long) image->rows)
3568 break;
3569
3570 if (num_passes != 1)
3571 {
3572 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3573 if (status == MagickFalse)
3574 break;
3575 }
3576 }
3577 }
3578
3579 else /* image->storage_class != DirectClass */
3580
3581 for (pass=0; pass < num_passes; pass++)
3582 {
3583 register Quantum
3584 *r;
3585
3586 /*
3587 Convert grayscale image to PseudoClass pixel packets.
3588 */
3589 if (logging != MagickFalse)
3590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3591 " Converting grayscale pixels to pixel packets");
3592
3593 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3594 BlendPixelTrait : UndefinedPixelTrait;
3595
3596 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3597 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3598 sizeof(*quantum_scanline));
3599
3600 if (quantum_scanline == (Quantum *) NULL)
3601 png_error(ping,"Memory allocation failed");
3602
3603 for (y=0; y < (ssize_t) image->rows; y++)
3604 {
3605 Quantum
3606 alpha;
3607
3608 if (num_passes > 1)
3609 row_offset=ping_rowbytes*y;
3610
3611 else
3612 row_offset=0;
3613
3614 png_read_row(ping,ping_pixels+row_offset,NULL);
3615
3616 if (pass < num_passes-1)
3617 continue;
3618
3619 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3620
3621 if (q == (Quantum *) NULL)
3622 break;
3623
3624 p=ping_pixels+row_offset;
3625 r=quantum_scanline;
3626
3627 switch (ping_bit_depth)
3628 {
3629 case 8:
3630 {
3631
3632 if (ping_color_type == 4)
3633 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3634 {
3635 *r++=*p++;
3636
3637 alpha=ScaleCharToQuantum((unsigned char)*p++);
3638
3639 SetPixelAlpha(image,alpha,q);
3640
3641 if (alpha != OpaqueAlpha)
3642 found_transparent_pixel = MagickTrue;
3643
3644 q+=GetPixelChannels(image);
3645 }
3646
3647 else
3648 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3649 *r++=*p++;
3650
3651 break;
3652 }
3653
3654 case 16:
3655 {
3656 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3657 {
3658 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3659 unsigned long
3660 quantum;
3661
3662 if (image->colors > 256)
3663 quantum=(((unsigned int) *p++) << 8);
3664
3665 else
3666 quantum=0;
3667
3668 quantum|=(*p++);
3669 *r=ScaleShortToQuantum(quantum);
3670 r++;
3671
3672 if (ping_color_type == 4)
3673 {
3674 if (image->colors > 256)
3675 quantum=(((unsigned int) *p++) << 8);
3676 else
3677 quantum=0;
3678
3679 quantum|=(*p++);
3680
3681 alpha=ScaleShortToQuantum(quantum);
3682 SetPixelAlpha(image,alpha,q);
3683
3684 if (alpha != OpaqueAlpha)
3685 found_transparent_pixel = MagickTrue;
3686
3687 q+=GetPixelChannels(image);
3688 }
3689
3690 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3691 *r++=(*p++);
3692 p++; /* strip low byte */
3693
3694 if (ping_color_type == 4)
3695 {
3696 SetPixelAlpha(image,*p++,q);
3697
3698 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3699 found_transparent_pixel = MagickTrue;
3700
3701 p++;
3702 q+=GetPixelChannels(image);
3703 }
3704 #endif
3705 }
3706
3707 break;
3708 }
3709
3710 default:
3711 break;
3712 }
3713
3714 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3715 break;
3716
3717 /*
3718 Transfer image scanline.
3719 */
3720 r=quantum_scanline;
3721
3722 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3723
3724 if (q == (Quantum *) NULL)
3725 break;
3726 for (x=0; x < (ssize_t) image->columns; x++)
3727 {
3728 ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3729 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3730 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3731 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3732 SetPixelIndex(image,index,q);
3733 r++;
3734 q+=GetPixelChannels(image);
3735 }
3736
3737 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3738 break;
3739
3740 if (num_passes == 1)
3741 {
3742 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3743 image->rows);
3744
3745 if (status == MagickFalse)
3746 break;
3747 }
3748 }
3749 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3750 if (y < (long) image->rows)
3751 break;
3752 if (num_passes != 1)
3753 {
3754 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3755
3756 if (status == MagickFalse)
3757 break;
3758 }
3759 }
3760
3761 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3762 UndefinedPixelTrait;
3763
3764 if (logging != MagickFalse)
3765 {
3766 if (found_transparent_pixel != MagickFalse)
3767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3768 " Found transparent pixel");
3769 else
3770 {
3771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3772 " No transparent pixel was found");
3773
3774 ping_color_type&=0x03;
3775 }
3776 }
3777 }
3778 quantum_info=DestroyQuantumInfo(quantum_info);
3779
3780 png_read_end(ping,end_info);
3781
3782 if (logging != MagickFalse)
3783 {
3784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3785 " image->storage_class=%d\n",(int) image->storage_class);
3786 }
3787
3788 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3789 (ssize_t) image_info->first_scene && image->delay != 0)
3790 {
3791 png_destroy_read_struct(&ping,&ping_info,&end_info);
3792 pixel_info=RelinquishVirtualMemory(pixel_info);
3793 image->colors=2;
3794 (void) SetImageBackgroundColor(image,exception);
3795 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3796 UnlockSemaphoreInfo(ping_semaphore);
3797 #endif
3798 if (logging != MagickFalse)
3799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3800 " exit ReadOnePNGImage() early.");
3801 return(image);
3802 }
3803
3804 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3805 {
3806 ClassType
3807 storage_class;
3808
3809 /*
3810 Image has a transparent background.
3811 */
3812 storage_class=image->storage_class;
3813 image->alpha_trait=BlendPixelTrait;
3814
3815 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3816
3817 if (storage_class == PseudoClass)
3818 {
3819 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3820 {
3821 for (x=0; x < ping_num_trans; x++)
3822 {
3823 image->colormap[x].alpha_trait=BlendPixelTrait;
3824 image->colormap[x].alpha =
3825 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3826 }
3827 }
3828
3829 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3830 {
3831 for (x=0; x < (int) image->colors; x++)
3832 {
3833 if (ScaleQuantumToShort(image->colormap[x].red) ==
3834 transparent_color.alpha)
3835 {
3836 image->colormap[x].alpha_trait=BlendPixelTrait;
3837 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3838 }
3839 }
3840 }
3841 (void) SyncImage(image,exception);
3842 }
3843
3844 #if 1 /* Should have already been done above, but glennrp problem P10
3845 * needs this.
3846 */
3847 else
3848 {
3849 for (y=0; y < (ssize_t) image->rows; y++)
3850 {
3851 image->storage_class=storage_class;
3852 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3853
3854 if (q == (Quantum *) NULL)
3855 break;
3856
3857
3858 /* Caution: on a Q8 build, this does not distinguish between
3859 * 16-bit colors that differ only in the low byte
3860 */
3861 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3862 {
3863 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3864 transparent_color.red &&
3865 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3866 transparent_color.green &&
3867 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3868 transparent_color.blue)
3869 {
3870 SetPixelAlpha(image,TransparentAlpha,q);
3871 }
3872 else
3873 {
3874 SetPixelAlpha(image,OpaqueAlpha,q);
3875 }
3876
3877 q+=GetPixelChannels(image);
3878 }
3879
3880 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3881 break;
3882 }
3883 }
3884 #endif
3885
3886 image->storage_class=DirectClass;
3887 }
3888
3889 for (j = 0; j < 2; j++)
3890 {
3891 if (j == 0)
3892 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3893 MagickTrue : MagickFalse;
3894 else
3895 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3896 MagickTrue : MagickFalse;
3897
3898 if (status != MagickFalse)
3899 for (i=0; i < (ssize_t) num_text; i++)
3900 {
3901 /* Check for a profile */
3902
3903 if (logging != MagickFalse)
3904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3905 " Reading PNG text chunk");
3906
3907 if (strlen(text[i].key) > 16 &&
3908 memcmp(text[i].key, "Raw profile type ",17) == 0)
3909 {
3910 const char
3911 *value;
3912
3913 value=GetImageOption(image_info,"profile:skip");
3914
3915 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3916 {
3917 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3918 (int) i,exception);
3919 num_raw_profiles++;
3920 if (logging != MagickFalse)
3921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3922 " Read raw profile %s",text[i].key+17);
3923 }
3924 else
3925 {
3926 if (logging != MagickFalse)
3927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3928 " Skipping raw profile %s",text[i].key+17);
3929 }
3930 }
3931
3932 else
3933 {
3934 char
3935 *value;
3936
3937 length=text[i].text_length;
3938 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3939 sizeof(*value));
3940 if (value == (char *) NULL)
3941 {
3942 png_error(ping,"Memory allocation failed");
3943 break;
3944 }
3945 *value='\0';
3946 (void) ConcatenateMagickString(value,text[i].text,length+2);
3947
3948 /* Don't save "density" or "units" property if we have a pHYs
3949 * chunk
3950 */
3951 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3952 (LocaleCompare(text[i].key,"density") != 0 &&
3953 LocaleCompare(text[i].key,"units") != 0))
3954 {
3955 char
3956 key[MagickPathExtent];
3957
3958 (void) FormatLocaleString(key,MagickPathExtent,"%s",
3959 text[i].key);
3960 if (LocaleCompare(key,"version") == 0)
3961 (void) FormatLocaleString(key,MagickPathExtent,"png:%s",
3962 text[i].key);
3963 (void) SetImageProperty(image,key,value,exception);
3964 }
3965
3966 if (logging != MagickFalse)
3967 {
3968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " length: %lu\n"
3970 " Keyword: %s",
3971 (unsigned long) length,
3972 text[i].key);
3973 }
3974
3975 value=DestroyString(value);
3976 }
3977 }
3978 num_text_total += num_text;
3979 }
3980
3981 #ifdef MNG_OBJECT_BUFFERS
3982 /*
3983 Store the object if necessary.
3984 */
3985 if (object_id && !mng_info->frozen[object_id])
3986 {
3987 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3988 {
3989 /*
3990 create a new object buffer.
3991 */
3992 mng_info->ob[object_id]=(MngBuffer *)
3993 AcquireMagickMemory(sizeof(MngBuffer));
3994
3995 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3996 {
3997 mng_info->ob[object_id]->image=(Image *) NULL;
3998 mng_info->ob[object_id]->reference_count=1;
3999 }
4000 }
4001
4002 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4003 mng_info->ob[object_id]->frozen)
4004 {
4005 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4006 png_error(ping,"Memory allocation failed");
4007
4008 if (mng_info->ob[object_id]->frozen)
4009 png_error(ping,"Cannot overwrite frozen MNG object buffer");
4010 }
4011
4012 else
4013 {
4014
4015 if (mng_info->ob[object_id]->image != (Image *) NULL)
4016 mng_info->ob[object_id]->image=DestroyImage
4017 (mng_info->ob[object_id]->image);
4018
4019 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4020 exception);
4021
4022 if (mng_info->ob[object_id]->image != (Image *) NULL)
4023 mng_info->ob[object_id]->image->file=(FILE *) NULL;
4024
4025 else
4026 png_error(ping, "Cloning image for object buffer failed");
4027
4028 if (ping_width > 250000L || ping_height > 250000L)
4029 png_error(ping,"PNG Image dimensions are too large.");
4030
4031 mng_info->ob[object_id]->width=ping_width;
4032 mng_info->ob[object_id]->height=ping_height;
4033 mng_info->ob[object_id]->color_type=ping_color_type;
4034 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4035 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4036 mng_info->ob[object_id]->compression_method=
4037 ping_compression_method;
4038 mng_info->ob[object_id]->filter_method=ping_filter_method;
4039
4040 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4041 {
4042 png_colorp
4043 plte;
4044
4045 /*
4046 Copy the PLTE to the object buffer.
4047 */
4048 png_get_PLTE(ping,ping_info,&plte,&number_colors);
4049 mng_info->ob[object_id]->plte_length=number_colors;
4050
4051 for (i=0; i < number_colors; i++)
4052 {
4053 mng_info->ob[object_id]->plte[i]=plte[i];
4054 }
4055 }
4056
4057 else
4058 mng_info->ob[object_id]->plte_length=0;
4059 }
4060 }
4061 #endif
4062
4063 /* Set image->alpha_trait to MagickTrue if the input colortype supports
4064 * alpha or if a valid tRNS chunk is present, no matter whether there
4065 * is actual transparency present.
4066 */
4067 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4068 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4069 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4070 BlendPixelTrait : UndefinedPixelTrait;
4071 if (image->alpha_trait == BlendPixelTrait)
4072 (void) SetImageStorageClass(image,DirectClass,exception);
4073
4074 #if 0 /* I'm not sure what's wrong here but it does not work. */
4075 if (image->alpha_trait != UndefinedPixelTrait)
4076 {
4077 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4078 (void) SetImageType(image,GrayscaleAlphaType,exception);
4079
4080 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4081 (void) SetImageType(image,PaletteAlphaType,exception);
4082
4083 else
4084 (void) SetImageType(image,TrueColorAlphaType,exception);
4085 }
4086
4087 else
4088 {
4089 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4090 (void) SetImageType(image,GrayscaleType,exception);
4091
4092 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4093 (void) SetImageType(image,PaletteType,exception);
4094
4095 else
4096 (void) SetImageType(image,TrueColorType,exception);
4097 }
4098 #endif
4099
4100 /* Set more properties for identify to retrieve */
4101 {
4102 char
4103 msg[MagickPathExtent];
4104
4105 if (num_text_total != 0)
4106 {
4107 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4108 (void) FormatLocaleString(msg,MagickPathExtent,
4109 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4110 (void) SetImageProperty(image,"png:text",msg,
4111 exception);
4112 }
4113
4114 if (num_raw_profiles != 0)
4115 {
4116 (void) FormatLocaleString(msg,MagickPathExtent,
4117 "%d were found", num_raw_profiles);
4118 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4119 exception);
4120 }
4121
4122 /* cHRM chunk: */
4123 if (ping_found_cHRM != MagickFalse)
4124 {
4125 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4126 "chunk was found (see Chromaticity, above)");
4127 (void) SetImageProperty(image,"png:cHRM",msg,
4128 exception);
4129 }
4130
4131 /* bKGD chunk: */
4132 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4133 {
4134 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4135 "chunk was found (see Background color, above)");
4136 (void) SetImageProperty(image,"png:bKGD",msg,
4137 exception);
4138 }
4139
4140 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4141 "chunk was found");
4142
4143 #if defined(PNG_iCCP_SUPPORTED)
4144 /* iCCP chunk: */
4145 if (ping_found_iCCP != MagickFalse)
4146 (void) SetImageProperty(image,"png:iCCP",msg,
4147 exception);
4148 #endif
4149
4150 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4151 (void) SetImageProperty(image,"png:tRNS",msg,
4152 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,
4163 exception);
4164 }
4165 #endif
4166
4167 /* gAMA chunk: */
4168 if (ping_found_gAMA != MagickFalse)
4169 {
4170 (void) FormatLocaleString(msg,MagickPathExtent,
4171 "gamma=%.8g (See Gamma, above)",
4172 file_gamma);
4173 (void) SetImageProperty(image,"png:gAMA",msg,
4174 exception);
4175 }
4176
4177 #if defined(PNG_pHYs_SUPPORTED)
4178 /* pHYs chunk: */
4179 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4180 {
4181 (void) FormatLocaleString(msg,MagickPathExtent,
4182 "x_res=%.10g, y_res=%.10g, units=%d",
4183 (double) x_resolution,(double) y_resolution, unit_type);
4184 (void) SetImageProperty(image,"png:pHYs",msg,
4185 exception);
4186 }
4187 #endif
4188
4189 #if defined(PNG_oFFs_SUPPORTED)
4190 /* oFFs chunk: */
4191 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4192 {
4193 (void) FormatLocaleString(msg,MagickPathExtent,
4194 "x_off=%.20g, y_off=%.20g",
4195 (double) image->page.x,(double) image->page.y);
4196 (void) SetImageProperty(image,"png:oFFs",msg,
4197 exception);
4198 }
4199 #endif
4200
4201 #if defined(PNG_tIME_SUPPORTED)
4202 read_tIME_chunk(image,ping,end_info,exception);
4203 #endif
4204 #if defined(PNG_READ_eXIf_SUPPORTED)
4205 read_eXIf_chunk(image,ping,end_info,exception);
4206 #endif
4207
4208 /* caNv chunk: */
4209 if ((image->page.width != 0 && image->page.width != image->columns) ||
4210 (image->page.height != 0 && image->page.height != image->rows) ||
4211 (image->page.x != 0 || image->page.y != 0))
4212 {
4213 (void) FormatLocaleString(msg,MagickPathExtent,
4214 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4215 (double) image->page.width,(double) image->page.height,
4216 (double) image->page.x,(double) image->page.y);
4217 (void) SetImageProperty(image,"png:caNv",msg,
4218 exception);
4219 }
4220 }
4221
4222 /*
4223 Relinquish resources.
4224 */
4225 png_destroy_read_struct(&ping,&ping_info,&end_info);
4226
4227 pixel_info=RelinquishVirtualMemory(pixel_info);
4228
4229 if (logging != MagickFalse)
4230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4231 " exit ReadOnePNGImage()");
4232
4233 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4234 UnlockSemaphoreInfo(ping_semaphore);
4235 #endif
4236
4237 /* } for navigation to beginning of SETJMP-protected block, revert to
4238 * Throwing an Exception when an error occurs.
4239 */
4240
4241 return(image);
4242
4243 /* end of reading one PNG image */
4244 }
4245
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4246 static Image *ReadPNGImage(const ImageInfo *image_info,
4247 ExceptionInfo *exception)
4248 {
4249 Image
4250 *image;
4251
4252 MagickBooleanType
4253 logging,
4254 status;
4255
4256 MngInfo
4257 *mng_info;
4258
4259 char
4260 magic_number[MagickPathExtent];
4261
4262 ssize_t
4263 count;
4264
4265 /*
4266 Open image file.
4267 */
4268 assert(image_info != (const ImageInfo *) NULL);
4269 assert(image_info->signature == MagickCoreSignature);
4270
4271 if (image_info->debug != MagickFalse)
4272 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4273 image_info->filename);
4274
4275 assert(exception != (ExceptionInfo *) NULL);
4276 assert(exception->signature == MagickCoreSignature);
4277 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4278 image=AcquireImage(image_info,exception);
4279 mng_info=(MngInfo *) NULL;
4280 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4281
4282 if (status == MagickFalse)
4283 return(DestroyImageList(image));
4284
4285 /*
4286 Verify PNG signature.
4287 */
4288 count=ReadBlob(image,8,(unsigned char *) magic_number);
4289
4290 if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4291 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4292
4293 /*
4294 Verify that file size large enough to contain a PNG datastream.
4295 */
4296 if (GetBlobSize(image) < 61)
4297 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4298
4299 /*
4300 Allocate a MngInfo structure.
4301 */
4302 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4303
4304 if (mng_info == (MngInfo *) NULL)
4305 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4306
4307 /*
4308 Initialize members of the MngInfo structure.
4309 */
4310 (void) memset(mng_info,0,sizeof(MngInfo));
4311 mng_info->image=image;
4312
4313 image=ReadOnePNGImage(mng_info,image_info,exception);
4314 mng_info=MngInfoFreeStruct(mng_info);
4315
4316 if (image == (Image *) NULL)
4317 {
4318 if (logging != MagickFalse)
4319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4320 "exit ReadPNGImage() with error");
4321
4322 return((Image *) NULL);
4323 }
4324
4325 (void) CloseBlob(image);
4326
4327 if ((image->columns == 0) || (image->rows == 0))
4328 {
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 "exit ReadPNGImage() with error.");
4332
4333 ThrowReaderException(CorruptImageError,"CorruptImage");
4334 }
4335
4336 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4337 ((image->gamma < .45) || (image->gamma > .46)) &&
4338 !(image->chromaticity.red_primary.x>0.6399f &&
4339 image->chromaticity.red_primary.x<0.6401f &&
4340 image->chromaticity.red_primary.y>0.3299f &&
4341 image->chromaticity.red_primary.y<0.3301f &&
4342 image->chromaticity.green_primary.x>0.2999f &&
4343 image->chromaticity.green_primary.x<0.3001f &&
4344 image->chromaticity.green_primary.y>0.5999f &&
4345 image->chromaticity.green_primary.y<0.6001f &&
4346 image->chromaticity.blue_primary.x>0.1499f &&
4347 image->chromaticity.blue_primary.x<0.1501f &&
4348 image->chromaticity.blue_primary.y>0.0599f &&
4349 image->chromaticity.blue_primary.y<0.0601f &&
4350 image->chromaticity.white_point.x>0.3126f &&
4351 image->chromaticity.white_point.x<0.3128f &&
4352 image->chromaticity.white_point.y>0.3289f &&
4353 image->chromaticity.white_point.y<0.3291f))
4354 {
4355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4356 "SetImageColorspace to RGBColorspace");
4357 SetImageColorspace(image,RGBColorspace,exception);
4358 }
4359
4360 if (logging != MagickFalse)
4361 {
4362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4363 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4364 (double) image->page.width,(double) image->page.height,
4365 (double) image->page.x,(double) image->page.y);
4366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4367 " image->colorspace: %d", (int) image->colorspace);
4368 }
4369
4370 if (logging != MagickFalse)
4371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4372
4373 return(image);
4374 }
4375
4376
4377
4378 #if defined(JNG_SUPPORTED)
4379 /*
4380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381 % %
4382 % %
4383 % %
4384 % R e a d O n e J N G I m a g e %
4385 % %
4386 % %
4387 % %
4388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4389 %
4390 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4391 % (minus the 8-byte signature) and returns it. It allocates the memory
4392 % necessary for the new Image structure and returns a pointer to the new
4393 % image.
4394 %
4395 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4396 %
4397 % The format of the ReadOneJNGImage method is:
4398 %
4399 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4400 % ExceptionInfo *exception)
4401 %
4402 % A description of each parameter follows:
4403 %
4404 % o mng_info: Specifies a pointer to a MngInfo structure.
4405 %
4406 % o image_info: the image info.
4407 %
4408 % o exception: return any errors or warnings in this structure.
4409 %
4410 */
4411 static void
DestroyJNG(unsigned char * chunk,Image ** color_image,ImageInfo ** color_image_info,Image ** alpha_image,ImageInfo ** alpha_image_info)4412 DestroyJNG(unsigned char *chunk,Image **color_image,
4413 ImageInfo **color_image_info,Image **alpha_image,
4414 ImageInfo **alpha_image_info)
4415 {
4416 (void) RelinquishMagickMemory(chunk);
4417 if (color_image_info && *color_image_info)
4418 {
4419 DestroyImageInfo(*color_image_info);
4420 *color_image_info = (ImageInfo *)NULL;
4421 }
4422 if (alpha_image_info && *alpha_image_info)
4423 {
4424 DestroyImageInfo(*alpha_image_info);
4425 *alpha_image_info = (ImageInfo *)NULL;
4426 }
4427 if (color_image && *color_image)
4428 {
4429 DestroyImage(*color_image);
4430 *color_image = (Image *)NULL;
4431 }
4432 if (alpha_image && *alpha_image)
4433 {
4434 DestroyImage(*alpha_image);
4435 *alpha_image = (Image *)NULL;
4436 }
4437 }
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4438 static Image *ReadOneJNGImage(MngInfo *mng_info,
4439 const ImageInfo *image_info, ExceptionInfo *exception)
4440 {
4441 Image
4442 *alpha_image,
4443 *color_image,
4444 *image,
4445 *jng_image;
4446
4447 ImageInfo
4448 *alpha_image_info,
4449 *color_image_info;
4450
4451 MagickBooleanType
4452 logging;
4453
4454 ssize_t
4455 y;
4456
4457 MagickBooleanType
4458 status;
4459
4460 png_uint_32
4461 jng_height,
4462 jng_width;
4463
4464 png_byte
4465 jng_color_type,
4466 jng_image_sample_depth,
4467 jng_image_compression_method,
4468 jng_image_interlace_method,
4469 jng_alpha_sample_depth,
4470 jng_alpha_compression_method,
4471 jng_alpha_filter_method,
4472 jng_alpha_interlace_method;
4473
4474 register const Quantum
4475 *s;
4476
4477 register ssize_t
4478 i,
4479 x;
4480
4481 register Quantum
4482 *q;
4483
4484 register unsigned char
4485 *p;
4486
4487 unsigned int
4488 read_JSEP,
4489 reading_idat;
4490
4491 size_t
4492 length;
4493
4494 jng_alpha_compression_method=0;
4495 jng_alpha_sample_depth=8;
4496 jng_color_type=0;
4497 jng_height=0;
4498 jng_width=0;
4499 alpha_image=(Image *) NULL;
4500 color_image=(Image *) NULL;
4501 alpha_image_info=(ImageInfo *) NULL;
4502 color_image_info=(ImageInfo *) NULL;
4503
4504 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4505 " Enter ReadOneJNGImage()");
4506
4507 image=mng_info->image;
4508
4509 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4510 {
4511 /*
4512 Allocate next image structure.
4513 */
4514 if (logging != MagickFalse)
4515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4516 " AcquireNextImage()");
4517
4518 AcquireNextImage(image_info,image,exception);
4519
4520 if (GetNextImageInList(image) == (Image *) NULL)
4521 return(DestroyImageList(image));
4522
4523 image=SyncNextImageInList(image);
4524 }
4525 mng_info->image=image;
4526
4527 /*
4528 Signature bytes have already been read.
4529 */
4530
4531 read_JSEP=MagickFalse;
4532 reading_idat=MagickFalse;
4533 for (;;)
4534 {
4535 char
4536 type[MagickPathExtent];
4537
4538 unsigned char
4539 *chunk;
4540
4541 unsigned int
4542 count;
4543
4544 /*
4545 Read a new JNG chunk.
4546 */
4547 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4548 2*GetBlobSize(image));
4549
4550 if (status == MagickFalse)
4551 break;
4552
4553 type[0]='\0';
4554 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4555 length=(size_t) ReadBlobMSBLong(image);
4556 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4557
4558 if (logging != MagickFalse)
4559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4560 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4561 type[0],type[1],type[2],type[3],(double) length);
4562
4563 if (length > PNG_UINT_31_MAX || count == 0)
4564 {
4565 DestroyJNG(NULL,&color_image,&color_image_info,
4566 &alpha_image,&alpha_image_info);
4567 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4568 }
4569 if (length > GetBlobSize(image))
4570 {
4571 DestroyJNG(NULL,&color_image,&color_image_info,
4572 &alpha_image,&alpha_image_info);
4573 ThrowReaderException(CorruptImageError,
4574 "InsufficientImageDataInFile");
4575 }
4576
4577 p=NULL;
4578 chunk=(unsigned char *) NULL;
4579
4580 if (length != 0)
4581 {
4582 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4583
4584 if (chunk == (unsigned char *) NULL)
4585 {
4586 DestroyJNG(NULL,&color_image,&color_image_info,
4587 &alpha_image,&alpha_image_info);
4588 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4589 }
4590
4591 for (i=0; i < (ssize_t) length; i++)
4592 {
4593 int
4594 c;
4595
4596 c=ReadBlobByte(image);
4597 if (c == EOF)
4598 break;
4599 chunk[i]=(unsigned char) c;
4600 }
4601 for ( ; i < (ssize_t) length; i++)
4602 chunk[i]='\0';
4603
4604 p=chunk;
4605 }
4606
4607 (void) ReadBlobMSBLong(image); /* read crc word */
4608
4609 if (memcmp(type,mng_JHDR,4) == 0)
4610 {
4611 if (length == 16)
4612 {
4613 jng_width=(png_uint_32)mng_get_long(p);
4614 jng_height=(png_uint_32)mng_get_long(&p[4]);
4615 if ((jng_width == 0) || (jng_height == 0))
4616 {
4617 DestroyJNG(chunk,&color_image,&color_image_info,
4618 &alpha_image,&alpha_image_info);
4619 ThrowReaderException(CorruptImageError,
4620 "NegativeOrZeroImageSize");
4621 }
4622 jng_color_type=p[8];
4623 jng_image_sample_depth=p[9];
4624 jng_image_compression_method=p[10];
4625 jng_image_interlace_method=p[11];
4626
4627 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4628 NoInterlace;
4629
4630 jng_alpha_sample_depth=p[12];
4631 jng_alpha_compression_method=p[13];
4632 jng_alpha_filter_method=p[14];
4633 jng_alpha_interlace_method=p[15];
4634
4635 if (logging != MagickFalse)
4636 {
4637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4638 " jng_width: %16lu, jng_height: %16lu\n"
4639 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4640 " jng_image_compression_method:%3d",
4641 (unsigned long) jng_width, (unsigned long) jng_height,
4642 jng_color_type, jng_image_sample_depth,
4643 jng_image_compression_method);
4644
4645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646 " jng_image_interlace_method: %3d"
4647 " jng_alpha_sample_depth: %3d",
4648 jng_image_interlace_method,
4649 jng_alpha_sample_depth);
4650
4651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4652 " jng_alpha_compression_method:%3d\n"
4653 " jng_alpha_filter_method: %3d\n"
4654 " jng_alpha_interlace_method: %3d",
4655 jng_alpha_compression_method,
4656 jng_alpha_filter_method,
4657 jng_alpha_interlace_method);
4658 }
4659 }
4660
4661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4662
4663 if ((jng_width > 65535) || (jng_height > 65535) ||
4664 (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4665 (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4666 {
4667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4668 " JNG width or height too large: (%lu x %lu)",
4669 (long) jng_width, (long) jng_height);
4670 DestroyJNG(chunk,&color_image,&color_image_info,
4671 &alpha_image,&alpha_image_info);
4672 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4673 }
4674
4675 continue;
4676 }
4677
4678
4679 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4680 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4681 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4682 {
4683 /*
4684 o create color_image
4685 o open color_blob, attached to color_image
4686 o if (color type has alpha)
4687 open alpha_blob, attached to alpha_image
4688 */
4689
4690 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4691
4692 if (color_image_info == (ImageInfo *) NULL)
4693 {
4694 DestroyJNG(chunk,&color_image,&color_image_info,
4695 &alpha_image,&alpha_image_info);
4696 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4697 }
4698
4699 GetImageInfo(color_image_info);
4700 color_image=AcquireImage(color_image_info,exception);
4701
4702 if (color_image == (Image *) NULL)
4703 {
4704 DestroyJNG(chunk,&color_image,&color_image_info,
4705 &alpha_image,&alpha_image_info);
4706 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4707 }
4708
4709 if (logging != MagickFalse)
4710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4711 " Creating color_blob.");
4712
4713 (void) AcquireUniqueFilename(color_image->filename);
4714 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4715 exception);
4716
4717 if (status == MagickFalse)
4718 {
4719 DestroyJNG(chunk,&color_image,&color_image_info,
4720 &alpha_image,&alpha_image_info);
4721 return(DestroyImageList(image));
4722 }
4723
4724 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4725 {
4726 alpha_image_info=(ImageInfo *)
4727 AcquireMagickMemory(sizeof(ImageInfo));
4728
4729 if (alpha_image_info == (ImageInfo *) NULL)
4730 {
4731 DestroyJNG(chunk,&color_image,&color_image_info,
4732 &alpha_image,&alpha_image_info);
4733 ThrowReaderException(ResourceLimitError,
4734 "MemoryAllocationFailed");
4735 }
4736
4737 GetImageInfo(alpha_image_info);
4738 alpha_image=AcquireImage(alpha_image_info,exception);
4739
4740 if (alpha_image == (Image *) NULL)
4741 {
4742 DestroyJNG(chunk,&color_image,&color_image_info,
4743 &alpha_image,&alpha_image_info);
4744 ThrowReaderException(ResourceLimitError,
4745 "MemoryAllocationFailed");
4746 }
4747
4748 if (logging != MagickFalse)
4749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4750 " Creating alpha_blob.");
4751
4752 (void) AcquireUniqueFilename(alpha_image->filename);
4753 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4754 exception);
4755
4756 if (status == MagickFalse)
4757 {
4758 DestroyJNG(chunk,&color_image,&color_image_info,
4759 &alpha_image,&alpha_image_info);
4760 return(DestroyImageList(image));
4761 }
4762
4763 if (jng_alpha_compression_method == 0)
4764 {
4765 unsigned char
4766 data[18];
4767
4768 if (logging != MagickFalse)
4769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4770 " Writing IHDR chunk to alpha_blob.");
4771
4772 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4773 "\211PNG\r\n\032\n");
4774
4775 (void) WriteBlobMSBULong(alpha_image,13L);
4776 PNGType(data,mng_IHDR);
4777 LogPNGChunk(logging,mng_IHDR,13L);
4778 PNGLong(data+4,jng_width);
4779 PNGLong(data+8,jng_height);
4780 data[12]=jng_alpha_sample_depth;
4781 data[13]=0; /* color_type gray */
4782 data[14]=0; /* compression method 0 */
4783 data[15]=0; /* filter_method 0 */
4784 data[16]=0; /* interlace_method 0 */
4785 (void) WriteBlob(alpha_image,17,data);
4786 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4787 }
4788 }
4789 reading_idat=MagickTrue;
4790 }
4791
4792 if (memcmp(type,mng_JDAT,4) == 0)
4793 {
4794 /* Copy chunk to color_image->blob */
4795
4796 if (logging != MagickFalse)
4797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798 " Copying JDAT chunk data to color_blob.");
4799
4800 if ((length != 0) && (color_image != (Image *) NULL))
4801 (void) WriteBlob(color_image,length,chunk);
4802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4803 continue;
4804 }
4805
4806 if (memcmp(type,mng_IDAT,4) == 0)
4807 {
4808 png_byte
4809 data[5];
4810
4811 /* Copy IDAT header and chunk data to alpha_image->blob */
4812
4813 if (alpha_image != NULL && image_info->ping == MagickFalse)
4814 {
4815 if (logging != MagickFalse)
4816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4817 " Copying IDAT chunk data to alpha_blob.");
4818
4819 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4820 PNGType(data,mng_IDAT);
4821 LogPNGChunk(logging,mng_IDAT,length);
4822 (void) WriteBlob(alpha_image,4,data);
4823 (void) WriteBlob(alpha_image,length,chunk);
4824 (void) WriteBlobMSBULong(alpha_image,
4825 crc32(crc32(0,data,4),chunk,(uInt) length));
4826 }
4827
4828 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4829
4830 continue;
4831 }
4832
4833 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4834 {
4835 /* Copy chunk data to alpha_image->blob */
4836
4837 if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4838 (length != 0))
4839 {
4840 if (logging != MagickFalse)
4841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4842 " Copying JDAA chunk data to alpha_blob.");
4843
4844 (void) WriteBlob(alpha_image,length,chunk);
4845 }
4846
4847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4848
4849 continue;
4850 }
4851
4852 if (memcmp(type,mng_JSEP,4) == 0)
4853 {
4854 read_JSEP=MagickTrue;
4855
4856 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4857
4858 continue;
4859 }
4860
4861 if (memcmp(type,mng_bKGD,4) == 0)
4862 {
4863 if (length == 2)
4864 {
4865 image->background_color.red=ScaleCharToQuantum(p[1]);
4866 image->background_color.green=image->background_color.red;
4867 image->background_color.blue=image->background_color.red;
4868 }
4869
4870 if (length == 6)
4871 {
4872 image->background_color.red=ScaleCharToQuantum(p[1]);
4873 image->background_color.green=ScaleCharToQuantum(p[3]);
4874 image->background_color.blue=ScaleCharToQuantum(p[5]);
4875 }
4876
4877 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4878 continue;
4879 }
4880
4881 if (memcmp(type,mng_gAMA,4) == 0)
4882 {
4883 if (length == 4)
4884 image->gamma=((float) mng_get_long(p))*0.00001;
4885
4886 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4887 continue;
4888 }
4889
4890 if (memcmp(type,mng_cHRM,4) == 0)
4891 {
4892 if (length == 32)
4893 {
4894 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4895 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4896 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4897 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4898 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4899 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4900 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4901 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4902 }
4903
4904 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4905 continue;
4906 }
4907
4908 if (memcmp(type,mng_sRGB,4) == 0)
4909 {
4910 if (length == 1)
4911 {
4912 image->rendering_intent=
4913 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4914 image->gamma=0.45455f;
4915 image->chromaticity.red_primary.x=0.6400f;
4916 image->chromaticity.red_primary.y=0.3300f;
4917 image->chromaticity.green_primary.x=0.3000f;
4918 image->chromaticity.green_primary.y=0.6000f;
4919 image->chromaticity.blue_primary.x=0.1500f;
4920 image->chromaticity.blue_primary.y=0.0600f;
4921 image->chromaticity.white_point.x=0.3127f;
4922 image->chromaticity.white_point.y=0.3290f;
4923 }
4924
4925 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4926 continue;
4927 }
4928
4929 if (memcmp(type,mng_oFFs,4) == 0)
4930 {
4931 if (length > 8)
4932 {
4933 image->page.x=(ssize_t) mng_get_long(p);
4934 image->page.y=(ssize_t) mng_get_long(&p[4]);
4935
4936 if ((int) p[8] != 0)
4937 {
4938 image->page.x/=10000;
4939 image->page.y/=10000;
4940 }
4941 }
4942
4943 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4944
4945 continue;
4946 }
4947
4948 if (memcmp(type,mng_pHYs,4) == 0)
4949 {
4950 if (length > 8)
4951 {
4952 image->resolution.x=(double) mng_get_long(p);
4953 image->resolution.y=(double) mng_get_long(&p[4]);
4954 if ((int) p[8] == PNG_RESOLUTION_METER)
4955 {
4956 image->units=PixelsPerCentimeterResolution;
4957 image->resolution.x=image->resolution.x/100.0f;
4958 image->resolution.y=image->resolution.y/100.0f;
4959 }
4960 }
4961
4962 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963 continue;
4964 }
4965
4966 #if 0
4967 if (memcmp(type,mng_iCCP,4) == 0)
4968 {
4969 /* To do: */
4970 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971
4972 continue;
4973 }
4974 #endif
4975
4976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4977
4978 if (memcmp(type,mng_IEND,4))
4979 continue;
4980
4981 break;
4982 }
4983
4984
4985 /* IEND found */
4986
4987 /*
4988 Finish up reading image data:
4989
4990 o read main image from color_blob.
4991
4992 o close color_blob.
4993
4994 o if (color_type has alpha)
4995 if alpha_encoding is PNG
4996 read secondary image from alpha_blob via ReadPNG
4997 if alpha_encoding is JPEG
4998 read secondary image from alpha_blob via ReadJPEG
4999
5000 o close alpha_blob.
5001
5002 o copy intensity of secondary image into
5003 alpha samples of main image.
5004
5005 o destroy the secondary image.
5006 */
5007
5008 if (color_image_info == (ImageInfo *) NULL)
5009 {
5010 assert(color_image == (Image *) NULL);
5011 assert(alpha_image == (Image *) NULL);
5012 if (color_image != (Image *) NULL)
5013 color_image=DestroyImageList(color_image);
5014 return(DestroyImageList(image));
5015 }
5016
5017 if (color_image == (Image *) NULL)
5018 {
5019 assert(alpha_image == (Image *) NULL);
5020 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5021 }
5022
5023 (void) SeekBlob(color_image,0,SEEK_SET);
5024
5025 if (logging != MagickFalse)
5026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5027 " Reading jng_image from color_blob.");
5028
5029 assert(color_image_info != (ImageInfo *) NULL);
5030 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5031 "jpeg:%s",color_image->filename);
5032
5033 color_image_info->ping=MagickFalse; /* To do: avoid this */
5034 jng_image=ReadImage(color_image_info,exception);
5035
5036 (void) RelinquishUniqueFileResource(color_image->filename);
5037 color_image=DestroyImage(color_image);
5038 color_image_info=DestroyImageInfo(color_image_info);
5039
5040 if (jng_image == (Image *) NULL)
5041 {
5042 DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5043 return(DestroyImageList(image));
5044 }
5045
5046
5047 if (logging != MagickFalse)
5048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5049 " Copying jng_image pixels to main image.");
5050
5051 image->rows=jng_height;
5052 image->columns=jng_width;
5053
5054 status=SetImageExtent(image,image->columns,image->rows,exception);
5055 if (status == MagickFalse)
5056 {
5057 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5058 &alpha_image_info);
5059 jng_image=DestroyImageList(jng_image);
5060 return(DestroyImageList(image));
5061 }
5062 if ((image->columns != jng_image->columns) ||
5063 (image->rows != jng_image->rows))
5064 {
5065 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5066 &alpha_image_info);
5067 jng_image=DestroyImageList(jng_image);
5068 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5069 }
5070 for (y=0; y < (ssize_t) image->rows; y++)
5071 {
5072 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5073 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5074 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5075 break;
5076 for (x=(ssize_t) image->columns; x != 0; x--)
5077 {
5078 SetPixelRed(image,GetPixelRed(jng_image,s),q);
5079 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5080 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5081 q+=GetPixelChannels(image);
5082 s+=GetPixelChannels(jng_image);
5083 }
5084
5085 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5086 break;
5087 }
5088
5089 jng_image=DestroyImage(jng_image);
5090
5091 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
5092 {
5093 if (jng_alpha_compression_method == 0)
5094 {
5095 png_byte
5096 data[5];
5097 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5098 PNGType(data,mng_IEND);
5099 LogPNGChunk(logging,mng_IEND,0L);
5100 (void) WriteBlob(alpha_image,4,data);
5101 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5102 }
5103
5104 (void) CloseBlob(alpha_image);
5105
5106 if (logging != MagickFalse)
5107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5108 " Reading alpha from alpha_blob.");
5109
5110 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5111 "%s",alpha_image->filename);
5112
5113 jng_image=ReadImage(alpha_image_info,exception);
5114
5115 if (jng_image != (Image *) NULL)
5116 for (y=0; y < (ssize_t) image->rows; y++)
5117 {
5118 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5119 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5120 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5121 break;
5122
5123 if (image->alpha_trait != UndefinedPixelTrait)
5124 for (x=(ssize_t) image->columns; x != 0; x--)
5125 {
5126 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5127 q+=GetPixelChannels(image);
5128 s+=GetPixelChannels(jng_image);
5129 }
5130
5131 else
5132 for (x=(ssize_t) image->columns; x != 0; x--)
5133 {
5134 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5135 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5136 image->alpha_trait=BlendPixelTrait;
5137 q+=GetPixelChannels(image);
5138 s+=GetPixelChannels(jng_image);
5139 }
5140
5141 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5142 break;
5143 }
5144 (void) RelinquishUniqueFileResource(alpha_image->filename);
5145 alpha_image=DestroyImage(alpha_image);
5146 alpha_image_info=DestroyImageInfo(alpha_image_info);
5147 if (jng_image != (Image *) NULL)
5148 jng_image=DestroyImage(jng_image);
5149 }
5150
5151 /* Read the JNG image. */
5152
5153 if (mng_info->mng_type == 0)
5154 {
5155 mng_info->mng_width=jng_width;
5156 mng_info->mng_height=jng_height;
5157 }
5158
5159 if (image->page.width == 0 && image->page.height == 0)
5160 {
5161 image->page.width=jng_width;
5162 image->page.height=jng_height;
5163 }
5164
5165 if (image->page.x == 0 && image->page.y == 0)
5166 {
5167 image->page.x=mng_info->x_off[mng_info->object_id];
5168 image->page.y=mng_info->y_off[mng_info->object_id];
5169 }
5170
5171 else
5172 {
5173 image->page.y=mng_info->y_off[mng_info->object_id];
5174 }
5175
5176 mng_info->image_found++;
5177 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5178 2*GetBlobSize(image));
5179
5180 if (status == MagickFalse)
5181 return(DestroyImageList(image));
5182
5183 if (logging != MagickFalse)
5184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5185 " exit ReadOneJNGImage()");
5186
5187 return(image);
5188 }
5189
5190 /*
5191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5192 % %
5193 % %
5194 % %
5195 % R e a d J N G I m a g e %
5196 % %
5197 % %
5198 % %
5199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5200 %
5201 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5202 % (including the 8-byte signature) and returns it. It allocates the memory
5203 % necessary for the new Image structure and returns a pointer to the new
5204 % image.
5205 %
5206 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5207 %
5208 % The format of the ReadJNGImage method is:
5209 %
5210 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5211 % *exception)
5212 %
5213 % A description of each parameter follows:
5214 %
5215 % o image_info: the image info.
5216 %
5217 % o exception: return any errors or warnings in this structure.
5218 %
5219 */
5220
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)5221 static Image *ReadJNGImage(const ImageInfo *image_info,
5222 ExceptionInfo *exception)
5223 {
5224 Image
5225 *image;
5226
5227 MagickBooleanType
5228 logging,
5229 status;
5230
5231 MngInfo
5232 *mng_info;
5233
5234 char
5235 magic_number[MagickPathExtent];
5236
5237 size_t
5238 count;
5239
5240 /*
5241 Open image file.
5242 */
5243 assert(image_info != (const ImageInfo *) NULL);
5244 assert(image_info->signature == MagickCoreSignature);
5245 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5246 image_info->filename);
5247 assert(exception != (ExceptionInfo *) NULL);
5248 assert(exception->signature == MagickCoreSignature);
5249 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5250 image=AcquireImage(image_info,exception);
5251 mng_info=(MngInfo *) NULL;
5252 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5253
5254 if (status == MagickFalse)
5255 return(DestroyImageList(image));
5256
5257 if (LocaleCompare(image_info->magick,"JNG") != 0)
5258 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5259
5260 /* Verify JNG signature. */
5261
5262 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5263
5264 if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5265 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5266
5267 /*
5268 Verify that file size large enough to contain a JNG datastream.
5269 */
5270 if (GetBlobSize(image) < 147)
5271 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5272
5273 /* Allocate a MngInfo structure. */
5274
5275 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5276
5277 if (mng_info == (MngInfo *) NULL)
5278 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5279
5280 /* Initialize members of the MngInfo structure. */
5281
5282 (void) memset(mng_info,0,sizeof(MngInfo));
5283
5284 mng_info->image=image;
5285 image=ReadOneJNGImage(mng_info,image_info,exception);
5286 mng_info=MngInfoFreeStruct(mng_info);
5287
5288 if (image == (Image *) NULL)
5289 {
5290 if (logging != MagickFalse)
5291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5292 "exit ReadJNGImage() with error");
5293
5294 return((Image *) NULL);
5295 }
5296 (void) CloseBlob(image);
5297
5298 if (image->columns == 0 || image->rows == 0)
5299 {
5300 if (logging != MagickFalse)
5301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5302 "exit ReadJNGImage() with error");
5303
5304 ThrowReaderException(CorruptImageError,"CorruptImage");
5305 }
5306
5307 if (logging != MagickFalse)
5308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5309
5310 return(image);
5311 }
5312 #endif
5313
ReadOneMNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)5314 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5315 ExceptionInfo *exception)
5316 {
5317 char
5318 page_geometry[MagickPathExtent];
5319
5320 Image
5321 *image;
5322
5323 MagickBooleanType
5324 logging;
5325
5326 volatile int
5327 first_mng_object,
5328 object_id,
5329 term_chunk_found,
5330 skip_to_iend;
5331
5332 volatile ssize_t
5333 image_count=0;
5334
5335 MagickBooleanType
5336 status;
5337
5338 MagickOffsetType
5339 offset;
5340
5341 MngBox
5342 default_fb,
5343 fb,
5344 previous_fb;
5345
5346 #if defined(MNG_INSERT_LAYERS)
5347 PixelInfo
5348 mng_background_color;
5349 #endif
5350
5351 register unsigned char
5352 *p;
5353
5354 register ssize_t
5355 i;
5356
5357 size_t
5358 count;
5359
5360 ssize_t
5361 loop_level;
5362
5363 volatile short
5364 skipping_loop;
5365
5366 #if defined(MNG_INSERT_LAYERS)
5367 unsigned int
5368 mandatory_back=0;
5369 #endif
5370
5371 volatile unsigned int
5372 #ifdef MNG_OBJECT_BUFFERS
5373 mng_background_object=0,
5374 #endif
5375 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5376
5377 size_t
5378 default_frame_timeout,
5379 frame_timeout,
5380 #if defined(MNG_INSERT_LAYERS)
5381 image_height,
5382 image_width,
5383 #endif
5384 length;
5385
5386 /* These delays are all measured in image ticks_per_second,
5387 * not in MNG ticks_per_second
5388 */
5389 volatile size_t
5390 default_frame_delay,
5391 final_delay,
5392 final_image_delay,
5393 frame_delay,
5394 #if defined(MNG_INSERT_LAYERS)
5395 insert_layers,
5396 #endif
5397 mng_iterations=1,
5398 simplicity=0,
5399 subframe_height=0,
5400 subframe_width=0;
5401
5402 previous_fb.top=0;
5403 previous_fb.bottom=0;
5404 previous_fb.left=0;
5405 previous_fb.right=0;
5406 default_fb.top=0;
5407 default_fb.bottom=0;
5408 default_fb.left=0;
5409 default_fb.right=0;
5410
5411 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5412 " Enter ReadOneMNGImage()");
5413
5414 image=mng_info->image;
5415
5416 if (LocaleCompare(image_info->magick,"MNG") == 0)
5417 {
5418 char
5419 magic_number[MagickPathExtent];
5420
5421 /* Verify MNG signature. */
5422 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5423 if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5424 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5425
5426 /* Initialize some nonzero members of the MngInfo structure. */
5427 for (i=0; i < MNG_MAX_OBJECTS; i++)
5428 {
5429 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5430 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5431 }
5432 mng_info->exists[0]=MagickTrue;
5433 }
5434
5435 skipping_loop=(-1);
5436 first_mng_object=MagickTrue;
5437 mng_type=0;
5438 #if defined(MNG_INSERT_LAYERS)
5439 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5440 #endif
5441 default_frame_delay=0;
5442 default_frame_timeout=0;
5443 frame_delay=0;
5444 final_delay=1;
5445 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5446 object_id=0;
5447 skip_to_iend=MagickFalse;
5448 term_chunk_found=MagickFalse;
5449 mng_info->framing_mode=1;
5450 #if defined(MNG_INSERT_LAYERS)
5451 mandatory_back=MagickFalse;
5452 #endif
5453 #if defined(MNG_INSERT_LAYERS)
5454 mng_background_color=image->background_color;
5455 #endif
5456 default_fb=mng_info->frame;
5457 previous_fb=mng_info->frame;
5458 do
5459 {
5460 char
5461 type[MagickPathExtent];
5462
5463 if (LocaleCompare(image_info->magick,"MNG") == 0)
5464 {
5465 unsigned char
5466 *chunk;
5467
5468 /*
5469 Read a new chunk.
5470 */
5471 type[0]='\0';
5472 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5473 length=(size_t) ReadBlobMSBLong(image);
5474 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5475
5476 if (logging != MagickFalse)
5477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5478 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5479 type[0],type[1],type[2],type[3],(double) length);
5480
5481 if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5482 (count < 4))
5483 ThrowReaderException(CorruptImageError,"CorruptImage");
5484
5485 p=NULL;
5486 chunk=(unsigned char *) NULL;
5487
5488 if (length != 0)
5489 {
5490 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5491
5492 if (chunk == (unsigned char *) NULL)
5493 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5494
5495 for (i=0; i < (ssize_t) length; i++)
5496 {
5497 int
5498 c;
5499
5500 c=ReadBlobByte(image);
5501 if (c == EOF)
5502 {
5503 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5504 ThrowReaderException(CorruptImageError,
5505 "InsufficientImageDataInFile");
5506 }
5507 chunk[i]=(unsigned char) c;
5508 }
5509
5510 p=chunk;
5511 }
5512
5513 (void) ReadBlobMSBLong(image); /* read crc word */
5514
5515 #if !defined(JNG_SUPPORTED)
5516 if (memcmp(type,mng_JHDR,4) == 0)
5517 {
5518 skip_to_iend=MagickTrue;
5519
5520 if (mng_info->jhdr_warning == 0)
5521 (void) ThrowMagickException(exception,GetMagickModule(),
5522 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5523
5524 mng_info->jhdr_warning++;
5525 }
5526 #endif
5527 if (memcmp(type,mng_DHDR,4) == 0)
5528 {
5529 skip_to_iend=MagickTrue;
5530
5531 if (mng_info->dhdr_warning == 0)
5532 (void) ThrowMagickException(exception,GetMagickModule(),
5533 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5534
5535 mng_info->dhdr_warning++;
5536 }
5537 if (memcmp(type,mng_MEND,4) == 0)
5538 {
5539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5540 break;
5541 }
5542
5543 if (skip_to_iend)
5544 {
5545 if (memcmp(type,mng_IEND,4) == 0)
5546 skip_to_iend=MagickFalse;
5547
5548 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5549
5550 if (logging != MagickFalse)
5551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5552 " Skip to IEND.");
5553
5554 continue;
5555 }
5556
5557 if (memcmp(type,mng_MHDR,4) == 0)
5558 {
5559 if (length != 28)
5560 {
5561 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5562 ThrowReaderException(CorruptImageError,"CorruptImage");
5563 }
5564
5565 mng_info->mng_width=(unsigned long)mng_get_long(p);
5566 mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5567
5568 if (logging != MagickFalse)
5569 {
5570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5571 " MNG width: %.20g",(double) mng_info->mng_width);
5572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5573 " MNG height: %.20g",(double) mng_info->mng_height);
5574 }
5575
5576 p+=8;
5577 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5578
5579 if (mng_info->ticks_per_second == 0)
5580 default_frame_delay=0;
5581
5582 else
5583 default_frame_delay=1UL*image->ticks_per_second/
5584 mng_info->ticks_per_second;
5585
5586 frame_delay=default_frame_delay;
5587 simplicity=0;
5588
5589 p+=16;
5590 simplicity=(size_t) mng_get_long(p);
5591
5592 mng_type=1; /* Full MNG */
5593
5594 if ((simplicity != 0) && ((simplicity | 11) == 11))
5595 mng_type=2; /* LC */
5596
5597 if ((simplicity != 0) && ((simplicity | 9) == 9))
5598 mng_type=3; /* VLC */
5599
5600 #if defined(MNG_INSERT_LAYERS)
5601 if (mng_type != 3)
5602 insert_layers=MagickTrue;
5603 #endif
5604 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5605 {
5606 /* Allocate next image structure. */
5607 AcquireNextImage(image_info,image,exception);
5608
5609 if (GetNextImageInList(image) == (Image *) NULL)
5610 return((Image *) NULL);
5611
5612 image=SyncNextImageInList(image);
5613 mng_info->image=image;
5614 }
5615
5616 if ((mng_info->mng_width > 65535L) ||
5617 (mng_info->mng_height > 65535L))
5618 {
5619 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5620 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5621 }
5622
5623 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5624 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5625 mng_info->mng_height);
5626
5627 mng_info->frame.left=0;
5628 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5629 mng_info->frame.top=0;
5630 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5631 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5632
5633 for (i=0; i < MNG_MAX_OBJECTS; i++)
5634 mng_info->object_clip[i]=mng_info->frame;
5635
5636 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5637 continue;
5638 }
5639
5640 if (memcmp(type,mng_TERM,4) == 0)
5641 {
5642 int
5643 repeat=0;
5644
5645 if (length != 0)
5646 repeat=p[0];
5647
5648 if (repeat == 3 && length > 9)
5649 {
5650 final_delay=(png_uint_32) mng_get_long(&p[2]);
5651 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5652
5653 if (mng_iterations == PNG_UINT_31_MAX)
5654 mng_iterations=0;
5655
5656 image->iterations=mng_iterations;
5657 term_chunk_found=MagickTrue;
5658 }
5659
5660 if (logging != MagickFalse)
5661 {
5662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5663 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5664 repeat,(double) final_delay, (double) image->iterations);
5665 }
5666
5667 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5668 continue;
5669 }
5670 if (memcmp(type,mng_DEFI,4) == 0)
5671 {
5672 if (mng_type == 3)
5673 {
5674 (void) ThrowMagickException(exception,GetMagickModule(),
5675 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5676 image->filename);
5677 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5678 continue;
5679 }
5680
5681 if (length < 2)
5682 {
5683 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5684 ThrowReaderException(CorruptImageError,"CorruptImage");
5685 }
5686
5687 object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5688
5689 if (mng_type == 2 && object_id != 0)
5690 (void) ThrowMagickException(exception,GetMagickModule(),
5691 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5692 image->filename);
5693
5694 if (object_id >= MNG_MAX_OBJECTS)
5695 {
5696 /*
5697 Instead of using a warning we should allocate a larger
5698 MngInfo structure and continue.
5699 */
5700 (void) ThrowMagickException(exception,GetMagickModule(),
5701 CoderError,"object id too large","`%s'",image->filename);
5702 object_id=MNG_MAX_OBJECTS-1;
5703 }
5704
5705 if (mng_info->exists[object_id])
5706 if (mng_info->frozen[object_id])
5707 {
5708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5709 (void) ThrowMagickException(exception,
5710 GetMagickModule(),CoderError,
5711 "DEFI cannot redefine a frozen MNG object","`%s'",
5712 image->filename);
5713 continue;
5714 }
5715
5716 mng_info->exists[object_id]=MagickTrue;
5717
5718 if (length > 2)
5719 mng_info->invisible[object_id]=p[2];
5720
5721 /*
5722 Extract object offset info.
5723 */
5724 if (length > 11)
5725 {
5726 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5727 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5728 if (logging != MagickFalse)
5729 {
5730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5731 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5732 object_id,(double) mng_info->x_off[object_id],
5733 object_id,(double) mng_info->y_off[object_id]);
5734 }
5735 }
5736
5737 /*
5738 Extract object clipping info.
5739 */
5740 if (length > 27)
5741 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5742 &p[12]);
5743
5744 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5745 continue;
5746 }
5747 if (memcmp(type,mng_bKGD,4) == 0)
5748 {
5749 mng_info->have_global_bkgd=MagickFalse;
5750
5751 if (length > 5)
5752 {
5753 mng_info->mng_global_bkgd.red=
5754 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5755
5756 mng_info->mng_global_bkgd.green=
5757 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5758
5759 mng_info->mng_global_bkgd.blue=
5760 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5761
5762 mng_info->have_global_bkgd=MagickTrue;
5763 }
5764
5765 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5766 continue;
5767 }
5768 if (memcmp(type,mng_BACK,4) == 0)
5769 {
5770 #if defined(MNG_INSERT_LAYERS)
5771 if (length > 6)
5772 mandatory_back=p[6];
5773
5774 else
5775 mandatory_back=0;
5776
5777 if (mandatory_back && length > 5)
5778 {
5779 mng_background_color.red=
5780 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5781
5782 mng_background_color.green=
5783 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5784
5785 mng_background_color.blue=
5786 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5787
5788 mng_background_color.alpha=OpaqueAlpha;
5789 }
5790
5791 #ifdef MNG_OBJECT_BUFFERS
5792 if (length > 8)
5793 mng_background_object=(p[7] << 8) | p[8];
5794 #endif
5795 #endif
5796 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5797 continue;
5798 }
5799
5800 if (memcmp(type,mng_PLTE,4) == 0)
5801 {
5802 /* Read global PLTE. */
5803
5804 if (length && (length < 769))
5805 {
5806 /* Read global PLTE. */
5807
5808 if (mng_info->global_plte == (png_colorp) NULL)
5809 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5810 sizeof(*mng_info->global_plte));
5811
5812 if (mng_info->global_plte == (png_colorp) NULL)
5813 {
5814 mng_info->global_plte_length=0;
5815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816 ThrowReaderException(ResourceLimitError,
5817 "MemoryAllocationFailed");
5818 }
5819
5820 for (i=0; i < (ssize_t) (length/3); i++)
5821 {
5822 mng_info->global_plte[i].red=p[3*i];
5823 mng_info->global_plte[i].green=p[3*i+1];
5824 mng_info->global_plte[i].blue=p[3*i+2];
5825 }
5826
5827 mng_info->global_plte_length=(unsigned int) (length/3);
5828 }
5829 #ifdef MNG_LOOSE
5830 for ( ; i < 256; i++)
5831 {
5832 mng_info->global_plte[i].red=i;
5833 mng_info->global_plte[i].green=i;
5834 mng_info->global_plte[i].blue=i;
5835 }
5836
5837 if (length != 0)
5838 mng_info->global_plte_length=256;
5839 #endif
5840 else
5841 mng_info->global_plte_length=0;
5842
5843 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5844 continue;
5845 }
5846
5847 if (memcmp(type,mng_tRNS,4) == 0)
5848 {
5849 /* read global tRNS */
5850
5851 if (length > 0 && length < 257)
5852 for (i=0; i < (ssize_t) length; i++)
5853 mng_info->global_trns[i]=p[i];
5854
5855 #ifdef MNG_LOOSE
5856 for ( ; i < 256; i++)
5857 mng_info->global_trns[i]=255;
5858 #endif
5859 mng_info->global_trns_length=(unsigned int) length;
5860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5861 continue;
5862 }
5863 if (memcmp(type,mng_gAMA,4) == 0)
5864 {
5865 if (length == 4)
5866 {
5867 ssize_t
5868 igamma;
5869
5870 igamma=mng_get_long(p);
5871 mng_info->global_gamma=((float) igamma)*0.00001;
5872 mng_info->have_global_gama=MagickTrue;
5873 }
5874
5875 else
5876 mng_info->have_global_gama=MagickFalse;
5877
5878 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5879 continue;
5880 }
5881
5882 if (memcmp(type,mng_cHRM,4) == 0)
5883 {
5884 /* Read global cHRM */
5885
5886 if (length == 32)
5887 {
5888 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5889 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5890 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5891 mng_info->global_chrm.red_primary.y=0.00001*
5892 mng_get_long(&p[12]);
5893 mng_info->global_chrm.green_primary.x=0.00001*
5894 mng_get_long(&p[16]);
5895 mng_info->global_chrm.green_primary.y=0.00001*
5896 mng_get_long(&p[20]);
5897 mng_info->global_chrm.blue_primary.x=0.00001*
5898 mng_get_long(&p[24]);
5899 mng_info->global_chrm.blue_primary.y=0.00001*
5900 mng_get_long(&p[28]);
5901 mng_info->have_global_chrm=MagickTrue;
5902 }
5903 else
5904 mng_info->have_global_chrm=MagickFalse;
5905
5906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5907 continue;
5908 }
5909
5910 if (memcmp(type,mng_sRGB,4) == 0)
5911 {
5912 /*
5913 Read global sRGB.
5914 */
5915 if (length != 0)
5916 {
5917 mng_info->global_srgb_intent=
5918 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5919 mng_info->have_global_srgb=MagickTrue;
5920 }
5921 else
5922 mng_info->have_global_srgb=MagickFalse;
5923
5924 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5925 continue;
5926 }
5927
5928 if (memcmp(type,mng_iCCP,4) == 0)
5929 {
5930 /* To do: */
5931
5932 /*
5933 Read global iCCP.
5934 */
5935 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5936
5937 continue;
5938 }
5939
5940 if (memcmp(type,mng_FRAM,4) == 0)
5941 {
5942 if (mng_type == 3)
5943 (void) ThrowMagickException(exception,GetMagickModule(),
5944 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5945 image->filename);
5946
5947 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5948 image->delay=frame_delay;
5949
5950 frame_delay=default_frame_delay;
5951 frame_timeout=default_frame_timeout;
5952 fb=default_fb;
5953
5954 if (length != 0)
5955 if (p[0])
5956 mng_info->framing_mode=p[0];
5957
5958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5960 " Framing_mode=%d",mng_info->framing_mode);
5961
5962 if (length > 6)
5963 {
5964 /* Note the delay and frame clipping boundaries. */
5965
5966 p++; /* framing mode */
5967
5968 while (((p-chunk) < (long) length) && *p)
5969 p++; /* frame name */
5970
5971 p++; /* frame name terminator */
5972
5973 if ((p-chunk) < (ssize_t) (length-4))
5974 {
5975 int
5976 change_delay,
5977 change_timeout,
5978 change_clipping;
5979
5980 change_delay=(*p++);
5981 change_timeout=(*p++);
5982 change_clipping=(*p++);
5983 p++; /* change_sync */
5984
5985 if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5986 {
5987 frame_delay=1UL*image->ticks_per_second*
5988 mng_get_long(p);
5989
5990 if (mng_info->ticks_per_second != 0)
5991 frame_delay/=mng_info->ticks_per_second;
5992
5993 else
5994 frame_delay=PNG_UINT_31_MAX;
5995
5996 if (change_delay == 2)
5997 default_frame_delay=frame_delay;
5998
5999 p+=4;
6000
6001 if (logging != MagickFalse)
6002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6003 " Framing_delay=%.20g",(double) frame_delay);
6004 }
6005
6006 if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
6007 {
6008 frame_timeout=1UL*image->ticks_per_second*
6009 mng_get_long(p);
6010
6011 if (mng_info->ticks_per_second != 0)
6012 frame_timeout/=mng_info->ticks_per_second;
6013
6014 else
6015 frame_timeout=PNG_UINT_31_MAX;
6016
6017 if (change_timeout == 2)
6018 default_frame_timeout=frame_timeout;
6019
6020 p+=4;
6021
6022 if (logging != MagickFalse)
6023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6024 " Framing_timeout=%.20g",(double) frame_timeout);
6025 }
6026
6027 if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6028 {
6029 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6030 p+=16;
6031 previous_fb=fb;
6032
6033 if (logging != MagickFalse)
6034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6035 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6036 (double) fb.left,(double) fb.right,(double) fb.top,
6037 (double) fb.bottom);
6038
6039 if (change_clipping == 2)
6040 default_fb=fb;
6041 }
6042 }
6043 }
6044 mng_info->clip=fb;
6045 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6046
6047 subframe_width=(size_t) (mng_info->clip.right
6048 -mng_info->clip.left);
6049
6050 subframe_height=(size_t) (mng_info->clip.bottom
6051 -mng_info->clip.top);
6052 /*
6053 Insert a background layer behind the frame if framing_mode is 4.
6054 */
6055 #if defined(MNG_INSERT_LAYERS)
6056 if (logging != MagickFalse)
6057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6058 " subframe_width=%.20g, subframe_height=%.20g",(double)
6059 subframe_width,(double) subframe_height);
6060
6061 if (insert_layers && (mng_info->framing_mode == 4) &&
6062 (subframe_width) && (subframe_height))
6063 {
6064 /* Allocate next image structure. */
6065 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6066 {
6067 AcquireNextImage(image_info,image,exception);
6068
6069 if (GetNextImageInList(image) == (Image *) NULL)
6070 return(DestroyImageList(image));
6071
6072 image=SyncNextImageInList(image);
6073 }
6074
6075 mng_info->image=image;
6076
6077 if (term_chunk_found)
6078 {
6079 image->start_loop=MagickTrue;
6080 image->iterations=mng_iterations;
6081 term_chunk_found=MagickFalse;
6082 }
6083
6084 else
6085 image->start_loop=MagickFalse;
6086
6087 image->columns=subframe_width;
6088 image->rows=subframe_height;
6089 image->page.width=subframe_width;
6090 image->page.height=subframe_height;
6091 image->page.x=mng_info->clip.left;
6092 image->page.y=mng_info->clip.top;
6093 image->background_color=mng_background_color;
6094 image->alpha_trait=UndefinedPixelTrait;
6095 image->delay=0;
6096 if (SetImageBackgroundColor(image,exception) == MagickFalse)
6097 {
6098 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6099 return(DestroyImageList(image));
6100 }
6101 if (logging != MagickFalse)
6102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6103 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6104 (double) mng_info->clip.left,
6105 (double) mng_info->clip.right,
6106 (double) mng_info->clip.top,
6107 (double) mng_info->clip.bottom);
6108 }
6109 #endif
6110 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6111 continue;
6112 }
6113
6114 if (memcmp(type,mng_CLIP,4) == 0)
6115 {
6116 unsigned int
6117 first_object,
6118 last_object;
6119
6120 /*
6121 Read CLIP.
6122 */
6123 if (length > 3)
6124 {
6125 first_object=(p[0] << 8) | p[1];
6126 last_object=(p[2] << 8) | p[3];
6127 p+=4;
6128
6129 for (i=(int) first_object; i <= (int) last_object; i++)
6130 {
6131 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6132 continue;
6133
6134 if (mng_info->exists[i] && !mng_info->frozen[i])
6135 {
6136 MngBox
6137 box;
6138
6139 box=mng_info->object_clip[i];
6140 if ((p-chunk) < (ssize_t) (length-17))
6141 mng_info->object_clip[i]=
6142 mng_read_box(box,(char) p[0],&p[1]);
6143 }
6144 }
6145
6146 }
6147 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6148 continue;
6149 }
6150
6151 if (memcmp(type,mng_SAVE,4) == 0)
6152 {
6153 for (i=1; i < MNG_MAX_OBJECTS; i++)
6154 if (mng_info->exists[i])
6155 {
6156 mng_info->frozen[i]=MagickTrue;
6157 #ifdef MNG_OBJECT_BUFFERS
6158 if (mng_info->ob[i] != (MngBuffer *) NULL)
6159 mng_info->ob[i]->frozen=MagickTrue;
6160 #endif
6161 }
6162
6163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6164
6165 continue;
6166 }
6167
6168 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6169 {
6170 /* Read DISC or SEEK. */
6171
6172 if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6173 {
6174 for (i=1; i < MNG_MAX_OBJECTS; i++)
6175 MngInfoDiscardObject(mng_info,i);
6176 }
6177
6178 else
6179 {
6180 register ssize_t
6181 j;
6182
6183 for (j=1; j < (ssize_t) length; j+=2)
6184 {
6185 i=p[j-1] << 8 | p[j];
6186 MngInfoDiscardObject(mng_info,i);
6187 }
6188 }
6189
6190 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6191
6192 continue;
6193 }
6194
6195 if (memcmp(type,mng_MOVE,4) == 0)
6196 {
6197 size_t
6198 first_object,
6199 last_object;
6200
6201 /* read MOVE */
6202
6203 if (length > 3)
6204 {
6205 first_object=(p[0] << 8) | p[1];
6206 last_object=(p[2] << 8) | p[3];
6207 p+=4;
6208
6209 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6210 {
6211 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6212 continue;
6213
6214 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6215 (p-chunk) < (ssize_t) (length-8))
6216 {
6217 MngPair
6218 new_pair;
6219
6220 MngPair
6221 old_pair;
6222
6223 old_pair.a=mng_info->x_off[i];
6224 old_pair.b=mng_info->y_off[i];
6225 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6226 mng_info->x_off[i]=new_pair.a;
6227 mng_info->y_off[i]=new_pair.b;
6228 }
6229 }
6230 }
6231
6232 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6233 continue;
6234 }
6235
6236 if (memcmp(type,mng_LOOP,4) == 0)
6237 {
6238 ssize_t loop_iters=1;
6239 if (length > 4)
6240 {
6241 loop_level=chunk[0];
6242 mng_info->loop_active[loop_level]=1; /* mark loop active */
6243
6244 /* Record starting point. */
6245 loop_iters=mng_get_long(&chunk[1]);
6246
6247 if (logging != MagickFalse)
6248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6249 " LOOP level %.20g has %.20g iterations ",
6250 (double) loop_level, (double) loop_iters);
6251
6252 if (loop_iters <= 0)
6253 skipping_loop=loop_level;
6254
6255 else
6256 {
6257 if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6258 loop_iters=GetMagickResourceLimit(ListLengthResource);
6259 if (loop_iters >= 2147483647L)
6260 loop_iters=2147483647L;
6261 mng_info->loop_jump[loop_level]=TellBlob(image);
6262 mng_info->loop_count[loop_level]=loop_iters;
6263 }
6264
6265 mng_info->loop_iteration[loop_level]=0;
6266 }
6267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6268 continue;
6269 }
6270
6271 if (memcmp(type,mng_ENDL,4) == 0)
6272 {
6273 if (length > 0)
6274 {
6275 loop_level=chunk[0];
6276
6277 if (skipping_loop > 0)
6278 {
6279 if (skipping_loop == loop_level)
6280 {
6281 /*
6282 Found end of zero-iteration loop.
6283 */
6284 skipping_loop=(-1);
6285 mng_info->loop_active[loop_level]=0;
6286 }
6287 }
6288
6289 else
6290 {
6291 if (mng_info->loop_active[loop_level] == 1)
6292 {
6293 mng_info->loop_count[loop_level]--;
6294 mng_info->loop_iteration[loop_level]++;
6295
6296 if (logging != MagickFalse)
6297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6298 " ENDL: LOOP level %.20g has %.20g remaining iters",
6299 (double) loop_level,(double)
6300 mng_info->loop_count[loop_level]);
6301
6302 if (mng_info->loop_count[loop_level] > 0)
6303 {
6304 offset=
6305 SeekBlob(image,mng_info->loop_jump[loop_level],
6306 SEEK_SET);
6307
6308 if (offset < 0)
6309 {
6310 chunk=(unsigned char *) RelinquishMagickMemory(
6311 chunk);
6312 ThrowReaderException(CorruptImageError,
6313 "ImproperImageHeader");
6314 }
6315 }
6316
6317 else
6318 {
6319 short
6320 last_level;
6321
6322 /*
6323 Finished loop.
6324 */
6325 mng_info->loop_active[loop_level]=0;
6326 last_level=(-1);
6327 for (i=0; i < loop_level; i++)
6328 if (mng_info->loop_active[i] == 1)
6329 last_level=(short) i;
6330 loop_level=last_level;
6331 }
6332 }
6333 }
6334 }
6335
6336 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6337 continue;
6338 }
6339
6340 if (memcmp(type,mng_CLON,4) == 0)
6341 {
6342 if (mng_info->clon_warning == 0)
6343 (void) ThrowMagickException(exception,GetMagickModule(),
6344 CoderError,"CLON is not implemented yet","`%s'",
6345 image->filename);
6346
6347 mng_info->clon_warning++;
6348 }
6349
6350 if (memcmp(type,mng_MAGN,4) == 0)
6351 {
6352 png_uint_16
6353 magn_first,
6354 magn_last,
6355 magn_mb,
6356 magn_ml,
6357 magn_mr,
6358 magn_mt,
6359 magn_mx,
6360 magn_my,
6361 magn_methx,
6362 magn_methy;
6363
6364 if (length > 1)
6365 magn_first=(p[0] << 8) | p[1];
6366
6367 else
6368 magn_first=0;
6369
6370 if (length > 3)
6371 magn_last=(p[2] << 8) | p[3];
6372
6373 else
6374 magn_last=magn_first;
6375 #ifndef MNG_OBJECT_BUFFERS
6376 if (magn_first || magn_last)
6377 if (mng_info->magn_warning == 0)
6378 {
6379 (void) ThrowMagickException(exception,
6380 GetMagickModule(),CoderError,
6381 "MAGN is not implemented yet for nonzero objects",
6382 "`%s'",image->filename);
6383
6384 mng_info->magn_warning++;
6385 }
6386 #endif
6387 if (length > 4)
6388 magn_methx=p[4];
6389
6390 else
6391 magn_methx=0;
6392
6393 if (length > 6)
6394 magn_mx=(p[5] << 8) | p[6];
6395
6396 else
6397 magn_mx=1;
6398
6399 if (magn_mx == 0)
6400 magn_mx=1;
6401
6402 if (length > 8)
6403 magn_my=(p[7] << 8) | p[8];
6404
6405 else
6406 magn_my=magn_mx;
6407
6408 if (magn_my == 0)
6409 magn_my=1;
6410
6411 if (length > 10)
6412 magn_ml=(p[9] << 8) | p[10];
6413
6414 else
6415 magn_ml=magn_mx;
6416
6417 if (magn_ml == 0)
6418 magn_ml=1;
6419
6420 if (length > 12)
6421 magn_mr=(p[11] << 8) | p[12];
6422
6423 else
6424 magn_mr=magn_mx;
6425
6426 if (magn_mr == 0)
6427 magn_mr=1;
6428
6429 if (length > 14)
6430 magn_mt=(p[13] << 8) | p[14];
6431
6432 else
6433 magn_mt=magn_my;
6434
6435 if (magn_mt == 0)
6436 magn_mt=1;
6437
6438 if (length > 16)
6439 magn_mb=(p[15] << 8) | p[16];
6440
6441 else
6442 magn_mb=magn_my;
6443
6444 if (magn_mb == 0)
6445 magn_mb=1;
6446
6447 if (length > 17)
6448 magn_methy=p[17];
6449
6450 else
6451 magn_methy=magn_methx;
6452
6453
6454 if (magn_methx > 5 || magn_methy > 5)
6455 if (mng_info->magn_warning == 0)
6456 {
6457 (void) ThrowMagickException(exception,
6458 GetMagickModule(),CoderError,
6459 "Unknown MAGN method in MNG datastream","`%s'",
6460 image->filename);
6461
6462 mng_info->magn_warning++;
6463 }
6464 #ifdef MNG_OBJECT_BUFFERS
6465 /* Magnify existing objects in the range magn_first to magn_last */
6466 #endif
6467 if (magn_first == 0 || magn_last == 0)
6468 {
6469 /* Save the magnification factors for object 0 */
6470 mng_info->magn_mb=magn_mb;
6471 mng_info->magn_ml=magn_ml;
6472 mng_info->magn_mr=magn_mr;
6473 mng_info->magn_mt=magn_mt;
6474 mng_info->magn_mx=magn_mx;
6475 mng_info->magn_my=magn_my;
6476 mng_info->magn_methx=magn_methx;
6477 mng_info->magn_methy=magn_methy;
6478 }
6479 }
6480
6481 if (memcmp(type,mng_PAST,4) == 0)
6482 {
6483 if (mng_info->past_warning == 0)
6484 (void) ThrowMagickException(exception,GetMagickModule(),
6485 CoderError,"PAST is not implemented yet","`%s'",
6486 image->filename);
6487
6488 mng_info->past_warning++;
6489 }
6490
6491 if (memcmp(type,mng_SHOW,4) == 0)
6492 {
6493 if (mng_info->show_warning == 0)
6494 (void) ThrowMagickException(exception,GetMagickModule(),
6495 CoderError,"SHOW is not implemented yet","`%s'",
6496 image->filename);
6497
6498 mng_info->show_warning++;
6499 }
6500
6501 if (memcmp(type,mng_sBIT,4) == 0)
6502 {
6503 if (length < 4)
6504 mng_info->have_global_sbit=MagickFalse;
6505
6506 else
6507 {
6508 mng_info->global_sbit.gray=p[0];
6509 mng_info->global_sbit.red=p[0];
6510 mng_info->global_sbit.green=p[1];
6511 mng_info->global_sbit.blue=p[2];
6512 mng_info->global_sbit.alpha=p[3];
6513 mng_info->have_global_sbit=MagickTrue;
6514 }
6515 }
6516 if (memcmp(type,mng_pHYs,4) == 0)
6517 {
6518 if (length > 8)
6519 {
6520 mng_info->global_x_pixels_per_unit=
6521 (size_t) mng_get_long(p);
6522 mng_info->global_y_pixels_per_unit=
6523 (size_t) mng_get_long(&p[4]);
6524 mng_info->global_phys_unit_type=p[8];
6525 mng_info->have_global_phys=MagickTrue;
6526 }
6527
6528 else
6529 mng_info->have_global_phys=MagickFalse;
6530 }
6531 if (memcmp(type,mng_pHYg,4) == 0)
6532 {
6533 if (mng_info->phyg_warning == 0)
6534 (void) ThrowMagickException(exception,GetMagickModule(),
6535 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6536
6537 mng_info->phyg_warning++;
6538 }
6539 if (memcmp(type,mng_BASI,4) == 0)
6540 {
6541 skip_to_iend=MagickTrue;
6542
6543 if (mng_info->basi_warning == 0)
6544 (void) ThrowMagickException(exception,GetMagickModule(),
6545 CoderError,"BASI is not implemented yet","`%s'",
6546 image->filename);
6547
6548 mng_info->basi_warning++;
6549 #ifdef MNG_BASI_SUPPORTED
6550 basi_width=(unsigned long) mng_get_long(p);
6551 basi_width=(unsigned long) mng_get_long(&p[4]);
6552 basi_color_type=p[8];
6553 basi_compression_method=p[9];
6554 basi_filter_type=p[10];
6555 basi_interlace_method=p[11];
6556 if (length > 11)
6557 basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6558
6559 else
6560 basi_red=0;
6561
6562 if (length > 13)
6563 basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6564
6565 else
6566 basi_green=0;
6567
6568 if (length > 15)
6569 basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6570
6571 else
6572 basi_blue=0;
6573
6574 if (length > 17)
6575 basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6576
6577 else
6578 {
6579 if (basi_sample_depth == 16)
6580 basi_alpha=65535L;
6581 else
6582 basi_alpha=255;
6583 }
6584
6585 if (length > 19)
6586 basi_viewable=p[20];
6587
6588 else
6589 basi_viewable=0;
6590
6591 #endif
6592 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6593 continue;
6594 }
6595
6596 if (memcmp(type,mng_IHDR,4)
6597 #if defined(JNG_SUPPORTED)
6598 && memcmp(type,mng_JHDR,4)
6599 #endif
6600 )
6601 {
6602 /* Not an IHDR or JHDR chunk */
6603 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6604
6605 continue;
6606 }
6607 /* Process IHDR */
6608 if (logging != MagickFalse)
6609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6610 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6611
6612 mng_info->exists[object_id]=MagickTrue;
6613 mng_info->viewable[object_id]=MagickTrue;
6614
6615 if (mng_info->invisible[object_id])
6616 {
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Skipping invisible object");
6620
6621 skip_to_iend=MagickTrue;
6622 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6623 continue;
6624 }
6625 #if defined(MNG_INSERT_LAYERS)
6626 if (length < 8)
6627 {
6628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6629 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6630 }
6631
6632 image_width=(size_t) mng_get_long(p);
6633 image_height=(size_t) mng_get_long(&p[4]);
6634 #endif
6635 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6636
6637 /*
6638 Insert a transparent background layer behind the entire animation
6639 if it is not full screen.
6640 */
6641 #if defined(MNG_INSERT_LAYERS)
6642 if (insert_layers && mng_type && first_mng_object)
6643 {
6644 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6645 (image_width < mng_info->mng_width) ||
6646 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6647 (image_height < mng_info->mng_height) ||
6648 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6649 {
6650 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6651 {
6652 /*
6653 Allocate next image structure.
6654 */
6655 AcquireNextImage(image_info,image,exception);
6656
6657 if (GetNextImageInList(image) == (Image *) NULL)
6658 return(DestroyImageList(image));
6659
6660 image=SyncNextImageInList(image);
6661 }
6662 mng_info->image=image;
6663
6664 if (term_chunk_found)
6665 {
6666 image->start_loop=MagickTrue;
6667 image->iterations=mng_iterations;
6668 term_chunk_found=MagickFalse;
6669 }
6670
6671 else
6672 image->start_loop=MagickFalse;
6673
6674 /* Make a background rectangle. */
6675
6676 image->delay=0;
6677 image->columns=mng_info->mng_width;
6678 image->rows=mng_info->mng_height;
6679 image->page.width=mng_info->mng_width;
6680 image->page.height=mng_info->mng_height;
6681 image->page.x=0;
6682 image->page.y=0;
6683 image->background_color=mng_background_color;
6684 (void) SetImageBackgroundColor(image,exception);
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687 " Inserted transparent background layer, W=%.20g, H=%.20g",
6688 (double) mng_info->mng_width,(double) mng_info->mng_height);
6689 }
6690 }
6691 /*
6692 Insert a background layer behind the upcoming image if
6693 framing_mode is 3, and we haven't already inserted one.
6694 */
6695 if (insert_layers && (mng_info->framing_mode == 3) &&
6696 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6697 (simplicity & 0x08)))
6698 {
6699 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6700 {
6701 /*
6702 Allocate next image structure.
6703 */
6704 AcquireNextImage(image_info,image,exception);
6705
6706 if (GetNextImageInList(image) == (Image *) NULL)
6707 return(DestroyImageList(image));
6708
6709 image=SyncNextImageInList(image);
6710 }
6711
6712 mng_info->image=image;
6713
6714 if (term_chunk_found)
6715 {
6716 image->start_loop=MagickTrue;
6717 image->iterations=mng_iterations;
6718 term_chunk_found=MagickFalse;
6719 }
6720
6721 else
6722 image->start_loop=MagickFalse;
6723
6724 image->delay=0;
6725 image->columns=subframe_width;
6726 image->rows=subframe_height;
6727 image->page.width=subframe_width;
6728 image->page.height=subframe_height;
6729 image->page.x=mng_info->clip.left;
6730 image->page.y=mng_info->clip.top;
6731 image->background_color=mng_background_color;
6732 image->alpha_trait=UndefinedPixelTrait;
6733 (void) SetImageBackgroundColor(image,exception);
6734
6735 if (logging != MagickFalse)
6736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6737 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6738 (double) mng_info->clip.left,(double) mng_info->clip.right,
6739 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6740 }
6741 #endif /* MNG_INSERT_LAYERS */
6742 first_mng_object=MagickFalse;
6743
6744 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6745 {
6746 /*
6747 Allocate next image structure.
6748 */
6749 AcquireNextImage(image_info,image,exception);
6750
6751 if (GetNextImageInList(image) == (Image *) NULL)
6752 return(DestroyImageList(image));
6753
6754 image=SyncNextImageInList(image);
6755 }
6756 mng_info->image=image;
6757 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6758 GetBlobSize(image));
6759
6760 if (status == MagickFalse)
6761 break;
6762
6763 if (term_chunk_found)
6764 {
6765 image->start_loop=MagickTrue;
6766 term_chunk_found=MagickFalse;
6767 }
6768
6769 else
6770 image->start_loop=MagickFalse;
6771
6772 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6773 {
6774 image->delay=frame_delay;
6775 frame_delay=default_frame_delay;
6776 }
6777
6778 else
6779 image->delay=0;
6780
6781 image->page.width=mng_info->mng_width;
6782 image->page.height=mng_info->mng_height;
6783 image->page.x=mng_info->x_off[object_id];
6784 image->page.y=mng_info->y_off[object_id];
6785 image->iterations=mng_iterations;
6786
6787 /*
6788 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6789 */
6790
6791 if (logging != MagickFalse)
6792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6794 type[2],type[3]);
6795
6796 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6797
6798 if (offset < 0)
6799 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6800 }
6801
6802 mng_info->image=image;
6803 mng_info->mng_type=mng_type;
6804 mng_info->object_id=object_id;
6805
6806 if (memcmp(type,mng_IHDR,4) == 0)
6807 image=ReadOnePNGImage(mng_info,image_info,exception);
6808
6809 #if defined(JNG_SUPPORTED)
6810 else
6811 image=ReadOneJNGImage(mng_info,image_info,exception);
6812 #endif
6813
6814 if (image == (Image *) NULL)
6815 {
6816 if (logging != MagickFalse)
6817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6818 "exit ReadJNGImage() with error");
6819
6820 return((Image *) NULL);
6821 }
6822
6823 if (image->columns == 0 || image->rows == 0)
6824 {
6825 (void) CloseBlob(image);
6826 return(DestroyImageList(image));
6827 }
6828
6829 mng_info->image=image;
6830
6831 if (mng_type)
6832 {
6833 MngBox
6834 crop_box;
6835
6836 if (mng_info->magn_methx || mng_info->magn_methy)
6837 {
6838 png_uint_32
6839 magnified_height,
6840 magnified_width;
6841
6842 if (logging != MagickFalse)
6843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6844 " Processing MNG MAGN chunk");
6845
6846 if (mng_info->magn_methx == 1)
6847 {
6848 magnified_width=mng_info->magn_ml;
6849
6850 if (image->columns > 1)
6851 magnified_width += mng_info->magn_mr;
6852
6853 if (image->columns > 2)
6854 magnified_width += (png_uint_32)
6855 ((image->columns-2)*(mng_info->magn_mx));
6856 }
6857
6858 else
6859 {
6860 magnified_width=(png_uint_32) image->columns;
6861
6862 if (image->columns > 1)
6863 magnified_width += mng_info->magn_ml-1;
6864
6865 if (image->columns > 2)
6866 magnified_width += mng_info->magn_mr-1;
6867
6868 if (image->columns > 3)
6869 magnified_width += (png_uint_32)
6870 ((image->columns-3)*(mng_info->magn_mx-1));
6871 }
6872
6873 if (mng_info->magn_methy == 1)
6874 {
6875 magnified_height=mng_info->magn_mt;
6876
6877 if (image->rows > 1)
6878 magnified_height += mng_info->magn_mb;
6879
6880 if (image->rows > 2)
6881 magnified_height += (png_uint_32)
6882 ((image->rows-2)*(mng_info->magn_my));
6883 }
6884
6885 else
6886 {
6887 magnified_height=(png_uint_32) image->rows;
6888
6889 if (image->rows > 1)
6890 magnified_height += mng_info->magn_mt-1;
6891
6892 if (image->rows > 2)
6893 magnified_height += mng_info->magn_mb-1;
6894
6895 if (image->rows > 3)
6896 magnified_height += (png_uint_32)
6897 ((image->rows-3)*(mng_info->magn_my-1));
6898 }
6899
6900 if (magnified_height > image->rows ||
6901 magnified_width > image->columns)
6902 {
6903 Image
6904 *large_image;
6905
6906 int
6907 yy;
6908
6909 Quantum
6910 *next,
6911 *prev;
6912
6913 png_uint_16
6914 magn_methx,
6915 magn_methy;
6916
6917 ssize_t
6918 m,
6919 y;
6920
6921 register Quantum
6922 *n,
6923 *q;
6924
6925 register ssize_t
6926 x;
6927
6928 /* Allocate next image structure. */
6929
6930 if (logging != MagickFalse)
6931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 " Allocate magnified image");
6933
6934 AcquireNextImage(image_info,image,exception);
6935
6936 if (GetNextImageInList(image) == (Image *) NULL)
6937 return(DestroyImageList(image));
6938
6939 large_image=SyncNextImageInList(image);
6940
6941 large_image->columns=magnified_width;
6942 large_image->rows=magnified_height;
6943
6944 magn_methx=mng_info->magn_methx;
6945 magn_methy=mng_info->magn_methy;
6946
6947 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6948 #define QM unsigned short
6949 if (magn_methx != 1 || magn_methy != 1)
6950 {
6951 /*
6952 Scale pixels to unsigned shorts to prevent
6953 overflow of intermediate values of interpolations
6954 */
6955 for (y=0; y < (ssize_t) image->rows; y++)
6956 {
6957 q=GetAuthenticPixels(image,0,y,image->columns,1,
6958 exception);
6959 if (q == (Quantum *) NULL)
6960 break;
6961 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6962 {
6963 SetPixelRed(image,ScaleQuantumToShort(
6964 GetPixelRed(image,q)),q);
6965 SetPixelGreen(image,ScaleQuantumToShort(
6966 GetPixelGreen(image,q)),q);
6967 SetPixelBlue(image,ScaleQuantumToShort(
6968 GetPixelBlue(image,q)),q);
6969 SetPixelAlpha(image,ScaleQuantumToShort(
6970 GetPixelAlpha(image,q)),q);
6971 q+=GetPixelChannels(image);
6972 }
6973
6974 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6975 break;
6976 }
6977 }
6978 #else
6979 #define QM Quantum
6980 #endif
6981
6982 if (image->alpha_trait != UndefinedPixelTrait)
6983 (void) SetImageBackgroundColor(large_image,exception);
6984
6985 else
6986 {
6987 large_image->background_color.alpha=OpaqueAlpha;
6988 (void) SetImageBackgroundColor(large_image,exception);
6989
6990 if (magn_methx == 4)
6991 magn_methx=2;
6992
6993 if (magn_methx == 5)
6994 magn_methx=3;
6995
6996 if (magn_methy == 4)
6997 magn_methy=2;
6998
6999 if (magn_methy == 5)
7000 magn_methy=3;
7001 }
7002
7003 /* magnify the rows into the right side of the large image */
7004
7005 if (logging != MagickFalse)
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " Magnify the rows to %.20g",
7008 (double) large_image->rows);
7009 m=(ssize_t) mng_info->magn_mt;
7010 yy=0;
7011 length=(size_t) GetPixelChannels(image)*image->columns;
7012 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7013 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7014
7015 if ((prev == (Quantum *) NULL) ||
7016 (next == (Quantum *) NULL))
7017 {
7018 if (prev != (Quantum *) NULL)
7019 prev=(Quantum *) RelinquishMagickMemory(prev);
7020 if (next != (Quantum *) NULL)
7021 next=(Quantum *) RelinquishMagickMemory(next);
7022 image=DestroyImageList(image);
7023 ThrowReaderException(ResourceLimitError,
7024 "MemoryAllocationFailed");
7025 }
7026
7027 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7028 (void) memcpy(next,n,length);
7029
7030 for (y=0; y < (ssize_t) image->rows; y++)
7031 {
7032 if (y == 0)
7033 m=(ssize_t) mng_info->magn_mt;
7034
7035 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7036 m=(ssize_t) mng_info->magn_mb;
7037
7038 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7039 m=(ssize_t) mng_info->magn_mb;
7040
7041 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7042 m=1;
7043
7044 else
7045 m=(ssize_t) mng_info->magn_my;
7046
7047 n=prev;
7048 prev=next;
7049 next=n;
7050
7051 if (y < (ssize_t) image->rows-1)
7052 {
7053 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7054 exception);
7055 (void) memcpy(next,n,length);
7056 }
7057
7058 for (i=0; i < m; i++, yy++)
7059 {
7060 register Quantum
7061 *pixels;
7062
7063 assert(yy < (ssize_t) large_image->rows);
7064 pixels=prev;
7065 n=next;
7066 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7067 1,exception);
7068 if (q == (Quantum *) NULL)
7069 break;
7070 q+=(large_image->columns-image->columns)*
7071 GetPixelChannels(large_image);
7072
7073 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7074 {
7075 /* To do: get color as function of indexes[x] */
7076 /*
7077 if (image->storage_class == PseudoClass)
7078 {
7079 }
7080 */
7081
7082 if (magn_methy <= 1)
7083 {
7084 /* replicate previous */
7085 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7086 SetPixelGreen(large_image,GetPixelGreen(image,
7087 pixels),q);
7088 SetPixelBlue(large_image,GetPixelBlue(image,
7089 pixels),q);
7090 SetPixelAlpha(large_image,GetPixelAlpha(image,
7091 pixels),q);
7092 }
7093
7094 else if (magn_methy == 2 || magn_methy == 4)
7095 {
7096 if (i == 0)
7097 {
7098 SetPixelRed(large_image,GetPixelRed(image,
7099 pixels),q);
7100 SetPixelGreen(large_image,GetPixelGreen(image,
7101 pixels),q);
7102 SetPixelBlue(large_image,GetPixelBlue(image,
7103 pixels),q);
7104 SetPixelAlpha(large_image,GetPixelAlpha(image,
7105 pixels),q);
7106 }
7107
7108 else
7109 {
7110 /* Interpolate */
7111 SetPixelRed(large_image,((QM) (((ssize_t)
7112 (2*i*(GetPixelRed(image,n)
7113 -GetPixelRed(image,pixels)+m))/
7114 ((ssize_t) (m*2))
7115 +GetPixelRed(image,pixels)))),q);
7116 SetPixelGreen(large_image,((QM) (((ssize_t)
7117 (2*i*(GetPixelGreen(image,n)
7118 -GetPixelGreen(image,pixels)+m))/
7119 ((ssize_t) (m*2))
7120 +GetPixelGreen(image,pixels)))),q);
7121 SetPixelBlue(large_image,((QM) (((ssize_t)
7122 (2*i*(GetPixelBlue(image,n)
7123 -GetPixelBlue(image,pixels)+m))/
7124 ((ssize_t) (m*2))
7125 +GetPixelBlue(image,pixels)))),q);
7126
7127 if (image->alpha_trait != UndefinedPixelTrait)
7128 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7129 (2*i*(GetPixelAlpha(image,n)
7130 -GetPixelAlpha(image,pixels)+m))
7131 /((ssize_t) (m*2))+
7132 GetPixelAlpha(image,pixels)))),q);
7133 }
7134
7135 if (magn_methy == 4)
7136 {
7137 /* Replicate nearest */
7138 if (i <= ((m+1) << 1))
7139 SetPixelAlpha(large_image,GetPixelAlpha(image,
7140 pixels),q);
7141 else
7142 SetPixelAlpha(large_image,GetPixelAlpha(image,
7143 n),q);
7144 }
7145 }
7146
7147 else /* if (magn_methy == 3 || magn_methy == 5) */
7148 {
7149 /* Replicate nearest */
7150 if (i <= ((m+1) << 1))
7151 {
7152 SetPixelRed(large_image,GetPixelRed(image,
7153 pixels),q);
7154 SetPixelGreen(large_image,GetPixelGreen(image,
7155 pixels),q);
7156 SetPixelBlue(large_image,GetPixelBlue(image,
7157 pixels),q);
7158 SetPixelAlpha(large_image,GetPixelAlpha(image,
7159 pixels),q);
7160 }
7161
7162 else
7163 {
7164 SetPixelRed(large_image,GetPixelRed(image,n),q);
7165 SetPixelGreen(large_image,GetPixelGreen(image,n),
7166 q);
7167 SetPixelBlue(large_image,GetPixelBlue(image,n),
7168 q);
7169 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7170 q);
7171 }
7172
7173 if (magn_methy == 5)
7174 {
7175 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7176 (GetPixelAlpha(image,n)
7177 -GetPixelAlpha(image,pixels))
7178 +m))/((ssize_t) (m*2))
7179 +GetPixelAlpha(image,pixels)),q);
7180 }
7181 }
7182 n+=GetPixelChannels(image);
7183 q+=GetPixelChannels(large_image);
7184 pixels+=GetPixelChannels(image);
7185 } /* x */
7186
7187 if (SyncAuthenticPixels(large_image,exception) == 0)
7188 break;
7189
7190 } /* i */
7191 } /* y */
7192
7193 prev=(Quantum *) RelinquishMagickMemory(prev);
7194 next=(Quantum *) RelinquishMagickMemory(next);
7195
7196 length=image->columns;
7197
7198 if (logging != MagickFalse)
7199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7200 " Delete original image");
7201
7202 DeleteImageFromList(&image);
7203
7204 image=large_image;
7205
7206 mng_info->image=image;
7207
7208 /* magnify the columns */
7209 if (logging != MagickFalse)
7210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7211 " Magnify the columns to %.20g",
7212 (double) image->columns);
7213
7214 for (y=0; y < (ssize_t) image->rows; y++)
7215 {
7216 register Quantum
7217 *pixels;
7218
7219 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7220 if (q == (Quantum *) NULL)
7221 break;
7222 pixels=q+(image->columns-length)*GetPixelChannels(image);
7223 n=pixels+GetPixelChannels(image);
7224
7225 for (x=(ssize_t) (image->columns-length);
7226 x < (ssize_t) image->columns; x++)
7227 {
7228 /* To do: Rewrite using Get/Set***PixelChannel() */
7229
7230 if (x == (ssize_t) (image->columns-length))
7231 m=(ssize_t) mng_info->magn_ml;
7232
7233 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7234 m=(ssize_t) mng_info->magn_mr;
7235
7236 else if (magn_methx <= 1 &&
7237 x == (ssize_t) image->columns-1)
7238 m=(ssize_t) mng_info->magn_mr;
7239
7240 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7241 m=1;
7242
7243 else
7244 m=(ssize_t) mng_info->magn_mx;
7245
7246 for (i=0; i < m; i++)
7247 {
7248 if (magn_methx <= 1)
7249 {
7250 /* replicate previous */
7251 SetPixelRed(image,GetPixelRed(image,pixels),q);
7252 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7253 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7254 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7255 }
7256
7257 else if (magn_methx == 2 || magn_methx == 4)
7258 {
7259 if (i == 0)
7260 {
7261 SetPixelRed(image,GetPixelRed(image,pixels),q);
7262 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7263 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7264 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7265 }
7266
7267 /* To do: Rewrite using Get/Set***PixelChannel() */
7268 else
7269 {
7270 /* Interpolate */
7271 SetPixelRed(image,(QM) ((2*i*(
7272 GetPixelRed(image,n)
7273 -GetPixelRed(image,pixels))+m)
7274 /((ssize_t) (m*2))+
7275 GetPixelRed(image,pixels)),q);
7276
7277 SetPixelGreen(image,(QM) ((2*i*(
7278 GetPixelGreen(image,n)
7279 -GetPixelGreen(image,pixels))+m)
7280 /((ssize_t) (m*2))+
7281 GetPixelGreen(image,pixels)),q);
7282
7283 SetPixelBlue(image,(QM) ((2*i*(
7284 GetPixelBlue(image,n)
7285 -GetPixelBlue(image,pixels))+m)
7286 /((ssize_t) (m*2))+
7287 GetPixelBlue(image,pixels)),q);
7288 if (image->alpha_trait != UndefinedPixelTrait)
7289 SetPixelAlpha(image,(QM) ((2*i*(
7290 GetPixelAlpha(image,n)
7291 -GetPixelAlpha(image,pixels))+m)
7292 /((ssize_t) (m*2))+
7293 GetPixelAlpha(image,pixels)),q);
7294 }
7295
7296 if (magn_methx == 4)
7297 {
7298 /* Replicate nearest */
7299 if (i <= ((m+1) << 1))
7300 {
7301 SetPixelAlpha(image,
7302 GetPixelAlpha(image,pixels)+0,q);
7303 }
7304 else
7305 {
7306 SetPixelAlpha(image,
7307 GetPixelAlpha(image,n)+0,q);
7308 }
7309 }
7310 }
7311
7312 else /* if (magn_methx == 3 || magn_methx == 5) */
7313 {
7314 /* Replicate nearest */
7315 if (i <= ((m+1) << 1))
7316 {
7317 SetPixelRed(image,GetPixelRed(image,pixels),q);
7318 SetPixelGreen(image,GetPixelGreen(image,
7319 pixels),q);
7320 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7321 SetPixelAlpha(image,GetPixelAlpha(image,
7322 pixels),q);
7323 }
7324
7325 else
7326 {
7327 SetPixelRed(image,GetPixelRed(image,n),q);
7328 SetPixelGreen(image,GetPixelGreen(image,n),q);
7329 SetPixelBlue(image,GetPixelBlue(image,n),q);
7330 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7331 }
7332
7333 if (magn_methx == 5)
7334 {
7335 /* Interpolate */
7336 SetPixelAlpha(image,
7337 (QM) ((2*i*( GetPixelAlpha(image,n)
7338 -GetPixelAlpha(image,pixels))+m)/
7339 ((ssize_t) (m*2))
7340 +GetPixelAlpha(image,pixels)),q);
7341 }
7342 }
7343 q+=GetPixelChannels(image);
7344 }
7345 n+=GetPixelChannels(image);
7346 }
7347
7348 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7349 break;
7350 }
7351 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7352 if (magn_methx != 1 || magn_methy != 1)
7353 {
7354 /*
7355 Rescale pixels to Quantum
7356 */
7357 for (y=0; y < (ssize_t) image->rows; y++)
7358 {
7359 q=GetAuthenticPixels(image,0,y,image->columns,1,
7360 exception);
7361 if (q == (Quantum *) NULL)
7362 break;
7363
7364 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7365 {
7366 SetPixelRed(image,ScaleShortToQuantum(
7367 GetPixelRed(image,q)),q);
7368 SetPixelGreen(image,ScaleShortToQuantum(
7369 GetPixelGreen(image,q)),q);
7370 SetPixelBlue(image,ScaleShortToQuantum(
7371 GetPixelBlue(image,q)),q);
7372 SetPixelAlpha(image,ScaleShortToQuantum(
7373 GetPixelAlpha(image,q)),q);
7374 q+=GetPixelChannels(image);
7375 }
7376
7377 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7378 break;
7379 }
7380 }
7381 #endif
7382 if (logging != MagickFalse)
7383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7384 " Finished MAGN processing");
7385 }
7386 }
7387
7388 /*
7389 Crop_box is with respect to the upper left corner of the MNG.
7390 */
7391 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7392 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7393 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7394 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7395 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7396 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7397 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7398 if ((crop_box.left != (mng_info->image_box.left
7399 +mng_info->x_off[object_id])) ||
7400 (crop_box.right != (mng_info->image_box.right
7401 +mng_info->x_off[object_id])) ||
7402 (crop_box.top != (mng_info->image_box.top
7403 +mng_info->y_off[object_id])) ||
7404 (crop_box.bottom != (mng_info->image_box.bottom
7405 +mng_info->y_off[object_id])))
7406 {
7407 if (logging != MagickFalse)
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " Crop the PNG image");
7410
7411 if ((crop_box.left < crop_box.right) &&
7412 (crop_box.top < crop_box.bottom))
7413 {
7414 Image
7415 *im;
7416
7417 RectangleInfo
7418 crop_info;
7419
7420 /*
7421 Crop_info is with respect to the upper left corner of
7422 the image.
7423 */
7424 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7425 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7426 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7427 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7428 image->page.width=image->columns;
7429 image->page.height=image->rows;
7430 image->page.x=0;
7431 image->page.y=0;
7432 im=CropImage(image,&crop_info,exception);
7433
7434 if (im != (Image *) NULL)
7435 {
7436 image->columns=im->columns;
7437 image->rows=im->rows;
7438 im=DestroyImage(im);
7439 image->page.width=image->columns;
7440 image->page.height=image->rows;
7441 image->page.x=crop_box.left;
7442 image->page.y=crop_box.top;
7443 }
7444 }
7445
7446 else
7447 {
7448 /*
7449 No pixels in crop area. The MNG spec still requires
7450 a layer, though, so make a single transparent pixel in
7451 the top left corner.
7452 */
7453 image->columns=1;
7454 image->rows=1;
7455 image->colors=2;
7456 (void) SetImageBackgroundColor(image,exception);
7457 image->page.width=1;
7458 image->page.height=1;
7459 image->page.x=0;
7460 image->page.y=0;
7461 }
7462 }
7463 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7464 image=mng_info->image;
7465 #endif
7466 }
7467
7468 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7469 /* PNG does not handle depths greater than 16 so reduce it even
7470 * if lossy.
7471 */
7472 if (image->depth > 16)
7473 image->depth=16;
7474 #endif
7475
7476 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7477 if (image->depth > 8)
7478 {
7479 /* To do: fill low byte properly */
7480 image->depth=16;
7481 }
7482
7483 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7484 image->depth = 8;
7485 #endif
7486
7487 if (image_info->number_scenes != 0)
7488 {
7489 if (mng_info->scenes_found >
7490 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7491 break;
7492 }
7493
7494 if (logging != MagickFalse)
7495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7496 " Finished reading image datastream.");
7497
7498 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7499
7500 (void) CloseBlob(image);
7501
7502 if (logging != MagickFalse)
7503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504 " Finished reading all image datastreams.");
7505
7506 #if defined(MNG_INSERT_LAYERS)
7507 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7508 (mng_info->mng_height))
7509 {
7510 /*
7511 Insert a background layer if nothing else was found.
7512 */
7513 if (logging != MagickFalse)
7514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7515 " No images found. Inserting a background layer.");
7516
7517 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7518 {
7519 /*
7520 Allocate next image structure.
7521 */
7522 AcquireNextImage(image_info,image,exception);
7523 if (GetNextImageInList(image) == (Image *) NULL)
7524 {
7525 if (logging != MagickFalse)
7526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7527 " Allocation failed, returning NULL.");
7528
7529 return(DestroyImageList(image));;
7530 }
7531 image=SyncNextImageInList(image);
7532 }
7533 image->columns=mng_info->mng_width;
7534 image->rows=mng_info->mng_height;
7535 image->page.width=mng_info->mng_width;
7536 image->page.height=mng_info->mng_height;
7537 image->page.x=0;
7538 image->page.y=0;
7539 image->background_color=mng_background_color;
7540 image->alpha_trait=UndefinedPixelTrait;
7541
7542 if (image_info->ping == MagickFalse)
7543 (void) SetImageBackgroundColor(image,exception);
7544
7545 mng_info->image_found++;
7546 }
7547 #endif
7548 image->iterations=mng_iterations;
7549
7550 if (mng_iterations == 1)
7551 image->start_loop=MagickTrue;
7552
7553 while (GetPreviousImageInList(image) != (Image *) NULL)
7554 {
7555 image_count++;
7556 if (image_count > 10*mng_info->image_found)
7557 {
7558 if (logging != MagickFalse)
7559 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7560
7561 (void) ThrowMagickException(exception,GetMagickModule(),
7562 CoderError,"Linked list is corrupted, beginning of list not found",
7563 "`%s'",image_info->filename);
7564
7565 return(DestroyImageList(image));
7566 }
7567
7568 image=GetPreviousImageInList(image);
7569
7570 if (GetNextImageInList(image) == (Image *) NULL)
7571 {
7572 if (logging != MagickFalse)
7573 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7574
7575 (void) ThrowMagickException(exception,GetMagickModule(),
7576 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7577 image_info->filename);
7578 }
7579 }
7580
7581 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7582 GetNextImageInList(image) ==
7583 (Image *) NULL)
7584 {
7585 if (logging != MagickFalse)
7586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587 " First image null");
7588
7589 (void) ThrowMagickException(exception,GetMagickModule(),
7590 CoderError,"image->next for first image is NULL but shouldn't be.",
7591 "`%s'",image_info->filename);
7592 }
7593
7594 if (mng_info->image_found == 0)
7595 {
7596 if (logging != MagickFalse)
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " No visible images found.");
7599
7600 (void) ThrowMagickException(exception,GetMagickModule(),
7601 CoderError,"No visible images in file","`%s'",image_info->filename);
7602
7603 return(DestroyImageList(image));
7604 }
7605
7606 if (mng_info->ticks_per_second)
7607 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7608 final_delay/mng_info->ticks_per_second;
7609
7610 else
7611 image->start_loop=MagickTrue;
7612
7613 /* Find final nonzero image delay */
7614 final_image_delay=0;
7615
7616 while (GetNextImageInList(image) != (Image *) NULL)
7617 {
7618 if (image->delay)
7619 final_image_delay=image->delay;
7620
7621 image=GetNextImageInList(image);
7622 }
7623
7624 if (final_delay < final_image_delay)
7625 final_delay=final_image_delay;
7626
7627 image->delay=final_delay;
7628
7629 if (logging != MagickFalse)
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7632 (double) final_delay);
7633
7634 if (logging != MagickFalse)
7635 {
7636 int
7637 scene;
7638
7639 scene=0;
7640 image=GetFirstImageInList(image);
7641
7642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7643 " Before coalesce:");
7644
7645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7646 " scene 0 delay=%.20g",(double) image->delay);
7647
7648 while (GetNextImageInList(image) != (Image *) NULL)
7649 {
7650 image=GetNextImageInList(image);
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " scene %.20g delay=%.20g",(double) scene++,
7653 (double) image->delay);
7654 }
7655 }
7656
7657 image=GetFirstImageInList(image);
7658 #ifdef MNG_COALESCE_LAYERS
7659 if (insert_layers)
7660 {
7661 Image
7662 *next_image,
7663 *next;
7664
7665 size_t
7666 scene;
7667
7668 if (logging != MagickFalse)
7669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670 " Coalesce Images");
7671
7672 scene=image->scene;
7673 next_image=CoalesceImages(image,exception);
7674
7675 if (next_image == (Image *) NULL)
7676 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7677
7678 image=DestroyImageList(image);
7679 image=next_image;
7680
7681 for (next=image; next != (Image *) NULL; next=next_image)
7682 {
7683 next->page.width=mng_info->mng_width;
7684 next->page.height=mng_info->mng_height;
7685 next->page.x=0;
7686 next->page.y=0;
7687 next->scene=scene++;
7688 next_image=GetNextImageInList(next);
7689
7690 if (next_image == (Image *) NULL)
7691 break;
7692
7693 if (next->delay == 0)
7694 {
7695 scene--;
7696 next_image->previous=GetPreviousImageInList(next);
7697 if (GetPreviousImageInList(next) == (Image *) NULL)
7698 image=next_image;
7699 else
7700 next->previous->next=next_image;
7701 next=DestroyImage(next);
7702 }
7703 }
7704 }
7705 #endif
7706
7707 while (GetNextImageInList(image) != (Image *) NULL)
7708 image=GetNextImageInList(image);
7709
7710 image->dispose=BackgroundDispose;
7711
7712 if (logging != MagickFalse)
7713 {
7714 int
7715 scene;
7716
7717 scene=0;
7718 image=GetFirstImageInList(image);
7719
7720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7721 " After coalesce:");
7722
7723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7724 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7725 (double) image->dispose);
7726
7727 while (GetNextImageInList(image) != (Image *) NULL)
7728 {
7729 image=GetNextImageInList(image);
7730
7731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7732 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7733 (double) image->delay,(double) image->dispose);
7734 }
7735 }
7736
7737 if (logging != MagickFalse)
7738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7739 " exit ReadOneMNGImage();");
7740
7741 return(image);
7742 }
7743
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7744 static Image *ReadMNGImage(const ImageInfo *image_info,
7745 ExceptionInfo *exception)
7746 {
7747 Image
7748 *image;
7749
7750 MagickBooleanType
7751 logging,
7752 status;
7753
7754 MngInfo
7755 *mng_info;
7756
7757 /* Open image file. */
7758
7759 assert(image_info != (const ImageInfo *) NULL);
7760 assert(image_info->signature == MagickCoreSignature);
7761 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7762 image_info->filename);
7763 assert(exception != (ExceptionInfo *) NULL);
7764 assert(exception->signature == MagickCoreSignature);
7765 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7766 image=AcquireImage(image_info,exception);
7767 mng_info=(MngInfo *) NULL;
7768 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7769
7770 if (status == MagickFalse)
7771 return(DestroyImageList(image));
7772
7773 /* Allocate a MngInfo structure. */
7774
7775 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7776
7777 if (mng_info == (MngInfo *) NULL)
7778 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7779
7780 /* Initialize members of the MngInfo structure. */
7781
7782 (void) memset(mng_info,0,sizeof(MngInfo));
7783 mng_info->image=image;
7784 image=ReadOneMNGImage(mng_info,image_info,exception);
7785 mng_info=MngInfoFreeStruct(mng_info);
7786
7787 if (image == (Image *) NULL)
7788 {
7789 if (logging != MagickFalse)
7790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7791 "exit ReadMNGImage() with error");
7792
7793 return((Image *) NULL);
7794 }
7795 (void) CloseBlob(image);
7796
7797 if (logging != MagickFalse)
7798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7799
7800 return(GetFirstImageInList(image));
7801 }
7802 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7803 static Image *ReadPNGImage(const ImageInfo *image_info,
7804 ExceptionInfo *exception)
7805 {
7806 printf("Your PNG library is too old: You have libpng-%s\n",
7807 PNG_LIBPNG_VER_STRING);
7808
7809 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7810 "PNG library is too old","`%s'",image_info->filename);
7811
7812 return(Image *) NULL;
7813 }
7814
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7815 static Image *ReadMNGImage(const ImageInfo *image_info,
7816 ExceptionInfo *exception)
7817 {
7818 return(ReadPNGImage(image_info,exception));
7819 }
7820 #endif /* PNG_LIBPNG_VER > 10011 */
7821 #endif
7822
7823 /*
7824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7825 % %
7826 % %
7827 % %
7828 % R e g i s t e r P N G I m a g e %
7829 % %
7830 % %
7831 % %
7832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7833 %
7834 % RegisterPNGImage() adds properties for the PNG image format to
7835 % the list of supported formats. The properties include the image format
7836 % tag, a method to read and/or write the format, whether the format
7837 % supports the saving of more than one frame to the same file or blob,
7838 % whether the format supports native in-memory I/O, and a brief
7839 % description of the format.
7840 %
7841 % The format of the RegisterPNGImage method is:
7842 %
7843 % size_t RegisterPNGImage(void)
7844 %
7845 */
RegisterPNGImage(void)7846 ModuleExport size_t RegisterPNGImage(void)
7847 {
7848 char
7849 version[MagickPathExtent];
7850
7851 MagickInfo
7852 *entry;
7853
7854 static const char
7855 *PNGNote=
7856 {
7857 "See http://www.libpng.org/ for details about the PNG format."
7858 },
7859
7860 *JNGNote=
7861 {
7862 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7863 "format."
7864 },
7865
7866 *MNGNote=
7867 {
7868 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7869 "format."
7870 };
7871
7872 *version='\0';
7873
7874 #if defined(PNG_LIBPNG_VER_STRING)
7875 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7876 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7877 MagickPathExtent);
7878
7879 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7880 {
7881 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7882 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7883 MagickPathExtent);
7884 }
7885 #endif
7886
7887 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7888 entry->flags|=CoderDecoderSeekableStreamFlag;
7889
7890 #if defined(MAGICKCORE_PNG_DELEGATE)
7891 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7892 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7893 #endif
7894
7895 entry->magick=(IsImageFormatHandler *) IsMNG;
7896
7897 if (*version != '\0')
7898 entry->version=ConstantString(version);
7899
7900 entry->mime_type=ConstantString("video/x-mng");
7901 entry->note=ConstantString(MNGNote);
7902 (void) RegisterMagickInfo(entry);
7903
7904 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7905
7906 #if defined(MAGICKCORE_PNG_DELEGATE)
7907 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7908 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7909 #endif
7910
7911 entry->magick=(IsImageFormatHandler *) IsPNG;
7912 entry->flags|=CoderDecoderSeekableStreamFlag;
7913 entry->flags^=CoderAdjoinFlag;
7914 entry->mime_type=ConstantString("image/png");
7915
7916 if (*version != '\0')
7917 entry->version=ConstantString(version);
7918
7919 entry->note=ConstantString(PNGNote);
7920 (void) RegisterMagickInfo(entry);
7921
7922 entry=AcquireMagickInfo("PNG","PNG8",
7923 "8-bit indexed with optional binary transparency");
7924
7925 #if defined(MAGICKCORE_PNG_DELEGATE)
7926 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7927 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7928 #endif
7929
7930 entry->magick=(IsImageFormatHandler *) IsPNG;
7931 entry->flags|=CoderDecoderSeekableStreamFlag;
7932 entry->flags^=CoderAdjoinFlag;
7933 entry->mime_type=ConstantString("image/png");
7934 (void) RegisterMagickInfo(entry);
7935
7936 entry=AcquireMagickInfo("PNG","PNG24",
7937 "opaque or binary transparent 24-bit RGB");
7938 *version='\0';
7939
7940 #if defined(ZLIB_VERSION)
7941 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7942 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7943
7944 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7945 {
7946 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7947 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7948 }
7949 #endif
7950
7951 if (*version != '\0')
7952 entry->version=ConstantString(version);
7953
7954 #if defined(MAGICKCORE_PNG_DELEGATE)
7955 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7956 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7957 #endif
7958
7959 entry->magick=(IsImageFormatHandler *) IsPNG;
7960 entry->flags|=CoderDecoderSeekableStreamFlag;
7961 entry->flags^=CoderAdjoinFlag;
7962 entry->mime_type=ConstantString("image/png");
7963 (void) RegisterMagickInfo(entry);
7964
7965 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7966
7967 #if defined(MAGICKCORE_PNG_DELEGATE)
7968 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7969 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7970 #endif
7971
7972 entry->magick=(IsImageFormatHandler *) IsPNG;
7973 entry->flags|=CoderDecoderSeekableStreamFlag;
7974 entry->flags^=CoderAdjoinFlag;
7975 entry->mime_type=ConstantString("image/png");
7976 (void) RegisterMagickInfo(entry);
7977
7978 entry=AcquireMagickInfo("PNG","PNG48",
7979 "opaque or binary transparent 48-bit RGB");
7980
7981 #if defined(MAGICKCORE_PNG_DELEGATE)
7982 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7983 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7984 #endif
7985
7986 entry->magick=(IsImageFormatHandler *) IsPNG;
7987 entry->flags|=CoderDecoderSeekableStreamFlag;
7988 entry->flags^=CoderAdjoinFlag;
7989 entry->mime_type=ConstantString("image/png");
7990 (void) RegisterMagickInfo(entry);
7991
7992 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7993
7994 #if defined(MAGICKCORE_PNG_DELEGATE)
7995 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7996 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7997 #endif
7998
7999 entry->magick=(IsImageFormatHandler *) IsPNG;
8000 entry->flags|=CoderDecoderSeekableStreamFlag;
8001 entry->flags^=CoderAdjoinFlag;
8002 entry->mime_type=ConstantString("image/png");
8003 (void) RegisterMagickInfo(entry);
8004
8005 entry=AcquireMagickInfo("PNG","PNG00",
8006 "PNG inheriting bit-depth, color-type from original, if possible");
8007
8008 #if defined(MAGICKCORE_PNG_DELEGATE)
8009 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8010 entry->encoder=(EncodeImageHandler *) WritePNGImage;
8011 #endif
8012
8013 entry->magick=(IsImageFormatHandler *) IsPNG;
8014 entry->flags|=CoderDecoderSeekableStreamFlag;
8015 entry->flags^=CoderAdjoinFlag;
8016 entry->mime_type=ConstantString("image/png");
8017 (void) RegisterMagickInfo(entry);
8018
8019 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
8020
8021 #if defined(JNG_SUPPORTED)
8022 #if defined(MAGICKCORE_PNG_DELEGATE)
8023 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
8024 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8025 #endif
8026 #endif
8027
8028 entry->magick=(IsImageFormatHandler *) IsJNG;
8029 entry->flags|=CoderDecoderSeekableStreamFlag;
8030 entry->flags^=CoderAdjoinFlag;
8031 entry->mime_type=ConstantString("image/x-jng");
8032 entry->note=ConstantString(JNGNote);
8033 (void) RegisterMagickInfo(entry);
8034
8035 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8036 ping_semaphore=AcquireSemaphoreInfo();
8037 #endif
8038
8039 return(MagickImageCoderSignature);
8040 }
8041
8042 /*
8043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8044 % %
8045 % %
8046 % %
8047 % U n r e g i s t e r P N G I m a g e %
8048 % %
8049 % %
8050 % %
8051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8052 %
8053 % UnregisterPNGImage() removes format registrations made by the
8054 % PNG module from the list of supported formats.
8055 %
8056 % The format of the UnregisterPNGImage method is:
8057 %
8058 % UnregisterPNGImage(void)
8059 %
8060 */
UnregisterPNGImage(void)8061 ModuleExport void UnregisterPNGImage(void)
8062 {
8063 (void) UnregisterMagickInfo("MNG");
8064 (void) UnregisterMagickInfo("PNG");
8065 (void) UnregisterMagickInfo("PNG8");
8066 (void) UnregisterMagickInfo("PNG24");
8067 (void) UnregisterMagickInfo("PNG32");
8068 (void) UnregisterMagickInfo("PNG48");
8069 (void) UnregisterMagickInfo("PNG64");
8070 (void) UnregisterMagickInfo("PNG00");
8071 (void) UnregisterMagickInfo("JNG");
8072
8073 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8074 if (ping_semaphore != (SemaphoreInfo *) NULL)
8075 RelinquishSemaphoreInfo(&ping_semaphore);
8076 #endif
8077 }
8078
8079 #if defined(MAGICKCORE_PNG_DELEGATE)
8080 #if PNG_LIBPNG_VER > 10011
8081 /*
8082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8083 % %
8084 % %
8085 % %
8086 % W r i t e M N G I m a g e %
8087 % %
8088 % %
8089 % %
8090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8091 %
8092 % WriteMNGImage() writes an image in the Portable Network Graphics
8093 % Group's "Multiple-image Network Graphics" encoded image format.
8094 %
8095 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8096 %
8097 % The format of the WriteMNGImage method is:
8098 %
8099 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8100 % Image *image,ExceptionInfo *exception)
8101 %
8102 % A description of each parameter follows.
8103 %
8104 % o image_info: the image info.
8105 %
8106 % o image: The image.
8107 %
8108 % o exception: return any errors or warnings in this structure.
8109 %
8110 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8111 % "To do" under ReadPNGImage):
8112 %
8113 % Preserve all unknown and not-yet-handled known chunks found in input
8114 % PNG file and copy them into output PNG files according to the PNG
8115 % copying rules.
8116 %
8117 % Write the iCCP chunk at MNG level when (icc profile length > 0)
8118 %
8119 % Improve selection of color type (use indexed-colour or indexed-colour
8120 % with tRNS when 256 or fewer unique RGBA values are present).
8121 %
8122 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8123 % This will be complicated if we limit ourselves to generating MNG-LC
8124 % files. For now we ignore disposal method 3 and simply overlay the next
8125 % image on it.
8126 %
8127 % Check for identical PLTE's or PLTE/tRNS combinations and use a
8128 % global MNG PLTE or PLTE/tRNS combination when appropriate.
8129 % [mostly done 15 June 1999 but still need to take care of tRNS]
8130 %
8131 % Check for identical sRGB and replace with a global sRGB (and remove
8132 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8133 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8134 % local gAMA/cHRM with local sRGB if appropriate).
8135 %
8136 % Check for identical sBIT chunks and write global ones.
8137 %
8138 % Provide option to skip writing the signature tEXt chunks.
8139 %
8140 % Use signatures to detect identical objects and reuse the first
8141 % instance of such objects instead of writing duplicate objects.
8142 %
8143 % Use a smaller-than-32k value of compression window size when
8144 % appropriate.
8145 %
8146 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8147 % ancillary text chunks and save profiles.
8148 %
8149 % Provide an option to force LC files (to ensure exact framing rate)
8150 % instead of VLC.
8151 %
8152 % Provide an option to force VLC files instead of LC, even when offsets
8153 % are present. This will involve expanding the embedded images with a
8154 % transparent region at the top and/or left.
8155 */
8156
8157 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)8158 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8159 png_info *ping_info, unsigned char *profile_type, unsigned char
8160 *profile_description, unsigned char *profile_data, png_uint_32 length)
8161 {
8162 png_textp
8163 text;
8164
8165 register ssize_t
8166 i;
8167
8168 unsigned char
8169 *sp;
8170
8171 png_charp
8172 dp;
8173
8174 png_uint_32
8175 allocated_length,
8176 description_length;
8177
8178 unsigned char
8179 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8180
8181 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8182 return;
8183
8184 if (image_info->verbose)
8185 {
8186 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8187 (char *) profile_type, (double) length);
8188 }
8189
8190 #if PNG_LIBPNG_VER >= 10400
8191 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8192 #else
8193 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8194 #endif
8195 description_length=(png_uint_32) strlen((const char *) profile_description);
8196 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8197 + description_length);
8198 #if PNG_LIBPNG_VER >= 10400
8199 text[0].text=(png_charp) png_malloc(ping,
8200 (png_alloc_size_t) allocated_length);
8201 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8202 #else
8203 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8204 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8205 #endif
8206 text[0].key[0]='\0';
8207 (void) ConcatenateMagickString(text[0].key,
8208 "Raw profile type ",MagickPathExtent);
8209 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8210 sp=profile_data;
8211 dp=text[0].text;
8212 *dp++='\n';
8213 (void) CopyMagickString(dp,(const char *) profile_description,
8214 allocated_length);
8215 dp+=description_length;
8216 *dp++='\n';
8217 (void) FormatLocaleString(dp,allocated_length-
8218 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8219 dp+=8;
8220
8221 for (i=0; i < (ssize_t) length; i++)
8222 {
8223 if (i%36 == 0)
8224 *dp++='\n';
8225 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8226 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8227 }
8228
8229 *dp++='\n';
8230 *dp='\0';
8231 text[0].text_length=(png_size_t) (dp-text[0].text);
8232 text[0].compression=image_info->compression == NoCompression ||
8233 (image_info->compression == UndefinedCompression &&
8234 text[0].text_length < 128) ? -1 : 0;
8235
8236 if (text[0].text_length <= allocated_length)
8237 png_set_text(ping,ping_info,text,1);
8238
8239 png_free(ping,text[0].text);
8240 png_free(ping,text[0].key);
8241 png_free(ping,text);
8242 }
8243
IsColorEqual(const Image * image,const Quantum * p,const PixelInfo * q)8244 static inline MagickBooleanType IsColorEqual(const Image *image,
8245 const Quantum *p, const PixelInfo *q)
8246 {
8247 MagickRealType
8248 blue,
8249 green,
8250 red;
8251
8252 red=(MagickRealType) GetPixelRed(image,p);
8253 green=(MagickRealType) GetPixelGreen(image,p);
8254 blue=(MagickRealType) GetPixelBlue(image,p);
8255 if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8256 (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8257 (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8258 return(MagickTrue);
8259 return(MagickFalse);
8260 }
8261
8262 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * timestamp,ExceptionInfo * exception)8263 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8264 const char *timestamp,ExceptionInfo *exception)
8265 {
8266 int
8267 ret;
8268
8269 int
8270 day,
8271 hour,
8272 minute,
8273 month,
8274 second,
8275 year;
8276
8277 int
8278 addhours=0,
8279 addminutes=0;
8280
8281 png_time
8282 ptime;
8283
8284 assert(timestamp != (const char *) NULL);
8285 LogMagickEvent(CoderEvent,GetMagickModule(),
8286 " Writing tIME chunk: timestamp property is %30s\n",timestamp);
8287 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8288 &minute, &second);
8289 addhours=0;
8290 addminutes=0;
8291 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8292 &minute, &second, &addhours, &addminutes);
8293 LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " Date format specified for png:tIME=%s" ,timestamp);
8295 LogMagickEvent(CoderEvent,GetMagickModule(),
8296 " ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8297 ret,year,month,day,hour,minute,second,addhours,addminutes);
8298 if (ret < 6)
8299 {
8300 LogMagickEvent(CoderEvent,GetMagickModule(),
8301 " Invalid date, ret=%d",ret);
8302 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8303 "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8304 image->filename,ret);
8305 return;
8306 }
8307 if (addhours < 0)
8308 {
8309 addhours+=24;
8310 addminutes=-addminutes;
8311 day--;
8312 }
8313 hour+=addhours;
8314 minute+=addminutes;
8315 if (day == 0)
8316 {
8317 month--;
8318 day=31;
8319 if(month == 2)
8320 day=28;
8321 else
8322 {
8323 if(month == 4 || month == 6 || month == 9 || month == 11)
8324 day=30;
8325 else
8326 day=31;
8327 }
8328 }
8329 if (month == 0)
8330 {
8331 month++;
8332 year--;
8333 }
8334 if (minute > 59)
8335 {
8336 hour++;
8337 minute-=60;
8338 }
8339 if (hour > 23)
8340 {
8341 day ++;
8342 hour -=24;
8343 }
8344 if (hour < 0)
8345 {
8346 day --;
8347 hour +=24;
8348 }
8349 /* To do: fix this for leap years */
8350 if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8351 month == 9 || month == 11) && day > 30))
8352 {
8353 month++;
8354 day = 1;
8355 }
8356 if (month > 12)
8357 {
8358 year++;
8359 month=1;
8360 }
8361
8362 ptime.year = year;
8363 ptime.month = month;
8364 ptime.day = day;
8365 ptime.hour = hour;
8366 ptime.minute = minute;
8367 ptime.second = second;
8368
8369 LogMagickEvent(CoderEvent,GetMagickModule(),
8370 " png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8371 ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8372 ptime.second, addhours, addminutes);
8373 png_set_tIME(ping,info,&ptime);
8374 }
8375 #endif
8376
8377 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)8378 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8379 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8380 {
8381 char
8382 im_vers[32],
8383 libpng_runv[32],
8384 libpng_vers[32],
8385 zlib_runv[32],
8386 zlib_vers[32];
8387
8388 Image
8389 *image;
8390
8391 ImageInfo
8392 *image_info;
8393
8394 char
8395 *name,
8396 s[2];
8397
8398 const char
8399 *property,
8400 *value;
8401
8402 const StringInfo
8403 *profile;
8404
8405 int
8406 num_passes,
8407 pass,
8408 ping_wrote_caNv;
8409
8410 png_byte
8411 ping_trans_alpha[256];
8412
8413 png_color
8414 palette[257];
8415
8416 png_color_16
8417 ping_background,
8418 ping_trans_color;
8419
8420 png_info
8421 *ping_info;
8422
8423 png_struct
8424 *ping;
8425
8426 png_uint_32
8427 ping_height,
8428 ping_width;
8429
8430 ssize_t
8431 y;
8432
8433 MagickBooleanType
8434 image_matte,
8435 logging,
8436 matte,
8437
8438 ping_have_blob,
8439 ping_have_cheap_transparency,
8440 ping_have_color,
8441 ping_have_non_bw,
8442 ping_have_PLTE,
8443 ping_have_bKGD,
8444 ping_have_eXIf,
8445 ping_have_iCCP,
8446 ping_have_pHYs,
8447 ping_have_sRGB,
8448 ping_have_tRNS,
8449
8450 ping_exclude_bKGD,
8451 ping_exclude_cHRM,
8452 ping_exclude_date,
8453 /* ping_exclude_EXIF, */
8454 ping_exclude_eXIf,
8455 ping_exclude_gAMA,
8456 ping_exclude_iCCP,
8457 /* ping_exclude_iTXt, */
8458 ping_exclude_oFFs,
8459 ping_exclude_pHYs,
8460 ping_exclude_sRGB,
8461 ping_exclude_tEXt,
8462 ping_exclude_tIME,
8463 /* ping_exclude_tRNS, */
8464 ping_exclude_caNv,
8465 ping_exclude_zCCP, /* hex-encoded iCCP */
8466 ping_exclude_zTXt,
8467
8468 ping_preserve_colormap,
8469 ping_preserve_iCCP,
8470 ping_need_colortype_warning,
8471
8472 status,
8473 tried_332,
8474 tried_333,
8475 tried_444;
8476
8477 MemoryInfo
8478 *volatile pixel_info;
8479
8480 QuantumInfo
8481 *quantum_info;
8482
8483 PNGErrorInfo
8484 error_info;
8485
8486 register ssize_t
8487 i,
8488 x;
8489
8490 unsigned char
8491 *ping_pixels;
8492
8493 volatile int
8494 image_colors,
8495 ping_bit_depth,
8496 ping_color_type,
8497 ping_interlace_method,
8498 ping_compression_method,
8499 ping_filter_method,
8500 ping_num_trans;
8501
8502 volatile size_t
8503 image_depth,
8504 old_bit_depth;
8505
8506 size_t
8507 quality,
8508 rowbytes,
8509 save_image_depth;
8510
8511 int
8512 j,
8513 number_colors,
8514 number_opaque,
8515 number_semitransparent,
8516 number_transparent,
8517 ping_pHYs_unit_type;
8518
8519 png_uint_32
8520 ping_pHYs_x_resolution,
8521 ping_pHYs_y_resolution;
8522
8523 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8524 " Enter WriteOnePNGImage()");
8525
8526 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8527 if (image == (Image *) NULL)
8528 return(MagickFalse);
8529 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8530
8531 /* Define these outside of the following "if logging()" block so they will
8532 * show in debuggers.
8533 */
8534 *im_vers='\0';
8535 (void) ConcatenateMagickString(im_vers,
8536 MagickLibVersionText,MagickPathExtent);
8537 (void) ConcatenateMagickString(im_vers,
8538 MagickLibAddendum,MagickPathExtent);
8539
8540 *libpng_vers='\0';
8541 (void) ConcatenateMagickString(libpng_vers,
8542 PNG_LIBPNG_VER_STRING,32);
8543 *libpng_runv='\0';
8544 (void) ConcatenateMagickString(libpng_runv,
8545 png_get_libpng_ver(NULL),32);
8546
8547 *zlib_vers='\0';
8548 (void) ConcatenateMagickString(zlib_vers,
8549 ZLIB_VERSION,32);
8550 *zlib_runv='\0';
8551 (void) ConcatenateMagickString(zlib_runv,
8552 zlib_version,32);
8553
8554 if (logging != MagickFalse)
8555 {
8556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557 " IM version = %s", im_vers);
8558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559 " Libpng version = %s", libpng_vers);
8560 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8561 {
8562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8563 " running with %s", libpng_runv);
8564 }
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Zlib version = %s", zlib_vers);
8567 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8568 {
8569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570 " running with %s", zlib_runv);
8571 }
8572 }
8573
8574 /* Initialize some stuff */
8575 ping_bit_depth=0,
8576 ping_color_type=0,
8577 ping_interlace_method=0,
8578 ping_compression_method=0,
8579 ping_filter_method=0,
8580 ping_num_trans = 0;
8581
8582 ping_background.red = 0;
8583 ping_background.green = 0;
8584 ping_background.blue = 0;
8585 ping_background.gray = 0;
8586 ping_background.index = 0;
8587
8588 ping_trans_color.red=0;
8589 ping_trans_color.green=0;
8590 ping_trans_color.blue=0;
8591 ping_trans_color.gray=0;
8592
8593 ping_pHYs_unit_type = 0;
8594 ping_pHYs_x_resolution = 0;
8595 ping_pHYs_y_resolution = 0;
8596
8597 ping_have_blob=MagickFalse;
8598 ping_have_cheap_transparency=MagickFalse;
8599 ping_have_color=MagickTrue;
8600 ping_have_non_bw=MagickTrue;
8601 ping_have_PLTE=MagickFalse;
8602 ping_have_bKGD=MagickFalse;
8603 ping_have_eXIf=MagickTrue;
8604 ping_have_iCCP=MagickFalse;
8605 ping_have_pHYs=MagickFalse;
8606 ping_have_sRGB=MagickFalse;
8607 ping_have_tRNS=MagickFalse;
8608
8609 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8610 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8611 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8612 ping_exclude_date=mng_info->ping_exclude_date;
8613 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8614 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8615 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8616 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8617 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8618 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8619 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8620 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8621 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8622 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8623 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8624 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8625
8626 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8627 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8628 ping_need_colortype_warning = MagickFalse;
8629
8630 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8631 * i.e., eliminate the ICC profile and set image->rendering_intent.
8632 * Note that this will not involve any changes to the actual pixels
8633 * but merely passes information to applications that read the resulting
8634 * PNG image.
8635 *
8636 * To do: recognize other variants of the sRGB profile, using the CRC to
8637 * verify all recognized variants including the 7 already known.
8638 *
8639 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8640 *
8641 * Use something other than image->rendering_intent to record the fact
8642 * that the sRGB profile was found.
8643 *
8644 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8645 * profile. Record the Blackpoint Compensation, if any.
8646 */
8647 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8648 {
8649 ResetImageProfileIterator(image);
8650 for (name=GetNextImageProfile(image); name != (char *) NULL; )
8651 {
8652 profile=GetImageProfile(image,name);
8653
8654 if (profile != (StringInfo *) NULL)
8655 {
8656 if ((LocaleCompare(name,"ICC") == 0) ||
8657 (LocaleCompare(name,"ICM") == 0))
8658
8659 {
8660 int
8661 icheck,
8662 got_crc=0;
8663
8664
8665 png_uint_32
8666 length,
8667 profile_crc=0;
8668
8669 unsigned char
8670 *data;
8671
8672 length=(png_uint_32) GetStringInfoLength(profile);
8673
8674 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8675 {
8676 if (length == sRGB_info[icheck].len)
8677 {
8678 if (got_crc == 0)
8679 {
8680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681 " Got a %lu-byte ICC profile (potentially sRGB)",
8682 (unsigned long) length);
8683
8684 data=GetStringInfoDatum(profile);
8685 profile_crc=crc32(0,data,length);
8686
8687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8688 " with crc=%8x",(unsigned int) profile_crc);
8689 got_crc++;
8690 }
8691
8692 if (profile_crc == sRGB_info[icheck].crc)
8693 {
8694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8695 " It is sRGB with rendering intent = %s",
8696 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8697 sRGB_info[icheck].intent));
8698 if (image->rendering_intent==UndefinedIntent)
8699 {
8700 image->rendering_intent=
8701 Magick_RenderingIntent_from_PNG_RenderingIntent(
8702 sRGB_info[icheck].intent);
8703 }
8704 ping_exclude_iCCP = MagickTrue;
8705 ping_exclude_zCCP = MagickTrue;
8706 ping_have_sRGB = MagickTrue;
8707 break;
8708 }
8709 }
8710 }
8711 if (sRGB_info[icheck].len == 0)
8712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8713 " Got %lu-byte ICC profile not recognized as sRGB",
8714 (unsigned long) length);
8715 }
8716 }
8717 name=GetNextImageProfile(image);
8718 }
8719 }
8720
8721 number_opaque = 0;
8722 number_semitransparent = 0;
8723 number_transparent = 0;
8724
8725 if (logging != MagickFalse)
8726 {
8727 if (image->storage_class == UndefinedClass)
8728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8729 " image->storage_class=UndefinedClass");
8730 if (image->storage_class == DirectClass)
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " image->storage_class=DirectClass");
8733 if (image->storage_class == PseudoClass)
8734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735 " image->storage_class=PseudoClass");
8736 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8737 " image->taint=MagickTrue":
8738 " image->taint=MagickFalse");
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740 " image->gamma=%g", image->gamma);
8741 }
8742
8743 if (image->storage_class == PseudoClass &&
8744 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8745 mng_info->write_png48 || mng_info->write_png64 ||
8746 (mng_info->write_png_colortype != 1 &&
8747 mng_info->write_png_colortype != 5)))
8748 {
8749 (void) SyncImage(image,exception);
8750 image->storage_class = DirectClass;
8751 }
8752
8753 if (ping_preserve_colormap == MagickFalse)
8754 {
8755 if (image->storage_class != PseudoClass && image->colormap != NULL)
8756 {
8757 /* Free the bogus colormap; it can cause trouble later */
8758 if (logging != MagickFalse)
8759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8760 " Freeing bogus colormap");
8761 (void) RelinquishMagickMemory(image->colormap);
8762 image->colormap=NULL;
8763 }
8764 }
8765
8766 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8767 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8768
8769 /*
8770 Sometimes we get PseudoClass images whose RGB values don't match
8771 the colors in the colormap. This code syncs the RGB values.
8772 */
8773 image->depth=GetImageQuantumDepth(image,MagickFalse);
8774 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8775 (void) SyncImage(image,exception);
8776
8777 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8778 if (image->depth > 8)
8779 {
8780 if (logging != MagickFalse)
8781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8782 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8783
8784 image->depth=8;
8785 }
8786 #endif
8787
8788 /* Respect the -depth option */
8789 if (image->depth < 4)
8790 {
8791 register Quantum
8792 *r;
8793
8794 if (image->depth > 2)
8795 {
8796 /* Scale to 4-bit */
8797 LBR04PacketRGBA(image->background_color);
8798
8799 for (y=0; y < (ssize_t) image->rows; y++)
8800 {
8801 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8802
8803 if (r == (Quantum *) NULL)
8804 break;
8805
8806 for (x=0; x < (ssize_t) image->columns; x++)
8807 {
8808 LBR04PixelRGBA(r);
8809 r+=GetPixelChannels(image);
8810 }
8811
8812 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8813 break;
8814 }
8815
8816 if (image->storage_class == PseudoClass && image->colormap != NULL)
8817 {
8818 for (i=0; i < (ssize_t) image->colors; i++)
8819 {
8820 LBR04PacketRGBA(image->colormap[i]);
8821 }
8822 }
8823 }
8824 else if (image->depth > 1)
8825 {
8826 /* Scale to 2-bit */
8827 LBR02PacketRGBA(image->background_color);
8828
8829 for (y=0; y < (ssize_t) image->rows; y++)
8830 {
8831 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8832
8833 if (r == (Quantum *) NULL)
8834 break;
8835
8836 for (x=0; x < (ssize_t) image->columns; x++)
8837 {
8838 LBR02PixelRGBA(r);
8839 r+=GetPixelChannels(image);
8840 }
8841
8842 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8843 break;
8844 }
8845
8846 if (image->storage_class == PseudoClass && image->colormap != NULL)
8847 {
8848 for (i=0; i < (ssize_t) image->colors; i++)
8849 {
8850 LBR02PacketRGBA(image->colormap[i]);
8851 }
8852 }
8853 }
8854 else
8855 {
8856 /* Scale to 1-bit */
8857 LBR01PacketRGBA(image->background_color);
8858
8859 for (y=0; y < (ssize_t) image->rows; y++)
8860 {
8861 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8862
8863 if (r == (Quantum *) NULL)
8864 break;
8865
8866 for (x=0; x < (ssize_t) image->columns; x++)
8867 {
8868 LBR01PixelRGBA(r);
8869 r+=GetPixelChannels(image);
8870 }
8871
8872 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8873 break;
8874 }
8875
8876 if (image->storage_class == PseudoClass && image->colormap != NULL)
8877 {
8878 for (i=0; i < (ssize_t) image->colors; i++)
8879 {
8880 LBR01PacketRGBA(image->colormap[i]);
8881 }
8882 }
8883 }
8884 }
8885
8886 /* To do: set to next higher multiple of 8 */
8887 if (image->depth < 8)
8888 image->depth=8;
8889
8890 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8891 /* PNG does not handle depths greater than 16 so reduce it even
8892 * if lossy
8893 */
8894 if (image->depth > 8)
8895 image->depth=16;
8896 #endif
8897
8898 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8899 if (image->depth > 8)
8900 {
8901 /* To do: fill low byte properly */
8902 image->depth=16;
8903 }
8904
8905 if (image->depth == 16 && mng_info->write_png_depth != 16)
8906 if (mng_info->write_png8 ||
8907 LosslessReduceDepthOK(image,exception) != MagickFalse)
8908 image->depth = 8;
8909 #endif
8910
8911 image_colors = (int) image->colors;
8912 number_opaque = (int) image->colors;
8913 number_transparent = 0;
8914 number_semitransparent = 0;
8915
8916 if (mng_info->write_png_colortype &&
8917 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8918 mng_info->write_png_colortype < 4 &&
8919 image->alpha_trait == UndefinedPixelTrait)))
8920 {
8921 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8922 * are not going to need the result.
8923 */
8924 if (mng_info->write_png_colortype == 1 ||
8925 mng_info->write_png_colortype == 5)
8926 ping_have_color=MagickFalse;
8927
8928 if (image->alpha_trait != UndefinedPixelTrait)
8929 {
8930 number_transparent = 2;
8931 number_semitransparent = 1;
8932 }
8933 }
8934
8935 if (mng_info->write_png_colortype < 7)
8936 {
8937 /* BUILD_PALETTE
8938 *
8939 * Normally we run this just once, but in the case of writing PNG8
8940 * we reduce the transparency to binary and run again, then if there
8941 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8942 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8943 * palette. Then (To do) we take care of a final reduction that is only
8944 * needed if there are still 256 colors present and one of them has both
8945 * transparent and opaque instances.
8946 */
8947
8948 tried_332 = MagickFalse;
8949 tried_333 = MagickFalse;
8950 tried_444 = MagickFalse;
8951
8952 if (image->depth != GetImageDepth(image,exception))
8953 (void) SetImageDepth(image,image->depth,exception);
8954 for (j=0; j<6; j++)
8955 {
8956 /*
8957 * Sometimes we get DirectClass images that have 256 colors or fewer.
8958 * This code will build a colormap.
8959 *
8960 * Also, sometimes we get PseudoClass images with an out-of-date
8961 * colormap. This code will replace the colormap with a new one.
8962 * Sometimes we get PseudoClass images that have more than 256 colors.
8963 * This code will delete the colormap and change the image to
8964 * DirectClass.
8965 *
8966 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8967 * even though it sometimes contains left-over non-opaque values.
8968 *
8969 * Also we gather some information (number of opaque, transparent,
8970 * and semitransparent pixels, and whether the image has any non-gray
8971 * pixels or only black-and-white pixels) that we might need later.
8972 *
8973 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8974 * we need to check for bogus non-opaque values, at least.
8975 */
8976
8977 int
8978 n;
8979
8980 PixelInfo
8981 opaque[260],
8982 semitransparent[260],
8983 transparent[260];
8984
8985 register const Quantum
8986 *r;
8987
8988 register Quantum
8989 *q;
8990
8991 if (logging != MagickFalse)
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993 " Enter BUILD_PALETTE:");
8994
8995 if (logging != MagickFalse)
8996 {
8997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8998 " image->columns=%.20g",(double) image->columns);
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " image->rows=%.20g",(double) image->rows);
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " image->alpha_trait=%.20g",(double) image->alpha_trait);
9003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " image->depth=%.20g",(double) image->depth);
9005
9006 if (image->storage_class == PseudoClass && image->colormap != NULL)
9007 {
9008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9009 " Original colormap:");
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " i (red,green,blue,alpha)");
9012
9013 for (i=0; i < 256; i++)
9014 {
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " %d (%d,%d,%d,%d)",
9017 (int) i,
9018 (int) image->colormap[i].red,
9019 (int) image->colormap[i].green,
9020 (int) image->colormap[i].blue,
9021 (int) image->colormap[i].alpha);
9022 }
9023
9024 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
9025 {
9026 if (i > 255)
9027 {
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " %d (%d,%d,%d,%d)",
9030 (int) i,
9031 (int) image->colormap[i].red,
9032 (int) image->colormap[i].green,
9033 (int) image->colormap[i].blue,
9034 (int) image->colormap[i].alpha);
9035 }
9036 }
9037 }
9038
9039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040 " image->colors=%d",(int) image->colors);
9041
9042 if (image->colors == 0)
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " (zero means unknown)");
9045
9046 if (ping_preserve_colormap == MagickFalse)
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " Regenerate the colormap");
9049 }
9050
9051 image_colors=0;
9052 number_opaque = 0;
9053 number_semitransparent = 0;
9054 number_transparent = 0;
9055
9056 for (y=0; y < (ssize_t) image->rows; y++)
9057 {
9058 r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9059
9060 if (r == (const Quantum *) NULL)
9061 break;
9062
9063 for (x=0; x < (ssize_t) image->columns; x++)
9064 {
9065 if (image->alpha_trait == UndefinedPixelTrait ||
9066 GetPixelAlpha(image,r) == OpaqueAlpha)
9067 {
9068 if (number_opaque < 259)
9069 {
9070 if (number_opaque == 0)
9071 {
9072 GetPixelInfoPixel(image,r,opaque);
9073 opaque[0].alpha=OpaqueAlpha;
9074 number_opaque=1;
9075 }
9076
9077 for (i=0; i< (ssize_t) number_opaque; i++)
9078 {
9079 if (IsColorEqual(image,r,opaque+i))
9080 break;
9081 }
9082
9083 if (i == (ssize_t) number_opaque && number_opaque < 259)
9084 {
9085 number_opaque++;
9086 GetPixelInfoPixel(image,r,opaque+i);
9087 opaque[i].alpha=OpaqueAlpha;
9088 }
9089 }
9090 }
9091 else if (GetPixelAlpha(image,r) == TransparentAlpha)
9092 {
9093 if (number_transparent < 259)
9094 {
9095 if (number_transparent == 0)
9096 {
9097 GetPixelInfoPixel(image,r,transparent);
9098 ping_trans_color.red=(unsigned short)
9099 GetPixelRed(image,r);
9100 ping_trans_color.green=(unsigned short)
9101 GetPixelGreen(image,r);
9102 ping_trans_color.blue=(unsigned short)
9103 GetPixelBlue(image,r);
9104 ping_trans_color.gray=(unsigned short)
9105 GetPixelGray(image,r);
9106 number_transparent = 1;
9107 }
9108
9109 for (i=0; i< (ssize_t) number_transparent; i++)
9110 {
9111 if (IsColorEqual(image,r,transparent+i))
9112 break;
9113 }
9114
9115 if (i == (ssize_t) number_transparent &&
9116 number_transparent < 259)
9117 {
9118 number_transparent++;
9119 GetPixelInfoPixel(image,r,transparent+i);
9120 }
9121 }
9122 }
9123 else
9124 {
9125 if (number_semitransparent < 259)
9126 {
9127 if (number_semitransparent == 0)
9128 {
9129 GetPixelInfoPixel(image,r,semitransparent);
9130 number_semitransparent = 1;
9131 }
9132
9133 for (i=0; i< (ssize_t) number_semitransparent; i++)
9134 {
9135 if (IsColorEqual(image,r,semitransparent+i)
9136 && GetPixelAlpha(image,r) ==
9137 semitransparent[i].alpha)
9138 break;
9139 }
9140
9141 if (i == (ssize_t) number_semitransparent &&
9142 number_semitransparent < 259)
9143 {
9144 number_semitransparent++;
9145 GetPixelInfoPixel(image,r,semitransparent+i);
9146 }
9147 }
9148 }
9149 r+=GetPixelChannels(image);
9150 }
9151 }
9152
9153 if (mng_info->write_png8 == MagickFalse &&
9154 ping_exclude_bKGD == MagickFalse)
9155 {
9156 /* Add the background color to the palette, if it
9157 * isn't already there.
9158 */
9159 if (logging != MagickFalse)
9160 {
9161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9162 " Check colormap for background (%d,%d,%d)",
9163 (int) image->background_color.red,
9164 (int) image->background_color.green,
9165 (int) image->background_color.blue);
9166 }
9167 for (i=0; i<number_opaque; i++)
9168 {
9169 if (opaque[i].red == image->background_color.red &&
9170 opaque[i].green == image->background_color.green &&
9171 opaque[i].blue == image->background_color.blue)
9172 break;
9173 }
9174 if (number_opaque < 259 && i == number_opaque)
9175 {
9176 opaque[i] = image->background_color;
9177 ping_background.index = i;
9178 number_opaque++;
9179 if (logging != MagickFalse)
9180 {
9181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9182 " background_color index is %d",(int) i);
9183 }
9184
9185 }
9186 else if (logging != MagickFalse)
9187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188 " No room in the colormap to add background color");
9189 }
9190
9191 image_colors=number_opaque+number_transparent+number_semitransparent;
9192
9193 if (logging != MagickFalse)
9194 {
9195 if (image_colors > 256)
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " image has more than 256 colors");
9198
9199 else
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " image has %d colors",image_colors);
9202 }
9203
9204 if (ping_preserve_colormap != MagickFalse)
9205 break;
9206
9207 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9208 {
9209 ping_have_color=MagickFalse;
9210 ping_have_non_bw=MagickFalse;
9211
9212 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9213 {
9214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215 "incompatible colorspace");
9216 ping_have_color=MagickTrue;
9217 ping_have_non_bw=MagickTrue;
9218 }
9219
9220 if(image_colors > 256)
9221 {
9222 for (y=0; y < (ssize_t) image->rows; y++)
9223 {
9224 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9225
9226 if (q == (Quantum *) NULL)
9227 break;
9228
9229 r=q;
9230 for (x=0; x < (ssize_t) image->columns; x++)
9231 {
9232 if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9233 GetPixelRed(image,r) != GetPixelBlue(image,r))
9234 {
9235 ping_have_color=MagickTrue;
9236 ping_have_non_bw=MagickTrue;
9237 break;
9238 }
9239 r+=GetPixelChannels(image);
9240 }
9241
9242 if (ping_have_color != MagickFalse)
9243 break;
9244
9245 /* Worst case is black-and-white; we are looking at every
9246 * pixel twice.
9247 */
9248
9249 if (ping_have_non_bw == MagickFalse)
9250 {
9251 r=q;
9252 for (x=0; x < (ssize_t) image->columns; x++)
9253 {
9254 if (GetPixelRed(image,r) != 0 &&
9255 GetPixelRed(image,r) != QuantumRange)
9256 {
9257 ping_have_non_bw=MagickTrue;
9258 break;
9259 }
9260 r+=GetPixelChannels(image);
9261 }
9262 }
9263 }
9264 }
9265 }
9266
9267 if (image_colors < 257)
9268 {
9269 PixelInfo
9270 colormap[260];
9271
9272 /*
9273 * Initialize image colormap.
9274 */
9275
9276 if (logging != MagickFalse)
9277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278 " Sort the new colormap");
9279
9280 /* Sort palette, transparent first */;
9281
9282 n = 0;
9283
9284 for (i=0; i<number_transparent; i++)
9285 colormap[n++] = transparent[i];
9286
9287 for (i=0; i<number_semitransparent; i++)
9288 colormap[n++] = semitransparent[i];
9289
9290 for (i=0; i<number_opaque; i++)
9291 colormap[n++] = opaque[i];
9292
9293 ping_background.index +=
9294 (number_transparent + number_semitransparent);
9295
9296 /* image_colors < 257; search the colormap instead of the pixels
9297 * to get ping_have_color and ping_have_non_bw
9298 */
9299 for (i=0; i<n; i++)
9300 {
9301 if (ping_have_color == MagickFalse)
9302 {
9303 if (colormap[i].red != colormap[i].green ||
9304 colormap[i].red != colormap[i].blue)
9305 {
9306 ping_have_color=MagickTrue;
9307 ping_have_non_bw=MagickTrue;
9308 break;
9309 }
9310 }
9311
9312 if (ping_have_non_bw == MagickFalse)
9313 {
9314 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9315 ping_have_non_bw=MagickTrue;
9316 }
9317 }
9318
9319 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9320 (number_transparent == 0 && number_semitransparent == 0)) &&
9321 (((mng_info->write_png_colortype-1) ==
9322 PNG_COLOR_TYPE_PALETTE) ||
9323 (mng_info->write_png_colortype == 0)))
9324 {
9325 if (logging != MagickFalse)
9326 {
9327 if (n != (ssize_t) image_colors)
9328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329 " image_colors (%d) and n (%d) don't match",
9330 image_colors, n);
9331
9332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9333 " AcquireImageColormap");
9334 }
9335
9336 image->colors = image_colors;
9337
9338 if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9339 {
9340 (void) ThrowMagickException(exception,GetMagickModule(),
9341 ResourceLimitError,"MemoryAllocationFailed","`%s'",
9342 image->filename);
9343 break;
9344 }
9345
9346 for (i=0; i< (ssize_t) image_colors; i++)
9347 image->colormap[i] = colormap[i];
9348
9349 if (logging != MagickFalse)
9350 {
9351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9352 " image->colors=%d (%d)",
9353 (int) image->colors, image_colors);
9354
9355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9356 " Update the pixel indexes");
9357 }
9358
9359 /* Sync the pixel indices with the new colormap */
9360
9361 for (y=0; y < (ssize_t) image->rows; y++)
9362 {
9363 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9364
9365 if (q == (Quantum *) NULL)
9366 break;
9367
9368 for (x=0; x < (ssize_t) image->columns; x++)
9369 {
9370 for (i=0; i< (ssize_t) image_colors; i++)
9371 {
9372 if ((image->alpha_trait == UndefinedPixelTrait ||
9373 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9374 image->colormap[i].red == GetPixelRed(image,q) &&
9375 image->colormap[i].green == GetPixelGreen(image,q) &&
9376 image->colormap[i].blue == GetPixelBlue(image,q))
9377 {
9378 SetPixelIndex(image,i,q);
9379 break;
9380 }
9381 }
9382 q+=GetPixelChannels(image);
9383 }
9384
9385 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9386 break;
9387 }
9388 }
9389 }
9390
9391 if (logging != MagickFalse)
9392 {
9393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9394 " image->colors=%d", (int) image->colors);
9395
9396 if (image->colormap != NULL)
9397 {
9398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399 " i (red,green,blue,alpha)");
9400
9401 for (i=0; i < (ssize_t) image->colors; i++)
9402 {
9403 if (i < 300 || i >= (ssize_t) image->colors - 10)
9404 {
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406 " %d (%d,%d,%d,%d)",
9407 (int) i,
9408 (int) image->colormap[i].red,
9409 (int) image->colormap[i].green,
9410 (int) image->colormap[i].blue,
9411 (int) image->colormap[i].alpha);
9412 }
9413 }
9414 }
9415
9416 if (number_transparent < 257)
9417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418 " number_transparent = %d",
9419 number_transparent);
9420 else
9421
9422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423 " number_transparent > 256");
9424
9425 if (number_opaque < 257)
9426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9427 " number_opaque = %d",
9428 number_opaque);
9429
9430 else
9431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9432 " number_opaque > 256");
9433
9434 if (number_semitransparent < 257)
9435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436 " number_semitransparent = %d",
9437 number_semitransparent);
9438
9439 else
9440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9441 " number_semitransparent > 256");
9442
9443 if (ping_have_non_bw == MagickFalse)
9444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9445 " All pixels and the background are black or white");
9446
9447 else if (ping_have_color == MagickFalse)
9448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449 " All pixels and the background are gray");
9450
9451 else
9452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453 " At least one pixel or the background is non-gray");
9454
9455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456 " Exit BUILD_PALETTE:");
9457 }
9458
9459 if (mng_info->write_png8 == MagickFalse)
9460 break;
9461
9462 /* Make any reductions necessary for the PNG8 format */
9463 if (image_colors <= 256 &&
9464 image_colors != 0 && image->colormap != NULL &&
9465 number_semitransparent == 0 &&
9466 number_transparent <= 1)
9467 break;
9468
9469 /* PNG8 can't have semitransparent colors so we threshold the
9470 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9471 * transparent color so if more than one is transparent we merge
9472 * them into image->background_color.
9473 */
9474 if (number_semitransparent != 0 || number_transparent > 1)
9475 {
9476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9477 " Thresholding the alpha channel to binary");
9478
9479 for (y=0; y < (ssize_t) image->rows; y++)
9480 {
9481 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9482
9483 if (q == (Quantum *) NULL)
9484 break;
9485
9486 for (x=0; x < (ssize_t) image->columns; x++)
9487 {
9488 if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9489 {
9490 SetPixelViaPixelInfo(image,&image->background_color,q);
9491 SetPixelAlpha(image,TransparentAlpha,q);
9492 }
9493 else
9494 SetPixelAlpha(image,OpaqueAlpha,q);
9495 q+=GetPixelChannels(image);
9496 }
9497
9498 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9499 break;
9500
9501 if (image_colors != 0 && image_colors <= 256 &&
9502 image->colormap != NULL)
9503 for (i=0; i<image_colors; i++)
9504 image->colormap[i].alpha =
9505 (image->colormap[i].alpha > TransparentAlpha/2 ?
9506 TransparentAlpha : OpaqueAlpha);
9507 }
9508 continue;
9509 }
9510
9511 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9512 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9513 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9514 * colors or less.
9515 */
9516 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9517 {
9518 if (logging != MagickFalse)
9519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9520 " Quantizing the background color to 4-4-4");
9521
9522 tried_444 = MagickTrue;
9523
9524 LBR04PacketRGB(image->background_color);
9525
9526 if (logging != MagickFalse)
9527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9528 " Quantizing the pixel colors to 4-4-4");
9529
9530 if (image->colormap == NULL)
9531 {
9532 for (y=0; y < (ssize_t) image->rows; y++)
9533 {
9534 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9535
9536 if (q == (Quantum *) NULL)
9537 break;
9538
9539 for (x=0; x < (ssize_t) image->columns; x++)
9540 {
9541 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9542 LBR04PixelRGB(q);
9543 q+=GetPixelChannels(image);
9544 }
9545
9546 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9547 break;
9548 }
9549 }
9550
9551 else /* Should not reach this; colormap already exists and
9552 must be <= 256 */
9553 {
9554 if (logging != MagickFalse)
9555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556 " Quantizing the colormap to 4-4-4");
9557
9558 for (i=0; i<image_colors; i++)
9559 {
9560 LBR04PacketRGB(image->colormap[i]);
9561 }
9562 }
9563 continue;
9564 }
9565
9566 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9567 {
9568 if (logging != MagickFalse)
9569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570 " Quantizing the background color to 3-3-3");
9571
9572 tried_333 = MagickTrue;
9573
9574 LBR03PacketRGB(image->background_color);
9575
9576 if (logging != MagickFalse)
9577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9578 " Quantizing the pixel colors to 3-3-3-1");
9579
9580 if (image->colormap == NULL)
9581 {
9582 for (y=0; y < (ssize_t) image->rows; y++)
9583 {
9584 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9585
9586 if (q == (Quantum *) NULL)
9587 break;
9588
9589 for (x=0; x < (ssize_t) image->columns; x++)
9590 {
9591 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9592 LBR03RGB(q);
9593 q+=GetPixelChannels(image);
9594 }
9595
9596 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9597 break;
9598 }
9599 }
9600
9601 else /* Should not reach this; colormap already exists and
9602 must be <= 256 */
9603 {
9604 if (logging != MagickFalse)
9605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606 " Quantizing the colormap to 3-3-3-1");
9607 for (i=0; i<image_colors; i++)
9608 {
9609 LBR03PacketRGB(image->colormap[i]);
9610 }
9611 }
9612 continue;
9613 }
9614
9615 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9616 {
9617 if (logging != MagickFalse)
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " Quantizing the background color to 3-3-2");
9620
9621 tried_332 = MagickTrue;
9622
9623 /* Red and green were already done so we only quantize the blue
9624 * channel
9625 */
9626
9627 LBR02PacketBlue(image->background_color);
9628
9629 if (logging != MagickFalse)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Quantizing the pixel colors to 3-3-2-1");
9632
9633 if (image->colormap == NULL)
9634 {
9635 for (y=0; y < (ssize_t) image->rows; y++)
9636 {
9637 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9638
9639 if (q == (Quantum *) NULL)
9640 break;
9641
9642 for (x=0; x < (ssize_t) image->columns; x++)
9643 {
9644 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9645 LBR02PixelBlue(q);
9646 q+=GetPixelChannels(image);
9647 }
9648
9649 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9650 break;
9651 }
9652 }
9653
9654 else /* Should not reach this; colormap already exists and
9655 must be <= 256 */
9656 {
9657 if (logging != MagickFalse)
9658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9659 " Quantizing the colormap to 3-3-2-1");
9660 for (i=0; i<image_colors; i++)
9661 {
9662 LBR02PacketBlue(image->colormap[i]);
9663 }
9664 }
9665 continue;
9666 }
9667
9668 if (image_colors == 0 || image_colors > 256)
9669 {
9670 /* Take care of special case with 256 opaque colors + 1 transparent
9671 * color. We don't need to quantize to 2-3-2-1; we only need to
9672 * eliminate one color, so we'll merge the two darkest red
9673 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9674 */
9675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " Merging two dark red background colors to 3-3-2-1");
9678
9679 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9680 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9681 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9682 {
9683 image->background_color.red=ScaleCharToQuantum(0x24);
9684 }
9685
9686 if (logging != MagickFalse)
9687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9688 " Merging two dark red pixel colors to 3-3-2-1");
9689
9690 if (image->colormap == NULL)
9691 {
9692 for (y=0; y < (ssize_t) image->rows; y++)
9693 {
9694 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9695
9696 if (q == (Quantum *) NULL)
9697 break;
9698
9699 for (x=0; x < (ssize_t) image->columns; x++)
9700 {
9701 if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9702 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9703 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9704 GetPixelAlpha(image,q) == OpaqueAlpha)
9705 {
9706 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9707 }
9708 q+=GetPixelChannels(image);
9709 }
9710
9711 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9712 break;
9713
9714 }
9715 }
9716
9717 else
9718 {
9719 for (i=0; i<image_colors; i++)
9720 {
9721 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9722 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9723 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9724 {
9725 image->colormap[i].red=ScaleCharToQuantum(0x24);
9726 }
9727 }
9728 }
9729 }
9730 }
9731 }
9732 /* END OF BUILD_PALETTE */
9733
9734 /* If we are excluding the tRNS chunk and there is transparency,
9735 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9736 * PNG.
9737 */
9738 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9739 (number_transparent != 0 || number_semitransparent != 0))
9740 {
9741 unsigned int colortype=mng_info->write_png_colortype;
9742
9743 if (ping_have_color == MagickFalse)
9744 mng_info->write_png_colortype = 5;
9745
9746 else
9747 mng_info->write_png_colortype = 7;
9748
9749 if (colortype != 0 &&
9750 mng_info->write_png_colortype != colortype)
9751 ping_need_colortype_warning=MagickTrue;
9752
9753 }
9754
9755 /* See if cheap transparency is possible. It is only possible
9756 * when there is a single transparent color, no semitransparent
9757 * color, and no opaque color that has the same RGB components
9758 * as the transparent color. We only need this information if
9759 * we are writing a PNG with colortype 0 or 2, and we have not
9760 * excluded the tRNS chunk.
9761 */
9762 if (number_transparent == 1 &&
9763 mng_info->write_png_colortype < 4)
9764 {
9765 ping_have_cheap_transparency = MagickTrue;
9766
9767 if (number_semitransparent != 0)
9768 ping_have_cheap_transparency = MagickFalse;
9769
9770 else if (image_colors == 0 || image_colors > 256 ||
9771 image->colormap == NULL)
9772 {
9773 register const Quantum
9774 *q;
9775
9776 for (y=0; y < (ssize_t) image->rows; y++)
9777 {
9778 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9779
9780 if (q == (Quantum *) NULL)
9781 break;
9782
9783 for (x=0; x < (ssize_t) image->columns; x++)
9784 {
9785 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9786 (unsigned short) GetPixelRed(image,q) ==
9787 ping_trans_color.red &&
9788 (unsigned short) GetPixelGreen(image,q) ==
9789 ping_trans_color.green &&
9790 (unsigned short) GetPixelBlue(image,q) ==
9791 ping_trans_color.blue)
9792 {
9793 ping_have_cheap_transparency = MagickFalse;
9794 break;
9795 }
9796
9797 q+=GetPixelChannels(image);
9798 }
9799
9800 if (ping_have_cheap_transparency == MagickFalse)
9801 break;
9802 }
9803 }
9804 else
9805 {
9806 /* Assuming that image->colormap[0] is the one transparent color
9807 * and that all others are opaque.
9808 */
9809 if (image_colors > 1)
9810 for (i=1; i<image_colors; i++)
9811 if (image->colormap[i].red == image->colormap[0].red &&
9812 image->colormap[i].green == image->colormap[0].green &&
9813 image->colormap[i].blue == image->colormap[0].blue)
9814 {
9815 ping_have_cheap_transparency = MagickFalse;
9816 break;
9817 }
9818 }
9819
9820 if (logging != MagickFalse)
9821 {
9822 if (ping_have_cheap_transparency == MagickFalse)
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824 " Cheap transparency is not possible.");
9825
9826 else
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Cheap transparency is possible.");
9829 }
9830 }
9831 else
9832 ping_have_cheap_transparency = MagickFalse;
9833
9834 image_depth=image->depth;
9835
9836 quantum_info = (QuantumInfo *) NULL;
9837 number_colors=0;
9838 image_colors=(int) image->colors;
9839 image_matte=image->alpha_trait !=
9840 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9841
9842 if (mng_info->write_png_colortype < 5)
9843 mng_info->IsPalette=image->storage_class == PseudoClass &&
9844 image_colors <= 256 && image->colormap != NULL;
9845 else
9846 mng_info->IsPalette = MagickFalse;
9847
9848 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9849 (image->colors == 0 || image->colormap == NULL))
9850 {
9851 image_info=DestroyImageInfo(image_info);
9852 image=DestroyImage(image);
9853 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9854 "Cannot write PNG8 or color-type 3; colormap is NULL",
9855 "`%s'",IMimage->filename);
9856 return(MagickFalse);
9857 }
9858
9859 /*
9860 Allocate the PNG structures
9861 */
9862 #ifdef PNG_USER_MEM_SUPPORTED
9863 error_info.image=image;
9864 error_info.exception=exception;
9865 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9866 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9867 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9868
9869 #else
9870 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9871 MagickPNGErrorHandler,MagickPNGWarningHandler);
9872
9873 #endif
9874 if (ping == (png_struct *) NULL)
9875 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9876
9877 ping_info=png_create_info_struct(ping);
9878
9879 if (ping_info == (png_info *) NULL)
9880 {
9881 png_destroy_write_struct(&ping,(png_info **) NULL);
9882 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9883 }
9884
9885 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9886 pixel_info=(MemoryInfo *) NULL;
9887
9888 if (setjmp(png_jmpbuf(ping)))
9889 {
9890 /*
9891 PNG write failed.
9892 */
9893 #ifdef PNG_DEBUG
9894 if (image_info->verbose)
9895 (void) printf("PNG write has failed.\n");
9896 #endif
9897 png_destroy_write_struct(&ping,&ping_info);
9898 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9899 UnlockSemaphoreInfo(ping_semaphore);
9900 #endif
9901
9902 if (pixel_info != (MemoryInfo *) NULL)
9903 pixel_info=RelinquishVirtualMemory(pixel_info);
9904
9905 if (quantum_info != (QuantumInfo *) NULL)
9906 quantum_info=DestroyQuantumInfo(quantum_info);
9907
9908 if (ping_have_blob != MagickFalse)
9909 (void) CloseBlob(image);
9910 image_info=DestroyImageInfo(image_info);
9911 image=DestroyImage(image);
9912 return(MagickFalse);
9913 }
9914
9915 /* { For navigation to end of SETJMP-protected block. Within this
9916 * block, use png_error() instead of Throwing an Exception, to ensure
9917 * that libpng is able to clean up, and that the semaphore is unlocked.
9918 */
9919
9920 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9921 LockSemaphoreInfo(ping_semaphore);
9922 #endif
9923
9924 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9925 /* Allow benign errors */
9926 png_set_benign_errors(ping, 1);
9927 #endif
9928
9929 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9930 /* Reject images with too many rows or columns */
9931 png_set_user_limits(ping,
9932 (png_uint_32) MagickMin(0x7fffffffL,
9933 GetMagickResourceLimit(WidthResource)),
9934 (png_uint_32) MagickMin(0x7fffffffL,
9935 GetMagickResourceLimit(HeightResource)));
9936 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9937
9938 /*
9939 Prepare PNG for writing.
9940 */
9941
9942 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9943 if (mng_info->write_mng)
9944 {
9945 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9946 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9947 /* Disable new libpng-1.5.10 feature when writing a MNG because
9948 * zero-length PLTE is OK
9949 */
9950 png_set_check_for_invalid_index (ping, 0);
9951 # endif
9952 }
9953
9954 #else
9955 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9956 if (mng_info->write_mng)
9957 png_permit_empty_plte(ping,MagickTrue);
9958
9959 # endif
9960 #endif
9961
9962 x=0;
9963
9964 ping_width=(png_uint_32) image->columns;
9965 ping_height=(png_uint_32) image->rows;
9966
9967 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9968 image_depth=8;
9969
9970 if (mng_info->write_png48 || mng_info->write_png64)
9971 image_depth=16;
9972
9973 if (mng_info->write_png_depth != 0)
9974 image_depth=mng_info->write_png_depth;
9975
9976 /* Adjust requested depth to next higher valid depth if necessary */
9977 if (image_depth > 8)
9978 image_depth=16;
9979
9980 if ((image_depth > 4) && (image_depth < 8))
9981 image_depth=8;
9982
9983 if (image_depth == 3)
9984 image_depth=4;
9985
9986 if (logging != MagickFalse)
9987 {
9988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " width=%.20g",(double) ping_width);
9990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991 " height=%.20g",(double) ping_height);
9992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993 " image_matte=%.20g",(double) image->alpha_trait);
9994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995 " image->depth=%.20g",(double) image->depth);
9996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9997 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9998 }
9999
10000 save_image_depth=image_depth;
10001 ping_bit_depth=(png_byte) save_image_depth;
10002
10003
10004 #if defined(PNG_pHYs_SUPPORTED)
10005 if (ping_exclude_pHYs == MagickFalse)
10006 {
10007 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
10008 (!mng_info->write_mng || !mng_info->equal_physs))
10009 {
10010 if (logging != MagickFalse)
10011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012 " Setting up pHYs chunk");
10013
10014 if (image->units == PixelsPerInchResolution)
10015 {
10016 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10017 ping_pHYs_x_resolution=
10018 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
10019 ping_pHYs_y_resolution=
10020 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
10021 }
10022
10023 else if (image->units == PixelsPerCentimeterResolution)
10024 {
10025 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10026 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10027 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10028 }
10029
10030 else
10031 {
10032 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10033 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10034 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10035 }
10036
10037 if (logging != MagickFalse)
10038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10040 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10041 (int) ping_pHYs_unit_type);
10042 ping_have_pHYs = MagickTrue;
10043 }
10044 }
10045 #endif
10046
10047 if (ping_exclude_bKGD == MagickFalse)
10048 {
10049 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10050 {
10051 unsigned int
10052 mask;
10053
10054 mask=0xffff;
10055 if (ping_bit_depth == 8)
10056 mask=0x00ff;
10057
10058 if (ping_bit_depth == 4)
10059 mask=0x000f;
10060
10061 if (ping_bit_depth == 2)
10062 mask=0x0003;
10063
10064 if (ping_bit_depth == 1)
10065 mask=0x0001;
10066
10067 ping_background.red=(png_uint_16)
10068 (ScaleQuantumToShort(image->background_color.red) & mask);
10069
10070 ping_background.green=(png_uint_16)
10071 (ScaleQuantumToShort(image->background_color.green) & mask);
10072
10073 ping_background.blue=(png_uint_16)
10074 (ScaleQuantumToShort(image->background_color.blue) & mask);
10075
10076 ping_background.gray=(png_uint_16) ping_background.green;
10077 }
10078
10079 if (logging != MagickFalse)
10080 {
10081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082 " Setting up bKGD chunk (1)");
10083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084 " background_color index is %d",
10085 (int) ping_background.index);
10086
10087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10088 " ping_bit_depth=%d",ping_bit_depth);
10089 }
10090
10091 ping_have_bKGD = MagickTrue;
10092 }
10093
10094 /*
10095 Select the color type.
10096 */
10097 matte=image_matte;
10098 old_bit_depth=0;
10099
10100 if (mng_info->IsPalette && mng_info->write_png8)
10101 {
10102 /* To do: make this a function cause it's used twice, except
10103 for reducing the sample depth from 8. */
10104
10105 number_colors=image_colors;
10106
10107 ping_have_tRNS=MagickFalse;
10108
10109 /*
10110 Set image palette.
10111 */
10112 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10113
10114 if (logging != MagickFalse)
10115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10116 " Setting up PLTE chunk with %d colors (%d)",
10117 number_colors, image_colors);
10118
10119 for (i=0; i < (ssize_t) number_colors; i++)
10120 {
10121 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10122 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10123 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10124 if (logging != MagickFalse)
10125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10126 #if MAGICKCORE_QUANTUM_DEPTH == 8
10127 " %3ld (%3d,%3d,%3d)",
10128 #else
10129 " %5ld (%5d,%5d,%5d)",
10130 #endif
10131 (long) i,palette[i].red,palette[i].green,palette[i].blue);
10132
10133 }
10134
10135 ping_have_PLTE=MagickTrue;
10136 image_depth=ping_bit_depth;
10137 ping_num_trans=0;
10138
10139 if (matte != MagickFalse)
10140 {
10141 /*
10142 Identify which colormap entry is transparent.
10143 */
10144 assert(number_colors <= 256);
10145 assert(image->colormap != NULL);
10146
10147 for (i=0; i < (ssize_t) number_transparent; i++)
10148 ping_trans_alpha[i]=0;
10149
10150
10151 ping_num_trans=(unsigned short) (number_transparent +
10152 number_semitransparent);
10153
10154 if (ping_num_trans == 0)
10155 ping_have_tRNS=MagickFalse;
10156
10157 else
10158 ping_have_tRNS=MagickTrue;
10159 }
10160
10161 if (ping_exclude_bKGD == MagickFalse)
10162 {
10163 /*
10164 * Identify which colormap entry is the background color.
10165 */
10166
10167 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10168 if (IsPNGColorEqual(ping_background,image->colormap[i]))
10169 break;
10170
10171 ping_background.index=(png_byte) i;
10172
10173 if (logging != MagickFalse)
10174 {
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " background_color index is %d",
10177 (int) ping_background.index);
10178 }
10179 }
10180 } /* end of write_png8 */
10181
10182 else if (mng_info->write_png_colortype == 1)
10183 {
10184 image_matte=MagickFalse;
10185 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10186 }
10187
10188 else if (mng_info->write_png24 || mng_info->write_png48 ||
10189 mng_info->write_png_colortype == 3)
10190 {
10191 image_matte=MagickFalse;
10192 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10193 }
10194
10195 else if (mng_info->write_png32 || mng_info->write_png64 ||
10196 mng_info->write_png_colortype == 7)
10197 {
10198 image_matte=MagickTrue;
10199 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10200 }
10201
10202 else /* mng_info->write_pngNN not specified */
10203 {
10204 image_depth=ping_bit_depth;
10205
10206 if (mng_info->write_png_colortype != 0)
10207 {
10208 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10209
10210 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10211 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10212 image_matte=MagickTrue;
10213
10214 else
10215 image_matte=MagickFalse;
10216
10217 if (logging != MagickFalse)
10218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219 " PNG colortype %d was specified:",(int) ping_color_type);
10220 }
10221
10222 else /* write_png_colortype not specified */
10223 {
10224 if (logging != MagickFalse)
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " Selecting PNG colortype:");
10227
10228 if (image_info->type == TrueColorType)
10229 {
10230 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10231 image_matte=MagickFalse;
10232 }
10233 else if (image_info->type == TrueColorAlphaType)
10234 {
10235 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10236 image_matte=MagickTrue;
10237 }
10238 else if (image_info->type == PaletteType ||
10239 image_info->type == PaletteAlphaType)
10240 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10241 else
10242 {
10243 if (ping_have_color == MagickFalse)
10244 {
10245 if (image_matte == MagickFalse)
10246 {
10247 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10248 image_matte=MagickFalse;
10249 }
10250
10251 else
10252 {
10253 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10254 image_matte=MagickTrue;
10255 }
10256 }
10257 else
10258 {
10259 if (image_matte == MagickFalse)
10260 {
10261 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10262 image_matte=MagickFalse;
10263 }
10264
10265 else
10266 {
10267 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10268 image_matte=MagickTrue;
10269 }
10270 }
10271 }
10272
10273 }
10274
10275 if (logging != MagickFalse)
10276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10277 " Selected PNG colortype=%d",ping_color_type);
10278
10279 if (ping_bit_depth < 8)
10280 {
10281 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10282 ping_color_type == PNG_COLOR_TYPE_RGB ||
10283 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10284 ping_bit_depth=8;
10285 }
10286
10287 old_bit_depth=ping_bit_depth;
10288
10289 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10290 {
10291 if (image->alpha_trait == UndefinedPixelTrait &&
10292 ping_have_non_bw == MagickFalse)
10293 ping_bit_depth=1;
10294 }
10295
10296 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10297 {
10298 size_t one = 1;
10299 ping_bit_depth=1;
10300
10301 if (image->colors == 0)
10302 {
10303 /* DO SOMETHING */
10304 png_error(ping,"image has 0 colors");
10305 }
10306
10307 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10308 ping_bit_depth <<= 1;
10309 }
10310
10311 if (logging != MagickFalse)
10312 {
10313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314 " Number of colors: %.20g",(double) image_colors);
10315
10316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317 " Tentative PNG bit depth: %d",ping_bit_depth);
10318 }
10319
10320 if (ping_bit_depth < (int) mng_info->write_png_depth)
10321 ping_bit_depth = mng_info->write_png_depth;
10322 }
10323 (void) old_bit_depth;
10324 image_depth=ping_bit_depth;
10325
10326 if (logging != MagickFalse)
10327 {
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " Tentative PNG color type: %s (%.20g)",
10330 PngColorTypeToString(ping_color_type),
10331 (double) ping_color_type);
10332
10333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10334 " image_info->type: %.20g",(double) image_info->type);
10335
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337 " image_depth: %.20g",(double) image_depth);
10338
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340
10341 " image->depth: %.20g",(double) image->depth);
10342
10343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10344 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10345 }
10346
10347 if (matte != MagickFalse)
10348 {
10349 if (mng_info->IsPalette)
10350 {
10351 if (mng_info->write_png_colortype == 0)
10352 {
10353 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10354
10355 if (ping_have_color != MagickFalse)
10356 ping_color_type=PNG_COLOR_TYPE_RGBA;
10357 }
10358
10359 /*
10360 * Determine if there is any transparent color.
10361 */
10362 if (number_transparent + number_semitransparent == 0)
10363 {
10364 /*
10365 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10366 */
10367
10368 image_matte=MagickFalse;
10369
10370 if (mng_info->write_png_colortype == 0)
10371 ping_color_type&=0x03;
10372 }
10373
10374 else
10375 {
10376 unsigned int
10377 mask;
10378
10379 mask=0xffff;
10380
10381 if (ping_bit_depth == 8)
10382 mask=0x00ff;
10383
10384 if (ping_bit_depth == 4)
10385 mask=0x000f;
10386
10387 if (ping_bit_depth == 2)
10388 mask=0x0003;
10389
10390 if (ping_bit_depth == 1)
10391 mask=0x0001;
10392
10393 ping_trans_color.red=(png_uint_16)
10394 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10395
10396 ping_trans_color.green=(png_uint_16)
10397 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10398
10399 ping_trans_color.blue=(png_uint_16)
10400 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10401
10402 ping_trans_color.gray=(png_uint_16)
10403 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10404 image->colormap)) & mask);
10405
10406 ping_trans_color.index=(png_byte) 0;
10407
10408 ping_have_tRNS=MagickTrue;
10409 }
10410
10411 if (ping_have_tRNS != MagickFalse)
10412 {
10413 /*
10414 * Determine if there is one and only one transparent color
10415 * and if so if it is fully transparent.
10416 */
10417 if (ping_have_cheap_transparency == MagickFalse)
10418 ping_have_tRNS=MagickFalse;
10419 }
10420
10421 if (ping_have_tRNS != MagickFalse)
10422 {
10423 if (mng_info->write_png_colortype == 0)
10424 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10425
10426 if (image_depth == 8)
10427 {
10428 ping_trans_color.red&=0xff;
10429 ping_trans_color.green&=0xff;
10430 ping_trans_color.blue&=0xff;
10431 ping_trans_color.gray&=0xff;
10432 }
10433 }
10434 }
10435 else
10436 {
10437 if (image_depth == 8)
10438 {
10439 ping_trans_color.red&=0xff;
10440 ping_trans_color.green&=0xff;
10441 ping_trans_color.blue&=0xff;
10442 ping_trans_color.gray&=0xff;
10443 }
10444 }
10445 }
10446
10447 matte=image_matte;
10448
10449 if (ping_have_tRNS != MagickFalse)
10450 image_matte=MagickFalse;
10451
10452 if ((mng_info->IsPalette) &&
10453 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10454 ping_have_color == MagickFalse &&
10455 (image_matte == MagickFalse || image_depth >= 8))
10456 {
10457 size_t one=1;
10458
10459 if (image_matte != MagickFalse)
10460 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10461
10462 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10463 {
10464 ping_color_type=PNG_COLOR_TYPE_GRAY;
10465
10466 if (save_image_depth == 16 && image_depth == 8)
10467 {
10468 if (logging != MagickFalse)
10469 {
10470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10471 " Scaling ping_trans_color (0)");
10472 }
10473 ping_trans_color.gray*=0x0101;
10474 }
10475 }
10476
10477 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10478 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10479
10480 if ((image_colors == 0) ||
10481 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10482 image_colors=(int) (one << image_depth);
10483
10484 if (image_depth > 8)
10485 ping_bit_depth=16;
10486
10487 else
10488 {
10489 ping_bit_depth=8;
10490 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10491 {
10492 if(!mng_info->write_png_depth)
10493 {
10494 ping_bit_depth=1;
10495
10496 while ((int) (one << ping_bit_depth)
10497 < (ssize_t) image_colors)
10498 ping_bit_depth <<= 1;
10499 }
10500 }
10501
10502 else if (ping_color_type ==
10503 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10504 mng_info->IsPalette)
10505 {
10506 /* Check if grayscale is reducible */
10507
10508 int
10509 depth_4_ok=MagickTrue,
10510 depth_2_ok=MagickTrue,
10511 depth_1_ok=MagickTrue;
10512
10513 for (i=0; i < (ssize_t) image_colors; i++)
10514 {
10515 unsigned char
10516 intensity;
10517
10518 intensity=ScaleQuantumToChar(image->colormap[i].red);
10519
10520 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10521 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10522 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10523 depth_2_ok=depth_1_ok=MagickFalse;
10524 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10525 depth_1_ok=MagickFalse;
10526 }
10527
10528 if (depth_1_ok && mng_info->write_png_depth <= 1)
10529 ping_bit_depth=1;
10530
10531 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10532 ping_bit_depth=2;
10533
10534 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10535 ping_bit_depth=4;
10536 }
10537 }
10538
10539 image_depth=ping_bit_depth;
10540 }
10541
10542 else
10543
10544 if (mng_info->IsPalette)
10545 {
10546 number_colors=image_colors;
10547
10548 if (image_depth <= 8)
10549 {
10550 /*
10551 Set image palette.
10552 */
10553 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10554
10555 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10556 {
10557 for (i=0; i < (ssize_t) number_colors; i++)
10558 {
10559 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10560 palette[i].green=
10561 ScaleQuantumToChar(image->colormap[i].green);
10562 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10563 }
10564
10565 if (logging != MagickFalse)
10566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567 " Setting up PLTE chunk with %d colors",
10568 number_colors);
10569
10570 ping_have_PLTE=MagickTrue;
10571 }
10572
10573 /* color_type is PNG_COLOR_TYPE_PALETTE */
10574 if (mng_info->write_png_depth == 0)
10575 {
10576 size_t
10577 one;
10578
10579 ping_bit_depth=1;
10580 one=1;
10581
10582 while ((one << ping_bit_depth) < (size_t) number_colors)
10583 ping_bit_depth <<= 1;
10584 }
10585
10586 ping_num_trans=0;
10587
10588 if (matte != MagickFalse)
10589 {
10590 /*
10591 * Set up trans_colors array.
10592 */
10593 assert(number_colors <= 256);
10594
10595 ping_num_trans=(unsigned short) (number_transparent +
10596 number_semitransparent);
10597
10598 if (ping_num_trans == 0)
10599 ping_have_tRNS=MagickFalse;
10600
10601 else
10602 {
10603 if (logging != MagickFalse)
10604 {
10605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606 " Scaling ping_trans_color (1)");
10607 }
10608 ping_have_tRNS=MagickTrue;
10609
10610 for (i=0; i < ping_num_trans; i++)
10611 {
10612 ping_trans_alpha[i]= (png_byte)
10613 ScaleQuantumToChar(image->colormap[i].alpha);
10614 }
10615 }
10616 }
10617 }
10618 }
10619
10620 else
10621 {
10622
10623 if (image_depth < 8)
10624 image_depth=8;
10625
10626 if ((save_image_depth == 16) && (image_depth == 8))
10627 {
10628 if (logging != MagickFalse)
10629 {
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " Scaling ping_trans_color from (%d,%d,%d)",
10632 (int) ping_trans_color.red,
10633 (int) ping_trans_color.green,
10634 (int) ping_trans_color.blue);
10635 }
10636
10637 ping_trans_color.red*=0x0101;
10638 ping_trans_color.green*=0x0101;
10639 ping_trans_color.blue*=0x0101;
10640 ping_trans_color.gray*=0x0101;
10641
10642 if (logging != MagickFalse)
10643 {
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " to (%d,%d,%d)",
10646 (int) ping_trans_color.red,
10647 (int) ping_trans_color.green,
10648 (int) ping_trans_color.blue);
10649 }
10650 }
10651 }
10652
10653 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10654 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10655
10656 /*
10657 Adjust background and transparency samples in sub-8-bit grayscale files.
10658 */
10659 if (ping_bit_depth < 8 && ping_color_type ==
10660 PNG_COLOR_TYPE_GRAY)
10661 {
10662 png_uint_16
10663 maxval;
10664
10665 size_t
10666 one=1;
10667
10668 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10669
10670 if (ping_exclude_bKGD == MagickFalse)
10671 {
10672
10673 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10674 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10675 &image->background_color))) +.5)));
10676
10677 if (logging != MagickFalse)
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " Setting up bKGD chunk (2)");
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " background_color index is %d",
10682 (int) ping_background.index);
10683
10684 ping_have_bKGD = MagickTrue;
10685 }
10686
10687 if (logging != MagickFalse)
10688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10689 " Scaling ping_trans_color.gray from %d",
10690 (int)ping_trans_color.gray);
10691
10692 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10693 ping_trans_color.gray)+.5);
10694
10695 if (logging != MagickFalse)
10696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697 " to %d", (int)ping_trans_color.gray);
10698 }
10699
10700 if (ping_exclude_bKGD == MagickFalse)
10701 {
10702 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10703 {
10704 /*
10705 Identify which colormap entry is the background color.
10706 */
10707
10708 number_colors=image_colors;
10709
10710 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10711 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10712 break;
10713
10714 ping_background.index=(png_byte) i;
10715
10716 if (logging != MagickFalse)
10717 {
10718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10719 " Setting up bKGD chunk with index=%d",(int) i);
10720 }
10721
10722 if (i < (ssize_t) number_colors)
10723 {
10724 ping_have_bKGD = MagickTrue;
10725
10726 if (logging != MagickFalse)
10727 {
10728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729 " background =(%d,%d,%d)",
10730 (int) ping_background.red,
10731 (int) ping_background.green,
10732 (int) ping_background.blue);
10733 }
10734 }
10735
10736 else /* Can't happen */
10737 {
10738 if (logging != MagickFalse)
10739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10740 " No room in PLTE to add bKGD color");
10741 ping_have_bKGD = MagickFalse;
10742 }
10743 }
10744 }
10745
10746 if (logging != MagickFalse)
10747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10748 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10749 ping_color_type);
10750 /*
10751 Initialize compression level and filtering.
10752 */
10753 if (logging != MagickFalse)
10754 {
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " Setting up deflate compression");
10757
10758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10759 " Compression buffer size: 32768");
10760 }
10761
10762 png_set_compression_buffer_size(ping,32768L);
10763
10764 if (logging != MagickFalse)
10765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10766 " Compression mem level: 9");
10767
10768 png_set_compression_mem_level(ping, 9);
10769
10770 /* Untangle the "-quality" setting:
10771
10772 Undefined is 0; the default is used.
10773 Default is 75
10774
10775 10's digit:
10776
10777 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10778 zlib default compression level
10779
10780 1-9: the zlib compression level
10781
10782 1's digit:
10783
10784 0-4: the PNG filter method
10785
10786 5: libpng adaptive filtering if compression level > 5
10787 libpng filter type "none" if compression level <= 5
10788 or if image is grayscale or palette
10789
10790 6: libpng adaptive filtering
10791
10792 7: "LOCO" filtering (intrapixel differing) if writing
10793 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10794 and earlier because of a missing "else".
10795
10796 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10797 filtering. Unused prior to IM-6.7.0-10, was same as 6
10798
10799 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10800 Unused prior to IM-6.7.0-10, was same as 6
10801
10802 Note that using the -quality option, not all combinations of
10803 PNG filter type, zlib compression level, and zlib compression
10804 strategy are possible. This will be addressed soon in a
10805 release that accomodates "-define png:compression-strategy", etc.
10806
10807 */
10808
10809 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10810 image_info->quality;
10811
10812 if (quality <= 9)
10813 {
10814 if (mng_info->write_png_compression_strategy == 0)
10815 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10816 }
10817
10818 else if (mng_info->write_png_compression_level == 0)
10819 {
10820 int
10821 level;
10822
10823 level=(int) MagickMin((ssize_t) quality/10,9);
10824
10825 mng_info->write_png_compression_level = level+1;
10826 }
10827
10828 if (mng_info->write_png_compression_strategy == 0)
10829 {
10830 if ((quality %10) == 8 || (quality %10) == 9)
10831 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10832 mng_info->write_png_compression_strategy=Z_RLE+1;
10833 #else
10834 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10835 #endif
10836 }
10837
10838 if (mng_info->write_png_compression_filter == 0)
10839 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10840
10841 if (logging != MagickFalse)
10842 {
10843 if (mng_info->write_png_compression_level)
10844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845 " Compression level: %d",
10846 (int) mng_info->write_png_compression_level-1);
10847
10848 if (mng_info->write_png_compression_strategy)
10849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10850 " Compression strategy: %d",
10851 (int) mng_info->write_png_compression_strategy-1);
10852
10853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10854 " Setting up filtering");
10855
10856 if (mng_info->write_png_compression_filter == 6)
10857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10858 " Base filter method: ADAPTIVE");
10859 else if (mng_info->write_png_compression_filter == 0 ||
10860 mng_info->write_png_compression_filter == 1)
10861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862 " Base filter method: NONE");
10863 else
10864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10865 " Base filter method: %d",
10866 (int) mng_info->write_png_compression_filter-1);
10867 }
10868
10869 if (mng_info->write_png_compression_level != 0)
10870 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10871
10872 if (mng_info->write_png_compression_filter == 6)
10873 {
10874 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10875 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10876 (quality < 50))
10877 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10878 else
10879 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10880 }
10881 else if (mng_info->write_png_compression_filter == 7 ||
10882 mng_info->write_png_compression_filter == 10)
10883 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10884
10885 else if (mng_info->write_png_compression_filter == 8)
10886 {
10887 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10888 if (mng_info->write_mng)
10889 {
10890 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10891 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10892 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10893 }
10894 #endif
10895 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10896 }
10897
10898 else if (mng_info->write_png_compression_filter == 9)
10899 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10900
10901 else if (mng_info->write_png_compression_filter != 0)
10902 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10903 mng_info->write_png_compression_filter-1);
10904
10905 if (mng_info->write_png_compression_strategy != 0)
10906 png_set_compression_strategy(ping,
10907 mng_info->write_png_compression_strategy-1);
10908
10909 ping_interlace_method=image_info->interlace != NoInterlace;
10910
10911 if (mng_info->write_mng)
10912 png_set_sig_bytes(ping,8);
10913
10914 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10915
10916 if (mng_info->write_png_colortype != 0)
10917 {
10918 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10919 if (ping_have_color != MagickFalse)
10920 {
10921 ping_color_type = PNG_COLOR_TYPE_RGB;
10922
10923 if (ping_bit_depth < 8)
10924 ping_bit_depth=8;
10925 }
10926
10927 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10928 if (ping_have_color != MagickFalse)
10929 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10930 }
10931
10932 if (ping_need_colortype_warning != MagickFalse ||
10933 ((mng_info->write_png_depth &&
10934 (int) mng_info->write_png_depth != ping_bit_depth) ||
10935 (mng_info->write_png_colortype &&
10936 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10937 mng_info->write_png_colortype != 7 &&
10938 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10939 {
10940 if (logging != MagickFalse)
10941 {
10942 if (ping_need_colortype_warning != MagickFalse)
10943 {
10944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10945 " Image has transparency but tRNS chunk was excluded");
10946 }
10947
10948 if (mng_info->write_png_depth)
10949 {
10950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10951 " Defined png:bit-depth=%u, Computed depth=%u",
10952 mng_info->write_png_depth,
10953 ping_bit_depth);
10954 }
10955
10956 if (mng_info->write_png_colortype)
10957 {
10958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10959 " Defined png:color-type=%u, Computed color type=%u",
10960 mng_info->write_png_colortype-1,
10961 ping_color_type);
10962 }
10963 }
10964
10965 png_warning(ping,
10966 "Cannot write image with defined png:bit-depth or png:color-type.");
10967 }
10968
10969 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10970 {
10971 /* Add an opaque matte channel */
10972 image->alpha_trait = BlendPixelTrait;
10973 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10974
10975 if (logging != MagickFalse)
10976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10977 " Added an opaque matte channel");
10978 }
10979
10980 if (number_transparent != 0 || number_semitransparent != 0)
10981 {
10982 if (ping_color_type < 4)
10983 {
10984 ping_have_tRNS=MagickTrue;
10985 if (logging != MagickFalse)
10986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10987 " Setting ping_have_tRNS=MagickTrue.");
10988 }
10989 }
10990
10991 if (logging != MagickFalse)
10992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10993 " Writing PNG header chunks");
10994
10995 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10996 ping_bit_depth,ping_color_type,
10997 ping_interlace_method,ping_compression_method,
10998 ping_filter_method);
10999
11000 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
11001 {
11002 png_set_PLTE(ping,ping_info,palette,number_colors);
11003
11004 if (logging != MagickFalse)
11005 {
11006 for (i=0; i< (ssize_t) number_colors; i++)
11007 {
11008 if (i < ping_num_trans)
11009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
11011 (int) i,
11012 (int) palette[i].red,
11013 (int) palette[i].green,
11014 (int) palette[i].blue,
11015 (int) i,
11016 (int) ping_trans_alpha[i]);
11017 else
11018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11019 " PLTE[%d] = (%d,%d,%d)",
11020 (int) i,
11021 (int) palette[i].red,
11022 (int) palette[i].green,
11023 (int) palette[i].blue);
11024 }
11025 }
11026 }
11027
11028 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11029 if (ping_exclude_sRGB != MagickFalse ||
11030 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11031 {
11032 if ((ping_exclude_tEXt == MagickFalse ||
11033 ping_exclude_zTXt == MagickFalse) &&
11034 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11035 {
11036 ResetImageProfileIterator(image);
11037 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11038 {
11039 profile=GetImageProfile(image,name);
11040
11041 if (profile != (StringInfo *) NULL)
11042 {
11043 #ifdef PNG_WRITE_iCCP_SUPPORTED
11044 if ((LocaleCompare(name,"ICC") == 0) ||
11045 (LocaleCompare(name,"ICM") == 0))
11046 {
11047 ping_have_iCCP = MagickTrue;
11048 if (ping_exclude_iCCP == MagickFalse)
11049 {
11050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11051 " Setting up iCCP chunk");
11052
11053 png_set_iCCP(ping,ping_info,(png_charp) name,0,
11054 #if (PNG_LIBPNG_VER < 10500)
11055 (png_charp) GetStringInfoDatum(profile),
11056 #else
11057 (const png_byte *) GetStringInfoDatum(profile),
11058 #endif
11059 (png_uint_32) GetStringInfoLength(profile));
11060 }
11061 else
11062 {
11063 /* Do not write hex-encoded ICC chunk */
11064 name=GetNextImageProfile(image);
11065 continue;
11066 }
11067 }
11068 #endif /* WRITE_iCCP */
11069
11070 if (LocaleCompare(name,"exif") == 0)
11071 {
11072 /* Do not write hex-encoded ICC chunk; we will
11073 write it later as an eXIf chunk */
11074 name=GetNextImageProfile(image);
11075 continue;
11076 }
11077
11078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11079 " Setting up zTXt chunk with uuencoded %s profile",
11080 name);
11081 Magick_png_write_raw_profile(image_info,ping,ping_info,
11082 (unsigned char *) name,(unsigned char *) name,
11083 GetStringInfoDatum(profile),
11084 (png_uint_32) GetStringInfoLength(profile));
11085 }
11086 name=GetNextImageProfile(image);
11087 }
11088 }
11089 }
11090
11091 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11092 if ((mng_info->have_write_global_srgb == 0) &&
11093 ping_have_iCCP != MagickTrue &&
11094 (ping_have_sRGB != MagickFalse ||
11095 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11096 {
11097 if (ping_exclude_sRGB == MagickFalse)
11098 {
11099 /*
11100 Note image rendering intent.
11101 */
11102 if (logging != MagickFalse)
11103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104 " Setting up sRGB chunk");
11105
11106 (void) png_set_sRGB(ping,ping_info,(
11107 Magick_RenderingIntent_to_PNG_RenderingIntent(
11108 image->rendering_intent)));
11109
11110 ping_have_sRGB = MagickTrue;
11111 }
11112 }
11113
11114 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11115 #endif
11116 {
11117 if (ping_exclude_gAMA == MagickFalse &&
11118 ping_have_iCCP == MagickFalse &&
11119 ping_have_sRGB == MagickFalse &&
11120 (ping_exclude_sRGB == MagickFalse ||
11121 (image->gamma < .45 || image->gamma > .46)))
11122 {
11123 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11124 {
11125 /*
11126 Note image gamma.
11127 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11128 */
11129 if (logging != MagickFalse)
11130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11131 " Setting up gAMA chunk");
11132
11133 png_set_gAMA(ping,ping_info,image->gamma);
11134 }
11135 }
11136
11137 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11138 {
11139 if ((mng_info->have_write_global_chrm == 0) &&
11140 (image->chromaticity.red_primary.x != 0.0))
11141 {
11142 /*
11143 Note image chromaticity.
11144 Note: if cHRM+gAMA == sRGB write sRGB instead.
11145 */
11146 PrimaryInfo
11147 bp,
11148 gp,
11149 rp,
11150 wp;
11151
11152 wp=image->chromaticity.white_point;
11153 rp=image->chromaticity.red_primary;
11154 gp=image->chromaticity.green_primary;
11155 bp=image->chromaticity.blue_primary;
11156
11157 if (logging != MagickFalse)
11158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11159 " Setting up cHRM chunk");
11160
11161 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11162 bp.x,bp.y);
11163 }
11164 }
11165 }
11166
11167 if (ping_exclude_bKGD == MagickFalse)
11168 {
11169 if (ping_have_bKGD != MagickFalse)
11170 {
11171 png_set_bKGD(ping,ping_info,&ping_background);
11172 if (logging != MagickFalse)
11173 {
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175 " Setting up bKGD chunk");
11176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177 " background color = (%d,%d,%d)",
11178 (int) ping_background.red,
11179 (int) ping_background.green,
11180 (int) ping_background.blue);
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182 " index = %d, gray=%d",
11183 (int) ping_background.index,
11184 (int) ping_background.gray);
11185 }
11186 }
11187 }
11188
11189 if (ping_exclude_pHYs == MagickFalse)
11190 {
11191 if (ping_have_pHYs != MagickFalse)
11192 {
11193 png_set_pHYs(ping,ping_info,
11194 ping_pHYs_x_resolution,
11195 ping_pHYs_y_resolution,
11196 ping_pHYs_unit_type);
11197
11198 if (logging != MagickFalse)
11199 {
11200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201 " Setting up pHYs chunk");
11202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203 " x_resolution=%lu",
11204 (unsigned long) ping_pHYs_x_resolution);
11205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11206 " y_resolution=%lu",
11207 (unsigned long) ping_pHYs_y_resolution);
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209 " unit_type=%lu",
11210 (unsigned long) ping_pHYs_unit_type);
11211 }
11212 }
11213 }
11214
11215 #if defined(PNG_tIME_SUPPORTED)
11216 if (ping_exclude_tIME == MagickFalse)
11217 {
11218 const char
11219 *timestamp;
11220
11221 if (image->taint == MagickFalse)
11222 {
11223 timestamp=GetImageOption(image_info,"png:tIME");
11224
11225 if (timestamp == (const char *) NULL)
11226 timestamp=GetImageProperty(image,"png:tIME",exception);
11227 }
11228
11229 else
11230 {
11231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232 " Reset tIME in tainted image");
11233
11234 timestamp=GetImageProperty(image,"date:modify",exception);
11235 }
11236
11237 if (timestamp != (const char *) NULL)
11238 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11239 }
11240 #endif
11241
11242 if (mng_info->need_blob != MagickFalse)
11243 {
11244 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11245 MagickFalse)
11246 png_error(ping,"WriteBlob Failed");
11247
11248 ping_have_blob=MagickTrue;
11249 }
11250
11251 png_write_info_before_PLTE(ping, ping_info);
11252
11253 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11254 {
11255 if (logging != MagickFalse)
11256 {
11257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11258 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11259 }
11260
11261 if (ping_color_type == 3)
11262 (void) png_set_tRNS(ping, ping_info,
11263 ping_trans_alpha,
11264 ping_num_trans,
11265 NULL);
11266
11267 else
11268 {
11269 (void) png_set_tRNS(ping, ping_info,
11270 NULL,
11271 0,
11272 &ping_trans_color);
11273
11274 if (logging != MagickFalse)
11275 {
11276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11277 " tRNS color =(%d,%d,%d)",
11278 (int) ping_trans_color.red,
11279 (int) ping_trans_color.green,
11280 (int) ping_trans_color.blue);
11281 }
11282 }
11283 }
11284
11285 png_write_info(ping,ping_info);
11286
11287 /* write orNT if image->orientation is defined */
11288 if (image->orientation != UndefinedOrientation)
11289 {
11290 unsigned char
11291 chunk[6];
11292 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11293 PNGType(chunk,mng_orNT);
11294 LogPNGChunk(logging,mng_orNT,1L);
11295 /* PNG uses Exif orientation values */
11296 chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11297 (void) WriteBlob(image,5,chunk);
11298 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11299 }
11300
11301 ping_wrote_caNv = MagickFalse;
11302
11303 /* write caNv chunk */
11304 if (ping_exclude_caNv == MagickFalse)
11305 {
11306 if ((image->page.width != 0 && image->page.width != image->columns) ||
11307 (image->page.height != 0 && image->page.height != image->rows) ||
11308 image->page.x != 0 || image->page.y != 0)
11309 {
11310 unsigned char
11311 chunk[20];
11312
11313 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11314 PNGType(chunk,mng_caNv);
11315 LogPNGChunk(logging,mng_caNv,16L);
11316 PNGLong(chunk+4,(png_uint_32) image->page.width);
11317 PNGLong(chunk+8,(png_uint_32) image->page.height);
11318 PNGsLong(chunk+12,(png_int_32) image->page.x);
11319 PNGsLong(chunk+16,(png_int_32) image->page.y);
11320 (void) WriteBlob(image,20,chunk);
11321 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11322 ping_wrote_caNv = MagickTrue;
11323 }
11324 }
11325
11326 #if defined(PNG_oFFs_SUPPORTED)
11327 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11328 {
11329 if (image->page.x || image->page.y)
11330 {
11331 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11332 (png_int_32) image->page.y, 0);
11333
11334 if (logging != MagickFalse)
11335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11337 (int) image->page.x, (int) image->page.y);
11338 }
11339 }
11340 #endif
11341
11342 #if (PNG_LIBPNG_VER == 10206)
11343 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11344 #define PNG_HAVE_IDAT 0x04
11345 ping->mode |= PNG_HAVE_IDAT;
11346 #undef PNG_HAVE_IDAT
11347 #endif
11348
11349 png_set_packing(ping);
11350 /*
11351 Allocate memory.
11352 */
11353 rowbytes=image->columns;
11354 if (image_depth > 8)
11355 rowbytes*=2;
11356 switch (ping_color_type)
11357 {
11358 case PNG_COLOR_TYPE_RGB:
11359 rowbytes*=3;
11360 break;
11361
11362 case PNG_COLOR_TYPE_GRAY_ALPHA:
11363 rowbytes*=2;
11364 break;
11365
11366 case PNG_COLOR_TYPE_RGBA:
11367 rowbytes*=4;
11368 break;
11369
11370 default:
11371 break;
11372 }
11373
11374 if (logging != MagickFalse)
11375 {
11376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11377 " Writing PNG image data");
11378
11379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11380 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11381 }
11382 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11383 if (pixel_info == (MemoryInfo *) NULL)
11384 png_error(ping,"Allocation of memory for pixels failed");
11385 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11386 (void) memset(ping_pixels,0,rowbytes*sizeof(*ping_pixels));
11387 /*
11388 Initialize image scanlines.
11389 */
11390 quantum_info=AcquireQuantumInfo(image_info,image);
11391 if (quantum_info == (QuantumInfo *) NULL)
11392 png_error(ping,"Memory allocation for quantum_info failed");
11393 quantum_info->format=UndefinedQuantumFormat;
11394 SetQuantumDepth(image,quantum_info,image_depth);
11395 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11396 num_passes=png_set_interlace_handling(ping);
11397
11398 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11399 !mng_info->write_png48 && !mng_info->write_png64 &&
11400 !mng_info->write_png32) &&
11401 (mng_info->IsPalette ||
11402 (image_info->type == BilevelType)) &&
11403 image_matte == MagickFalse &&
11404 ping_have_non_bw == MagickFalse)
11405 {
11406 /* Palette, Bilevel, or Opaque Monochrome */
11407 QuantumType
11408 quantum_type;
11409
11410 register const Quantum
11411 *p;
11412
11413 quantum_type=RedQuantum;
11414 if (mng_info->IsPalette)
11415 {
11416 quantum_type=GrayQuantum;
11417 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE)
11418 quantum_type=IndexQuantum;
11419 }
11420 SetQuantumDepth(image,quantum_info,8);
11421 for (pass=0; pass < num_passes; pass++)
11422 {
11423 /*
11424 Convert PseudoClass image to a PNG monochrome image.
11425 */
11426 for (y=0; y < (ssize_t) image->rows; y++)
11427 {
11428 if (logging != MagickFalse && y == 0)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " Writing row of pixels (0)");
11431
11432 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11433
11434 if (p == (const Quantum *) NULL)
11435 break;
11436
11437 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11438 quantum_info,quantum_type,ping_pixels,exception);
11439 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11440 for (i=0; i < (ssize_t) image->columns; i++)
11441 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11442 255 : 0);
11443
11444 if (logging != MagickFalse && y == 0)
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446 " Writing row of pixels (1)");
11447
11448 png_write_row(ping,ping_pixels);
11449
11450 status=SetImageProgress(image,SaveImageTag,
11451 (MagickOffsetType) (pass * image->rows + y),
11452 num_passes * image->rows);
11453
11454 if (status == MagickFalse)
11455 break;
11456 }
11457 }
11458 }
11459
11460 else /* Not Palette, Bilevel, or Opaque Monochrome */
11461 {
11462 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11463 !mng_info->write_png48 && !mng_info->write_png64 &&
11464 !mng_info->write_png32) && (image_matte != MagickFalse ||
11465 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11466 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11467 {
11468 register const Quantum
11469 *p;
11470
11471 for (pass=0; pass < num_passes; pass++)
11472 {
11473
11474 for (y=0; y < (ssize_t) image->rows; y++)
11475 {
11476 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11477
11478 if (p == (const Quantum *) NULL)
11479 break;
11480
11481 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11482 {
11483 if (mng_info->IsPalette)
11484 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11485 quantum_info,GrayQuantum,ping_pixels,exception);
11486
11487 else
11488 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11489 quantum_info,RedQuantum,ping_pixels,exception);
11490
11491 if (logging != MagickFalse && y == 0)
11492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11493 " Writing GRAY PNG pixels (2)");
11494 }
11495
11496 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11497 {
11498 if (logging != MagickFalse && y == 0)
11499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11500 " Writing GRAY_ALPHA PNG pixels (2)");
11501
11502 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11503 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11504 }
11505
11506 if (logging != MagickFalse && y == 0)
11507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11508 " Writing row of pixels (2)");
11509
11510 png_write_row(ping,ping_pixels);
11511
11512 status=SetImageProgress(image,SaveImageTag,
11513 (MagickOffsetType) (pass * image->rows + y),
11514 num_passes * image->rows);
11515
11516 if (status == MagickFalse)
11517 break;
11518 }
11519 }
11520 }
11521
11522 else
11523 {
11524 register const Quantum
11525 *p;
11526
11527 for (pass=0; pass < num_passes; pass++)
11528 {
11529 if ((image_depth > 8) ||
11530 mng_info->write_png24 ||
11531 mng_info->write_png32 ||
11532 mng_info->write_png48 ||
11533 mng_info->write_png64 ||
11534 (!mng_info->write_png8 && !mng_info->IsPalette))
11535 {
11536 for (y=0; y < (ssize_t) image->rows; y++)
11537 {
11538 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11539
11540 if (p == (const Quantum *) NULL)
11541 break;
11542
11543 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11544 {
11545 if (image->storage_class == DirectClass)
11546 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11547 quantum_info,RedQuantum,ping_pixels,exception);
11548
11549 else
11550 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11551 quantum_info,GrayQuantum,ping_pixels,exception);
11552 }
11553
11554 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11555 {
11556 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11557 quantum_info,GrayAlphaQuantum,ping_pixels,
11558 exception);
11559
11560 if (logging != MagickFalse && y == 0)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " Writing GRAY_ALPHA PNG pixels (3)");
11563 }
11564
11565 else if (image_matte != MagickFalse)
11566 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567 quantum_info,RGBAQuantum,ping_pixels,exception);
11568
11569 else
11570 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11571 quantum_info,RGBQuantum,ping_pixels,exception);
11572
11573 if (logging != MagickFalse && y == 0)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 " Writing row of pixels (3)");
11576
11577 png_write_row(ping,ping_pixels);
11578
11579 status=SetImageProgress(image,SaveImageTag,
11580 (MagickOffsetType) (pass * image->rows + y),
11581 num_passes * image->rows);
11582
11583 if (status == MagickFalse)
11584 break;
11585 }
11586 }
11587
11588 else
11589 /* not ((image_depth > 8) ||
11590 mng_info->write_png24 || mng_info->write_png32 ||
11591 mng_info->write_png48 || mng_info->write_png64 ||
11592 (!mng_info->write_png8 && !mng_info->IsPalette))
11593 */
11594 {
11595 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11596 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11597 {
11598 if (logging != MagickFalse)
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11600 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11601
11602 SetQuantumDepth(image,quantum_info,8);
11603 image_depth=8;
11604 }
11605
11606 for (y=0; y < (ssize_t) image->rows; y++)
11607 {
11608 if (logging != MagickFalse && y == 0)
11609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11610 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11611 pass);
11612
11613 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11614
11615 if (p == (const Quantum *) NULL)
11616 break;
11617
11618 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11619 {
11620 SetQuantumDepth(image,quantum_info,image->depth);
11621
11622 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11623 quantum_info,GrayQuantum,ping_pixels,exception);
11624 }
11625
11626 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11627 {
11628 if (logging != MagickFalse && y == 0)
11629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630 " Writing GRAY_ALPHA PNG pixels (4)");
11631
11632 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11633 quantum_info,GrayAlphaQuantum,ping_pixels,
11634 exception);
11635 }
11636
11637 else
11638 {
11639 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11640 quantum_info,IndexQuantum,ping_pixels,exception);
11641
11642 if (logging != MagickFalse && y <= 2)
11643 {
11644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11645 " Writing row of non-gray pixels (4)");
11646
11647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11648 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11649 (int)ping_pixels[0],(int)ping_pixels[1]);
11650 }
11651 }
11652 png_write_row(ping,ping_pixels);
11653
11654 status=SetImageProgress(image,SaveImageTag,
11655 (MagickOffsetType) (pass * image->rows + y),
11656 num_passes * image->rows);
11657
11658 if (status == MagickFalse)
11659 break;
11660 }
11661 }
11662 }
11663 }
11664 }
11665
11666 if (quantum_info != (QuantumInfo *) NULL)
11667 quantum_info=DestroyQuantumInfo(quantum_info);
11668
11669 if (logging != MagickFalse)
11670 {
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672 " Wrote PNG image data");
11673
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 " Width: %.20g",(double) ping_width);
11676
11677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678 " Height: %.20g",(double) ping_height);
11679
11680 if (mng_info->write_png_depth)
11681 {
11682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11683 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11684 }
11685
11686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11687 " PNG bit-depth written: %d",ping_bit_depth);
11688
11689 if (mng_info->write_png_colortype)
11690 {
11691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11693 }
11694
11695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11696 " PNG color-type written: %d",ping_color_type);
11697
11698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11699 " PNG Interlace method: %d",ping_interlace_method);
11700 }
11701 /*
11702 Generate text chunks after IDAT.
11703 */
11704 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11705 {
11706 ResetImagePropertyIterator(image);
11707 property=GetNextImageProperty(image);
11708 while (property != (const char *) NULL)
11709 {
11710 png_textp
11711 text;
11712
11713 value=GetImageProperty(image,property,exception);
11714
11715 /* Don't write any "png:" or "jpeg:" properties; those are just for
11716 * "identify" or for passing through to another JPEG
11717 */
11718 if ((LocaleNCompare(property,"png:",4) != 0 &&
11719 LocaleNCompare(property,"jpeg:",5) != 0) &&
11720
11721
11722 /* Suppress density and units if we wrote a pHYs chunk */
11723 (ping_exclude_pHYs != MagickFalse ||
11724 LocaleCompare(property,"density") != 0 ||
11725 LocaleCompare(property,"units") != 0) &&
11726
11727 /* Suppress the IM-generated Date:create and Date:modify */
11728 (ping_exclude_date == MagickFalse ||
11729 LocaleNCompare(property, "Date:",5) != 0))
11730 {
11731 if (value != (const char *) NULL)
11732 {
11733
11734 #if PNG_LIBPNG_VER >= 10400
11735 text=(png_textp) png_malloc(ping,
11736 (png_alloc_size_t) sizeof(png_text));
11737 #else
11738 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11739 #endif
11740 text[0].key=(char *) property;
11741 text[0].text=(char *) value;
11742 text[0].text_length=strlen(value);
11743
11744 if (ping_exclude_tEXt != MagickFalse)
11745 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11746
11747 else if (ping_exclude_zTXt != MagickFalse)
11748 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11749
11750 else
11751 {
11752 text[0].compression=image_info->compression == NoCompression ||
11753 (image_info->compression == UndefinedCompression &&
11754 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11755 PNG_TEXT_COMPRESSION_zTXt ;
11756 }
11757
11758 if (logging != MagickFalse)
11759 {
11760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11761 " Setting up text chunk");
11762
11763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764 " keyword: '%s'",text[0].key);
11765 }
11766
11767 png_set_text(ping,ping_info,text,1);
11768 png_free(ping,text);
11769 }
11770 }
11771 property=GetNextImageProperty(image);
11772 }
11773 }
11774
11775 /* write eXIf profile */
11776 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11777 {
11778 ResetImageProfileIterator(image);
11779
11780 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11781 {
11782 if (LocaleCompare(name,"exif") == 0)
11783 {
11784 profile=GetImageProfile(image,name);
11785
11786 if (profile != (StringInfo *) NULL)
11787 {
11788 png_uint_32
11789 length;
11790
11791 unsigned char
11792 chunk[4],
11793 *data;
11794
11795 StringInfo
11796 *ping_profile;
11797
11798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11799 " Have eXIf profile");
11800
11801 ping_profile=CloneStringInfo(profile);
11802 data=GetStringInfoDatum(ping_profile),
11803 length=(png_uint_32) GetStringInfoLength(ping_profile);
11804
11805 PNGType(chunk,mng_eXIf);
11806 if (length < 7)
11807 {
11808 ping_profile=DestroyStringInfo(ping_profile);
11809 break; /* otherwise crashes */
11810 }
11811
11812 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11813 *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11814 {
11815 /* skip the "Exif\0\0" JFIF Exif Header ID */
11816 length -= 6;
11817 data += 6;
11818 }
11819
11820 LogPNGChunk(logging,chunk,length);
11821 (void) WriteBlobMSBULong(image,length);
11822 (void) WriteBlob(image,4,chunk);
11823 (void) WriteBlob(image,length,data);
11824 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11825 (uInt) length));
11826 ping_profile=DestroyStringInfo(ping_profile);
11827 break;
11828 }
11829 }
11830 name=GetNextImageProfile(image);
11831 }
11832 }
11833
11834 if (logging != MagickFalse)
11835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 " Writing PNG end info");
11837
11838 png_write_end(ping,ping_info);
11839
11840 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11841 {
11842 if (mng_info->page.x || mng_info->page.y ||
11843 (ping_width != mng_info->page.width) ||
11844 (ping_height != mng_info->page.height))
11845 {
11846 unsigned char
11847 chunk[32];
11848
11849 /*
11850 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11851 */
11852 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11853 PNGType(chunk,mng_FRAM);
11854 LogPNGChunk(logging,mng_FRAM,27L);
11855 chunk[4]=4;
11856 chunk[5]=0; /* frame name separator (no name) */
11857 chunk[6]=1; /* flag for changing delay, for next frame only */
11858 chunk[7]=0; /* flag for changing frame timeout */
11859 chunk[8]=1; /* flag for changing frame clipping for next frame */
11860 chunk[9]=0; /* flag for changing frame sync_id */
11861 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11862 chunk[14]=0; /* clipping boundaries delta type */
11863 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11864 PNGLong(chunk+19,
11865 (png_uint_32) (mng_info->page.x + ping_width));
11866 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11867 PNGLong(chunk+27,
11868 (png_uint_32) (mng_info->page.y + ping_height));
11869 (void) WriteBlob(image,31,chunk);
11870 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11871 mng_info->old_framing_mode=4;
11872 mng_info->framing_mode=1;
11873 }
11874
11875 else
11876 mng_info->framing_mode=3;
11877 }
11878 if (mng_info->write_mng && !mng_info->need_fram &&
11879 ((int) image->dispose == 3))
11880 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11881
11882 /*
11883 Free PNG resources.
11884 */
11885
11886 png_destroy_write_struct(&ping,&ping_info);
11887
11888 pixel_info=RelinquishVirtualMemory(pixel_info);
11889
11890 if (ping_have_blob != MagickFalse)
11891 (void) CloseBlob(image);
11892
11893 image_info=DestroyImageInfo(image_info);
11894 image=DestroyImage(image);
11895
11896 /* Store bit depth actually written */
11897 s[0]=(char) ping_bit_depth;
11898 s[1]='\0';
11899
11900 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11901
11902 if (logging != MagickFalse)
11903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11904 " exit WriteOnePNGImage()");
11905
11906 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11907 UnlockSemaphoreInfo(ping_semaphore);
11908 #endif
11909
11910 /* } for navigation to beginning of SETJMP-protected block. Revert to
11911 * Throwing an Exception when an error occurs.
11912 */
11913
11914 return(MagickTrue);
11915 /* End write one PNG image */
11916
11917 }
11918
11919 /*
11920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11921 % %
11922 % %
11923 % %
11924 % W r i t e P N G I m a g e %
11925 % %
11926 % %
11927 % %
11928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11929 %
11930 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11931 % Multiple-image Network Graphics (MNG) image file.
11932 %
11933 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11934 %
11935 % The format of the WritePNGImage method is:
11936 %
11937 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11938 % Image *image,ExceptionInfo *exception)
11939 %
11940 % A description of each parameter follows:
11941 %
11942 % o image_info: the image info.
11943 %
11944 % o image: The image.
11945 %
11946 % o exception: return any errors or warnings in this structure.
11947 %
11948 % Returns MagickTrue on success, MagickFalse on failure.
11949 %
11950 % Communicating with the PNG encoder:
11951 %
11952 % While the datastream written is always in PNG format and normally would
11953 % be given the "png" file extension, this method also writes the following
11954 % pseudo-formats which are subsets of png:
11955 %
11956 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11957 % a depth greater than 8, the depth is reduced. If transparency
11958 % is present, the tRNS chunk must only have values 0 and 255
11959 % (i.e., transparency is binary: fully opaque or fully
11960 % transparent). If other values are present they will be
11961 % 50%-thresholded to binary transparency. If more than 256
11962 % colors are present, they will be quantized to the 4-4-4-1,
11963 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11964 % of any resulting fully-transparent pixels is changed to
11965 % the image's background color.
11966 %
11967 % If you want better quantization or dithering of the colors
11968 % or alpha than that, you need to do it before calling the
11969 % PNG encoder. The pixels contain 8-bit indices even if
11970 % they could be represented with 1, 2, or 4 bits. Grayscale
11971 % images will be written as indexed PNG files even though the
11972 % PNG grayscale type might be slightly more efficient. Please
11973 % note that writing to the PNG8 format may result in loss
11974 % of color and alpha data.
11975 %
11976 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11977 % chunk can be present to convey binary transparency by naming
11978 % one of the colors as transparent. The only loss incurred
11979 % is reduction of sample depth to 8. If the image has more
11980 % than one transparent color, has semitransparent pixels, or
11981 % has an opaque pixel with the same RGB components as the
11982 % transparent color, an image is not written.
11983 %
11984 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11985 % transparency is permitted, i.e., the alpha sample for
11986 % each pixel can have any value from 0 to 255. The alpha
11987 % channel is present even if the image is fully opaque.
11988 % The only loss in data is the reduction of the sample depth
11989 % to 8.
11990 %
11991 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11992 % chunk can be present to convey binary transparency by naming
11993 % one of the colors as transparent. If the image has more
11994 % than one transparent color, has semitransparent pixels, or
11995 % has an opaque pixel with the same RGB components as the
11996 % transparent color, an image is not written.
11997 %
11998 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11999 % transparency is permitted, i.e., the alpha sample for
12000 % each pixel can have any value from 0 to 65535. The alpha
12001 % channel is present even if the image is fully opaque.
12002 %
12003 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
12004 % image, if the input was a PNG, is written. If these values
12005 % cannot be found, or if the pixels have been changed in a way
12006 % that makes this impossible, then "PNG00" falls back to the
12007 % regular "PNG" format.
12008 %
12009 % o -define: For more precise control of the PNG output, you can use the
12010 % Image options "png:bit-depth" and "png:color-type". These
12011 % can be set from the commandline with "-define" and also
12012 % from the application programming interfaces. The options
12013 % are case-independent and are converted to lowercase before
12014 % being passed to this encoder.
12015 %
12016 % png:color-type can be 0, 2, 3, 4, or 6.
12017 %
12018 % When png:color-type is 0 (Grayscale), png:bit-depth can
12019 % be 1, 2, 4, 8, or 16.
12020 %
12021 % When png:color-type is 2 (RGB), png:bit-depth can
12022 % be 8 or 16.
12023 %
12024 % When png:color-type is 3 (Indexed), png:bit-depth can
12025 % be 1, 2, 4, or 8. This refers to the number of bits
12026 % used to store the index. The color samples always have
12027 % bit-depth 8 in indexed PNG files.
12028 %
12029 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12030 % png:bit-depth can be 8 or 16.
12031 %
12032 % If the image cannot be written without loss with the
12033 % requested bit-depth and color-type, a PNG file will not
12034 % be written, a warning will be issued, and the encoder will
12035 % return MagickFalse.
12036 %
12037 % Since image encoders should not be responsible for the "heavy lifting",
12038 % the user should make sure that ImageMagick has already reduced the
12039 % image depth and number of colors and limit transparency to binary
12040 % transparency prior to attempting to write the image with depth, color,
12041 % or transparency limitations.
12042 %
12043 % Note that another definition, "png:bit-depth-written" exists, but it
12044 % is not intended for external use. It is only used internally by the
12045 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12046 %
12047 % As of version 6.6.6 the following optimizations are always done:
12048 %
12049 % o 32-bit depth is reduced to 16.
12050 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
12051 % high byte and low byte are identical.
12052 % o Palette is sorted to remove unused entries and to put a
12053 % transparent color first, if BUILD_PNG_PALETTE is defined.
12054 % o Opaque matte channel is removed when writing an indexed PNG.
12055 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
12056 % this can be done without loss and a larger bit depth N was not
12057 % requested via the "-define png:bit-depth=N" option.
12058 % o If matte channel is present but only one transparent color is
12059 % present, RGB+tRNS is written instead of RGBA
12060 % o Opaque matte channel is removed (or added, if color-type 4 or 6
12061 % was requested when converting an opaque image).
12062 %
12063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12064 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12065 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12066 Image *image,ExceptionInfo *exception)
12067 {
12068 MagickBooleanType
12069 excluding,
12070 logging,
12071 status;
12072
12073 MngInfo
12074 *mng_info;
12075
12076 const char
12077 *value;
12078
12079 int
12080 source;
12081
12082 /*
12083 Open image file.
12084 */
12085 assert(image_info != (const ImageInfo *) NULL);
12086 assert(image_info->signature == MagickCoreSignature);
12087 assert(image != (Image *) NULL);
12088 assert(image->signature == MagickCoreSignature);
12089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12090 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12091 /*
12092 Allocate a MngInfo structure.
12093 */
12094 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12095
12096 if (mng_info == (MngInfo *) NULL)
12097 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12098
12099 /*
12100 Initialize members of the MngInfo structure.
12101 */
12102 (void) memset(mng_info,0,sizeof(MngInfo));
12103 mng_info->image=image;
12104 mng_info->equal_backgrounds=MagickTrue;
12105
12106 /* See if user has requested a specific PNG subformat */
12107
12108 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12109 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12110 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12111 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12112 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12113
12114 value=GetImageOption(image_info,"png:format");
12115
12116 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12117 {
12118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12119 " Format=%s",value);
12120
12121 mng_info->write_png8 = MagickFalse;
12122 mng_info->write_png24 = MagickFalse;
12123 mng_info->write_png32 = MagickFalse;
12124 mng_info->write_png48 = MagickFalse;
12125 mng_info->write_png64 = MagickFalse;
12126
12127 if (LocaleCompare(value,"png8") == 0)
12128 mng_info->write_png8 = MagickTrue;
12129
12130 else if (LocaleCompare(value,"png24") == 0)
12131 mng_info->write_png24 = MagickTrue;
12132
12133 else if (LocaleCompare(value,"png32") == 0)
12134 mng_info->write_png32 = MagickTrue;
12135
12136 else if (LocaleCompare(value,"png48") == 0)
12137 mng_info->write_png48 = MagickTrue;
12138
12139 else if (LocaleCompare(value,"png64") == 0)
12140 mng_info->write_png64 = MagickTrue;
12141
12142 else if ((LocaleCompare(value,"png00") == 0) ||
12143 LocaleCompare(image_info->magick,"PNG00") == 0)
12144 {
12145 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12146 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12147
12148 if (value != (char *) NULL)
12149 {
12150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12151 " png00 inherited bit depth=%s",value);
12152
12153 if (LocaleCompare(value,"1") == 0)
12154 mng_info->write_png_depth = 1;
12155
12156 else if (LocaleCompare(value,"2") == 0)
12157 mng_info->write_png_depth = 2;
12158
12159 else if (LocaleCompare(value,"4") == 0)
12160 mng_info->write_png_depth = 4;
12161
12162 else if (LocaleCompare(value,"8") == 0)
12163 mng_info->write_png_depth = 8;
12164
12165 else if (LocaleCompare(value,"16") == 0)
12166 mng_info->write_png_depth = 16;
12167 }
12168
12169 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12170
12171 if (value != (char *) NULL)
12172 {
12173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174 " png00 inherited color type=%s",value);
12175
12176 if (LocaleCompare(value,"0") == 0)
12177 mng_info->write_png_colortype = 1;
12178
12179 else if (LocaleCompare(value,"2") == 0)
12180 mng_info->write_png_colortype = 3;
12181
12182 else if (LocaleCompare(value,"3") == 0)
12183 mng_info->write_png_colortype = 4;
12184
12185 else if (LocaleCompare(value,"4") == 0)
12186 mng_info->write_png_colortype = 5;
12187
12188 else if (LocaleCompare(value,"6") == 0)
12189 mng_info->write_png_colortype = 7;
12190 }
12191 }
12192 }
12193
12194 if (mng_info->write_png8)
12195 {
12196 mng_info->write_png_colortype = /* 3 */ 4;
12197 mng_info->write_png_depth = 8;
12198 image->depth = 8;
12199 }
12200
12201 if (mng_info->write_png24)
12202 {
12203 mng_info->write_png_colortype = /* 2 */ 3;
12204 mng_info->write_png_depth = 8;
12205 image->depth = 8;
12206
12207 if (image->alpha_trait != UndefinedPixelTrait)
12208 (void) SetImageType(image,TrueColorAlphaType,exception);
12209
12210 else
12211 (void) SetImageType(image,TrueColorType,exception);
12212
12213 (void) SyncImage(image,exception);
12214 }
12215
12216 if (mng_info->write_png32)
12217 {
12218 mng_info->write_png_colortype = /* 6 */ 7;
12219 mng_info->write_png_depth = 8;
12220 image->depth = 8;
12221 image->alpha_trait = BlendPixelTrait;
12222
12223 (void) SetImageType(image,TrueColorAlphaType,exception);
12224 (void) SyncImage(image,exception);
12225 }
12226
12227 if (mng_info->write_png48)
12228 {
12229 mng_info->write_png_colortype = /* 2 */ 3;
12230 mng_info->write_png_depth = 16;
12231 image->depth = 16;
12232
12233 if (image->alpha_trait != UndefinedPixelTrait)
12234 (void) SetImageType(image,TrueColorAlphaType,exception);
12235
12236 else
12237 (void) SetImageType(image,TrueColorType,exception);
12238
12239 (void) SyncImage(image,exception);
12240 }
12241
12242 if (mng_info->write_png64)
12243 {
12244 mng_info->write_png_colortype = /* 6 */ 7;
12245 mng_info->write_png_depth = 16;
12246 image->depth = 16;
12247 image->alpha_trait = BlendPixelTrait;
12248
12249 (void) SetImageType(image,TrueColorAlphaType,exception);
12250 (void) SyncImage(image,exception);
12251 }
12252
12253 value=GetImageOption(image_info,"png:bit-depth");
12254
12255 if (value != (char *) NULL)
12256 {
12257 if (LocaleCompare(value,"1") == 0)
12258 mng_info->write_png_depth = 1;
12259
12260 else if (LocaleCompare(value,"2") == 0)
12261 mng_info->write_png_depth = 2;
12262
12263 else if (LocaleCompare(value,"4") == 0)
12264 mng_info->write_png_depth = 4;
12265
12266 else if (LocaleCompare(value,"8") == 0)
12267 mng_info->write_png_depth = 8;
12268
12269 else if (LocaleCompare(value,"16") == 0)
12270 mng_info->write_png_depth = 16;
12271
12272 else
12273 (void) ThrowMagickException(exception,
12274 GetMagickModule(),CoderWarning,
12275 "ignoring invalid defined png:bit-depth",
12276 "=%s",value);
12277
12278 if (logging != MagickFalse)
12279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12280 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12281 }
12282
12283 value=GetImageOption(image_info,"png:color-type");
12284
12285 if (value != (char *) NULL)
12286 {
12287 /* We must store colortype+1 because 0 is a valid colortype */
12288 if (LocaleCompare(value,"0") == 0)
12289 mng_info->write_png_colortype = 1;
12290
12291 else if (LocaleCompare(value,"1") == 0)
12292 mng_info->write_png_colortype = 2;
12293
12294 else if (LocaleCompare(value,"2") == 0)
12295 mng_info->write_png_colortype = 3;
12296
12297 else if (LocaleCompare(value,"3") == 0)
12298 mng_info->write_png_colortype = 4;
12299
12300 else if (LocaleCompare(value,"4") == 0)
12301 mng_info->write_png_colortype = 5;
12302
12303 else if (LocaleCompare(value,"6") == 0)
12304 mng_info->write_png_colortype = 7;
12305
12306 else
12307 (void) ThrowMagickException(exception,
12308 GetMagickModule(),CoderWarning,
12309 "ignoring invalid defined png:color-type",
12310 "=%s",value);
12311
12312 if (logging != MagickFalse)
12313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12314 " png:color-type=%d was defined.\n",
12315 mng_info->write_png_colortype-1);
12316 }
12317
12318 /* Check for chunks to be excluded:
12319 *
12320 * The default is to not exclude any known chunks except for any
12321 * listed in the "unused_chunks" array, above.
12322 *
12323 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12324 * define (in the image properties or in the image artifacts)
12325 * or via a mng_info member. For convenience, in addition
12326 * to or instead of a comma-separated list of chunks, the
12327 * "exclude-chunk" string can be simply "all" or "none".
12328 *
12329 * Note that the "-strip" option provides a convenient way of
12330 * doing the equivalent of
12331 *
12332 * -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12333 * iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12334 *
12335 * The exclude-chunk define takes priority over the mng_info.
12336 *
12337 * A "png:include-chunk" define takes priority over both the
12338 * mng_info and the "png:exclude-chunk" define. Like the
12339 * "exclude-chunk" string, it can define "all" or "none" as
12340 * well as a comma-separated list. Chunks that are unknown to
12341 * ImageMagick are always excluded, regardless of their "copy-safe"
12342 * status according to the PNG specification, and even if they
12343 * appear in the "include-chunk" list. Such defines appearing among
12344 * the image options take priority over those found among the image
12345 * artifacts.
12346 *
12347 * Finally, all chunks listed in the "unused_chunks" array are
12348 * automatically excluded, regardless of the other instructions
12349 * or lack thereof.
12350 *
12351 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12352 * will not be written and the gAMA chunk will only be written if it
12353 * is not between .45 and .46, or approximately (1.0/2.2).
12354 *
12355 * If you exclude tRNS and the image has transparency, the colortype
12356 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12357 *
12358 * The -strip option causes StripImage() to set the png:include-chunk
12359 * artifact to "none,trns,gama".
12360 */
12361
12362 mng_info->ping_exclude_bKGD=MagickFalse;
12363 mng_info->ping_exclude_caNv=MagickFalse;
12364 mng_info->ping_exclude_cHRM=MagickFalse;
12365 mng_info->ping_exclude_date=MagickFalse;
12366 mng_info->ping_exclude_eXIf=MagickFalse;
12367 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12368 mng_info->ping_exclude_gAMA=MagickFalse;
12369 mng_info->ping_exclude_iCCP=MagickFalse;
12370 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12371 mng_info->ping_exclude_oFFs=MagickFalse;
12372 mng_info->ping_exclude_pHYs=MagickFalse;
12373 mng_info->ping_exclude_sRGB=MagickFalse;
12374 mng_info->ping_exclude_tEXt=MagickFalse;
12375 mng_info->ping_exclude_tIME=MagickFalse;
12376 mng_info->ping_exclude_tRNS=MagickFalse;
12377 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12378 mng_info->ping_exclude_zTXt=MagickFalse;
12379
12380 mng_info->ping_preserve_colormap=MagickFalse;
12381
12382 value=GetImageOption(image_info,"png:preserve-colormap");
12383 if (value == NULL)
12384 value=GetImageArtifact(image,"png:preserve-colormap");
12385 if (value != NULL)
12386 mng_info->ping_preserve_colormap=MagickTrue;
12387
12388 mng_info->ping_preserve_iCCP=MagickFalse;
12389
12390 value=GetImageOption(image_info,"png:preserve-iCCP");
12391 if (value == NULL)
12392 value=GetImageArtifact(image,"png:preserve-iCCP");
12393 if (value != NULL)
12394 mng_info->ping_preserve_iCCP=MagickTrue;
12395
12396 /* These compression-level, compression-strategy, and compression-filter
12397 * defines take precedence over values from the -quality option.
12398 */
12399 value=GetImageOption(image_info,"png:compression-level");
12400 if (value == NULL)
12401 value=GetImageArtifact(image,"png:compression-level");
12402 if (value != NULL)
12403 {
12404 /* We have to add 1 to everything because 0 is a valid input,
12405 * and we want to use 0 (the default) to mean undefined.
12406 */
12407 if (LocaleCompare(value,"0") == 0)
12408 mng_info->write_png_compression_level = 1;
12409
12410 else if (LocaleCompare(value,"1") == 0)
12411 mng_info->write_png_compression_level = 2;
12412
12413 else if (LocaleCompare(value,"2") == 0)
12414 mng_info->write_png_compression_level = 3;
12415
12416 else if (LocaleCompare(value,"3") == 0)
12417 mng_info->write_png_compression_level = 4;
12418
12419 else if (LocaleCompare(value,"4") == 0)
12420 mng_info->write_png_compression_level = 5;
12421
12422 else if (LocaleCompare(value,"5") == 0)
12423 mng_info->write_png_compression_level = 6;
12424
12425 else if (LocaleCompare(value,"6") == 0)
12426 mng_info->write_png_compression_level = 7;
12427
12428 else if (LocaleCompare(value,"7") == 0)
12429 mng_info->write_png_compression_level = 8;
12430
12431 else if (LocaleCompare(value,"8") == 0)
12432 mng_info->write_png_compression_level = 9;
12433
12434 else if (LocaleCompare(value,"9") == 0)
12435 mng_info->write_png_compression_level = 10;
12436
12437 else
12438 (void) ThrowMagickException(exception,
12439 GetMagickModule(),CoderWarning,
12440 "ignoring invalid defined png:compression-level",
12441 "=%s",value);
12442 }
12443
12444 value=GetImageOption(image_info,"png:compression-strategy");
12445 if (value == NULL)
12446 value=GetImageArtifact(image,"png:compression-strategy");
12447 if (value != NULL)
12448 {
12449 if (LocaleCompare(value,"0") == 0)
12450 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12451
12452 else if (LocaleCompare(value,"1") == 0)
12453 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12454
12455 else if (LocaleCompare(value,"2") == 0)
12456 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12457
12458 else if (LocaleCompare(value,"3") == 0)
12459 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12460 mng_info->write_png_compression_strategy = Z_RLE+1;
12461 #else
12462 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12463 #endif
12464
12465 else if (LocaleCompare(value,"4") == 0)
12466 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12467 mng_info->write_png_compression_strategy = Z_FIXED+1;
12468 #else
12469 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12470 #endif
12471
12472 else
12473 (void) ThrowMagickException(exception,
12474 GetMagickModule(),CoderWarning,
12475 "ignoring invalid defined png:compression-strategy",
12476 "=%s",value);
12477 }
12478
12479 value=GetImageOption(image_info,"png:compression-filter");
12480 if (value == NULL)
12481 value=GetImageArtifact(image,"png:compression-filter");
12482 if (value != NULL)
12483 {
12484 /* To do: combinations of filters allowed by libpng
12485 * masks 0x08 through 0xf8
12486 *
12487 * Implement this as a comma-separated list of 0,1,2,3,4,5
12488 * where 5 is a special case meaning PNG_ALL_FILTERS.
12489 */
12490
12491 if (LocaleCompare(value,"0") == 0)
12492 mng_info->write_png_compression_filter = 1;
12493
12494 else if (LocaleCompare(value,"1") == 0)
12495 mng_info->write_png_compression_filter = 2;
12496
12497 else if (LocaleCompare(value,"2") == 0)
12498 mng_info->write_png_compression_filter = 3;
12499
12500 else if (LocaleCompare(value,"3") == 0)
12501 mng_info->write_png_compression_filter = 4;
12502
12503 else if (LocaleCompare(value,"4") == 0)
12504 mng_info->write_png_compression_filter = 5;
12505
12506 else if (LocaleCompare(value,"5") == 0)
12507 mng_info->write_png_compression_filter = 6;
12508
12509 else
12510 (void) ThrowMagickException(exception,
12511 GetMagickModule(),CoderWarning,
12512 "ignoring invalid defined png:compression-filter",
12513 "=%s",value);
12514 }
12515
12516 for (source=0; source<8; source++)
12517 {
12518 value = NULL;
12519
12520 if (source == 0)
12521 value=GetImageOption(image_info,"png:exclude-chunks");
12522
12523 if (source == 1)
12524 value=GetImageArtifact(image,"png:exclude-chunks");
12525
12526 if (source == 2)
12527 value=GetImageOption(image_info,"png:exclude-chunk");
12528
12529 if (source == 3)
12530 value=GetImageArtifact(image,"png:exclude-chunk");
12531
12532 if (source == 4)
12533 value=GetImageOption(image_info,"png:include-chunks");
12534
12535 if (source == 5)
12536 value=GetImageArtifact(image,"png:include-chunks");
12537
12538 if (source == 6)
12539 value=GetImageOption(image_info,"png:include-chunk");
12540
12541 if (source == 7)
12542 value=GetImageArtifact(image,"png:include-chunk");
12543
12544 if (value == NULL)
12545 continue;
12546
12547 if (source < 4)
12548 excluding = MagickTrue;
12549 else
12550 excluding = MagickFalse;
12551
12552 if (logging != MagickFalse)
12553 {
12554 if (source == 0 || source == 2)
12555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12556 " png:exclude-chunk=%s found in image options.\n", value);
12557 else if (source == 1 || source == 3)
12558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12559 " png:exclude-chunk=%s found in image artifacts.\n", value);
12560 else if (source == 4 || source == 6)
12561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562 " png:include-chunk=%s found in image options.\n", value);
12563 else /* if (source == 5 || source == 7) */
12564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565 " png:include-chunk=%s found in image artifacts.\n", value);
12566 }
12567
12568 if (IsOptionMember("all",value) != MagickFalse)
12569 {
12570 mng_info->ping_exclude_bKGD=excluding;
12571 mng_info->ping_exclude_caNv=excluding;
12572 mng_info->ping_exclude_cHRM=excluding;
12573 mng_info->ping_exclude_date=excluding;
12574 mng_info->ping_exclude_EXIF=excluding;
12575 mng_info->ping_exclude_eXIf=excluding;
12576 mng_info->ping_exclude_gAMA=excluding;
12577 mng_info->ping_exclude_iCCP=excluding;
12578 /* mng_info->ping_exclude_iTXt=excluding; */
12579 mng_info->ping_exclude_oFFs=excluding;
12580 mng_info->ping_exclude_pHYs=excluding;
12581 mng_info->ping_exclude_sRGB=excluding;
12582 mng_info->ping_exclude_tEXt=excluding;
12583 mng_info->ping_exclude_tIME=excluding;
12584 mng_info->ping_exclude_tRNS=excluding;
12585 mng_info->ping_exclude_zCCP=excluding;
12586 mng_info->ping_exclude_zTXt=excluding;
12587 }
12588
12589 if (IsOptionMember("none",value) != MagickFalse)
12590 {
12591 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12592 MagickTrue;
12593 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12594 MagickTrue;
12595 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12596 MagickTrue;
12597 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12598 MagickTrue;
12599 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12600 MagickTrue;
12601 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12602 MagickTrue;
12603 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12604 MagickTrue;
12605 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12606 MagickTrue;
12607 /* mng_info->ping_exclude_iTXt=!excluding; */
12608 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12609 MagickTrue;
12610 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12611 MagickTrue;
12612 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12613 MagickTrue;
12614 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12615 MagickTrue;
12616 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12617 MagickTrue;
12618 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12619 MagickTrue;
12620 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12621 MagickTrue;
12622 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12623 MagickTrue;
12624 }
12625
12626 if (IsOptionMember("bkgd",value) != MagickFalse)
12627 mng_info->ping_exclude_bKGD=excluding;
12628
12629 if (IsOptionMember("caNv",value) != MagickFalse)
12630 mng_info->ping_exclude_caNv=excluding;
12631
12632 if (IsOptionMember("chrm",value) != MagickFalse)
12633 mng_info->ping_exclude_cHRM=excluding;
12634
12635 if (IsOptionMember("date",value) != MagickFalse)
12636 mng_info->ping_exclude_date=excluding;
12637
12638 if (IsOptionMember("exif",value) != MagickFalse)
12639 {
12640 mng_info->ping_exclude_EXIF=excluding;
12641 mng_info->ping_exclude_eXIf=excluding;
12642 }
12643
12644 if (IsOptionMember("gama",value) != MagickFalse)
12645 mng_info->ping_exclude_gAMA=excluding;
12646
12647 if (IsOptionMember("iccp",value) != MagickFalse)
12648 mng_info->ping_exclude_iCCP=excluding;
12649
12650 #if 0
12651 if (IsOptionMember("itxt",value) != MagickFalse)
12652 mng_info->ping_exclude_iTXt=excluding;
12653 #endif
12654
12655 if (IsOptionMember("offs",value) != MagickFalse)
12656 mng_info->ping_exclude_oFFs=excluding;
12657
12658 if (IsOptionMember("phys",value) != MagickFalse)
12659 mng_info->ping_exclude_pHYs=excluding;
12660
12661 if (IsOptionMember("srgb",value) != MagickFalse)
12662 mng_info->ping_exclude_sRGB=excluding;
12663
12664 if (IsOptionMember("text",value) != MagickFalse)
12665 mng_info->ping_exclude_tEXt=excluding;
12666
12667 if (IsOptionMember("time",value) != MagickFalse)
12668 mng_info->ping_exclude_tIME=excluding;
12669
12670 if (IsOptionMember("trns",value) != MagickFalse)
12671 mng_info->ping_exclude_tRNS=excluding;
12672
12673 if (IsOptionMember("zccp",value) != MagickFalse)
12674 mng_info->ping_exclude_zCCP=excluding;
12675
12676 if (IsOptionMember("ztxt",value) != MagickFalse)
12677 mng_info->ping_exclude_zTXt=excluding;
12678 }
12679
12680 if (logging != MagickFalse)
12681 {
12682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12683 " Chunks to be excluded from the output png:");
12684 if (mng_info->ping_exclude_bKGD != MagickFalse)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686 " bKGD");
12687 if (mng_info->ping_exclude_caNv != MagickFalse)
12688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12689 " caNv");
12690 if (mng_info->ping_exclude_cHRM != MagickFalse)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692 " cHRM");
12693 if (mng_info->ping_exclude_date != MagickFalse)
12694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695 " date");
12696 if (mng_info->ping_exclude_EXIF != MagickFalse)
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698 " EXIF");
12699 if (mng_info->ping_exclude_eXIf != MagickFalse)
12700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701 " eXIf");
12702 if (mng_info->ping_exclude_gAMA != MagickFalse)
12703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704 " gAMA");
12705 if (mng_info->ping_exclude_iCCP != MagickFalse)
12706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707 " iCCP");
12708 #if 0
12709 if (mng_info->ping_exclude_iTXt != MagickFalse)
12710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12711 " iTXt");
12712 #endif
12713
12714 if (mng_info->ping_exclude_oFFs != MagickFalse)
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 " oFFs");
12717 if (mng_info->ping_exclude_pHYs != MagickFalse)
12718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719 " pHYs");
12720 if (mng_info->ping_exclude_sRGB != MagickFalse)
12721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12722 " sRGB");
12723 if (mng_info->ping_exclude_tEXt != MagickFalse)
12724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12725 " tEXt");
12726 if (mng_info->ping_exclude_tIME != MagickFalse)
12727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728 " tIME");
12729 if (mng_info->ping_exclude_tRNS != MagickFalse)
12730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12731 " tRNS");
12732 if (mng_info->ping_exclude_zCCP != MagickFalse)
12733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734 " zCCP");
12735 if (mng_info->ping_exclude_zTXt != MagickFalse)
12736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12737 " zTXt");
12738 }
12739
12740 mng_info->need_blob = MagickTrue;
12741
12742 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12743
12744 mng_info=MngInfoFreeStruct(mng_info);
12745
12746 if (logging != MagickFalse)
12747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12748
12749 return(status);
12750 }
12751
12752 #if defined(JNG_SUPPORTED)
12753
12754 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12755 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12756 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12757 {
12758 Image
12759 *jpeg_image;
12760
12761 ImageInfo
12762 *jpeg_image_info;
12763
12764 MagickBooleanType
12765 logging,
12766 status;
12767
12768 size_t
12769 length;
12770
12771 unsigned char
12772 *blob,
12773 chunk[80],
12774 *p;
12775
12776 unsigned int
12777 jng_alpha_compression_method,
12778 jng_alpha_sample_depth,
12779 jng_color_type,
12780 transparent;
12781
12782 size_t
12783 jng_alpha_quality,
12784 jng_quality;
12785
12786 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12787 " Enter WriteOneJNGImage()");
12788
12789 blob=(unsigned char *) NULL;
12790 jpeg_image=(Image *) NULL;
12791 jpeg_image_info=(ImageInfo *) NULL;
12792 length=0;
12793
12794 status=MagickTrue;
12795 transparent=image_info->type==GrayscaleAlphaType ||
12796 image_info->type==TrueColorAlphaType ||
12797 image->alpha_trait != UndefinedPixelTrait;
12798
12799 jng_alpha_sample_depth = 0;
12800
12801 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12802
12803 jng_alpha_compression_method=(image->compression==JPEGCompression ||
12804 image_info->compression==JPEGCompression) ? 8 : 0;
12805
12806 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12807 image_info->quality;
12808
12809 if (jng_alpha_quality >= 1000)
12810 jng_alpha_quality /= 1000;
12811
12812 length=0;
12813
12814 if (transparent != 0)
12815 {
12816 jng_color_type=14;
12817
12818 /* Create JPEG blob, image, and image_info */
12819 if (logging != MagickFalse)
12820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12821 " Creating jpeg_image_info for alpha.");
12822
12823 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12824
12825 if (jpeg_image_info == (ImageInfo *) NULL)
12826 {
12827 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12828 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12829 }
12830
12831 if (logging != MagickFalse)
12832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12833 " Creating jpeg_image.");
12834
12835 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12836 if (jpeg_image == (Image *) NULL)
12837 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12838 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12839 jpeg_image->alpha_trait=UndefinedPixelTrait;
12840 jpeg_image->quality=jng_alpha_quality;
12841 jpeg_image_info->type=GrayscaleType;
12842 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12843 (void) AcquireUniqueFilename(jpeg_image->filename);
12844 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12845 "%s",jpeg_image->filename);
12846 }
12847 else
12848 {
12849 jng_alpha_compression_method=0;
12850 jng_color_type=10;
12851 jng_alpha_sample_depth=0;
12852 }
12853
12854 /* To do: check bit depth of PNG alpha channel */
12855
12856 /* Check if image is grayscale. */
12857 if (image_info->type != TrueColorAlphaType && image_info->type !=
12858 TrueColorType && SetImageGray(image,exception))
12859 jng_color_type-=2;
12860
12861 if (logging != MagickFalse)
12862 {
12863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12864 " JNG Quality = %d",(int) jng_quality);
12865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12866 " JNG Color Type = %d",jng_color_type);
12867 if (transparent != 0)
12868 {
12869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12875 }
12876 }
12877
12878 if (transparent != 0)
12879 {
12880 if (jng_alpha_compression_method==0)
12881 {
12882 const char
12883 *value;
12884
12885 /* Encode alpha as a grayscale PNG blob */
12886 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12887 exception);
12888 if (status == MagickFalse)
12889 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12890
12891 if (logging != MagickFalse)
12892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12893 " Creating PNG blob.");
12894
12895 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12896 MagickPathExtent);
12897 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12898 jpeg_image_info->interlace=NoInterlace;
12899
12900 /* Exclude all ancillary chunks */
12901 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12902
12903 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12904 &length,exception);
12905
12906 /* Retrieve sample depth used */
12907 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12908 if (value != (char *) NULL)
12909 jng_alpha_sample_depth= (unsigned int) value[0];
12910 }
12911 else
12912 {
12913 /* Encode alpha as a grayscale JPEG blob */
12914
12915 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12916 exception);
12917 if (status == MagickFalse)
12918 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12919
12920 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12921 MagickPathExtent);
12922 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12923 jpeg_image_info->interlace=NoInterlace;
12924 if (logging != MagickFalse)
12925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12926 " Creating blob.");
12927 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12928 jpeg_image,&length,
12929 exception);
12930 jng_alpha_sample_depth=8;
12931
12932 if (logging != MagickFalse)
12933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12934 " Successfully read jpeg_image into a blob, length=%.20g.",
12935 (double) length);
12936
12937 }
12938 /* Destroy JPEG image and image_info */
12939 jpeg_image=DestroyImage(jpeg_image);
12940 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12941 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12942 }
12943
12944 /* Write JHDR chunk */
12945 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12946 PNGType(chunk,mng_JHDR);
12947 LogPNGChunk(logging,mng_JHDR,16L);
12948 PNGLong(chunk+4,(png_uint_32) image->columns);
12949 PNGLong(chunk+8,(png_uint_32) image->rows);
12950 chunk[12]=jng_color_type;
12951 chunk[13]=8; /* sample depth */
12952 chunk[14]=8; /*jng_image_compression_method */
12953 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12954 chunk[16]=jng_alpha_sample_depth;
12955 chunk[17]=jng_alpha_compression_method;
12956 chunk[18]=0; /*jng_alpha_filter_method */
12957 chunk[19]=0; /*jng_alpha_interlace_method */
12958 (void) WriteBlob(image,20,chunk);
12959 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12960 if (logging != MagickFalse)
12961 {
12962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12963 " JNG width:%15lu",(unsigned long) image->columns);
12964
12965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12966 " JNG height:%14lu",(unsigned long) image->rows);
12967
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969 " JNG color type:%10d",jng_color_type);
12970
12971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972 " JNG sample depth:%8d",8);
12973
12974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975 " JNG compression:%9d",8);
12976
12977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978 " JNG interlace:%11d",0);
12979
12980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12981 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12982
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984 " JNG alpha compression:%3d",jng_alpha_compression_method);
12985
12986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12987 " JNG alpha filter:%8d",0);
12988
12989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990 " JNG alpha interlace:%5d",0);
12991 }
12992
12993 /*
12994 Write leading ancillary chunks
12995 */
12996
12997 if (transparent != 0)
12998 {
12999 /*
13000 Write JNG bKGD chunk
13001 */
13002
13003 unsigned char
13004 blue,
13005 green,
13006 red;
13007
13008 ssize_t
13009 num_bytes;
13010
13011 if (jng_color_type == 8 || jng_color_type == 12)
13012 num_bytes=6L;
13013 else
13014 num_bytes=10L;
13015 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13016 PNGType(chunk,mng_bKGD);
13017 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13018 red=ScaleQuantumToChar(image->background_color.red);
13019 green=ScaleQuantumToChar(image->background_color.green);
13020 blue=ScaleQuantumToChar(image->background_color.blue);
13021 *(chunk+4)=0;
13022 *(chunk+5)=red;
13023 *(chunk+6)=0;
13024 *(chunk+7)=green;
13025 *(chunk+8)=0;
13026 *(chunk+9)=blue;
13027 (void) WriteBlob(image,(size_t) num_bytes,chunk);
13028 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13029 }
13030
13031 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13032 {
13033 /*
13034 Write JNG sRGB chunk
13035 */
13036 (void) WriteBlobMSBULong(image,1L);
13037 PNGType(chunk,mng_sRGB);
13038 LogPNGChunk(logging,mng_sRGB,1L);
13039
13040 if (image->rendering_intent != UndefinedIntent)
13041 chunk[4]=(unsigned char)
13042 Magick_RenderingIntent_to_PNG_RenderingIntent(
13043 (image->rendering_intent));
13044
13045 else
13046 chunk[4]=(unsigned char)
13047 Magick_RenderingIntent_to_PNG_RenderingIntent(
13048 (PerceptualIntent));
13049
13050 (void) WriteBlob(image,5,chunk);
13051 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13052 }
13053 else
13054 {
13055 if (image->gamma != 0.0)
13056 {
13057 /*
13058 Write JNG gAMA chunk
13059 */
13060 (void) WriteBlobMSBULong(image,4L);
13061 PNGType(chunk,mng_gAMA);
13062 LogPNGChunk(logging,mng_gAMA,4L);
13063 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13064 (void) WriteBlob(image,8,chunk);
13065 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13066 }
13067
13068 if ((mng_info->equal_chrms == MagickFalse) &&
13069 (image->chromaticity.red_primary.x != 0.0))
13070 {
13071 PrimaryInfo
13072 primary;
13073
13074 /*
13075 Write JNG cHRM chunk
13076 */
13077 (void) WriteBlobMSBULong(image,32L);
13078 PNGType(chunk,mng_cHRM);
13079 LogPNGChunk(logging,mng_cHRM,32L);
13080 primary=image->chromaticity.white_point;
13081 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13082 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13083 primary=image->chromaticity.red_primary;
13084 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13085 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13086 primary=image->chromaticity.green_primary;
13087 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13088 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13089 primary=image->chromaticity.blue_primary;
13090 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13091 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13092 (void) WriteBlob(image,36,chunk);
13093 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13094 }
13095 }
13096
13097 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13098 {
13099 /*
13100 Write JNG pHYs chunk
13101 */
13102 (void) WriteBlobMSBULong(image,9L);
13103 PNGType(chunk,mng_pHYs);
13104 LogPNGChunk(logging,mng_pHYs,9L);
13105 if (image->units == PixelsPerInchResolution)
13106 {
13107 PNGLong(chunk+4,(png_uint_32)
13108 (image->resolution.x*100.0/2.54+0.5));
13109
13110 PNGLong(chunk+8,(png_uint_32)
13111 (image->resolution.y*100.0/2.54+0.5));
13112
13113 chunk[12]=1;
13114 }
13115
13116 else
13117 {
13118 if (image->units == PixelsPerCentimeterResolution)
13119 {
13120 PNGLong(chunk+4,(png_uint_32)
13121 (image->resolution.x*100.0+0.5));
13122
13123 PNGLong(chunk+8,(png_uint_32)
13124 (image->resolution.y*100.0+0.5));
13125
13126 chunk[12]=1;
13127 }
13128
13129 else
13130 {
13131 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13132 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13133 chunk[12]=0;
13134 }
13135 }
13136 (void) WriteBlob(image,13,chunk);
13137 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13138 }
13139
13140 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13141 {
13142 /*
13143 Write JNG oFFs chunk
13144 */
13145 (void) WriteBlobMSBULong(image,9L);
13146 PNGType(chunk,mng_oFFs);
13147 LogPNGChunk(logging,mng_oFFs,9L);
13148 PNGsLong(chunk+4,(ssize_t) (image->page.x));
13149 PNGsLong(chunk+8,(ssize_t) (image->page.y));
13150 chunk[12]=0;
13151 (void) WriteBlob(image,13,chunk);
13152 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13153 }
13154
13155 if (transparent != 0)
13156 {
13157 if (jng_alpha_compression_method==0)
13158 {
13159 register ssize_t
13160 i;
13161
13162 size_t
13163 len;
13164
13165 /* Write IDAT chunk header */
13166 if (logging != MagickFalse)
13167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13168 " Write IDAT chunks from blob, length=%.20g.",(double)
13169 length);
13170
13171 /* Copy IDAT chunks */
13172 len=0;
13173 p=blob+8;
13174 for (i=8; i<(ssize_t) length; i+=len+12)
13175 {
13176 len=(((unsigned int) *(p ) & 0xff) << 24) +
13177 (((unsigned int) *(p + 1) & 0xff) << 16) +
13178 (((unsigned int) *(p + 2) & 0xff) << 8) +
13179 (((unsigned int) *(p + 3) & 0xff) ) ;
13180 p+=4;
13181
13182 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13183 {
13184 /* Found an IDAT chunk. */
13185 (void) WriteBlobMSBULong(image,len);
13186 LogPNGChunk(logging,mng_IDAT,len);
13187 (void) WriteBlob(image,len+4,p);
13188 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13189 }
13190
13191 else
13192 {
13193 if (logging != MagickFalse)
13194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13195 " Skipping %c%c%c%c chunk, length=%.20g.",
13196 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13197 }
13198 p+=(8+len);
13199 }
13200 }
13201 else if (length != 0)
13202 {
13203 /* Write JDAA chunk header */
13204 if (logging != MagickFalse)
13205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13206 " Write JDAA chunk, length=%.20g.",(double) length);
13207 (void) WriteBlobMSBULong(image,(size_t) length);
13208 PNGType(chunk,mng_JDAA);
13209 LogPNGChunk(logging,mng_JDAA,length);
13210 /* Write JDAT chunk(s) data */
13211 (void) WriteBlob(image,4,chunk);
13212 (void) WriteBlob(image,length,blob);
13213 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13214 (uInt) length));
13215 }
13216 blob=(unsigned char *) RelinquishMagickMemory(blob);
13217 }
13218
13219 /* Encode image as a JPEG blob */
13220 if (logging != MagickFalse)
13221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13222 " Creating jpeg_image_info.");
13223 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13224 if (jpeg_image_info == (ImageInfo *) NULL)
13225 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13226
13227 if (logging != MagickFalse)
13228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229 " Creating jpeg_image.");
13230
13231 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13232 if (jpeg_image == (Image *) NULL)
13233 {
13234 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13235 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13236 }
13237 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13238
13239 (void) AcquireUniqueFilename(jpeg_image->filename);
13240 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13241 jpeg_image->filename);
13242
13243 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13244 exception);
13245
13246 if (logging != MagickFalse)
13247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13248 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13249 (double) jpeg_image->rows);
13250
13251 if (status == MagickFalse)
13252 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13253
13254 if (jng_color_type == 8 || jng_color_type == 12)
13255 jpeg_image_info->type=GrayscaleType;
13256
13257 jpeg_image_info->quality=jng_quality;
13258 jpeg_image->quality=jng_quality;
13259 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13260 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13261
13262 if (logging != MagickFalse)
13263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13264 " Creating blob.");
13265
13266 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13267 exception);
13268
13269 if (logging != MagickFalse)
13270 {
13271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13272 " Successfully read jpeg_image into a blob, length=%.20g.",
13273 (double) length);
13274
13275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13276 " Write JDAT chunk, length=%.20g.",(double) length);
13277 }
13278
13279 /* Write JDAT chunk(s) */
13280 (void) WriteBlobMSBULong(image,(size_t) length);
13281 PNGType(chunk,mng_JDAT);
13282 LogPNGChunk(logging,mng_JDAT,length);
13283 (void) WriteBlob(image,4,chunk);
13284 (void) WriteBlob(image,length,blob);
13285 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13286
13287 jpeg_image=DestroyImage(jpeg_image);
13288 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13289 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13290 blob=(unsigned char *) RelinquishMagickMemory(blob);
13291
13292 /* Write IEND chunk */
13293 (void) WriteBlobMSBULong(image,0L);
13294 PNGType(chunk,mng_IEND);
13295 LogPNGChunk(logging,mng_IEND,0);
13296 (void) WriteBlob(image,4,chunk);
13297 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13298
13299 if (logging != MagickFalse)
13300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13301 " exit WriteOneJNGImage()");
13302
13303 return(status);
13304 }
13305
13306 /*
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 % %
13309 % %
13310 % %
13311 % W r i t e J N G I m a g e %
13312 % %
13313 % %
13314 % %
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13316 %
13317 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13318 %
13319 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13320 %
13321 % The format of the WriteJNGImage method is:
13322 %
13323 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13324 % Image *image,ExceptionInfo *exception)
13325 %
13326 % A description of each parameter follows:
13327 %
13328 % o image_info: the image info.
13329 %
13330 % o image: The image.
13331 %
13332 % o exception: return any errors or warnings in this structure.
13333 %
13334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13335 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13336 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13337 Image *image, ExceptionInfo *exception)
13338 {
13339 MagickBooleanType
13340 logging,
13341 status;
13342
13343 MngInfo
13344 *mng_info;
13345
13346 /*
13347 Open image file.
13348 */
13349 assert(image_info != (const ImageInfo *) NULL);
13350 assert(image_info->signature == MagickCoreSignature);
13351 assert(image != (Image *) NULL);
13352 assert(image->signature == MagickCoreSignature);
13353 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13354 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13355 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13356 if (status == MagickFalse)
13357 return(status);
13358 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13359 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13360
13361 /*
13362 Allocate a MngInfo structure.
13363 */
13364 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13365 if (mng_info == (MngInfo *) NULL)
13366 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13367 /*
13368 Initialize members of the MngInfo structure.
13369 */
13370 (void) memset(mng_info,0,sizeof(MngInfo));
13371 mng_info->image=image;
13372
13373 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13374
13375 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13376 mng_info=MngInfoFreeStruct(mng_info);
13377 (void) CloseBlob(image);
13378
13379 (void) CatchImageException(image);
13380 if (logging != MagickFalse)
13381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13382 return(status);
13383 }
13384 #endif
13385
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13386 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13387 Image *image, ExceptionInfo *exception)
13388 {
13389 Image
13390 *next_image;
13391
13392 MagickBooleanType
13393 status;
13394
13395 volatile MagickBooleanType
13396 logging;
13397
13398 MngInfo
13399 *mng_info;
13400
13401 int
13402 image_count,
13403 need_iterations,
13404 need_matte;
13405
13406 volatile int
13407 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13408 defined(PNG_MNG_FEATURES_SUPPORTED)
13409 need_local_plte,
13410 #endif
13411 all_images_are_gray,
13412 need_defi,
13413 use_global_plte;
13414
13415 register ssize_t
13416 i;
13417
13418 unsigned char
13419 chunk[800];
13420
13421 volatile unsigned int
13422 write_jng,
13423 write_mng;
13424
13425 volatile size_t
13426 scene;
13427
13428 size_t
13429 final_delay=0,
13430 imageListLength,
13431 initial_delay;
13432
13433 #if (PNG_LIBPNG_VER < 10200)
13434 if (image_info->verbose)
13435 printf("Your PNG library (libpng-%s) is rather old.\n",
13436 PNG_LIBPNG_VER_STRING);
13437 #endif
13438
13439 /*
13440 Open image file.
13441 */
13442 assert(image_info != (const ImageInfo *) NULL);
13443 assert(image_info->signature == MagickCoreSignature);
13444 assert(image != (Image *) NULL);
13445 assert(image->signature == MagickCoreSignature);
13446 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13447 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13448 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13449 if (status == MagickFalse)
13450 return(status);
13451
13452 /*
13453 Allocate a MngInfo structure.
13454 */
13455 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13456 if (mng_info == (MngInfo *) NULL)
13457 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13458 /*
13459 Initialize members of the MngInfo structure.
13460 */
13461 (void) memset(mng_info,0,sizeof(MngInfo));
13462 mng_info->image=image;
13463 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13464
13465 /*
13466 * See if user has requested a specific PNG subformat to be used
13467 * for all of the PNGs in the MNG being written, e.g.,
13468 *
13469 * convert *.png png8:animation.mng
13470 *
13471 * To do: check -define png:bit_depth and png:color_type as well,
13472 * or perhaps use mng:bit_depth and mng:color_type instead for
13473 * global settings.
13474 */
13475
13476 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13477 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13478 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13479
13480 write_jng=MagickFalse;
13481 if (image_info->compression == JPEGCompression)
13482 write_jng=MagickTrue;
13483
13484 mng_info->adjoin=image_info->adjoin &&
13485 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13486
13487 if (logging != MagickFalse)
13488 {
13489 /* Log some info about the input */
13490 Image
13491 *p;
13492
13493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13494 " Checking input image(s)\n"
13495 " Image_info depth: %.20g, Type: %d",
13496 (double) image_info->depth, image_info->type);
13497
13498 scene=0;
13499 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13500 {
13501
13502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13503 " Scene: %.20g\n, Image depth: %.20g",
13504 (double) scene++, (double) p->depth);
13505
13506 if (p->alpha_trait != UndefinedPixelTrait)
13507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13508 " Matte: True");
13509
13510 else
13511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13512 " Matte: False");
13513
13514 if (p->storage_class == PseudoClass)
13515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13516 " Storage class: PseudoClass");
13517
13518 else
13519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13520 " Storage class: DirectClass");
13521
13522 if (p->colors)
13523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13524 " Number of colors: %.20g",(double) p->colors);
13525
13526 else
13527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13528 " Number of colors: unspecified");
13529
13530 if (mng_info->adjoin == MagickFalse)
13531 break;
13532 }
13533 }
13534
13535 use_global_plte=MagickFalse;
13536 all_images_are_gray=MagickFalse;
13537 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13538 need_local_plte=MagickTrue;
13539 #endif
13540 need_defi=MagickFalse;
13541 need_matte=MagickFalse;
13542 mng_info->framing_mode=1;
13543 mng_info->old_framing_mode=1;
13544
13545 if (write_mng)
13546 if (image_info->page != (char *) NULL)
13547 {
13548 /*
13549 Determine image bounding box.
13550 */
13551 SetGeometry(image,&mng_info->page);
13552 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13553 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13554 }
13555 if (write_mng)
13556 {
13557 unsigned int
13558 need_geom;
13559
13560 unsigned short
13561 red,
13562 green,
13563 blue;
13564
13565 const char *
13566 option;
13567
13568 mng_info->page=image->page;
13569 need_geom=MagickTrue;
13570 if (mng_info->page.width || mng_info->page.height)
13571 need_geom=MagickFalse;
13572 /*
13573 Check all the scenes.
13574 */
13575 initial_delay=image->delay;
13576 need_iterations=MagickFalse;
13577 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13578 mng_info->equal_physs=MagickTrue,
13579 mng_info->equal_gammas=MagickTrue;
13580 mng_info->equal_srgbs=MagickTrue;
13581 mng_info->equal_backgrounds=MagickTrue;
13582 image_count=0;
13583 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13584 defined(PNG_MNG_FEATURES_SUPPORTED)
13585 all_images_are_gray=MagickTrue;
13586 mng_info->equal_palettes=MagickFalse;
13587 need_local_plte=MagickFalse;
13588 #endif
13589 for (next_image=image; next_image != (Image *) NULL; )
13590 {
13591 if (need_geom)
13592 {
13593 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13594 mng_info->page.width=next_image->columns+next_image->page.x;
13595
13596 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13597 mng_info->page.height=next_image->rows+next_image->page.y;
13598 }
13599
13600 if (next_image->page.x || next_image->page.y)
13601 need_defi=MagickTrue;
13602
13603 if (next_image->alpha_trait != UndefinedPixelTrait)
13604 need_matte=MagickTrue;
13605
13606 if ((int) next_image->dispose >= BackgroundDispose)
13607 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13608 next_image->page.x || next_image->page.y ||
13609 ((next_image->columns < mng_info->page.width) &&
13610 (next_image->rows < mng_info->page.height)))
13611 mng_info->need_fram=MagickTrue;
13612
13613 if (next_image->iterations)
13614 need_iterations=MagickTrue;
13615
13616 final_delay=next_image->delay;
13617
13618 if (final_delay != initial_delay || final_delay > 1UL*
13619 next_image->ticks_per_second)
13620 mng_info->need_fram=1;
13621
13622 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13623 defined(PNG_MNG_FEATURES_SUPPORTED)
13624 /*
13625 check for global palette possibility.
13626 */
13627 if (image->alpha_trait != UndefinedPixelTrait)
13628 need_local_plte=MagickTrue;
13629
13630 if (need_local_plte == 0)
13631 {
13632 if (SetImageGray(image,exception) == MagickFalse)
13633 all_images_are_gray=MagickFalse;
13634 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13635 if (use_global_plte == 0)
13636 use_global_plte=mng_info->equal_palettes;
13637 need_local_plte=!mng_info->equal_palettes;
13638 }
13639 #endif
13640 if (GetNextImageInList(next_image) != (Image *) NULL)
13641 {
13642 if (next_image->background_color.red !=
13643 next_image->next->background_color.red ||
13644 next_image->background_color.green !=
13645 next_image->next->background_color.green ||
13646 next_image->background_color.blue !=
13647 next_image->next->background_color.blue)
13648 mng_info->equal_backgrounds=MagickFalse;
13649
13650 if (next_image->gamma != next_image->next->gamma)
13651 mng_info->equal_gammas=MagickFalse;
13652
13653 if (next_image->rendering_intent !=
13654 next_image->next->rendering_intent)
13655 mng_info->equal_srgbs=MagickFalse;
13656
13657 if ((next_image->units != next_image->next->units) ||
13658 (next_image->resolution.x != next_image->next->resolution.x) ||
13659 (next_image->resolution.y != next_image->next->resolution.y))
13660 mng_info->equal_physs=MagickFalse;
13661
13662 if (mng_info->equal_chrms)
13663 {
13664 if (next_image->chromaticity.red_primary.x !=
13665 next_image->next->chromaticity.red_primary.x ||
13666 next_image->chromaticity.red_primary.y !=
13667 next_image->next->chromaticity.red_primary.y ||
13668 next_image->chromaticity.green_primary.x !=
13669 next_image->next->chromaticity.green_primary.x ||
13670 next_image->chromaticity.green_primary.y !=
13671 next_image->next->chromaticity.green_primary.y ||
13672 next_image->chromaticity.blue_primary.x !=
13673 next_image->next->chromaticity.blue_primary.x ||
13674 next_image->chromaticity.blue_primary.y !=
13675 next_image->next->chromaticity.blue_primary.y ||
13676 next_image->chromaticity.white_point.x !=
13677 next_image->next->chromaticity.white_point.x ||
13678 next_image->chromaticity.white_point.y !=
13679 next_image->next->chromaticity.white_point.y)
13680 mng_info->equal_chrms=MagickFalse;
13681 }
13682 }
13683 image_count++;
13684 next_image=GetNextImageInList(next_image);
13685 }
13686 if (image_count < 2)
13687 {
13688 mng_info->equal_backgrounds=MagickFalse;
13689 mng_info->equal_chrms=MagickFalse;
13690 mng_info->equal_gammas=MagickFalse;
13691 mng_info->equal_srgbs=MagickFalse;
13692 mng_info->equal_physs=MagickFalse;
13693 use_global_plte=MagickFalse;
13694 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13695 need_local_plte=MagickTrue;
13696 #endif
13697 need_iterations=MagickFalse;
13698 }
13699
13700 if (mng_info->need_fram == MagickFalse)
13701 {
13702 /*
13703 Only certain framing rates 100/n are exactly representable without
13704 the FRAM chunk but we'll allow some slop in VLC files
13705 */
13706 if (final_delay == 0)
13707 {
13708 if (need_iterations != MagickFalse)
13709 {
13710 /*
13711 It's probably a GIF with loop; don't run it *too* fast.
13712 */
13713 if (mng_info->adjoin)
13714 {
13715 final_delay=10;
13716 (void) ThrowMagickException(exception,GetMagickModule(),
13717 CoderWarning,
13718 "input has zero delay between all frames; assuming",
13719 " 10 cs `%s'","");
13720 }
13721 }
13722 else
13723 mng_info->ticks_per_second=0;
13724 }
13725 if (final_delay != 0)
13726 mng_info->ticks_per_second=(png_uint_32)
13727 (image->ticks_per_second/final_delay);
13728 if (final_delay > 50)
13729 mng_info->ticks_per_second=2;
13730
13731 if (final_delay > 75)
13732 mng_info->ticks_per_second=1;
13733
13734 if (final_delay > 125)
13735 mng_info->need_fram=MagickTrue;
13736
13737 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13738 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13739 (final_delay != 25) && (final_delay != 50) &&
13740 (final_delay != (size_t) image->ticks_per_second))
13741 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13742 }
13743
13744 if (mng_info->need_fram != MagickFalse)
13745 mng_info->ticks_per_second=image->ticks_per_second;
13746 /*
13747 If pseudocolor, we should also check to see if all the
13748 palettes are identical and write a global PLTE if they are.
13749 ../glennrp Feb 99.
13750 */
13751 /*
13752 Write the MNG version 1.0 signature and MHDR chunk.
13753 */
13754 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13755 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13756 PNGType(chunk,mng_MHDR);
13757 LogPNGChunk(logging,mng_MHDR,28L);
13758 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13759 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13760 PNGLong(chunk+12,mng_info->ticks_per_second);
13761 PNGLong(chunk+16,0L); /* layer count=unknown */
13762 PNGLong(chunk+20,0L); /* frame count=unknown */
13763 PNGLong(chunk+24,0L); /* play time=unknown */
13764 if (write_jng)
13765 {
13766 if (need_matte)
13767 {
13768 if (need_defi || mng_info->need_fram || use_global_plte)
13769 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13770
13771 else
13772 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13773 }
13774
13775 else
13776 {
13777 if (need_defi || mng_info->need_fram || use_global_plte)
13778 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13779
13780 else
13781 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13782 }
13783 }
13784
13785 else
13786 {
13787 if (need_matte)
13788 {
13789 if (need_defi || mng_info->need_fram || use_global_plte)
13790 PNGLong(chunk+28,11L); /* simplicity=LC */
13791
13792 else
13793 PNGLong(chunk+28,9L); /* simplicity=VLC */
13794 }
13795
13796 else
13797 {
13798 if (need_defi || mng_info->need_fram || use_global_plte)
13799 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13800
13801 else
13802 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13803 }
13804 }
13805 (void) WriteBlob(image,32,chunk);
13806 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13807 option=GetImageOption(image_info,"mng:need-cacheoff");
13808 if (option != (const char *) NULL)
13809 {
13810 size_t
13811 length;
13812 /*
13813 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13814 */
13815 PNGType(chunk,mng_nEED);
13816 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13817 (void) WriteBlobMSBULong(image,(size_t) length);
13818 LogPNGChunk(logging,mng_nEED,(size_t) length);
13819 length+=4;
13820 (void) WriteBlob(image,length,chunk);
13821 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13822 }
13823 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13824 (GetNextImageInList(image) != (Image *) NULL) &&
13825 (image->iterations != 1))
13826 {
13827 /*
13828 Write MNG TERM chunk
13829 */
13830 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13831 PNGType(chunk,mng_TERM);
13832 LogPNGChunk(logging,mng_TERM,10L);
13833 chunk[4]=3; /* repeat animation */
13834 chunk[5]=0; /* show last frame when done */
13835 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13836 final_delay/MagickMax(image->ticks_per_second,1)));
13837
13838 if (image->iterations == 0)
13839 PNGLong(chunk+10,PNG_UINT_31_MAX);
13840
13841 else
13842 PNGLong(chunk+10,(png_uint_32) image->iterations);
13843
13844 if (logging != MagickFalse)
13845 {
13846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13847 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13848 final_delay/MagickMax(image->ticks_per_second,1)));
13849
13850 if (image->iterations == 0)
13851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13852 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13853
13854 else
13855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13856 " Image iterations: %.20g",(double) image->iterations);
13857 }
13858 (void) WriteBlob(image,14,chunk);
13859 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13860 }
13861 /*
13862 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13863 */
13864 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13865 mng_info->equal_srgbs)
13866 {
13867 /*
13868 Write MNG sRGB chunk
13869 */
13870 (void) WriteBlobMSBULong(image,1L);
13871 PNGType(chunk,mng_sRGB);
13872 LogPNGChunk(logging,mng_sRGB,1L);
13873
13874 if (image->rendering_intent != UndefinedIntent)
13875 chunk[4]=(unsigned char)
13876 Magick_RenderingIntent_to_PNG_RenderingIntent(
13877 (image->rendering_intent));
13878
13879 else
13880 chunk[4]=(unsigned char)
13881 Magick_RenderingIntent_to_PNG_RenderingIntent(
13882 (PerceptualIntent));
13883
13884 (void) WriteBlob(image,5,chunk);
13885 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13886 mng_info->have_write_global_srgb=MagickTrue;
13887 }
13888
13889 else
13890 {
13891 if (image->gamma && mng_info->equal_gammas)
13892 {
13893 /*
13894 Write MNG gAMA chunk
13895 */
13896 (void) WriteBlobMSBULong(image,4L);
13897 PNGType(chunk,mng_gAMA);
13898 LogPNGChunk(logging,mng_gAMA,4L);
13899 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13900 (void) WriteBlob(image,8,chunk);
13901 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13902 mng_info->have_write_global_gama=MagickTrue;
13903 }
13904 if (mng_info->equal_chrms)
13905 {
13906 PrimaryInfo
13907 primary;
13908
13909 /*
13910 Write MNG cHRM chunk
13911 */
13912 (void) WriteBlobMSBULong(image,32L);
13913 PNGType(chunk,mng_cHRM);
13914 LogPNGChunk(logging,mng_cHRM,32L);
13915 primary=image->chromaticity.white_point;
13916 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13917 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13918 primary=image->chromaticity.red_primary;
13919 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13920 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13921 primary=image->chromaticity.green_primary;
13922 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13923 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13924 primary=image->chromaticity.blue_primary;
13925 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13926 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13927 (void) WriteBlob(image,36,chunk);
13928 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13929 mng_info->have_write_global_chrm=MagickTrue;
13930 }
13931 }
13932 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13933 {
13934 /*
13935 Write MNG pHYs chunk
13936 */
13937 (void) WriteBlobMSBULong(image,9L);
13938 PNGType(chunk,mng_pHYs);
13939 LogPNGChunk(logging,mng_pHYs,9L);
13940
13941 if (image->units == PixelsPerInchResolution)
13942 {
13943 PNGLong(chunk+4,(png_uint_32)
13944 (image->resolution.x*100.0/2.54+0.5));
13945
13946 PNGLong(chunk+8,(png_uint_32)
13947 (image->resolution.y*100.0/2.54+0.5));
13948
13949 chunk[12]=1;
13950 }
13951
13952 else
13953 {
13954 if (image->units == PixelsPerCentimeterResolution)
13955 {
13956 PNGLong(chunk+4,(png_uint_32)
13957 (image->resolution.x*100.0+0.5));
13958
13959 PNGLong(chunk+8,(png_uint_32)
13960 (image->resolution.y*100.0+0.5));
13961
13962 chunk[12]=1;
13963 }
13964
13965 else
13966 {
13967 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13968 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13969 chunk[12]=0;
13970 }
13971 }
13972 (void) WriteBlob(image,13,chunk);
13973 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13974 }
13975 /*
13976 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13977 or does not cover the entire frame.
13978 */
13979 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13980 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13981 (image->page.width+image->page.x < mng_info->page.width))
13982 || (image->page.height && (image->page.height+image->page.y
13983 < mng_info->page.height))))
13984 {
13985 (void) WriteBlobMSBULong(image,6L);
13986 PNGType(chunk,mng_BACK);
13987 LogPNGChunk(logging,mng_BACK,6L);
13988 red=ScaleQuantumToShort(image->background_color.red);
13989 green=ScaleQuantumToShort(image->background_color.green);
13990 blue=ScaleQuantumToShort(image->background_color.blue);
13991 PNGShort(chunk+4,red);
13992 PNGShort(chunk+6,green);
13993 PNGShort(chunk+8,blue);
13994 (void) WriteBlob(image,10,chunk);
13995 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13996 if (mng_info->equal_backgrounds)
13997 {
13998 (void) WriteBlobMSBULong(image,6L);
13999 PNGType(chunk,mng_bKGD);
14000 LogPNGChunk(logging,mng_bKGD,6L);
14001 (void) WriteBlob(image,10,chunk);
14002 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14003 }
14004 }
14005
14006 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14007 if ((need_local_plte == MagickFalse) &&
14008 (image->storage_class == PseudoClass) &&
14009 (all_images_are_gray == MagickFalse))
14010 {
14011 size_t
14012 data_length;
14013
14014 /*
14015 Write MNG PLTE chunk
14016 */
14017 data_length=3*image->colors;
14018 (void) WriteBlobMSBULong(image,data_length);
14019 PNGType(chunk,mng_PLTE);
14020 LogPNGChunk(logging,mng_PLTE,data_length);
14021
14022 for (i=0; i < (ssize_t) image->colors; i++)
14023 {
14024 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14025 image->colormap[i].red) & 0xff);
14026 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14027 image->colormap[i].green) & 0xff);
14028 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14029 image->colormap[i].blue) & 0xff);
14030 }
14031
14032 (void) WriteBlob(image,data_length+4,chunk);
14033 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14034 mng_info->have_write_global_plte=MagickTrue;
14035 }
14036 #endif
14037 }
14038 scene=0;
14039 mng_info->delay=0;
14040 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14041 defined(PNG_MNG_FEATURES_SUPPORTED)
14042 mng_info->equal_palettes=MagickFalse;
14043 #endif
14044 imageListLength=GetImageListLength(image);
14045 do
14046 {
14047 if (mng_info->adjoin)
14048 {
14049 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14050 defined(PNG_MNG_FEATURES_SUPPORTED)
14051 /*
14052 If we aren't using a global palette for the entire MNG, check to
14053 see if we can use one for two or more consecutive images.
14054 */
14055 if (need_local_plte && use_global_plte && !all_images_are_gray)
14056 {
14057 if (mng_info->IsPalette)
14058 {
14059 /*
14060 When equal_palettes is true, this image has the same palette
14061 as the previous PseudoClass image
14062 */
14063 mng_info->have_write_global_plte=mng_info->equal_palettes;
14064 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14065 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14066 {
14067 /*
14068 Write MNG PLTE chunk
14069 */
14070 size_t
14071 data_length;
14072
14073 data_length=3*image->colors;
14074 (void) WriteBlobMSBULong(image,data_length);
14075 PNGType(chunk,mng_PLTE);
14076 LogPNGChunk(logging,mng_PLTE,data_length);
14077
14078 for (i=0; i < (ssize_t) image->colors; i++)
14079 {
14080 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14081 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14082 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14083 }
14084
14085 (void) WriteBlob(image,data_length+4,chunk);
14086 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14087 (uInt) (data_length+4)));
14088 mng_info->have_write_global_plte=MagickTrue;
14089 }
14090 }
14091 else
14092 mng_info->have_write_global_plte=MagickFalse;
14093 }
14094 #endif
14095 if (need_defi)
14096 {
14097 ssize_t
14098 previous_x,
14099 previous_y;
14100
14101 if (scene != 0)
14102 {
14103 previous_x=mng_info->page.x;
14104 previous_y=mng_info->page.y;
14105 }
14106 else
14107 {
14108 previous_x=0;
14109 previous_y=0;
14110 }
14111 mng_info->page=image->page;
14112 if ((mng_info->page.x != previous_x) ||
14113 (mng_info->page.y != previous_y))
14114 {
14115 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
14116 PNGType(chunk,mng_DEFI);
14117 LogPNGChunk(logging,mng_DEFI,12L);
14118 chunk[4]=0; /* object 0 MSB */
14119 chunk[5]=0; /* object 0 LSB */
14120 chunk[6]=0; /* visible */
14121 chunk[7]=0; /* abstract */
14122 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14123 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14124 (void) WriteBlob(image,16,chunk);
14125 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14126 }
14127 }
14128 }
14129
14130 mng_info->write_mng=write_mng;
14131
14132 if ((int) image->dispose >= 3)
14133 mng_info->framing_mode=3;
14134
14135 if (mng_info->need_fram && mng_info->adjoin &&
14136 ((image->delay != mng_info->delay) ||
14137 (mng_info->framing_mode != mng_info->old_framing_mode)))
14138 {
14139 if (image->delay == mng_info->delay)
14140 {
14141 /*
14142 Write a MNG FRAM chunk with the new framing mode.
14143 */
14144 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
14145 PNGType(chunk,mng_FRAM);
14146 LogPNGChunk(logging,mng_FRAM,1L);
14147 chunk[4]=(unsigned char) mng_info->framing_mode;
14148 (void) WriteBlob(image,5,chunk);
14149 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14150 }
14151 else
14152 {
14153 /*
14154 Write a MNG FRAM chunk with the delay.
14155 */
14156 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
14157 PNGType(chunk,mng_FRAM);
14158 LogPNGChunk(logging,mng_FRAM,10L);
14159 chunk[4]=(unsigned char) mng_info->framing_mode;
14160 chunk[5]=0; /* frame name separator (no name) */
14161 chunk[6]=2; /* flag for changing default delay */
14162 chunk[7]=0; /* flag for changing frame timeout */
14163 chunk[8]=0; /* flag for changing frame clipping */
14164 chunk[9]=0; /* flag for changing frame sync_id */
14165 PNGLong(chunk+10,(png_uint_32)
14166 ((mng_info->ticks_per_second*
14167 image->delay)/MagickMax(image->ticks_per_second,1)));
14168 (void) WriteBlob(image,14,chunk);
14169 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14170 mng_info->delay=(png_uint_32) image->delay;
14171 }
14172 mng_info->old_framing_mode=mng_info->framing_mode;
14173 }
14174
14175 #if defined(JNG_SUPPORTED)
14176 if (image_info->compression == JPEGCompression)
14177 {
14178 ImageInfo
14179 *write_info;
14180
14181 if (logging != MagickFalse)
14182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14183 " Writing JNG object.");
14184 /* To do: specify the desired alpha compression method. */
14185 write_info=CloneImageInfo(image_info);
14186 write_info->compression=UndefinedCompression;
14187 status=WriteOneJNGImage(mng_info,write_info,image,exception);
14188 write_info=DestroyImageInfo(write_info);
14189 }
14190 else
14191 #endif
14192 {
14193 if (logging != MagickFalse)
14194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14195 " Writing PNG object.");
14196
14197 mng_info->need_blob = MagickFalse;
14198 mng_info->ping_preserve_colormap = MagickFalse;
14199
14200 /* We don't want any ancillary chunks written */
14201 mng_info->ping_exclude_bKGD=MagickTrue;
14202 mng_info->ping_exclude_caNv=MagickTrue;
14203 mng_info->ping_exclude_cHRM=MagickTrue;
14204 mng_info->ping_exclude_date=MagickTrue;
14205 mng_info->ping_exclude_EXIF=MagickTrue;
14206 mng_info->ping_exclude_gAMA=MagickTrue;
14207 mng_info->ping_exclude_iCCP=MagickTrue;
14208 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14209 mng_info->ping_exclude_oFFs=MagickTrue;
14210 mng_info->ping_exclude_pHYs=MagickTrue;
14211 mng_info->ping_exclude_sRGB=MagickTrue;
14212 mng_info->ping_exclude_tEXt=MagickTrue;
14213 mng_info->ping_exclude_tRNS=MagickTrue;
14214 mng_info->ping_exclude_zCCP=MagickTrue;
14215 mng_info->ping_exclude_zTXt=MagickTrue;
14216
14217 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14218 }
14219
14220 if (status == MagickFalse)
14221 {
14222 mng_info=MngInfoFreeStruct(mng_info);
14223 (void) CloseBlob(image);
14224 return(MagickFalse);
14225 }
14226 (void) CatchImageException(image);
14227 if (GetNextImageInList(image) == (Image *) NULL)
14228 break;
14229 image=SyncNextImageInList(image);
14230 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14231
14232 if (status == MagickFalse)
14233 break;
14234
14235 } while (mng_info->adjoin);
14236
14237 if (write_mng)
14238 {
14239 while (GetPreviousImageInList(image) != (Image *) NULL)
14240 image=GetPreviousImageInList(image);
14241 /*
14242 Write the MEND chunk.
14243 */
14244 (void) WriteBlobMSBULong(image,0x00000000L);
14245 PNGType(chunk,mng_MEND);
14246 LogPNGChunk(logging,mng_MEND,0L);
14247 (void) WriteBlob(image,4,chunk);
14248 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14249 }
14250 /*
14251 Relinquish resources.
14252 */
14253 (void) CloseBlob(image);
14254 mng_info=MngInfoFreeStruct(mng_info);
14255
14256 if (logging != MagickFalse)
14257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14258
14259 return(MagickTrue);
14260 }
14261 #else /* PNG_LIBPNG_VER > 10011 */
14262
WritePNGImage(const ImageInfo * image_info,Image * image)14263 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14264 Image *image)
14265 {
14266 (void) image;
14267 printf("Your PNG library is too old: You have libpng-%s\n",
14268 PNG_LIBPNG_VER_STRING);
14269
14270 ThrowBinaryException(CoderError,"PNG library is too old",
14271 image_info->filename);
14272 }
14273
WriteMNGImage(const ImageInfo * image_info,Image * image)14274 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14275 Image *image)
14276 {
14277 return(WritePNGImage(image_info,image));
14278 }
14279 #endif /* PNG_LIBPNG_VER > 10011 */
14280 #endif
14281