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-2016 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 % http://www.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
41 /*
42 Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
93 */
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111 Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116 Features under construction. Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
130 #endif
131
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135 png_uint_32 len;
136 png_uint_32 crc;
137 png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141 {
142 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143 { 3048, 0x3b8772b9UL, 0},
144
145 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146 { 3052, 0x427ebb21UL, 1},
147
148 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149 {60988, 0x306fd8aeUL, 0},
150
151 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152 {60960, 0xbbef7812UL, 0},
153
154 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155 { 3024, 0x5d5129ceUL, 1},
156
157 /* HP-Microsoft sRGB v2 perceptual */
158 { 3144, 0x182ea552UL, 0},
159
160 /* HP-Microsoft sRGB v2 media-relative */
161 { 3144, 0xf29e526dUL, 1},
162
163 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164 { 524, 0xd4938c39UL, 0},
165
166 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167 { 3212, 0x034af5a1UL, 0},
168
169 /* Not recognized */
170 { 0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174 * and PixelInfos all have the same image->depth, and for use
175 * in PNG8 quantization.
176 */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182 0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186 0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190 0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194 0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197 { \
198 LBR01PacketRed((pixelpacket)); \
199 LBR01PacketGreen((pixelpacket)); \
200 LBR01PacketBlue((pixelpacket)); \
201 }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204 { \
205 LBR01PacketRGB((pixelpacket)); \
206 LBR01PacketAlpha((pixelpacket)); \
207 }
208
209 #define LBR01PixelRed(pixel) \
210 (SetPixelRed(image, \
211 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212 0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215 (SetPixelGreen(image, \
216 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217 0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220 (SetPixelBlue(image, \
221 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222 0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225 (SetPixelAlpha(image, \
226 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227 0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230 { \
231 LBR01PixelRed((pixel)); \
232 LBR01PixelGreen((pixel)); \
233 LBR01PixelBlue((pixel)); \
234 }
235
236 #define LBR01PixelRGBA(pixel) \
237 { \
238 LBR01PixelRGB((pixel)); \
239 LBR01PixelAlpha((pixel)); \
240 }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245 { \
246 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247 (pixelpacket).red=ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249 }
250 #define LBR02PacketGreen(pixelpacket) \
251 { \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253 (pixelpacket).green=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255 }
256 #define LBR02PacketBlue(pixelpacket) \
257 { \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259 (pixelpacket).blue=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261 }
262 #define LBR02PacketAlpha(pixelpacket) \
263 { \
264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265 (pixelpacket).alpha=ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267 }
268
269 #define LBR02PacketRGB(pixelpacket) \
270 { \
271 LBR02PacketRed((pixelpacket)); \
272 LBR02PacketGreen((pixelpacket)); \
273 LBR02PacketBlue((pixelpacket)); \
274 }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277 { \
278 LBR02PacketRGB((pixelpacket)); \
279 LBR02PacketAlpha((pixelpacket)); \
280 }
281
282 #define LBR02PixelRed(pixel) \
283 { \
284 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285 & 0xc0; \
286 SetPixelRed(image, ScaleCharToQuantum( \
287 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288 (pixel)); \
289 }
290 #define LBR02PixelGreen(pixel) \
291 { \
292 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293 & 0xc0; \
294 SetPixelGreen(image, ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296 (pixel)); \
297 }
298 #define LBR02PixelBlue(pixel) \
299 { \
300 unsigned char lbr_bits= \
301 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302 SetPixelBlue(image, ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304 (pixel)); \
305 }
306 #define LBR02PixelAlpha(pixel) \
307 { \
308 unsigned char lbr_bits= \
309 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310 SetPixelAlpha(image, ScaleCharToQuantum( \
311 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312 (pixel) ); \
313 }
314
315 #define LBR02PixelRGB(pixel) \
316 { \
317 LBR02PixelRed((pixel)); \
318 LBR02PixelGreen((pixel)); \
319 LBR02PixelBlue((pixel)); \
320 }
321
322 #define LBR02PixelRGBA(pixel) \
323 { \
324 LBR02PixelRGB((pixel)); \
325 LBR02PixelAlpha((pixel)); \
326 }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329 PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332 { \
333 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334 (pixelpacket).red=ScaleCharToQuantum( \
335 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336 }
337 #define LBR03PacketGreen(pixelpacket) \
338 { \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340 (pixelpacket).green=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342 }
343 #define LBR03PacketBlue(pixelpacket) \
344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346 (pixelpacket).blue=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348 }
349
350 #define LBR03PacketRGB(pixelpacket) \
351 { \
352 LBR03PacketRed((pixelpacket)); \
353 LBR03PacketGreen((pixelpacket)); \
354 LBR03PacketBlue((pixelpacket)); \
355 }
356
357 #define LBR03PixelRed(pixel) \
358 { \
359 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360 & 0xe0; \
361 SetPixelRed(image, ScaleCharToQuantum( \
362 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363 }
364 #define LBR03Green(pixel) \
365 { \
366 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367 & 0xe0; \
368 SetPixelGreen(image, ScaleCharToQuantum( \
369 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370 }
371 #define LBR03Blue(pixel) \
372 { \
373 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374 & 0xe0; \
375 SetPixelBlue(image, ScaleCharToQuantum( \
376 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377 }
378
379 #define LBR03RGB(pixel) \
380 { \
381 LBR03PixelRed((pixel)); \
382 LBR03Green((pixel)); \
383 LBR03Blue((pixel)); \
384 }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389 { \
390 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392 }
393 #define LBR04PacketGreen(pixelpacket) \
394 { \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397 }
398 #define LBR04PacketBlue(pixelpacket) \
399 { \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402 }
403 #define LBR04PacketAlpha(pixelpacket) \
404 { \
405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407 }
408
409 #define LBR04PacketRGB(pixelpacket) \
410 { \
411 LBR04PacketRed((pixelpacket)); \
412 LBR04PacketGreen((pixelpacket)); \
413 LBR04PacketBlue((pixelpacket)); \
414 }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417 { \
418 LBR04PacketRGB((pixelpacket)); \
419 LBR04PacketAlpha((pixelpacket)); \
420 }
421
422 #define LBR04PixelRed(pixel) \
423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425 & 0xf0; \
426 SetPixelRed(image,\
427 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428 }
429 #define LBR04PixelGreen(pixel) \
430 { \
431 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432 & 0xf0; \
433 SetPixelGreen(image,\
434 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435 }
436 #define LBR04PixelBlue(pixel) \
437 { \
438 unsigned char lbr_bits= \
439 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440 SetPixelBlue(image,\
441 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442 }
443 #define LBR04PixelAlpha(pixel) \
444 { \
445 unsigned char lbr_bits= \
446 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447 SetPixelAlpha(image,\
448 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449 }
450
451 #define LBR04PixelRGB(pixel) \
452 { \
453 LBR04PixelRed((pixel)); \
454 LBR04PixelGreen((pixel)); \
455 LBR04PixelBlue((pixel)); \
456 }
457
458 #define LBR04PixelRGBA(pixel) \
459 { \
460 LBR04PixelRGB((pixel)); \
461 LBR04PixelAlpha((pixel)); \
462 }
463
464 /*
465 Establish thread safety.
466 setjmp/longjmp is claimed to be safe on these platforms:
467 setjmp/longjmp is alleged to be unsafe on these platforms:
468 */
469 #ifdef PNG_SETJMP_SUPPORTED
470 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471 # define IMPNG_SETJMP_NOT_THREAD_SAFE
472 # endif
473
474 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
475 static SemaphoreInfo
476 *ping_semaphore = (SemaphoreInfo *) NULL;
477 # endif
478 #endif
479
480 /*
481 This temporary until I set up malloc'ed object attributes array.
482 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
483 waste more memory.
484 */
485 #define MNG_MAX_OBJECTS 256
486
487 /*
488 If this not defined, spec is interpreted strictly. If it is
489 defined, an attempt will be made to recover from some errors,
490 including
491 o global PLTE too short
492 */
493 #undef MNG_LOOSE
494
495 /*
496 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
497 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
498 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
499 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502 will be enabled by default in libpng-1.2.0.
503 */
504 #ifdef PNG_MNG_FEATURES_SUPPORTED
505 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506 # define PNG_READ_EMPTY_PLTE_SUPPORTED
507 # endif
508 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
510 # endif
511 #endif
512
513 /*
514 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
515 This macro is only defined in libpng-1.0.3 and later.
516 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
517 */
518 #ifndef PNG_UINT_31_MAX
519 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
520 #endif
521
522 /*
523 Constant strings for known chunk types. If you need to add a chunk,
524 add a string holding the name here. To make the code more
525 portable, we use ASCII numbers like this, not characters.
526 */
527
528 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
529 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
530 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
531 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
532 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
533 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
534 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
535 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
536 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
537 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
538 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
539 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
540 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
541 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
542 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
543 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
544 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
545 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
546 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
547 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
548 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
549 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
550 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
551 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
552 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
553 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
554 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
555 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
556 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
557 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
558 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
559 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
560 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
561 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
562
563 #if defined(JNG_SUPPORTED)
564 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
565 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
566 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
567 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
568 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
569 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
570 #endif
571
572 #if 0
573 /* Other known chunks that are not yet supported by ImageMagick: */
574 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
575 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
576 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
577 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
578 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
579 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
580 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
581 #endif
582
583 typedef struct _MngBox
584 {
585 long
586 left,
587 right,
588 top,
589 bottom;
590 } MngBox;
591
592 typedef struct _MngPair
593 {
594 volatile long
595 a,
596 b;
597 } MngPair;
598
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
601 {
602
603 size_t
604 height,
605 width;
606
607 Image
608 *image;
609
610 png_color
611 plte[256];
612
613 int
614 reference_count;
615
616 unsigned char
617 alpha_sample_depth,
618 compression_method,
619 color_type,
620 concrete,
621 filter_method,
622 frozen,
623 image_type,
624 interlace_method,
625 pixel_sample_depth,
626 plte_length,
627 sample_depth,
628 viewable;
629 } MngBuffer;
630 #endif
631
632 typedef struct _MngInfo
633 {
634
635 #ifdef MNG_OBJECT_BUFFERS
636 MngBuffer
637 *ob[MNG_MAX_OBJECTS];
638 #endif
639
640 Image *
641 image;
642
643 RectangleInfo
644 page;
645
646 int
647 adjoin,
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649 bytes_in_read_buffer,
650 found_empty_plte,
651 #endif
652 equal_backgrounds,
653 equal_chrms,
654 equal_gammas,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656 defined(PNG_MNG_FEATURES_SUPPORTED)
657 equal_palettes,
658 #endif
659 equal_physs,
660 equal_srgbs,
661 framing_mode,
662 have_global_bkgd,
663 have_global_chrm,
664 have_global_gama,
665 have_global_phys,
666 have_global_sbit,
667 have_global_srgb,
668 have_saved_bkgd_index,
669 have_write_global_chrm,
670 have_write_global_gama,
671 have_write_global_plte,
672 have_write_global_srgb,
673 need_fram,
674 object_id,
675 old_framing_mode,
676 saved_bkgd_index;
677
678 int
679 new_number_colors;
680
681 ssize_t
682 image_found,
683 loop_count[256],
684 loop_iteration[256],
685 scenes_found,
686 x_off[MNG_MAX_OBJECTS],
687 y_off[MNG_MAX_OBJECTS];
688
689 MngBox
690 clip,
691 frame,
692 image_box,
693 object_clip[MNG_MAX_OBJECTS];
694
695 unsigned char
696 /* These flags could be combined into one byte */
697 exists[MNG_MAX_OBJECTS],
698 frozen[MNG_MAX_OBJECTS],
699 loop_active[256],
700 invisible[MNG_MAX_OBJECTS],
701 viewable[MNG_MAX_OBJECTS];
702
703 MagickOffsetType
704 loop_jump[256];
705
706 png_colorp
707 global_plte;
708
709 png_color_8
710 global_sbit;
711
712 png_byte
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
714 read_buffer[8],
715 #endif
716 global_trns[256];
717
718 float
719 global_gamma;
720
721 ChromaticityInfo
722 global_chrm;
723
724 RenderingIntent
725 global_srgb_intent;
726
727 unsigned int
728 delay,
729 global_plte_length,
730 global_trns_length,
731 global_x_pixels_per_unit,
732 global_y_pixels_per_unit,
733 mng_width,
734 mng_height,
735 ticks_per_second;
736
737 MagickBooleanType
738 need_blob;
739
740 unsigned int
741 IsPalette,
742 global_phys_unit_type,
743 basi_warning,
744 clon_warning,
745 dhdr_warning,
746 jhdr_warning,
747 magn_warning,
748 past_warning,
749 phyg_warning,
750 phys_warning,
751 sbit_warning,
752 show_warning,
753 mng_type,
754 write_mng,
755 write_png_colortype,
756 write_png_depth,
757 write_png_compression_level,
758 write_png_compression_strategy,
759 write_png_compression_filter,
760 write_png8,
761 write_png24,
762 write_png32,
763 write_png48,
764 write_png64;
765
766 #ifdef MNG_BASI_SUPPORTED
767 size_t
768 basi_width,
769 basi_height;
770
771 unsigned int
772 basi_depth,
773 basi_color_type,
774 basi_compression_method,
775 basi_filter_type,
776 basi_interlace_method,
777 basi_red,
778 basi_green,
779 basi_blue,
780 basi_alpha,
781 basi_viewable;
782 #endif
783
784 png_uint_16
785 magn_first,
786 magn_last,
787 magn_mb,
788 magn_ml,
789 magn_mr,
790 magn_mt,
791 magn_mx,
792 magn_my,
793 magn_methx,
794 magn_methy;
795
796 PixelInfo
797 mng_global_bkgd;
798
799 /* Added at version 6.6.6-7 */
800 MagickBooleanType
801 ping_exclude_bKGD,
802 ping_exclude_cHRM,
803 ping_exclude_date,
804 ping_exclude_EXIF,
805 ping_exclude_gAMA,
806 ping_exclude_iCCP,
807 /* ping_exclude_iTXt, */
808 ping_exclude_oFFs,
809 ping_exclude_pHYs,
810 ping_exclude_sRGB,
811 ping_exclude_tEXt,
812 ping_exclude_tRNS,
813 ping_exclude_vpAg,
814 ping_exclude_zCCP, /* hex-encoded iCCP */
815 ping_exclude_zTXt,
816 ping_preserve_colormap,
817 /* Added at version 6.8.5-7 */
818 ping_preserve_iCCP,
819 /* Added at version 6.8.9-9 */
820 ping_exclude_tIME;
821
822 } MngInfo;
823 #endif /* VER */
824
825 /*
826 Forward declarations.
827 */
828 static MagickBooleanType
829 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
830
831 static MagickBooleanType
832 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
833
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 #endif
838
839 #if PNG_LIBPNG_VER > 10011
840
841
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
845 {
846 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
847 *
848 * This is true if the high byte and the next highest byte of
849 * each sample of the image, the colormap, and the background color
850 * are equal to each other. We check this by seeing if the samples
851 * are unchanged when we scale them down to 8 and back up to Quantum.
852 *
853 * We don't use the method GetImageDepth() because it doesn't check
854 * background and doesn't handle PseudoClass specially.
855 */
856
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
859
860 MagickBooleanType
861 ok_to_reduce=MagickFalse;
862
863 if (image->depth >= 16)
864 {
865
866 const Quantum
867 *p;
868
869 ok_to_reduce=
870 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873 MagickTrue : MagickFalse;
874
875 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
876 {
877 int indx;
878
879 for (indx=0; indx < (ssize_t) image->colors; indx++)
880 {
881 ok_to_reduce=(
882 QuantumToCharToQuantumEqQuantum(
883 image->colormap[indx].red) &&
884 QuantumToCharToQuantumEqQuantum(
885 image->colormap[indx].green) &&
886 QuantumToCharToQuantumEqQuantum(
887 image->colormap[indx].blue)) ?
888 MagickTrue : MagickFalse;
889
890 if (ok_to_reduce == MagickFalse)
891 break;
892 }
893 }
894
895 if ((ok_to_reduce != MagickFalse) &&
896 (image->storage_class != PseudoClass))
897 {
898 ssize_t
899 y;
900
901 register ssize_t
902 x;
903
904 for (y=0; y < (ssize_t) image->rows; y++)
905 {
906 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
907
908 if (p == (const Quantum *) NULL)
909 {
910 ok_to_reduce = MagickFalse;
911 break;
912 }
913
914 for (x=(ssize_t) image->columns-1; x >= 0; x--)
915 {
916 ok_to_reduce=
917 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920 MagickTrue : MagickFalse;
921
922 if (ok_to_reduce == MagickFalse)
923 break;
924
925 p+=GetPixelChannels(image);
926 }
927 if (x >= 0)
928 break;
929 }
930 }
931
932 if (ok_to_reduce != MagickFalse)
933 {
934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935 " OK to reduce PNG bit depth to 8 without loss of info");
936 }
937 else
938 {
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940 " Not OK to reduce PNG bit depth to 8 without loss of info");
941 }
942 }
943
944 return ok_to_reduce;
945 }
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
947
PngColorTypeToString(const unsigned int color_type)948 static const char* PngColorTypeToString(const unsigned int color_type)
949 {
950 const char
951 *result = "Unknown";
952
953 switch (color_type)
954 {
955 case PNG_COLOR_TYPE_GRAY:
956 result = "Gray";
957 break;
958 case PNG_COLOR_TYPE_GRAY_ALPHA:
959 result = "Gray+Alpha";
960 break;
961 case PNG_COLOR_TYPE_PALETTE:
962 result = "Palette";
963 break;
964 case PNG_COLOR_TYPE_RGB:
965 result = "RGB";
966 break;
967 case PNG_COLOR_TYPE_RGB_ALPHA:
968 result = "RGB+Alpha";
969 break;
970 }
971
972 return result;
973 }
974
975 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
977 {
978 switch (intent)
979 {
980 case PerceptualIntent:
981 return 0;
982
983 case RelativeIntent:
984 return 1;
985
986 case SaturationIntent:
987 return 2;
988
989 case AbsoluteIntent:
990 return 3;
991
992 default:
993 return -1;
994 }
995 }
996
997 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
999 {
1000 switch (ping_intent)
1001 {
1002 case 0:
1003 return PerceptualIntent;
1004
1005 case 1:
1006 return RelativeIntent;
1007
1008 case 2:
1009 return SaturationIntent;
1010
1011 case 3:
1012 return AbsoluteIntent;
1013
1014 default:
1015 return UndefinedIntent;
1016 }
1017 }
1018
1019 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1021 {
1022 switch (ping_intent)
1023 {
1024 case 0:
1025 return "Perceptual Intent";
1026
1027 case 1:
1028 return "Relative Intent";
1029
1030 case 2:
1031 return "Saturation Intent";
1032
1033 case 3:
1034 return "Absolute Intent";
1035
1036 default:
1037 return "Undefined Intent";
1038 }
1039 }
1040
1041 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1043 {
1044 switch (ping_colortype)
1045 {
1046 case 0:
1047 return "Grayscale";
1048
1049 case 2:
1050 return "Truecolor";
1051
1052 case 3:
1053 return "Indexed";
1054
1055 case 4:
1056 return "GrayAlpha";
1057
1058 case 6:
1059 return "RGBA";
1060
1061 default:
1062 return "UndefinedColorType";
1063 }
1064 }
1065
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1068
1069 /*
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 % %
1072 % %
1073 % %
1074 % I s M N G %
1075 % %
1076 % %
1077 % %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 %
1080 % IsMNG() returns MagickTrue if the image format type, identified by the
1081 % magick string, is MNG.
1082 %
1083 % The format of the IsMNG method is:
1084 %
1085 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1086 %
1087 % A description of each parameter follows:
1088 %
1089 % o magick: compare image format pattern against these bytes.
1090 %
1091 % o length: Specifies the length of the magick string.
1092 %
1093 %
1094 */
IsMNG(const unsigned char * magick,const size_t length)1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1096 {
1097 if (length < 8)
1098 return(MagickFalse);
1099
1100 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1101 return(MagickTrue);
1102
1103 return(MagickFalse);
1104 }
1105
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 % %
1109 % %
1110 % %
1111 % I s J N G %
1112 % %
1113 % %
1114 % %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 % IsJNG() returns MagickTrue if the image format type, identified by the
1118 % magick string, is JNG.
1119 %
1120 % The format of the IsJNG method is:
1121 %
1122 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1123 %
1124 % A description of each parameter follows:
1125 %
1126 % o magick: compare image format pattern against these bytes.
1127 %
1128 % o length: Specifies the length of the magick string.
1129 %
1130 %
1131 */
IsJNG(const unsigned char * magick,const size_t length)1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1133 {
1134 if (length < 8)
1135 return(MagickFalse);
1136
1137 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1138 return(MagickTrue);
1139
1140 return(MagickFalse);
1141 }
1142
1143 /*
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 % %
1146 % %
1147 % %
1148 % I s P N G %
1149 % %
1150 % %
1151 % %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 %
1154 % IsPNG() returns MagickTrue if the image format type, identified by the
1155 % magick string, is PNG.
1156 %
1157 % The format of the IsPNG method is:
1158 %
1159 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1160 %
1161 % A description of each parameter follows:
1162 %
1163 % o magick: compare image format pattern against these bytes.
1164 %
1165 % o length: Specifies the length of the magick string.
1166 %
1167 */
IsPNG(const unsigned char * magick,const size_t length)1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1169 {
1170 if (length < 8)
1171 return(MagickFalse);
1172
1173 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1174 return(MagickTrue);
1175
1176 return(MagickFalse);
1177 }
1178
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1181 extern "C" {
1182 #endif
1183
1184 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1186 {
1187 unsigned char
1188 buffer[4];
1189
1190 assert(image != (Image *) NULL);
1191 assert(image->signature == MagickCoreSignature);
1192 buffer[0]=(unsigned char) (value >> 24);
1193 buffer[1]=(unsigned char) (value >> 16);
1194 buffer[2]=(unsigned char) (value >> 8);
1195 buffer[3]=(unsigned char) value;
1196 return((size_t) WriteBlob(image,4,buffer));
1197 }
1198
PNGLong(png_bytep p,png_uint_32 value)1199 static void PNGLong(png_bytep p,png_uint_32 value)
1200 {
1201 *p++=(png_byte) ((value >> 24) & 0xff);
1202 *p++=(png_byte) ((value >> 16) & 0xff);
1203 *p++=(png_byte) ((value >> 8) & 0xff);
1204 *p++=(png_byte) (value & 0xff);
1205 }
1206
1207 #if defined(JNG_SUPPORTED)
PNGsLong(png_bytep p,png_int_32 value)1208 static void PNGsLong(png_bytep p,png_int_32 value)
1209 {
1210 *p++=(png_byte) ((value >> 24) & 0xff);
1211 *p++=(png_byte) ((value >> 16) & 0xff);
1212 *p++=(png_byte) ((value >> 8) & 0xff);
1213 *p++=(png_byte) (value & 0xff);
1214 }
1215 #endif
1216
PNGShort(png_bytep p,png_uint_16 value)1217 static void PNGShort(png_bytep p,png_uint_16 value)
1218 {
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1221 }
1222
PNGType(png_bytep p,const png_byte * type)1223 static void PNGType(png_bytep p,const png_byte *type)
1224 {
1225 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1226 }
1227
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1228 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1229 size_t length)
1230 {
1231 if (logging != MagickFalse)
1232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233 " Writing %c%c%c%c chunk, length: %.20g",
1234 type[0],type[1],type[2],type[3],(double) length);
1235 }
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1237
1238 #if defined(__cplusplus) || defined(c_plusplus)
1239 }
1240 #endif
1241
1242 #if PNG_LIBPNG_VER > 10011
1243 /*
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 % %
1246 % %
1247 % %
1248 % R e a d P N G I m a g e %
1249 % %
1250 % %
1251 % %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %
1254 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 % Multiple-image Network Graphics (MNG) image file and returns it. It
1256 % allocates the memory necessary for the new Image structure and returns a
1257 % pointer to the new image or set of images.
1258 %
1259 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1260 %
1261 % The format of the ReadPNGImage method is:
1262 %
1263 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1264 %
1265 % A description of each parameter follows:
1266 %
1267 % o image_info: the image info.
1268 %
1269 % o exception: return any errors or warnings in this structure.
1270 %
1271 % To do, more or less in chronological order (as of version 5.5.2,
1272 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1273 %
1274 % Get 16-bit cheap transparency working.
1275 %
1276 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1277 %
1278 % Preserve all unknown and not-yet-handled known chunks found in input
1279 % PNG file and copy them into output PNG files according to the PNG
1280 % copying rules.
1281 %
1282 % (At this point, PNG encoding should be in full MNG compliance)
1283 %
1284 % Provide options for choice of background to use when the MNG BACK
1285 % chunk is not present or is not mandatory (i.e., leave transparent,
1286 % user specified, MNG BACK, PNG bKGD)
1287 %
1288 % Implement LOOP/ENDL [done, but could do discretionary loops more
1289 % efficiently by linking in the duplicate frames.].
1290 %
1291 % Decode and act on the MHDR simplicity profile (offer option to reject
1292 % files or attempt to process them anyway when the profile isn't LC or VLC).
1293 %
1294 % Upgrade to full MNG without Delta-PNG.
1295 %
1296 % o BACK [done a while ago except for background image ID]
1297 % o MOVE [done 15 May 1999]
1298 % o CLIP [done 15 May 1999]
1299 % o DISC [done 19 May 1999]
1300 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 % o SEEK [partially done 19 May 1999 (discard function only)]
1302 % o SHOW
1303 % o PAST
1304 % o BASI
1305 % o MNG-level tEXt/iTXt/zTXt
1306 % o pHYg
1307 % o pHYs
1308 % o sBIT
1309 % o bKGD
1310 % o iTXt (wait for libpng implementation).
1311 %
1312 % Use the scene signature to discover when an identical scene is
1313 % being reused, and just point to the original image->exception instead
1314 % of storing another set of pixels. This not specific to MNG
1315 % but could be applied generally.
1316 %
1317 % Upgrade to full MNG with Delta-PNG.
1318 %
1319 % JNG tEXt/iTXt/zTXt
1320 %
1321 % We will not attempt to read files containing the CgBI chunk.
1322 % They are really Xcode files meant for display on the iPhone.
1323 % These are not valid PNG files and it is impossible to recover
1324 % the original PNG from files that have been converted to Xcode-PNG,
1325 % since irretrievable loss of color data has occurred due to the
1326 % use of premultiplied alpha.
1327 */
1328
1329 #if defined(__cplusplus) || defined(c_plusplus)
1330 extern "C" {
1331 #endif
1332
1333 /*
1334 This the function that does the actual reading of data. It is
1335 the same as the one supplied in libpng, except that it receives the
1336 datastream from the ReadBlob() function instead of standard input.
1337 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1339 {
1340 Image
1341 *image;
1342
1343 image=(Image *) png_get_io_ptr(png_ptr);
1344 if (length != 0)
1345 {
1346 png_size_t
1347 check;
1348
1349 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350 if (check != length)
1351 {
1352 char
1353 msg[MagickPathExtent];
1354
1355 (void) FormatLocaleString(msg,MagickPathExtent,
1356 "Expected %.20g bytes; found %.20g bytes",(double) length,
1357 (double) check);
1358 png_warning(png_ptr,msg);
1359 png_error(png_ptr,"Read Exception");
1360 }
1361 }
1362 }
1363
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365 !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367 * older than libpng-1.0.3a, which was the first to allow the empty
1368 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1370 * encountered after an empty PLTE, so we have to look ahead for bKGD
1371 * chunks and remove them from the datastream that is passed to libpng,
1372 * and store their contents for later use.
1373 */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1375 {
1376 MngInfo
1377 *mng_info;
1378
1379 Image
1380 *image;
1381
1382 png_size_t
1383 check;
1384
1385 register ssize_t
1386 i;
1387
1388 i=0;
1389 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390 image=(Image *) mng_info->image;
1391 while (mng_info->bytes_in_read_buffer && length)
1392 {
1393 data[i]=mng_info->read_buffer[i];
1394 mng_info->bytes_in_read_buffer--;
1395 length--;
1396 i++;
1397 }
1398 if (length != 0)
1399 {
1400 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1401
1402 if (check != length)
1403 png_error(png_ptr,"Read Exception");
1404
1405 if (length == 4)
1406 {
1407 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1408 (data[3] == 0))
1409 {
1410 check=(png_size_t) ReadBlob(image,(size_t) length,
1411 (char *) mng_info->read_buffer);
1412 mng_info->read_buffer[4]=0;
1413 mng_info->bytes_in_read_buffer=4;
1414 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415 mng_info->found_empty_plte=MagickTrue;
1416 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1417 {
1418 mng_info->found_empty_plte=MagickFalse;
1419 mng_info->have_saved_bkgd_index=MagickFalse;
1420 }
1421 }
1422
1423 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424 (data[3] == 1))
1425 {
1426 check=(png_size_t) ReadBlob(image,(size_t) length,
1427 (char *) mng_info->read_buffer);
1428 mng_info->read_buffer[4]=0;
1429 mng_info->bytes_in_read_buffer=4;
1430 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431 if (mng_info->found_empty_plte)
1432 {
1433 /*
1434 Skip the bKGD data byte and CRC.
1435 */
1436 check=(png_size_t)
1437 ReadBlob(image,5,(char *) mng_info->read_buffer);
1438 check=(png_size_t) ReadBlob(image,(size_t) length,
1439 (char *) mng_info->read_buffer);
1440 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441 mng_info->have_saved_bkgd_index=MagickTrue;
1442 mng_info->bytes_in_read_buffer=0;
1443 }
1444 }
1445 }
1446 }
1447 }
1448 #endif
1449
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451 {
1452 Image
1453 *image;
1454
1455 image=(Image *) png_get_io_ptr(png_ptr);
1456 if (length != 0)
1457 {
1458 png_size_t
1459 check;
1460
1461 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1462
1463 if (check != length)
1464 png_error(png_ptr,"WriteBlob Failed");
1465 }
1466 }
1467
png_flush_data(png_structp png_ptr)1468 static void png_flush_data(png_structp png_ptr)
1469 {
1470 (void) png_ptr;
1471 }
1472
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1474 static int PalettesAreEqual(Image *a,Image *b)
1475 {
1476 ssize_t
1477 i;
1478
1479 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480 return((int) MagickFalse);
1481
1482 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483 return((int) MagickFalse);
1484
1485 if (a->colors != b->colors)
1486 return((int) MagickFalse);
1487
1488 for (i=0; i < (ssize_t) a->colors; i++)
1489 {
1490 if ((a->colormap[i].red != b->colormap[i].red) ||
1491 (a->colormap[i].green != b->colormap[i].green) ||
1492 (a->colormap[i].blue != b->colormap[i].blue))
1493 return((int) MagickFalse);
1494 }
1495
1496 return((int) MagickTrue);
1497 }
1498 #endif
1499
MngInfoDiscardObject(MngInfo * mng_info,int i)1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1501 {
1502 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503 mng_info->exists[i] && !mng_info->frozen[i])
1504 {
1505 #ifdef MNG_OBJECT_BUFFERS
1506 if (mng_info->ob[i] != (MngBuffer *) NULL)
1507 {
1508 if (mng_info->ob[i]->reference_count > 0)
1509 mng_info->ob[i]->reference_count--;
1510
1511 if (mng_info->ob[i]->reference_count == 0)
1512 {
1513 if (mng_info->ob[i]->image != (Image *) NULL)
1514 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1515
1516 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1517 }
1518 }
1519 mng_info->ob[i]=(MngBuffer *) NULL;
1520 #endif
1521 mng_info->exists[i]=MagickFalse;
1522 mng_info->invisible[i]=MagickFalse;
1523 mng_info->viewable[i]=MagickFalse;
1524 mng_info->frozen[i]=MagickFalse;
1525 mng_info->x_off[i]=0;
1526 mng_info->y_off[i]=0;
1527 mng_info->object_clip[i].left=0;
1528 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529 mng_info->object_clip[i].top=0;
1530 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1531 }
1532 }
1533
MngInfoFreeStruct(MngInfo * mng_info,MagickBooleanType * have_mng_structure)1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535 MagickBooleanType *have_mng_structure)
1536 {
1537 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1538 {
1539 register ssize_t
1540 i;
1541
1542 for (i=1; i < MNG_MAX_OBJECTS; i++)
1543 MngInfoDiscardObject(mng_info,i);
1544
1545 if (mng_info->global_plte != (png_colorp) NULL)
1546 mng_info->global_plte=(png_colorp)
1547 RelinquishMagickMemory(mng_info->global_plte);
1548
1549 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550 *have_mng_structure=MagickFalse;
1551 }
1552 }
1553
mng_minimum_box(MngBox box1,MngBox box2)1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1555 {
1556 MngBox
1557 box;
1558
1559 box=box1;
1560 if (box.left < box2.left)
1561 box.left=box2.left;
1562
1563 if (box.top < box2.top)
1564 box.top=box2.top;
1565
1566 if (box.right > box2.right)
1567 box.right=box2.right;
1568
1569 if (box.bottom > box2.bottom)
1570 box.bottom=box2.bottom;
1571
1572 return box;
1573 }
1574
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1576 {
1577 MngBox
1578 box;
1579
1580 /*
1581 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1582 */
1583 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1584 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1585 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1586 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587 if (delta_type != 0)
1588 {
1589 box.left+=previous_box.left;
1590 box.right+=previous_box.right;
1591 box.top+=previous_box.top;
1592 box.bottom+=previous_box.bottom;
1593 }
1594
1595 return(box);
1596 }
1597
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1599 unsigned char *p)
1600 {
1601 MngPair
1602 pair;
1603 /*
1604 Read two ssize_ts from CLON, MOVE or PAST chunk
1605 */
1606 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1608
1609 if (delta_type != 0)
1610 {
1611 pair.a+=previous_pair.a;
1612 pair.b+=previous_pair.b;
1613 }
1614
1615 return(pair);
1616 }
1617
mng_get_long(unsigned char * p)1618 static long mng_get_long(unsigned char *p)
1619 {
1620 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1621 }
1622
1623 typedef struct _PNGErrorInfo
1624 {
1625 Image
1626 *image;
1627
1628 ExceptionInfo
1629 *exception;
1630 } PNGErrorInfo;
1631
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1633 {
1634 ExceptionInfo
1635 *exception;
1636
1637 Image
1638 *image;
1639
1640 PNGErrorInfo
1641 *error_info;
1642
1643 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644 image=error_info->image;
1645 exception=error_info->exception;
1646
1647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1649
1650 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651 "`%s'",image->filename);
1652
1653 #if (PNG_LIBPNG_VER < 10500)
1654 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655 * are building with libpng-1.4.x and can be ignored.
1656 */
1657 longjmp(ping->jmpbuf,1);
1658 #else
1659 png_longjmp(ping,1);
1660 #endif
1661 }
1662
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1664 {
1665 ExceptionInfo
1666 *exception;
1667
1668 Image
1669 *image;
1670
1671 PNGErrorInfo
1672 *error_info;
1673
1674 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675 png_error(ping, message);
1676
1677 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678 image=error_info->image;
1679 exception=error_info->exception;
1680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1682
1683 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684 message,"`%s'",image->filename);
1685 }
1686
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1690 #else
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1692 #endif
1693 {
1694 (void) png_ptr;
1695 return((png_voidp) AcquireMagickMemory((size_t) size));
1696 }
1697
1698 /*
1699 Free a pointer. It is removed from the list at the same time.
1700 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1702 {
1703 (void) png_ptr;
1704 ptr=RelinquishMagickMemory(ptr);
1705 return((png_free_ptr) NULL);
1706 }
1707 #endif
1708
1709 #if defined(__cplusplus) || defined(c_plusplus)
1710 }
1711 #endif
1712
1713 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1716 {
1717 register ssize_t
1718 i;
1719
1720 register unsigned char
1721 *dp;
1722
1723 register png_charp
1724 sp;
1725
1726 png_uint_32
1727 length,
1728 nibbles;
1729
1730 StringInfo
1731 *profile;
1732
1733 const unsigned char
1734 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1739 13,14,15};
1740
1741 sp=text[ii].text+1;
1742 /* look for newline */
1743 while (*sp != '\n')
1744 sp++;
1745
1746 /* look for length */
1747 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1748 sp++;
1749
1750 length=(png_uint_32) StringToLong(sp);
1751
1752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753 " length: %lu",(unsigned long) length);
1754
1755 while (*sp != ' ' && *sp != '\n')
1756 sp++;
1757
1758 /* allocate space */
1759 if (length == 0)
1760 {
1761 png_warning(ping,"invalid profile length");
1762 return(MagickFalse);
1763 }
1764
1765 profile=BlobToStringInfo((const void *) NULL,length);
1766
1767 if (profile == (StringInfo *) NULL)
1768 {
1769 png_warning(ping, "unable to copy profile");
1770 return(MagickFalse);
1771 }
1772
1773 /* copy profile, skipping white space and column 1 "=" signs */
1774 dp=GetStringInfoDatum(profile);
1775 nibbles=length*2;
1776
1777 for (i=0; i < (ssize_t) nibbles; i++)
1778 {
1779 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1780 {
1781 if (*sp == '\0')
1782 {
1783 png_warning(ping, "ran out of profile data");
1784 profile=DestroyStringInfo(profile);
1785 return(MagickFalse);
1786 }
1787 sp++;
1788 }
1789
1790 if (i%2 == 0)
1791 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1792
1793 else
1794 (*dp++)+=unhex[(int) *sp++];
1795 }
1796 /*
1797 We have already read "Raw profile type.
1798 */
1799 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800 profile=DestroyStringInfo(profile);
1801
1802 if (image_info->verbose)
1803 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1804
1805 return MagickTrue;
1806 }
1807
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
read_vpag_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1810 {
1811 Image
1812 *image;
1813
1814
1815 /* The unknown chunk structure contains the chunk data:
1816 png_byte name[5];
1817 png_byte *data;
1818 png_size_t size;
1819
1820 Note that libpng has already taken care of the CRC handling.
1821 */
1822
1823 LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " read_vpag_chunk: found %c%c%c%c chunk",
1825 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1826
1827 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829 return(0); /* Did not recognize */
1830
1831 /* recognized vpAg */
1832
1833 if (chunk->size != 9)
1834 return(-1); /* Error return */
1835
1836 if (chunk->data[8] != 0)
1837 return(0); /* ImageMagick requires pixel units */
1838
1839 image=(Image *) png_get_user_chunk_ptr(ping);
1840
1841 image->page.width=(size_t) ((chunk->data[0] << 24) |
1842 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1843
1844 image->page.height=(size_t) ((chunk->data[4] << 24) |
1845 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1846
1847 /* Return one of the following: */
1848 /* return(-n); chunk had an error */
1849 /* return(0); did not recognize */
1850 /* return(n); success */
1851
1852 return(1);
1853
1854 }
1855 #endif
1856
1857 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859 ExceptionInfo *exception)
1860 {
1861 png_timep
1862 time;
1863
1864 if (png_get_tIME(ping,info,&time))
1865 {
1866 char
1867 timestamp[21];
1868
1869 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870 time->year,time->month,time->day,time->hour,time->minute,time->second);
1871 SetImageProperty(image,"png:tIME",timestamp,exception);
1872 }
1873 }
1874 #endif
1875
1876 /*
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878 % %
1879 % %
1880 % %
1881 % R e a d O n e P N G I m a g e %
1882 % %
1883 % %
1884 % %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886 %
1887 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 % (minus the 8-byte signature) and returns it. It allocates the memory
1889 % necessary for the new Image structure and returns a pointer to the new
1890 % image.
1891 %
1892 % The format of the ReadOnePNGImage method is:
1893 %
1894 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 % ExceptionInfo *exception)
1896 %
1897 % A description of each parameter follows:
1898 %
1899 % o mng_info: Specifies a pointer to a MngInfo structure.
1900 %
1901 % o image_info: the image info.
1902 %
1903 % o exception: return any errors or warnings in this structure.
1904 %
1905 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907 const ImageInfo *image_info, ExceptionInfo *exception)
1908 {
1909 /* Read one PNG image */
1910
1911 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1912
1913 Image
1914 *image;
1915
1916 char
1917 im_vers[32],
1918 libpng_runv[32],
1919 libpng_vers[32],
1920 zlib_runv[32],
1921 zlib_vers[32];
1922
1923 int
1924 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1925 num_raw_profiles,
1926 num_text,
1927 num_text_total,
1928 num_passes,
1929 number_colors,
1930 pass,
1931 ping_bit_depth,
1932 ping_color_type,
1933 ping_file_depth,
1934 ping_interlace_method,
1935 ping_compression_method,
1936 ping_filter_method,
1937 ping_num_trans,
1938 unit_type;
1939
1940 double
1941 file_gamma;
1942
1943 MagickBooleanType
1944 logging,
1945 ping_found_cHRM,
1946 ping_found_gAMA,
1947 ping_found_iCCP,
1948 ping_found_sRGB,
1949 ping_found_sRGB_cHRM,
1950 ping_preserve_iCCP,
1951 status;
1952
1953 MemoryInfo
1954 *volatile pixel_info;
1955
1956 PixelInfo
1957 transparent_color;
1958
1959 PNGErrorInfo
1960 error_info;
1961
1962 png_bytep
1963 ping_trans_alpha;
1964
1965 png_color_16p
1966 ping_background,
1967 ping_trans_color;
1968
1969 png_info
1970 *end_info,
1971 *ping_info;
1972
1973 png_struct
1974 *ping;
1975
1976 png_textp
1977 text;
1978
1979 png_uint_32
1980 ping_height,
1981 ping_width,
1982 x_resolution,
1983 y_resolution;
1984
1985 QuantumInfo
1986 *quantum_info;
1987
1988 ssize_t
1989 ping_rowbytes,
1990 y;
1991
1992 register unsigned char
1993 *p;
1994
1995 register ssize_t
1996 i,
1997 x;
1998
1999 register Quantum
2000 *q;
2001
2002 size_t
2003 length,
2004 row_offset;
2005
2006 ssize_t
2007 j;
2008
2009 unsigned char
2010 *ping_pixels;
2011
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013 png_byte unused_chunks[]=
2014 {
2015 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2016 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2017 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2018 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2019 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2022 #endif
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024 /* ignore the APNG chunks */
2025 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2026 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2027 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2028 #endif
2029 };
2030 #endif
2031
2032 /* Define these outside of the following "if logging()" block so they will
2033 * show in debuggers.
2034 */
2035 *im_vers='\0';
2036 (void) ConcatenateMagickString(im_vers,
2037 MagickLibVersionText,32);
2038 (void) ConcatenateMagickString(im_vers,
2039 MagickLibAddendum,32);
2040
2041 *libpng_vers='\0';
2042 (void) ConcatenateMagickString(libpng_vers,
2043 PNG_LIBPNG_VER_STRING,32);
2044 *libpng_runv='\0';
2045 (void) ConcatenateMagickString(libpng_runv,
2046 png_get_libpng_ver(NULL),32);
2047
2048 *zlib_vers='\0';
2049 (void) ConcatenateMagickString(zlib_vers,
2050 ZLIB_VERSION,32);
2051 *zlib_runv='\0';
2052 (void) ConcatenateMagickString(zlib_runv,
2053 zlib_version,32);
2054
2055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056 " Enter ReadOnePNGImage()\n"
2057 " IM version = %s\n"
2058 " Libpng version = %s",
2059 im_vers, libpng_vers);
2060
2061 if (logging != MagickFalse)
2062 {
2063 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2064 {
2065 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2066 libpng_runv);
2067 }
2068 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2069 zlib_vers);
2070 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2071 {
2072 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2073 zlib_runv);
2074 }
2075 }
2076
2077 #if (PNG_LIBPNG_VER < 10200)
2078 if (image_info->verbose)
2079 printf("Your PNG library (libpng-%s) is rather old.\n",
2080 PNG_LIBPNG_VER_STRING);
2081 #endif
2082
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2085 if (image_info->verbose)
2086 {
2087 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088 PNG_LIBPNG_VER_STRING);
2089 printf("Please update it.\n");
2090 }
2091 # endif
2092 #endif
2093
2094
2095 quantum_info = (QuantumInfo *) NULL;
2096 image=mng_info->image;
2097
2098 if (logging != MagickFalse)
2099 {
2100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101 " Before reading:\n"
2102 " image->alpha_trait=%d"
2103 " image->rendering_intent=%d\n"
2104 " image->colorspace=%d\n"
2105 " image->gamma=%f",
2106 (int) image->alpha_trait, (int) image->rendering_intent,
2107 (int) image->colorspace, image->gamma);
2108 }
2109 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2110
2111 /* Set to an out-of-range color unless tRNS chunk is present */
2112 transparent_color.red=65537;
2113 transparent_color.green=65537;
2114 transparent_color.blue=65537;
2115 transparent_color.alpha=65537;
2116
2117 number_colors=0;
2118 num_text = 0;
2119 num_text_total = 0;
2120 num_raw_profiles = 0;
2121
2122 ping_found_cHRM = MagickFalse;
2123 ping_found_gAMA = MagickFalse;
2124 ping_found_iCCP = MagickFalse;
2125 ping_found_sRGB = MagickFalse;
2126 ping_found_sRGB_cHRM = MagickFalse;
2127 ping_preserve_iCCP = MagickFalse;
2128
2129
2130 /*
2131 Allocate the PNG structures
2132 */
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134 error_info.image=image;
2135 error_info.exception=exception;
2136 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2139 #else
2140 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141 MagickPNGErrorHandler,MagickPNGWarningHandler);
2142 #endif
2143 if (ping == (png_struct *) NULL)
2144 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2145
2146 ping_info=png_create_info_struct(ping);
2147
2148 if (ping_info == (png_info *) NULL)
2149 {
2150 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152 }
2153
2154 end_info=png_create_info_struct(ping);
2155
2156 if (end_info == (png_info *) NULL)
2157 {
2158 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160 }
2161
2162 pixel_info=(MemoryInfo *) NULL;
2163
2164 if (setjmp(png_jmpbuf(ping)))
2165 {
2166 /*
2167 PNG image is corrupt.
2168 */
2169 png_destroy_read_struct(&ping,&ping_info,&end_info);
2170
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172 UnlockSemaphoreInfo(ping_semaphore);
2173 #endif
2174
2175 if (pixel_info != (MemoryInfo *) NULL)
2176 pixel_info=RelinquishVirtualMemory(pixel_info);
2177
2178 if (logging != MagickFalse)
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " exit ReadOnePNGImage() with error.");
2181
2182 if (image != (Image *) NULL)
2183 {
2184 const char
2185 *option;
2186
2187 option=GetImageOption(image_info,"png:preserve-corrupt-image");
2188 if (IsStringTrue(option) == MagickFalse)
2189 image->columns=0;
2190 }
2191
2192 return(GetFirstImageInList(image));
2193 }
2194
2195 /* { For navigation to end of SETJMP-protected block. Within this
2196 * block, use png_error() instead of Throwing an Exception, to ensure
2197 * that libpng is able to clean up, and that the semaphore is unlocked.
2198 */
2199
2200 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2201 LockSemaphoreInfo(ping_semaphore);
2202 #endif
2203
2204 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2205 /* Allow benign errors */
2206 png_set_benign_errors(ping, 1);
2207 #endif
2208
2209 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2210 /* Reject images with too many rows or columns */
2211 png_set_user_limits(ping,
2212 (png_uint_32) MagickMin(0x7fffffffL,
2213 GetMagickResourceLimit(WidthResource)),
2214 (png_uint_32) MagickMin(0x7fffffffL,
2215 GetMagickResourceLimit(HeightResource)));
2216 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2217
2218 /*
2219 Prepare PNG for reading.
2220 */
2221
2222 mng_info->image_found++;
2223 png_set_sig_bytes(ping,8);
2224
2225 if (LocaleCompare(image_info->magick,"MNG") == 0)
2226 {
2227 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2228 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2229 png_set_read_fn(ping,image,png_get_data);
2230 #else
2231 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2232 png_permit_empty_plte(ping,MagickTrue);
2233 png_set_read_fn(ping,image,png_get_data);
2234 #else
2235 mng_info->image=image;
2236 mng_info->bytes_in_read_buffer=0;
2237 mng_info->found_empty_plte=MagickFalse;
2238 mng_info->have_saved_bkgd_index=MagickFalse;
2239 png_set_read_fn(ping,mng_info,mng_get_data);
2240 #endif
2241 #endif
2242 }
2243
2244 else
2245 png_set_read_fn(ping,image,png_get_data);
2246
2247 {
2248 const char
2249 *value;
2250
2251 value=GetImageOption(image_info,"profile:skip");
2252
2253 if (IsOptionMember("ICC",value) == MagickFalse)
2254 {
2255
2256 value=GetImageOption(image_info,"png:preserve-iCCP");
2257
2258 if (value == NULL)
2259 value=GetImageArtifact(image,"png:preserve-iCCP");
2260
2261 if (value != NULL)
2262 ping_preserve_iCCP=MagickTrue;
2263
2264 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2265 /* Don't let libpng check for ICC/sRGB profile because we're going
2266 * to do that anyway. This feature was added at libpng-1.6.12.
2267 * If logging, go ahead and check and issue a warning as appropriate.
2268 */
2269 if (logging == MagickFalse)
2270 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2271 #endif
2272 }
2273 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2274 else
2275 {
2276 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2277 }
2278 #endif
2279 }
2280 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2281 /* Ignore unused chunks and all unknown chunks except for vpAg */
2282 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2283 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2284 #else
2285 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2286 #endif
2287 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2288 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2289 (int)sizeof(unused_chunks)/5);
2290 /* Callback for other unknown chunks */
2291 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2292 #endif
2293
2294 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2295 # if (PNG_LIBPNG_VER >= 10400)
2296 /* Limit the size of the chunk storage cache used for sPLT, text,
2297 * and unknown chunks.
2298 */
2299 png_set_chunk_cache_max(ping, 32767);
2300 # endif
2301 #endif
2302
2303 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2304 /* Disable new libpng-1.5.10 feature */
2305 png_set_check_for_invalid_index (ping, 0);
2306 #endif
2307
2308 #if (PNG_LIBPNG_VER < 10400)
2309 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2310 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2311 /* Disable thread-unsafe features of pnggccrd */
2312 if (png_access_version_number() >= 10200)
2313 {
2314 png_uint_32 mmx_disable_mask=0;
2315 png_uint_32 asm_flags;
2316
2317 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2318 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2319 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2320 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2321 asm_flags=png_get_asm_flags(ping);
2322 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2323 }
2324 # endif
2325 #endif
2326
2327 png_read_info(ping,ping_info);
2328
2329 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2330 &ping_bit_depth,&ping_color_type,
2331 &ping_interlace_method,&ping_compression_method,
2332 &ping_filter_method);
2333
2334 ping_file_depth = ping_bit_depth;
2335
2336 /* Swap bytes if requested */
2337 if (ping_file_depth == 16)
2338 {
2339 const char
2340 *value;
2341
2342 value=GetImageOption(image_info,"png:swap-bytes");
2343
2344 if (value == NULL)
2345 value=GetImageArtifact(image,"png:swap-bytes");
2346
2347 if (value != NULL)
2348 png_set_swap(ping);
2349 }
2350
2351 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2352 {
2353 char
2354 msg[MagickPathExtent];
2355
2356 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_color_type);
2357 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2358
2359 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_bit_depth);
2360 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2361 }
2362
2363 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2364 &ping_trans_color);
2365
2366 (void) png_get_bKGD(ping, ping_info, &ping_background);
2367
2368 if (ping_bit_depth < 8)
2369 {
2370 png_set_packing(ping);
2371 ping_bit_depth = 8;
2372 }
2373
2374 image->depth=ping_bit_depth;
2375 image->depth=GetImageQuantumDepth(image,MagickFalse);
2376 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2377
2378 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2379 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2380 {
2381 image->rendering_intent=UndefinedIntent;
2382 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2383 (void) ResetMagickMemory(&image->chromaticity,0,
2384 sizeof(image->chromaticity));
2385 }
2386
2387 if (logging != MagickFalse)
2388 {
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390 " PNG width: %.20g, height: %.20g\n"
2391 " PNG color_type: %d, bit_depth: %d\n"
2392 " PNG compression_method: %d\n"
2393 " PNG interlace_method: %d, filter_method: %d",
2394 (double) ping_width, (double) ping_height,
2395 ping_color_type, ping_bit_depth,
2396 ping_compression_method,
2397 ping_interlace_method,ping_filter_method);
2398
2399 }
2400
2401 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2402 {
2403 ping_found_iCCP=MagickTrue;
2404 if (logging != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " Found PNG iCCP chunk.");
2407 }
2408
2409 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2410 {
2411 ping_found_gAMA=MagickTrue;
2412 if (logging != MagickFalse)
2413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2414 " Found PNG gAMA chunk.");
2415 }
2416
2417 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2418 {
2419 ping_found_cHRM=MagickTrue;
2420 if (logging != MagickFalse)
2421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422 " Found PNG cHRM chunk.");
2423 }
2424
2425 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2426 PNG_INFO_sRGB))
2427 {
2428 ping_found_sRGB=MagickTrue;
2429 if (logging != MagickFalse)
2430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2431 " Found PNG sRGB chunk.");
2432 }
2433
2434 #ifdef PNG_READ_iCCP_SUPPORTED
2435 if (ping_found_iCCP !=MagickTrue &&
2436 ping_found_sRGB != MagickTrue &&
2437 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2438 {
2439 ping_found_iCCP=MagickTrue;
2440 if (logging != MagickFalse)
2441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2442 " Found PNG iCCP chunk.");
2443 }
2444
2445 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2446 {
2447 int
2448 compression;
2449
2450 #if (PNG_LIBPNG_VER < 10500)
2451 png_charp
2452 info;
2453 #else
2454 png_bytep
2455 info;
2456 #endif
2457
2458 png_charp
2459 name;
2460
2461 png_uint_32
2462 profile_length;
2463
2464 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2465 &profile_length);
2466
2467 if (profile_length != 0)
2468 {
2469 StringInfo
2470 *profile;
2471
2472 if (logging != MagickFalse)
2473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2474 " Reading PNG iCCP chunk.");
2475
2476 profile=BlobToStringInfo(info,profile_length);
2477
2478 if (profile == (StringInfo *) NULL)
2479 {
2480 png_warning(ping, "ICC profile is NULL");
2481 profile=DestroyStringInfo(profile);
2482 }
2483 else
2484 {
2485 if (ping_preserve_iCCP == MagickFalse)
2486 {
2487 int
2488 icheck,
2489 got_crc=0;
2490
2491
2492 png_uint_32
2493 length,
2494 profile_crc=0;
2495
2496 unsigned char
2497 *data;
2498
2499 length=(png_uint_32) GetStringInfoLength(profile);
2500
2501 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2502 {
2503 if (length == sRGB_info[icheck].len)
2504 {
2505 if (got_crc == 0)
2506 {
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " Got a %lu-byte ICC profile (potentially sRGB)",
2509 (unsigned long) length);
2510
2511 data=GetStringInfoDatum(profile);
2512 profile_crc=crc32(0,data,length);
2513
2514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2515 " with crc=%8x",(unsigned int) profile_crc);
2516 got_crc++;
2517 }
2518
2519 if (profile_crc == sRGB_info[icheck].crc)
2520 {
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " It is sRGB with rendering intent = %s",
2523 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2524 sRGB_info[icheck].intent));
2525 if (image->rendering_intent==UndefinedIntent)
2526 {
2527 image->rendering_intent=
2528 Magick_RenderingIntent_from_PNG_RenderingIntent(
2529 sRGB_info[icheck].intent);
2530 }
2531 break;
2532 }
2533 }
2534 }
2535 if (sRGB_info[icheck].len == 0)
2536 {
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 " Got a %lu-byte ICC profile not recognized as sRGB",
2539 (unsigned long) length);
2540 (void) SetImageProfile(image,"icc",profile,exception);
2541 }
2542 }
2543 else /* Preserve-iCCP */
2544 {
2545 (void) SetImageProfile(image,"icc",profile,exception);
2546 }
2547
2548 profile=DestroyStringInfo(profile);
2549 }
2550 }
2551 }
2552 #endif
2553
2554 #if defined(PNG_READ_sRGB_SUPPORTED)
2555 {
2556 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2557 PNG_INFO_sRGB))
2558 {
2559 if (png_get_sRGB(ping,ping_info,&intent))
2560 {
2561 if (image->rendering_intent == UndefinedIntent)
2562 image->rendering_intent=
2563 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2564
2565 if (logging != MagickFalse)
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2568 }
2569 }
2570
2571 else if (mng_info->have_global_srgb)
2572 {
2573 if (image->rendering_intent == UndefinedIntent)
2574 image->rendering_intent=
2575 Magick_RenderingIntent_from_PNG_RenderingIntent
2576 (mng_info->global_srgb_intent);
2577 }
2578 }
2579 #endif
2580
2581
2582 {
2583 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2584 if (mng_info->have_global_gama)
2585 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2586
2587 if (png_get_gAMA(ping,ping_info,&file_gamma))
2588 {
2589 image->gamma=(float) file_gamma;
2590 if (logging != MagickFalse)
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2593 }
2594 }
2595
2596 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2597 {
2598 if (mng_info->have_global_chrm != MagickFalse)
2599 {
2600 (void) png_set_cHRM(ping,ping_info,
2601 mng_info->global_chrm.white_point.x,
2602 mng_info->global_chrm.white_point.y,
2603 mng_info->global_chrm.red_primary.x,
2604 mng_info->global_chrm.red_primary.y,
2605 mng_info->global_chrm.green_primary.x,
2606 mng_info->global_chrm.green_primary.y,
2607 mng_info->global_chrm.blue_primary.x,
2608 mng_info->global_chrm.blue_primary.y);
2609 }
2610 }
2611
2612 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2613 {
2614 (void) png_get_cHRM(ping,ping_info,
2615 &image->chromaticity.white_point.x,
2616 &image->chromaticity.white_point.y,
2617 &image->chromaticity.red_primary.x,
2618 &image->chromaticity.red_primary.y,
2619 &image->chromaticity.green_primary.x,
2620 &image->chromaticity.green_primary.y,
2621 &image->chromaticity.blue_primary.x,
2622 &image->chromaticity.blue_primary.y);
2623
2624 ping_found_cHRM=MagickTrue;
2625
2626 if (image->chromaticity.red_primary.x>0.6399f &&
2627 image->chromaticity.red_primary.x<0.6401f &&
2628 image->chromaticity.red_primary.y>0.3299f &&
2629 image->chromaticity.red_primary.y<0.3301f &&
2630 image->chromaticity.green_primary.x>0.2999f &&
2631 image->chromaticity.green_primary.x<0.3001f &&
2632 image->chromaticity.green_primary.y>0.5999f &&
2633 image->chromaticity.green_primary.y<0.6001f &&
2634 image->chromaticity.blue_primary.x>0.1499f &&
2635 image->chromaticity.blue_primary.x<0.1501f &&
2636 image->chromaticity.blue_primary.y>0.0599f &&
2637 image->chromaticity.blue_primary.y<0.0601f &&
2638 image->chromaticity.white_point.x>0.3126f &&
2639 image->chromaticity.white_point.x<0.3128f &&
2640 image->chromaticity.white_point.y>0.3289f &&
2641 image->chromaticity.white_point.y<0.3291f)
2642 ping_found_sRGB_cHRM=MagickTrue;
2643 }
2644
2645 if (image->rendering_intent != UndefinedIntent)
2646 {
2647 if (ping_found_sRGB != MagickTrue &&
2648 (ping_found_gAMA != MagickTrue ||
2649 (image->gamma > .45 && image->gamma < .46)) &&
2650 (ping_found_cHRM != MagickTrue ||
2651 ping_found_sRGB_cHRM != MagickFalse) &&
2652 ping_found_iCCP != MagickTrue)
2653 {
2654 png_set_sRGB(ping,ping_info,
2655 Magick_RenderingIntent_to_PNG_RenderingIntent
2656 (image->rendering_intent));
2657 file_gamma=1.000f/2.200f;
2658 ping_found_sRGB=MagickTrue;
2659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660 " Setting sRGB as if in input");
2661 }
2662 }
2663
2664 #if defined(PNG_oFFs_SUPPORTED)
2665 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2666 {
2667 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2668 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2669
2670 if (logging != MagickFalse)
2671 if (image->page.x || image->page.y)
2672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2674 image->page.x,(double) image->page.y);
2675 }
2676 #endif
2677 #if defined(PNG_pHYs_SUPPORTED)
2678 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2679 {
2680 if (mng_info->have_global_phys)
2681 {
2682 png_set_pHYs(ping,ping_info,
2683 mng_info->global_x_pixels_per_unit,
2684 mng_info->global_y_pixels_per_unit,
2685 mng_info->global_phys_unit_type);
2686 }
2687 }
2688
2689 x_resolution=0;
2690 y_resolution=0;
2691 unit_type=0;
2692 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2693 {
2694 /*
2695 Set image resolution.
2696 */
2697 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2698 &unit_type);
2699 image->resolution.x=(double) x_resolution;
2700 image->resolution.y=(double) y_resolution;
2701
2702 if (unit_type == PNG_RESOLUTION_METER)
2703 {
2704 image->units=PixelsPerCentimeterResolution;
2705 image->resolution.x=(double) x_resolution/100.0;
2706 image->resolution.y=(double) y_resolution/100.0;
2707 }
2708
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2712 (double) x_resolution,(double) y_resolution,unit_type);
2713 }
2714 #endif
2715
2716 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2717 {
2718 png_colorp
2719 palette;
2720
2721 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2722
2723 if ((number_colors == 0) &&
2724 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2725 {
2726 if (mng_info->global_plte_length)
2727 {
2728 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2729 (int) mng_info->global_plte_length);
2730
2731 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2732 {
2733 if (mng_info->global_trns_length)
2734 {
2735 png_warning(ping,
2736 "global tRNS has more entries than global PLTE");
2737 }
2738 else
2739 {
2740 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2741 (int) mng_info->global_trns_length,NULL);
2742 }
2743 }
2744 #ifdef PNG_READ_bKGD_SUPPORTED
2745 if (
2746 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2747 mng_info->have_saved_bkgd_index ||
2748 #endif
2749 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2750 {
2751 png_color_16
2752 background;
2753
2754 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2755 if (mng_info->have_saved_bkgd_index)
2756 background.index=mng_info->saved_bkgd_index;
2757 #endif
2758 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2759 background.index=ping_background->index;
2760
2761 background.red=(png_uint_16)
2762 mng_info->global_plte[background.index].red;
2763
2764 background.green=(png_uint_16)
2765 mng_info->global_plte[background.index].green;
2766
2767 background.blue=(png_uint_16)
2768 mng_info->global_plte[background.index].blue;
2769
2770 background.gray=(png_uint_16)
2771 mng_info->global_plte[background.index].green;
2772
2773 png_set_bKGD(ping,ping_info,&background);
2774 }
2775 #endif
2776 }
2777 else
2778 png_error(ping,"No global PLTE in file");
2779 }
2780 }
2781
2782 #ifdef PNG_READ_bKGD_SUPPORTED
2783 if (mng_info->have_global_bkgd &&
2784 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2785 image->background_color=mng_info->mng_global_bkgd;
2786
2787 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2788 {
2789 unsigned int
2790 bkgd_scale;
2791
2792 /* Set image background color.
2793 * Scale background components to 16-bit, then scale
2794 * to quantum depth
2795 */
2796
2797 bkgd_scale = 1;
2798
2799 if (ping_file_depth == 1)
2800 bkgd_scale = 255;
2801
2802 else if (ping_file_depth == 2)
2803 bkgd_scale = 85;
2804
2805 else if (ping_file_depth == 4)
2806 bkgd_scale = 17;
2807
2808 if (ping_file_depth <= 8)
2809 bkgd_scale *= 257;
2810
2811 ping_background->red *= bkgd_scale;
2812 ping_background->green *= bkgd_scale;
2813 ping_background->blue *= bkgd_scale;
2814
2815 if (logging != MagickFalse)
2816 {
2817 if (logging != MagickFalse)
2818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2819 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2820 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2821 ping_background->red,ping_background->green,
2822 ping_background->blue,
2823 bkgd_scale,ping_background->red,
2824 ping_background->green,ping_background->blue);
2825 }
2826
2827 image->background_color.red=
2828 ScaleShortToQuantum(ping_background->red);
2829
2830 image->background_color.green=
2831 ScaleShortToQuantum(ping_background->green);
2832
2833 image->background_color.blue=
2834 ScaleShortToQuantum(ping_background->blue);
2835
2836 image->background_color.alpha=OpaqueAlpha;
2837
2838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " image->background_color=(%.20g,%.20g,%.20g).",
2841 (double) image->background_color.red,
2842 (double) image->background_color.green,
2843 (double) image->background_color.blue);
2844 }
2845 #endif /* PNG_READ_bKGD_SUPPORTED */
2846
2847 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2848 {
2849 /*
2850 Image has a tRNS chunk.
2851 */
2852 int
2853 max_sample;
2854
2855 size_t
2856 one=1;
2857
2858 if (logging != MagickFalse)
2859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2860 " Reading PNG tRNS chunk.");
2861
2862 max_sample = (int) ((one << ping_file_depth) - 1);
2863
2864 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2865 (int)ping_trans_color->gray > max_sample) ||
2866 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2867 ((int)ping_trans_color->red > max_sample ||
2868 (int)ping_trans_color->green > max_sample ||
2869 (int)ping_trans_color->blue > max_sample)))
2870 {
2871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " Ignoring PNG tRNS chunk with out-of-range sample.");
2874 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2875 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2876 image->alpha_trait=UndefinedPixelTrait;
2877 }
2878 else
2879 {
2880 int
2881 scale_to_short;
2882
2883 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2884
2885 /* Scale transparent_color to short */
2886 transparent_color.red= scale_to_short*ping_trans_color->red;
2887 transparent_color.green= scale_to_short*ping_trans_color->green;
2888 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2889 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2890
2891 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2892 {
2893 if (logging != MagickFalse)
2894 {
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2897 (int) ping_trans_color->gray,(int) transparent_color.alpha);
2898
2899 }
2900 transparent_color.red=transparent_color.alpha;
2901 transparent_color.green=transparent_color.alpha;
2902 transparent_color.blue=transparent_color.alpha;
2903 }
2904 }
2905 }
2906 #if defined(PNG_READ_sBIT_SUPPORTED)
2907 if (mng_info->have_global_sbit)
2908 {
2909 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2910 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2911 }
2912 #endif
2913 num_passes=png_set_interlace_handling(ping);
2914
2915 png_read_update_info(ping,ping_info);
2916
2917 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2918
2919 /*
2920 Initialize image structure.
2921 */
2922 mng_info->image_box.left=0;
2923 mng_info->image_box.right=(ssize_t) ping_width;
2924 mng_info->image_box.top=0;
2925 mng_info->image_box.bottom=(ssize_t) ping_height;
2926 if (mng_info->mng_type == 0)
2927 {
2928 mng_info->mng_width=ping_width;
2929 mng_info->mng_height=ping_height;
2930 mng_info->frame=mng_info->image_box;
2931 mng_info->clip=mng_info->image_box;
2932 }
2933
2934 else
2935 {
2936 image->page.y=mng_info->y_off[mng_info->object_id];
2937 }
2938
2939 image->compression=ZipCompression;
2940 image->columns=ping_width;
2941 image->rows=ping_height;
2942
2943 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2944 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2945 {
2946 double
2947 image_gamma = image->gamma;
2948
2949 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2950 " image->gamma=%f",(float) image_gamma);
2951
2952 if (image_gamma > 0.75)
2953 {
2954 /* Set image->rendering_intent to Undefined,
2955 * image->colorspace to GRAY, and reset image->chromaticity.
2956 */
2957 image->intensity = Rec709LuminancePixelIntensityMethod;
2958 SetImageColorspace(image,GRAYColorspace,exception);
2959 }
2960 else
2961 {
2962 RenderingIntent
2963 save_rendering_intent = image->rendering_intent;
2964 ChromaticityInfo
2965 save_chromaticity = image->chromaticity;
2966
2967 SetImageColorspace(image,GRAYColorspace,exception);
2968 image->rendering_intent = save_rendering_intent;
2969 image->chromaticity = save_chromaticity;
2970 }
2971
2972 image->gamma = image_gamma;
2973 }
2974
2975 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2976 " image->colorspace=%d",(int) image->colorspace);
2977
2978 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2979 ((int) ping_bit_depth < 16 &&
2980 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2981 {
2982 size_t
2983 one;
2984
2985 image->storage_class=PseudoClass;
2986 one=1;
2987 image->colors=one << ping_file_depth;
2988 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2989 if (image->colors > 256)
2990 image->colors=256;
2991 #else
2992 if (image->colors > 65536L)
2993 image->colors=65536L;
2994 #endif
2995 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2996 {
2997 png_colorp
2998 palette;
2999
3000 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3001 image->colors=(size_t) number_colors;
3002
3003 if (logging != MagickFalse)
3004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3005 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3006 }
3007 }
3008
3009 if (image->storage_class == PseudoClass)
3010 {
3011 /*
3012 Initialize image colormap.
3013 */
3014 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3015 png_error(ping,"Memory allocation failed");
3016
3017 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3018 {
3019 png_colorp
3020 palette;
3021
3022 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3023
3024 for (i=0; i < (ssize_t) number_colors; i++)
3025 {
3026 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3027 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3028 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3029 }
3030
3031 for ( ; i < (ssize_t) image->colors; i++)
3032 {
3033 image->colormap[i].red=0;
3034 image->colormap[i].green=0;
3035 image->colormap[i].blue=0;
3036 }
3037 }
3038
3039 else
3040 {
3041 Quantum
3042 scale;
3043
3044 scale = (Quantum) ((65535UL)/((1UL << ping_file_depth)-1));
3045
3046 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3047 scale = ScaleShortToQuantum(scale);
3048 #endif
3049
3050 for (i=0; i < (ssize_t) image->colors; i++)
3051 {
3052 image->colormap[i].red=(Quantum) (i*scale);
3053 image->colormap[i].green=(Quantum) (i*scale);
3054 image->colormap[i].blue=(Quantum) (i*scale);
3055 }
3056 }
3057 }
3058
3059 /* Set some properties for reporting by "identify" */
3060 {
3061 char
3062 msg[MagickPathExtent];
3063
3064 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3065 ping_interlace_method in value */
3066
3067 (void) FormatLocaleString(msg,MagickPathExtent,
3068 "%d, %d",(int) ping_width, (int) ping_height);
3069 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3070
3071 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3072 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3073
3074 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3075 (int) ping_color_type,
3076 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3077 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3078
3079 if (ping_interlace_method == 0)
3080 {
3081 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3082 (int) ping_interlace_method);
3083 }
3084 else if (ping_interlace_method == 1)
3085 {
3086 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3087 (int) ping_interlace_method);
3088 }
3089 else
3090 {
3091 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3092 (int) ping_interlace_method);
3093 }
3094 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3095
3096 if (number_colors != 0)
3097 {
3098 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3099 (int) number_colors);
3100 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3101 exception);
3102 }
3103 }
3104 #if defined(PNG_tIME_SUPPORTED)
3105 read_tIME_chunk(image,ping,ping_info,exception);
3106 #endif
3107
3108
3109 /*
3110 Read image scanlines.
3111 */
3112 if (image->delay != 0)
3113 mng_info->scenes_found++;
3114
3115 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3116 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3117 (image_info->first_scene+image_info->number_scenes))))
3118 {
3119 /* This happens later in non-ping decodes */
3120 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3121 image->storage_class=DirectClass;
3122 image->alpha_trait=
3123 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3124 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3125 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3126 BlendPixelTrait : UndefinedPixelTrait;
3127
3128 if (logging != MagickFalse)
3129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130 " Skipping PNG image data for scene %.20g",(double)
3131 mng_info->scenes_found-1);
3132 png_destroy_read_struct(&ping,&ping_info,&end_info);
3133
3134 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3135 UnlockSemaphoreInfo(ping_semaphore);
3136 #endif
3137
3138 if (logging != MagickFalse)
3139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3140 " exit ReadOnePNGImage().");
3141
3142 return(image);
3143 }
3144
3145 if (logging != MagickFalse)
3146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3147 " Reading PNG IDAT chunk(s)");
3148
3149 status=SetImageExtent(image,image->columns,image->rows,exception);
3150 if (status == MagickFalse)
3151 return(DestroyImageList(image));
3152
3153 if (num_passes > 1)
3154 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3155 sizeof(*ping_pixels));
3156 else
3157 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3158
3159 if (pixel_info == (MemoryInfo *) NULL)
3160 png_error(ping,"Memory allocation failed");
3161 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3162
3163 if (logging != MagickFalse)
3164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3165 " Converting PNG pixels to pixel packets");
3166 /*
3167 Convert PNG pixels to pixel packets.
3168 */
3169 quantum_info=AcquireQuantumInfo(image_info,image);
3170
3171 if (quantum_info == (QuantumInfo *) NULL)
3172 png_error(ping,"Failed to allocate quantum_info");
3173
3174 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3175
3176 {
3177
3178 MagickBooleanType
3179 found_transparent_pixel;
3180
3181 found_transparent_pixel=MagickFalse;
3182
3183 if (image->storage_class == DirectClass)
3184 {
3185 for (pass=0; pass < num_passes; pass++)
3186 {
3187 /*
3188 Convert image to DirectClass pixel packets.
3189 */
3190 image->alpha_trait=
3191 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3192 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3193 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3194 BlendPixelTrait : UndefinedPixelTrait;
3195
3196 for (y=0; y < (ssize_t) image->rows; y++)
3197 {
3198 if (num_passes > 1)
3199 row_offset=ping_rowbytes*y;
3200
3201 else
3202 row_offset=0;
3203
3204 png_read_row(ping,ping_pixels+row_offset,NULL);
3205
3206 if (pass < num_passes-1)
3207 continue;
3208
3209 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3210
3211 if (q == (Quantum *) NULL)
3212 break;
3213
3214 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3215 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3216 GrayQuantum,ping_pixels+row_offset,exception);
3217
3218 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3219 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3220 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3221
3222 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3223 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3224 RGBAQuantum,ping_pixels+row_offset,exception);
3225
3226 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3227 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3228 IndexQuantum,ping_pixels+row_offset,exception);
3229
3230 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3231 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3232 RGBQuantum,ping_pixels+row_offset,exception);
3233
3234 if (found_transparent_pixel == MagickFalse)
3235 {
3236 /* Is there a transparent pixel in the row? */
3237 if (y== 0 && logging != MagickFalse)
3238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " Looking for cheap transparent pixel");
3240
3241 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3242 {
3243 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3244 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3245 (GetPixelAlpha(image,q) != OpaqueAlpha))
3246 {
3247 if (logging != MagickFalse)
3248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249 " ...got one.");
3250
3251 found_transparent_pixel = MagickTrue;
3252 break;
3253 }
3254 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3255 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3256 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3257 transparent_color.red &&
3258 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3259 transparent_color.green &&
3260 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3261 transparent_color.blue))
3262 {
3263 if (logging != MagickFalse)
3264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3265 " ...got one.");
3266 found_transparent_pixel = MagickTrue;
3267 break;
3268 }
3269 q+=GetPixelChannels(image);
3270 }
3271 }
3272
3273 if (num_passes == 1)
3274 {
3275 status=SetImageProgress(image,LoadImageTag,
3276 (MagickOffsetType) y, image->rows);
3277
3278 if (status == MagickFalse)
3279 break;
3280 }
3281 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3282 break;
3283 }
3284
3285 if (num_passes != 1)
3286 {
3287 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3288 if (status == MagickFalse)
3289 break;
3290 }
3291 }
3292 }
3293
3294 else /* image->storage_class != DirectClass */
3295
3296 for (pass=0; pass < num_passes; pass++)
3297 {
3298 Quantum
3299 *quantum_scanline;
3300
3301 register Quantum
3302 *r;
3303
3304 /*
3305 Convert grayscale image to PseudoClass pixel packets.
3306 */
3307 if (logging != MagickFalse)
3308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3309 " Converting grayscale pixels to pixel packets");
3310
3311 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3312 BlendPixelTrait : UndefinedPixelTrait;
3313
3314 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3315 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3316 sizeof(*quantum_scanline));
3317
3318 if (quantum_scanline == (Quantum *) NULL)
3319 png_error(ping,"Memory allocation failed");
3320
3321 for (y=0; y < (ssize_t) image->rows; y++)
3322 {
3323 Quantum
3324 alpha;
3325
3326 if (num_passes > 1)
3327 row_offset=ping_rowbytes*y;
3328
3329 else
3330 row_offset=0;
3331
3332 png_read_row(ping,ping_pixels+row_offset,NULL);
3333
3334 if (pass < num_passes-1)
3335 continue;
3336
3337 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3338
3339 if (q == (Quantum *) NULL)
3340 break;
3341
3342 p=ping_pixels+row_offset;
3343 r=quantum_scanline;
3344
3345 switch (ping_bit_depth)
3346 {
3347 case 8:
3348 {
3349
3350 if (ping_color_type == 4)
3351 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3352 {
3353 *r++=*p++;
3354
3355 alpha=ScaleCharToQuantum((unsigned char)*p++);
3356
3357 SetPixelAlpha(image,alpha,q);
3358
3359 if (alpha != OpaqueAlpha)
3360 found_transparent_pixel = MagickTrue;
3361
3362 q+=GetPixelChannels(image);
3363 }
3364
3365 else
3366 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3367 *r++=*p++;
3368
3369 break;
3370 }
3371
3372 case 16:
3373 {
3374 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3375 {
3376 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3377 unsigned short
3378 quantum;
3379
3380 if (image->colors > 256)
3381 quantum=((*p++) << 8);
3382
3383 else
3384 quantum=0;
3385
3386 quantum|=(*p++);
3387 *r=ScaleShortToQuantum(quantum);
3388 r++;
3389
3390 if (ping_color_type == 4)
3391 {
3392 if (image->colors > 256)
3393 quantum=((*p++) << 8);
3394 else
3395 quantum=0;
3396
3397 quantum|=(*p++);
3398
3399 alpha=ScaleShortToQuantum(quantum);
3400 SetPixelAlpha(image,alpha,q);
3401
3402 if (alpha != OpaqueAlpha)
3403 found_transparent_pixel = MagickTrue;
3404
3405 q+=GetPixelChannels(image);
3406 }
3407
3408 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3409 *r++=(*p++);
3410 p++; /* strip low byte */
3411
3412 if (ping_color_type == 4)
3413 {
3414 SetPixelAlpha(image,*p++,q);
3415
3416 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3417 found_transparent_pixel = MagickTrue;
3418
3419 p++;
3420 q+=GetPixelChannels(image);
3421 }
3422 #endif
3423 }
3424
3425 break;
3426 }
3427
3428 default:
3429 break;
3430 }
3431
3432 /*
3433 Transfer image scanline.
3434 */
3435 r=quantum_scanline;
3436
3437 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3438
3439 if (q == (Quantum *) NULL)
3440 break;
3441 for (x=0; x < (ssize_t) image->columns; x++)
3442 {
3443 SetPixelIndex(image,*r++,q);
3444 q+=GetPixelChannels(image);
3445 }
3446
3447 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3448 break;
3449
3450 if (num_passes == 1)
3451 {
3452 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3453 image->rows);
3454
3455 if (status == MagickFalse)
3456 break;
3457 }
3458 }
3459
3460 if (num_passes != 1)
3461 {
3462 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3463
3464 if (status == MagickFalse)
3465 break;
3466 }
3467
3468 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3469 }
3470
3471 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3472 UndefinedPixelTrait;
3473
3474 if (logging != MagickFalse)
3475 {
3476 if (found_transparent_pixel != MagickFalse)
3477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478 " Found transparent pixel");
3479 else
3480 {
3481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3482 " No transparent pixel was found");
3483
3484 ping_color_type&=0x03;
3485 }
3486 }
3487 }
3488
3489 if (quantum_info != (QuantumInfo *) NULL)
3490 quantum_info=DestroyQuantumInfo(quantum_info);
3491
3492 if (image->storage_class == PseudoClass)
3493 {
3494 PixelTrait
3495 alpha_trait;
3496
3497 alpha_trait=image->alpha_trait;
3498 image->alpha_trait=UndefinedPixelTrait;
3499 (void) SyncImage(image,exception);
3500 image->alpha_trait=alpha_trait;
3501 }
3502
3503 png_read_end(ping,end_info);
3504
3505 if (logging != MagickFalse)
3506 {
3507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3508 " image->storage_class=%d\n",(int) image->storage_class);
3509 }
3510
3511 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3512 (ssize_t) image_info->first_scene && image->delay != 0)
3513 {
3514 png_destroy_read_struct(&ping,&ping_info,&end_info);
3515 pixel_info=RelinquishVirtualMemory(pixel_info);
3516 image->colors=2;
3517 (void) SetImageBackgroundColor(image,exception);
3518 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3519 UnlockSemaphoreInfo(ping_semaphore);
3520 #endif
3521 if (logging != MagickFalse)
3522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523 " exit ReadOnePNGImage() early.");
3524 return(image);
3525 }
3526
3527 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3528 {
3529 ClassType
3530 storage_class;
3531
3532 /*
3533 Image has a transparent background.
3534 */
3535 storage_class=image->storage_class;
3536 image->alpha_trait=BlendPixelTrait;
3537
3538 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3539
3540 if (storage_class == PseudoClass)
3541 {
3542 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3543 {
3544 for (x=0; x < ping_num_trans; x++)
3545 {
3546 image->colormap[x].alpha_trait=BlendPixelTrait;
3547 image->colormap[x].alpha =
3548 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3549 }
3550 }
3551
3552 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3553 {
3554 for (x=0; x < (int) image->colors; x++)
3555 {
3556 if (ScaleQuantumToShort(image->colormap[x].red) ==
3557 transparent_color.alpha)
3558 {
3559 image->colormap[x].alpha_trait=BlendPixelTrait;
3560 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3561 }
3562 }
3563 }
3564 (void) SyncImage(image,exception);
3565 }
3566
3567 #if 1 /* Should have already been done above, but glennrp problem P10
3568 * needs this.
3569 */
3570 else
3571 {
3572 for (y=0; y < (ssize_t) image->rows; y++)
3573 {
3574 image->storage_class=storage_class;
3575 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3576
3577 if (q == (Quantum *) NULL)
3578 break;
3579
3580
3581 /* Caution: on a Q8 build, this does not distinguish between
3582 * 16-bit colors that differ only in the low byte
3583 */
3584 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3585 {
3586 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3587 transparent_color.red &&
3588 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3589 transparent_color.green &&
3590 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3591 transparent_color.blue)
3592 {
3593 SetPixelAlpha(image,TransparentAlpha,q);
3594 }
3595
3596 #if 0 /* I have not found a case where this is needed. */
3597 else
3598 {
3599 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3600 }
3601 #endif
3602
3603 q+=GetPixelChannels(image);
3604 }
3605
3606 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3607 break;
3608 }
3609 }
3610 #endif
3611
3612 image->storage_class=DirectClass;
3613 }
3614
3615 for (j = 0; j < 2; j++)
3616 {
3617 if (j == 0)
3618 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3619 MagickTrue : MagickFalse;
3620 else
3621 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3622 MagickTrue : MagickFalse;
3623
3624 if (status != MagickFalse)
3625 for (i=0; i < (ssize_t) num_text; i++)
3626 {
3627 /* Check for a profile */
3628
3629 if (logging != MagickFalse)
3630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3631 " Reading PNG text chunk");
3632
3633 if (strlen(text[i].key) > 16 &&
3634 memcmp(text[i].key, "Raw profile type ",17) == 0)
3635 {
3636 const char
3637 *value;
3638
3639 value=GetImageOption(image_info,"profile:skip");
3640
3641 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3642 {
3643 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3644 (int) i,exception);
3645 num_raw_profiles++;
3646 if (logging != MagickFalse)
3647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3648 " Read raw profile %s",text[i].key+17);
3649 }
3650 else
3651 {
3652 if (logging != MagickFalse)
3653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3654 " Skipping raw profile %s",text[i].key+17);
3655 }
3656 }
3657
3658 else
3659 {
3660 char
3661 *value;
3662
3663 length=text[i].text_length;
3664 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3665 sizeof(*value));
3666 if (value == (char *) NULL)
3667 {
3668 png_error(ping,"Memory allocation failed");
3669 break;
3670 }
3671 *value='\0';
3672 (void) ConcatenateMagickString(value,text[i].text,length+2);
3673
3674 /* Don't save "density" or "units" property if we have a pHYs
3675 * chunk
3676 */
3677 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3678 (LocaleCompare(text[i].key,"density") != 0 &&
3679 LocaleCompare(text[i].key,"units") != 0))
3680 (void) SetImageProperty(image,text[i].key,value,exception);
3681
3682 if (logging != MagickFalse)
3683 {
3684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3685 " length: %lu\n"
3686 " Keyword: %s",
3687 (unsigned long) length,
3688 text[i].key);
3689 }
3690
3691 value=DestroyString(value);
3692 }
3693 }
3694 num_text_total += num_text;
3695 }
3696
3697 #ifdef MNG_OBJECT_BUFFERS
3698 /*
3699 Store the object if necessary.
3700 */
3701 if (object_id && !mng_info->frozen[object_id])
3702 {
3703 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3704 {
3705 /*
3706 create a new object buffer.
3707 */
3708 mng_info->ob[object_id]=(MngBuffer *)
3709 AcquireMagickMemory(sizeof(MngBuffer));
3710
3711 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3712 {
3713 mng_info->ob[object_id]->image=(Image *) NULL;
3714 mng_info->ob[object_id]->reference_count=1;
3715 }
3716 }
3717
3718 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3719 mng_info->ob[object_id]->frozen)
3720 {
3721 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3722 png_error(ping,"Memory allocation failed");
3723
3724 if (mng_info->ob[object_id]->frozen)
3725 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3726 }
3727
3728 else
3729 {
3730
3731 if (mng_info->ob[object_id]->image != (Image *) NULL)
3732 mng_info->ob[object_id]->image=DestroyImage
3733 (mng_info->ob[object_id]->image);
3734
3735 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3736 exception);
3737
3738 if (mng_info->ob[object_id]->image != (Image *) NULL)
3739 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3740
3741 else
3742 png_error(ping, "Cloning image for object buffer failed");
3743
3744 if (ping_width > 250000L || ping_height > 250000L)
3745 png_error(ping,"PNG Image dimensions are too large.");
3746
3747 mng_info->ob[object_id]->width=ping_width;
3748 mng_info->ob[object_id]->height=ping_height;
3749 mng_info->ob[object_id]->color_type=ping_color_type;
3750 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3751 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3752 mng_info->ob[object_id]->compression_method=
3753 ping_compression_method;
3754 mng_info->ob[object_id]->filter_method=ping_filter_method;
3755
3756 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3757 {
3758 png_colorp
3759 plte;
3760
3761 /*
3762 Copy the PLTE to the object buffer.
3763 */
3764 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3765 mng_info->ob[object_id]->plte_length=number_colors;
3766
3767 for (i=0; i < number_colors; i++)
3768 {
3769 mng_info->ob[object_id]->plte[i]=plte[i];
3770 }
3771 }
3772
3773 else
3774 mng_info->ob[object_id]->plte_length=0;
3775 }
3776 }
3777 #endif
3778
3779 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3780 * alpha or if a valid tRNS chunk is present, no matter whether there
3781 * is actual transparency present.
3782 */
3783 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3784 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3785 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3786 BlendPixelTrait : UndefinedPixelTrait;
3787
3788 #if 0 /* I'm not sure what's wrong here but it does not work. */
3789 if (image->alpha_trait != UndefinedPixelTrait)
3790 {
3791 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3792 (void) SetImageType(image,GrayscaleAlphaType,exception);
3793
3794 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3795 (void) SetImageType(image,PaletteAlphaType,exception);
3796
3797 else
3798 (void) SetImageType(image,TrueColorAlphaType,exception);
3799 }
3800
3801 else
3802 {
3803 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3804 (void) SetImageType(image,GrayscaleType,exception);
3805
3806 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3807 (void) SetImageType(image,PaletteType,exception);
3808
3809 else
3810 (void) SetImageType(image,TrueColorType,exception);
3811 }
3812 #endif
3813
3814 /* Set more properties for identify to retrieve */
3815 {
3816 char
3817 msg[MagickPathExtent];
3818
3819 if (num_text_total != 0)
3820 {
3821 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3822 (void) FormatLocaleString(msg,MagickPathExtent,
3823 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3824 (void) SetImageProperty(image,"png:text",msg,
3825 exception);
3826 }
3827
3828 if (num_raw_profiles != 0)
3829 {
3830 (void) FormatLocaleString(msg,MagickPathExtent,
3831 "%d were found", num_raw_profiles);
3832 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3833 exception);
3834 }
3835
3836 if (ping_found_cHRM != MagickFalse)
3837 {
3838 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3839 "chunk was found (see Chromaticity, above)");
3840 (void) SetImageProperty(image,"png:cHRM",msg,
3841 exception);
3842 }
3843
3844 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3845 {
3846 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3847 "chunk was found (see Background color, above)");
3848 (void) SetImageProperty(image,"png:bKGD",msg,
3849 exception);
3850 }
3851
3852 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3853 "chunk was found");
3854
3855 #if defined(PNG_iCCP_SUPPORTED)
3856 if (ping_found_iCCP != MagickFalse)
3857 (void) SetImageProperty(image,"png:iCCP",msg,
3858 exception);
3859 #endif
3860
3861 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3862 (void) SetImageProperty(image,"png:tRNS",msg,
3863 exception);
3864
3865 #if defined(PNG_sRGB_SUPPORTED)
3866 if (ping_found_sRGB != MagickFalse)
3867 {
3868 (void) FormatLocaleString(msg,MagickPathExtent,
3869 "intent=%d (%s)",
3870 (int) intent,
3871 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3872 (void) SetImageProperty(image,"png:sRGB",msg,
3873 exception);
3874 }
3875 #endif
3876
3877 if (ping_found_gAMA != MagickFalse)
3878 {
3879 (void) FormatLocaleString(msg,MagickPathExtent,
3880 "gamma=%.8g (See Gamma, above)",
3881 file_gamma);
3882 (void) SetImageProperty(image,"png:gAMA",msg,
3883 exception);
3884 }
3885
3886 #if defined(PNG_pHYs_SUPPORTED)
3887 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3888 {
3889 (void) FormatLocaleString(msg,MagickPathExtent,
3890 "x_res=%.10g, y_res=%.10g, units=%d",
3891 (double) x_resolution,(double) y_resolution, unit_type);
3892 (void) SetImageProperty(image,"png:pHYs",msg,
3893 exception);
3894 }
3895 #endif
3896
3897 #if defined(PNG_oFFs_SUPPORTED)
3898 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3899 {
3900 (void) FormatLocaleString(msg,MagickPathExtent,"x_off=%.20g, y_off=%.20g",
3901 (double) image->page.x,(double) image->page.y);
3902 (void) SetImageProperty(image,"png:oFFs",msg,
3903 exception);
3904 }
3905 #endif
3906
3907 #if defined(PNG_tIME_SUPPORTED)
3908 read_tIME_chunk(image,ping,end_info,exception);
3909 #endif
3910
3911 if ((image->page.width != 0 && image->page.width != image->columns) ||
3912 (image->page.height != 0 && image->page.height != image->rows))
3913 {
3914 (void) FormatLocaleString(msg,MagickPathExtent,
3915 "width=%.20g, height=%.20g",
3916 (double) image->page.width,(double) image->page.height);
3917 (void) SetImageProperty(image,"png:vpAg",msg,
3918 exception);
3919 }
3920 }
3921
3922 /*
3923 Relinquish resources.
3924 */
3925 png_destroy_read_struct(&ping,&ping_info,&end_info);
3926
3927 pixel_info=RelinquishVirtualMemory(pixel_info);
3928
3929 if (logging != MagickFalse)
3930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931 " exit ReadOnePNGImage()");
3932
3933 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3934 UnlockSemaphoreInfo(ping_semaphore);
3935 #endif
3936
3937 /* } for navigation to beginning of SETJMP-protected block, revert to
3938 * Throwing an Exception when an error occurs.
3939 */
3940
3941 return(image);
3942
3943 /* end of reading one PNG image */
3944 }
3945
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)3946 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3947 {
3948 Image
3949 *image;
3950
3951 MagickBooleanType
3952 have_mng_structure,
3953 logging,
3954 status;
3955
3956 MngInfo
3957 *mng_info;
3958
3959 char
3960 magic_number[MagickPathExtent];
3961
3962 ssize_t
3963 count;
3964
3965 /*
3966 Open image file.
3967 */
3968 assert(image_info != (const ImageInfo *) NULL);
3969 assert(image_info->signature == MagickCoreSignature);
3970
3971 if (image_info->debug != MagickFalse)
3972 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3973 image_info->filename);
3974
3975 assert(exception != (ExceptionInfo *) NULL);
3976 assert(exception->signature == MagickCoreSignature);
3977 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3978 image=AcquireImage(image_info,exception);
3979 mng_info=(MngInfo *) NULL;
3980 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3981
3982 if (status == MagickFalse)
3983 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3984
3985 /*
3986 Verify PNG signature.
3987 */
3988 count=ReadBlob(image,8,(unsigned char *) magic_number);
3989
3990 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3991 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3992
3993 /*
3994 Allocate a MngInfo structure.
3995 */
3996 have_mng_structure=MagickFalse;
3997 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3998
3999 if (mng_info == (MngInfo *) NULL)
4000 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4001
4002 /*
4003 Initialize members of the MngInfo structure.
4004 */
4005 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4006 mng_info->image=image;
4007 have_mng_structure=MagickTrue;
4008
4009 image=ReadOnePNGImage(mng_info,image_info,exception);
4010 MngInfoFreeStruct(mng_info,&have_mng_structure);
4011
4012 if (image == (Image *) NULL)
4013 {
4014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 "exit ReadPNGImage() with error");
4017
4018 return((Image *) NULL);
4019 }
4020
4021 (void) CloseBlob(image);
4022
4023 if ((image->columns == 0) || (image->rows == 0))
4024 {
4025 if (logging != MagickFalse)
4026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027 "exit ReadPNGImage() with error.");
4028
4029 ThrowReaderException(CorruptImageError,"CorruptImage");
4030 }
4031
4032 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4033 ((image->gamma < .45) || (image->gamma > .46)) &&
4034 !(image->chromaticity.red_primary.x>0.6399f &&
4035 image->chromaticity.red_primary.x<0.6401f &&
4036 image->chromaticity.red_primary.y>0.3299f &&
4037 image->chromaticity.red_primary.y<0.3301f &&
4038 image->chromaticity.green_primary.x>0.2999f &&
4039 image->chromaticity.green_primary.x<0.3001f &&
4040 image->chromaticity.green_primary.y>0.5999f &&
4041 image->chromaticity.green_primary.y<0.6001f &&
4042 image->chromaticity.blue_primary.x>0.1499f &&
4043 image->chromaticity.blue_primary.x<0.1501f &&
4044 image->chromaticity.blue_primary.y>0.0599f &&
4045 image->chromaticity.blue_primary.y<0.0601f &&
4046 image->chromaticity.white_point.x>0.3126f &&
4047 image->chromaticity.white_point.x<0.3128f &&
4048 image->chromaticity.white_point.y>0.3289f &&
4049 image->chromaticity.white_point.y<0.3291f))
4050 {
4051 SetImageColorspace(image,RGBColorspace,exception);
4052 }
4053
4054 if (logging != MagickFalse)
4055 {
4056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4057 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4058 (double) image->page.width,(double) image->page.height,
4059 (double) image->page.x,(double) image->page.y);
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " image->colorspace: %d", (int) image->colorspace);
4062 }
4063
4064 if (logging != MagickFalse)
4065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4066
4067 return(image);
4068 }
4069
4070
4071
4072 #if defined(JNG_SUPPORTED)
4073 /*
4074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4075 % %
4076 % %
4077 % %
4078 % R e a d O n e J N G I m a g e %
4079 % %
4080 % %
4081 % %
4082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4083 %
4084 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4085 % (minus the 8-byte signature) and returns it. It allocates the memory
4086 % necessary for the new Image structure and returns a pointer to the new
4087 % image.
4088 %
4089 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4090 %
4091 % The format of the ReadOneJNGImage method is:
4092 %
4093 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4094 % ExceptionInfo *exception)
4095 %
4096 % A description of each parameter follows:
4097 %
4098 % o mng_info: Specifies a pointer to a MngInfo structure.
4099 %
4100 % o image_info: the image info.
4101 %
4102 % o exception: return any errors or warnings in this structure.
4103 %
4104 */
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4105 static Image *ReadOneJNGImage(MngInfo *mng_info,
4106 const ImageInfo *image_info, ExceptionInfo *exception)
4107 {
4108 Image
4109 *alpha_image,
4110 *color_image,
4111 *image,
4112 *jng_image;
4113
4114 ImageInfo
4115 *alpha_image_info,
4116 *color_image_info;
4117
4118 MagickBooleanType
4119 logging;
4120
4121 ssize_t
4122 y;
4123
4124 MagickBooleanType
4125 status;
4126
4127 png_uint_32
4128 jng_height,
4129 jng_width;
4130
4131 png_byte
4132 jng_color_type,
4133 jng_image_sample_depth,
4134 jng_image_compression_method,
4135 jng_image_interlace_method,
4136 jng_alpha_sample_depth,
4137 jng_alpha_compression_method,
4138 jng_alpha_filter_method,
4139 jng_alpha_interlace_method;
4140
4141 register const Quantum
4142 *s;
4143
4144 register ssize_t
4145 i,
4146 x;
4147
4148 register Quantum
4149 *q;
4150
4151 register unsigned char
4152 *p;
4153
4154 unsigned int
4155 read_JSEP,
4156 reading_idat;
4157
4158 size_t
4159 length;
4160
4161 jng_alpha_compression_method=0;
4162 jng_alpha_sample_depth=8;
4163 jng_color_type=0;
4164 jng_height=0;
4165 jng_width=0;
4166 alpha_image=(Image *) NULL;
4167 color_image=(Image *) NULL;
4168 alpha_image_info=(ImageInfo *) NULL;
4169 color_image_info=(ImageInfo *) NULL;
4170
4171 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4172 " Enter ReadOneJNGImage()");
4173
4174 image=mng_info->image;
4175
4176 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4177 {
4178 /*
4179 Allocate next image structure.
4180 */
4181 if (logging != MagickFalse)
4182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183 " AcquireNextImage()");
4184
4185 AcquireNextImage(image_info,image,exception);
4186
4187 if (GetNextImageInList(image) == (Image *) NULL)
4188 return((Image *) NULL);
4189
4190 image=SyncNextImageInList(image);
4191 }
4192 mng_info->image=image;
4193
4194 /*
4195 Signature bytes have already been read.
4196 */
4197
4198 read_JSEP=MagickFalse;
4199 reading_idat=MagickFalse;
4200 for (;;)
4201 {
4202 char
4203 type[MagickPathExtent];
4204
4205 unsigned char
4206 *chunk;
4207
4208 unsigned int
4209 count;
4210
4211 /*
4212 Read a new JNG chunk.
4213 */
4214 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4215 2*GetBlobSize(image));
4216
4217 if (status == MagickFalse)
4218 break;
4219
4220 type[0]='\0';
4221 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4222 length=ReadBlobMSBLong(image);
4223 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4224
4225 if (logging != MagickFalse)
4226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4227 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4228 type[0],type[1],type[2],type[3],(double) length);
4229
4230 if (length > PNG_UINT_31_MAX || count == 0)
4231 ThrowReaderException(CorruptImageError,"CorruptImage");
4232
4233 p=NULL;
4234 chunk=(unsigned char *) NULL;
4235
4236 if (length != 0)
4237 {
4238 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4239
4240 if (chunk == (unsigned char *) NULL)
4241 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4242
4243 for (i=0; i < (ssize_t) length; i++)
4244 chunk[i]=(unsigned char) ReadBlobByte(image);
4245
4246 p=chunk;
4247 }
4248
4249 (void) ReadBlobMSBLong(image); /* read crc word */
4250
4251 if (memcmp(type,mng_JHDR,4) == 0)
4252 {
4253 if (length == 16)
4254 {
4255 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4256 (p[2] << 8) | p[3]);
4257 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4258 (p[6] << 8) | p[7]);
4259 if ((jng_width == 0) || (jng_height == 0))
4260 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4261 jng_color_type=p[8];
4262 jng_image_sample_depth=p[9];
4263 jng_image_compression_method=p[10];
4264 jng_image_interlace_method=p[11];
4265
4266 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4267 NoInterlace;
4268
4269 jng_alpha_sample_depth=p[12];
4270 jng_alpha_compression_method=p[13];
4271 jng_alpha_filter_method=p[14];
4272 jng_alpha_interlace_method=p[15];
4273
4274 if (logging != MagickFalse)
4275 {
4276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4277 " jng_width: %16lu, jng_height: %16lu\n"
4278 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4279 " jng_image_compression_method:%3d",
4280 (unsigned long) jng_width, (unsigned long) jng_height,
4281 jng_color_type, jng_image_sample_depth,
4282 jng_image_compression_method);
4283
4284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4285 " jng_image_interlace_method: %3d"
4286 " jng_alpha_sample_depth: %3d",
4287 jng_image_interlace_method,
4288 jng_alpha_sample_depth);
4289
4290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4291 " jng_alpha_compression_method:%3d\n"
4292 " jng_alpha_filter_method: %3d\n"
4293 " jng_alpha_interlace_method: %3d",
4294 jng_alpha_compression_method,
4295 jng_alpha_filter_method,
4296 jng_alpha_interlace_method);
4297 }
4298 }
4299
4300 if (length != 0)
4301 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4302
4303 continue;
4304 }
4305
4306
4307 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4308 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4309 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4310 {
4311 /*
4312 o create color_image
4313 o open color_blob, attached to color_image
4314 o if (color type has alpha)
4315 open alpha_blob, attached to alpha_image
4316 */
4317
4318 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4319
4320 if (color_image_info == (ImageInfo *) NULL)
4321 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4322
4323 GetImageInfo(color_image_info);
4324 color_image=AcquireImage(color_image_info,exception);
4325
4326 if (color_image == (Image *) NULL)
4327 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4328
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 " Creating color_blob.");
4332
4333 (void) AcquireUniqueFilename(color_image->filename);
4334 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4335 exception);
4336
4337 if (status == MagickFalse)
4338 return((Image *) NULL);
4339
4340 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4341 {
4342 alpha_image_info=(ImageInfo *)
4343 AcquireMagickMemory(sizeof(ImageInfo));
4344
4345 if (alpha_image_info == (ImageInfo *) NULL)
4346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4347
4348 GetImageInfo(alpha_image_info);
4349 alpha_image=AcquireImage(alpha_image_info,exception);
4350
4351 if (alpha_image == (Image *) NULL)
4352 {
4353 alpha_image=DestroyImage(alpha_image);
4354 ThrowReaderException(ResourceLimitError,
4355 "MemoryAllocationFailed");
4356 }
4357
4358 if (logging != MagickFalse)
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " Creating alpha_blob.");
4361
4362 (void) AcquireUniqueFilename(alpha_image->filename);
4363 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4364 exception);
4365
4366 if (status == MagickFalse)
4367 return((Image *) NULL);
4368
4369 if (jng_alpha_compression_method == 0)
4370 {
4371 unsigned char
4372 data[18];
4373
4374 if (logging != MagickFalse)
4375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4376 " Writing IHDR chunk to alpha_blob.");
4377
4378 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4379 "\211PNG\r\n\032\n");
4380
4381 (void) WriteBlobMSBULong(alpha_image,13L);
4382 PNGType(data,mng_IHDR);
4383 LogPNGChunk(logging,mng_IHDR,13L);
4384 PNGLong(data+4,jng_width);
4385 PNGLong(data+8,jng_height);
4386 data[12]=jng_alpha_sample_depth;
4387 data[13]=0; /* color_type gray */
4388 data[14]=0; /* compression method 0 */
4389 data[15]=0; /* filter_method 0 */
4390 data[16]=0; /* interlace_method 0 */
4391 (void) WriteBlob(alpha_image,17,data);
4392 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4393 }
4394 }
4395 reading_idat=MagickTrue;
4396 }
4397
4398 if (memcmp(type,mng_JDAT,4) == 0)
4399 {
4400 /* Copy chunk to color_image->blob */
4401
4402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " Copying JDAT chunk data to color_blob.");
4405
4406 (void) WriteBlob(color_image,length,chunk);
4407
4408 if (length != 0)
4409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4410
4411 continue;
4412 }
4413
4414 if (memcmp(type,mng_IDAT,4) == 0)
4415 {
4416 png_byte
4417 data[5];
4418
4419 /* Copy IDAT header and chunk data to alpha_image->blob */
4420
4421 if (alpha_image != NULL && image_info->ping == MagickFalse)
4422 {
4423 if (logging != MagickFalse)
4424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425 " Copying IDAT chunk data to alpha_blob.");
4426
4427 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4428 PNGType(data,mng_IDAT);
4429 LogPNGChunk(logging,mng_IDAT,length);
4430 (void) WriteBlob(alpha_image,4,data);
4431 (void) WriteBlob(alpha_image,length,chunk);
4432 (void) WriteBlobMSBULong(alpha_image,
4433 crc32(crc32(0,data,4),chunk,(uInt) length));
4434 }
4435
4436 if (length != 0)
4437 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4438
4439 continue;
4440 }
4441
4442 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4443 {
4444 /* Copy chunk data to alpha_image->blob */
4445
4446 if (alpha_image != NULL && image_info->ping == MagickFalse)
4447 {
4448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " Copying JDAA chunk data to alpha_blob.");
4451
4452 (void) WriteBlob(alpha_image,length,chunk);
4453 }
4454
4455 if (length != 0)
4456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4457
4458 continue;
4459 }
4460
4461 if (memcmp(type,mng_JSEP,4) == 0)
4462 {
4463 read_JSEP=MagickTrue;
4464
4465 if (length != 0)
4466 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4467
4468 continue;
4469 }
4470
4471 if (memcmp(type,mng_bKGD,4) == 0)
4472 {
4473 if (length == 2)
4474 {
4475 image->background_color.red=ScaleCharToQuantum(p[1]);
4476 image->background_color.green=image->background_color.red;
4477 image->background_color.blue=image->background_color.red;
4478 }
4479
4480 if (length == 6)
4481 {
4482 image->background_color.red=ScaleCharToQuantum(p[1]);
4483 image->background_color.green=ScaleCharToQuantum(p[3]);
4484 image->background_color.blue=ScaleCharToQuantum(p[5]);
4485 }
4486
4487 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4488 continue;
4489 }
4490
4491 if (memcmp(type,mng_gAMA,4) == 0)
4492 {
4493 if (length == 4)
4494 image->gamma=((float) mng_get_long(p))*0.00001;
4495
4496 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4497 continue;
4498 }
4499
4500 if (memcmp(type,mng_cHRM,4) == 0)
4501 {
4502 if (length == 32)
4503 {
4504 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4505 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4506 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4507 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4508 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4509 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4510 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4511 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4512 }
4513
4514 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4515 continue;
4516 }
4517
4518 if (memcmp(type,mng_sRGB,4) == 0)
4519 {
4520 if (length == 1)
4521 {
4522 image->rendering_intent=
4523 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4524 image->gamma=1.000f/2.200f;
4525 image->chromaticity.red_primary.x=0.6400f;
4526 image->chromaticity.red_primary.y=0.3300f;
4527 image->chromaticity.green_primary.x=0.3000f;
4528 image->chromaticity.green_primary.y=0.6000f;
4529 image->chromaticity.blue_primary.x=0.1500f;
4530 image->chromaticity.blue_primary.y=0.0600f;
4531 image->chromaticity.white_point.x=0.3127f;
4532 image->chromaticity.white_point.y=0.3290f;
4533 }
4534
4535 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4536 continue;
4537 }
4538
4539 if (memcmp(type,mng_oFFs,4) == 0)
4540 {
4541 if (length > 8)
4542 {
4543 image->page.x=(ssize_t) mng_get_long(p);
4544 image->page.y=(ssize_t) mng_get_long(&p[4]);
4545
4546 if ((int) p[8] != 0)
4547 {
4548 image->page.x/=10000;
4549 image->page.y/=10000;
4550 }
4551 }
4552
4553 if (length != 0)
4554 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4555
4556 continue;
4557 }
4558
4559 if (memcmp(type,mng_pHYs,4) == 0)
4560 {
4561 if (length > 8)
4562 {
4563 image->resolution.x=(double) mng_get_long(p);
4564 image->resolution.y=(double) mng_get_long(&p[4]);
4565 if ((int) p[8] == PNG_RESOLUTION_METER)
4566 {
4567 image->units=PixelsPerCentimeterResolution;
4568 image->resolution.x=image->resolution.x/100.0f;
4569 image->resolution.y=image->resolution.y/100.0f;
4570 }
4571 }
4572
4573 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4574 continue;
4575 }
4576
4577 #if 0
4578 if (memcmp(type,mng_iCCP,4) == 0)
4579 {
4580 /* To do: */
4581 if (length != 0)
4582 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4583
4584 continue;
4585 }
4586 #endif
4587
4588 if (length != 0)
4589 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4590
4591 if (memcmp(type,mng_IEND,4))
4592 continue;
4593
4594 break;
4595 }
4596
4597
4598 /* IEND found */
4599
4600 /*
4601 Finish up reading image data:
4602
4603 o read main image from color_blob.
4604
4605 o close color_blob.
4606
4607 o if (color_type has alpha)
4608 if alpha_encoding is PNG
4609 read secondary image from alpha_blob via ReadPNG
4610 if alpha_encoding is JPEG
4611 read secondary image from alpha_blob via ReadJPEG
4612
4613 o close alpha_blob.
4614
4615 o copy intensity of secondary image into
4616 alpha samples of main image.
4617
4618 o destroy the secondary image.
4619 */
4620
4621 if (color_image_info == (ImageInfo *) NULL)
4622 {
4623 assert(color_image == (Image *) NULL);
4624 assert(alpha_image == (Image *) NULL);
4625 return((Image *) NULL);
4626 }
4627
4628 if (color_image == (Image *) NULL)
4629 {
4630 assert(alpha_image == (Image *) NULL);
4631 return((Image *) NULL);
4632 }
4633
4634 (void) SeekBlob(color_image,0,SEEK_SET);
4635
4636 if (logging != MagickFalse)
4637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4638 " Reading jng_image from color_blob.");
4639
4640 assert(color_image_info != (ImageInfo *) NULL);
4641 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4642 color_image->filename);
4643
4644 color_image_info->ping=MagickFalse; /* To do: avoid this */
4645 jng_image=ReadImage(color_image_info,exception);
4646
4647 (void) RelinquishUniqueFileResource(color_image->filename);
4648 color_image=DestroyImage(color_image);
4649 color_image_info=DestroyImageInfo(color_image_info);
4650
4651 if (jng_image == (Image *) NULL)
4652 return((Image *) NULL);
4653
4654 if (logging != MagickFalse)
4655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4656 " Copying jng_image pixels to main image.");
4657
4658 image->rows=jng_height;
4659 image->columns=jng_width;
4660
4661 status=SetImageExtent(image,image->columns,image->rows,exception);
4662 if (status == MagickFalse)
4663 return(DestroyImageList(image));
4664
4665 for (y=0; y < (ssize_t) image->rows; y++)
4666 {
4667 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4668 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4669 for (x=(ssize_t) image->columns; x != 0; x--)
4670 {
4671 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4672 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4673 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4674 q+=GetPixelChannels(image);
4675 s+=GetPixelChannels(jng_image);
4676 }
4677
4678 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4679 break;
4680 }
4681
4682 jng_image=DestroyImage(jng_image);
4683
4684 if (image_info->ping == MagickFalse)
4685 {
4686 if (jng_color_type >= 12)
4687 {
4688 if (jng_alpha_compression_method == 0)
4689 {
4690 png_byte
4691 data[5];
4692 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4693 PNGType(data,mng_IEND);
4694 LogPNGChunk(logging,mng_IEND,0L);
4695 (void) WriteBlob(alpha_image,4,data);
4696 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4697 }
4698
4699 (void) CloseBlob(alpha_image);
4700
4701 if (logging != MagickFalse)
4702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4703 " Reading alpha from alpha_blob.");
4704
4705 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4706 "%s",alpha_image->filename);
4707
4708 jng_image=ReadImage(alpha_image_info,exception);
4709
4710 if (jng_image != (Image *) NULL)
4711 for (y=0; y < (ssize_t) image->rows; y++)
4712 {
4713 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4714 exception);
4715 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4716
4717 if (image->alpha_trait != UndefinedPixelTrait)
4718 for (x=(ssize_t) image->columns; x != 0; x--)
4719 {
4720 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4721 q+=GetPixelChannels(image);
4722 s+=GetPixelChannels(jng_image);
4723 }
4724
4725 else
4726 for (x=(ssize_t) image->columns; x != 0; x--)
4727 {
4728 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4729 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4730 image->alpha_trait=BlendPixelTrait;
4731 q+=GetPixelChannels(image);
4732 s+=GetPixelChannels(jng_image);
4733 }
4734
4735 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4736 break;
4737 }
4738 (void) RelinquishUniqueFileResource(alpha_image->filename);
4739 alpha_image=DestroyImage(alpha_image);
4740 alpha_image_info=DestroyImageInfo(alpha_image_info);
4741 if (jng_image != (Image *) NULL)
4742 jng_image=DestroyImage(jng_image);
4743 }
4744 }
4745
4746 /* Read the JNG image. */
4747
4748 if (mng_info->mng_type == 0)
4749 {
4750 mng_info->mng_width=jng_width;
4751 mng_info->mng_height=jng_height;
4752 }
4753
4754 if (image->page.width == 0 && image->page.height == 0)
4755 {
4756 image->page.width=jng_width;
4757 image->page.height=jng_height;
4758 }
4759
4760 if (image->page.x == 0 && image->page.y == 0)
4761 {
4762 image->page.x=mng_info->x_off[mng_info->object_id];
4763 image->page.y=mng_info->y_off[mng_info->object_id];
4764 }
4765
4766 else
4767 {
4768 image->page.y=mng_info->y_off[mng_info->object_id];
4769 }
4770
4771 mng_info->image_found++;
4772 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4773 2*GetBlobSize(image));
4774
4775 if (status == MagickFalse)
4776 return((Image *) NULL);
4777
4778 if (logging != MagickFalse)
4779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4780 " exit ReadOneJNGImage()");
4781
4782 return(image);
4783 }
4784
4785 /*
4786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4787 % %
4788 % %
4789 % %
4790 % R e a d J N G I m a g e %
4791 % %
4792 % %
4793 % %
4794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4795 %
4796 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4797 % (including the 8-byte signature) and returns it. It allocates the memory
4798 % necessary for the new Image structure and returns a pointer to the new
4799 % image.
4800 %
4801 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4802 %
4803 % The format of the ReadJNGImage method is:
4804 %
4805 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4806 % *exception)
4807 %
4808 % A description of each parameter follows:
4809 %
4810 % o image_info: the image info.
4811 %
4812 % o exception: return any errors or warnings in this structure.
4813 %
4814 */
4815
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4816 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4817 {
4818 Image
4819 *image;
4820
4821 MagickBooleanType
4822 have_mng_structure,
4823 logging,
4824 status;
4825
4826 MngInfo
4827 *mng_info;
4828
4829 char
4830 magic_number[MagickPathExtent];
4831
4832 size_t
4833 count;
4834
4835 /*
4836 Open image file.
4837 */
4838 assert(image_info != (const ImageInfo *) NULL);
4839 assert(image_info->signature == MagickCoreSignature);
4840 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4841 assert(exception != (ExceptionInfo *) NULL);
4842 assert(exception->signature == MagickCoreSignature);
4843 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4844 image=AcquireImage(image_info,exception);
4845 mng_info=(MngInfo *) NULL;
4846 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4847
4848 if (status == MagickFalse)
4849 return((Image *) NULL);
4850
4851 if (LocaleCompare(image_info->magick,"JNG") != 0)
4852 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4853
4854 /* Verify JNG signature. */
4855
4856 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4857
4858 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4859 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4860
4861 /* Allocate a MngInfo structure. */
4862
4863 have_mng_structure=MagickFalse;
4864 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4865
4866 if (mng_info == (MngInfo *) NULL)
4867 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4868
4869 /* Initialize members of the MngInfo structure. */
4870
4871 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4872 have_mng_structure=MagickTrue;
4873
4874 mng_info->image=image;
4875 image=ReadOneJNGImage(mng_info,image_info,exception);
4876 MngInfoFreeStruct(mng_info,&have_mng_structure);
4877
4878 if (image == (Image *) NULL)
4879 {
4880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882 "exit ReadJNGImage() with error");
4883
4884 return((Image *) NULL);
4885 }
4886 (void) CloseBlob(image);
4887
4888 if (image->columns == 0 || image->rows == 0)
4889 {
4890 if (logging != MagickFalse)
4891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4892 "exit ReadJNGImage() with error");
4893
4894 ThrowReaderException(CorruptImageError,"CorruptImage");
4895 }
4896
4897 if (logging != MagickFalse)
4898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4899
4900 return(image);
4901 }
4902 #endif
4903
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4904 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4905 {
4906 char
4907 page_geometry[MagickPathExtent];
4908
4909 Image
4910 *image;
4911
4912 MagickBooleanType
4913 logging,
4914 have_mng_structure;
4915
4916 volatile int
4917 first_mng_object,
4918 object_id,
4919 term_chunk_found,
4920 skip_to_iend;
4921
4922 volatile ssize_t
4923 image_count=0;
4924
4925 MagickBooleanType
4926 status;
4927
4928 MagickOffsetType
4929 offset;
4930
4931 MngInfo
4932 *mng_info;
4933
4934 MngBox
4935 default_fb,
4936 fb,
4937 previous_fb;
4938
4939 #if defined(MNG_INSERT_LAYERS)
4940 PixelInfo
4941 mng_background_color;
4942 #endif
4943
4944 register unsigned char
4945 *p;
4946
4947 register ssize_t
4948 i;
4949
4950 size_t
4951 count;
4952
4953 ssize_t
4954 loop_level;
4955
4956 volatile short
4957 skipping_loop;
4958
4959 #if defined(MNG_INSERT_LAYERS)
4960 unsigned int
4961 mandatory_back=0;
4962 #endif
4963
4964 volatile unsigned int
4965 #ifdef MNG_OBJECT_BUFFERS
4966 mng_background_object=0,
4967 #endif
4968 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4969
4970 size_t
4971 default_frame_timeout,
4972 frame_timeout,
4973 #if defined(MNG_INSERT_LAYERS)
4974 image_height,
4975 image_width,
4976 #endif
4977 length;
4978
4979 /* These delays are all measured in image ticks_per_second,
4980 * not in MNG ticks_per_second
4981 */
4982 volatile size_t
4983 default_frame_delay,
4984 final_delay,
4985 final_image_delay,
4986 frame_delay,
4987 #if defined(MNG_INSERT_LAYERS)
4988 insert_layers,
4989 #endif
4990 mng_iterations=1,
4991 simplicity=0,
4992 subframe_height=0,
4993 subframe_width=0;
4994
4995 previous_fb.top=0;
4996 previous_fb.bottom=0;
4997 previous_fb.left=0;
4998 previous_fb.right=0;
4999 default_fb.top=0;
5000 default_fb.bottom=0;
5001 default_fb.left=0;
5002 default_fb.right=0;
5003
5004 /* Open image file. */
5005
5006 assert(image_info != (const ImageInfo *) NULL);
5007 assert(image_info->signature == MagickCoreSignature);
5008 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5009 assert(exception != (ExceptionInfo *) NULL);
5010 assert(exception->signature == MagickCoreSignature);
5011 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5012 image=AcquireImage(image_info,exception);
5013 mng_info=(MngInfo *) NULL;
5014 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5015
5016 if (status == MagickFalse)
5017 return((Image *) NULL);
5018
5019 first_mng_object=MagickFalse;
5020 skipping_loop=(-1);
5021 have_mng_structure=MagickFalse;
5022
5023 /* Allocate a MngInfo structure. */
5024
5025 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5026
5027 if (mng_info == (MngInfo *) NULL)
5028 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5029
5030 /* Initialize members of the MngInfo structure. */
5031
5032 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5033 mng_info->image=image;
5034 have_mng_structure=MagickTrue;
5035
5036 if (LocaleCompare(image_info->magick,"MNG") == 0)
5037 {
5038 char
5039 magic_number[MagickPathExtent];
5040
5041 /* Verify MNG signature. */
5042 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5043 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5044 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5045
5046 /* Initialize some nonzero members of the MngInfo structure. */
5047 for (i=0; i < MNG_MAX_OBJECTS; i++)
5048 {
5049 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5050 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5051 }
5052 mng_info->exists[0]=MagickTrue;
5053 }
5054
5055 first_mng_object=MagickTrue;
5056 mng_type=0;
5057 #if defined(MNG_INSERT_LAYERS)
5058 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5059 #endif
5060 default_frame_delay=0;
5061 default_frame_timeout=0;
5062 frame_delay=0;
5063 final_delay=1;
5064 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5065 object_id=0;
5066 skip_to_iend=MagickFalse;
5067 term_chunk_found=MagickFalse;
5068 mng_info->framing_mode=1;
5069 #if defined(MNG_INSERT_LAYERS)
5070 mandatory_back=MagickFalse;
5071 #endif
5072 #if defined(MNG_INSERT_LAYERS)
5073 mng_background_color=image->background_color;
5074 #endif
5075 default_fb=mng_info->frame;
5076 previous_fb=mng_info->frame;
5077 do
5078 {
5079 char
5080 type[MagickPathExtent];
5081
5082 if (LocaleCompare(image_info->magick,"MNG") == 0)
5083 {
5084 unsigned char
5085 *chunk;
5086
5087 /*
5088 Read a new chunk.
5089 */
5090 type[0]='\0';
5091 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5092 length=ReadBlobMSBLong(image);
5093 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5094
5095 if (logging != MagickFalse)
5096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5098 type[0],type[1],type[2],type[3],(double) length);
5099
5100 if (length > PNG_UINT_31_MAX)
5101 {
5102 status=MagickFalse;
5103 break;
5104 }
5105
5106 if (count == 0)
5107 ThrowReaderException(CorruptImageError,"CorruptImage");
5108
5109 p=NULL;
5110 chunk=(unsigned char *) NULL;
5111
5112 if (length != 0)
5113 {
5114 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5115
5116 if (chunk == (unsigned char *) NULL)
5117 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5118
5119 for (i=0; i < (ssize_t) length; i++)
5120 chunk[i]=(unsigned char) ReadBlobByte(image);
5121
5122 p=chunk;
5123 }
5124
5125 (void) ReadBlobMSBLong(image); /* read crc word */
5126
5127 #if !defined(JNG_SUPPORTED)
5128 if (memcmp(type,mng_JHDR,4) == 0)
5129 {
5130 skip_to_iend=MagickTrue;
5131
5132 if (mng_info->jhdr_warning == 0)
5133 (void) ThrowMagickException(exception,GetMagickModule(),
5134 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5135
5136 mng_info->jhdr_warning++;
5137 }
5138 #endif
5139 if (memcmp(type,mng_DHDR,4) == 0)
5140 {
5141 skip_to_iend=MagickTrue;
5142
5143 if (mng_info->dhdr_warning == 0)
5144 (void) ThrowMagickException(exception,GetMagickModule(),
5145 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5146
5147 mng_info->dhdr_warning++;
5148 }
5149 if (memcmp(type,mng_MEND,4) == 0)
5150 break;
5151
5152 if (skip_to_iend)
5153 {
5154 if (memcmp(type,mng_IEND,4) == 0)
5155 skip_to_iend=MagickFalse;
5156
5157 if (length != 0)
5158 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5159
5160 if (logging != MagickFalse)
5161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5162 " Skip to IEND.");
5163
5164 continue;
5165 }
5166
5167 if (memcmp(type,mng_MHDR,4) == 0)
5168 {
5169 if (length != 28)
5170 {
5171 if (chunk)
5172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5173 ThrowReaderException(CorruptImageError,"CorruptImage");
5174 }
5175
5176 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5177 (p[2] << 8) | p[3]);
5178
5179 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5180 (p[6] << 8) | p[7]);
5181
5182 if (logging != MagickFalse)
5183 {
5184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5185 " MNG width: %.20g",(double) mng_info->mng_width);
5186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5187 " MNG height: %.20g",(double) mng_info->mng_height);
5188 }
5189
5190 p+=8;
5191 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5192
5193 if (mng_info->ticks_per_second == 0)
5194 default_frame_delay=0;
5195
5196 else
5197 default_frame_delay=1UL*image->ticks_per_second/
5198 mng_info->ticks_per_second;
5199
5200 frame_delay=default_frame_delay;
5201 simplicity=0;
5202
5203 p+=16;
5204 simplicity=(size_t) mng_get_long(p);
5205
5206 mng_type=1; /* Full MNG */
5207
5208 if ((simplicity != 0) && ((simplicity | 11) == 11))
5209 mng_type=2; /* LC */
5210
5211 if ((simplicity != 0) && ((simplicity | 9) == 9))
5212 mng_type=3; /* VLC */
5213
5214 #if defined(MNG_INSERT_LAYERS)
5215 if (mng_type != 3)
5216 insert_layers=MagickTrue;
5217 #endif
5218 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5219 {
5220 /* Allocate next image structure. */
5221 AcquireNextImage(image_info,image,exception);
5222
5223 if (GetNextImageInList(image) == (Image *) NULL)
5224 return((Image *) NULL);
5225
5226 image=SyncNextImageInList(image);
5227 mng_info->image=image;
5228 }
5229
5230 if ((mng_info->mng_width > 65535L) ||
5231 (mng_info->mng_height > 65535L))
5232 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5233
5234 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5235 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5236 mng_info->mng_height);
5237
5238 mng_info->frame.left=0;
5239 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5240 mng_info->frame.top=0;
5241 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5242 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5243
5244 for (i=0; i < MNG_MAX_OBJECTS; i++)
5245 mng_info->object_clip[i]=mng_info->frame;
5246
5247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5248 continue;
5249 }
5250
5251 if (memcmp(type,mng_TERM,4) == 0)
5252 {
5253 int
5254 repeat=0;
5255
5256 if (length != 0)
5257 repeat=p[0];
5258
5259 if (repeat == 3)
5260 {
5261 final_delay=(png_uint_32) mng_get_long(&p[2]);
5262 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5263
5264 if (mng_iterations == PNG_UINT_31_MAX)
5265 mng_iterations=0;
5266
5267 image->iterations=mng_iterations;
5268 term_chunk_found=MagickTrue;
5269 }
5270
5271 if (logging != MagickFalse)
5272 {
5273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5274 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5275 repeat,(double) final_delay, (double) image->iterations);
5276 }
5277
5278 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5279 continue;
5280 }
5281 if (memcmp(type,mng_DEFI,4) == 0)
5282 {
5283 if (mng_type == 3)
5284 (void) ThrowMagickException(exception,GetMagickModule(),
5285 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5286 image->filename);
5287
5288 if (length < 2)
5289 {
5290 if (chunk)
5291 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5292 ThrowReaderException(CorruptImageError,"CorruptImage");
5293 }
5294
5295 object_id=(p[0] << 8) | p[1];
5296
5297 if (mng_type == 2 && object_id != 0)
5298 (void) ThrowMagickException(exception,GetMagickModule(),
5299 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5300 image->filename);
5301
5302 if (object_id > MNG_MAX_OBJECTS)
5303 {
5304 /*
5305 Instead of using a warning we should allocate a larger
5306 MngInfo structure and continue.
5307 */
5308 (void) ThrowMagickException(exception,GetMagickModule(),
5309 CoderError,"object id too large","`%s'",image->filename);
5310 object_id=MNG_MAX_OBJECTS;
5311 }
5312
5313 if (mng_info->exists[object_id])
5314 if (mng_info->frozen[object_id])
5315 {
5316 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5317 (void) ThrowMagickException(exception,
5318 GetMagickModule(),CoderError,
5319 "DEFI cannot redefine a frozen MNG object","`%s'",
5320 image->filename);
5321 continue;
5322 }
5323
5324 mng_info->exists[object_id]=MagickTrue;
5325
5326 if (length > 2)
5327 mng_info->invisible[object_id]=p[2];
5328
5329 /*
5330 Extract object offset info.
5331 */
5332 if (length > 11)
5333 {
5334 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5335 (p[5] << 16) | (p[6] << 8) | p[7]);
5336
5337 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5338 (p[9] << 16) | (p[10] << 8) | p[11]);
5339
5340 if (logging != MagickFalse)
5341 {
5342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5343 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5344 object_id,(double) mng_info->x_off[object_id],
5345 object_id,(double) mng_info->y_off[object_id]);
5346 }
5347 }
5348
5349 /*
5350 Extract object clipping info.
5351 */
5352 if (length > 27)
5353 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5354 &p[12]);
5355
5356 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5357 continue;
5358 }
5359 if (memcmp(type,mng_bKGD,4) == 0)
5360 {
5361 mng_info->have_global_bkgd=MagickFalse;
5362
5363 if (length > 5)
5364 {
5365 mng_info->mng_global_bkgd.red=
5366 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5367
5368 mng_info->mng_global_bkgd.green=
5369 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5370
5371 mng_info->mng_global_bkgd.blue=
5372 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5373
5374 mng_info->have_global_bkgd=MagickTrue;
5375 }
5376
5377 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5378 continue;
5379 }
5380 if (memcmp(type,mng_BACK,4) == 0)
5381 {
5382 #if defined(MNG_INSERT_LAYERS)
5383 if (length > 6)
5384 mandatory_back=p[6];
5385
5386 else
5387 mandatory_back=0;
5388
5389 if (mandatory_back && length > 5)
5390 {
5391 mng_background_color.red=
5392 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5393
5394 mng_background_color.green=
5395 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5396
5397 mng_background_color.blue=
5398 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5399
5400 mng_background_color.alpha=OpaqueAlpha;
5401 }
5402
5403 #ifdef MNG_OBJECT_BUFFERS
5404 if (length > 8)
5405 mng_background_object=(p[7] << 8) | p[8];
5406 #endif
5407 #endif
5408 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5409 continue;
5410 }
5411
5412 if (memcmp(type,mng_PLTE,4) == 0)
5413 {
5414 /* Read global PLTE. */
5415
5416 if (length && (length < 769))
5417 {
5418 if (mng_info->global_plte == (png_colorp) NULL)
5419 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5420 sizeof(*mng_info->global_plte));
5421
5422 for (i=0; i < (ssize_t) (length/3); i++)
5423 {
5424 mng_info->global_plte[i].red=p[3*i];
5425 mng_info->global_plte[i].green=p[3*i+1];
5426 mng_info->global_plte[i].blue=p[3*i+2];
5427 }
5428
5429 mng_info->global_plte_length=(unsigned int) (length/3);
5430 }
5431 #ifdef MNG_LOOSE
5432 for ( ; i < 256; i++)
5433 {
5434 mng_info->global_plte[i].red=i;
5435 mng_info->global_plte[i].green=i;
5436 mng_info->global_plte[i].blue=i;
5437 }
5438
5439 if (length != 0)
5440 mng_info->global_plte_length=256;
5441 #endif
5442 else
5443 mng_info->global_plte_length=0;
5444
5445 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5446 continue;
5447 }
5448
5449 if (memcmp(type,mng_tRNS,4) == 0)
5450 {
5451 /* read global tRNS */
5452
5453 if (length > 0 && length < 257)
5454 for (i=0; i < (ssize_t) length; i++)
5455 mng_info->global_trns[i]=p[i];
5456
5457 #ifdef MNG_LOOSE
5458 for ( ; i < 256; i++)
5459 mng_info->global_trns[i]=255;
5460 #endif
5461 mng_info->global_trns_length=(unsigned int) length;
5462 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5463 continue;
5464 }
5465 if (memcmp(type,mng_gAMA,4) == 0)
5466 {
5467 if (length == 4)
5468 {
5469 ssize_t
5470 igamma;
5471
5472 igamma=mng_get_long(p);
5473 mng_info->global_gamma=((float) igamma)*0.00001;
5474 mng_info->have_global_gama=MagickTrue;
5475 }
5476
5477 else
5478 mng_info->have_global_gama=MagickFalse;
5479
5480 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5481 continue;
5482 }
5483
5484 if (memcmp(type,mng_cHRM,4) == 0)
5485 {
5486 /* Read global cHRM */
5487
5488 if (length == 32)
5489 {
5490 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5491 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5492 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5493 mng_info->global_chrm.red_primary.y=0.00001*
5494 mng_get_long(&p[12]);
5495 mng_info->global_chrm.green_primary.x=0.00001*
5496 mng_get_long(&p[16]);
5497 mng_info->global_chrm.green_primary.y=0.00001*
5498 mng_get_long(&p[20]);
5499 mng_info->global_chrm.blue_primary.x=0.00001*
5500 mng_get_long(&p[24]);
5501 mng_info->global_chrm.blue_primary.y=0.00001*
5502 mng_get_long(&p[28]);
5503 mng_info->have_global_chrm=MagickTrue;
5504 }
5505 else
5506 mng_info->have_global_chrm=MagickFalse;
5507
5508 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5509 continue;
5510 }
5511
5512 if (memcmp(type,mng_sRGB,4) == 0)
5513 {
5514 /*
5515 Read global sRGB.
5516 */
5517 if (length != 0)
5518 {
5519 mng_info->global_srgb_intent=
5520 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5521 mng_info->have_global_srgb=MagickTrue;
5522 }
5523 else
5524 mng_info->have_global_srgb=MagickFalse;
5525
5526 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5527 continue;
5528 }
5529
5530 if (memcmp(type,mng_iCCP,4) == 0)
5531 {
5532 /* To do: */
5533
5534 /*
5535 Read global iCCP.
5536 */
5537 if (length != 0)
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539
5540 continue;
5541 }
5542
5543 if (memcmp(type,mng_FRAM,4) == 0)
5544 {
5545 if (mng_type == 3)
5546 (void) ThrowMagickException(exception,GetMagickModule(),
5547 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5548 image->filename);
5549
5550 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5551 image->delay=frame_delay;
5552
5553 frame_delay=default_frame_delay;
5554 frame_timeout=default_frame_timeout;
5555 fb=default_fb;
5556
5557 if (length != 0)
5558 if (p[0])
5559 mng_info->framing_mode=p[0];
5560
5561 if (logging != MagickFalse)
5562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5563 " Framing_mode=%d",mng_info->framing_mode);
5564
5565 if (length > 6)
5566 {
5567 /* Note the delay and frame clipping boundaries. */
5568
5569 p++; /* framing mode */
5570
5571 while (*p && ((p-chunk) < (ssize_t) length))
5572 p++; /* frame name */
5573
5574 p++; /* frame name terminator */
5575
5576 if ((p-chunk) < (ssize_t) (length-4))
5577 {
5578 int
5579 change_delay,
5580 change_timeout,
5581 change_clipping;
5582
5583 change_delay=(*p++);
5584 change_timeout=(*p++);
5585 change_clipping=(*p++);
5586 p++; /* change_sync */
5587
5588 if (change_delay)
5589 {
5590 frame_delay=1UL*image->ticks_per_second*
5591 mng_get_long(p);
5592
5593 if (mng_info->ticks_per_second != 0)
5594 frame_delay/=mng_info->ticks_per_second;
5595
5596 else
5597 frame_delay=PNG_UINT_31_MAX;
5598
5599 if (change_delay == 2)
5600 default_frame_delay=frame_delay;
5601
5602 p+=4;
5603
5604 if (logging != MagickFalse)
5605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5606 " Framing_delay=%.20g",(double) frame_delay);
5607 }
5608
5609 if (change_timeout)
5610 {
5611 frame_timeout=1UL*image->ticks_per_second*
5612 mng_get_long(p);
5613
5614 if (mng_info->ticks_per_second != 0)
5615 frame_timeout/=mng_info->ticks_per_second;
5616
5617 else
5618 frame_timeout=PNG_UINT_31_MAX;
5619
5620 if (change_timeout == 2)
5621 default_frame_timeout=frame_timeout;
5622
5623 p+=4;
5624
5625 if (logging != MagickFalse)
5626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5627 " Framing_timeout=%.20g",(double) frame_timeout);
5628 }
5629
5630 if (change_clipping)
5631 {
5632 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5633 p+=17;
5634 previous_fb=fb;
5635
5636 if (logging != MagickFalse)
5637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5638 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5639 (double) fb.left,(double) fb.right,(double) fb.top,
5640 (double) fb.bottom);
5641
5642 if (change_clipping == 2)
5643 default_fb=fb;
5644 }
5645 }
5646 }
5647 mng_info->clip=fb;
5648 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5649
5650 subframe_width=(size_t) (mng_info->clip.right
5651 -mng_info->clip.left);
5652
5653 subframe_height=(size_t) (mng_info->clip.bottom
5654 -mng_info->clip.top);
5655 /*
5656 Insert a background layer behind the frame if framing_mode is 4.
5657 */
5658 #if defined(MNG_INSERT_LAYERS)
5659 if (logging != MagickFalse)
5660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5661 " subframe_width=%.20g, subframe_height=%.20g",(double)
5662 subframe_width,(double) subframe_height);
5663
5664 if (insert_layers && (mng_info->framing_mode == 4) &&
5665 (subframe_width) && (subframe_height))
5666 {
5667 /* Allocate next image structure. */
5668 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5669 {
5670 AcquireNextImage(image_info,image,exception);
5671
5672 if (GetNextImageInList(image) == (Image *) NULL)
5673 {
5674 image=DestroyImageList(image);
5675 MngInfoFreeStruct(mng_info,&have_mng_structure);
5676 return((Image *) NULL);
5677 }
5678
5679 image=SyncNextImageInList(image);
5680 }
5681
5682 mng_info->image=image;
5683
5684 if (term_chunk_found)
5685 {
5686 image->start_loop=MagickTrue;
5687 image->iterations=mng_iterations;
5688 term_chunk_found=MagickFalse;
5689 }
5690
5691 else
5692 image->start_loop=MagickFalse;
5693
5694 image->columns=subframe_width;
5695 image->rows=subframe_height;
5696 image->page.width=subframe_width;
5697 image->page.height=subframe_height;
5698 image->page.x=mng_info->clip.left;
5699 image->page.y=mng_info->clip.top;
5700 image->background_color=mng_background_color;
5701 image->alpha_trait=UndefinedPixelTrait;
5702 image->delay=0;
5703 (void) SetImageBackgroundColor(image,exception);
5704
5705 if (logging != MagickFalse)
5706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5707 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5708 (double) mng_info->clip.left,(double) mng_info->clip.right,
5709 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5710 }
5711 #endif
5712 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5713 continue;
5714 }
5715
5716 if (memcmp(type,mng_CLIP,4) == 0)
5717 {
5718 unsigned int
5719 first_object,
5720 last_object;
5721
5722 /*
5723 Read CLIP.
5724 */
5725 if (length > 3)
5726 {
5727 first_object=(p[0] << 8) | p[1];
5728 last_object=(p[2] << 8) | p[3];
5729 p+=4;
5730
5731 for (i=(int) first_object; i <= (int) last_object; i++)
5732 {
5733 if (mng_info->exists[i] && !mng_info->frozen[i])
5734 {
5735 MngBox
5736 box;
5737
5738 box=mng_info->object_clip[i];
5739 if ((p-chunk) < (ssize_t) (length-17))
5740 mng_info->object_clip[i]=
5741 mng_read_box(box,(char) p[0],&p[1]);
5742 }
5743 }
5744
5745 }
5746 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5747 continue;
5748 }
5749
5750 if (memcmp(type,mng_SAVE,4) == 0)
5751 {
5752 for (i=1; i < MNG_MAX_OBJECTS; i++)
5753 if (mng_info->exists[i])
5754 {
5755 mng_info->frozen[i]=MagickTrue;
5756 #ifdef MNG_OBJECT_BUFFERS
5757 if (mng_info->ob[i] != (MngBuffer *) NULL)
5758 mng_info->ob[i]->frozen=MagickTrue;
5759 #endif
5760 }
5761
5762 if (length != 0)
5763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5764
5765 continue;
5766 }
5767
5768 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5769 {
5770 /* Read DISC or SEEK. */
5771
5772 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5773 {
5774 for (i=1; i < MNG_MAX_OBJECTS; i++)
5775 MngInfoDiscardObject(mng_info,i);
5776 }
5777
5778 else
5779 {
5780 register ssize_t
5781 j;
5782
5783 for (j=1; j < (ssize_t) length; j+=2)
5784 {
5785 i=p[j-1] << 8 | p[j];
5786 MngInfoDiscardObject(mng_info,i);
5787 }
5788 }
5789
5790 if (length != 0)
5791 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5792
5793 continue;
5794 }
5795
5796 if (memcmp(type,mng_MOVE,4) == 0)
5797 {
5798 size_t
5799 first_object,
5800 last_object;
5801
5802 /* read MOVE */
5803
5804 if (length > 3)
5805 {
5806 first_object=(p[0] << 8) | p[1];
5807 last_object=(p[2] << 8) | p[3];
5808 p+=4;
5809
5810 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5811 {
5812 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5813 (p-chunk) < (ssize_t) (length-8))
5814 {
5815 MngPair
5816 new_pair;
5817
5818 MngPair
5819 old_pair;
5820
5821 old_pair.a=mng_info->x_off[i];
5822 old_pair.b=mng_info->y_off[i];
5823 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5824 mng_info->x_off[i]=new_pair.a;
5825 mng_info->y_off[i]=new_pair.b;
5826 }
5827 }
5828 }
5829
5830 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5831 continue;
5832 }
5833
5834 if (memcmp(type,mng_LOOP,4) == 0)
5835 {
5836 ssize_t loop_iters=1;
5837 if (length > 4)
5838 {
5839 loop_level=chunk[0];
5840 mng_info->loop_active[loop_level]=1; /* mark loop active */
5841
5842 /* Record starting point. */
5843 loop_iters=mng_get_long(&chunk[1]);
5844
5845 if (logging != MagickFalse)
5846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5847 " LOOP level %.20g has %.20g iterations ",
5848 (double) loop_level, (double) loop_iters);
5849
5850 if (loop_iters == 0)
5851 skipping_loop=loop_level;
5852
5853 else
5854 {
5855 mng_info->loop_jump[loop_level]=TellBlob(image);
5856 mng_info->loop_count[loop_level]=loop_iters;
5857 }
5858
5859 mng_info->loop_iteration[loop_level]=0;
5860 }
5861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5862 continue;
5863 }
5864
5865 if (memcmp(type,mng_ENDL,4) == 0)
5866 {
5867 if (length > 0)
5868 {
5869 loop_level=chunk[0];
5870
5871 if (skipping_loop > 0)
5872 {
5873 if (skipping_loop == loop_level)
5874 {
5875 /*
5876 Found end of zero-iteration loop.
5877 */
5878 skipping_loop=(-1);
5879 mng_info->loop_active[loop_level]=0;
5880 }
5881 }
5882
5883 else
5884 {
5885 if (mng_info->loop_active[loop_level] == 1)
5886 {
5887 mng_info->loop_count[loop_level]--;
5888 mng_info->loop_iteration[loop_level]++;
5889
5890 if (logging != MagickFalse)
5891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5892 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5893 (double) loop_level,(double)
5894 mng_info->loop_count[loop_level]);
5895
5896 if (mng_info->loop_count[loop_level] != 0)
5897 {
5898 offset=
5899 SeekBlob(image,mng_info->loop_jump[loop_level],
5900 SEEK_SET);
5901
5902 if (offset < 0)
5903 ThrowReaderException(CorruptImageError,
5904 "ImproperImageHeader");
5905 }
5906
5907 else
5908 {
5909 short
5910 last_level;
5911
5912 /*
5913 Finished loop.
5914 */
5915 mng_info->loop_active[loop_level]=0;
5916 last_level=(-1);
5917 for (i=0; i < loop_level; i++)
5918 if (mng_info->loop_active[i] == 1)
5919 last_level=(short) i;
5920 loop_level=last_level;
5921 }
5922 }
5923 }
5924 }
5925
5926 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5927 continue;
5928 }
5929
5930 if (memcmp(type,mng_CLON,4) == 0)
5931 {
5932 if (mng_info->clon_warning == 0)
5933 (void) ThrowMagickException(exception,GetMagickModule(),
5934 CoderError,"CLON is not implemented yet","`%s'",
5935 image->filename);
5936
5937 mng_info->clon_warning++;
5938 }
5939
5940 if (memcmp(type,mng_MAGN,4) == 0)
5941 {
5942 png_uint_16
5943 magn_first,
5944 magn_last,
5945 magn_mb,
5946 magn_ml,
5947 magn_mr,
5948 magn_mt,
5949 magn_mx,
5950 magn_my,
5951 magn_methx,
5952 magn_methy;
5953
5954 if (length > 1)
5955 magn_first=(p[0] << 8) | p[1];
5956
5957 else
5958 magn_first=0;
5959
5960 if (length > 3)
5961 magn_last=(p[2] << 8) | p[3];
5962
5963 else
5964 magn_last=magn_first;
5965 #ifndef MNG_OBJECT_BUFFERS
5966 if (magn_first || magn_last)
5967 if (mng_info->magn_warning == 0)
5968 {
5969 (void) ThrowMagickException(exception,
5970 GetMagickModule(),CoderError,
5971 "MAGN is not implemented yet for nonzero objects",
5972 "`%s'",image->filename);
5973
5974 mng_info->magn_warning++;
5975 }
5976 #endif
5977 if (length > 4)
5978 magn_methx=p[4];
5979
5980 else
5981 magn_methx=0;
5982
5983 if (length > 6)
5984 magn_mx=(p[5] << 8) | p[6];
5985
5986 else
5987 magn_mx=1;
5988
5989 if (magn_mx == 0)
5990 magn_mx=1;
5991
5992 if (length > 8)
5993 magn_my=(p[7] << 8) | p[8];
5994
5995 else
5996 magn_my=magn_mx;
5997
5998 if (magn_my == 0)
5999 magn_my=1;
6000
6001 if (length > 10)
6002 magn_ml=(p[9] << 8) | p[10];
6003
6004 else
6005 magn_ml=magn_mx;
6006
6007 if (magn_ml == 0)
6008 magn_ml=1;
6009
6010 if (length > 12)
6011 magn_mr=(p[11] << 8) | p[12];
6012
6013 else
6014 magn_mr=magn_mx;
6015
6016 if (magn_mr == 0)
6017 magn_mr=1;
6018
6019 if (length > 14)
6020 magn_mt=(p[13] << 8) | p[14];
6021
6022 else
6023 magn_mt=magn_my;
6024
6025 if (magn_mt == 0)
6026 magn_mt=1;
6027
6028 if (length > 16)
6029 magn_mb=(p[15] << 8) | p[16];
6030
6031 else
6032 magn_mb=magn_my;
6033
6034 if (magn_mb == 0)
6035 magn_mb=1;
6036
6037 if (length > 17)
6038 magn_methy=p[17];
6039
6040 else
6041 magn_methy=magn_methx;
6042
6043
6044 if (magn_methx > 5 || magn_methy > 5)
6045 if (mng_info->magn_warning == 0)
6046 {
6047 (void) ThrowMagickException(exception,
6048 GetMagickModule(),CoderError,
6049 "Unknown MAGN method in MNG datastream","`%s'",
6050 image->filename);
6051
6052 mng_info->magn_warning++;
6053 }
6054 #ifdef MNG_OBJECT_BUFFERS
6055 /* Magnify existing objects in the range magn_first to magn_last */
6056 #endif
6057 if (magn_first == 0 || magn_last == 0)
6058 {
6059 /* Save the magnification factors for object 0 */
6060 mng_info->magn_mb=magn_mb;
6061 mng_info->magn_ml=magn_ml;
6062 mng_info->magn_mr=magn_mr;
6063 mng_info->magn_mt=magn_mt;
6064 mng_info->magn_mx=magn_mx;
6065 mng_info->magn_my=magn_my;
6066 mng_info->magn_methx=magn_methx;
6067 mng_info->magn_methy=magn_methy;
6068 }
6069 }
6070
6071 if (memcmp(type,mng_PAST,4) == 0)
6072 {
6073 if (mng_info->past_warning == 0)
6074 (void) ThrowMagickException(exception,GetMagickModule(),
6075 CoderError,"PAST is not implemented yet","`%s'",
6076 image->filename);
6077
6078 mng_info->past_warning++;
6079 }
6080
6081 if (memcmp(type,mng_SHOW,4) == 0)
6082 {
6083 if (mng_info->show_warning == 0)
6084 (void) ThrowMagickException(exception,GetMagickModule(),
6085 CoderError,"SHOW is not implemented yet","`%s'",
6086 image->filename);
6087
6088 mng_info->show_warning++;
6089 }
6090
6091 if (memcmp(type,mng_sBIT,4) == 0)
6092 {
6093 if (length < 4)
6094 mng_info->have_global_sbit=MagickFalse;
6095
6096 else
6097 {
6098 mng_info->global_sbit.gray=p[0];
6099 mng_info->global_sbit.red=p[0];
6100 mng_info->global_sbit.green=p[1];
6101 mng_info->global_sbit.blue=p[2];
6102 mng_info->global_sbit.alpha=p[3];
6103 mng_info->have_global_sbit=MagickTrue;
6104 }
6105 }
6106 if (memcmp(type,mng_pHYs,4) == 0)
6107 {
6108 if (length > 8)
6109 {
6110 mng_info->global_x_pixels_per_unit=
6111 (size_t) mng_get_long(p);
6112 mng_info->global_y_pixels_per_unit=
6113 (size_t) mng_get_long(&p[4]);
6114 mng_info->global_phys_unit_type=p[8];
6115 mng_info->have_global_phys=MagickTrue;
6116 }
6117
6118 else
6119 mng_info->have_global_phys=MagickFalse;
6120 }
6121 if (memcmp(type,mng_pHYg,4) == 0)
6122 {
6123 if (mng_info->phyg_warning == 0)
6124 (void) ThrowMagickException(exception,GetMagickModule(),
6125 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6126
6127 mng_info->phyg_warning++;
6128 }
6129 if (memcmp(type,mng_BASI,4) == 0)
6130 {
6131 skip_to_iend=MagickTrue;
6132
6133 if (mng_info->basi_warning == 0)
6134 (void) ThrowMagickException(exception,GetMagickModule(),
6135 CoderError,"BASI is not implemented yet","`%s'",
6136 image->filename);
6137
6138 mng_info->basi_warning++;
6139 #ifdef MNG_BASI_SUPPORTED
6140 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6141 (p[2] << 8) | p[3]);
6142 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6143 (p[6] << 8) | p[7]);
6144 basi_color_type=p[8];
6145 basi_compression_method=p[9];
6146 basi_filter_type=p[10];
6147 basi_interlace_method=p[11];
6148 if (length > 11)
6149 basi_red=(p[12] << 8) & p[13];
6150
6151 else
6152 basi_red=0;
6153
6154 if (length > 13)
6155 basi_green=(p[14] << 8) & p[15];
6156
6157 else
6158 basi_green=0;
6159
6160 if (length > 15)
6161 basi_blue=(p[16] << 8) & p[17];
6162
6163 else
6164 basi_blue=0;
6165
6166 if (length > 17)
6167 basi_alpha=(p[18] << 8) & p[19];
6168
6169 else
6170 {
6171 if (basi_sample_depth == 16)
6172 basi_alpha=65535L;
6173 else
6174 basi_alpha=255;
6175 }
6176
6177 if (length > 19)
6178 basi_viewable=p[20];
6179
6180 else
6181 basi_viewable=0;
6182
6183 #endif
6184 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6185 continue;
6186 }
6187
6188 if (memcmp(type,mng_IHDR,4)
6189 #if defined(JNG_SUPPORTED)
6190 && memcmp(type,mng_JHDR,4)
6191 #endif
6192 )
6193 {
6194 /* Not an IHDR or JHDR chunk */
6195 if (length != 0)
6196 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6197
6198 continue;
6199 }
6200 /* Process IHDR */
6201 if (logging != MagickFalse)
6202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6203 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6204
6205 mng_info->exists[object_id]=MagickTrue;
6206 mng_info->viewable[object_id]=MagickTrue;
6207
6208 if (mng_info->invisible[object_id])
6209 {
6210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Skipping invisible object");
6213
6214 skip_to_iend=MagickTrue;
6215 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6216 continue;
6217 }
6218 #if defined(MNG_INSERT_LAYERS)
6219 if (length < 8)
6220 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6221
6222 image_width=(size_t) mng_get_long(p);
6223 image_height=(size_t) mng_get_long(&p[4]);
6224 #endif
6225 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6226
6227 /*
6228 Insert a transparent background layer behind the entire animation
6229 if it is not full screen.
6230 */
6231 #if defined(MNG_INSERT_LAYERS)
6232 if (insert_layers && mng_type && first_mng_object)
6233 {
6234 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6235 (image_width < mng_info->mng_width) ||
6236 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6237 (image_height < mng_info->mng_height) ||
6238 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6239 {
6240 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6241 {
6242 /*
6243 Allocate next image structure.
6244 */
6245 AcquireNextImage(image_info,image,exception);
6246
6247 if (GetNextImageInList(image) == (Image *) NULL)
6248 {
6249 image=DestroyImageList(image);
6250 MngInfoFreeStruct(mng_info,&have_mng_structure);
6251 return((Image *) NULL);
6252 }
6253
6254 image=SyncNextImageInList(image);
6255 }
6256 mng_info->image=image;
6257
6258 if (term_chunk_found)
6259 {
6260 image->start_loop=MagickTrue;
6261 image->iterations=mng_iterations;
6262 term_chunk_found=MagickFalse;
6263 }
6264
6265 else
6266 image->start_loop=MagickFalse;
6267
6268 /* Make a background rectangle. */
6269
6270 image->delay=0;
6271 image->columns=mng_info->mng_width;
6272 image->rows=mng_info->mng_height;
6273 image->page.width=mng_info->mng_width;
6274 image->page.height=mng_info->mng_height;
6275 image->page.x=0;
6276 image->page.y=0;
6277 image->background_color=mng_background_color;
6278 (void) SetImageBackgroundColor(image,exception);
6279 if (logging != MagickFalse)
6280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6281 " Inserted transparent background layer, W=%.20g, H=%.20g",
6282 (double) mng_info->mng_width,(double) mng_info->mng_height);
6283 }
6284 }
6285 /*
6286 Insert a background layer behind the upcoming image if
6287 framing_mode is 3, and we haven't already inserted one.
6288 */
6289 if (insert_layers && (mng_info->framing_mode == 3) &&
6290 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6291 (simplicity & 0x08)))
6292 {
6293 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6294 {
6295 /*
6296 Allocate next image structure.
6297 */
6298 AcquireNextImage(image_info,image,exception);
6299
6300 if (GetNextImageInList(image) == (Image *) NULL)
6301 {
6302 image=DestroyImageList(image);
6303 MngInfoFreeStruct(mng_info,&have_mng_structure);
6304 return((Image *) NULL);
6305 }
6306
6307 image=SyncNextImageInList(image);
6308 }
6309
6310 mng_info->image=image;
6311
6312 if (term_chunk_found)
6313 {
6314 image->start_loop=MagickTrue;
6315 image->iterations=mng_iterations;
6316 term_chunk_found=MagickFalse;
6317 }
6318
6319 else
6320 image->start_loop=MagickFalse;
6321
6322 image->delay=0;
6323 image->columns=subframe_width;
6324 image->rows=subframe_height;
6325 image->page.width=subframe_width;
6326 image->page.height=subframe_height;
6327 image->page.x=mng_info->clip.left;
6328 image->page.y=mng_info->clip.top;
6329 image->background_color=mng_background_color;
6330 image->alpha_trait=UndefinedPixelTrait;
6331 (void) SetImageBackgroundColor(image,exception);
6332
6333 if (logging != MagickFalse)
6334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6335 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6336 (double) mng_info->clip.left,(double) mng_info->clip.right,
6337 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6338 }
6339 #endif /* MNG_INSERT_LAYERS */
6340 first_mng_object=MagickFalse;
6341
6342 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6343 {
6344 /*
6345 Allocate next image structure.
6346 */
6347 AcquireNextImage(image_info,image,exception);
6348
6349 if (GetNextImageInList(image) == (Image *) NULL)
6350 {
6351 image=DestroyImageList(image);
6352 MngInfoFreeStruct(mng_info,&have_mng_structure);
6353 return((Image *) NULL);
6354 }
6355
6356 image=SyncNextImageInList(image);
6357 }
6358 mng_info->image=image;
6359 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6360 GetBlobSize(image));
6361
6362 if (status == MagickFalse)
6363 break;
6364
6365 if (term_chunk_found)
6366 {
6367 image->start_loop=MagickTrue;
6368 term_chunk_found=MagickFalse;
6369 }
6370
6371 else
6372 image->start_loop=MagickFalse;
6373
6374 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6375 {
6376 image->delay=frame_delay;
6377 frame_delay=default_frame_delay;
6378 }
6379
6380 else
6381 image->delay=0;
6382
6383 image->page.width=mng_info->mng_width;
6384 image->page.height=mng_info->mng_height;
6385 image->page.x=mng_info->x_off[object_id];
6386 image->page.y=mng_info->y_off[object_id];
6387 image->iterations=mng_iterations;
6388
6389 /*
6390 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6391 */
6392
6393 if (logging != MagickFalse)
6394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6395 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6396 type[2],type[3]);
6397
6398 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6399
6400 if (offset < 0)
6401 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6402 }
6403
6404 mng_info->image=image;
6405 mng_info->mng_type=mng_type;
6406 mng_info->object_id=object_id;
6407
6408 if (memcmp(type,mng_IHDR,4) == 0)
6409 image=ReadOnePNGImage(mng_info,image_info,exception);
6410
6411 #if defined(JNG_SUPPORTED)
6412 else
6413 image=ReadOneJNGImage(mng_info,image_info,exception);
6414 #endif
6415
6416 if (image == (Image *) NULL)
6417 {
6418 if (logging != MagickFalse)
6419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6420 "exit ReadJNGImage() with error");
6421
6422 MngInfoFreeStruct(mng_info,&have_mng_structure);
6423 return((Image *) NULL);
6424 }
6425
6426 if (image->columns == 0 || image->rows == 0)
6427 {
6428 (void) CloseBlob(image);
6429 image=DestroyImageList(image);
6430 MngInfoFreeStruct(mng_info,&have_mng_structure);
6431 return((Image *) NULL);
6432 }
6433
6434 mng_info->image=image;
6435
6436 if (mng_type)
6437 {
6438 MngBox
6439 crop_box;
6440
6441 if (mng_info->magn_methx || mng_info->magn_methy)
6442 {
6443 png_uint_32
6444 magnified_height,
6445 magnified_width;
6446
6447 if (logging != MagickFalse)
6448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6449 " Processing MNG MAGN chunk");
6450
6451 if (mng_info->magn_methx == 1)
6452 {
6453 magnified_width=mng_info->magn_ml;
6454
6455 if (image->columns > 1)
6456 magnified_width += mng_info->magn_mr;
6457
6458 if (image->columns > 2)
6459 magnified_width += (png_uint_32)
6460 ((image->columns-2)*(mng_info->magn_mx));
6461 }
6462
6463 else
6464 {
6465 magnified_width=(png_uint_32) image->columns;
6466
6467 if (image->columns > 1)
6468 magnified_width += mng_info->magn_ml-1;
6469
6470 if (image->columns > 2)
6471 magnified_width += mng_info->magn_mr-1;
6472
6473 if (image->columns > 3)
6474 magnified_width += (png_uint_32)
6475 ((image->columns-3)*(mng_info->magn_mx-1));
6476 }
6477
6478 if (mng_info->magn_methy == 1)
6479 {
6480 magnified_height=mng_info->magn_mt;
6481
6482 if (image->rows > 1)
6483 magnified_height += mng_info->magn_mb;
6484
6485 if (image->rows > 2)
6486 magnified_height += (png_uint_32)
6487 ((image->rows-2)*(mng_info->magn_my));
6488 }
6489
6490 else
6491 {
6492 magnified_height=(png_uint_32) image->rows;
6493
6494 if (image->rows > 1)
6495 magnified_height += mng_info->magn_mt-1;
6496
6497 if (image->rows > 2)
6498 magnified_height += mng_info->magn_mb-1;
6499
6500 if (image->rows > 3)
6501 magnified_height += (png_uint_32)
6502 ((image->rows-3)*(mng_info->magn_my-1));
6503 }
6504
6505 if (magnified_height > image->rows ||
6506 magnified_width > image->columns)
6507 {
6508 Image
6509 *large_image;
6510
6511 int
6512 yy;
6513
6514 Quantum
6515 *next,
6516 *prev;
6517
6518 png_uint_16
6519 magn_methx,
6520 magn_methy;
6521
6522 ssize_t
6523 m,
6524 y;
6525
6526 register Quantum
6527 *n,
6528 *q;
6529
6530 register ssize_t
6531 x;
6532
6533 /* Allocate next image structure. */
6534
6535 if (logging != MagickFalse)
6536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6537 " Allocate magnified image");
6538
6539 AcquireNextImage(image_info,image,exception);
6540
6541 if (GetNextImageInList(image) == (Image *) NULL)
6542 {
6543 image=DestroyImageList(image);
6544 MngInfoFreeStruct(mng_info,&have_mng_structure);
6545 return((Image *) NULL);
6546 }
6547
6548 large_image=SyncNextImageInList(image);
6549
6550 large_image->columns=magnified_width;
6551 large_image->rows=magnified_height;
6552
6553 magn_methx=mng_info->magn_methx;
6554 magn_methy=mng_info->magn_methy;
6555
6556 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6557 #define QM unsigned short
6558 if (magn_methx != 1 || magn_methy != 1)
6559 {
6560 /*
6561 Scale pixels to unsigned shorts to prevent
6562 overflow of intermediate values of interpolations
6563 */
6564 for (y=0; y < (ssize_t) image->rows; y++)
6565 {
6566 q=GetAuthenticPixels(image,0,y,image->columns,1,
6567 exception);
6568
6569 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6570 {
6571 SetPixelRed(image,ScaleQuantumToShort(
6572 GetPixelRed(image,q)),q);
6573 SetPixelGreen(image,ScaleQuantumToShort(
6574 GetPixelGreen(image,q)),q);
6575 SetPixelBlue(image,ScaleQuantumToShort(
6576 GetPixelBlue(image,q)),q);
6577 SetPixelAlpha(image,ScaleQuantumToShort(
6578 GetPixelAlpha(image,q)),q);
6579 q+=GetPixelChannels(image);
6580 }
6581
6582 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6583 break;
6584 }
6585 }
6586 #else
6587 #define QM Quantum
6588 #endif
6589
6590 if (image->alpha_trait != UndefinedPixelTrait)
6591 (void) SetImageBackgroundColor(large_image,exception);
6592
6593 else
6594 {
6595 large_image->background_color.alpha=OpaqueAlpha;
6596 (void) SetImageBackgroundColor(large_image,exception);
6597
6598 if (magn_methx == 4)
6599 magn_methx=2;
6600
6601 if (magn_methx == 5)
6602 magn_methx=3;
6603
6604 if (magn_methy == 4)
6605 magn_methy=2;
6606
6607 if (magn_methy == 5)
6608 magn_methy=3;
6609 }
6610
6611 /* magnify the rows into the right side of the large image */
6612
6613 if (logging != MagickFalse)
6614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6615 " Magnify the rows to %.20g",(double) large_image->rows);
6616 m=(ssize_t) mng_info->magn_mt;
6617 yy=0;
6618 length=(size_t) GetPixelChannels(image)*image->columns;
6619 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6620 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6621
6622 if ((prev == (Quantum *) NULL) ||
6623 (next == (Quantum *) NULL))
6624 {
6625 image=DestroyImageList(image);
6626 MngInfoFreeStruct(mng_info,&have_mng_structure);
6627 ThrowReaderException(ResourceLimitError,
6628 "MemoryAllocationFailed");
6629 }
6630
6631 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6632 (void) CopyMagickMemory(next,n,length);
6633
6634 for (y=0; y < (ssize_t) image->rows; y++)
6635 {
6636 if (y == 0)
6637 m=(ssize_t) mng_info->magn_mt;
6638
6639 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6640 m=(ssize_t) mng_info->magn_mb;
6641
6642 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6643 m=(ssize_t) mng_info->magn_mb;
6644
6645 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6646 m=1;
6647
6648 else
6649 m=(ssize_t) mng_info->magn_my;
6650
6651 n=prev;
6652 prev=next;
6653 next=n;
6654
6655 if (y < (ssize_t) image->rows-1)
6656 {
6657 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6658 exception);
6659 (void) CopyMagickMemory(next,n,length);
6660 }
6661
6662 for (i=0; i < m; i++, yy++)
6663 {
6664 register Quantum
6665 *pixels;
6666
6667 assert(yy < (ssize_t) large_image->rows);
6668 pixels=prev;
6669 n=next;
6670 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6671 1,exception);
6672 q+=(large_image->columns-image->columns)*
6673 GetPixelChannels(large_image);
6674
6675 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6676 {
6677 /* To do: get color as function of indexes[x] */
6678 /*
6679 if (image->storage_class == PseudoClass)
6680 {
6681 }
6682 */
6683
6684 if (magn_methy <= 1)
6685 {
6686 /* replicate previous */
6687 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6688 SetPixelGreen(large_image,GetPixelGreen(image,
6689 pixels),q);
6690 SetPixelBlue(large_image,GetPixelBlue(image,
6691 pixels),q);
6692 SetPixelAlpha(large_image,GetPixelAlpha(image,
6693 pixels),q);
6694 }
6695
6696 else if (magn_methy == 2 || magn_methy == 4)
6697 {
6698 if (i == 0)
6699 {
6700 SetPixelRed(large_image,GetPixelRed(image,
6701 pixels),q);
6702 SetPixelGreen(large_image,GetPixelGreen(image,
6703 pixels),q);
6704 SetPixelBlue(large_image,GetPixelBlue(image,
6705 pixels),q);
6706 SetPixelAlpha(large_image,GetPixelAlpha(image,
6707 pixels),q);
6708 }
6709
6710 else
6711 {
6712 /* Interpolate */
6713 SetPixelRed(large_image,((QM) (((ssize_t)
6714 (2*i*(GetPixelRed(image,n)
6715 -GetPixelRed(image,pixels)+m))/
6716 ((ssize_t) (m*2))
6717 +GetPixelRed(image,pixels)))),q);
6718 SetPixelGreen(large_image,((QM) (((ssize_t)
6719 (2*i*(GetPixelGreen(image,n)
6720 -GetPixelGreen(image,pixels)+m))/
6721 ((ssize_t) (m*2))
6722 +GetPixelGreen(image,pixels)))),q);
6723 SetPixelBlue(large_image,((QM) (((ssize_t)
6724 (2*i*(GetPixelBlue(image,n)
6725 -GetPixelBlue(image,pixels)+m))/
6726 ((ssize_t) (m*2))
6727 +GetPixelBlue(image,pixels)))),q);
6728
6729 if (image->alpha_trait != UndefinedPixelTrait)
6730 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6731 (2*i*(GetPixelAlpha(image,n)
6732 -GetPixelAlpha(image,pixels)+m))
6733 /((ssize_t) (m*2))+
6734 GetPixelAlpha(image,pixels)))),q);
6735 }
6736
6737 if (magn_methy == 4)
6738 {
6739 /* Replicate nearest */
6740 if (i <= ((m+1) << 1))
6741 SetPixelAlpha(large_image,GetPixelAlpha(image,
6742 pixels),q);
6743 else
6744 SetPixelAlpha(large_image,GetPixelAlpha(image,
6745 n),q);
6746 }
6747 }
6748
6749 else /* if (magn_methy == 3 || magn_methy == 5) */
6750 {
6751 /* Replicate nearest */
6752 if (i <= ((m+1) << 1))
6753 {
6754 SetPixelRed(large_image,GetPixelRed(image,
6755 pixels),q);
6756 SetPixelGreen(large_image,GetPixelGreen(image,
6757 pixels),q);
6758 SetPixelBlue(large_image,GetPixelBlue(image,
6759 pixels),q);
6760 SetPixelAlpha(large_image,GetPixelAlpha(image,
6761 pixels),q);
6762 }
6763
6764 else
6765 {
6766 SetPixelRed(large_image,GetPixelRed(image,n),q);
6767 SetPixelGreen(large_image,GetPixelGreen(image,n),
6768 q);
6769 SetPixelBlue(large_image,GetPixelBlue(image,n),
6770 q);
6771 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6772 q);
6773 }
6774
6775 if (magn_methy == 5)
6776 {
6777 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6778 (GetPixelAlpha(image,n)
6779 -GetPixelAlpha(image,pixels))
6780 +m))/((ssize_t) (m*2))
6781 +GetPixelAlpha(image,pixels)),q);
6782 }
6783 }
6784 n+=GetPixelChannels(image);
6785 q+=GetPixelChannels(large_image);
6786 pixels+=GetPixelChannels(image);
6787 } /* x */
6788
6789 if (SyncAuthenticPixels(large_image,exception) == 0)
6790 break;
6791
6792 } /* i */
6793 } /* y */
6794
6795 prev=(Quantum *) RelinquishMagickMemory(prev);
6796 next=(Quantum *) RelinquishMagickMemory(next);
6797
6798 length=image->columns;
6799
6800 if (logging != MagickFalse)
6801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6802 " Delete original image");
6803
6804 DeleteImageFromList(&image);
6805
6806 image=large_image;
6807
6808 mng_info->image=image;
6809
6810 /* magnify the columns */
6811 if (logging != MagickFalse)
6812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6813 " Magnify the columns to %.20g",(double) image->columns);
6814
6815 for (y=0; y < (ssize_t) image->rows; y++)
6816 {
6817 register Quantum
6818 *pixels;
6819
6820 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6821 pixels=q+(image->columns-length)*GetPixelChannels(image);
6822 n=pixels+GetPixelChannels(image);
6823
6824 for (x=(ssize_t) (image->columns-length);
6825 x < (ssize_t) image->columns; x++)
6826 {
6827 /* To do: Rewrite using Get/Set***PixelChannel() */
6828
6829 if (x == (ssize_t) (image->columns-length))
6830 m=(ssize_t) mng_info->magn_ml;
6831
6832 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6833 m=(ssize_t) mng_info->magn_mr;
6834
6835 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6836 m=(ssize_t) mng_info->magn_mr;
6837
6838 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6839 m=1;
6840
6841 else
6842 m=(ssize_t) mng_info->magn_mx;
6843
6844 for (i=0; i < m; i++)
6845 {
6846 if (magn_methx <= 1)
6847 {
6848 /* replicate previous */
6849 SetPixelRed(image,GetPixelRed(image,pixels),q);
6850 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6851 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6852 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6853 }
6854
6855 else if (magn_methx == 2 || magn_methx == 4)
6856 {
6857 if (i == 0)
6858 {
6859 SetPixelRed(image,GetPixelRed(image,pixels),q);
6860 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6861 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6862 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6863 }
6864
6865 /* To do: Rewrite using Get/Set***PixelChannel() */
6866 else
6867 {
6868 /* Interpolate */
6869 SetPixelRed(image,(QM) ((2*i*(
6870 GetPixelRed(image,n)
6871 -GetPixelRed(image,pixels))+m)
6872 /((ssize_t) (m*2))+
6873 GetPixelRed(image,pixels)),q);
6874
6875 SetPixelGreen(image,(QM) ((2*i*(
6876 GetPixelGreen(image,n)
6877 -GetPixelGreen(image,pixels))+m)
6878 /((ssize_t) (m*2))+
6879 GetPixelGreen(image,pixels)),q);
6880
6881 SetPixelBlue(image,(QM) ((2*i*(
6882 GetPixelBlue(image,n)
6883 -GetPixelBlue(image,pixels))+m)
6884 /((ssize_t) (m*2))+
6885 GetPixelBlue(image,pixels)),q);
6886 if (image->alpha_trait != UndefinedPixelTrait)
6887 SetPixelAlpha(image,(QM) ((2*i*(
6888 GetPixelAlpha(image,n)
6889 -GetPixelAlpha(image,pixels))+m)
6890 /((ssize_t) (m*2))+
6891 GetPixelAlpha(image,pixels)),q);
6892 }
6893
6894 if (magn_methx == 4)
6895 {
6896 /* Replicate nearest */
6897 if (i <= ((m+1) << 1))
6898 {
6899 SetPixelAlpha(image,
6900 GetPixelAlpha(image,pixels)+0,q);
6901 }
6902 else
6903 {
6904 SetPixelAlpha(image,
6905 GetPixelAlpha(image,n)+0,q);
6906 }
6907 }
6908 }
6909
6910 else /* if (magn_methx == 3 || magn_methx == 5) */
6911 {
6912 /* Replicate nearest */
6913 if (i <= ((m+1) << 1))
6914 {
6915 SetPixelRed(image,GetPixelRed(image,pixels),q);
6916 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6917 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6918 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6919 }
6920
6921 else
6922 {
6923 SetPixelRed(image,GetPixelRed(image,n),q);
6924 SetPixelGreen(image,GetPixelGreen(image,n),q);
6925 SetPixelBlue(image,GetPixelBlue(image,n),q);
6926 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6927 }
6928
6929 if (magn_methx == 5)
6930 {
6931 /* Interpolate */
6932 SetPixelAlpha(image,
6933 (QM) ((2*i*( GetPixelAlpha(image,n)
6934 -GetPixelAlpha(image,pixels))+m)/
6935 ((ssize_t) (m*2))
6936 +GetPixelAlpha(image,pixels)),q);
6937 }
6938 }
6939 q+=GetPixelChannels(image);
6940 }
6941 n+=GetPixelChannels(image);
6942 }
6943
6944 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6945 break;
6946 }
6947 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6948 if (magn_methx != 1 || magn_methy != 1)
6949 {
6950 /*
6951 Rescale pixels to Quantum
6952 */
6953 for (y=0; y < (ssize_t) image->rows; y++)
6954 {
6955 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6956
6957 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6958 {
6959 SetPixelRed(image,ScaleShortToQuantum(
6960 GetPixelRed(image,q)),q);
6961 SetPixelGreen(image,ScaleShortToQuantum(
6962 GetPixelGreen(image,q)),q);
6963 SetPixelBlue(image,ScaleShortToQuantum(
6964 GetPixelBlue(image,q)),q);
6965 SetPixelAlpha(image,ScaleShortToQuantum(
6966 GetPixelAlpha(image,q)),q);
6967 q+=GetPixelChannels(image);
6968 }
6969
6970 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6971 break;
6972 }
6973 }
6974 #endif
6975 if (logging != MagickFalse)
6976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6977 " Finished MAGN processing");
6978 }
6979 }
6980
6981 /*
6982 Crop_box is with respect to the upper left corner of the MNG.
6983 */
6984 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6985 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6986 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6987 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6988 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6989 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6990 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6991 if ((crop_box.left != (mng_info->image_box.left
6992 +mng_info->x_off[object_id])) ||
6993 (crop_box.right != (mng_info->image_box.right
6994 +mng_info->x_off[object_id])) ||
6995 (crop_box.top != (mng_info->image_box.top
6996 +mng_info->y_off[object_id])) ||
6997 (crop_box.bottom != (mng_info->image_box.bottom
6998 +mng_info->y_off[object_id])))
6999 {
7000 if (logging != MagickFalse)
7001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7002 " Crop the PNG image");
7003
7004 if ((crop_box.left < crop_box.right) &&
7005 (crop_box.top < crop_box.bottom))
7006 {
7007 Image
7008 *im;
7009
7010 RectangleInfo
7011 crop_info;
7012
7013 /*
7014 Crop_info is with respect to the upper left corner of
7015 the image.
7016 */
7017 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7018 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7019 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7020 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7021 image->page.width=image->columns;
7022 image->page.height=image->rows;
7023 image->page.x=0;
7024 image->page.y=0;
7025 im=CropImage(image,&crop_info,exception);
7026
7027 if (im != (Image *) NULL)
7028 {
7029 image->columns=im->columns;
7030 image->rows=im->rows;
7031 im=DestroyImage(im);
7032 image->page.width=image->columns;
7033 image->page.height=image->rows;
7034 image->page.x=crop_box.left;
7035 image->page.y=crop_box.top;
7036 }
7037 }
7038
7039 else
7040 {
7041 /*
7042 No pixels in crop area. The MNG spec still requires
7043 a layer, though, so make a single transparent pixel in
7044 the top left corner.
7045 */
7046 image->columns=1;
7047 image->rows=1;
7048 image->colors=2;
7049 (void) SetImageBackgroundColor(image,exception);
7050 image->page.width=1;
7051 image->page.height=1;
7052 image->page.x=0;
7053 image->page.y=0;
7054 }
7055 }
7056 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7057 image=mng_info->image;
7058 #endif
7059 }
7060
7061 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7062 /* PNG does not handle depths greater than 16 so reduce it even
7063 * if lossy.
7064 */
7065 if (image->depth > 16)
7066 image->depth=16;
7067 #endif
7068
7069 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7070 if (image->depth > 8)
7071 {
7072 /* To do: fill low byte properly */
7073 image->depth=16;
7074 }
7075
7076 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7077 image->depth = 8;
7078 #endif
7079
7080 if (image_info->number_scenes != 0)
7081 {
7082 if (mng_info->scenes_found >
7083 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7084 break;
7085 }
7086
7087 if (logging != MagickFalse)
7088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7089 " Finished reading image datastream.");
7090
7091 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7092
7093 (void) CloseBlob(image);
7094
7095 if (logging != MagickFalse)
7096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097 " Finished reading all image datastreams.");
7098
7099 #if defined(MNG_INSERT_LAYERS)
7100 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7101 (mng_info->mng_height))
7102 {
7103 /*
7104 Insert a background layer if nothing else was found.
7105 */
7106 if (logging != MagickFalse)
7107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7108 " No images found. Inserting a background layer.");
7109
7110 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7111 {
7112 /*
7113 Allocate next image structure.
7114 */
7115 AcquireNextImage(image_info,image,exception);
7116 if (GetNextImageInList(image) == (Image *) NULL)
7117 {
7118 image=DestroyImageList(image);
7119 MngInfoFreeStruct(mng_info,&have_mng_structure);
7120
7121 if (logging != MagickFalse)
7122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7123 " Allocation failed, returning NULL.");
7124
7125 return((Image *) NULL);
7126 }
7127 image=SyncNextImageInList(image);
7128 }
7129 image->columns=mng_info->mng_width;
7130 image->rows=mng_info->mng_height;
7131 image->page.width=mng_info->mng_width;
7132 image->page.height=mng_info->mng_height;
7133 image->page.x=0;
7134 image->page.y=0;
7135 image->background_color=mng_background_color;
7136 image->alpha_trait=UndefinedPixelTrait;
7137
7138 if (image_info->ping == MagickFalse)
7139 (void) SetImageBackgroundColor(image,exception);
7140
7141 mng_info->image_found++;
7142 }
7143 #endif
7144 image->iterations=mng_iterations;
7145
7146 if (mng_iterations == 1)
7147 image->start_loop=MagickTrue;
7148
7149 while (GetPreviousImageInList(image) != (Image *) NULL)
7150 {
7151 image_count++;
7152 if (image_count > 10*mng_info->image_found)
7153 {
7154 if (logging != MagickFalse)
7155 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7156
7157 (void) ThrowMagickException(exception,GetMagickModule(),
7158 CoderError,"Linked list is corrupted, beginning of list not found",
7159 "`%s'",image_info->filename);
7160
7161 return((Image *) NULL);
7162 }
7163
7164 image=GetPreviousImageInList(image);
7165
7166 if (GetNextImageInList(image) == (Image *) NULL)
7167 {
7168 if (logging != MagickFalse)
7169 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7170
7171 (void) ThrowMagickException(exception,GetMagickModule(),
7172 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7173 image_info->filename);
7174 }
7175 }
7176
7177 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7178 GetNextImageInList(image) ==
7179 (Image *) NULL)
7180 {
7181 if (logging != MagickFalse)
7182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7183 " First image null");
7184
7185 (void) ThrowMagickException(exception,GetMagickModule(),
7186 CoderError,"image->next for first image is NULL but shouldn't be.",
7187 "`%s'",image_info->filename);
7188 }
7189
7190 if (mng_info->image_found == 0)
7191 {
7192 if (logging != MagickFalse)
7193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194 " No visible images found.");
7195
7196 (void) ThrowMagickException(exception,GetMagickModule(),
7197 CoderError,"No visible images in file","`%s'",image_info->filename);
7198
7199 if (image != (Image *) NULL)
7200 image=DestroyImageList(image);
7201
7202 MngInfoFreeStruct(mng_info,&have_mng_structure);
7203 return((Image *) NULL);
7204 }
7205
7206 if (mng_info->ticks_per_second)
7207 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7208 final_delay/mng_info->ticks_per_second;
7209
7210 else
7211 image->start_loop=MagickTrue;
7212
7213 /* Find final nonzero image delay */
7214 final_image_delay=0;
7215
7216 while (GetNextImageInList(image) != (Image *) NULL)
7217 {
7218 if (image->delay)
7219 final_image_delay=image->delay;
7220
7221 image=GetNextImageInList(image);
7222 }
7223
7224 if (final_delay < final_image_delay)
7225 final_delay=final_image_delay;
7226
7227 image->delay=final_delay;
7228
7229 if (logging != MagickFalse)
7230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7232 (double) final_delay);
7233
7234 if (logging != MagickFalse)
7235 {
7236 int
7237 scene;
7238
7239 scene=0;
7240 image=GetFirstImageInList(image);
7241
7242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7243 " Before coalesce:");
7244
7245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7246 " scene 0 delay=%.20g",(double) image->delay);
7247
7248 while (GetNextImageInList(image) != (Image *) NULL)
7249 {
7250 image=GetNextImageInList(image);
7251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7252 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7253 }
7254 }
7255
7256 image=GetFirstImageInList(image);
7257 #ifdef MNG_COALESCE_LAYERS
7258 if (insert_layers)
7259 {
7260 Image
7261 *next_image,
7262 *next;
7263
7264 size_t
7265 scene;
7266
7267 if (logging != MagickFalse)
7268 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7269
7270 scene=image->scene;
7271 next_image=CoalesceImages(image,exception);
7272
7273 if (next_image == (Image *) NULL)
7274 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7275
7276 image=DestroyImageList(image);
7277 image=next_image;
7278
7279 for (next=image; next != (Image *) NULL; next=next_image)
7280 {
7281 next->page.width=mng_info->mng_width;
7282 next->page.height=mng_info->mng_height;
7283 next->page.x=0;
7284 next->page.y=0;
7285 next->scene=scene++;
7286 next_image=GetNextImageInList(next);
7287
7288 if (next_image == (Image *) NULL)
7289 break;
7290
7291 if (next->delay == 0)
7292 {
7293 scene--;
7294 next_image->previous=GetPreviousImageInList(next);
7295 if (GetPreviousImageInList(next) == (Image *) NULL)
7296 image=next_image;
7297 else
7298 next->previous->next=next_image;
7299 next=DestroyImage(next);
7300 }
7301 }
7302 }
7303 #endif
7304
7305 while (GetNextImageInList(image) != (Image *) NULL)
7306 image=GetNextImageInList(image);
7307
7308 image->dispose=BackgroundDispose;
7309
7310 if (logging != MagickFalse)
7311 {
7312 int
7313 scene;
7314
7315 scene=0;
7316 image=GetFirstImageInList(image);
7317
7318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7319 " After coalesce:");
7320
7321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7323 (double) image->dispose);
7324
7325 while (GetNextImageInList(image) != (Image *) NULL)
7326 {
7327 image=GetNextImageInList(image);
7328
7329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7330 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7331 (double) image->delay,(double) image->dispose);
7332 }
7333 }
7334
7335 image=GetFirstImageInList(image);
7336 MngInfoFreeStruct(mng_info,&have_mng_structure);
7337 have_mng_structure=MagickFalse;
7338
7339 if (logging != MagickFalse)
7340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7341
7342 return(GetFirstImageInList(image));
7343 }
7344 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7345 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7346 {
7347 printf("Your PNG library is too old: You have libpng-%s\n",
7348 PNG_LIBPNG_VER_STRING);
7349
7350 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7351 "PNG library is too old","`%s'",image_info->filename);
7352
7353 return(Image *) NULL;
7354 }
7355
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7356 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7357 {
7358 return(ReadPNGImage(image_info,exception));
7359 }
7360 #endif /* PNG_LIBPNG_VER > 10011 */
7361 #endif
7362
7363 /*
7364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7365 % %
7366 % %
7367 % %
7368 % R e g i s t e r P N G I m a g e %
7369 % %
7370 % %
7371 % %
7372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7373 %
7374 % RegisterPNGImage() adds properties for the PNG image format to
7375 % the list of supported formats. The properties include the image format
7376 % tag, a method to read and/or write the format, whether the format
7377 % supports the saving of more than one frame to the same file or blob,
7378 % whether the format supports native in-memory I/O, and a brief
7379 % description of the format.
7380 %
7381 % The format of the RegisterPNGImage method is:
7382 %
7383 % size_t RegisterPNGImage(void)
7384 %
7385 */
RegisterPNGImage(void)7386 ModuleExport size_t RegisterPNGImage(void)
7387 {
7388 char
7389 version[MagickPathExtent];
7390
7391 MagickInfo
7392 *entry;
7393
7394 static const char
7395 *PNGNote=
7396 {
7397 "See http://www.libpng.org/ for details about the PNG format."
7398 },
7399
7400 *JNGNote=
7401 {
7402 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7403 "format."
7404 },
7405
7406 *MNGNote=
7407 {
7408 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7409 "format."
7410 };
7411
7412 *version='\0';
7413
7414 #if defined(PNG_LIBPNG_VER_STRING)
7415 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7416 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MagickPathExtent);
7417
7418 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7419 {
7420 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7421 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7422 MagickPathExtent);
7423 }
7424 #endif
7425
7426 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7427 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
7428
7429 #if defined(MAGICKCORE_PNG_DELEGATE)
7430 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7431 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7432 #endif
7433
7434 entry->magick=(IsImageFormatHandler *) IsMNG;
7435
7436 if (*version != '\0')
7437 entry->version=ConstantString(version);
7438
7439 entry->mime_type=ConstantString("video/x-mng");
7440 entry->note=ConstantString(MNGNote);
7441 (void) RegisterMagickInfo(entry);
7442
7443 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7444
7445 #if defined(MAGICKCORE_PNG_DELEGATE)
7446 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7447 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7448 #endif
7449
7450 entry->magick=(IsImageFormatHandler *) IsPNG;
7451 entry->flags^=CoderAdjoinFlag;
7452 entry->mime_type=ConstantString("image/png");
7453
7454 if (*version != '\0')
7455 entry->version=ConstantString(version);
7456
7457 entry->note=ConstantString(PNGNote);
7458 (void) RegisterMagickInfo(entry);
7459
7460 entry=AcquireMagickInfo("PNG","PNG8",
7461 "8-bit indexed with optional binary transparency");
7462
7463 #if defined(MAGICKCORE_PNG_DELEGATE)
7464 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7465 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7466 #endif
7467
7468 entry->magick=(IsImageFormatHandler *) IsPNG;
7469 entry->flags^=CoderAdjoinFlag;
7470 entry->mime_type=ConstantString("image/png");
7471 (void) RegisterMagickInfo(entry);
7472
7473 entry=AcquireMagickInfo("PNG","PNG24",
7474 "opaque or binary transparent 24-bit RGB");
7475 *version='\0';
7476
7477 #if defined(ZLIB_VERSION)
7478 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7479 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7480
7481 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7482 {
7483 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7484 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7485 }
7486 #endif
7487
7488 if (*version != '\0')
7489 entry->version=ConstantString(version);
7490
7491 #if defined(MAGICKCORE_PNG_DELEGATE)
7492 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7493 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7494 #endif
7495
7496 entry->magick=(IsImageFormatHandler *) IsPNG;
7497 entry->flags^=CoderAdjoinFlag;
7498 entry->mime_type=ConstantString("image/png");
7499 (void) RegisterMagickInfo(entry);
7500
7501 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7502
7503 #if defined(MAGICKCORE_PNG_DELEGATE)
7504 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7505 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7506 #endif
7507
7508 entry->magick=(IsImageFormatHandler *) IsPNG;
7509 entry->flags^=CoderAdjoinFlag;
7510 entry->mime_type=ConstantString("image/png");
7511 (void) RegisterMagickInfo(entry);
7512
7513 entry=AcquireMagickInfo("PNG","PNG48",
7514 "opaque or binary transparent 48-bit RGB");
7515
7516 #if defined(MAGICKCORE_PNG_DELEGATE)
7517 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7518 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7519 #endif
7520
7521 entry->magick=(IsImageFormatHandler *) IsPNG;
7522 entry->flags^=CoderAdjoinFlag;
7523 entry->mime_type=ConstantString("image/png");
7524 (void) RegisterMagickInfo(entry);
7525
7526 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7527
7528 #if defined(MAGICKCORE_PNG_DELEGATE)
7529 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7530 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7531 #endif
7532
7533 entry->magick=(IsImageFormatHandler *) IsPNG;
7534 entry->flags^=CoderAdjoinFlag;
7535 entry->mime_type=ConstantString("image/png");
7536 (void) RegisterMagickInfo(entry);
7537
7538 entry=AcquireMagickInfo("PNG","PNG00",
7539 "PNG inheriting bit-depth, color-type from original, if possible");
7540
7541 #if defined(MAGICKCORE_PNG_DELEGATE)
7542 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7543 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7544 #endif
7545
7546 entry->magick=(IsImageFormatHandler *) IsPNG;
7547 entry->flags^=CoderAdjoinFlag;
7548 entry->mime_type=ConstantString("image/png");
7549 (void) RegisterMagickInfo(entry);
7550
7551 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7552
7553 #if defined(JNG_SUPPORTED)
7554 #if defined(MAGICKCORE_PNG_DELEGATE)
7555 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7556 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7557 #endif
7558 #endif
7559
7560 entry->magick=(IsImageFormatHandler *) IsJNG;
7561 entry->flags^=CoderAdjoinFlag;
7562 entry->mime_type=ConstantString("image/x-jng");
7563 entry->note=ConstantString(JNGNote);
7564 (void) RegisterMagickInfo(entry);
7565
7566 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7567 ping_semaphore=AcquireSemaphoreInfo();
7568 #endif
7569
7570 return(MagickImageCoderSignature);
7571 }
7572
7573 /*
7574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7575 % %
7576 % %
7577 % %
7578 % U n r e g i s t e r P N G I m a g e %
7579 % %
7580 % %
7581 % %
7582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7583 %
7584 % UnregisterPNGImage() removes format registrations made by the
7585 % PNG module from the list of supported formats.
7586 %
7587 % The format of the UnregisterPNGImage method is:
7588 %
7589 % UnregisterPNGImage(void)
7590 %
7591 */
UnregisterPNGImage(void)7592 ModuleExport void UnregisterPNGImage(void)
7593 {
7594 (void) UnregisterMagickInfo("MNG");
7595 (void) UnregisterMagickInfo("PNG");
7596 (void) UnregisterMagickInfo("PNG8");
7597 (void) UnregisterMagickInfo("PNG24");
7598 (void) UnregisterMagickInfo("PNG32");
7599 (void) UnregisterMagickInfo("PNG48");
7600 (void) UnregisterMagickInfo("PNG64");
7601 (void) UnregisterMagickInfo("PNG00");
7602 (void) UnregisterMagickInfo("JNG");
7603
7604 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7605 if (ping_semaphore != (SemaphoreInfo *) NULL)
7606 RelinquishSemaphoreInfo(&ping_semaphore);
7607 #endif
7608 }
7609
7610 #if defined(MAGICKCORE_PNG_DELEGATE)
7611 #if PNG_LIBPNG_VER > 10011
7612 /*
7613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7614 % %
7615 % %
7616 % %
7617 % W r i t e M N G I m a g e %
7618 % %
7619 % %
7620 % %
7621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7622 %
7623 % WriteMNGImage() writes an image in the Portable Network Graphics
7624 % Group's "Multiple-image Network Graphics" encoded image format.
7625 %
7626 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7627 %
7628 % The format of the WriteMNGImage method is:
7629 %
7630 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7631 % Image *image,ExceptionInfo *exception)
7632 %
7633 % A description of each parameter follows.
7634 %
7635 % o image_info: the image info.
7636 %
7637 % o image: The image.
7638 %
7639 % o exception: return any errors or warnings in this structure.
7640 %
7641 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7642 % "To do" under ReadPNGImage):
7643 %
7644 % Preserve all unknown and not-yet-handled known chunks found in input
7645 % PNG file and copy them into output PNG files according to the PNG
7646 % copying rules.
7647 %
7648 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7649 %
7650 % Improve selection of color type (use indexed-colour or indexed-colour
7651 % with tRNS when 256 or fewer unique RGBA values are present).
7652 %
7653 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7654 % This will be complicated if we limit ourselves to generating MNG-LC
7655 % files. For now we ignore disposal method 3 and simply overlay the next
7656 % image on it.
7657 %
7658 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7659 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7660 % [mostly done 15 June 1999 but still need to take care of tRNS]
7661 %
7662 % Check for identical sRGB and replace with a global sRGB (and remove
7663 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7664 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7665 % local gAMA/cHRM with local sRGB if appropriate).
7666 %
7667 % Check for identical sBIT chunks and write global ones.
7668 %
7669 % Provide option to skip writing the signature tEXt chunks.
7670 %
7671 % Use signatures to detect identical objects and reuse the first
7672 % instance of such objects instead of writing duplicate objects.
7673 %
7674 % Use a smaller-than-32k value of compression window size when
7675 % appropriate.
7676 %
7677 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7678 % ancillary text chunks and save profiles.
7679 %
7680 % Provide an option to force LC files (to ensure exact framing rate)
7681 % instead of VLC.
7682 %
7683 % Provide an option to force VLC files instead of LC, even when offsets
7684 % are present. This will involve expanding the embedded images with a
7685 % transparent region at the top and/or left.
7686 */
7687
7688 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)7689 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7690 png_info *ping_info, unsigned char *profile_type, unsigned char
7691 *profile_description, unsigned char *profile_data, png_uint_32 length)
7692 {
7693 png_textp
7694 text;
7695
7696 register ssize_t
7697 i;
7698
7699 unsigned char
7700 *sp;
7701
7702 png_charp
7703 dp;
7704
7705 png_uint_32
7706 allocated_length,
7707 description_length;
7708
7709 unsigned char
7710 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7711
7712 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7713 return;
7714
7715 if (image_info->verbose)
7716 {
7717 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7718 (char *) profile_type, (double) length);
7719 }
7720
7721 #if PNG_LIBPNG_VER >= 10400
7722 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7723 #else
7724 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7725 #endif
7726 description_length=(png_uint_32) strlen((const char *) profile_description);
7727 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7728 + description_length);
7729 #if PNG_LIBPNG_VER >= 10400
7730 text[0].text=(png_charp) png_malloc(ping,
7731 (png_alloc_size_t) allocated_length);
7732 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7733 #else
7734 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7735 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7736 #endif
7737 text[0].key[0]='\0';
7738 (void) ConcatenateMagickString(text[0].key,
7739 "Raw profile type ",MagickPathExtent);
7740 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7741 sp=profile_data;
7742 dp=text[0].text;
7743 *dp++='\n';
7744 (void) CopyMagickString(dp,(const char *) profile_description,
7745 allocated_length);
7746 dp+=description_length;
7747 *dp++='\n';
7748 (void) FormatLocaleString(dp,allocated_length-
7749 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7750 dp+=8;
7751
7752 for (i=0; i < (ssize_t) length; i++)
7753 {
7754 if (i%36 == 0)
7755 *dp++='\n';
7756 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7757 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7758 }
7759
7760 *dp++='\n';
7761 *dp='\0';
7762 text[0].text_length=(png_size_t) (dp-text[0].text);
7763 text[0].compression=image_info->compression == NoCompression ||
7764 (image_info->compression == UndefinedCompression &&
7765 text[0].text_length < 128) ? -1 : 0;
7766
7767 if (text[0].text_length <= allocated_length)
7768 png_set_text(ping,ping_info,text,1);
7769
7770 png_free(ping,text[0].text);
7771 png_free(ping,text[0].key);
7772 png_free(ping,text);
7773 }
7774
Magick_png_write_chunk_from_profile(Image * image,const char * string,MagickBooleanType logging)7775 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7776 const char *string, MagickBooleanType logging)
7777 {
7778 char
7779 *name;
7780
7781 const StringInfo
7782 *profile;
7783
7784 unsigned char
7785 *data;
7786
7787 png_uint_32 length;
7788
7789 ResetImageProfileIterator(image);
7790
7791 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7792 {
7793 profile=GetImageProfile(image,name);
7794
7795 if (profile != (const StringInfo *) NULL)
7796 {
7797 StringInfo
7798 *ping_profile;
7799
7800 if (LocaleNCompare(name,string,11) == 0)
7801 {
7802 if (logging != MagickFalse)
7803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7804 " Found %s profile",name);
7805
7806 ping_profile=CloneStringInfo(profile);
7807 data=GetStringInfoDatum(ping_profile),
7808 length=(png_uint_32) GetStringInfoLength(ping_profile);
7809 data[4]=data[3];
7810 data[3]=data[2];
7811 data[2]=data[1];
7812 data[1]=data[0];
7813 (void) WriteBlobMSBULong(image,length-5); /* data length */
7814 (void) WriteBlob(image,length-1,data+1);
7815 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7816 ping_profile=DestroyStringInfo(ping_profile);
7817 }
7818 }
7819
7820 name=GetNextImageProfile(image);
7821 }
7822
7823 return(MagickTrue);
7824 }
7825
Magick_png_color_equal(const Image * image,const Quantum * p,const PixelInfo * q)7826 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
7827 const Quantum *p, const PixelInfo *q)
7828 {
7829 MagickRealType
7830 value;
7831
7832 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
7833 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
7834 return(MagickFalse);
7835 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
7836 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
7837 return(MagickFalse);
7838 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
7839 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
7840 return(MagickFalse);
7841
7842 return(MagickTrue);
7843 }
7844
7845 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * date,ExceptionInfo * exception)7846 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7847 const char *date,ExceptionInfo *exception)
7848 {
7849 unsigned int
7850 day,
7851 hour,
7852 minute,
7853 month,
7854 second,
7855 year;
7856
7857 png_time
7858 ptime;
7859
7860 time_t
7861 ttime;
7862
7863 if (date != (const char *) NULL)
7864 {
7865 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7866 &second) != 6)
7867 {
7868 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7869 "Invalid date format specified for png:tIME","`%s'",
7870 image->filename);
7871 return;
7872 }
7873 ptime.year=(png_uint_16) year;
7874 ptime.month=(png_byte) month;
7875 ptime.day=(png_byte) day;
7876 ptime.hour=(png_byte) hour;
7877 ptime.minute=(png_byte) minute;
7878 ptime.second=(png_byte) second;
7879 }
7880 else
7881 {
7882 time(&ttime);
7883 png_convert_from_time_t(&ptime,ttime);
7884 }
7885 png_set_tIME(ping,info,&ptime);
7886 }
7887 #endif
7888
7889 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)7890 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7891 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7892 {
7893 char
7894 im_vers[32],
7895 libpng_runv[32],
7896 libpng_vers[32],
7897 zlib_runv[32],
7898 zlib_vers[32];
7899
7900 Image
7901 *image;
7902
7903 ImageInfo
7904 *image_info;
7905
7906 char
7907 s[2];
7908
7909 const char
7910 *name,
7911 *property,
7912 *value;
7913
7914 const StringInfo
7915 *profile;
7916
7917 int
7918 num_passes,
7919 pass;
7920
7921 png_byte
7922 ping_trans_alpha[256];
7923
7924 png_color
7925 palette[257];
7926
7927 png_color_16
7928 ping_background,
7929 ping_trans_color;
7930
7931 png_info
7932 *ping_info;
7933
7934 png_struct
7935 *ping;
7936
7937 png_uint_32
7938 ping_height,
7939 ping_width;
7940
7941 ssize_t
7942 y;
7943
7944 MagickBooleanType
7945 image_matte,
7946 logging,
7947 matte,
7948
7949 ping_have_blob,
7950 ping_have_cheap_transparency,
7951 ping_have_color,
7952 ping_have_non_bw,
7953 ping_have_PLTE,
7954 ping_have_bKGD,
7955 ping_have_iCCP,
7956 ping_have_pHYs,
7957 ping_have_sRGB,
7958 ping_have_tRNS,
7959
7960 ping_exclude_bKGD,
7961 ping_exclude_cHRM,
7962 ping_exclude_date,
7963 /* ping_exclude_EXIF, */
7964 ping_exclude_gAMA,
7965 ping_exclude_iCCP,
7966 /* ping_exclude_iTXt, */
7967 ping_exclude_oFFs,
7968 ping_exclude_pHYs,
7969 ping_exclude_sRGB,
7970 ping_exclude_tEXt,
7971 ping_exclude_tIME,
7972 /* ping_exclude_tRNS, */
7973 ping_exclude_vpAg,
7974 ping_exclude_zCCP, /* hex-encoded iCCP */
7975 ping_exclude_zTXt,
7976
7977 ping_preserve_colormap,
7978 ping_preserve_iCCP,
7979 ping_need_colortype_warning,
7980
7981 status,
7982 tried_332,
7983 tried_333,
7984 tried_444;
7985
7986 MemoryInfo
7987 *volatile pixel_info;
7988
7989 QuantumInfo
7990 *quantum_info;
7991
7992 PNGErrorInfo
7993 error_info;
7994
7995 register ssize_t
7996 i,
7997 x;
7998
7999 unsigned char
8000 *ping_pixels;
8001
8002 volatile int
8003 image_colors,
8004 ping_bit_depth,
8005 ping_color_type,
8006 ping_interlace_method,
8007 ping_compression_method,
8008 ping_filter_method,
8009 ping_num_trans;
8010
8011 volatile size_t
8012 image_depth,
8013 old_bit_depth;
8014
8015 size_t
8016 quality,
8017 rowbytes,
8018 save_image_depth;
8019
8020 int
8021 j,
8022 number_colors,
8023 number_opaque,
8024 number_semitransparent,
8025 number_transparent,
8026 ping_pHYs_unit_type;
8027
8028 png_uint_32
8029 ping_pHYs_x_resolution,
8030 ping_pHYs_y_resolution;
8031
8032 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8033 " Enter WriteOnePNGImage()");
8034
8035 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8036 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8037 if (image_info == (ImageInfo *) NULL)
8038 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8039
8040 /* Define these outside of the following "if logging()" block so they will
8041 * show in debuggers.
8042 */
8043 *im_vers='\0';
8044 (void) ConcatenateMagickString(im_vers,
8045 MagickLibVersionText,MagickPathExtent);
8046 (void) ConcatenateMagickString(im_vers,
8047 MagickLibAddendum,MagickPathExtent);
8048
8049 *libpng_vers='\0';
8050 (void) ConcatenateMagickString(libpng_vers,
8051 PNG_LIBPNG_VER_STRING,32);
8052 *libpng_runv='\0';
8053 (void) ConcatenateMagickString(libpng_runv,
8054 png_get_libpng_ver(NULL),32);
8055
8056 *zlib_vers='\0';
8057 (void) ConcatenateMagickString(zlib_vers,
8058 ZLIB_VERSION,32);
8059 *zlib_runv='\0';
8060 (void) ConcatenateMagickString(zlib_runv,
8061 zlib_version,32);
8062
8063 if (logging != MagickFalse)
8064 {
8065 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8066 im_vers);
8067 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8068 libpng_vers);
8069 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8070 {
8071 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8072 libpng_runv);
8073 }
8074 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8075 zlib_vers);
8076 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8077 {
8078 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8079 zlib_runv);
8080 }
8081 }
8082
8083 /* Initialize some stuff */
8084 ping_bit_depth=0,
8085 ping_color_type=0,
8086 ping_interlace_method=0,
8087 ping_compression_method=0,
8088 ping_filter_method=0,
8089 ping_num_trans = 0;
8090
8091 ping_background.red = 0;
8092 ping_background.green = 0;
8093 ping_background.blue = 0;
8094 ping_background.gray = 0;
8095 ping_background.index = 0;
8096
8097 ping_trans_color.red=0;
8098 ping_trans_color.green=0;
8099 ping_trans_color.blue=0;
8100 ping_trans_color.gray=0;
8101
8102 ping_pHYs_unit_type = 0;
8103 ping_pHYs_x_resolution = 0;
8104 ping_pHYs_y_resolution = 0;
8105
8106 ping_have_blob=MagickFalse;
8107 ping_have_cheap_transparency=MagickFalse;
8108 ping_have_color=MagickTrue;
8109 ping_have_non_bw=MagickTrue;
8110 ping_have_PLTE=MagickFalse;
8111 ping_have_bKGD=MagickFalse;
8112 ping_have_iCCP=MagickFalse;
8113 ping_have_pHYs=MagickFalse;
8114 ping_have_sRGB=MagickFalse;
8115 ping_have_tRNS=MagickFalse;
8116
8117 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8118 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8119 ping_exclude_date=mng_info->ping_exclude_date;
8120 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8121 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8122 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8123 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8124 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8125 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8126 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8127 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8128 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8129 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8130 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8131 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8132 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8133
8134 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8135 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8136 ping_need_colortype_warning = MagickFalse;
8137
8138 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8139 * i.e., eliminate the ICC profile and set image->rendering_intent.
8140 * Note that this will not involve any changes to the actual pixels
8141 * but merely passes information to applications that read the resulting
8142 * PNG image.
8143 *
8144 * To do: recognize other variants of the sRGB profile, using the CRC to
8145 * verify all recognized variants including the 7 already known.
8146 *
8147 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8148 *
8149 * Use something other than image->rendering_intent to record the fact
8150 * that the sRGB profile was found.
8151 *
8152 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8153 * profile. Record the Blackpoint Compensation, if any.
8154 */
8155 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8156 {
8157 char
8158 *name;
8159
8160 const StringInfo
8161 *profile;
8162
8163 ResetImageProfileIterator(image);
8164 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8165 {
8166 profile=GetImageProfile(image,name);
8167
8168 if (profile != (StringInfo *) NULL)
8169 {
8170 if ((LocaleCompare(name,"ICC") == 0) ||
8171 (LocaleCompare(name,"ICM") == 0))
8172
8173 {
8174 int
8175 icheck,
8176 got_crc=0;
8177
8178
8179 png_uint_32
8180 length,
8181 profile_crc=0;
8182
8183 unsigned char
8184 *data;
8185
8186 length=(png_uint_32) GetStringInfoLength(profile);
8187
8188 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8189 {
8190 if (length == sRGB_info[icheck].len)
8191 {
8192 if (got_crc == 0)
8193 {
8194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8195 " Got a %lu-byte ICC profile (potentially sRGB)",
8196 (unsigned long) length);
8197
8198 data=GetStringInfoDatum(profile);
8199 profile_crc=crc32(0,data,length);
8200
8201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 " with crc=%8x",(unsigned int) profile_crc);
8203 got_crc++;
8204 }
8205
8206 if (profile_crc == sRGB_info[icheck].crc)
8207 {
8208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209 " It is sRGB with rendering intent = %s",
8210 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8211 sRGB_info[icheck].intent));
8212 if (image->rendering_intent==UndefinedIntent)
8213 {
8214 image->rendering_intent=
8215 Magick_RenderingIntent_from_PNG_RenderingIntent(
8216 sRGB_info[icheck].intent);
8217 }
8218 ping_exclude_iCCP = MagickTrue;
8219 ping_exclude_zCCP = MagickTrue;
8220 ping_have_sRGB = MagickTrue;
8221 break;
8222 }
8223 }
8224 }
8225 if (sRGB_info[icheck].len == 0)
8226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227 " Got a %lu-byte ICC profile not recognized as sRGB",
8228 (unsigned long) length);
8229 }
8230 }
8231 name=GetNextImageProfile(image);
8232 }
8233 }
8234
8235 number_opaque = 0;
8236 number_semitransparent = 0;
8237 number_transparent = 0;
8238
8239 if (logging != MagickFalse)
8240 {
8241 if (image->storage_class == UndefinedClass)
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243 " image->storage_class=UndefinedClass");
8244 if (image->storage_class == DirectClass)
8245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8246 " image->storage_class=DirectClass");
8247 if (image->storage_class == PseudoClass)
8248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8249 " image->storage_class=PseudoClass");
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8251 " image->taint=MagickTrue":
8252 " image->taint=MagickFalse");
8253 }
8254
8255 if (image->storage_class == PseudoClass &&
8256 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8257 mng_info->write_png48 || mng_info->write_png64 ||
8258 (mng_info->write_png_colortype != 1 &&
8259 mng_info->write_png_colortype != 5)))
8260 {
8261 (void) SyncImage(image,exception);
8262 image->storage_class = DirectClass;
8263 }
8264
8265 if (ping_preserve_colormap == MagickFalse)
8266 {
8267 if (image->storage_class != PseudoClass && image->colormap != NULL)
8268 {
8269 /* Free the bogus colormap; it can cause trouble later */
8270 if (logging != MagickFalse)
8271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8272 " Freeing bogus colormap");
8273 (void) RelinquishMagickMemory(image->colormap);
8274 image->colormap=NULL;
8275 }
8276 }
8277
8278 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8279 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8280
8281 /*
8282 Sometimes we get PseudoClass images whose RGB values don't match
8283 the colors in the colormap. This code syncs the RGB values.
8284 */
8285 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8286 (void) SyncImage(image,exception);
8287
8288 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8289 if (image->depth > 8)
8290 {
8291 if (logging != MagickFalse)
8292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8294
8295 image->depth=8;
8296 }
8297 #endif
8298
8299 /* Respect the -depth option */
8300 if (image->depth < 4)
8301 {
8302 register Quantum
8303 *r;
8304
8305 if (image->depth > 2)
8306 {
8307 /* Scale to 4-bit */
8308 LBR04PacketRGBO(image->background_color);
8309
8310 for (y=0; y < (ssize_t) image->rows; y++)
8311 {
8312 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8313
8314 if (r == (Quantum *) NULL)
8315 break;
8316
8317 for (x=0; x < (ssize_t) image->columns; x++)
8318 {
8319 LBR04PixelRGBA(r);
8320 r+=GetPixelChannels(image);
8321 }
8322
8323 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8324 break;
8325 }
8326
8327 if (image->storage_class == PseudoClass && image->colormap != NULL)
8328 {
8329 for (i=0; i < (ssize_t) image->colors; i++)
8330 {
8331 LBR04PacketRGBO(image->colormap[i]);
8332 }
8333 }
8334 }
8335 else if (image->depth > 1)
8336 {
8337 /* Scale to 2-bit */
8338 LBR02PacketRGBO(image->background_color);
8339
8340 for (y=0; y < (ssize_t) image->rows; y++)
8341 {
8342 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8343
8344 if (r == (Quantum *) NULL)
8345 break;
8346
8347 for (x=0; x < (ssize_t) image->columns; x++)
8348 {
8349 LBR02PixelRGBA(r);
8350 r+=GetPixelChannels(image);
8351 }
8352
8353 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8354 break;
8355 }
8356
8357 if (image->storage_class == PseudoClass && image->colormap != NULL)
8358 {
8359 for (i=0; i < (ssize_t) image->colors; i++)
8360 {
8361 LBR02PacketRGBO(image->colormap[i]);
8362 }
8363 }
8364 }
8365 else
8366 {
8367 /* Scale to 1-bit */
8368 LBR01PacketRGBO(image->background_color);
8369
8370 for (y=0; y < (ssize_t) image->rows; y++)
8371 {
8372 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8373
8374 if (r == (Quantum *) NULL)
8375 break;
8376
8377 for (x=0; x < (ssize_t) image->columns; x++)
8378 {
8379 LBR01PixelRGBA(r);
8380 r+=GetPixelChannels(image);
8381 }
8382
8383 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8384 break;
8385 }
8386
8387 if (image->storage_class == PseudoClass && image->colormap != NULL)
8388 {
8389 for (i=0; i < (ssize_t) image->colors; i++)
8390 {
8391 LBR01PacketRGBO(image->colormap[i]);
8392 }
8393 }
8394 }
8395 }
8396
8397 /* To do: set to next higher multiple of 8 */
8398 if (image->depth < 8)
8399 image->depth=8;
8400
8401 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8402 /* PNG does not handle depths greater than 16 so reduce it even
8403 * if lossy
8404 */
8405 if (image->depth > 8)
8406 image->depth=16;
8407 #endif
8408
8409 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8410 if (image->depth > 8)
8411 {
8412 /* To do: fill low byte properly */
8413 image->depth=16;
8414 }
8415
8416 if (image->depth == 16 && mng_info->write_png_depth != 16)
8417 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8418 image->depth = 8;
8419 #endif
8420
8421 image_colors = (int) image->colors;
8422 number_opaque = (int) image->colors;
8423 number_transparent = 0;
8424 number_semitransparent = 0;
8425
8426 if (mng_info->write_png_colortype &&
8427 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8428 mng_info->write_png_colortype < 4 &&
8429 image->alpha_trait == UndefinedPixelTrait)))
8430 {
8431 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8432 * are not going to need the result.
8433 */
8434 if (mng_info->write_png_colortype == 1 ||
8435 mng_info->write_png_colortype == 5)
8436 ping_have_color=MagickFalse;
8437
8438 if (image->alpha_trait != UndefinedPixelTrait)
8439 {
8440 number_transparent = 2;
8441 number_semitransparent = 1;
8442 }
8443 }
8444
8445 if (mng_info->write_png_colortype < 7)
8446 {
8447 /* BUILD_PALETTE
8448 *
8449 * Normally we run this just once, but in the case of writing PNG8
8450 * we reduce the transparency to binary and run again, then if there
8451 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8452 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8453 * palette. Then (To do) we take care of a final reduction that is only
8454 * needed if there are still 256 colors present and one of them has both
8455 * transparent and opaque instances.
8456 */
8457
8458 tried_332 = MagickFalse;
8459 tried_333 = MagickFalse;
8460 tried_444 = MagickFalse;
8461
8462 for (j=0; j<6; j++)
8463 {
8464 /*
8465 * Sometimes we get DirectClass images that have 256 colors or fewer.
8466 * This code will build a colormap.
8467 *
8468 * Also, sometimes we get PseudoClass images with an out-of-date
8469 * colormap. This code will replace the colormap with a new one.
8470 * Sometimes we get PseudoClass images that have more than 256 colors.
8471 * This code will delete the colormap and change the image to
8472 * DirectClass.
8473 *
8474 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8475 * even though it sometimes contains left-over non-opaque values.
8476 *
8477 * Also we gather some information (number of opaque, transparent,
8478 * and semitransparent pixels, and whether the image has any non-gray
8479 * pixels or only black-and-white pixels) that we might need later.
8480 *
8481 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8482 * we need to check for bogus non-opaque values, at least.
8483 */
8484
8485 int
8486 n;
8487
8488 PixelInfo
8489 opaque[260],
8490 semitransparent[260],
8491 transparent[260];
8492
8493 register const Quantum
8494 *s;
8495
8496 register Quantum
8497 *q,
8498 *r;
8499
8500 if (logging != MagickFalse)
8501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8502 " Enter BUILD_PALETTE:");
8503
8504 if (logging != MagickFalse)
8505 {
8506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8507 " image->columns=%.20g",(double) image->columns);
8508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509 " image->rows=%.20g",(double) image->rows);
8510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8511 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513 " image->depth=%.20g",(double) image->depth);
8514
8515 if (image->storage_class == PseudoClass && image->colormap != NULL)
8516 {
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " Original colormap:");
8519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8520 " i (red,green,blue,alpha)");
8521
8522 for (i=0; i < 256; i++)
8523 {
8524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525 " %d (%d,%d,%d,%d)",
8526 (int) i,
8527 (int) image->colormap[i].red,
8528 (int) image->colormap[i].green,
8529 (int) image->colormap[i].blue,
8530 (int) image->colormap[i].alpha);
8531 }
8532
8533 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8534 {
8535 if (i > 255)
8536 {
8537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8538 " %d (%d,%d,%d,%d)",
8539 (int) i,
8540 (int) image->colormap[i].red,
8541 (int) image->colormap[i].green,
8542 (int) image->colormap[i].blue,
8543 (int) image->colormap[i].alpha);
8544 }
8545 }
8546 }
8547
8548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8549 " image->colors=%d",(int) image->colors);
8550
8551 if (image->colors == 0)
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " (zero means unknown)");
8554
8555 if (ping_preserve_colormap == MagickFalse)
8556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557 " Regenerate the colormap");
8558 }
8559
8560 image_colors=0;
8561 number_opaque = 0;
8562 number_semitransparent = 0;
8563 number_transparent = 0;
8564
8565 for (y=0; y < (ssize_t) image->rows; y++)
8566 {
8567 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8568
8569 if (q == (Quantum *) NULL)
8570 break;
8571
8572 for (x=0; x < (ssize_t) image->columns; x++)
8573 {
8574 if (image->alpha_trait == UndefinedPixelTrait ||
8575 GetPixelAlpha(image,q) == OpaqueAlpha)
8576 {
8577 if (number_opaque < 259)
8578 {
8579 if (number_opaque == 0)
8580 {
8581 GetPixelInfoPixel(image, q, opaque);
8582 opaque[0].alpha=OpaqueAlpha;
8583 number_opaque=1;
8584 }
8585
8586 for (i=0; i< (ssize_t) number_opaque; i++)
8587 {
8588 if (Magick_png_color_equal(image,q,opaque+i))
8589 break;
8590 }
8591
8592 if (i == (ssize_t) number_opaque && number_opaque < 259)
8593 {
8594 number_opaque++;
8595 GetPixelInfoPixel(image, q, opaque+i);
8596 opaque[i].alpha=OpaqueAlpha;
8597 }
8598 }
8599 }
8600 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8601 {
8602 if (number_transparent < 259)
8603 {
8604 if (number_transparent == 0)
8605 {
8606 GetPixelInfoPixel(image, q, transparent);
8607 ping_trans_color.red=(unsigned short)
8608 GetPixelRed(image,q);
8609 ping_trans_color.green=(unsigned short)
8610 GetPixelGreen(image,q);
8611 ping_trans_color.blue=(unsigned short)
8612 GetPixelBlue(image,q);
8613 ping_trans_color.gray=(unsigned short)
8614 GetPixelGray(image,q);
8615 number_transparent = 1;
8616 }
8617
8618 for (i=0; i< (ssize_t) number_transparent; i++)
8619 {
8620 if (Magick_png_color_equal(image,q,transparent+i))
8621 break;
8622 }
8623
8624 if (i == (ssize_t) number_transparent &&
8625 number_transparent < 259)
8626 {
8627 number_transparent++;
8628 GetPixelInfoPixel(image,q,transparent+i);
8629 }
8630 }
8631 }
8632 else
8633 {
8634 if (number_semitransparent < 259)
8635 {
8636 if (number_semitransparent == 0)
8637 {
8638 GetPixelInfoPixel(image,q,semitransparent);
8639 number_semitransparent = 1;
8640 }
8641
8642 for (i=0; i< (ssize_t) number_semitransparent; i++)
8643 {
8644 if (Magick_png_color_equal(image,q,semitransparent+i)
8645 && GetPixelAlpha(image,q) ==
8646 semitransparent[i].alpha)
8647 break;
8648 }
8649
8650 if (i == (ssize_t) number_semitransparent &&
8651 number_semitransparent < 259)
8652 {
8653 number_semitransparent++;
8654 GetPixelInfoPixel(image, q, semitransparent+i);
8655 }
8656 }
8657 }
8658 q+=GetPixelChannels(image);
8659 }
8660 }
8661
8662 if (mng_info->write_png8 == MagickFalse &&
8663 ping_exclude_bKGD == MagickFalse)
8664 {
8665 /* Add the background color to the palette, if it
8666 * isn't already there.
8667 */
8668 if (logging != MagickFalse)
8669 {
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671 " Check colormap for background (%d,%d,%d)",
8672 (int) image->background_color.red,
8673 (int) image->background_color.green,
8674 (int) image->background_color.blue);
8675 }
8676 for (i=0; i<number_opaque; i++)
8677 {
8678 if (opaque[i].red == image->background_color.red &&
8679 opaque[i].green == image->background_color.green &&
8680 opaque[i].blue == image->background_color.blue)
8681 break;
8682 }
8683 if (number_opaque < 259 && i == number_opaque)
8684 {
8685 opaque[i] = image->background_color;
8686 ping_background.index = i;
8687 number_opaque++;
8688 if (logging != MagickFalse)
8689 {
8690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8691 " background_color index is %d",(int) i);
8692 }
8693
8694 }
8695 else if (logging != MagickFalse)
8696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8697 " No room in the colormap to add background color");
8698 }
8699
8700 image_colors=number_opaque+number_transparent+number_semitransparent;
8701
8702 if (logging != MagickFalse)
8703 {
8704 if (image_colors > 256)
8705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8706 " image has more than 256 colors");
8707
8708 else
8709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710 " image has %d colors",image_colors);
8711 }
8712
8713 if (ping_preserve_colormap != MagickFalse)
8714 break;
8715
8716 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8717 {
8718 ping_have_color=MagickFalse;
8719 ping_have_non_bw=MagickFalse;
8720
8721 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8722 {
8723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8724 "incompatible colorspace");
8725 ping_have_color=MagickTrue;
8726 ping_have_non_bw=MagickTrue;
8727 }
8728
8729 if(image_colors > 256)
8730 {
8731 for (y=0; y < (ssize_t) image->rows; y++)
8732 {
8733 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8734
8735 if (q == (Quantum *) NULL)
8736 break;
8737
8738 s=q;
8739 for (x=0; x < (ssize_t) image->columns; x++)
8740 {
8741 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8742 GetPixelRed(image,s) != GetPixelBlue(image,s))
8743 {
8744 ping_have_color=MagickTrue;
8745 ping_have_non_bw=MagickTrue;
8746 break;
8747 }
8748 s+=GetPixelChannels(image);
8749 }
8750
8751 if (ping_have_color != MagickFalse)
8752 break;
8753
8754 /* Worst case is black-and-white; we are looking at every
8755 * pixel twice.
8756 */
8757
8758 if (ping_have_non_bw == MagickFalse)
8759 {
8760 s=q;
8761 for (x=0; x < (ssize_t) image->columns; x++)
8762 {
8763 if (GetPixelRed(image,s) != 0 &&
8764 GetPixelRed(image,s) != QuantumRange)
8765 {
8766 ping_have_non_bw=MagickTrue;
8767 break;
8768 }
8769 s+=GetPixelChannels(image);
8770 }
8771 }
8772 }
8773 }
8774 }
8775
8776 if (image_colors < 257)
8777 {
8778 PixelInfo
8779 colormap[260];
8780
8781 /*
8782 * Initialize image colormap.
8783 */
8784
8785 if (logging != MagickFalse)
8786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8787 " Sort the new colormap");
8788
8789 /* Sort palette, transparent first */;
8790
8791 n = 0;
8792
8793 for (i=0; i<number_transparent; i++)
8794 colormap[n++] = transparent[i];
8795
8796 for (i=0; i<number_semitransparent; i++)
8797 colormap[n++] = semitransparent[i];
8798
8799 for (i=0; i<number_opaque; i++)
8800 colormap[n++] = opaque[i];
8801
8802 ping_background.index +=
8803 (number_transparent + number_semitransparent);
8804
8805 /* image_colors < 257; search the colormap instead of the pixels
8806 * to get ping_have_color and ping_have_non_bw
8807 */
8808 for (i=0; i<n; i++)
8809 {
8810 if (ping_have_color == MagickFalse)
8811 {
8812 if (colormap[i].red != colormap[i].green ||
8813 colormap[i].red != colormap[i].blue)
8814 {
8815 ping_have_color=MagickTrue;
8816 ping_have_non_bw=MagickTrue;
8817 break;
8818 }
8819 }
8820
8821 if (ping_have_non_bw == MagickFalse)
8822 {
8823 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8824 ping_have_non_bw=MagickTrue;
8825 }
8826 }
8827
8828 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8829 (number_transparent == 0 && number_semitransparent == 0)) &&
8830 (((mng_info->write_png_colortype-1) ==
8831 PNG_COLOR_TYPE_PALETTE) ||
8832 (mng_info->write_png_colortype == 0)))
8833 {
8834 if (logging != MagickFalse)
8835 {
8836 if (n != (ssize_t) image_colors)
8837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8838 " image_colors (%d) and n (%d) don't match",
8839 image_colors, n);
8840
8841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8842 " AcquireImageColormap");
8843 }
8844
8845 image->colors = image_colors;
8846
8847 if (AcquireImageColormap(image,image_colors,exception) ==
8848 MagickFalse)
8849 ThrowWriterException(ResourceLimitError,
8850 "MemoryAllocationFailed");
8851
8852 for (i=0; i< (ssize_t) image_colors; i++)
8853 image->colormap[i] = colormap[i];
8854
8855 if (logging != MagickFalse)
8856 {
8857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8858 " image->colors=%d (%d)",
8859 (int) image->colors, image_colors);
8860
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " Update the pixel indexes");
8863 }
8864
8865 /* Sync the pixel indices with the new colormap */
8866
8867 for (y=0; y < (ssize_t) image->rows; y++)
8868 {
8869 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8870
8871 if (q == (Quantum *) NULL)
8872 break;
8873
8874 for (x=0; x < (ssize_t) image->columns; x++)
8875 {
8876 for (i=0; i< (ssize_t) image_colors; i++)
8877 {
8878 if ((image->alpha_trait == UndefinedPixelTrait ||
8879 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8880 image->colormap[i].red == GetPixelRed(image,q) &&
8881 image->colormap[i].green == GetPixelGreen(image,q) &&
8882 image->colormap[i].blue == GetPixelBlue(image,q))
8883 {
8884 SetPixelIndex(image,i,q);
8885 break;
8886 }
8887 }
8888 q+=GetPixelChannels(image);
8889 }
8890
8891 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8892 break;
8893 }
8894 }
8895 }
8896
8897 if (logging != MagickFalse)
8898 {
8899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8900 " image->colors=%d", (int) image->colors);
8901
8902 if (image->colormap != NULL)
8903 {
8904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905 " i (red,green,blue,alpha)");
8906
8907 for (i=0; i < (ssize_t) image->colors; i++)
8908 {
8909 if (i < 300 || i >= (ssize_t) image->colors - 10)
8910 {
8911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8912 " %d (%d,%d,%d,%d)",
8913 (int) i,
8914 (int) image->colormap[i].red,
8915 (int) image->colormap[i].green,
8916 (int) image->colormap[i].blue,
8917 (int) image->colormap[i].alpha);
8918 }
8919 }
8920 }
8921
8922 if (number_transparent < 257)
8923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8924 " number_transparent = %d",
8925 number_transparent);
8926 else
8927
8928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8929 " number_transparent > 256");
8930
8931 if (number_opaque < 257)
8932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8933 " number_opaque = %d",
8934 number_opaque);
8935
8936 else
8937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " number_opaque > 256");
8939
8940 if (number_semitransparent < 257)
8941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " number_semitransparent = %d",
8943 number_semitransparent);
8944
8945 else
8946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8947 " number_semitransparent > 256");
8948
8949 if (ping_have_non_bw == MagickFalse)
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " All pixels and the background are black or white");
8952
8953 else if (ping_have_color == MagickFalse)
8954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8955 " All pixels and the background are gray");
8956
8957 else
8958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " At least one pixel or the background is non-gray");
8960
8961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962 " Exit BUILD_PALETTE:");
8963 }
8964
8965 if (mng_info->write_png8 == MagickFalse)
8966 break;
8967
8968 /* Make any reductions necessary for the PNG8 format */
8969 if (image_colors <= 256 &&
8970 image_colors != 0 && image->colormap != NULL &&
8971 number_semitransparent == 0 &&
8972 number_transparent <= 1)
8973 break;
8974
8975 /* PNG8 can't have semitransparent colors so we threshold the
8976 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8977 * transparent color so if more than one is transparent we merge
8978 * them into image->background_color.
8979 */
8980 if (number_semitransparent != 0 || number_transparent > 1)
8981 {
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983 " Thresholding the alpha channel to binary");
8984
8985 for (y=0; y < (ssize_t) image->rows; y++)
8986 {
8987 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8988
8989 if (r == (Quantum *) NULL)
8990 break;
8991
8992 for (x=0; x < (ssize_t) image->columns; x++)
8993 {
8994 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8995 {
8996 SetPixelViaPixelInfo(image,&image->background_color,r);
8997 SetPixelAlpha(image,TransparentAlpha,r);
8998 }
8999 else
9000 SetPixelAlpha(image,OpaqueAlpha,r);
9001 r+=GetPixelChannels(image);
9002 }
9003
9004 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9005 break;
9006
9007 if (image_colors != 0 && image_colors <= 256 &&
9008 image->colormap != NULL)
9009 for (i=0; i<image_colors; i++)
9010 image->colormap[i].alpha =
9011 (image->colormap[i].alpha > TransparentAlpha/2 ?
9012 TransparentAlpha : OpaqueAlpha);
9013 }
9014 continue;
9015 }
9016
9017 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9018 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9019 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9020 * colors or less.
9021 */
9022 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9023 {
9024 if (logging != MagickFalse)
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " Quantizing the background color to 4-4-4");
9027
9028 tried_444 = MagickTrue;
9029
9030 LBR04PacketRGB(image->background_color);
9031
9032 if (logging != MagickFalse)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " Quantizing the pixel colors to 4-4-4");
9035
9036 if (image->colormap == NULL)
9037 {
9038 for (y=0; y < (ssize_t) image->rows; y++)
9039 {
9040 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9041
9042 if (r == (Quantum *) NULL)
9043 break;
9044
9045 for (x=0; x < (ssize_t) image->columns; x++)
9046 {
9047 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9048 LBR04PixelRGB(r);
9049 r+=GetPixelChannels(image);
9050 }
9051
9052 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9053 break;
9054 }
9055 }
9056
9057 else /* Should not reach this; colormap already exists and
9058 must be <= 256 */
9059 {
9060 if (logging != MagickFalse)
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 " Quantizing the colormap to 4-4-4");
9063
9064 for (i=0; i<image_colors; i++)
9065 {
9066 LBR04PacketRGB(image->colormap[i]);
9067 }
9068 }
9069 continue;
9070 }
9071
9072 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9073 {
9074 if (logging != MagickFalse)
9075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9076 " Quantizing the background color to 3-3-3");
9077
9078 tried_333 = MagickTrue;
9079
9080 LBR03PacketRGB(image->background_color);
9081
9082 if (logging != MagickFalse)
9083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084 " Quantizing the pixel colors to 3-3-3-1");
9085
9086 if (image->colormap == NULL)
9087 {
9088 for (y=0; y < (ssize_t) image->rows; y++)
9089 {
9090 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9091
9092 if (r == (Quantum *) NULL)
9093 break;
9094
9095 for (x=0; x < (ssize_t) image->columns; x++)
9096 {
9097 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9098 LBR03RGB(r);
9099 r+=GetPixelChannels(image);
9100 }
9101
9102 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9103 break;
9104 }
9105 }
9106
9107 else /* Should not reach this; colormap already exists and
9108 must be <= 256 */
9109 {
9110 if (logging != MagickFalse)
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112 " Quantizing the colormap to 3-3-3-1");
9113 for (i=0; i<image_colors; i++)
9114 {
9115 LBR03PacketRGB(image->colormap[i]);
9116 }
9117 }
9118 continue;
9119 }
9120
9121 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9122 {
9123 if (logging != MagickFalse)
9124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9125 " Quantizing the background color to 3-3-2");
9126
9127 tried_332 = MagickTrue;
9128
9129 /* Red and green were already done so we only quantize the blue
9130 * channel
9131 */
9132
9133 LBR02PacketBlue(image->background_color);
9134
9135 if (logging != MagickFalse)
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " Quantizing the pixel colors to 3-3-2-1");
9138
9139 if (image->colormap == NULL)
9140 {
9141 for (y=0; y < (ssize_t) image->rows; y++)
9142 {
9143 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9144
9145 if (r == (Quantum *) NULL)
9146 break;
9147
9148 for (x=0; x < (ssize_t) image->columns; x++)
9149 {
9150 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9151 LBR02PixelBlue(r);
9152 r+=GetPixelChannels(image);
9153 }
9154
9155 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9156 break;
9157 }
9158 }
9159
9160 else /* Should not reach this; colormap already exists and
9161 must be <= 256 */
9162 {
9163 if (logging != MagickFalse)
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " Quantizing the colormap to 3-3-2-1");
9166 for (i=0; i<image_colors; i++)
9167 {
9168 LBR02PacketBlue(image->colormap[i]);
9169 }
9170 }
9171 continue;
9172 }
9173
9174 if (image_colors == 0 || image_colors > 256)
9175 {
9176 /* Take care of special case with 256 opaque colors + 1 transparent
9177 * color. We don't need to quantize to 2-3-2-1; we only need to
9178 * eliminate one color, so we'll merge the two darkest red
9179 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9180 */
9181 if (logging != MagickFalse)
9182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9183 " Merging two dark red background colors to 3-3-2-1");
9184
9185 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9186 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9187 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9188 {
9189 image->background_color.red=ScaleCharToQuantum(0x24);
9190 }
9191
9192 if (logging != MagickFalse)
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " Merging two dark red pixel colors to 3-3-2-1");
9195
9196 if (image->colormap == NULL)
9197 {
9198 for (y=0; y < (ssize_t) image->rows; y++)
9199 {
9200 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9201
9202 if (r == (Quantum *) NULL)
9203 break;
9204
9205 for (x=0; x < (ssize_t) image->columns; x++)
9206 {
9207 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9208 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9209 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9210 GetPixelAlpha(image,r) == OpaqueAlpha)
9211 {
9212 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9213 }
9214 r+=GetPixelChannels(image);
9215 }
9216
9217 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9218 break;
9219
9220 }
9221 }
9222
9223 else
9224 {
9225 for (i=0; i<image_colors; i++)
9226 {
9227 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9228 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9229 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9230 {
9231 image->colormap[i].red=ScaleCharToQuantum(0x24);
9232 }
9233 }
9234 }
9235 }
9236 }
9237 }
9238 /* END OF BUILD_PALETTE */
9239
9240 /* If we are excluding the tRNS chunk and there is transparency,
9241 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9242 * PNG.
9243 */
9244 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9245 (number_transparent != 0 || number_semitransparent != 0))
9246 {
9247 unsigned int colortype=mng_info->write_png_colortype;
9248
9249 if (ping_have_color == MagickFalse)
9250 mng_info->write_png_colortype = 5;
9251
9252 else
9253 mng_info->write_png_colortype = 7;
9254
9255 if (colortype != 0 &&
9256 mng_info->write_png_colortype != colortype)
9257 ping_need_colortype_warning=MagickTrue;
9258
9259 }
9260
9261 /* See if cheap transparency is possible. It is only possible
9262 * when there is a single transparent color, no semitransparent
9263 * color, and no opaque color that has the same RGB components
9264 * as the transparent color. We only need this information if
9265 * we are writing a PNG with colortype 0 or 2, and we have not
9266 * excluded the tRNS chunk.
9267 */
9268 if (number_transparent == 1 &&
9269 mng_info->write_png_colortype < 4)
9270 {
9271 ping_have_cheap_transparency = MagickTrue;
9272
9273 if (number_semitransparent != 0)
9274 ping_have_cheap_transparency = MagickFalse;
9275
9276 else if (image_colors == 0 || image_colors > 256 ||
9277 image->colormap == NULL)
9278 {
9279 register const Quantum
9280 *q;
9281
9282 for (y=0; y < (ssize_t) image->rows; y++)
9283 {
9284 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9285
9286 if (q == (Quantum *) NULL)
9287 break;
9288
9289 for (x=0; x < (ssize_t) image->columns; x++)
9290 {
9291 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9292 (unsigned short) GetPixelRed(image,q) ==
9293 ping_trans_color.red &&
9294 (unsigned short) GetPixelGreen(image,q) ==
9295 ping_trans_color.green &&
9296 (unsigned short) GetPixelBlue(image,q) ==
9297 ping_trans_color.blue)
9298 {
9299 ping_have_cheap_transparency = MagickFalse;
9300 break;
9301 }
9302
9303 q+=GetPixelChannels(image);
9304 }
9305
9306 if (ping_have_cheap_transparency == MagickFalse)
9307 break;
9308 }
9309 }
9310 else
9311 {
9312 /* Assuming that image->colormap[0] is the one transparent color
9313 * and that all others are opaque.
9314 */
9315 if (image_colors > 1)
9316 for (i=1; i<image_colors; i++)
9317 if (image->colormap[i].red == image->colormap[0].red &&
9318 image->colormap[i].green == image->colormap[0].green &&
9319 image->colormap[i].blue == image->colormap[0].blue)
9320 {
9321 ping_have_cheap_transparency = MagickFalse;
9322 break;
9323 }
9324 }
9325
9326 if (logging != MagickFalse)
9327 {
9328 if (ping_have_cheap_transparency == MagickFalse)
9329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9330 " Cheap transparency is not possible.");
9331
9332 else
9333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9334 " Cheap transparency is possible.");
9335 }
9336 }
9337 else
9338 ping_have_cheap_transparency = MagickFalse;
9339
9340 image_depth=image->depth;
9341
9342 quantum_info = (QuantumInfo *) NULL;
9343 number_colors=0;
9344 image_colors=(int) image->colors;
9345 image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9346
9347 if (mng_info->write_png_colortype < 5)
9348 mng_info->IsPalette=image->storage_class == PseudoClass &&
9349 image_colors <= 256 && image->colormap != NULL;
9350 else
9351 mng_info->IsPalette = MagickFalse;
9352
9353 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9354 (image->colors == 0 || image->colormap == NULL))
9355 {
9356 image_info=DestroyImageInfo(image_info);
9357 image=DestroyImage(image);
9358 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9359 "Cannot write PNG8 or color-type 3; colormap is NULL",
9360 "`%s'",IMimage->filename);
9361 return(MagickFalse);
9362 }
9363
9364 /*
9365 Allocate the PNG structures
9366 */
9367 #ifdef PNG_USER_MEM_SUPPORTED
9368 error_info.image=image;
9369 error_info.exception=exception;
9370 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9371 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9372 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9373
9374 #else
9375 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9376 MagickPNGErrorHandler,MagickPNGWarningHandler);
9377
9378 #endif
9379 if (ping == (png_struct *) NULL)
9380 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9381
9382 ping_info=png_create_info_struct(ping);
9383
9384 if (ping_info == (png_info *) NULL)
9385 {
9386 png_destroy_write_struct(&ping,(png_info **) NULL);
9387 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9388 }
9389
9390 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9391 pixel_info=(MemoryInfo *) NULL;
9392
9393 if (setjmp(png_jmpbuf(ping)))
9394 {
9395 /*
9396 PNG write failed.
9397 */
9398 #ifdef PNG_DEBUG
9399 if (image_info->verbose)
9400 (void) printf("PNG write has failed.\n");
9401 #endif
9402 png_destroy_write_struct(&ping,&ping_info);
9403 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9404 UnlockSemaphoreInfo(ping_semaphore);
9405 #endif
9406
9407 if (pixel_info != (MemoryInfo *) NULL)
9408 pixel_info=RelinquishVirtualMemory(pixel_info);
9409
9410 if (quantum_info != (QuantumInfo *) NULL)
9411 quantum_info=DestroyQuantumInfo(quantum_info);
9412
9413 if (ping_have_blob != MagickFalse)
9414 (void) CloseBlob(image);
9415 image_info=DestroyImageInfo(image_info);
9416 image=DestroyImage(image);
9417 return(MagickFalse);
9418 }
9419
9420 /* { For navigation to end of SETJMP-protected block. Within this
9421 * block, use png_error() instead of Throwing an Exception, to ensure
9422 * that libpng is able to clean up, and that the semaphore is unlocked.
9423 */
9424
9425 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9426 LockSemaphoreInfo(ping_semaphore);
9427 #endif
9428
9429 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9430 /* Allow benign errors */
9431 png_set_benign_errors(ping, 1);
9432 #endif
9433
9434 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9435 /* Reject images with too many rows or columns */
9436 png_set_user_limits(ping,
9437 (png_uint_32) MagickMin(0x7fffffffL,
9438 GetMagickResourceLimit(WidthResource)),
9439 (png_uint_32) MagickMin(0x7fffffffL,
9440 GetMagickResourceLimit(HeightResource)));
9441 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9442
9443 /*
9444 Prepare PNG for writing.
9445 */
9446
9447 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9448 if (mng_info->write_mng)
9449 {
9450 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9451 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9452 /* Disable new libpng-1.5.10 feature when writing a MNG because
9453 * zero-length PLTE is OK
9454 */
9455 png_set_check_for_invalid_index (ping, 0);
9456 # endif
9457 }
9458
9459 #else
9460 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9461 if (mng_info->write_mng)
9462 png_permit_empty_plte(ping,MagickTrue);
9463
9464 # endif
9465 #endif
9466
9467 x=0;
9468
9469 ping_width=(png_uint_32) image->columns;
9470 ping_height=(png_uint_32) image->rows;
9471
9472 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9473 image_depth=8;
9474
9475 if (mng_info->write_png48 || mng_info->write_png64)
9476 image_depth=16;
9477
9478 if (mng_info->write_png_depth != 0)
9479 image_depth=mng_info->write_png_depth;
9480
9481 /* Adjust requested depth to next higher valid depth if necessary */
9482 if (image_depth > 8)
9483 image_depth=16;
9484
9485 if ((image_depth > 4) && (image_depth < 8))
9486 image_depth=8;
9487
9488 if (image_depth == 3)
9489 image_depth=4;
9490
9491 if (logging != MagickFalse)
9492 {
9493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494 " width=%.20g",(double) ping_width);
9495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9496 " height=%.20g",(double) ping_height);
9497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9498 " image_matte=%.20g",(double) image->alpha_trait);
9499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9500 " image->depth=%.20g",(double) image->depth);
9501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9502 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9503 }
9504
9505 save_image_depth=image_depth;
9506 ping_bit_depth=(png_byte) save_image_depth;
9507
9508
9509 #if defined(PNG_pHYs_SUPPORTED)
9510 if (ping_exclude_pHYs == MagickFalse)
9511 {
9512 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9513 (!mng_info->write_mng || !mng_info->equal_physs))
9514 {
9515 if (logging != MagickFalse)
9516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517 " Setting up pHYs chunk");
9518
9519 if (image->units == PixelsPerInchResolution)
9520 {
9521 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9522 ping_pHYs_x_resolution=
9523 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9524 ping_pHYs_y_resolution=
9525 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9526 }
9527
9528 else if (image->units == PixelsPerCentimeterResolution)
9529 {
9530 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9531 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9532 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9533 }
9534
9535 else
9536 {
9537 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9538 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9539 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9540 }
9541
9542 if (logging != MagickFalse)
9543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9545 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9546 (int) ping_pHYs_unit_type);
9547 ping_have_pHYs = MagickTrue;
9548 }
9549 }
9550 #endif
9551
9552 if (ping_exclude_bKGD == MagickFalse)
9553 {
9554 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9555 {
9556 unsigned int
9557 mask;
9558
9559 mask=0xffff;
9560 if (ping_bit_depth == 8)
9561 mask=0x00ff;
9562
9563 if (ping_bit_depth == 4)
9564 mask=0x000f;
9565
9566 if (ping_bit_depth == 2)
9567 mask=0x0003;
9568
9569 if (ping_bit_depth == 1)
9570 mask=0x0001;
9571
9572 ping_background.red=(png_uint_16)
9573 (ScaleQuantumToShort(image->background_color.red) & mask);
9574
9575 ping_background.green=(png_uint_16)
9576 (ScaleQuantumToShort(image->background_color.green) & mask);
9577
9578 ping_background.blue=(png_uint_16)
9579 (ScaleQuantumToShort(image->background_color.blue) & mask);
9580
9581 ping_background.gray=(png_uint_16) ping_background.green;
9582 }
9583
9584 if (logging != MagickFalse)
9585 {
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Setting up bKGD chunk (1)");
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589 " background_color index is %d",
9590 (int) ping_background.index);
9591
9592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9593 " ping_bit_depth=%d",ping_bit_depth);
9594 }
9595
9596 ping_have_bKGD = MagickTrue;
9597 }
9598
9599 /*
9600 Select the color type.
9601 */
9602 matte=image_matte;
9603 old_bit_depth=0;
9604
9605 if (mng_info->IsPalette && mng_info->write_png8)
9606 {
9607 /* To do: make this a function cause it's used twice, except
9608 for reducing the sample depth from 8. */
9609
9610 number_colors=image_colors;
9611
9612 ping_have_tRNS=MagickFalse;
9613
9614 /*
9615 Set image palette.
9616 */
9617 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9618
9619 if (logging != MagickFalse)
9620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621 " Setting up PLTE chunk with %d colors (%d)",
9622 number_colors, image_colors);
9623
9624 for (i=0; i < (ssize_t) number_colors; i++)
9625 {
9626 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9627 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9628 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9629 if (logging != MagickFalse)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 #if MAGICKCORE_QUANTUM_DEPTH == 8
9632 " %3ld (%3d,%3d,%3d)",
9633 #else
9634 " %5ld (%5d,%5d,%5d)",
9635 #endif
9636 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9637
9638 }
9639
9640 ping_have_PLTE=MagickTrue;
9641 image_depth=ping_bit_depth;
9642 ping_num_trans=0;
9643
9644 if (matte != MagickFalse)
9645 {
9646 /*
9647 Identify which colormap entry is transparent.
9648 */
9649 assert(number_colors <= 256);
9650 assert(image->colormap != NULL);
9651
9652 for (i=0; i < (ssize_t) number_transparent; i++)
9653 ping_trans_alpha[i]=0;
9654
9655
9656 ping_num_trans=(unsigned short) (number_transparent +
9657 number_semitransparent);
9658
9659 if (ping_num_trans == 0)
9660 ping_have_tRNS=MagickFalse;
9661
9662 else
9663 ping_have_tRNS=MagickTrue;
9664 }
9665
9666 if (ping_exclude_bKGD == MagickFalse)
9667 {
9668 /*
9669 * Identify which colormap entry is the background color.
9670 */
9671
9672 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9673 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9674 break;
9675
9676 ping_background.index=(png_byte) i;
9677
9678 if (logging != MagickFalse)
9679 {
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " background_color index is %d",
9682 (int) ping_background.index);
9683 }
9684 }
9685 } /* end of write_png8 */
9686
9687 else if (mng_info->write_png_colortype == 1)
9688 {
9689 image_matte=MagickFalse;
9690 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9691 }
9692
9693 else if (mng_info->write_png24 || mng_info->write_png48 ||
9694 mng_info->write_png_colortype == 3)
9695 {
9696 image_matte=MagickFalse;
9697 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9698 }
9699
9700 else if (mng_info->write_png32 || mng_info->write_png64 ||
9701 mng_info->write_png_colortype == 7)
9702 {
9703 image_matte=MagickTrue;
9704 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9705 }
9706
9707 else /* mng_info->write_pngNN not specified */
9708 {
9709 image_depth=ping_bit_depth;
9710
9711 if (mng_info->write_png_colortype != 0)
9712 {
9713 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9714
9715 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9716 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9717 image_matte=MagickTrue;
9718
9719 else
9720 image_matte=MagickFalse;
9721
9722 if (logging != MagickFalse)
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 " PNG colortype %d was specified:",(int) ping_color_type);
9725 }
9726
9727 else /* write_png_colortype not specified */
9728 {
9729 if (logging != MagickFalse)
9730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9731 " Selecting PNG colortype:");
9732
9733 ping_color_type=(png_byte) ((matte != MagickFalse)?
9734 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9735
9736 if (image_info->type == TrueColorType)
9737 {
9738 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9739 image_matte=MagickFalse;
9740 }
9741
9742 if (image_info->type == TrueColorAlphaType)
9743 {
9744 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9745 image_matte=MagickTrue;
9746 }
9747
9748 if (image_info->type == PaletteType ||
9749 image_info->type == PaletteAlphaType)
9750 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9751
9752 if (mng_info->write_png_colortype == 0 &&
9753 image_info->type == UndefinedType)
9754 {
9755 if (ping_have_color == MagickFalse)
9756 {
9757 if (image_matte == MagickFalse)
9758 {
9759 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9760 image_matte=MagickFalse;
9761 }
9762
9763 else
9764 {
9765 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9766 image_matte=MagickTrue;
9767 }
9768 }
9769 else
9770 {
9771 if (image_matte == MagickFalse)
9772 {
9773 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9774 image_matte=MagickFalse;
9775 }
9776
9777 else
9778 {
9779 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9780 image_matte=MagickTrue;
9781 }
9782 }
9783 }
9784
9785 }
9786
9787 if (logging != MagickFalse)
9788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9789 " Selected PNG colortype=%d",ping_color_type);
9790
9791 if (ping_bit_depth < 8)
9792 {
9793 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9794 ping_color_type == PNG_COLOR_TYPE_RGB ||
9795 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9796 ping_bit_depth=8;
9797 }
9798
9799 old_bit_depth=ping_bit_depth;
9800
9801 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9802 {
9803 if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9804 ping_bit_depth=1;
9805 }
9806
9807 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9808 {
9809 size_t one = 1;
9810 ping_bit_depth=1;
9811
9812 if (image->colors == 0)
9813 {
9814 /* DO SOMETHING */
9815 png_error(ping,"image has 0 colors");
9816 }
9817
9818 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9819 ping_bit_depth <<= 1;
9820 }
9821
9822 if (logging != MagickFalse)
9823 {
9824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Number of colors: %.20g",(double) image_colors);
9826
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Tentative PNG bit depth: %d",ping_bit_depth);
9829 }
9830
9831 if (ping_bit_depth < (int) mng_info->write_png_depth)
9832 ping_bit_depth = mng_info->write_png_depth;
9833 }
9834
9835 image_depth=ping_bit_depth;
9836
9837 if (logging != MagickFalse)
9838 {
9839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 " Tentative PNG color type: %s (%.20g)",
9841 PngColorTypeToString(ping_color_type),
9842 (double) ping_color_type);
9843
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " image_info->type: %.20g",(double) image_info->type);
9846
9847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9848 " image_depth: %.20g",(double) image_depth);
9849
9850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851
9852 " image->depth: %.20g",(double) image->depth);
9853
9854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9855 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9856 }
9857
9858 if (matte != MagickFalse)
9859 {
9860 if (mng_info->IsPalette)
9861 {
9862 if (mng_info->write_png_colortype == 0)
9863 {
9864 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9865
9866 if (ping_have_color != MagickFalse)
9867 ping_color_type=PNG_COLOR_TYPE_RGBA;
9868 }
9869
9870 /*
9871 * Determine if there is any transparent color.
9872 */
9873 if (number_transparent + number_semitransparent == 0)
9874 {
9875 /*
9876 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9877 */
9878
9879 image_matte=MagickFalse;
9880
9881 if (mng_info->write_png_colortype == 0)
9882 ping_color_type&=0x03;
9883 }
9884
9885 else
9886 {
9887 unsigned int
9888 mask;
9889
9890 mask=0xffff;
9891
9892 if (ping_bit_depth == 8)
9893 mask=0x00ff;
9894
9895 if (ping_bit_depth == 4)
9896 mask=0x000f;
9897
9898 if (ping_bit_depth == 2)
9899 mask=0x0003;
9900
9901 if (ping_bit_depth == 1)
9902 mask=0x0001;
9903
9904 ping_trans_color.red=(png_uint_16)
9905 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9906
9907 ping_trans_color.green=(png_uint_16)
9908 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9909
9910 ping_trans_color.blue=(png_uint_16)
9911 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9912
9913 ping_trans_color.gray=(png_uint_16)
9914 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9915 image->colormap)) & mask);
9916
9917 ping_trans_color.index=(png_byte) 0;
9918
9919 ping_have_tRNS=MagickTrue;
9920 }
9921
9922 if (ping_have_tRNS != MagickFalse)
9923 {
9924 /*
9925 * Determine if there is one and only one transparent color
9926 * and if so if it is fully transparent.
9927 */
9928 if (ping_have_cheap_transparency == MagickFalse)
9929 ping_have_tRNS=MagickFalse;
9930 }
9931
9932 if (ping_have_tRNS != MagickFalse)
9933 {
9934 if (mng_info->write_png_colortype == 0)
9935 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9936
9937 if (image_depth == 8)
9938 {
9939 ping_trans_color.red&=0xff;
9940 ping_trans_color.green&=0xff;
9941 ping_trans_color.blue&=0xff;
9942 ping_trans_color.gray&=0xff;
9943 }
9944 }
9945 }
9946 else
9947 {
9948 if (image_depth == 8)
9949 {
9950 ping_trans_color.red&=0xff;
9951 ping_trans_color.green&=0xff;
9952 ping_trans_color.blue&=0xff;
9953 ping_trans_color.gray&=0xff;
9954 }
9955 }
9956 }
9957
9958 matte=image_matte;
9959
9960 if (ping_have_tRNS != MagickFalse)
9961 image_matte=MagickFalse;
9962
9963 if ((mng_info->IsPalette) &&
9964 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9965 ping_have_color == MagickFalse &&
9966 (image_matte == MagickFalse || image_depth >= 8))
9967 {
9968 size_t one=1;
9969
9970 if (image_matte != MagickFalse)
9971 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9972
9973 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9974 {
9975 ping_color_type=PNG_COLOR_TYPE_GRAY;
9976
9977 if (save_image_depth == 16 && image_depth == 8)
9978 {
9979 if (logging != MagickFalse)
9980 {
9981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9982 " Scaling ping_trans_color (0)");
9983 }
9984 ping_trans_color.gray*=0x0101;
9985 }
9986 }
9987
9988 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9989 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9990
9991 if ((image_colors == 0) ||
9992 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9993 image_colors=(int) (one << image_depth);
9994
9995 if (image_depth > 8)
9996 ping_bit_depth=16;
9997
9998 else
9999 {
10000 ping_bit_depth=8;
10001 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10002 {
10003 if(!mng_info->write_png_depth)
10004 {
10005 ping_bit_depth=1;
10006
10007 while ((int) (one << ping_bit_depth)
10008 < (ssize_t) image_colors)
10009 ping_bit_depth <<= 1;
10010 }
10011 }
10012
10013 else if (ping_color_type ==
10014 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10015 mng_info->IsPalette)
10016 {
10017 /* Check if grayscale is reducible */
10018
10019 int
10020 depth_4_ok=MagickTrue,
10021 depth_2_ok=MagickTrue,
10022 depth_1_ok=MagickTrue;
10023
10024 for (i=0; i < (ssize_t) image_colors; i++)
10025 {
10026 unsigned char
10027 intensity;
10028
10029 intensity=ScaleQuantumToChar(image->colormap[i].red);
10030
10031 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10032 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10033 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10034 depth_2_ok=depth_1_ok=MagickFalse;
10035 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10036 depth_1_ok=MagickFalse;
10037 }
10038
10039 if (depth_1_ok && mng_info->write_png_depth <= 1)
10040 ping_bit_depth=1;
10041
10042 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10043 ping_bit_depth=2;
10044
10045 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10046 ping_bit_depth=4;
10047 }
10048 }
10049
10050 image_depth=ping_bit_depth;
10051 }
10052
10053 else
10054
10055 if (mng_info->IsPalette)
10056 {
10057 number_colors=image_colors;
10058
10059 if (image_depth <= 8)
10060 {
10061 /*
10062 Set image palette.
10063 */
10064 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10065
10066 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10067 {
10068 for (i=0; i < (ssize_t) number_colors; i++)
10069 {
10070 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10071 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10072 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10073 }
10074
10075 if (logging != MagickFalse)
10076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10077 " Setting up PLTE chunk with %d colors",
10078 number_colors);
10079
10080 ping_have_PLTE=MagickTrue;
10081 }
10082
10083 /* color_type is PNG_COLOR_TYPE_PALETTE */
10084 if (mng_info->write_png_depth == 0)
10085 {
10086 size_t
10087 one;
10088
10089 ping_bit_depth=1;
10090 one=1;
10091
10092 while ((one << ping_bit_depth) < (size_t) number_colors)
10093 ping_bit_depth <<= 1;
10094 }
10095
10096 ping_num_trans=0;
10097
10098 if (matte != MagickFalse)
10099 {
10100 /*
10101 * Set up trans_colors array.
10102 */
10103 assert(number_colors <= 256);
10104
10105 ping_num_trans=(unsigned short) (number_transparent +
10106 number_semitransparent);
10107
10108 if (ping_num_trans == 0)
10109 ping_have_tRNS=MagickFalse;
10110
10111 else
10112 {
10113 if (logging != MagickFalse)
10114 {
10115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10116 " Scaling ping_trans_color (1)");
10117 }
10118 ping_have_tRNS=MagickTrue;
10119
10120 for (i=0; i < ping_num_trans; i++)
10121 {
10122 ping_trans_alpha[i]= (png_byte)
10123 ScaleQuantumToChar(image->colormap[i].alpha);
10124 }
10125 }
10126 }
10127 }
10128 }
10129
10130 else
10131 {
10132
10133 if (image_depth < 8)
10134 image_depth=8;
10135
10136 if ((save_image_depth == 16) && (image_depth == 8))
10137 {
10138 if (logging != MagickFalse)
10139 {
10140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141 " Scaling ping_trans_color from (%d,%d,%d)",
10142 (int) ping_trans_color.red,
10143 (int) ping_trans_color.green,
10144 (int) ping_trans_color.blue);
10145 }
10146
10147 ping_trans_color.red*=0x0101;
10148 ping_trans_color.green*=0x0101;
10149 ping_trans_color.blue*=0x0101;
10150 ping_trans_color.gray*=0x0101;
10151
10152 if (logging != MagickFalse)
10153 {
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " to (%d,%d,%d)",
10156 (int) ping_trans_color.red,
10157 (int) ping_trans_color.green,
10158 (int) ping_trans_color.blue);
10159 }
10160 }
10161 }
10162
10163 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10164 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10165
10166 /*
10167 Adjust background and transparency samples in sub-8-bit grayscale files.
10168 */
10169 if (ping_bit_depth < 8 && ping_color_type ==
10170 PNG_COLOR_TYPE_GRAY)
10171 {
10172 png_uint_16
10173 maxval;
10174
10175 size_t
10176 one=1;
10177
10178 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10179
10180 if (ping_exclude_bKGD == MagickFalse)
10181 {
10182
10183 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10184 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10185 &image->background_color))) +.5)));
10186
10187 if (logging != MagickFalse)
10188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189 " Setting up bKGD chunk (2)");
10190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10191 " background_color index is %d",
10192 (int) ping_background.index);
10193
10194 ping_have_bKGD = MagickTrue;
10195 }
10196
10197 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Scaling ping_trans_color.gray from %d",
10200 (int)ping_trans_color.gray);
10201
10202 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10203 ping_trans_color.gray)+.5);
10204
10205 if (logging != MagickFalse)
10206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10207 " to %d", (int)ping_trans_color.gray);
10208 }
10209
10210 if (ping_exclude_bKGD == MagickFalse)
10211 {
10212 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10213 {
10214 /*
10215 Identify which colormap entry is the background color.
10216 */
10217
10218 number_colors=image_colors;
10219
10220 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10221 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10222 break;
10223
10224 ping_background.index=(png_byte) i;
10225
10226 if (logging != MagickFalse)
10227 {
10228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10229 " Setting up bKGD chunk with index=%d",(int) i);
10230 }
10231
10232 if (i < (ssize_t) number_colors)
10233 {
10234 ping_have_bKGD = MagickTrue;
10235
10236 if (logging != MagickFalse)
10237 {
10238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10239 " background =(%d,%d,%d)",
10240 (int) ping_background.red,
10241 (int) ping_background.green,
10242 (int) ping_background.blue);
10243 }
10244 }
10245
10246 else /* Can't happen */
10247 {
10248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " No room in PLTE to add bKGD color");
10251 ping_have_bKGD = MagickFalse;
10252 }
10253 }
10254 }
10255
10256 if (logging != MagickFalse)
10257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10258 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10259 ping_color_type);
10260 /*
10261 Initialize compression level and filtering.
10262 */
10263 if (logging != MagickFalse)
10264 {
10265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266 " Setting up deflate compression");
10267
10268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10269 " Compression buffer size: 32768");
10270 }
10271
10272 png_set_compression_buffer_size(ping,32768L);
10273
10274 if (logging != MagickFalse)
10275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276 " Compression mem level: 9");
10277
10278 png_set_compression_mem_level(ping, 9);
10279
10280 /* Untangle the "-quality" setting:
10281
10282 Undefined is 0; the default is used.
10283 Default is 75
10284
10285 10's digit:
10286
10287 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10288 zlib default compression level
10289
10290 1-9: the zlib compression level
10291
10292 1's digit:
10293
10294 0-4: the PNG filter method
10295
10296 5: libpng adaptive filtering if compression level > 5
10297 libpng filter type "none" if compression level <= 5
10298 or if image is grayscale or palette
10299
10300 6: libpng adaptive filtering
10301
10302 7: "LOCO" filtering (intrapixel differing) if writing
10303 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10304 and earlier because of a missing "else".
10305
10306 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10307 filtering. Unused prior to IM-6.7.0-10, was same as 6
10308
10309 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10310 Unused prior to IM-6.7.0-10, was same as 6
10311
10312 Note that using the -quality option, not all combinations of
10313 PNG filter type, zlib compression level, and zlib compression
10314 strategy are possible. This will be addressed soon in a
10315 release that accomodates "-define png:compression-strategy", etc.
10316
10317 */
10318
10319 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10320 image_info->quality;
10321
10322 if (quality <= 9)
10323 {
10324 if (mng_info->write_png_compression_strategy == 0)
10325 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10326 }
10327
10328 else if (mng_info->write_png_compression_level == 0)
10329 {
10330 int
10331 level;
10332
10333 level=(int) MagickMin((ssize_t) quality/10,9);
10334
10335 mng_info->write_png_compression_level = level+1;
10336 }
10337
10338 if (mng_info->write_png_compression_strategy == 0)
10339 {
10340 if ((quality %10) == 8 || (quality %10) == 9)
10341 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10342 mng_info->write_png_compression_strategy=Z_RLE+1;
10343 #else
10344 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10345 #endif
10346 }
10347
10348 if (mng_info->write_png_compression_filter == 0)
10349 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10350
10351 if (logging != MagickFalse)
10352 {
10353 if (mng_info->write_png_compression_level)
10354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10355 " Compression level: %d",
10356 (int) mng_info->write_png_compression_level-1);
10357
10358 if (mng_info->write_png_compression_strategy)
10359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Compression strategy: %d",
10361 (int) mng_info->write_png_compression_strategy-1);
10362
10363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10364 " Setting up filtering");
10365
10366 if (mng_info->write_png_compression_filter == 6)
10367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368 " Base filter method: ADAPTIVE");
10369 else if (mng_info->write_png_compression_filter == 0 ||
10370 mng_info->write_png_compression_filter == 1)
10371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10372 " Base filter method: NONE");
10373 else
10374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10375 " Base filter method: %d",
10376 (int) mng_info->write_png_compression_filter-1);
10377 }
10378
10379 if (mng_info->write_png_compression_level != 0)
10380 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10381
10382 if (mng_info->write_png_compression_filter == 6)
10383 {
10384 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10385 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10386 (quality < 50))
10387 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10388 else
10389 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10390 }
10391 else if (mng_info->write_png_compression_filter == 7 ||
10392 mng_info->write_png_compression_filter == 10)
10393 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10394
10395 else if (mng_info->write_png_compression_filter == 8)
10396 {
10397 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10398 if (mng_info->write_mng)
10399 {
10400 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10401 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10402 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10403 }
10404 #endif
10405 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10406 }
10407
10408 else if (mng_info->write_png_compression_filter == 9)
10409 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10410
10411 else if (mng_info->write_png_compression_filter != 0)
10412 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10413 mng_info->write_png_compression_filter-1);
10414
10415 if (mng_info->write_png_compression_strategy != 0)
10416 png_set_compression_strategy(ping,
10417 mng_info->write_png_compression_strategy-1);
10418
10419 ping_interlace_method=image_info->interlace != NoInterlace;
10420
10421 if (mng_info->write_mng)
10422 png_set_sig_bytes(ping,8);
10423
10424 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10425
10426 if (mng_info->write_png_colortype != 0)
10427 {
10428 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10429 if (ping_have_color != MagickFalse)
10430 {
10431 ping_color_type = PNG_COLOR_TYPE_RGB;
10432
10433 if (ping_bit_depth < 8)
10434 ping_bit_depth=8;
10435 }
10436
10437 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10438 if (ping_have_color != MagickFalse)
10439 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10440 }
10441
10442 if (ping_need_colortype_warning != MagickFalse ||
10443 ((mng_info->write_png_depth &&
10444 (int) mng_info->write_png_depth != ping_bit_depth) ||
10445 (mng_info->write_png_colortype &&
10446 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10447 mng_info->write_png_colortype != 7 &&
10448 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10449 {
10450 if (logging != MagickFalse)
10451 {
10452 if (ping_need_colortype_warning != MagickFalse)
10453 {
10454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455 " Image has transparency but tRNS chunk was excluded");
10456 }
10457
10458 if (mng_info->write_png_depth)
10459 {
10460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10461 " Defined png:bit-depth=%u, Computed depth=%u",
10462 mng_info->write_png_depth,
10463 ping_bit_depth);
10464 }
10465
10466 if (mng_info->write_png_colortype)
10467 {
10468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469 " Defined png:color-type=%u, Computed color type=%u",
10470 mng_info->write_png_colortype-1,
10471 ping_color_type);
10472 }
10473 }
10474
10475 png_warning(ping,
10476 "Cannot write image with defined png:bit-depth or png:color-type.");
10477 }
10478
10479 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10480 {
10481 /* Add an opaque matte channel */
10482 image->alpha_trait = BlendPixelTrait;
10483 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10484
10485 if (logging != MagickFalse)
10486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487 " Added an opaque matte channel");
10488 }
10489
10490 if (number_transparent != 0 || number_semitransparent != 0)
10491 {
10492 if (ping_color_type < 4)
10493 {
10494 ping_have_tRNS=MagickTrue;
10495 if (logging != MagickFalse)
10496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10497 " Setting ping_have_tRNS=MagickTrue.");
10498 }
10499 }
10500
10501 if (logging != MagickFalse)
10502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10503 " Writing PNG header chunks");
10504
10505 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10506 ping_bit_depth,ping_color_type,
10507 ping_interlace_method,ping_compression_method,
10508 ping_filter_method);
10509
10510 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10511 {
10512 png_set_PLTE(ping,ping_info,palette,number_colors);
10513
10514 if (logging != MagickFalse)
10515 {
10516 for (i=0; i< (ssize_t) number_colors; i++)
10517 {
10518 if (i < ping_num_trans)
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10521 (int) i,
10522 (int) palette[i].red,
10523 (int) palette[i].green,
10524 (int) palette[i].blue,
10525 (int) i,
10526 (int) ping_trans_alpha[i]);
10527 else
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " PLTE[%d] = (%d,%d,%d)",
10530 (int) i,
10531 (int) palette[i].red,
10532 (int) palette[i].green,
10533 (int) palette[i].blue);
10534 }
10535 }
10536 }
10537
10538 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10539 if (ping_exclude_sRGB != MagickFalse ||
10540 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10541 {
10542 if ((ping_exclude_tEXt == MagickFalse ||
10543 ping_exclude_zTXt == MagickFalse) &&
10544 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10545 {
10546 ResetImageProfileIterator(image);
10547 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10548 {
10549 profile=GetImageProfile(image,name);
10550
10551 if (profile != (StringInfo *) NULL)
10552 {
10553 #ifdef PNG_WRITE_iCCP_SUPPORTED
10554 if ((LocaleCompare(name,"ICC") == 0) ||
10555 (LocaleCompare(name,"ICM") == 0))
10556 {
10557
10558 if (ping_exclude_iCCP == MagickFalse)
10559 {
10560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561 " Setting up iCCP chunk");
10562
10563 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10564 #if (PNG_LIBPNG_VER < 10500)
10565 (png_charp) GetStringInfoDatum(profile),
10566 #else
10567 (const png_byte *) GetStringInfoDatum(profile),
10568 #endif
10569 (png_uint_32) GetStringInfoLength(profile));
10570 ping_have_iCCP = MagickTrue;
10571 }
10572 }
10573
10574 else
10575 #endif
10576 if (ping_exclude_zCCP == MagickFalse)
10577 {
10578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10579 " Setting up zTXT chunk with uuencoded ICC");
10580 Magick_png_write_raw_profile(image_info,ping,ping_info,
10581 (unsigned char *) name,(unsigned char *) name,
10582 GetStringInfoDatum(profile),
10583 (png_uint_32) GetStringInfoLength(profile));
10584 ping_have_iCCP = MagickTrue;
10585 }
10586 }
10587
10588 if (logging != MagickFalse)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Setting up text chunk with %s profile",name);
10591
10592 name=GetNextImageProfile(image);
10593 }
10594 }
10595 }
10596
10597 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10598 if ((mng_info->have_write_global_srgb == 0) &&
10599 ping_have_iCCP != MagickTrue &&
10600 (ping_have_sRGB != MagickFalse ||
10601 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10602 {
10603 if (ping_exclude_sRGB == MagickFalse)
10604 {
10605 /*
10606 Note image rendering intent.
10607 */
10608 if (logging != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " Setting up sRGB chunk");
10611
10612 (void) png_set_sRGB(ping,ping_info,(
10613 Magick_RenderingIntent_to_PNG_RenderingIntent(
10614 image->rendering_intent)));
10615
10616 ping_have_sRGB = MagickTrue;
10617 }
10618 }
10619
10620 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10621 #endif
10622 {
10623 if (ping_exclude_gAMA == MagickFalse &&
10624 ping_have_iCCP == MagickFalse &&
10625 ping_have_sRGB == MagickFalse &&
10626 (ping_exclude_sRGB == MagickFalse ||
10627 (image->gamma < .45 || image->gamma > .46)))
10628 {
10629 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10630 {
10631 /*
10632 Note image gamma.
10633 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10634 */
10635 if (logging != MagickFalse)
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " Setting up gAMA chunk");
10638
10639 png_set_gAMA(ping,ping_info,image->gamma);
10640 }
10641 }
10642
10643 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10644 {
10645 if ((mng_info->have_write_global_chrm == 0) &&
10646 (image->chromaticity.red_primary.x != 0.0))
10647 {
10648 /*
10649 Note image chromaticity.
10650 Note: if cHRM+gAMA == sRGB write sRGB instead.
10651 */
10652 PrimaryInfo
10653 bp,
10654 gp,
10655 rp,
10656 wp;
10657
10658 wp=image->chromaticity.white_point;
10659 rp=image->chromaticity.red_primary;
10660 gp=image->chromaticity.green_primary;
10661 bp=image->chromaticity.blue_primary;
10662
10663 if (logging != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " Setting up cHRM chunk");
10666
10667 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10668 bp.x,bp.y);
10669 }
10670 }
10671 }
10672
10673 if (ping_exclude_bKGD == MagickFalse)
10674 {
10675 if (ping_have_bKGD != MagickFalse)
10676 {
10677 png_set_bKGD(ping,ping_info,&ping_background);
10678 if (logging != MagickFalse)
10679 {
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Setting up bKGD chunk");
10682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10683 " background color = (%d,%d,%d)",
10684 (int) ping_background.red,
10685 (int) ping_background.green,
10686 (int) ping_background.blue);
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " index = %d, gray=%d",
10689 (int) ping_background.index,
10690 (int) ping_background.gray);
10691 }
10692 }
10693 }
10694
10695 if (ping_exclude_pHYs == MagickFalse)
10696 {
10697 if (ping_have_pHYs != MagickFalse)
10698 {
10699 png_set_pHYs(ping,ping_info,
10700 ping_pHYs_x_resolution,
10701 ping_pHYs_y_resolution,
10702 ping_pHYs_unit_type);
10703
10704 if (logging != MagickFalse)
10705 {
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " Setting up pHYs chunk");
10708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10709 " x_resolution=%lu",
10710 (unsigned long) ping_pHYs_x_resolution);
10711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10712 " y_resolution=%lu",
10713 (unsigned long) ping_pHYs_y_resolution);
10714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10715 " unit_type=%lu",
10716 (unsigned long) ping_pHYs_unit_type);
10717 }
10718 }
10719 }
10720
10721 #if defined(PNG_oFFs_SUPPORTED)
10722 if (ping_exclude_oFFs == MagickFalse)
10723 {
10724 if (image->page.x || image->page.y)
10725 {
10726 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10727 (png_int_32) image->page.y, 0);
10728
10729 if (logging != MagickFalse)
10730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10731 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10732 (int) image->page.x, (int) image->page.y);
10733 }
10734 }
10735 #endif
10736
10737 #if defined(PNG_tIME_SUPPORTED)
10738 if (ping_exclude_tIME == MagickFalse)
10739 {
10740 const char
10741 *timestamp;
10742
10743 if (image->taint == MagickFalse)
10744 {
10745 timestamp=GetImageOption(image_info,"png:tIME");
10746
10747 if (timestamp == (const char *) NULL)
10748 timestamp=GetImageProperty(image,"png:tIME",exception);
10749 }
10750
10751 else
10752 {
10753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10754 " Reset tIME in tainted image");
10755
10756 timestamp=GetImageProperty(image,"date:modify",exception);
10757 }
10758
10759 if (timestamp != (const char *) NULL)
10760 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10761 }
10762 #endif
10763
10764 if (mng_info->need_blob != MagickFalse)
10765 {
10766 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10767 MagickFalse)
10768 png_error(ping,"WriteBlob Failed");
10769
10770 ping_have_blob=MagickTrue;
10771 }
10772
10773 png_write_info_before_PLTE(ping, ping_info);
10774
10775 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10776 {
10777 if (logging != MagickFalse)
10778 {
10779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10780 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10781 }
10782
10783 if (ping_color_type == 3)
10784 (void) png_set_tRNS(ping, ping_info,
10785 ping_trans_alpha,
10786 ping_num_trans,
10787 NULL);
10788
10789 else
10790 {
10791 (void) png_set_tRNS(ping, ping_info,
10792 NULL,
10793 0,
10794 &ping_trans_color);
10795
10796 if (logging != MagickFalse)
10797 {
10798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799 " tRNS color =(%d,%d,%d)",
10800 (int) ping_trans_color.red,
10801 (int) ping_trans_color.green,
10802 (int) ping_trans_color.blue);
10803 }
10804 }
10805 }
10806
10807 /* write any png-chunk-b profiles */
10808 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10809
10810 png_write_info(ping,ping_info);
10811
10812 /* write any PNG-chunk-m profiles */
10813 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10814
10815 if (ping_exclude_vpAg == MagickFalse)
10816 {
10817 if ((image->page.width != 0 && image->page.width != image->columns) ||
10818 (image->page.height != 0 && image->page.height != image->rows))
10819 {
10820 unsigned char
10821 chunk[14];
10822
10823 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10824 PNGType(chunk,mng_vpAg);
10825 LogPNGChunk(logging,mng_vpAg,9L);
10826 PNGLong(chunk+4,(png_uint_32) image->page.width);
10827 PNGLong(chunk+8,(png_uint_32) image->page.height);
10828 chunk[12]=0; /* unit = pixels */
10829 (void) WriteBlob(image,13,chunk);
10830 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10831 }
10832 }
10833
10834 #if (PNG_LIBPNG_VER == 10206)
10835 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10836 #define PNG_HAVE_IDAT 0x04
10837 ping->mode |= PNG_HAVE_IDAT;
10838 #undef PNG_HAVE_IDAT
10839 #endif
10840
10841 png_set_packing(ping);
10842 /*
10843 Allocate memory.
10844 */
10845 rowbytes=image->columns;
10846 if (image_depth > 8)
10847 rowbytes*=2;
10848 switch (ping_color_type)
10849 {
10850 case PNG_COLOR_TYPE_RGB:
10851 rowbytes*=3;
10852 break;
10853
10854 case PNG_COLOR_TYPE_GRAY_ALPHA:
10855 rowbytes*=2;
10856 break;
10857
10858 case PNG_COLOR_TYPE_RGBA:
10859 rowbytes*=4;
10860 break;
10861
10862 default:
10863 break;
10864 }
10865
10866 if (logging != MagickFalse)
10867 {
10868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10869 " Writing PNG image data");
10870
10871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10873 }
10874 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10875 if (pixel_info == (MemoryInfo *) NULL)
10876 png_error(ping,"Allocation of memory for pixels failed");
10877 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10878
10879 /*
10880 Initialize image scanlines.
10881 */
10882 quantum_info=AcquireQuantumInfo(image_info,image);
10883 if (quantum_info == (QuantumInfo *) NULL)
10884 png_error(ping,"Memory allocation for quantum_info failed");
10885 quantum_info->format=UndefinedQuantumFormat;
10886 SetQuantumDepth(image,quantum_info,image_depth);
10887 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10888 num_passes=png_set_interlace_handling(ping);
10889
10890 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10891 !mng_info->write_png48 && !mng_info->write_png64 &&
10892 !mng_info->write_png32) &&
10893 (mng_info->IsPalette ||
10894 (image_info->type == BilevelType)) &&
10895 image_matte == MagickFalse &&
10896 ping_have_non_bw == MagickFalse)
10897 {
10898 /* Palette, Bilevel, or Opaque Monochrome */
10899 register const Quantum
10900 *p;
10901
10902 SetQuantumDepth(image,quantum_info,8);
10903 for (pass=0; pass < num_passes; pass++)
10904 {
10905 /*
10906 Convert PseudoClass image to a PNG monochrome image.
10907 */
10908 for (y=0; y < (ssize_t) image->rows; y++)
10909 {
10910 if (logging != MagickFalse && y == 0)
10911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10912 " Writing row of pixels (0)");
10913
10914 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10915
10916 if (p == (const Quantum *) NULL)
10917 break;
10918
10919 if (mng_info->IsPalette)
10920 {
10921 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10922 quantum_info,GrayQuantum,ping_pixels,exception);
10923 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10924 mng_info->write_png_depth &&
10925 mng_info->write_png_depth != old_bit_depth)
10926 {
10927 /* Undo pixel scaling */
10928 for (i=0; i < (ssize_t) image->columns; i++)
10929 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10930 >> (8-old_bit_depth));
10931 }
10932 }
10933
10934 else
10935 {
10936 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10937 quantum_info,RedQuantum,ping_pixels,exception);
10938 }
10939
10940 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10941 for (i=0; i < (ssize_t) image->columns; i++)
10942 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10943 255 : 0);
10944
10945 if (logging != MagickFalse && y == 0)
10946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10947 " Writing row of pixels (1)");
10948
10949 png_write_row(ping,ping_pixels);
10950
10951 status=SetImageProgress(image,SaveImageTag,
10952 (MagickOffsetType) (pass * image->rows + y),
10953 num_passes * image->rows);
10954
10955 if (status == MagickFalse)
10956 break;
10957 }
10958 }
10959 }
10960
10961 else /* Not Palette, Bilevel, or Opaque Monochrome */
10962 {
10963 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10964 !mng_info->write_png48 && !mng_info->write_png64 &&
10965 !mng_info->write_png32) && (image_matte != MagickFalse ||
10966 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10967 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10968 {
10969 register const Quantum
10970 *p;
10971
10972 for (pass=0; pass < num_passes; pass++)
10973 {
10974
10975 for (y=0; y < (ssize_t) image->rows; y++)
10976 {
10977 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10978
10979 if (p == (const Quantum *) NULL)
10980 break;
10981
10982 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10983 {
10984 if (mng_info->IsPalette)
10985 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10986 quantum_info,GrayQuantum,ping_pixels,exception);
10987
10988 else
10989 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10990 quantum_info,RedQuantum,ping_pixels,exception);
10991
10992 if (logging != MagickFalse && y == 0)
10993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10994 " Writing GRAY PNG pixels (2)");
10995 }
10996
10997 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10998 {
10999 if (logging != MagickFalse && y == 0)
11000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11001 " Writing GRAY_ALPHA PNG pixels (2)");
11002
11003 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11004 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11005 }
11006
11007 if (logging != MagickFalse && y == 0)
11008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11009 " Writing row of pixels (2)");
11010
11011 png_write_row(ping,ping_pixels);
11012
11013 status=SetImageProgress(image,SaveImageTag,
11014 (MagickOffsetType) (pass * image->rows + y),
11015 num_passes * image->rows);
11016
11017 if (status == MagickFalse)
11018 break;
11019 }
11020 }
11021 }
11022
11023 else
11024 {
11025 register const Quantum
11026 *p;
11027
11028 for (pass=0; pass < num_passes; pass++)
11029 {
11030 if ((image_depth > 8) ||
11031 mng_info->write_png24 ||
11032 mng_info->write_png32 ||
11033 mng_info->write_png48 ||
11034 mng_info->write_png64 ||
11035 (!mng_info->write_png8 && !mng_info->IsPalette))
11036 {
11037 for (y=0; y < (ssize_t) image->rows; y++)
11038 {
11039 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11040
11041 if (p == (const Quantum *) NULL)
11042 break;
11043
11044 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11045 {
11046 if (image->storage_class == DirectClass)
11047 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048 quantum_info,RedQuantum,ping_pixels,exception);
11049
11050 else
11051 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11052 quantum_info,GrayQuantum,ping_pixels,exception);
11053 }
11054
11055 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11056 {
11057 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11058 quantum_info,GrayAlphaQuantum,ping_pixels,
11059 exception);
11060
11061 if (logging != MagickFalse && y == 0)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " Writing GRAY_ALPHA PNG pixels (3)");
11064 }
11065
11066 else if (image_matte != MagickFalse)
11067 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11068 quantum_info,RGBAQuantum,ping_pixels,exception);
11069
11070 else
11071 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11072 quantum_info,RGBQuantum,ping_pixels,exception);
11073
11074 if (logging != MagickFalse && y == 0)
11075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11076 " Writing row of pixels (3)");
11077
11078 png_write_row(ping,ping_pixels);
11079
11080 status=SetImageProgress(image,SaveImageTag,
11081 (MagickOffsetType) (pass * image->rows + y),
11082 num_passes * image->rows);
11083
11084 if (status == MagickFalse)
11085 break;
11086 }
11087 }
11088
11089 else
11090 /* not ((image_depth > 8) ||
11091 mng_info->write_png24 || mng_info->write_png32 ||
11092 mng_info->write_png48 || mng_info->write_png64 ||
11093 (!mng_info->write_png8 && !mng_info->IsPalette))
11094 */
11095 {
11096 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11097 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11098 {
11099 if (logging != MagickFalse)
11100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11102
11103 SetQuantumDepth(image,quantum_info,8);
11104 image_depth=8;
11105 }
11106
11107 for (y=0; y < (ssize_t) image->rows; y++)
11108 {
11109 if (logging != MagickFalse && y == 0)
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11112
11113 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11114
11115 if (p == (const Quantum *) NULL)
11116 break;
11117
11118 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11119 {
11120 SetQuantumDepth(image,quantum_info,image->depth);
11121
11122 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11123 quantum_info,GrayQuantum,ping_pixels,exception);
11124 }
11125
11126 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11127 {
11128 if (logging != MagickFalse && y == 0)
11129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11130 " Writing GRAY_ALPHA PNG pixels (4)");
11131
11132 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11133 quantum_info,GrayAlphaQuantum,ping_pixels,
11134 exception);
11135 }
11136
11137 else
11138 {
11139 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11140 quantum_info,IndexQuantum,ping_pixels,exception);
11141
11142 if (logging != MagickFalse && y <= 2)
11143 {
11144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11145 " Writing row of non-gray pixels (4)");
11146
11147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11149 (int)ping_pixels[0],(int)ping_pixels[1]);
11150 }
11151 }
11152 png_write_row(ping,ping_pixels);
11153
11154 status=SetImageProgress(image,SaveImageTag,
11155 (MagickOffsetType) (pass * image->rows + y),
11156 num_passes * image->rows);
11157
11158 if (status == MagickFalse)
11159 break;
11160 }
11161 }
11162 }
11163 }
11164 }
11165
11166 if (quantum_info != (QuantumInfo *) NULL)
11167 quantum_info=DestroyQuantumInfo(quantum_info);
11168
11169 if (logging != MagickFalse)
11170 {
11171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11172 " Wrote PNG image data");
11173
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175 " Width: %.20g",(double) ping_width);
11176
11177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11178 " Height: %.20g",(double) ping_height);
11179
11180 if (mng_info->write_png_depth)
11181 {
11182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11183 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11184 }
11185
11186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187 " PNG bit-depth written: %d",ping_bit_depth);
11188
11189 if (mng_info->write_png_colortype)
11190 {
11191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11193 }
11194
11195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11196 " PNG color-type written: %d",ping_color_type);
11197
11198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11199 " PNG Interlace method: %d",ping_interlace_method);
11200 }
11201 /*
11202 Generate text chunks after IDAT.
11203 */
11204 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11205 {
11206 ResetImagePropertyIterator(image);
11207 property=GetNextImageProperty(image);
11208 while (property != (const char *) NULL)
11209 {
11210 png_textp
11211 text;
11212
11213 value=GetImageProperty(image,property,exception);
11214
11215 /* Don't write any "png:" or "jpeg:" properties; those are just for
11216 * "identify" or for passing through to another JPEG
11217 */
11218 if ((LocaleNCompare(property,"png:",4) != 0 &&
11219 LocaleNCompare(property,"jpeg:",5) != 0) &&
11220
11221
11222 /* Suppress density and units if we wrote a pHYs chunk */
11223 (ping_exclude_pHYs != MagickFalse ||
11224 LocaleCompare(property,"density") != 0 ||
11225 LocaleCompare(property,"units") != 0) &&
11226
11227 /* Suppress the IM-generated Date:create and Date:modify */
11228 (ping_exclude_date == MagickFalse ||
11229 LocaleNCompare(property, "Date:",5) != 0))
11230 {
11231 if (value != (const char *) NULL)
11232 {
11233
11234 #if PNG_LIBPNG_VER >= 10400
11235 text=(png_textp) png_malloc(ping,
11236 (png_alloc_size_t) sizeof(png_text));
11237 #else
11238 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11239 #endif
11240 text[0].key=(char *) property;
11241 text[0].text=(char *) value;
11242 text[0].text_length=strlen(value);
11243
11244 if (ping_exclude_tEXt != MagickFalse)
11245 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11246
11247 else if (ping_exclude_zTXt != MagickFalse)
11248 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11249
11250 else
11251 {
11252 text[0].compression=image_info->compression == NoCompression ||
11253 (image_info->compression == UndefinedCompression &&
11254 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11255 PNG_TEXT_COMPRESSION_zTXt ;
11256 }
11257
11258 if (logging != MagickFalse)
11259 {
11260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11261 " Setting up text chunk");
11262
11263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11264 " keyword: '%s'",text[0].key);
11265 }
11266
11267 png_set_text(ping,ping_info,text,1);
11268 png_free(ping,text);
11269 }
11270 }
11271 property=GetNextImageProperty(image);
11272 }
11273 }
11274
11275 /* write any PNG-chunk-e profiles */
11276 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11277
11278 if (logging != MagickFalse)
11279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11280 " Writing PNG end info");
11281
11282 png_write_end(ping,ping_info);
11283
11284 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11285 {
11286 if (mng_info->page.x || mng_info->page.y ||
11287 (ping_width != mng_info->page.width) ||
11288 (ping_height != mng_info->page.height))
11289 {
11290 unsigned char
11291 chunk[32];
11292
11293 /*
11294 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11295 */
11296 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11297 PNGType(chunk,mng_FRAM);
11298 LogPNGChunk(logging,mng_FRAM,27L);
11299 chunk[4]=4;
11300 chunk[5]=0; /* frame name separator (no name) */
11301 chunk[6]=1; /* flag for changing delay, for next frame only */
11302 chunk[7]=0; /* flag for changing frame timeout */
11303 chunk[8]=1; /* flag for changing frame clipping for next frame */
11304 chunk[9]=0; /* flag for changing frame sync_id */
11305 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11306 chunk[14]=0; /* clipping boundaries delta type */
11307 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11308 PNGLong(chunk+19,
11309 (png_uint_32) (mng_info->page.x + ping_width));
11310 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11311 PNGLong(chunk+27,
11312 (png_uint_32) (mng_info->page.y + ping_height));
11313 (void) WriteBlob(image,31,chunk);
11314 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11315 mng_info->old_framing_mode=4;
11316 mng_info->framing_mode=1;
11317 }
11318
11319 else
11320 mng_info->framing_mode=3;
11321 }
11322 if (mng_info->write_mng && !mng_info->need_fram &&
11323 ((int) image->dispose == 3))
11324 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11325
11326 /*
11327 Free PNG resources.
11328 */
11329
11330 png_destroy_write_struct(&ping,&ping_info);
11331
11332 pixel_info=RelinquishVirtualMemory(pixel_info);
11333
11334 if (ping_have_blob != MagickFalse)
11335 (void) CloseBlob(image);
11336
11337 image_info=DestroyImageInfo(image_info);
11338 image=DestroyImage(image);
11339
11340 /* Store bit depth actually written */
11341 s[0]=(char) ping_bit_depth;
11342 s[1]='\0';
11343
11344 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11345
11346 if (logging != MagickFalse)
11347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11348 " exit WriteOnePNGImage()");
11349
11350 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11351 UnlockSemaphoreInfo(ping_semaphore);
11352 #endif
11353
11354 /* } for navigation to beginning of SETJMP-protected block. Revert to
11355 * Throwing an Exception when an error occurs.
11356 */
11357
11358 return(MagickTrue);
11359 /* End write one PNG image */
11360
11361 }
11362
11363 /*
11364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11365 % %
11366 % %
11367 % %
11368 % W r i t e P N G I m a g e %
11369 % %
11370 % %
11371 % %
11372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11373 %
11374 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11375 % Multiple-image Network Graphics (MNG) image file.
11376 %
11377 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11378 %
11379 % The format of the WritePNGImage method is:
11380 %
11381 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11382 % Image *image,ExceptionInfo *exception)
11383 %
11384 % A description of each parameter follows:
11385 %
11386 % o image_info: the image info.
11387 %
11388 % o image: The image.
11389 %
11390 % o exception: return any errors or warnings in this structure.
11391 %
11392 % Returns MagickTrue on success, MagickFalse on failure.
11393 %
11394 % Communicating with the PNG encoder:
11395 %
11396 % While the datastream written is always in PNG format and normally would
11397 % be given the "png" file extension, this method also writes the following
11398 % pseudo-formats which are subsets of png:
11399 %
11400 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11401 % a depth greater than 8, the depth is reduced. If transparency
11402 % is present, the tRNS chunk must only have values 0 and 255
11403 % (i.e., transparency is binary: fully opaque or fully
11404 % transparent). If other values are present they will be
11405 % 50%-thresholded to binary transparency. If more than 256
11406 % colors are present, they will be quantized to the 4-4-4-1,
11407 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11408 % of any resulting fully-transparent pixels is changed to
11409 % the image's background color.
11410 %
11411 % If you want better quantization or dithering of the colors
11412 % or alpha than that, you need to do it before calling the
11413 % PNG encoder. The pixels contain 8-bit indices even if
11414 % they could be represented with 1, 2, or 4 bits. Grayscale
11415 % images will be written as indexed PNG files even though the
11416 % PNG grayscale type might be slightly more efficient. Please
11417 % note that writing to the PNG8 format may result in loss
11418 % of color and alpha data.
11419 %
11420 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11421 % chunk can be present to convey binary transparency by naming
11422 % one of the colors as transparent. The only loss incurred
11423 % is reduction of sample depth to 8. If the image has more
11424 % than one transparent color, has semitransparent pixels, or
11425 % has an opaque pixel with the same RGB components as the
11426 % transparent color, an image is not written.
11427 %
11428 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11429 % transparency is permitted, i.e., the alpha sample for
11430 % each pixel can have any value from 0 to 255. The alpha
11431 % channel is present even if the image is fully opaque.
11432 % The only loss in data is the reduction of the sample depth
11433 % to 8.
11434 %
11435 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11436 % chunk can be present to convey binary transparency by naming
11437 % one of the colors as transparent. If the image has more
11438 % than one transparent color, has semitransparent pixels, or
11439 % has an opaque pixel with the same RGB components as the
11440 % transparent color, an image is not written.
11441 %
11442 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11443 % transparency is permitted, i.e., the alpha sample for
11444 % each pixel can have any value from 0 to 65535. The alpha
11445 % channel is present even if the image is fully opaque.
11446 %
11447 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11448 % image, if the input was a PNG, is written. If these values
11449 % cannot be found, or if the pixels have been changed in a way
11450 % that makes this impossible, then "PNG00" falls back to the
11451 % regular "PNG" format.
11452 %
11453 % o -define: For more precise control of the PNG output, you can use the
11454 % Image options "png:bit-depth" and "png:color-type". These
11455 % can be set from the commandline with "-define" and also
11456 % from the application programming interfaces. The options
11457 % are case-independent and are converted to lowercase before
11458 % being passed to this encoder.
11459 %
11460 % png:color-type can be 0, 2, 3, 4, or 6.
11461 %
11462 % When png:color-type is 0 (Grayscale), png:bit-depth can
11463 % be 1, 2, 4, 8, or 16.
11464 %
11465 % When png:color-type is 2 (RGB), png:bit-depth can
11466 % be 8 or 16.
11467 %
11468 % When png:color-type is 3 (Indexed), png:bit-depth can
11469 % be 1, 2, 4, or 8. This refers to the number of bits
11470 % used to store the index. The color samples always have
11471 % bit-depth 8 in indexed PNG files.
11472 %
11473 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11474 % png:bit-depth can be 8 or 16.
11475 %
11476 % If the image cannot be written without loss with the
11477 % requested bit-depth and color-type, a PNG file will not
11478 % be written, a warning will be issued, and the encoder will
11479 % return MagickFalse.
11480 %
11481 % Since image encoders should not be responsible for the "heavy lifting",
11482 % the user should make sure that ImageMagick has already reduced the
11483 % image depth and number of colors and limit transparency to binary
11484 % transparency prior to attempting to write the image with depth, color,
11485 % or transparency limitations.
11486 %
11487 % Note that another definition, "png:bit-depth-written" exists, but it
11488 % is not intended for external use. It is only used internally by the
11489 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11490 %
11491 % It is possible to request that the PNG encoder write previously-formatted
11492 % ancillary chunks in the output PNG file, using the "-profile" commandline
11493 % option as shown below or by setting the profile via a programming
11494 % interface:
11495 %
11496 % -profile PNG-chunk-x:<file>
11497 %
11498 % where x is a location flag and <file> is a file containing the chunk
11499 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11500 % This encoder will compute the chunk length and CRC, so those must not
11501 % be included in the file.
11502 %
11503 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11504 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11505 % of the same type, then add a short unique string after the "x" to prevent
11506 % subsequent profiles from overwriting the preceding ones, e.g.,
11507 %
11508 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11509 %
11510 % As of version 6.6.6 the following optimizations are always done:
11511 %
11512 % o 32-bit depth is reduced to 16.
11513 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11514 % high byte and low byte are identical.
11515 % o Palette is sorted to remove unused entries and to put a
11516 % transparent color first, if BUILD_PNG_PALETTE is defined.
11517 % o Opaque matte channel is removed when writing an indexed PNG.
11518 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11519 % this can be done without loss and a larger bit depth N was not
11520 % requested via the "-define png:bit-depth=N" option.
11521 % o If matte channel is present but only one transparent color is
11522 % present, RGB+tRNS is written instead of RGBA
11523 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11524 % was requested when converting an opaque image).
11525 %
11526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11527 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)11528 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11529 Image *image,ExceptionInfo *exception)
11530 {
11531 MagickBooleanType
11532 excluding,
11533 logging,
11534 have_mng_structure,
11535 status;
11536
11537 MngInfo
11538 *mng_info;
11539
11540 const char
11541 *value;
11542
11543 int
11544 source;
11545
11546 /*
11547 Open image file.
11548 */
11549 assert(image_info != (const ImageInfo *) NULL);
11550 assert(image_info->signature == MagickCoreSignature);
11551 assert(image != (Image *) NULL);
11552 assert(image->signature == MagickCoreSignature);
11553 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11554 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11555 /*
11556 Allocate a MngInfo structure.
11557 */
11558 have_mng_structure=MagickFalse;
11559 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11560
11561 if (mng_info == (MngInfo *) NULL)
11562 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11563
11564 /*
11565 Initialize members of the MngInfo structure.
11566 */
11567 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11568 mng_info->image=image;
11569 mng_info->equal_backgrounds=MagickTrue;
11570 have_mng_structure=MagickTrue;
11571
11572 /* See if user has requested a specific PNG subformat */
11573
11574 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11575 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11576 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11577 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11578 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11579
11580 value=GetImageOption(image_info,"png:format");
11581
11582 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11583 {
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " Format=%s",value);
11586
11587 mng_info->write_png8 = MagickFalse;
11588 mng_info->write_png24 = MagickFalse;
11589 mng_info->write_png32 = MagickFalse;
11590 mng_info->write_png48 = MagickFalse;
11591 mng_info->write_png64 = MagickFalse;
11592
11593 if (LocaleCompare(value,"png8") == 0)
11594 mng_info->write_png8 = MagickTrue;
11595
11596 else if (LocaleCompare(value,"png24") == 0)
11597 mng_info->write_png24 = MagickTrue;
11598
11599 else if (LocaleCompare(value,"png32") == 0)
11600 mng_info->write_png32 = MagickTrue;
11601
11602 else if (LocaleCompare(value,"png48") == 0)
11603 mng_info->write_png48 = MagickTrue;
11604
11605 else if (LocaleCompare(value,"png64") == 0)
11606 mng_info->write_png64 = MagickTrue;
11607
11608 else if ((LocaleCompare(value,"png00") == 0) ||
11609 LocaleCompare(image_info->magick,"PNG00") == 0)
11610 {
11611 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11612 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11613
11614 if (value != (char *) NULL)
11615 {
11616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11617 " png00 inherited bit depth=%s",value);
11618
11619 if (LocaleCompare(value,"1") == 0)
11620 mng_info->write_png_depth = 1;
11621
11622 else if (LocaleCompare(value,"2") == 0)
11623 mng_info->write_png_depth = 2;
11624
11625 else if (LocaleCompare(value,"4") == 0)
11626 mng_info->write_png_depth = 4;
11627
11628 else if (LocaleCompare(value,"8") == 0)
11629 mng_info->write_png_depth = 8;
11630
11631 else if (LocaleCompare(value,"16") == 0)
11632 mng_info->write_png_depth = 16;
11633 }
11634
11635 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11636
11637 if (value != (char *) NULL)
11638 {
11639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640 " png00 inherited color type=%s",value);
11641
11642 if (LocaleCompare(value,"0") == 0)
11643 mng_info->write_png_colortype = 1;
11644
11645 else if (LocaleCompare(value,"2") == 0)
11646 mng_info->write_png_colortype = 3;
11647
11648 else if (LocaleCompare(value,"3") == 0)
11649 mng_info->write_png_colortype = 4;
11650
11651 else if (LocaleCompare(value,"4") == 0)
11652 mng_info->write_png_colortype = 5;
11653
11654 else if (LocaleCompare(value,"6") == 0)
11655 mng_info->write_png_colortype = 7;
11656 }
11657 }
11658 }
11659
11660 if (mng_info->write_png8)
11661 {
11662 mng_info->write_png_colortype = /* 3 */ 4;
11663 mng_info->write_png_depth = 8;
11664 image->depth = 8;
11665 }
11666
11667 if (mng_info->write_png24)
11668 {
11669 mng_info->write_png_colortype = /* 2 */ 3;
11670 mng_info->write_png_depth = 8;
11671 image->depth = 8;
11672
11673 if (image->alpha_trait != UndefinedPixelTrait)
11674 (void) SetImageType(image,TrueColorAlphaType,exception);
11675
11676 else
11677 (void) SetImageType(image,TrueColorType,exception);
11678
11679 (void) SyncImage(image,exception);
11680 }
11681
11682 if (mng_info->write_png32)
11683 {
11684 mng_info->write_png_colortype = /* 6 */ 7;
11685 mng_info->write_png_depth = 8;
11686 image->depth = 8;
11687 image->alpha_trait = BlendPixelTrait;
11688
11689 (void) SetImageType(image,TrueColorAlphaType,exception);
11690 (void) SyncImage(image,exception);
11691 }
11692
11693 if (mng_info->write_png48)
11694 {
11695 mng_info->write_png_colortype = /* 2 */ 3;
11696 mng_info->write_png_depth = 16;
11697 image->depth = 16;
11698
11699 if (image->alpha_trait != UndefinedPixelTrait)
11700 (void) SetImageType(image,TrueColorAlphaType,exception);
11701
11702 else
11703 (void) SetImageType(image,TrueColorType,exception);
11704
11705 (void) SyncImage(image,exception);
11706 }
11707
11708 if (mng_info->write_png64)
11709 {
11710 mng_info->write_png_colortype = /* 6 */ 7;
11711 mng_info->write_png_depth = 16;
11712 image->depth = 16;
11713 image->alpha_trait = BlendPixelTrait;
11714
11715 (void) SetImageType(image,TrueColorAlphaType,exception);
11716 (void) SyncImage(image,exception);
11717 }
11718
11719 value=GetImageOption(image_info,"png:bit-depth");
11720
11721 if (value != (char *) NULL)
11722 {
11723 if (LocaleCompare(value,"1") == 0)
11724 mng_info->write_png_depth = 1;
11725
11726 else if (LocaleCompare(value,"2") == 0)
11727 mng_info->write_png_depth = 2;
11728
11729 else if (LocaleCompare(value,"4") == 0)
11730 mng_info->write_png_depth = 4;
11731
11732 else if (LocaleCompare(value,"8") == 0)
11733 mng_info->write_png_depth = 8;
11734
11735 else if (LocaleCompare(value,"16") == 0)
11736 mng_info->write_png_depth = 16;
11737
11738 else
11739 (void) ThrowMagickException(exception,
11740 GetMagickModule(),CoderWarning,
11741 "ignoring invalid defined png:bit-depth",
11742 "=%s",value);
11743
11744 if (logging != MagickFalse)
11745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11746 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11747 }
11748
11749 value=GetImageOption(image_info,"png:color-type");
11750
11751 if (value != (char *) NULL)
11752 {
11753 /* We must store colortype+1 because 0 is a valid colortype */
11754 if (LocaleCompare(value,"0") == 0)
11755 mng_info->write_png_colortype = 1;
11756
11757 else if (LocaleCompare(value,"1") == 0)
11758 mng_info->write_png_colortype = 2;
11759
11760 else if (LocaleCompare(value,"2") == 0)
11761 mng_info->write_png_colortype = 3;
11762
11763 else if (LocaleCompare(value,"3") == 0)
11764 mng_info->write_png_colortype = 4;
11765
11766 else if (LocaleCompare(value,"4") == 0)
11767 mng_info->write_png_colortype = 5;
11768
11769 else if (LocaleCompare(value,"6") == 0)
11770 mng_info->write_png_colortype = 7;
11771
11772 else
11773 (void) ThrowMagickException(exception,
11774 GetMagickModule(),CoderWarning,
11775 "ignoring invalid defined png:color-type",
11776 "=%s",value);
11777
11778 if (logging != MagickFalse)
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11781 }
11782
11783 /* Check for chunks to be excluded:
11784 *
11785 * The default is to not exclude any known chunks except for any
11786 * listed in the "unused_chunks" array, above.
11787 *
11788 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11789 * define (in the image properties or in the image artifacts)
11790 * or via a mng_info member. For convenience, in addition
11791 * to or instead of a comma-separated list of chunks, the
11792 * "exclude-chunk" string can be simply "all" or "none".
11793 *
11794 * The exclude-chunk define takes priority over the mng_info.
11795 *
11796 * A "png:include-chunk" define takes priority over both the
11797 * mng_info and the "png:exclude-chunk" define. Like the
11798 * "exclude-chunk" string, it can define "all" or "none" as
11799 * well as a comma-separated list. Chunks that are unknown to
11800 * ImageMagick are always excluded, regardless of their "copy-safe"
11801 * status according to the PNG specification, and even if they
11802 * appear in the "include-chunk" list. Such defines appearing among
11803 * the image options take priority over those found among the image
11804 * artifacts.
11805 *
11806 * Finally, all chunks listed in the "unused_chunks" array are
11807 * automatically excluded, regardless of the other instructions
11808 * or lack thereof.
11809 *
11810 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11811 * will not be written and the gAMA chunk will only be written if it
11812 * is not between .45 and .46, or approximately (1.0/2.2).
11813 *
11814 * If you exclude tRNS and the image has transparency, the colortype
11815 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11816 *
11817 * The -strip option causes StripImage() to set the png:include-chunk
11818 * artifact to "none,trns,gama".
11819 */
11820
11821 mng_info->ping_exclude_bKGD=MagickFalse;
11822 mng_info->ping_exclude_cHRM=MagickFalse;
11823 mng_info->ping_exclude_date=MagickFalse;
11824 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11825 mng_info->ping_exclude_gAMA=MagickFalse;
11826 mng_info->ping_exclude_iCCP=MagickFalse;
11827 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11828 mng_info->ping_exclude_oFFs=MagickFalse;
11829 mng_info->ping_exclude_pHYs=MagickFalse;
11830 mng_info->ping_exclude_sRGB=MagickFalse;
11831 mng_info->ping_exclude_tEXt=MagickFalse;
11832 mng_info->ping_exclude_tIME=MagickFalse;
11833 mng_info->ping_exclude_tRNS=MagickFalse;
11834 mng_info->ping_exclude_vpAg=MagickFalse;
11835 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11836 mng_info->ping_exclude_zTXt=MagickFalse;
11837
11838 mng_info->ping_preserve_colormap=MagickFalse;
11839
11840 value=GetImageOption(image_info,"png:preserve-colormap");
11841 if (value == NULL)
11842 value=GetImageArtifact(image,"png:preserve-colormap");
11843 if (value != NULL)
11844 mng_info->ping_preserve_colormap=MagickTrue;
11845
11846 mng_info->ping_preserve_iCCP=MagickFalse;
11847
11848 value=GetImageOption(image_info,"png:preserve-iCCP");
11849 if (value == NULL)
11850 value=GetImageArtifact(image,"png:preserve-iCCP");
11851 if (value != NULL)
11852 mng_info->ping_preserve_iCCP=MagickTrue;
11853
11854 /* These compression-level, compression-strategy, and compression-filter
11855 * defines take precedence over values from the -quality option.
11856 */
11857 value=GetImageOption(image_info,"png:compression-level");
11858 if (value == NULL)
11859 value=GetImageArtifact(image,"png:compression-level");
11860 if (value != NULL)
11861 {
11862 /* We have to add 1 to everything because 0 is a valid input,
11863 * and we want to use 0 (the default) to mean undefined.
11864 */
11865 if (LocaleCompare(value,"0") == 0)
11866 mng_info->write_png_compression_level = 1;
11867
11868 else if (LocaleCompare(value,"1") == 0)
11869 mng_info->write_png_compression_level = 2;
11870
11871 else if (LocaleCompare(value,"2") == 0)
11872 mng_info->write_png_compression_level = 3;
11873
11874 else if (LocaleCompare(value,"3") == 0)
11875 mng_info->write_png_compression_level = 4;
11876
11877 else if (LocaleCompare(value,"4") == 0)
11878 mng_info->write_png_compression_level = 5;
11879
11880 else if (LocaleCompare(value,"5") == 0)
11881 mng_info->write_png_compression_level = 6;
11882
11883 else if (LocaleCompare(value,"6") == 0)
11884 mng_info->write_png_compression_level = 7;
11885
11886 else if (LocaleCompare(value,"7") == 0)
11887 mng_info->write_png_compression_level = 8;
11888
11889 else if (LocaleCompare(value,"8") == 0)
11890 mng_info->write_png_compression_level = 9;
11891
11892 else if (LocaleCompare(value,"9") == 0)
11893 mng_info->write_png_compression_level = 10;
11894
11895 else
11896 (void) ThrowMagickException(exception,
11897 GetMagickModule(),CoderWarning,
11898 "ignoring invalid defined png:compression-level",
11899 "=%s",value);
11900 }
11901
11902 value=GetImageOption(image_info,"png:compression-strategy");
11903 if (value == NULL)
11904 value=GetImageArtifact(image,"png:compression-strategy");
11905 if (value != NULL)
11906 {
11907 if (LocaleCompare(value,"0") == 0)
11908 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11909
11910 else if (LocaleCompare(value,"1") == 0)
11911 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11912
11913 else if (LocaleCompare(value,"2") == 0)
11914 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11915
11916 else if (LocaleCompare(value,"3") == 0)
11917 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11918 mng_info->write_png_compression_strategy = Z_RLE+1;
11919 #else
11920 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11921 #endif
11922
11923 else if (LocaleCompare(value,"4") == 0)
11924 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11925 mng_info->write_png_compression_strategy = Z_FIXED+1;
11926 #else
11927 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11928 #endif
11929
11930 else
11931 (void) ThrowMagickException(exception,
11932 GetMagickModule(),CoderWarning,
11933 "ignoring invalid defined png:compression-strategy",
11934 "=%s",value);
11935 }
11936
11937 value=GetImageOption(image_info,"png:compression-filter");
11938 if (value == NULL)
11939 value=GetImageArtifact(image,"png:compression-filter");
11940 if (value != NULL)
11941 {
11942 /* To do: combinations of filters allowed by libpng
11943 * masks 0x08 through 0xf8
11944 *
11945 * Implement this as a comma-separated list of 0,1,2,3,4,5
11946 * where 5 is a special case meaning PNG_ALL_FILTERS.
11947 */
11948
11949 if (LocaleCompare(value,"0") == 0)
11950 mng_info->write_png_compression_filter = 1;
11951
11952 else if (LocaleCompare(value,"1") == 0)
11953 mng_info->write_png_compression_filter = 2;
11954
11955 else if (LocaleCompare(value,"2") == 0)
11956 mng_info->write_png_compression_filter = 3;
11957
11958 else if (LocaleCompare(value,"3") == 0)
11959 mng_info->write_png_compression_filter = 4;
11960
11961 else if (LocaleCompare(value,"4") == 0)
11962 mng_info->write_png_compression_filter = 5;
11963
11964 else if (LocaleCompare(value,"5") == 0)
11965 mng_info->write_png_compression_filter = 6;
11966
11967 else
11968 (void) ThrowMagickException(exception,
11969 GetMagickModule(),CoderWarning,
11970 "ignoring invalid defined png:compression-filter",
11971 "=%s",value);
11972 }
11973
11974 for (source=0; source<8; source++)
11975 {
11976 value = NULL;
11977
11978 if (source == 0)
11979 value=GetImageOption(image_info,"png:exclude-chunks");
11980
11981 if (source == 1)
11982 value=GetImageArtifact(image,"png:exclude-chunks");
11983
11984 if (source == 2)
11985 value=GetImageOption(image_info,"png:exclude-chunk");
11986
11987 if (source == 3)
11988 value=GetImageArtifact(image,"png:exclude-chunk");
11989
11990 if (source == 4)
11991 value=GetImageOption(image_info,"png:include-chunks");
11992
11993 if (source == 5)
11994 value=GetImageArtifact(image,"png:include-chunks");
11995
11996 if (source == 6)
11997 value=GetImageOption(image_info,"png:include-chunk");
11998
11999 if (source == 7)
12000 value=GetImageArtifact(image,"png:include-chunk");
12001
12002 if (value == NULL)
12003 continue;
12004
12005 if (source < 4)
12006 excluding = MagickTrue;
12007 else
12008 excluding = MagickFalse;
12009
12010 if (logging != MagickFalse)
12011 {
12012 if (source == 0 || source == 2)
12013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12014 " png:exclude-chunk=%s found in image options.\n", value);
12015 else if (source == 1 || source == 3)
12016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12017 " png:exclude-chunk=%s found in image artifacts.\n", value);
12018 else if (source == 4 || source == 6)
12019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12020 " png:include-chunk=%s found in image options.\n", value);
12021 else /* if (source == 5 || source == 7) */
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12023 " png:include-chunk=%s found in image artifacts.\n", value);
12024 }
12025
12026 if (IsOptionMember("all",value) != MagickFalse)
12027 {
12028 mng_info->ping_exclude_bKGD=excluding;
12029 mng_info->ping_exclude_cHRM=excluding;
12030 mng_info->ping_exclude_date=excluding;
12031 mng_info->ping_exclude_EXIF=excluding;
12032 mng_info->ping_exclude_gAMA=excluding;
12033 mng_info->ping_exclude_iCCP=excluding;
12034 /* mng_info->ping_exclude_iTXt=excluding; */
12035 mng_info->ping_exclude_oFFs=excluding;
12036 mng_info->ping_exclude_pHYs=excluding;
12037 mng_info->ping_exclude_sRGB=excluding;
12038 mng_info->ping_exclude_tEXt=excluding;
12039 mng_info->ping_exclude_tIME=excluding;
12040 mng_info->ping_exclude_tRNS=excluding;
12041 mng_info->ping_exclude_vpAg=excluding;
12042 mng_info->ping_exclude_zCCP=excluding;
12043 mng_info->ping_exclude_zTXt=excluding;
12044 }
12045
12046 if (IsOptionMember("none",value) != MagickFalse)
12047 {
12048 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12049 MagickTrue;
12050 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12051 MagickTrue;
12052 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12053 MagickTrue;
12054 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12055 MagickTrue;
12056 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12057 MagickTrue;
12058 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12059 MagickTrue;
12060 /* mng_info->ping_exclude_iTXt=!excluding; */
12061 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12062 MagickTrue;
12063 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12064 MagickTrue;
12065 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12066 MagickTrue;
12067 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12068 MagickTrue;
12069 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12070 MagickTrue;
12071 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12072 MagickTrue;
12073 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12074 MagickTrue;
12075 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12076 MagickTrue;
12077 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12078 MagickTrue;
12079 }
12080
12081 if (IsOptionMember("bkgd",value) != MagickFalse)
12082 mng_info->ping_exclude_bKGD=excluding;
12083
12084 if (IsOptionMember("chrm",value) != MagickFalse)
12085 mng_info->ping_exclude_cHRM=excluding;
12086
12087 if (IsOptionMember("date",value) != MagickFalse)
12088 mng_info->ping_exclude_date=excluding;
12089
12090 if (IsOptionMember("exif",value) != MagickFalse)
12091 mng_info->ping_exclude_EXIF=excluding;
12092
12093 if (IsOptionMember("gama",value) != MagickFalse)
12094 mng_info->ping_exclude_gAMA=excluding;
12095
12096 if (IsOptionMember("iccp",value) != MagickFalse)
12097 mng_info->ping_exclude_iCCP=excluding;
12098
12099 #if 0
12100 if (IsOptionMember("itxt",value) != MagickFalse)
12101 mng_info->ping_exclude_iTXt=excluding;
12102 #endif
12103
12104 if (IsOptionMember("offs",value) != MagickFalse)
12105 mng_info->ping_exclude_oFFs=excluding;
12106
12107 if (IsOptionMember("phys",value) != MagickFalse)
12108 mng_info->ping_exclude_pHYs=excluding;
12109
12110 if (IsOptionMember("srgb",value) != MagickFalse)
12111 mng_info->ping_exclude_sRGB=excluding;
12112
12113 if (IsOptionMember("text",value) != MagickFalse)
12114 mng_info->ping_exclude_tEXt=excluding;
12115
12116 if (IsOptionMember("time",value) != MagickFalse)
12117 mng_info->ping_exclude_tIME=excluding;
12118
12119 if (IsOptionMember("trns",value) != MagickFalse)
12120 mng_info->ping_exclude_tRNS=excluding;
12121
12122 if (IsOptionMember("vpag",value) != MagickFalse)
12123 mng_info->ping_exclude_vpAg=excluding;
12124
12125 if (IsOptionMember("zccp",value) != MagickFalse)
12126 mng_info->ping_exclude_zCCP=excluding;
12127
12128 if (IsOptionMember("ztxt",value) != MagickFalse)
12129 mng_info->ping_exclude_zTXt=excluding;
12130 }
12131
12132 if (logging != MagickFalse)
12133 {
12134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12135 " Chunks to be excluded from the output png:");
12136 if (mng_info->ping_exclude_bKGD != MagickFalse)
12137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138 " bKGD");
12139 if (mng_info->ping_exclude_cHRM != MagickFalse)
12140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12141 " cHRM");
12142 if (mng_info->ping_exclude_date != MagickFalse)
12143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12144 " date");
12145 if (mng_info->ping_exclude_EXIF != MagickFalse)
12146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12147 " EXIF");
12148 if (mng_info->ping_exclude_gAMA != MagickFalse)
12149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12150 " gAMA");
12151 if (mng_info->ping_exclude_iCCP != MagickFalse)
12152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12153 " iCCP");
12154 #if 0
12155 if (mng_info->ping_exclude_iTXt != MagickFalse)
12156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12157 " iTXt");
12158 #endif
12159
12160 if (mng_info->ping_exclude_oFFs != MagickFalse)
12161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12162 " oFFs");
12163 if (mng_info->ping_exclude_pHYs != MagickFalse)
12164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12165 " pHYs");
12166 if (mng_info->ping_exclude_sRGB != MagickFalse)
12167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12168 " sRGB");
12169 if (mng_info->ping_exclude_tEXt != MagickFalse)
12170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12171 " tEXt");
12172 if (mng_info->ping_exclude_tIME != MagickFalse)
12173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174 " tIME");
12175 if (mng_info->ping_exclude_tRNS != MagickFalse)
12176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12177 " tRNS");
12178 if (mng_info->ping_exclude_vpAg != MagickFalse)
12179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180 " vpAg");
12181 if (mng_info->ping_exclude_zCCP != MagickFalse)
12182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12183 " zCCP");
12184 if (mng_info->ping_exclude_zTXt != MagickFalse)
12185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12186 " zTXt");
12187 }
12188
12189 mng_info->need_blob = MagickTrue;
12190
12191 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12192
12193 MngInfoFreeStruct(mng_info,&have_mng_structure);
12194
12195 if (logging != MagickFalse)
12196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12197
12198 return(status);
12199 }
12200
12201 #if defined(JNG_SUPPORTED)
12202
12203 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12204 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12205 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12206 {
12207 Image
12208 *jpeg_image;
12209
12210 ImageInfo
12211 *jpeg_image_info;
12212
12213 MagickBooleanType
12214 logging,
12215 status;
12216
12217 size_t
12218 length;
12219
12220 unsigned char
12221 *blob,
12222 chunk[80],
12223 *p;
12224
12225 unsigned int
12226 jng_alpha_compression_method,
12227 jng_alpha_sample_depth,
12228 jng_color_type,
12229 transparent;
12230
12231 size_t
12232 jng_alpha_quality,
12233 jng_quality;
12234
12235 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12236 " Enter WriteOneJNGImage()");
12237
12238 blob=(unsigned char *) NULL;
12239 jpeg_image=(Image *) NULL;
12240 jpeg_image_info=(ImageInfo *) NULL;
12241 length=0;
12242
12243 status=MagickTrue;
12244 transparent=image_info->type==GrayscaleAlphaType ||
12245 image_info->type==TrueColorAlphaType ||
12246 image->alpha_trait != UndefinedPixelTrait;
12247
12248 jng_alpha_sample_depth = 0;
12249
12250 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12251
12252 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12253
12254 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12255 image_info->quality;
12256
12257 if (jng_alpha_quality >= 1000)
12258 jng_alpha_quality /= 1000;
12259
12260 length=0;
12261
12262 if (transparent != 0)
12263 {
12264 jng_color_type=14;
12265
12266 /* Create JPEG blob, image, and image_info */
12267 if (logging != MagickFalse)
12268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12269 " Creating jpeg_image_info for alpha.");
12270
12271 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12272
12273 if (jpeg_image_info == (ImageInfo *) NULL)
12274 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12275
12276 if (logging != MagickFalse)
12277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12278 " Creating jpeg_image.");
12279
12280 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12281 if (jpeg_image == (Image *) NULL)
12282 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12283 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12284 jpeg_image->alpha_trait=UndefinedPixelTrait;
12285 jpeg_image->quality=jng_alpha_quality;
12286 jpeg_image_info->type=GrayscaleType;
12287 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12288 (void) AcquireUniqueFilename(jpeg_image->filename);
12289 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12290 "%s",jpeg_image->filename);
12291 }
12292 else
12293 {
12294 jng_alpha_compression_method=0;
12295 jng_color_type=10;
12296 jng_alpha_sample_depth=0;
12297 }
12298
12299 /* To do: check bit depth of PNG alpha channel */
12300
12301 /* Check if image is grayscale. */
12302 if (image_info->type != TrueColorAlphaType && image_info->type !=
12303 TrueColorType && SetImageGray(image,exception))
12304 jng_color_type-=2;
12305
12306 if (logging != MagickFalse)
12307 {
12308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12309 " JNG Quality = %d",(int) jng_quality);
12310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12311 " JNG Color Type = %d",jng_color_type);
12312 if (transparent != 0)
12313 {
12314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12319 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12320 }
12321 }
12322
12323 if (transparent != 0)
12324 {
12325 if (jng_alpha_compression_method==0)
12326 {
12327 const char
12328 *value;
12329
12330 /* Encode alpha as a grayscale PNG blob */
12331 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12332 exception);
12333 if (status == MagickFalse)
12334 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12335
12336 if (logging != MagickFalse)
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " Creating PNG blob.");
12339
12340 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MagickPathExtent);
12341 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12342 jpeg_image_info->interlace=NoInterlace;
12343
12344 /* Exclude all ancillary chunks */
12345 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12346
12347 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12348 &length,exception);
12349
12350 /* Retrieve sample depth used */
12351 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12352 if (value != (char *) NULL)
12353 jng_alpha_sample_depth= (unsigned int) value[0];
12354 }
12355 else
12356 {
12357 /* Encode alpha as a grayscale JPEG blob */
12358
12359 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12360 exception);
12361 if (status == MagickFalse)
12362 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12363
12364
12365 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12366 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12367 jpeg_image_info->interlace=NoInterlace;
12368 if (logging != MagickFalse)
12369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12370 " Creating blob.");
12371 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12372 exception);
12373 jng_alpha_sample_depth=8;
12374
12375 if (logging != MagickFalse)
12376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377 " Successfully read jpeg_image into a blob, length=%.20g.",
12378 (double) length);
12379
12380 }
12381 /* Destroy JPEG image and image_info */
12382 jpeg_image=DestroyImage(jpeg_image);
12383 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12384 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12385 }
12386
12387 /* Write JHDR chunk */
12388 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12389 PNGType(chunk,mng_JHDR);
12390 LogPNGChunk(logging,mng_JHDR,16L);
12391 PNGLong(chunk+4,(png_uint_32) image->columns);
12392 PNGLong(chunk+8,(png_uint_32) image->rows);
12393 chunk[12]=jng_color_type;
12394 chunk[13]=8; /* sample depth */
12395 chunk[14]=8; /*jng_image_compression_method */
12396 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12397 chunk[16]=jng_alpha_sample_depth;
12398 chunk[17]=jng_alpha_compression_method;
12399 chunk[18]=0; /*jng_alpha_filter_method */
12400 chunk[19]=0; /*jng_alpha_interlace_method */
12401 (void) WriteBlob(image,20,chunk);
12402 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12403 if (logging != MagickFalse)
12404 {
12405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12406 " JNG width:%15lu",(unsigned long) image->columns);
12407
12408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12409 " JNG height:%14lu",(unsigned long) image->rows);
12410
12411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12412 " JNG color type:%10d",jng_color_type);
12413
12414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12415 " JNG sample depth:%8d",8);
12416
12417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12418 " JNG compression:%9d",8);
12419
12420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12421 " JNG interlace:%11d",0);
12422
12423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12424 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12425
12426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12427 " JNG alpha compression:%3d",jng_alpha_compression_method);
12428
12429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12430 " JNG alpha filter:%8d",0);
12431
12432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12433 " JNG alpha interlace:%5d",0);
12434 }
12435
12436 /* Write any JNG-chunk-b profiles */
12437 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12438
12439 /*
12440 Write leading ancillary chunks
12441 */
12442
12443 if (transparent != 0)
12444 {
12445 /*
12446 Write JNG bKGD chunk
12447 */
12448
12449 unsigned char
12450 blue,
12451 green,
12452 red;
12453
12454 ssize_t
12455 num_bytes;
12456
12457 if (jng_color_type == 8 || jng_color_type == 12)
12458 num_bytes=6L;
12459 else
12460 num_bytes=10L;
12461 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12462 PNGType(chunk,mng_bKGD);
12463 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12464 red=ScaleQuantumToChar(image->background_color.red);
12465 green=ScaleQuantumToChar(image->background_color.green);
12466 blue=ScaleQuantumToChar(image->background_color.blue);
12467 *(chunk+4)=0;
12468 *(chunk+5)=red;
12469 *(chunk+6)=0;
12470 *(chunk+7)=green;
12471 *(chunk+8)=0;
12472 *(chunk+9)=blue;
12473 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12474 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12475 }
12476
12477 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12478 {
12479 /*
12480 Write JNG sRGB chunk
12481 */
12482 (void) WriteBlobMSBULong(image,1L);
12483 PNGType(chunk,mng_sRGB);
12484 LogPNGChunk(logging,mng_sRGB,1L);
12485
12486 if (image->rendering_intent != UndefinedIntent)
12487 chunk[4]=(unsigned char)
12488 Magick_RenderingIntent_to_PNG_RenderingIntent(
12489 (image->rendering_intent));
12490
12491 else
12492 chunk[4]=(unsigned char)
12493 Magick_RenderingIntent_to_PNG_RenderingIntent(
12494 (PerceptualIntent));
12495
12496 (void) WriteBlob(image,5,chunk);
12497 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12498 }
12499 else
12500 {
12501 if (image->gamma != 0.0)
12502 {
12503 /*
12504 Write JNG gAMA chunk
12505 */
12506 (void) WriteBlobMSBULong(image,4L);
12507 PNGType(chunk,mng_gAMA);
12508 LogPNGChunk(logging,mng_gAMA,4L);
12509 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12510 (void) WriteBlob(image,8,chunk);
12511 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12512 }
12513
12514 if ((mng_info->equal_chrms == MagickFalse) &&
12515 (image->chromaticity.red_primary.x != 0.0))
12516 {
12517 PrimaryInfo
12518 primary;
12519
12520 /*
12521 Write JNG cHRM chunk
12522 */
12523 (void) WriteBlobMSBULong(image,32L);
12524 PNGType(chunk,mng_cHRM);
12525 LogPNGChunk(logging,mng_cHRM,32L);
12526 primary=image->chromaticity.white_point;
12527 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12528 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12529 primary=image->chromaticity.red_primary;
12530 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12531 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12532 primary=image->chromaticity.green_primary;
12533 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12534 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12535 primary=image->chromaticity.blue_primary;
12536 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12537 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12538 (void) WriteBlob(image,36,chunk);
12539 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12540 }
12541 }
12542
12543 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12544 {
12545 /*
12546 Write JNG pHYs chunk
12547 */
12548 (void) WriteBlobMSBULong(image,9L);
12549 PNGType(chunk,mng_pHYs);
12550 LogPNGChunk(logging,mng_pHYs,9L);
12551 if (image->units == PixelsPerInchResolution)
12552 {
12553 PNGLong(chunk+4,(png_uint_32)
12554 (image->resolution.x*100.0/2.54+0.5));
12555
12556 PNGLong(chunk+8,(png_uint_32)
12557 (image->resolution.y*100.0/2.54+0.5));
12558
12559 chunk[12]=1;
12560 }
12561
12562 else
12563 {
12564 if (image->units == PixelsPerCentimeterResolution)
12565 {
12566 PNGLong(chunk+4,(png_uint_32)
12567 (image->resolution.x*100.0+0.5));
12568
12569 PNGLong(chunk+8,(png_uint_32)
12570 (image->resolution.y*100.0+0.5));
12571
12572 chunk[12]=1;
12573 }
12574
12575 else
12576 {
12577 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12578 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12579 chunk[12]=0;
12580 }
12581 }
12582 (void) WriteBlob(image,13,chunk);
12583 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12584 }
12585
12586 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12587 {
12588 /*
12589 Write JNG oFFs chunk
12590 */
12591 (void) WriteBlobMSBULong(image,9L);
12592 PNGType(chunk,mng_oFFs);
12593 LogPNGChunk(logging,mng_oFFs,9L);
12594 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12595 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12596 chunk[12]=0;
12597 (void) WriteBlob(image,13,chunk);
12598 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12599 }
12600 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12601 {
12602 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12603 PNGType(chunk,mng_vpAg);
12604 LogPNGChunk(logging,mng_vpAg,9L);
12605 PNGLong(chunk+4,(png_uint_32) image->page.width);
12606 PNGLong(chunk+8,(png_uint_32) image->page.height);
12607 chunk[12]=0; /* unit = pixels */
12608 (void) WriteBlob(image,13,chunk);
12609 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12610 }
12611
12612 if (transparent != 0)
12613 {
12614 if (jng_alpha_compression_method==0)
12615 {
12616 register ssize_t
12617 i;
12618
12619 size_t
12620 len;
12621
12622 /* Write IDAT chunk header */
12623 if (logging != MagickFalse)
12624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12625 " Write IDAT chunks from blob, length=%.20g.",(double)
12626 length);
12627
12628 /* Copy IDAT chunks */
12629 len=0;
12630 p=blob+8;
12631 for (i=8; i<(ssize_t) length; i+=len+12)
12632 {
12633 len=(size_t) (*p) << 24;
12634 len|=(size_t) (*(p+1)) << 16;
12635 len|=(size_t) (*(p+2)) << 8;
12636 len|=(size_t) (*(p+3));
12637 p+=4;
12638
12639 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12640 {
12641 /* Found an IDAT chunk. */
12642 (void) WriteBlobMSBULong(image,len);
12643 LogPNGChunk(logging,mng_IDAT,len);
12644 (void) WriteBlob(image,len+4,p);
12645 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12646 }
12647
12648 else
12649 {
12650 if (logging != MagickFalse)
12651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12652 " Skipping %c%c%c%c chunk, length=%.20g.",
12653 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12654 }
12655 p+=(8+len);
12656 }
12657 }
12658 else if (length != 0)
12659 {
12660 /* Write JDAA chunk header */
12661 if (logging != MagickFalse)
12662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12663 " Write JDAA chunk, length=%.20g.",(double) length);
12664 (void) WriteBlobMSBULong(image,(size_t) length);
12665 PNGType(chunk,mng_JDAA);
12666 LogPNGChunk(logging,mng_JDAA,length);
12667 /* Write JDAT chunk(s) data */
12668 (void) WriteBlob(image,4,chunk);
12669 (void) WriteBlob(image,length,blob);
12670 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12671 (uInt) length));
12672 }
12673 blob=(unsigned char *) RelinquishMagickMemory(blob);
12674 }
12675
12676 /* Encode image as a JPEG blob */
12677 if (logging != MagickFalse)
12678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12679 " Creating jpeg_image_info.");
12680 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12681 if (jpeg_image_info == (ImageInfo *) NULL)
12682 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12683
12684 if (logging != MagickFalse)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686 " Creating jpeg_image.");
12687
12688 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12689 if (jpeg_image == (Image *) NULL)
12690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12691 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12692
12693 (void) AcquireUniqueFilename(jpeg_image->filename);
12694 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12695 jpeg_image->filename);
12696
12697 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12698 exception);
12699
12700 if (logging != MagickFalse)
12701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12702 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12703 (double) jpeg_image->rows);
12704
12705 if (status == MagickFalse)
12706 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12707
12708 if (jng_color_type == 8 || jng_color_type == 12)
12709 jpeg_image_info->type=GrayscaleType;
12710
12711 jpeg_image_info->quality=jng_quality;
12712 jpeg_image->quality=jng_quality;
12713 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12714 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12715
12716 if (logging != MagickFalse)
12717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12718 " Creating blob.");
12719
12720 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12721 exception);
12722
12723 if (logging != MagickFalse)
12724 {
12725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12726 " Successfully read jpeg_image into a blob, length=%.20g.",
12727 (double) length);
12728
12729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12730 " Write JDAT chunk, length=%.20g.",(double) length);
12731 }
12732
12733 /* Write JDAT chunk(s) */
12734 (void) WriteBlobMSBULong(image,(size_t) length);
12735 PNGType(chunk,mng_JDAT);
12736 LogPNGChunk(logging,mng_JDAT,length);
12737 (void) WriteBlob(image,4,chunk);
12738 (void) WriteBlob(image,length,blob);
12739 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12740
12741 jpeg_image=DestroyImage(jpeg_image);
12742 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12743 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12744 blob=(unsigned char *) RelinquishMagickMemory(blob);
12745
12746 /* Write any JNG-chunk-e profiles */
12747 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12748
12749 /* Write IEND chunk */
12750 (void) WriteBlobMSBULong(image,0L);
12751 PNGType(chunk,mng_IEND);
12752 LogPNGChunk(logging,mng_IEND,0);
12753 (void) WriteBlob(image,4,chunk);
12754 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12755
12756 if (logging != MagickFalse)
12757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12758 " exit WriteOneJNGImage()");
12759
12760 return(status);
12761 }
12762
12763
12764 /*
12765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12766 % %
12767 % %
12768 % %
12769 % W r i t e J N G I m a g e %
12770 % %
12771 % %
12772 % %
12773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12774 %
12775 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12776 %
12777 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12778 %
12779 % The format of the WriteJNGImage method is:
12780 %
12781 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12782 % Image *image,ExceptionInfo *exception)
12783 %
12784 % A description of each parameter follows:
12785 %
12786 % o image_info: the image info.
12787 %
12788 % o image: The image.
12789 %
12790 % o exception: return any errors or warnings in this structure.
12791 %
12792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12793 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12794 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12795 ExceptionInfo *exception)
12796 {
12797 MagickBooleanType
12798 have_mng_structure,
12799 logging,
12800 status;
12801
12802 MngInfo
12803 *mng_info;
12804
12805 /*
12806 Open image file.
12807 */
12808 assert(image_info != (const ImageInfo *) NULL);
12809 assert(image_info->signature == MagickCoreSignature);
12810 assert(image != (Image *) NULL);
12811 assert(image->signature == MagickCoreSignature);
12812 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12813 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12814 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12815 if (status == MagickFalse)
12816 return(status);
12817 if ((image->columns > 65535UL) || (image->rows > 65535UL))
12818 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12819
12820 /*
12821 Allocate a MngInfo structure.
12822 */
12823 have_mng_structure=MagickFalse;
12824 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12825 if (mng_info == (MngInfo *) NULL)
12826 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12827 /*
12828 Initialize members of the MngInfo structure.
12829 */
12830 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12831 mng_info->image=image;
12832 have_mng_structure=MagickTrue;
12833
12834 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12835
12836 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12837 (void) CloseBlob(image);
12838
12839 (void) CatchImageException(image);
12840 MngInfoFreeStruct(mng_info,&have_mng_structure);
12841 if (logging != MagickFalse)
12842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12843 return(status);
12844 }
12845 #endif
12846
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12847 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12848 ExceptionInfo *exception)
12849 {
12850 const char
12851 *option;
12852
12853 Image
12854 *next_image;
12855
12856 MagickBooleanType
12857 have_mng_structure,
12858 status;
12859
12860 volatile MagickBooleanType
12861 logging;
12862
12863 MngInfo
12864 *mng_info;
12865
12866 int
12867 image_count,
12868 need_iterations,
12869 need_matte;
12870
12871 volatile int
12872 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12873 defined(PNG_MNG_FEATURES_SUPPORTED)
12874 need_local_plte,
12875 #endif
12876 all_images_are_gray,
12877 need_defi,
12878 use_global_plte;
12879
12880 register ssize_t
12881 i;
12882
12883 unsigned char
12884 chunk[800];
12885
12886 volatile unsigned int
12887 write_jng,
12888 write_mng;
12889
12890 volatile size_t
12891 scene;
12892
12893 size_t
12894 final_delay=0,
12895 initial_delay;
12896
12897 #if (PNG_LIBPNG_VER < 10200)
12898 if (image_info->verbose)
12899 printf("Your PNG library (libpng-%s) is rather old.\n",
12900 PNG_LIBPNG_VER_STRING);
12901 #endif
12902
12903 /*
12904 Open image file.
12905 */
12906 assert(image_info != (const ImageInfo *) NULL);
12907 assert(image_info->signature == MagickCoreSignature);
12908 assert(image != (Image *) NULL);
12909 assert(image->signature == MagickCoreSignature);
12910 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12911 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12912 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12913 if (status == MagickFalse)
12914 return(status);
12915
12916 /*
12917 Allocate a MngInfo structure.
12918 */
12919 have_mng_structure=MagickFalse;
12920 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12921 if (mng_info == (MngInfo *) NULL)
12922 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12923 /*
12924 Initialize members of the MngInfo structure.
12925 */
12926 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12927 mng_info->image=image;
12928 have_mng_structure=MagickTrue;
12929 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12930
12931 /*
12932 * See if user has requested a specific PNG subformat to be used
12933 * for all of the PNGs in the MNG being written, e.g.,
12934 *
12935 * convert *.png png8:animation.mng
12936 *
12937 * To do: check -define png:bit_depth and png:color_type as well,
12938 * or perhaps use mng:bit_depth and mng:color_type instead for
12939 * global settings.
12940 */
12941
12942 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12943 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12944 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12945
12946 write_jng=MagickFalse;
12947 if (image_info->compression == JPEGCompression)
12948 write_jng=MagickTrue;
12949
12950 mng_info->adjoin=image_info->adjoin &&
12951 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12952
12953 if (logging != MagickFalse)
12954 {
12955 /* Log some info about the input */
12956 Image
12957 *p;
12958
12959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12960 " Checking input image(s)\n"
12961 " Image_info depth: %.20g, Type: %d",
12962 (double) image_info->depth, image_info->type);
12963
12964 scene=0;
12965 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12966 {
12967
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969 " Scene: %.20g\n, Image depth: %.20g",
12970 (double) scene++, (double) p->depth);
12971
12972 if (p->alpha_trait != UndefinedPixelTrait)
12973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12974 " Matte: True");
12975
12976 else
12977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978 " Matte: False");
12979
12980 if (p->storage_class == PseudoClass)
12981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12982 " Storage class: PseudoClass");
12983
12984 else
12985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12986 " Storage class: DirectClass");
12987
12988 if (p->colors)
12989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990 " Number of colors: %.20g",(double) p->colors);
12991
12992 else
12993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12994 " Number of colors: unspecified");
12995
12996 if (mng_info->adjoin == MagickFalse)
12997 break;
12998 }
12999 }
13000
13001 use_global_plte=MagickFalse;
13002 all_images_are_gray=MagickFalse;
13003 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13004 need_local_plte=MagickTrue;
13005 #endif
13006 need_defi=MagickFalse;
13007 need_matte=MagickFalse;
13008 mng_info->framing_mode=1;
13009 mng_info->old_framing_mode=1;
13010
13011 if (write_mng)
13012 if (image_info->page != (char *) NULL)
13013 {
13014 /*
13015 Determine image bounding box.
13016 */
13017 SetGeometry(image,&mng_info->page);
13018 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13019 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13020 }
13021 if (write_mng)
13022 {
13023 unsigned int
13024 need_geom;
13025
13026 unsigned short
13027 red,
13028 green,
13029 blue;
13030
13031 mng_info->page=image->page;
13032 need_geom=MagickTrue;
13033 if (mng_info->page.width || mng_info->page.height)
13034 need_geom=MagickFalse;
13035 /*
13036 Check all the scenes.
13037 */
13038 initial_delay=image->delay;
13039 need_iterations=MagickFalse;
13040 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13041 mng_info->equal_physs=MagickTrue,
13042 mng_info->equal_gammas=MagickTrue;
13043 mng_info->equal_srgbs=MagickTrue;
13044 mng_info->equal_backgrounds=MagickTrue;
13045 image_count=0;
13046 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13047 defined(PNG_MNG_FEATURES_SUPPORTED)
13048 all_images_are_gray=MagickTrue;
13049 mng_info->equal_palettes=MagickFalse;
13050 need_local_plte=MagickFalse;
13051 #endif
13052 for (next_image=image; next_image != (Image *) NULL; )
13053 {
13054 if (need_geom)
13055 {
13056 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13057 mng_info->page.width=next_image->columns+next_image->page.x;
13058
13059 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13060 mng_info->page.height=next_image->rows+next_image->page.y;
13061 }
13062
13063 if (next_image->page.x || next_image->page.y)
13064 need_defi=MagickTrue;
13065
13066 if (next_image->alpha_trait != UndefinedPixelTrait)
13067 need_matte=MagickTrue;
13068
13069 if ((int) next_image->dispose >= BackgroundDispose)
13070 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13071 next_image->page.x || next_image->page.y ||
13072 ((next_image->columns < mng_info->page.width) &&
13073 (next_image->rows < mng_info->page.height)))
13074 mng_info->need_fram=MagickTrue;
13075
13076 if (next_image->iterations)
13077 need_iterations=MagickTrue;
13078
13079 final_delay=next_image->delay;
13080
13081 if (final_delay != initial_delay || final_delay > 1UL*
13082 next_image->ticks_per_second)
13083 mng_info->need_fram=1;
13084
13085 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13086 defined(PNG_MNG_FEATURES_SUPPORTED)
13087 /*
13088 check for global palette possibility.
13089 */
13090 if (image->alpha_trait != UndefinedPixelTrait)
13091 need_local_plte=MagickTrue;
13092
13093 if (need_local_plte == 0)
13094 {
13095 if (SetImageGray(image,exception) == MagickFalse)
13096 all_images_are_gray=MagickFalse;
13097 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13098 if (use_global_plte == 0)
13099 use_global_plte=mng_info->equal_palettes;
13100 need_local_plte=!mng_info->equal_palettes;
13101 }
13102 #endif
13103 if (GetNextImageInList(next_image) != (Image *) NULL)
13104 {
13105 if (next_image->background_color.red !=
13106 next_image->next->background_color.red ||
13107 next_image->background_color.green !=
13108 next_image->next->background_color.green ||
13109 next_image->background_color.blue !=
13110 next_image->next->background_color.blue)
13111 mng_info->equal_backgrounds=MagickFalse;
13112
13113 if (next_image->gamma != next_image->next->gamma)
13114 mng_info->equal_gammas=MagickFalse;
13115
13116 if (next_image->rendering_intent !=
13117 next_image->next->rendering_intent)
13118 mng_info->equal_srgbs=MagickFalse;
13119
13120 if ((next_image->units != next_image->next->units) ||
13121 (next_image->resolution.x != next_image->next->resolution.x) ||
13122 (next_image->resolution.y != next_image->next->resolution.y))
13123 mng_info->equal_physs=MagickFalse;
13124
13125 if (mng_info->equal_chrms)
13126 {
13127 if (next_image->chromaticity.red_primary.x !=
13128 next_image->next->chromaticity.red_primary.x ||
13129 next_image->chromaticity.red_primary.y !=
13130 next_image->next->chromaticity.red_primary.y ||
13131 next_image->chromaticity.green_primary.x !=
13132 next_image->next->chromaticity.green_primary.x ||
13133 next_image->chromaticity.green_primary.y !=
13134 next_image->next->chromaticity.green_primary.y ||
13135 next_image->chromaticity.blue_primary.x !=
13136 next_image->next->chromaticity.blue_primary.x ||
13137 next_image->chromaticity.blue_primary.y !=
13138 next_image->next->chromaticity.blue_primary.y ||
13139 next_image->chromaticity.white_point.x !=
13140 next_image->next->chromaticity.white_point.x ||
13141 next_image->chromaticity.white_point.y !=
13142 next_image->next->chromaticity.white_point.y)
13143 mng_info->equal_chrms=MagickFalse;
13144 }
13145 }
13146 image_count++;
13147 next_image=GetNextImageInList(next_image);
13148 }
13149 if (image_count < 2)
13150 {
13151 mng_info->equal_backgrounds=MagickFalse;
13152 mng_info->equal_chrms=MagickFalse;
13153 mng_info->equal_gammas=MagickFalse;
13154 mng_info->equal_srgbs=MagickFalse;
13155 mng_info->equal_physs=MagickFalse;
13156 use_global_plte=MagickFalse;
13157 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13158 need_local_plte=MagickTrue;
13159 #endif
13160 need_iterations=MagickFalse;
13161 }
13162
13163 if (mng_info->need_fram == MagickFalse)
13164 {
13165 /*
13166 Only certain framing rates 100/n are exactly representable without
13167 the FRAM chunk but we'll allow some slop in VLC files
13168 */
13169 if (final_delay == 0)
13170 {
13171 if (need_iterations != MagickFalse)
13172 {
13173 /*
13174 It's probably a GIF with loop; don't run it *too* fast.
13175 */
13176 if (mng_info->adjoin)
13177 {
13178 final_delay=10;
13179 (void) ThrowMagickException(exception,GetMagickModule(),
13180 CoderWarning,
13181 "input has zero delay between all frames; assuming",
13182 " 10 cs `%s'","");
13183 }
13184 }
13185 else
13186 mng_info->ticks_per_second=0;
13187 }
13188 if (final_delay != 0)
13189 mng_info->ticks_per_second=(png_uint_32)
13190 (image->ticks_per_second/final_delay);
13191 if (final_delay > 50)
13192 mng_info->ticks_per_second=2;
13193
13194 if (final_delay > 75)
13195 mng_info->ticks_per_second=1;
13196
13197 if (final_delay > 125)
13198 mng_info->need_fram=MagickTrue;
13199
13200 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13201 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13202 (final_delay != 25) && (final_delay != 50) &&
13203 (final_delay != (size_t) image->ticks_per_second))
13204 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13205 }
13206
13207 if (mng_info->need_fram != MagickFalse)
13208 mng_info->ticks_per_second=image->ticks_per_second;
13209 /*
13210 If pseudocolor, we should also check to see if all the
13211 palettes are identical and write a global PLTE if they are.
13212 ../glennrp Feb 99.
13213 */
13214 /*
13215 Write the MNG version 1.0 signature and MHDR chunk.
13216 */
13217 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13218 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13219 PNGType(chunk,mng_MHDR);
13220 LogPNGChunk(logging,mng_MHDR,28L);
13221 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13222 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13223 PNGLong(chunk+12,mng_info->ticks_per_second);
13224 PNGLong(chunk+16,0L); /* layer count=unknown */
13225 PNGLong(chunk+20,0L); /* frame count=unknown */
13226 PNGLong(chunk+24,0L); /* play time=unknown */
13227 if (write_jng)
13228 {
13229 if (need_matte)
13230 {
13231 if (need_defi || mng_info->need_fram || use_global_plte)
13232 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13233
13234 else
13235 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13236 }
13237
13238 else
13239 {
13240 if (need_defi || mng_info->need_fram || use_global_plte)
13241 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13242
13243 else
13244 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13245 }
13246 }
13247
13248 else
13249 {
13250 if (need_matte)
13251 {
13252 if (need_defi || mng_info->need_fram || use_global_plte)
13253 PNGLong(chunk+28,11L); /* simplicity=LC */
13254
13255 else
13256 PNGLong(chunk+28,9L); /* simplicity=VLC */
13257 }
13258
13259 else
13260 {
13261 if (need_defi || mng_info->need_fram || use_global_plte)
13262 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13263
13264 else
13265 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13266 }
13267 }
13268 (void) WriteBlob(image,32,chunk);
13269 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13270 option=GetImageOption(image_info,"mng:need-cacheoff");
13271 if (option != (const char *) NULL)
13272 {
13273 size_t
13274 length;
13275
13276 /*
13277 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13278 */
13279 PNGType(chunk,mng_nEED);
13280 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13281 (void) WriteBlobMSBULong(image,(size_t) length);
13282 LogPNGChunk(logging,mng_nEED,(size_t) length);
13283 length+=4;
13284 (void) WriteBlob(image,length,chunk);
13285 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13286 }
13287 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13288 (GetNextImageInList(image) != (Image *) NULL) &&
13289 (image->iterations != 1))
13290 {
13291 /*
13292 Write MNG TERM chunk
13293 */
13294 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13295 PNGType(chunk,mng_TERM);
13296 LogPNGChunk(logging,mng_TERM,10L);
13297 chunk[4]=3; /* repeat animation */
13298 chunk[5]=0; /* show last frame when done */
13299 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13300 final_delay/MagickMax(image->ticks_per_second,1)));
13301
13302 if (image->iterations == 0)
13303 PNGLong(chunk+10,PNG_UINT_31_MAX);
13304
13305 else
13306 PNGLong(chunk+10,(png_uint_32) image->iterations);
13307
13308 if (logging != MagickFalse)
13309 {
13310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13311 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13312 final_delay/MagickMax(image->ticks_per_second,1)));
13313
13314 if (image->iterations == 0)
13315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13316 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13317
13318 else
13319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13320 " Image iterations: %.20g",(double) image->iterations);
13321 }
13322 (void) WriteBlob(image,14,chunk);
13323 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13324 }
13325 /*
13326 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13327 */
13328 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13329 mng_info->equal_srgbs)
13330 {
13331 /*
13332 Write MNG sRGB chunk
13333 */
13334 (void) WriteBlobMSBULong(image,1L);
13335 PNGType(chunk,mng_sRGB);
13336 LogPNGChunk(logging,mng_sRGB,1L);
13337
13338 if (image->rendering_intent != UndefinedIntent)
13339 chunk[4]=(unsigned char)
13340 Magick_RenderingIntent_to_PNG_RenderingIntent(
13341 (image->rendering_intent));
13342
13343 else
13344 chunk[4]=(unsigned char)
13345 Magick_RenderingIntent_to_PNG_RenderingIntent(
13346 (PerceptualIntent));
13347
13348 (void) WriteBlob(image,5,chunk);
13349 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13350 mng_info->have_write_global_srgb=MagickTrue;
13351 }
13352
13353 else
13354 {
13355 if (image->gamma && mng_info->equal_gammas)
13356 {
13357 /*
13358 Write MNG gAMA chunk
13359 */
13360 (void) WriteBlobMSBULong(image,4L);
13361 PNGType(chunk,mng_gAMA);
13362 LogPNGChunk(logging,mng_gAMA,4L);
13363 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13364 (void) WriteBlob(image,8,chunk);
13365 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13366 mng_info->have_write_global_gama=MagickTrue;
13367 }
13368 if (mng_info->equal_chrms)
13369 {
13370 PrimaryInfo
13371 primary;
13372
13373 /*
13374 Write MNG cHRM chunk
13375 */
13376 (void) WriteBlobMSBULong(image,32L);
13377 PNGType(chunk,mng_cHRM);
13378 LogPNGChunk(logging,mng_cHRM,32L);
13379 primary=image->chromaticity.white_point;
13380 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13381 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13382 primary=image->chromaticity.red_primary;
13383 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13384 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13385 primary=image->chromaticity.green_primary;
13386 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13387 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13388 primary=image->chromaticity.blue_primary;
13389 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13390 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13391 (void) WriteBlob(image,36,chunk);
13392 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13393 mng_info->have_write_global_chrm=MagickTrue;
13394 }
13395 }
13396 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13397 {
13398 /*
13399 Write MNG pHYs chunk
13400 */
13401 (void) WriteBlobMSBULong(image,9L);
13402 PNGType(chunk,mng_pHYs);
13403 LogPNGChunk(logging,mng_pHYs,9L);
13404
13405 if (image->units == PixelsPerInchResolution)
13406 {
13407 PNGLong(chunk+4,(png_uint_32)
13408 (image->resolution.x*100.0/2.54+0.5));
13409
13410 PNGLong(chunk+8,(png_uint_32)
13411 (image->resolution.y*100.0/2.54+0.5));
13412
13413 chunk[12]=1;
13414 }
13415
13416 else
13417 {
13418 if (image->units == PixelsPerCentimeterResolution)
13419 {
13420 PNGLong(chunk+4,(png_uint_32)
13421 (image->resolution.x*100.0+0.5));
13422
13423 PNGLong(chunk+8,(png_uint_32)
13424 (image->resolution.y*100.0+0.5));
13425
13426 chunk[12]=1;
13427 }
13428
13429 else
13430 {
13431 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13432 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13433 chunk[12]=0;
13434 }
13435 }
13436 (void) WriteBlob(image,13,chunk);
13437 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13438 }
13439 /*
13440 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13441 or does not cover the entire frame.
13442 */
13443 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13444 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13445 (image->page.width+image->page.x < mng_info->page.width))
13446 || (image->page.height && (image->page.height+image->page.y
13447 < mng_info->page.height))))
13448 {
13449 (void) WriteBlobMSBULong(image,6L);
13450 PNGType(chunk,mng_BACK);
13451 LogPNGChunk(logging,mng_BACK,6L);
13452 red=ScaleQuantumToShort(image->background_color.red);
13453 green=ScaleQuantumToShort(image->background_color.green);
13454 blue=ScaleQuantumToShort(image->background_color.blue);
13455 PNGShort(chunk+4,red);
13456 PNGShort(chunk+6,green);
13457 PNGShort(chunk+8,blue);
13458 (void) WriteBlob(image,10,chunk);
13459 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13460 if (mng_info->equal_backgrounds)
13461 {
13462 (void) WriteBlobMSBULong(image,6L);
13463 PNGType(chunk,mng_bKGD);
13464 LogPNGChunk(logging,mng_bKGD,6L);
13465 (void) WriteBlob(image,10,chunk);
13466 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13467 }
13468 }
13469
13470 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13471 if ((need_local_plte == MagickFalse) &&
13472 (image->storage_class == PseudoClass) &&
13473 (all_images_are_gray == MagickFalse))
13474 {
13475 size_t
13476 data_length;
13477
13478 /*
13479 Write MNG PLTE chunk
13480 */
13481 data_length=3*image->colors;
13482 (void) WriteBlobMSBULong(image,data_length);
13483 PNGType(chunk,mng_PLTE);
13484 LogPNGChunk(logging,mng_PLTE,data_length);
13485
13486 for (i=0; i < (ssize_t) image->colors; i++)
13487 {
13488 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13489 image->colormap[i].red) & 0xff);
13490 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13491 image->colormap[i].green) & 0xff);
13492 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13493 image->colormap[i].blue) & 0xff);
13494 }
13495
13496 (void) WriteBlob(image,data_length+4,chunk);
13497 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13498 mng_info->have_write_global_plte=MagickTrue;
13499 }
13500 #endif
13501 }
13502 scene=0;
13503 mng_info->delay=0;
13504 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13505 defined(PNG_MNG_FEATURES_SUPPORTED)
13506 mng_info->equal_palettes=MagickFalse;
13507 #endif
13508 do
13509 {
13510 if (mng_info->adjoin)
13511 {
13512 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13513 defined(PNG_MNG_FEATURES_SUPPORTED)
13514 /*
13515 If we aren't using a global palette for the entire MNG, check to
13516 see if we can use one for two or more consecutive images.
13517 */
13518 if (need_local_plte && use_global_plte && !all_images_are_gray)
13519 {
13520 if (mng_info->IsPalette)
13521 {
13522 /*
13523 When equal_palettes is true, this image has the same palette
13524 as the previous PseudoClass image
13525 */
13526 mng_info->have_write_global_plte=mng_info->equal_palettes;
13527 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13528 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13529 {
13530 /*
13531 Write MNG PLTE chunk
13532 */
13533 size_t
13534 data_length;
13535
13536 data_length=3*image->colors;
13537 (void) WriteBlobMSBULong(image,data_length);
13538 PNGType(chunk,mng_PLTE);
13539 LogPNGChunk(logging,mng_PLTE,data_length);
13540
13541 for (i=0; i < (ssize_t) image->colors; i++)
13542 {
13543 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13544 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13545 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13546 }
13547
13548 (void) WriteBlob(image,data_length+4,chunk);
13549 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13550 (uInt) (data_length+4)));
13551 mng_info->have_write_global_plte=MagickTrue;
13552 }
13553 }
13554 else
13555 mng_info->have_write_global_plte=MagickFalse;
13556 }
13557 #endif
13558 if (need_defi)
13559 {
13560 ssize_t
13561 previous_x,
13562 previous_y;
13563
13564 if (scene)
13565 {
13566 previous_x=mng_info->page.x;
13567 previous_y=mng_info->page.y;
13568 }
13569 else
13570 {
13571 previous_x=0;
13572 previous_y=0;
13573 }
13574 mng_info->page=image->page;
13575 if ((mng_info->page.x != previous_x) ||
13576 (mng_info->page.y != previous_y))
13577 {
13578 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13579 PNGType(chunk,mng_DEFI);
13580 LogPNGChunk(logging,mng_DEFI,12L);
13581 chunk[4]=0; /* object 0 MSB */
13582 chunk[5]=0; /* object 0 LSB */
13583 chunk[6]=0; /* visible */
13584 chunk[7]=0; /* abstract */
13585 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13586 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13587 (void) WriteBlob(image,16,chunk);
13588 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13589 }
13590 }
13591 }
13592
13593 mng_info->write_mng=write_mng;
13594
13595 if ((int) image->dispose >= 3)
13596 mng_info->framing_mode=3;
13597
13598 if (mng_info->need_fram && mng_info->adjoin &&
13599 ((image->delay != mng_info->delay) ||
13600 (mng_info->framing_mode != mng_info->old_framing_mode)))
13601 {
13602 if (image->delay == mng_info->delay)
13603 {
13604 /*
13605 Write a MNG FRAM chunk with the new framing mode.
13606 */
13607 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13608 PNGType(chunk,mng_FRAM);
13609 LogPNGChunk(logging,mng_FRAM,1L);
13610 chunk[4]=(unsigned char) mng_info->framing_mode;
13611 (void) WriteBlob(image,5,chunk);
13612 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13613 }
13614 else
13615 {
13616 /*
13617 Write a MNG FRAM chunk with the delay.
13618 */
13619 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13620 PNGType(chunk,mng_FRAM);
13621 LogPNGChunk(logging,mng_FRAM,10L);
13622 chunk[4]=(unsigned char) mng_info->framing_mode;
13623 chunk[5]=0; /* frame name separator (no name) */
13624 chunk[6]=2; /* flag for changing default delay */
13625 chunk[7]=0; /* flag for changing frame timeout */
13626 chunk[8]=0; /* flag for changing frame clipping */
13627 chunk[9]=0; /* flag for changing frame sync_id */
13628 PNGLong(chunk+10,(png_uint_32)
13629 ((mng_info->ticks_per_second*
13630 image->delay)/MagickMax(image->ticks_per_second,1)));
13631 (void) WriteBlob(image,14,chunk);
13632 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13633 mng_info->delay=(png_uint_32) image->delay;
13634 }
13635 mng_info->old_framing_mode=mng_info->framing_mode;
13636 }
13637
13638 #if defined(JNG_SUPPORTED)
13639 if (image_info->compression == JPEGCompression)
13640 {
13641 ImageInfo
13642 *write_info;
13643
13644 if (logging != MagickFalse)
13645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13646 " Writing JNG object.");
13647 /* To do: specify the desired alpha compression method. */
13648 write_info=CloneImageInfo(image_info);
13649 write_info->compression=UndefinedCompression;
13650 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13651 write_info=DestroyImageInfo(write_info);
13652 }
13653 else
13654 #endif
13655 {
13656 if (logging != MagickFalse)
13657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13658 " Writing PNG object.");
13659
13660 mng_info->need_blob = MagickFalse;
13661 mng_info->ping_preserve_colormap = MagickFalse;
13662
13663 /* We don't want any ancillary chunks written */
13664 mng_info->ping_exclude_bKGD=MagickTrue;
13665 mng_info->ping_exclude_cHRM=MagickTrue;
13666 mng_info->ping_exclude_date=MagickTrue;
13667 mng_info->ping_exclude_EXIF=MagickTrue;
13668 mng_info->ping_exclude_gAMA=MagickTrue;
13669 mng_info->ping_exclude_iCCP=MagickTrue;
13670 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13671 mng_info->ping_exclude_oFFs=MagickTrue;
13672 mng_info->ping_exclude_pHYs=MagickTrue;
13673 mng_info->ping_exclude_sRGB=MagickTrue;
13674 mng_info->ping_exclude_tEXt=MagickTrue;
13675 mng_info->ping_exclude_tRNS=MagickTrue;
13676 mng_info->ping_exclude_vpAg=MagickTrue;
13677 mng_info->ping_exclude_zCCP=MagickTrue;
13678 mng_info->ping_exclude_zTXt=MagickTrue;
13679
13680 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13681 }
13682
13683 if (status == MagickFalse)
13684 {
13685 MngInfoFreeStruct(mng_info,&have_mng_structure);
13686 (void) CloseBlob(image);
13687 return(MagickFalse);
13688 }
13689 (void) CatchImageException(image);
13690 if (GetNextImageInList(image) == (Image *) NULL)
13691 break;
13692 image=SyncNextImageInList(image);
13693 status=SetImageProgress(image,SaveImagesTag,scene++,
13694 GetImageListLength(image));
13695
13696 if (status == MagickFalse)
13697 break;
13698
13699 } while (mng_info->adjoin);
13700
13701 if (write_mng)
13702 {
13703 while (GetPreviousImageInList(image) != (Image *) NULL)
13704 image=GetPreviousImageInList(image);
13705 /*
13706 Write the MEND chunk.
13707 */
13708 (void) WriteBlobMSBULong(image,0x00000000L);
13709 PNGType(chunk,mng_MEND);
13710 LogPNGChunk(logging,mng_MEND,0L);
13711 (void) WriteBlob(image,4,chunk);
13712 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13713 }
13714 /*
13715 Relinquish resources.
13716 */
13717 (void) CloseBlob(image);
13718 MngInfoFreeStruct(mng_info,&have_mng_structure);
13719
13720 if (logging != MagickFalse)
13721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13722
13723 return(MagickTrue);
13724 }
13725 #else /* PNG_LIBPNG_VER > 10011 */
13726
WritePNGImage(const ImageInfo * image_info,Image * image)13727 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13728 {
13729 (void) image;
13730 printf("Your PNG library is too old: You have libpng-%s\n",
13731 PNG_LIBPNG_VER_STRING);
13732
13733 ThrowBinaryException(CoderError,"PNG library is too old",
13734 image_info->filename);
13735 }
13736
WriteMNGImage(const ImageInfo * image_info,Image * image)13737 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13738 {
13739 return(WritePNGImage(image_info,image));
13740 }
13741 #endif /* PNG_LIBPNG_VER > 10011 */
13742 #endif
13743