blob: ef294760491df01e606fba15d092930eed505ae8 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
21% Copyright 1999-2009 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 "magick/studio.h"
45#include "magick/blob.h"
46#include "magick/blob-private.h"
47#include "magick/cache.h"
48#include "magick/color.h"
49#include "magick/color-private.h"
50#include "magick/colorspace.h"
51#include "magick/constitute.h"
52#include "magick/enhance.h"
53#include "magick/exception.h"
54#include "magick/exception-private.h"
55#include "magick/geometry.h"
56#include "magick/image.h"
57#include "magick/image-private.h"
58#include "magick/layer.h"
59#include "magick/list.h"
60#include "magick/log.h"
61#include "magick/magick.h"
62#include "magick/memory_.h"
63#include "magick/module.h"
64#include "magick/monitor.h"
65#include "magick/monitor-private.h"
66#include "magick/option.h"
67#include "magick/quantum-private.h"
68#include "magick/profile.h"
69#include "magick/property.h"
70#include "magick/quantize.h"
71#include "magick/resource_.h"
72#include "magick/semaphore.h"
73#include "magick/quantum-private.h"
74#include "magick/static.h"
75#include "magick/statistic.h"
76#include "magick/string_.h"
77#include "magick/transform.h"
78#include "magick/utility.h"
79#if defined(MAGICKCORE_PNG_DELEGATE)
80#include "png.h"
81#include "zlib.h"
82
83/* ImageMagick differences */
84#define first_scene scene
85
86#if PNG_LIBPNG_VER < 10400
87#define trans_color trans_values /* Changed at libpng-1.4.0beta35 */
88#define trans_alpha trans /* Changed at libpng-1.4.0beta74 */
89#endif
90
91#if PNG_LIBPNG_VER > 95
92/*
93 Optional declarations. Define or undefine them as you like.
94*/
95/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
96
97/*
98 Features under construction. Define these to work on them.
99*/
100#undef MNG_OBJECT_BUFFERS
101#undef MNG_BASI_SUPPORTED
102#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
103#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
104#define PNG_BUILD_PALETTE /* This works as of 5.4.3. */
105#define PNG_SORT_PALETTE /* This works as of 5.4.0. */
106#if defined(MAGICKCORE_JPEG_DELEGATE)
107# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
108#endif
109#if !defined(RGBColorMatchExact)
110#define IsPNGColorEqual(color,target) \
111 (((color).red == (target).red) && \
112 ((color).green == (target).green) && \
113 ((color).blue == (target).blue))
114#endif
115
116/*
117 Establish thread safety.
118 setjmp/longjmp is claimed to be safe on these platforms:
119 setjmp/longjmp is alleged to be unsafe on these platforms:
120*/
121#ifndef SETJMP_IS_THREAD_SAFE
122#define PNG_SETJMP_NOT_THREAD_SAFE
123#endif
124
125#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
126static SemaphoreInfo
127 *png_semaphore = (SemaphoreInfo *) NULL;
128#endif
129
130/*
131 This temporary until I set up malloc'ed object attributes array.
132 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
133 waste more memory.
134*/
135#define MNG_MAX_OBJECTS 256
136
137/*
138 If this not defined, spec is interpreted strictly. If it is
139 defined, an attempt will be made to recover from some errors,
140 including
141 o global PLTE too short
142*/
143#undef MNG_LOOSE
144
145/*
146 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
147 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
148 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
149 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
150 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
151 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
152 will be enabled by default in libpng-1.2.0.
153*/
154#if (PNG_LIBPNG_VER == 10009) /* work around libpng-1.0.9 bug */
155# undef PNG_READ_EMPTY_PLTE_SUPPORTED
156# undef PNG_WRITE_EMPTY_PLTE_SUPPORTED
157#endif
158#ifdef PNG_MNG_FEATURES_SUPPORTED
159# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
160# define PNG_READ_EMPTY_PLTE_SUPPORTED
161# endif
162# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
163# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
164# endif
165#endif
166
167/*
168 Maximum valid unsigned long in PNG/MNG chunks is (2^31)-1
169 This macro is only defined in libpng-1.0.3 and later.
170 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
171*/
172#ifndef PNG_UINT_31_MAX
173#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
174#endif
175
176/*
177 Constant strings for known chunk types. If you need to add a chunk,
178 add a string holding the name here. To make the code more
179 portable, we use ASCII numbers like this, not characters.
180*/
181
182static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
183static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
184static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
185static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
186static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
187static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
188static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
189static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
190static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
191static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
192static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
193static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
194static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
195static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
196static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
197static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
198static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
199static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
200static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
201static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
202static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
203static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
204static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
205static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
206static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
207static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
208static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
209static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
210static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
211static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
212static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
213static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
214static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
215static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
216
217#if defined(JNG_SUPPORTED)
218static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
219static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
220static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
221static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
222static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
223static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
224#endif
225
226/*
227Other known chunks that are not yet supported by ImageMagick:
228static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
229static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
230static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
231static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
232static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
233static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
234static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
235static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
236*/
237
238typedef struct _MngBox
239{
240 long
241 left,
242 right,
243 top,
244 bottom;
245} MngBox;
246
247typedef struct _MngPair
248{
249 volatile long
250 a,
251 b;
252} MngPair;
253
254#ifdef MNG_OBJECT_BUFFERS
255typedef struct _MngBuffer
256{
257
258 unsigned long
259 height,
260 width;
261
262 Image
263 *image;
264
265 png_color
266 plte[256];
267
268 int
269 reference_count;
270
271 unsigned char
272 alpha_sample_depth,
273 compression_method,
274 color_type,
275 concrete,
276 filter_method,
277 frozen,
278 image_type,
279 interlace_method,
280 pixel_sample_depth,
281 plte_length,
282 sample_depth,
283 viewable;
284} MngBuffer;
285#endif
286
287typedef struct _MngInfo
288{
289
290#ifdef MNG_OBJECT_BUFFERS
291 MngBuffer
292 *ob[MNG_MAX_OBJECTS];
293#endif
294
295 Image *
296 image;
297
298 RectangleInfo
299 page;
300
301 int
302 adjoin,
303#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
304 bytes_in_read_buffer,
305 found_empty_plte,
306#endif
307 equal_backgrounds,
308 equal_chrms,
309 equal_gammas,
310#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
311 defined(PNG_MNG_FEATURES_SUPPORTED)
312 equal_palettes,
313#endif
314 equal_physs,
315 equal_srgbs,
316 framing_mode,
317 have_global_bkgd,
318 have_global_chrm,
319 have_global_gama,
320 have_global_phys,
321 have_global_sbit,
322 have_global_srgb,
323 have_saved_bkgd_index,
324 have_write_global_chrm,
325 have_write_global_gama,
326 have_write_global_plte,
327 have_write_global_srgb,
328 need_fram,
329 object_id,
330 old_framing_mode,
331 optimize,
332 saved_bkgd_index;
333
334 int
335 new_number_colors;
336
337 long
338 image_found,
339 loop_count[256],
340 loop_iteration[256],
341 scenes_found,
342 x_off[MNG_MAX_OBJECTS],
343 y_off[MNG_MAX_OBJECTS];
344
345 MngBox
346 clip,
347 frame,
348 image_box,
349 object_clip[MNG_MAX_OBJECTS];
350
351 unsigned char
352 /* These flags could be combined into one byte */
353 exists[MNG_MAX_OBJECTS],
354 frozen[MNG_MAX_OBJECTS],
355 loop_active[256],
356 invisible[MNG_MAX_OBJECTS],
357 viewable[MNG_MAX_OBJECTS];
358
359 MagickOffsetType
360 loop_jump[256];
361
362 png_colorp
363 global_plte;
364
365 png_color_8
366 global_sbit;
367
368 png_byte
369#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
370 read_buffer[8],
371#endif
372 global_trns[256];
373
374 float
375 global_gamma;
376
377 ChromaticityInfo
378 global_chrm;
379
380 RenderingIntent
381 global_srgb_intent;
382
383 unsigned long
384 delay,
385 global_plte_length,
386 global_trns_length,
387 global_x_pixels_per_unit,
388 global_y_pixels_per_unit,
389 mng_width,
390 mng_height,
391 ticks_per_second;
392
393 unsigned int
394 IsPalette,
395 global_phys_unit_type,
396 basi_warning,
397 clon_warning,
398 dhdr_warning,
399 jhdr_warning,
400 magn_warning,
401 past_warning,
402 phyg_warning,
403 phys_warning,
404 sbit_warning,
405 show_warning,
406 mng_type,
407 write_mng,
408 write_png_colortype,
409 write_png_depth,
410 write_png8,
411 write_png24,
412 write_png32;
413
414#ifdef MNG_BASI_SUPPORTED
415 unsigned long
416 basi_width,
417 basi_height;
418
419 unsigned int
420 basi_depth,
421 basi_color_type,
422 basi_compression_method,
423 basi_filter_type,
424 basi_interlace_method,
425 basi_red,
426 basi_green,
427 basi_blue,
428 basi_alpha,
429 basi_viewable;
430#endif
431
432 png_uint_16
433 magn_first,
434 magn_last,
435 magn_mb,
436 magn_ml,
437 magn_mr,
438 magn_mt,
439 magn_mx,
440 magn_my,
441 magn_methx,
442 magn_methy;
443
444 PixelPacket
445 mng_global_bkgd;
446
447} MngInfo;
448#endif /* VER */
449
450/*
451 Forward declarations.
452*/
453static MagickBooleanType
454 WritePNGImage(const ImageInfo *,Image *);
455static MagickBooleanType
456 WriteMNGImage(const ImageInfo *,Image *);
457#if defined(JNG_SUPPORTED)
458static MagickBooleanType
459 WriteJNGImage(const ImageInfo *,Image *);
460#endif
461
462static inline long MagickMax(const long x,const long y)
463{
464 if (x > y)
465 return(x);
466 return(y);
467}
468static inline long MagickMin(const long x,const long y)
469{
470 if (x < y)
471 return(x);
472 return(y);
473}
474
475#if PNG_LIBPNG_VER > 95
476#if defined(PNG_SORT_PALETTE)
477/*
478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479% %
480% %
481% %
482% C o m p r e s s C o l o r m a p T r a n s F i r s t %
483% %
484% %
485% %
486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487%
488% CompressColormapTransFirst compresses an image colormap removing
489% any duplicate and unused color entries and putting the transparent colors
490% first. Returns MagickTrue on success, MagickFalse on error.
491%
492% The format of the CompressColormapTransFirst method is:
493%
494% unsigned int CompressColormapTransFirst(Image *image)
495%
496% A description of each parameter follows:
497%
498% o image: the address of a structure of type Image.
499%
500*/
501static MagickBooleanType CompressColormapTransFirst(Image *image)
502{
503 int
504 remap_needed,
505 k;
506
507 long
508 j,
509 new_number_colors,
510 number_colors,
511 y;
512
513 PixelPacket
514 *colormap;
515
516 register const IndexPacket
517 *indices;
518
519 register const PixelPacket
520 *p;
521
522 IndexPacket
523 top_used;
524
525 register long
526 i,
527 x;
528
529 IndexPacket
530 *map,
531 *opacity;
532
533 unsigned char
534 *marker,
535 have_transparency;
536
537 /*
538 Determine if colormap can be compressed.
539 */
540 assert(image != (Image *) NULL);
541 assert(image->signature == MagickSignature);
542 if (image->debug != MagickFalse)
543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
544 if (image->storage_class != PseudoClass || image->colors > 256 ||
545 image->colors < 2)
546 return(MagickFalse);
547 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
548 if (marker == (unsigned char *) NULL)
549 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
550 image->filename);
551 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
552 if (opacity == (IndexPacket *) NULL)
553 {
554 marker=(unsigned char *) RelinquishMagickMemory(marker);
555 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
556 image->filename);
557 }
558 /*
559 Mark colors that are present.
560 */
561 number_colors=(long) image->colors;
562 for (i=0; i < number_colors; i++)
563 {
564 marker[i]=MagickFalse;
565 opacity[i]=OpaqueOpacity;
566 }
567 top_used=0;
568 for (y=0; y < (long) image->rows; y++)
569 {
570 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
571 if (p == (const PixelPacket *) NULL)
572 break;
573 indices=GetVirtualIndexQueue(image);
574 if (image->matte != MagickFalse)
575 for (x=0; x < (long) image->columns; x++)
576 {
577 marker[(int) indices[x]]=MagickTrue;
578 opacity[(int) indices[x]]=p->opacity;
579 if (indices[x] > top_used)
580 top_used=indices[x];
581 p++;
582 }
583 else
584 for (x=0; x < (long) image->columns; x++)
585 {
586 marker[(int) indices[x]]=MagickTrue;
587 if (indices[x] > top_used)
588 top_used=indices[x];
589 }
590 }
591
592 if (image->matte != MagickFalse)
593 {
594 /*
595 Mark background color, topmost occurrence if more than one.
596 */
597 for (i=number_colors-1; i; i--)
598 {
599 if (IsColorEqual(image->colormap+i,&image->background_color))
600 {
601 marker[i]=MagickTrue;
602 break;
603 }
604 }
605 }
606 /*
607 Unmark duplicates.
608 */
609 for (i=0; i < number_colors-1; i++)
610 if (marker[i])
611 {
612 for (j=i+1; j < number_colors; j++)
613 if ((opacity[i] == opacity[j]) &&
614 (IsColorEqual(image->colormap+i,image->colormap+j)))
615 marker[j]=MagickFalse;
616 }
617 /*
618 Count colors that still remain.
619 */
620 have_transparency=MagickFalse;
621 new_number_colors=0;
622 for (i=0; i < number_colors; i++)
623 if (marker[i])
624 {
625 new_number_colors++;
626 if (opacity[i] != OpaqueOpacity)
627 have_transparency=MagickTrue;
628 }
629 if ((!have_transparency || (marker[0] &&
630 (opacity[0] == (Quantum) TransparentOpacity)))
631 && (new_number_colors == number_colors))
632 {
633 /*
634 No duplicate or unused entries, and transparency-swap not needed.
635 */
636 marker=(unsigned char *) RelinquishMagickMemory(marker);
637 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
638 return(MagickTrue);
639 }
640
641 remap_needed=MagickFalse;
642 if ((long) top_used >= new_number_colors)
643 remap_needed=MagickTrue;
644
645 /*
646 Compress colormap.
647 */
648
649 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
650 sizeof(*colormap));
651 if (colormap == (PixelPacket *) NULL)
652 {
653 marker=(unsigned char *) RelinquishMagickMemory(marker);
654 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
655 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
656 image->filename);
657 }
658 /*
659 Eliminate unused colormap entries.
660 */
661 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
662 sizeof(*map));
663 if (map == (IndexPacket *) NULL)
664 {
665 marker=(unsigned char *) RelinquishMagickMemory(marker);
666 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
667 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
668 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
669 image->filename);
670 }
671 k=0;
672 for (i=0; i < number_colors; i++)
673 {
674 map[i]=(IndexPacket) k;
675 if (marker[i])
676 {
677 for (j=i+1; j < number_colors; j++)
678 {
679 if ((opacity[i] == opacity[j]) &&
680 (IsColorEqual(image->colormap+i,image->colormap+j)))
681 {
682 map[j]=(IndexPacket) k;
683 marker[j]=MagickFalse;
684 }
685 }
686 k++;
687 }
688 }
689 j=0;
690 for (i=0; i < number_colors; i++)
691 {
692 if (marker[i])
693 {
694 colormap[j]=image->colormap[i];
695 j++;
696 }
697 }
698 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
699 {
700 /*
701 Move the first transparent color to palette entry 0.
702 */
703 for (i=1; i < number_colors; i++)
704 {
705 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
706 {
707 PixelPacket
708 temp_colormap;
709
710 temp_colormap=colormap[0];
711 colormap[0]=colormap[(int) map[i]];
712 colormap[(long) map[i]]=temp_colormap;
713 for (j=0; j < number_colors; j++)
714 {
715 if (map[j] == 0)
716 map[j]=map[i];
717 else if (map[j] == map[i])
718 map[j]=0;
719 }
720 remap_needed=MagickTrue;
721 break;
722 }
723 }
724 }
725
726 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
727 marker=(unsigned char *) RelinquishMagickMemory(marker);
728
729 if (remap_needed)
730 {
731 ExceptionInfo
732 *exception;
733
734 register IndexPacket
735 *pixels;
736
737 register PixelPacket
738 *q;
739
740 /*
741 Remap pixels.
742 */
743 exception=(&image->exception);
744 for (y=0; y < (long) image->rows; y++)
745 {
746 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
747 if (q == (PixelPacket *) NULL)
748 break;
749 pixels=GetAuthenticIndexQueue(image);
750 for (x=0; x < (long) image->columns; x++)
751 {
752 j=(int) pixels[x];
753 pixels[x]=map[j];
754 }
755 if (SyncAuthenticPixels(image,exception) == MagickFalse)
756 break;
757 }
758 for (i=0; i < new_number_colors; i++)
759 image->colormap[i]=colormap[i];
760 }
761 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
762 image->colors=(unsigned long) new_number_colors;
763 map=(IndexPacket *) RelinquishMagickMemory(map);
764 return(MagickTrue);
765}
766#endif
767
768/*
769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770% %
771% %
772% %
773% I m a g e I s G r a y %
774% %
775% %
776% %
777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778% %
779% Like IsGrayImage except does not change DirectClass to PseudoClass %
780% %
781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782*/
783static MagickBooleanType ImageIsGray(Image *image)
784{
785 register const PixelPacket
786 *p;
787
788 register long
789 i,
790 x,
791 y;
792
793 assert(image != (Image *) NULL);
794 assert(image->signature == MagickSignature);
795 if (image->debug != MagickFalse)
796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
797
798 if (image->storage_class == PseudoClass)
799 {
800 for (i=0; i < (long) image->colors; i++)
801 if (IsGray(image->colormap+i) == MagickFalse)
802 return(MagickFalse);
803 return(MagickTrue);
804 }
805 for (y=0; y < (long) image->rows; y++)
806 {
807 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
808 if (p == (const PixelPacket *) NULL)
809 return(MagickFalse);
810 for (x=(long) image->columns-1; x >= 0; x--)
811 {
812 if (IsGray(p) == MagickFalse)
813 return(MagickFalse);
814 p++;
815 }
816 }
817 return(MagickTrue);
818}
819
820/*
821%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822% %
823% %
824% %
825% I m a g e I s M o n o c h r o m e %
826% %
827% %
828% %
829%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
830% %
831% Like IsMonochromeImage except does not change DirectClass to PseudoClass %
832% and is more accurate. %
833% %
834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835*/
836static MagickBooleanType ImageIsMonochrome(Image *image)
837{
838 register const PixelPacket
839 *p;
840
841 register long
842 i,
843 x,
844 y;
845
846 assert(image != (Image *) NULL);
847 assert(image->signature == MagickSignature);
848 if (image->debug != MagickFalse)
849 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
850 if (image->storage_class == PseudoClass)
851 {
852 for (i=0; i < (long) image->colors; i++)
853 {
854 if ((IsGray(image->colormap+i) == MagickFalse) ||
855 ((image->colormap[i].red != 0) &&
856 (image->colormap[i].red != (Quantum) QuantumRange)))
857 return(MagickFalse);
858 }
859 return(MagickTrue);
860 }
861 for (y=0; y < (long) image->rows; y++)
862 {
863 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
864 if (p == (const PixelPacket *) NULL)
865 return(MagickFalse);
866 for (x=(long) image->columns-1; x >= 0; x--)
867 {
868 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
869 return(MagickFalse);
870 if (IsGray(p) == MagickFalse)
871 return(MagickFalse);
872 if ((p->opacity != OpaqueOpacity) &&
873 (p->opacity != (Quantum) TransparentOpacity))
874 return(MagickFalse);
875 p++;
876 }
877 }
878 return(MagickTrue);
879}
880#endif /* PNG_LIBPNG_VER > 95 */
881#endif /* MAGICKCORE_PNG_DELEGATE */
882
883/*
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885% %
886% %
887% %
888% I s M N G %
889% %
890% %
891% %
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893%
894% IsMNG() returns MagickTrue if the image format type, identified by the
895% magick string, is MNG.
896%
897% The format of the IsMNG method is:
898%
899% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
900%
901% A description of each parameter follows:
902%
903% o magick: compare image format pattern against these bytes.
904%
905% o length: Specifies the length of the magick string.
906%
907%
908*/
909static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
910{
911 if (length < 8)
912 return(MagickFalse);
913 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
914 return(MagickTrue);
915 return(MagickFalse);
916}
917
918/*
919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920% %
921% %
922% %
923% I s J N G %
924% %
925% %
926% %
927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
928%
929% IsJNG() returns MagickTrue if the image format type, identified by the
930% magick string, is JNG.
931%
932% The format of the IsJNG method is:
933%
934% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
935%
936% A description of each parameter follows:
937%
938% o magick: compare image format pattern against these bytes.
939%
940% o length: Specifies the length of the magick string.
941%
942%
943*/
944static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
945{
946 if (length < 8)
947 return(MagickFalse);
948 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
949 return(MagickTrue);
950 return(MagickFalse);
951}
952
953/*
954%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
955% %
956% %
957% %
958% I s P N G %
959% %
960% %
961% %
962%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
963%
964% IsPNG() returns MagickTrue if the image format type, identified by the
965% magick string, is PNG.
966%
967% The format of the IsPNG method is:
968%
969% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
970%
971% A description of each parameter follows:
972%
973% o magick: compare image format pattern against these bytes.
974%
975% o length: Specifies the length of the magick string.
976%
977*/
978static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
979{
980 if (length < 8)
981 return(MagickFalse);
982 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
983 return(MagickTrue);
984 return(MagickFalse);
985}
986
987#if defined(MAGICKCORE_PNG_DELEGATE)
988#if defined(__cplusplus) || defined(c_plusplus)
989extern "C" {
990#endif
991
992#if (PNG_LIBPNG_VER > 95)
993static size_t WriteBlobMSBULong(Image *image,const unsigned long value)
994{
995 unsigned char
996 buffer[4];
997
998 assert(image != (Image *) NULL);
999 assert(image->signature == MagickSignature);
1000 buffer[0]=(unsigned char) (value >> 24);
1001 buffer[1]=(unsigned char) (value >> 16);
1002 buffer[2]=(unsigned char) (value >> 8);
1003 buffer[3]=(unsigned char) value;
1004 return((size_t) WriteBlob(image,4,buffer));
1005}
1006
1007static void PNGLong(png_bytep p,png_uint_32 value)
1008{
1009 *p++=(png_byte) ((value >> 24) & 0xff);
1010 *p++=(png_byte) ((value >> 16) & 0xff);
1011 *p++=(png_byte) ((value >> 8) & 0xff);
1012 *p++=(png_byte) (value & 0xff);
1013}
1014
1015static void PNGsLong(png_bytep p,png_int_32 value)
1016{
1017 *p++=(png_byte) ((value >> 24) & 0xff);
1018 *p++=(png_byte) ((value >> 16) & 0xff);
1019 *p++=(png_byte) ((value >> 8) & 0xff);
1020 *p++=(png_byte) (value & 0xff);
1021}
1022
1023static void PNGShort(png_bytep p,png_uint_16 value)
1024{
1025 *p++=(png_byte) ((value >> 8) & 0xff);
1026 *p++=(png_byte) (value & 0xff);
1027}
1028
1029static void PNGType(png_bytep p,png_bytep type)
1030{
1031 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1032}
1033
1034static void LogPNGChunk(int logging, png_bytep type, size_t length)
1035{
1036 if (logging != MagickFalse)
1037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1038 " Writing %c%c%c%c chunk, length: %lu",
1039 type[0],type[1],type[2],type[3],(unsigned long) length);
1040}
1041#endif /* PNG_LIBPNG_VER > 95 */
1042
1043#if defined(__cplusplus) || defined(c_plusplus)
1044}
1045#endif
1046
1047#if PNG_LIBPNG_VER > 95
1048/*
1049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1050% %
1051% %
1052% %
1053% R e a d P N G I m a g e %
1054% %
1055% %
1056% %
1057%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058%
1059% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1060% Multiple-image Network Graphics (MNG) image file and returns it. It
1061% allocates the memory necessary for the new Image structure and returns a
1062% pointer to the new image or set of images.
1063%
1064% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1065%
1066% The format of the ReadPNGImage method is:
1067%
1068% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1069%
1070% A description of each parameter follows:
1071%
1072% o image_info: the image info.
1073%
1074% o exception: return any errors or warnings in this structure.
1075%
1076% To do, more or less in chronological order (as of version 5.5.2,
1077% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1078%
1079% Get 16-bit cheap transparency working.
1080%
1081% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1082%
1083% Preserve all unknown and not-yet-handled known chunks found in input
1084% PNG file and copy them into output PNG files according to the PNG
1085% copying rules.
1086%
1087% (At this point, PNG encoding should be in full MNG compliance)
1088%
1089% Provide options for choice of background to use when the MNG BACK
1090% chunk is not present or is not mandatory (i.e., leave transparent,
1091% user specified, MNG BACK, PNG bKGD)
1092%
1093% Implement LOOP/ENDL [done, but could do discretionary loops more
1094% efficiently by linking in the duplicate frames.].
1095%
1096% Decode and act on the MHDR simplicity profile (offer option to reject
1097% files or attempt to process them anyway when the profile isn't LC or VLC).
1098%
1099% Upgrade to full MNG without Delta-PNG.
1100%
1101% o BACK [done a while ago except for background image ID]
1102% o MOVE [done 15 May 1999]
1103% o CLIP [done 15 May 1999]
1104% o DISC [done 19 May 1999]
1105% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1106% o SEEK [partially done 19 May 1999 (discard function only)]
1107% o SHOW
1108% o PAST
1109% o BASI
1110% o MNG-level tEXt/iTXt/zTXt
1111% o pHYg
1112% o pHYs
1113% o sBIT
1114% o bKGD
1115% o iTXt (wait for libpng implementation).
1116%
1117% Use the scene signature to discover when an identical scene is
1118% being reused, and just point to the original image->exception instead
1119% of storing another set of pixels. This not specific to MNG
1120% but could be applied generally.
1121%
1122% Upgrade to full MNG with Delta-PNG.
1123%
1124% JNG tEXt/iTXt/zTXt
1125%
1126% We will not attempt to read files containing the CgBI chunk.
1127% They are really Xcode files meant for display on the iPhone.
1128% These are not valid PNG files and it is impossible to recover
1129% the orginal PNG from files that have been converted to Xcode-PNG,
1130% since irretrievable loss of color data has occurred due to the
1131% use of premultiplied alpha.
1132*/
1133
1134#if defined(__cplusplus) || defined(c_plusplus)
1135extern "C" {
1136#endif
1137
1138/*
1139 This the function that does the actual reading of data. It is
1140 the same as the one supplied in libpng, except that it receives the
1141 datastream from the ReadBlob() function instead of standard input.
1142*/
1143static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1144{
1145 Image
1146 *image;
1147
1148 image=(Image *) png_get_io_ptr(png_ptr);
1149 if (length)
1150 {
1151 png_size_t
1152 check;
1153
1154 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1155 if (check != length)
1156 {
1157 char
1158 msg[MaxTextExtent];
1159
1160 (void) FormatMagickString(msg,MaxTextExtent,
1161 "Expected %lu bytes; found %lu bytes",(unsigned long) length,
1162 (unsigned long) check);
1163 png_warning(png_ptr,msg);
1164 png_error(png_ptr,"Read Exception");
1165 }
1166 }
1167}
1168
1169#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1170 !defined(PNG_MNG_FEATURES_SUPPORTED)
1171/* We use mng_get_data() instead of png_get_data() if we have a libpng
1172 * older than libpng-1.0.3a, which was the first to allow the empty
1173 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1174 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1175 * encountered after an empty PLTE, so we have to look ahead for bKGD
1176 * chunks and remove them from the datastream that is passed to libpng,
1177 * and store their contents for later use.
1178 */
1179static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1180{
1181 MngInfo
1182 *mng_info;
1183
1184 Image
1185 *image;
1186
1187 png_size_t
1188 check;
1189
1190 register long
1191 i;
1192
1193 i=0;
1194 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1195 image=(Image *) mng_info->image;
1196 while (mng_info->bytes_in_read_buffer && length)
1197 {
1198 data[i]=mng_info->read_buffer[i];
1199 mng_info->bytes_in_read_buffer--;
1200 length--;
1201 i++;
1202 }
1203 if (length)
1204 {
1205 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1206 if (check != length)
1207 png_error(png_ptr,"Read Exception");
1208 if (length == 4)
1209 {
1210 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1211 (data[3] == 0))
1212 {
1213 check=(png_size_t) ReadBlob(image,(size_t) length,
1214 (char *) mng_info->read_buffer);
1215 mng_info->read_buffer[4]=0;
1216 mng_info->bytes_in_read_buffer=4;
1217 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1218 mng_info->found_empty_plte=MagickTrue;
1219 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1220 {
1221 mng_info->found_empty_plte=MagickFalse;
1222 mng_info->have_saved_bkgd_index=MagickFalse;
1223 }
1224 }
1225 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1226 (data[3] == 1))
1227 {
1228 check=(png_size_t) ReadBlob(image,(size_t) length,
1229 (char *) mng_info->read_buffer);
1230 mng_info->read_buffer[4]=0;
1231 mng_info->bytes_in_read_buffer=4;
1232 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1233 if (mng_info->found_empty_plte)
1234 {
1235 /*
1236 Skip the bKGD data byte and CRC.
1237 */
1238 check=(png_size_t)
1239 ReadBlob(image,5,(char *) mng_info->read_buffer);
1240 check=(png_size_t) ReadBlob(image,(size_t) length,
1241 (char *) mng_info->read_buffer);
1242 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1243 mng_info->have_saved_bkgd_index=MagickTrue;
1244 mng_info->bytes_in_read_buffer=0;
1245 }
1246 }
1247 }
1248 }
1249}
1250#endif
1251
1252static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1253{
1254 Image
1255 *image;
1256
1257 image=(Image *) png_get_io_ptr(png_ptr);
1258 if (length)
1259 {
1260 png_size_t
1261 check;
1262
1263 check=(png_size_t) WriteBlob(image,(unsigned long) length,data);
1264 if (check != length)
1265 png_error(png_ptr,"WriteBlob Failed");
1266 }
1267}
1268
1269static void png_flush_data(png_structp png_ptr)
1270{
1271 (void) png_ptr;
1272}
1273
1274#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1275static int PalettesAreEqual(Image *a,Image *b)
1276{
1277 long
1278 i;
1279
1280 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1281 return((int) MagickFalse);
1282 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1283 return((int) MagickFalse);
1284 if (a->colors != b->colors)
1285 return((int) MagickFalse);
1286 for (i=0; i < (long) a->colors; i++)
1287 {
1288 if ((a->colormap[i].red != b->colormap[i].red) ||
1289 (a->colormap[i].green != b->colormap[i].green) ||
1290 (a->colormap[i].blue != b->colormap[i].blue))
1291 return((int) MagickFalse);
1292 }
1293 return((int) MagickTrue);
1294}
1295#endif
1296
1297static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1298{
1299 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1300 mng_info->exists[i] && !mng_info->frozen[i])
1301 {
1302#ifdef MNG_OBJECT_BUFFERS
1303 if (mng_info->ob[i] != (MngBuffer *) NULL)
1304 {
1305 if (mng_info->ob[i]->reference_count > 0)
1306 mng_info->ob[i]->reference_count--;
1307 if (mng_info->ob[i]->reference_count == 0)
1308 {
1309 if (mng_info->ob[i]->image != (Image *) NULL)
1310 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1311 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1312 }
1313 }
1314 mng_info->ob[i]=(MngBuffer *) NULL;
1315#endif
1316 mng_info->exists[i]=MagickFalse;
1317 mng_info->invisible[i]=MagickFalse;
1318 mng_info->viewable[i]=MagickFalse;
1319 mng_info->frozen[i]=MagickFalse;
1320 mng_info->x_off[i]=0;
1321 mng_info->y_off[i]=0;
1322 mng_info->object_clip[i].left=0;
1323 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
1324 mng_info->object_clip[i].top=0;
1325 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
1326 }
1327}
1328
1329static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1330{
1331 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1332 {
1333 register long
1334 i;
1335
1336 for (i=1; i < MNG_MAX_OBJECTS; i++)
1337 MngInfoDiscardObject(mng_info,i);
1338 if (mng_info->global_plte != (png_colorp) NULL)
1339 mng_info->global_plte=(png_colorp)
1340 RelinquishMagickMemory(mng_info->global_plte);
1341 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1342 *have_mng_structure=MagickFalse;
1343 }
1344}
1345
1346static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1347{
1348 MngBox
1349 box;
1350
1351 box=box1;
1352 if (box.left < box2.left)
1353 box.left=box2.left;
1354 if (box.top < box2.top)
1355 box.top=box2.top;
1356 if (box.right > box2.right)
1357 box.right=box2.right;
1358 if (box.bottom > box2.bottom)
1359 box.bottom=box2.bottom;
1360 return box;
1361}
1362
1363static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1364{
1365 MngBox
1366 box;
1367
1368 /*
1369 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1370 */
1371 box.left=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1372 box.right=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1373 box.top=(long) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1374 box.bottom=(long) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1375 if (delta_type != 0)
1376 {
1377 box.left+=previous_box.left;
1378 box.right+=previous_box.right;
1379 box.top+=previous_box.top;
1380 box.bottom+=previous_box.bottom;
1381 }
1382 return(box);
1383}
1384
1385static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1386 unsigned char *p)
1387{
1388 MngPair
1389 pair;
1390 /*
1391 Read two longs from CLON, MOVE or PAST chunk
1392 */
1393 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1394 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1395 if (delta_type != 0)
1396 {
1397 pair.a+=previous_pair.a;
1398 pair.b+=previous_pair.b;
1399 }
1400 return(pair);
1401}
1402
1403static long mng_get_long(unsigned char *p)
1404{
1405 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1406}
1407
1408static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1409{
1410 Image
1411 *image;
1412
1413 image=(Image *) png_get_error_ptr(ping);
1414 if (image->debug != MagickFalse)
1415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1416 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1417 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1418 message,"`%s'",image->filename);
1419 longjmp(ping->jmpbuf,1);
1420}
1421
1422static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1423{
1424 Image
1425 *image;
1426
1427 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1428 png_error(ping, message);
1429 image=(Image *) png_get_error_ptr(ping);
1430 if (image->debug != MagickFalse)
1431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1432 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,
1433 message);
1434 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1435 message,"`%s'",image->filename);
1436}
1437
1438#ifdef PNG_USER_MEM_SUPPORTED
1439static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1440{
1441#if (PNG_LIBPNG_VER < 10011)
1442 png_voidp
1443 ret;
1444
1445 png_ptr=png_ptr;
1446 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1447 if (ret == NULL)
1448 png_error("Insufficient memory.");
1449 return(ret);
1450#else
1451 png_ptr=png_ptr;
1452 return((png_voidp) AcquireMagickMemory((size_t) size));
1453#endif
1454}
1455
1456/*
1457 Free a pointer. It is removed from the list at the same time.
1458*/
1459static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1460{
1461 png_ptr=png_ptr;
1462 ptr=RelinquishMagickMemory(ptr);
1463 return((png_free_ptr) NULL);
1464}
1465#endif
1466
1467#if defined(__cplusplus) || defined(c_plusplus)
1468}
1469#endif
1470
1471static int
1472png_read_raw_profile(Image *image, const ImageInfo *image_info,
1473 png_textp text,int ii)
1474{
1475 register long
1476 i;
1477
1478 register unsigned char
1479 *dp;
1480
1481 register png_charp
1482 sp;
1483
1484 png_uint_32
1485 length,
1486 nibbles;
1487
1488 StringInfo
1489 *profile;
1490
1491 unsigned char
1492 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1493 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1494 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1495 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1496 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1497 13,14,15};
1498
1499 sp=text[ii].text+1;
1500 /* look for newline */
1501 while (*sp != '\n')
1502 sp++;
1503 /* look for length */
1504 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1505 sp++;
1506 length=(png_uint_32) atol(sp);
1507 while (*sp != ' ' && *sp != '\n')
1508 sp++;
1509 /* allocate space */
1510 if (length == 0)
1511 {
1512 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1513 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1514 return(MagickFalse);
1515 }
1516 profile=AcquireStringInfo(length);
1517 if (profile == (StringInfo *) NULL)
1518 {
1519 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1520 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1521 "unable to copy profile");
1522 return(MagickFalse);
1523 }
1524 /* copy profile, skipping white space and column 1 "=" signs */
1525 dp=GetStringInfoDatum(profile);
1526 nibbles=length*2;
1527 for (i=0; i < (long) nibbles; i++)
1528 {
1529 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1530 {
1531 if (*sp == '\0')
1532 {
1533 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1534 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1535 profile=DestroyStringInfo(profile);
1536 return(MagickFalse);
1537 }
1538 sp++;
1539 }
1540 if (i%2 == 0)
1541 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1542 else
1543 (*dp++)+=unhex[(int) *sp++];
1544 }
1545 /*
1546 We have already read "Raw profile type.
1547 */
1548 (void) SetImageProfile(image,&text[ii].key[17],profile);
1549 profile=DestroyStringInfo(profile);
1550 if (image_info->verbose)
1551 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1552 return MagickTrue;
1553}
1554
1555#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1556static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1557{
1558 Image
1559 *image;
1560
1561
1562 /* The unknown chunk structure contains the chunk data:
1563 png_byte name[5];
1564 png_byte *data;
1565 png_size_t size;
1566
1567 Note that libpng has already taken care of the CRC handling.
1568 */
1569
1570
1571 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1572 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1573 return(0); /* Did not recognize */
1574
1575 /* recognized vpAg */
1576
1577 if (chunk->size != 9)
1578 return(-1); /* Error return */
1579
1580 if (chunk->data[8] != 0)
1581 return(0); /* ImageMagick requires pixel units */
1582
1583 image=(Image *) png_get_user_chunk_ptr(ping);
1584
1585 image->page.width=(unsigned long) ((chunk->data[0] << 24) |
1586 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1587 image->page.height=(unsigned long) ((chunk->data[4] << 24) |
1588 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1589
1590 /* Return one of the following: */
1591 /* return(-n); chunk had an error */
1592 /* return(0); did not recognize */
1593 /* return(n); success */
1594
1595 return(1);
1596
1597}
1598#endif
1599
1600/*
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602% %
1603% %
1604% %
1605% R e a d O n e P N G I m a g e %
1606% %
1607% %
1608% %
1609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610%
1611% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1612% (minus the 8-byte signature) and returns it. It allocates the memory
1613% necessary for the new Image structure and returns a pointer to the new
1614% image.
1615%
1616% The format of the ReadOnePNGImage method is:
1617%
1618% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1619% ExceptionInfo *exception)
1620%
1621% A description of each parameter follows:
1622%
1623% o mng_info: Specifies a pointer to a MngInfo structure.
1624%
1625% o image_info: the image info.
1626%
1627% o exception: return any errors or warnings in this structure.
1628%
1629*/
1630static Image *ReadOnePNGImage(MngInfo *mng_info,
1631 const ImageInfo *image_info, ExceptionInfo *exception)
1632{
1633 /* Read one PNG image */
1634
1635 Image
1636 *image;
1637
1638 int
1639 logging,
1640 num_text,
1641 num_passes,
1642 pass;
1643
1644 MagickBooleanType
1645 status;
1646
1647 PixelPacket
1648 transparent_color;
1649
1650 png_info
1651 *end_info,
1652 *ping_info;
1653
1654 png_struct
1655 *ping;
1656
1657 png_textp
1658 text;
1659
1660 QuantumInfo
1661 *quantum_info;
1662
1663 unsigned char
1664 *png_pixels;
1665
1666 long
1667 y;
1668
1669 register unsigned char
1670 *p;
1671
1672 register IndexPacket
1673 *indices;
1674
1675 register long
1676 i,
1677 x;
1678
1679 register PixelPacket
1680 *q;
1681
1682 size_t
1683 length;
1684
1685 unsigned long
1686 row_offset;
1687
1688#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1689 png_byte unused_chunks[]=
1690 {
1691 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1692 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1693 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1694 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1695 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1696 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1697 };
1698#endif
1699
1700 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1701 " enter ReadOnePNGImage()");
1702
1703#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1704 AcquireSemaphoreInfo(&png_semaphore);
1705#endif
1706
1707#if (PNG_LIBPNG_VER < 10007)
1708 if (image_info->verbose)
1709 printf("Your PNG library (libpng-%s) is rather old.\n",
1710 PNG_LIBPNG_VER_STRING);
1711#endif
1712
1713 quantum_info = NULL;
1714 image=mng_info->image;
1715
1716 /*
1717 Allocate the PNG structures
1718 */
1719#ifdef PNG_USER_MEM_SUPPORTED
1720 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1721 PNGErrorHandler,PNGWarningHandler, NULL,
1722 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1723#else
1724 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1725 PNGErrorHandler,PNGWarningHandler);
1726#endif
1727 if (ping == (png_struct *) NULL)
1728 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1729 ping_info=png_create_info_struct(ping);
1730 if (ping_info == (png_info *) NULL)
1731 {
1732 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1733 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1734 }
1735 end_info=png_create_info_struct(ping);
1736 if (end_info == (png_info *) NULL)
1737 {
1738 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1739 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1740 }
1741 png_pixels=(unsigned char *) NULL;
1742 if (setjmp(ping->jmpbuf))
1743 {
1744 /*
1745 PNG image is corrupt.
1746 */
1747 png_destroy_read_struct(&ping,&ping_info,&end_info);
1748#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1749 RelinquishSemaphoreInfo(png_semaphore);
1750#endif
1751 if (logging != MagickFalse)
1752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753 " exit ReadOnePNGImage() with error.");
1754 if (image != (Image *) NULL)
1755 {
1756 if (image->exception.severity > exception->severity)
1757 InheritException(exception,&image->exception);
1758 image->columns=0;
1759 }
1760 return(GetFirstImageInList(image));
1761 }
1762 /*
1763 Prepare PNG for reading.
1764 */
1765 mng_info->image_found++;
1766 png_set_sig_bytes(ping,8);
1767 if (LocaleCompare(image_info->magick,"MNG") == 0)
1768 {
1769#if defined(PNG_MNG_FEATURES_SUPPORTED)
1770 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1771 png_set_read_fn(ping,image,png_get_data);
1772#else
1773#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1774 png_permit_empty_plte(ping,MagickTrue);
1775 png_set_read_fn(ping,image,png_get_data);
1776#else
1777 mng_info->image=image;
1778 mng_info->bytes_in_read_buffer=0;
1779 mng_info->found_empty_plte=MagickFalse;
1780 mng_info->have_saved_bkgd_index=MagickFalse;
1781 png_set_read_fn(ping,mng_info,mng_get_data);
1782#endif
1783#endif
1784 }
1785 else
1786 png_set_read_fn(ping,image,png_get_data);
1787
1788#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1789 /* Ignore unused chunks and all unknown chunks except for vpAg */
1790 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1791 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1792 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1793 (int)sizeof(unused_chunks)/5);
1794 /* Callback for other unknown chunks */
1795 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1796#endif
1797
1798#if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1799 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1800 /* Disable thread-unsafe features of pnggccrd */
1801 if (png_access_version_number() >= 10200)
1802 {
1803 png_uint_32 mmx_disable_mask=0;
1804 png_uint_32 asm_flags;
1805
1806 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1807 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1808 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1809 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1810 asm_flags=png_get_asm_flags(ping);
1811 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1812 }
1813#endif
1814
1815 png_read_info(ping,ping_info);
1816 if (ping_info->bit_depth < 8)
1817 {
1818 if (((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE))
1819 png_set_packing(ping);
1820 }
1821 image->depth=ping_info->bit_depth;
1822 image->depth=GetImageQuantumDepth(image,MagickFalse);
1823 image->interlace=ping_info->interlace_type != 0 ? PNGInterlace : NoInterlace;
1824 if (logging != MagickFalse)
1825 {
1826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1827 " PNG width: %lu, height: %lu",
1828 ping_info->width, ping_info->height);
1829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1830 " PNG color_type: %d, bit_depth: %d",
1831 ping_info->color_type, ping_info->bit_depth);
1832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1833 " PNG compression_method: %d",
1834 ping_info->compression_type);
1835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1836 " PNG interlace_method: %d, filter_method: %d",
1837 ping_info->interlace_type,ping_info->filter_type);
1838 }
1839
1840#if (PNG_LIBPNG_VER > 10008) && defined(PNG_READ_iCCP_SUPPORTED)
1841 if (ping_info->valid & PNG_INFO_iCCP)
1842 {
1843 int
1844 compression;
1845
1846 png_charp
1847 info,
1848 name;
1849
1850 png_uint_32
1851 profile_length;
1852
1853 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1854 &profile_length);
1855 if (profile_length != 0)
1856 {
1857 StringInfo
1858 *profile;
1859
1860 if (logging != MagickFalse)
1861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " Reading PNG iCCP chunk.");
1863 profile=AcquireStringInfo(profile_length);
1864 SetStringInfoDatum(profile,(const unsigned char *) info);
1865 (void) SetImageProfile(image,"icc",profile);
1866 profile=DestroyStringInfo(profile);
1867 }
1868 }
1869#endif
1870#if defined(PNG_READ_sRGB_SUPPORTED)
1871 {
1872 int
1873 intent;
1874
1875 if (mng_info->have_global_srgb)
1876 image->rendering_intent=(RenderingIntent)
1877 (mng_info->global_srgb_intent+1);
1878 if (png_get_sRGB(ping,ping_info,&intent))
1879 {
1880 image->rendering_intent=(RenderingIntent) (intent+1);
1881 if (logging != MagickFalse)
1882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1883 " Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1884 }
1885 }
1886#endif
1887 {
1888 double
1889 file_gamma;
1890
1891 if (mng_info->have_global_gama)
1892 image->gamma=mng_info->global_gamma;
1893 if (png_get_gAMA(ping,ping_info,&file_gamma))
1894 {
1895 image->gamma=(float) file_gamma;
1896 if (logging != MagickFalse)
1897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1898 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1899 }
1900 }
1901 if (mng_info->have_global_chrm != MagickFalse)
1902 image->chromaticity=mng_info->global_chrm;
1903 if (ping_info->valid & PNG_INFO_cHRM)
1904 {
1905 (void) png_get_cHRM(ping,ping_info,
1906 &image->chromaticity.white_point.x,
1907 &image->chromaticity.white_point.y,
1908 &image->chromaticity.red_primary.x,
1909 &image->chromaticity.red_primary.y,
1910 &image->chromaticity.green_primary.x,
1911 &image->chromaticity.green_primary.y,
1912 &image->chromaticity.blue_primary.x,
1913 &image->chromaticity.blue_primary.y);
1914 if (logging != MagickFalse)
1915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1916 " Reading PNG cHRM chunk.");
1917 }
1918 if (image->rendering_intent)
1919 {
1920 image->gamma=0.45455f;
1921 image->chromaticity.red_primary.x=0.6400f;
1922 image->chromaticity.red_primary.y=0.3300f;
1923 image->chromaticity.green_primary.x=0.3000f;
1924 image->chromaticity.green_primary.y=0.6000f;
1925 image->chromaticity.blue_primary.x=0.1500f;
1926 image->chromaticity.blue_primary.y=0.0600f;
1927 image->chromaticity.white_point.x=0.3127f;
1928 image->chromaticity.white_point.y=0.3290f;
1929 }
1930 if ((mng_info->have_global_gama != MagickFalse) || image->rendering_intent)
1931 ping_info->valid|=PNG_INFO_gAMA;
1932 if ((mng_info->have_global_chrm != MagickFalse) || image->rendering_intent)
1933 ping_info->valid|=PNG_INFO_cHRM;
1934#if defined(PNG_oFFs_SUPPORTED)
1935 if (ping_info->valid & PNG_INFO_oFFs)
1936 {
1937 image->page.x=png_get_x_offset_pixels(ping, ping_info);
1938 image->page.y=png_get_y_offset_pixels(ping, ping_info);
1939 if (logging != MagickFalse)
1940 if (image->page.x || image->page.y)
1941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1942 " Reading PNG oFFs chunk: x: %ld, y: %ld.",image->page.x,
1943 image->page.y);
1944 }
1945#endif
1946#if defined(PNG_pHYs_SUPPORTED)
1947 if (ping_info->valid & PNG_INFO_pHYs)
1948 {
1949 int
1950 unit_type;
1951
1952 png_uint_32
1953 x_resolution,
1954 y_resolution;
1955
1956 /*
1957 Set image resolution.
1958 */
1959 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1960 &unit_type);
1961 image->x_resolution=(float) x_resolution;
1962 image->y_resolution=(float) y_resolution;
1963 if (unit_type == PNG_RESOLUTION_METER)
1964 {
1965 image->units=PixelsPerCentimeterResolution;
1966 image->x_resolution=(double) x_resolution/100.0;
1967 image->y_resolution=(double) y_resolution/100.0;
1968 }
1969 if (logging != MagickFalse)
1970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1971 " Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.",
1972 x_resolution, y_resolution, unit_type);
1973 }
1974 else
1975 {
1976 if (mng_info->have_global_phys)
1977 {
1978 image->x_resolution=(float) mng_info->global_x_pixels_per_unit;
1979 image->y_resolution=(float) mng_info->global_y_pixels_per_unit;
1980 if (mng_info->global_phys_unit_type == PNG_RESOLUTION_METER)
1981 {
1982 image->units=PixelsPerCentimeterResolution;
1983 image->x_resolution=(double)
1984 mng_info->global_x_pixels_per_unit/100.0;
1985 image->y_resolution=(double)
1986 mng_info->global_y_pixels_per_unit/100.0;
1987 }
1988 ping_info->valid|=PNG_INFO_pHYs;
1989 }
1990 }
1991#endif
1992 if (ping_info->valid & PNG_INFO_PLTE)
1993 {
1994 int
1995 number_colors;
1996
1997 png_colorp
1998 palette;
1999
2000 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2001 if ((number_colors == 0) &&
2002 ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE))
2003 {
2004 if (mng_info->global_plte_length)
2005 {
2006 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2007 (int) mng_info->global_plte_length);
2008 if ((ping_info->valid & PNG_INFO_tRNS) == 0)
2009 if (mng_info->global_trns_length)
2010 {
2011 if (mng_info->global_trns_length >
2012 mng_info->global_plte_length)
2013 (void) ThrowMagickException(&image->exception,
2014 GetMagickModule(),CoderError,
2015 "global tRNS has more entries than global PLTE",
2016 "`%s'",image_info->filename);
2017 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2018 (int) mng_info->global_trns_length,NULL);
2019 }
2020#if defined(PNG_READ_bKGD_SUPPORTED)
2021 if (
2022#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2023 mng_info->have_saved_bkgd_index ||
2024#endif
2025 ping_info->valid & PNG_INFO_bKGD)
2026 {
2027 png_color_16
2028 background;
2029
2030#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2031 if (mng_info->have_saved_bkgd_index)
2032 background.index=mng_info->saved_bkgd_index;
2033 else
2034#endif
2035 background.index=ping_info->background.index;
2036 background.red=(png_uint_16)
2037 mng_info->global_plte[background.index].red;
2038 background.green=(png_uint_16)
2039 mng_info->global_plte[background.index].green;
2040 background.blue=(png_uint_16)
2041 mng_info->global_plte[background.index].blue;
2042 png_set_bKGD(ping,ping_info,&background);
2043 }
2044#endif
2045 }
2046 else
2047 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2048 CoderError,"No global PLTE in file","`%s'",
2049 image_info->filename);
2050 }
2051 }
2052
2053#if defined(PNG_READ_bKGD_SUPPORTED)
2054 if (mng_info->have_global_bkgd && !(ping_info->valid & PNG_INFO_bKGD))
2055 image->background_color=mng_info->mng_global_bkgd;
2056 if (ping_info->valid & PNG_INFO_bKGD)
2057 {
2058 /*
2059 Set image background color.
2060 */
2061 if (logging != MagickFalse)
2062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2063 " Reading PNG bKGD chunk.");
2064 if (ping_info->bit_depth <= MAGICKCORE_QUANTUM_DEPTH)
2065 {
2066 image->background_color.red=ping_info->background.red;
2067 image->background_color.green=ping_info->background.green;
2068 image->background_color.blue=ping_info->background.blue;
2069 }
2070 else
2071 {
2072 image->background_color.red=
2073 ScaleShortToQuantum(ping_info->background.red);
2074 image->background_color.green=
2075 ScaleShortToQuantum(ping_info->background.green);
2076 image->background_color.blue=
2077 ScaleShortToQuantum(ping_info->background.blue);
2078 }
2079 }
2080#endif
2081 transparent_color.red=0;
2082 transparent_color.green=0;
2083 transparent_color.blue=0;
2084 transparent_color.opacity=0;
2085 if (ping_info->valid & PNG_INFO_tRNS)
2086 {
2087 /*
2088 Image has a transparent background.
2089 */
2090 int
2091 max_sample;
2092
2093 if (logging != MagickFalse)
2094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " Reading PNG tRNS chunk.");
2096
2097 max_sample = (1 << ping_info->bit_depth) - 1;
2098
2099 if ((ping_info->color_type == PNG_COLOR_TYPE_GRAY &&
2100 (int)ping_info->trans_color.gray > max_sample) ||
2101 (ping_info->color_type == PNG_COLOR_TYPE_RGB &&
2102 ((int)ping_info->trans_color.red > max_sample ||
2103 (int)ping_info->trans_color.green > max_sample ||
2104 (int)ping_info->trans_color.blue > max_sample)))
2105 {
2106 if (logging != MagickFalse)
2107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2108 " Ignoring PNG tRNS chunk with out-of-range sample.");
2109#if (PNG_LIBPNG_VER < 10007)
2110 ping_info->trans_alpha=(unsigned char *) RelinquishMagickMemory(
2111 ping_info->trans_alpha);
2112 ping_info->valid&=(~PNG_INFO_tRNS);
2113#else
2114 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2115#endif
2116 image->matte=MagickFalse;
2117 }
2118 else
2119 {
2120 transparent_color.red= (Quantum)(ping_info->trans_color.red);
2121 transparent_color.green= (Quantum) (ping_info->trans_color.green);
2122 transparent_color.blue= (Quantum) (ping_info->trans_color.blue);
2123 transparent_color.opacity= (Quantum) (ping_info->trans_color.gray);
2124 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2125 {
2126 if (ping_info->bit_depth < 8)
2127 {
2128 transparent_color.opacity=(Quantum) (((
2129 ping_info->trans_color.gray)*255)/max_sample);
2130 }
2131 transparent_color.red=transparent_color.opacity;
2132 transparent_color.green=transparent_color.opacity;
2133 transparent_color.blue=transparent_color.opacity;
2134 }
2135 }
2136 }
2137#if defined(PNG_READ_sBIT_SUPPORTED)
2138 if (mng_info->have_global_sbit)
2139 {
2140 if (!(ping_info->valid & PNG_INFO_sBIT))
2141 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2142 }
2143#endif
2144 num_passes=png_set_interlace_handling(ping);
2145 png_read_update_info(ping,ping_info);
2146 /*
2147 Initialize image structure.
2148 */
2149 mng_info->image_box.left=0;
2150 mng_info->image_box.right=(long) ping_info->width;
2151 mng_info->image_box.top=0;
2152 mng_info->image_box.bottom=(long) ping_info->height;
2153 if (mng_info->mng_type == 0)
2154 {
2155 mng_info->mng_width=ping_info->width;
2156 mng_info->mng_height=ping_info->height;
2157 mng_info->frame=mng_info->image_box;
2158 mng_info->clip=mng_info->image_box;
2159 }
2160 else
2161 {
2162 image->page.y=mng_info->y_off[mng_info->object_id];
2163 }
2164 image->compression=ZipCompression;
2165 image->columns=ping_info->width;
2166 image->rows=ping_info->height;
2167 if (((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
2168 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2169 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY))
2170 {
2171 image->storage_class=PseudoClass;
2172 image->colors=1UL << ping_info->bit_depth;
2173#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2174 if (image->colors > 256)
2175 image->colors=256;
2176#else
2177 if (image->colors > 65536L)
2178 image->colors=65536L;
2179#endif
2180 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2181 {
2182 int
2183 number_colors;
2184
2185 png_colorp
2186 palette;
2187
2188 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2189 image->colors=(unsigned long) number_colors;
2190 if (logging != MagickFalse)
2191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2192 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2193 }
2194 }
2195
2196 if (image->storage_class == PseudoClass)
2197 {
2198 /*
2199 Initialize image colormap.
2200 */
2201 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2202 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2203 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2204 {
2205 int
2206 number_colors;
2207
2208 png_colorp
2209 palette;
2210
2211 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2212 for (i=0; i < (long) image->colors; i++)
2213 {
2214 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2215 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2216 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2217 }
2218 }
2219 else
2220 {
2221 unsigned long
2222 scale;
2223
2224 scale=(QuantumRange/((1UL << ping_info->bit_depth)-1));
2225 if (scale < 1)
2226 scale=1;
2227 for (i=0; i < (long) image->colors; i++)
2228 {
2229 image->colormap[i].red=(Quantum) (i*scale);
2230 image->colormap[i].green=(Quantum) (i*scale);
2231 image->colormap[i].blue=(Quantum) (i*scale);
2232 }
2233 }
2234 }
2235 /*
2236 Read image scanlines.
2237 */
2238 if (image->delay != 0)
2239 mng_info->scenes_found++;
2240 if ((image_info->number_scenes != 0) && (mng_info->scenes_found > (long)
2241 (image_info->first_scene+image_info->number_scenes)))
2242 {
2243 if (logging != MagickFalse)
2244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2245 " Skipping PNG image data for scene %ld",
2246 mng_info->scenes_found-1);
2247 png_destroy_read_struct(&ping,&ping_info,&end_info);
2248#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2249 RelinquishSemaphoreInfo(png_semaphore);
2250#endif
2251 if (logging != MagickFalse)
2252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2253 " exit ReadOnePNGImage().");
2254 return(image);
2255 }
2256 if (logging != MagickFalse)
2257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2258 " Reading PNG IDAT chunk(s)");
2259 if (num_passes > 1)
2260 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2261 ping_info->rowbytes*sizeof(*png_pixels));
2262 else
2263 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_info->rowbytes,
2264 sizeof(*png_pixels));
2265 if (png_pixels == (unsigned char *) NULL)
2266 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2267 if (logging != MagickFalse)
2268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2269 " Converting PNG pixels to pixel packets");
2270 /*
2271 Convert PNG pixels to pixel packets.
2272 */
2273 quantum_info=AcquireQuantumInfo(image_info,image);
2274 if (quantum_info == (QuantumInfo *) NULL)
2275 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2276 if (setjmp(ping->jmpbuf))
2277 {
2278 /*
2279 PNG image is corrupt.
2280 */
2281 png_destroy_read_struct(&ping,&ping_info,&end_info);
2282#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2283 RelinquishSemaphoreInfo(png_semaphore);
2284#endif
2285 if (quantum_info != (QuantumInfo *) NULL)
2286 quantum_info = DestroyQuantumInfo(quantum_info);
2287 if (png_pixels != (unsigned char *) NULL)
2288 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2289 if (logging != MagickFalse)
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 " exit ReadOnePNGImage() with error.");
2292 if (image != (Image *) NULL)
2293 {
2294 if (image->exception.severity > exception->severity)
2295 InheritException(exception,&image->exception);
2296 image->columns=0;
2297 }
2298 return(GetFirstImageInList(image));
2299 }
2300 if (image->storage_class == DirectClass)
2301 for (pass=0; pass < num_passes; pass++)
2302 {
2303 /*
2304 Convert image to DirectClass pixel packets.
2305 */
2306#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2307 int
2308 depth;
2309
2310 depth=(long) ping_info->bit_depth;
2311#endif
2312 image->matte=(((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2313 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2314 (ping_info->valid & PNG_INFO_tRNS)) ? MagickTrue : MagickFalse;
2315
2316 for (y=0; y < (long) image->rows; y++)
2317 {
2318 if (num_passes > 1)
2319 row_offset=ping_info->rowbytes*y;
2320 else
2321 row_offset=0;
2322 png_read_row(ping,png_pixels+row_offset,NULL);
2323 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2324 if (q == (PixelPacket *) NULL)
2325 break;
2326#if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2327 if (depth == 16)
2328 {
2329 register Quantum
2330 *p,
2331 *r;
2332
2333 r=png_pixels+row_offset;
2334 p=r;
2335 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2336 {
2337 for (x=(long) image->columns-1; x >= 0; x--)
2338 {
2339 *r++=*p++;
2340 p++;
2341 if ((ping_info->valid & PNG_INFO_tRNS) &&
2342 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2343 {
2344 /* Cheap transparency */
2345 *r++=TransparentOpacity;
2346 }
2347 else
2348 *r++=OpaqueOpacity;
2349 }
2350 }
2351 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB)
2352 {
2353 if (ping_info->valid & PNG_INFO_tRNS)
2354 for (x=(long) image->columns-1; x >= 0; x--)
2355 {
2356 *r++=*p++;
2357 p++;
2358 *r++=*p++;
2359 p++;
2360 *r++=*p++;
2361 p++;
2362 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2363 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2364 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2365 {
2366 /* Cheap transparency */
2367 *r++=TransparentOpacity;
2368 }
2369 else
2370 *r++=OpaqueOpacity;
2371 }
2372 else
2373 for (x=(long) image->columns-1; x >= 0; x--)
2374 {
2375 *r++=*p++;
2376 p++;
2377 *r++=*p++;
2378 p++;
2379 *r++=*p++;
2380 p++;
2381 *r++=OpaqueOpacity;
2382 }
2383 }
2384 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2385 for (x=(long) (4*image->columns); x != 0; x--)
2386 {
2387 *r++=*p++;
2388 p++;
2389 }
2390 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2391 for (x=(long) (2*image->columns); x != 0; x--)
2392 {
2393 *r++=*p++;
2394 p++;
2395 }
2396 }
2397 if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2398 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2399 GrayQuantum,png_pixels+row_offset);
2400 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY ||
2401 ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2402 {
2403 quantum_info->depth=8;
2404 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2405 GrayAlphaQuantum,png_pixels+row_offset);
2406 }
2407 else if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_RGB)
2408 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2409 RGBQuantum,png_pixels+row_offset);
2410 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB ||
2411 ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2412 {
2413 quantum_info->depth=8;
2414 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2415 RGBAQuantum,png_pixels+row_offset);
2416 }
2417 else if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2418 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2419 IndexQuantum,png_pixels+row_offset);
2420#else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2421 if ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2422 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2423 GrayQuantum,png_pixels+row_offset,exception);
2424 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2425 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2426 GrayAlphaQuantum,png_pixels+row_offset,exception);
2427 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2428 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2429 RGBAQuantum,png_pixels+row_offset,exception);
2430 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2431 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2432 IndexQuantum,png_pixels+row_offset,exception);
2433 else
2434 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2435 RGBQuantum,png_pixels+row_offset,exception);
2436#endif
2437 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2438 break;
2439 }
2440 if (image->previous == (Image *) NULL)
2441 {
2442 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2443 if (status == MagickFalse)
2444 break;
2445 }
2446 }
2447 else /* image->storage_class != DirectClass */
2448 for (pass=0; pass < num_passes; pass++)
2449 {
2450 Quantum
2451 *quantum_scanline;
2452
2453 register Quantum
2454 *r;
2455
2456 /*
2457 Convert grayscale image to PseudoClass pixel packets.
2458 */
2459 image->matte=ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2460 MagickTrue : MagickFalse;
2461 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2462 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2463 if (quantum_scanline == (Quantum *) NULL)
2464 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2465 for (y=0; y < (long) image->rows; y++)
2466 {
2467 if (num_passes > 1)
2468 row_offset=ping_info->rowbytes*y;
2469 else
2470 row_offset=0;
2471 png_read_row(ping,png_pixels+row_offset,NULL);
2472 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2473 if (q == (PixelPacket *) NULL)
2474 break;
2475 indices=GetAuthenticIndexQueue(image);
2476 p=png_pixels+row_offset;
2477 r=quantum_scanline;
2478 switch (ping_info->bit_depth)
2479 {
2480 case 1:
2481 {
2482 register long
2483 bit;
2484
2485 for (x=(long) image->columns-7; x > 0; x-=8)
2486 {
2487 for (bit=7; bit >= 0; bit--)
2488 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2489 p++;
2490 }
2491 if ((image->columns % 8) != 0)
2492 {
2493 for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--)
2494 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2495 }
2496 break;
2497 }
2498 case 2:
2499 {
2500 for (x=(long) image->columns-3; x > 0; x-=4)
2501 {
2502 *r++=(*p >> 6) & 0x03;
2503 *r++=(*p >> 4) & 0x03;
2504 *r++=(*p >> 2) & 0x03;
2505 *r++=(*p++) & 0x03;
2506 }
2507 if ((image->columns % 4) != 0)
2508 {
2509 for (i=3; i >= (long) (4-(image->columns % 4)); i--)
2510 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2511 }
2512 break;
2513 }
2514 case 4:
2515 {
2516 for (x=(long) image->columns-1; x > 0; x-=2)
2517 {
2518 *r++=(*p >> 4) & 0x0f;
2519 *r++=(*p++) & 0x0f;
2520 }
2521 if ((image->columns % 2) != 0)
2522 *r++=(*p++ >> 4) & 0x0f;
2523 break;
2524 }
2525 case 8:
2526 {
2527 if (ping_info->color_type == 4)
2528 for (x=(long) image->columns-1; x >= 0; x--)
2529 {
2530 *r++=*p++;
2531 /* In image.h, OpaqueOpacity is 0
2532 * TransparentOpacity is QuantumRange
2533 * In a PNG datastream, Opaque is QuantumRange
2534 * and Transparent is 0.
2535 */
2536 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2537 q++;
2538 }
2539 else
2540 for (x=(long) image->columns-1; x >= 0; x--)
2541 *r++=*p++;
2542 break;
2543 }
2544 case 16:
2545 {
2546 for (x=(long) image->columns-1; x >= 0; x--)
2547 {
2548#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2549 unsigned long
2550 quantum;
2551
2552 if (image->colors > 256)
2553 *r=((*p++) << 8);
2554 else
2555 *r=0;
2556 quantum=(*r);
2557 quantum|=(*p++);
2558 *r=(Quantum) quantum;
2559 r++;
2560 if (ping_info->color_type == 4)
2561 {
2562 quantum=((*p++) << 8);
2563 quantum|=(*p++);
2564 q->opacity=(Quantum) (QuantumRange-quantum);
2565 q++;
2566 }
2567#else
2568#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2569 unsigned long
2570 quantum;
2571
2572 if (image->colors > 256)
2573 *r=((*p++) << 8);
2574 else
2575 *r=0;
2576 quantum=(*r);
2577 quantum|=(*p++);
2578 *r=quantum;
2579 r++;
2580 if (ping_info->color_type == 4)
2581 {
2582 q->opacity=(*p << 8) | *(p+1);
2583 q->opacity*=65537L;
2584 q->opacity=(Quantum) (QuantumRange-q->opacity);
2585 p+=2;
2586 q++;
2587 }
2588#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2589 *r++=(*p++);
2590 p++; /* strip low byte */
2591 if (ping_info->color_type == 4)
2592 {
2593 q->opacity=(Quantum) (QuantumRange-(*p++));
2594 p++;
2595 q++;
2596 }
2597#endif
2598#endif
2599 }
2600 break;
2601 }
2602 default:
2603 break;
2604 }
2605 /*
2606 Transfer image scanline.
2607 */
2608 r=quantum_scanline;
2609 for (x=0; x < (long) image->columns; x++)
2610 indices[x]=(*r++);
2611 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2612 break;
2613 }
2614 if (image->previous == (Image *) NULL)
2615 {
2616 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2617 if (status == MagickFalse)
2618 break;
2619 }
2620 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2621 }
cristyb32b90a2009-09-07 21:45:48 +00002622 if (quantum_info != (QuantumInfo *) NULL)
2623 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00002624 if (image->storage_class == PseudoClass)
2625 (void) SyncImage(image);
2626 png_read_end(ping,ping_info);
2627
2628 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2629 (long) image_info->first_scene && image->delay != 0)
2630 {
2631 png_destroy_read_struct(&ping,&ping_info,&end_info);
2632 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2633 image->colors=2;
2634 (void) SetImageBackgroundColor(image);
2635#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2636 RelinquishSemaphoreInfo(png_semaphore);
2637#endif
2638 if (logging != MagickFalse)
2639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2640 " exit ReadOnePNGImage() early.");
2641 return(image);
2642 }
2643 if (ping_info->valid & PNG_INFO_tRNS)
2644 {
2645 ClassType
2646 storage_class;
2647
2648 /*
2649 Image has a transparent background.
2650 */
2651 storage_class=image->storage_class;
2652 image->matte=MagickTrue;
2653 for (y=0; y < (long) image->rows; y++)
2654 {
2655 image->storage_class=storage_class;
2656 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2657 if (q == (PixelPacket *) NULL)
2658 break;
2659 indices=GetAuthenticIndexQueue(image);
2660
2661 if (storage_class == PseudoClass)
2662 {
2663 IndexPacket
2664 indexpacket;
2665
2666 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2667 for (x=0; x < (long) image->columns; x++)
2668 {
2669 indexpacket=indices[x];
2670 if (indexpacket < ping_info->num_trans)
2671 q->opacity=ScaleCharToQuantum((unsigned char)
2672 (255-ping_info->trans_alpha[(long) indexpacket]));
2673 else
2674 q->opacity=OpaqueOpacity;
2675 q++;
2676 }
2677 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2678 for (x=0; x < (long) image->columns; x++)
2679 {
2680 indexpacket=indices[x];
2681 q->red=image->colormap[(long) indexpacket].red;
2682 q->green=q->red;
2683 q->blue=q->red;
2684 if (q->red == transparent_color.opacity)
2685 q->opacity=(Quantum) TransparentOpacity;
2686 else
2687 q->opacity=OpaqueOpacity;
2688 q++;
2689 }
2690 }
2691 else
2692 for (x=(long) image->columns-1; x >= 0; x--)
2693 {
2694 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2695 ScaleQuantumToChar(q->green) == transparent_color.green &&
2696 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2697 q->opacity=(Quantum) TransparentOpacity;
2698 else
2699 q->opacity=OpaqueOpacity;
2700 q++;
2701 }
2702 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2703 break;
2704 }
2705 image->storage_class=DirectClass;
2706 }
2707#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2708 if (image->depth > 8)
2709 image->depth=8;
2710#endif
2711 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2712 for (i=0; i < (long) num_text; i++)
2713 {
2714 /* Check for a profile */
2715
2716 if (logging != MagickFalse)
2717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2718 " Reading PNG text chunk");
2719 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2720 (void) png_read_raw_profile(image,image_info,text,(int) i);
2721 else
2722 {
2723 char
2724 *value;
2725
2726 length=text[i].text_length;
2727 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2728 sizeof(*value));
2729 if (value == (char *) NULL)
2730 {
2731 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2732 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2733 image->filename);
2734 break;
2735 }
2736 *value='\0';
2737 (void) ConcatenateMagickString(value,text[i].text,length+2);
2738 (void) SetImageProperty(image,text[i].key,value);
2739 if (logging != MagickFalse)
2740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2741 " Keyword: %s",text[i].key);
2742 value=DestroyString(value);
2743 }
2744 }
2745#ifdef MNG_OBJECT_BUFFERS
2746 /*
2747 Store the object if necessary.
2748 */
2749 if (object_id && !mng_info->frozen[object_id])
2750 {
2751 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2752 {
2753 /*
2754 create a new object buffer.
2755 */
2756 mng_info->ob[object_id]=(MngBuffer *)
2757 AcquireMagickMemory(sizeof(MngBuffer));
2758 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2759 {
2760 mng_info->ob[object_id]->image=(Image *) NULL;
2761 mng_info->ob[object_id]->reference_count=1;
2762 }
2763 }
2764 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2765 mng_info->ob[object_id]->frozen)
2766 {
2767 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2768 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2769 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2770 image->filename);
2771 if (mng_info->ob[object_id]->frozen)
2772 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2773 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2774 "`%s'",image->filename);
2775 }
2776 else
2777 {
2778 png_uint_32
2779 width,
2780 height;
2781
2782 int
2783 bit_depth,
2784 color_type,
2785 interlace_method,
2786 compression_method,
2787 filter_method;
2788
2789 if (mng_info->ob[object_id]->image != (Image *) NULL)
2790 mng_info->ob[object_id]->image=DestroyImage
2791 (mng_info->ob[object_id]->image);
2792 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2793 &image->exception);
2794 if (mng_info->ob[object_id]->image != (Image *) NULL)
2795 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2796 else
2797 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2798 ResourceLimitError,"Cloning image for object buffer failed",
2799 "`%s'",image->filename);
2800 png_get_IHDR(ping,ping_info,&width,&height,&bit_depth,&color_type,
2801 &interlace_method,&compression_method,&filter_method);
2802 if (width > 250000L || height > 250000L)
2803 png_error(ping,"PNG Image dimensions are too large.");
2804 mng_info->ob[object_id]->width=width;
2805 mng_info->ob[object_id]->height=height;
2806 mng_info->ob[object_id]->color_type=color_type;
2807 mng_info->ob[object_id]->sample_depth=bit_depth;
2808 mng_info->ob[object_id]->interlace_method=interlace_method;
2809 mng_info->ob[object_id]->compression_method=compression_method;
2810 mng_info->ob[object_id]->filter_method=filter_method;
2811 if (ping_info->valid & PNG_INFO_PLTE)
2812 {
2813 int
2814 number_colors;
2815
2816 png_colorp
2817 plte;
2818
2819 /*
2820 Copy the PLTE to the object buffer.
2821 */
2822 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2823 mng_info->ob[object_id]->plte_length=number_colors;
2824 for (i=0; i < number_colors; i++)
2825 {
2826 mng_info->ob[object_id]->plte[i]=plte[i];
2827 }
2828 }
2829 else
2830 mng_info->ob[object_id]->plte_length=0;
2831 }
2832 }
2833#endif
2834 /*
2835 Relinquish resources.
2836 */
2837 png_destroy_read_struct(&ping,&ping_info,&end_info);
2838
2839 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2840#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2841 RelinquishSemaphoreInfo(png_semaphore);
2842#endif
2843
2844 if (logging != MagickFalse)
2845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2846 " exit ReadOnePNGImage()");
2847 return(image);
2848
2849/* end of reading one PNG image */
2850}
2851
2852static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2853{
2854 Image
2855 *image,
2856 *previous;
2857
2858 MagickBooleanType
2859 status;
2860
2861 MngInfo
2862 *mng_info;
2863
2864 char
2865 magic_number[MaxTextExtent];
2866
2867 int
2868 have_mng_structure,
2869 logging;
2870
2871 ssize_t
2872 count;
2873
2874 /*
2875 Open image file.
2876 */
2877 assert(image_info != (const ImageInfo *) NULL);
2878 assert(image_info->signature == MagickSignature);
2879 if (image_info->debug != MagickFalse)
2880 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2881 image_info->filename);
2882 assert(exception != (ExceptionInfo *) NULL);
2883 assert(exception->signature == MagickSignature);
2884 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
2885 image=AcquireImage(image_info);
2886 mng_info=(MngInfo *) NULL;
2887 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2888 if (status == MagickFalse)
2889 ThrowReaderException(FileOpenError,"UnableToOpenFile");
2890 /*
2891 Verify PNG signature.
2892 */
2893 count=ReadBlob(image,8,(unsigned char *) magic_number);
2894 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
2895 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2896 /*
2897 Allocate a MngInfo structure.
2898 */
2899 have_mng_structure=MagickFalse;
2900 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
2901 if (mng_info == (MngInfo *) NULL)
2902 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2903 /*
2904 Initialize members of the MngInfo structure.
2905 */
2906 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
2907 mng_info->image=image;
2908 have_mng_structure=MagickTrue;
2909
2910 previous=image;
2911 image=ReadOnePNGImage(mng_info,image_info,exception);
2912 MngInfoFreeStruct(mng_info,&have_mng_structure);
2913 if (image == (Image *) NULL)
2914 {
2915 if (previous != (Image *) NULL)
2916 {
2917 if (previous->signature != MagickSignature)
2918 ThrowReaderException(CorruptImageError,"CorruptImage");
2919 (void) CloseBlob(previous);
2920 (void) DestroyImageList(previous);
2921 }
2922 if (logging != MagickFalse)
2923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2924 "exit ReadPNGImage() with error");
2925 return((Image *) NULL);
2926 }
2927 (void) CloseBlob(image);
2928 if ((image->columns == 0) || (image->rows == 0))
2929 {
2930 if (logging != MagickFalse)
2931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2932 "exit ReadPNGImage() with error.");
2933 ThrowReaderException(CorruptImageError,"CorruptImage");
2934 }
2935 if (LocaleCompare(image_info->magick,"PNG8") == 0)
2936 {
2937 (void) SetImageType(image,PaletteType);
2938 if (image->matte != MagickFalse)
2939 {
2940 /* To do: Reduce to binary transparency */
2941 }
2942 }
2943 if (LocaleCompare(image_info->magick,"PNG24") == 0)
2944 {
2945 (void) SetImageType(image,TrueColorType);
2946 image->matte=MagickFalse;
2947 }
2948 if (LocaleCompare(image_info->magick,"PNG32") == 0)
2949 (void) SetImageType(image,TrueColorMatteType);
2950 if (logging != MagickFalse)
2951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
2952 return(image);
2953}
2954
2955
2956
2957#if defined(JNG_SUPPORTED)
2958/*
2959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2960% %
2961% %
2962% %
2963% R e a d O n e J N G I m a g e %
2964% %
2965% %
2966% %
2967%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2968%
2969% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
2970% (minus the 8-byte signature) and returns it. It allocates the memory
2971% necessary for the new Image structure and returns a pointer to the new
2972% image.
2973%
2974% JNG support written by Glenn Randers-Pehrson, glennrp@image...
2975%
2976% The format of the ReadOneJNGImage method is:
2977%
2978% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2979% ExceptionInfo *exception)
2980%
2981% A description of each parameter follows:
2982%
2983% o mng_info: Specifies a pointer to a MngInfo structure.
2984%
2985% o image_info: the image info.
2986%
2987% o exception: return any errors or warnings in this structure.
2988%
2989*/
2990static Image *ReadOneJNGImage(MngInfo *mng_info,
2991 const ImageInfo *image_info, ExceptionInfo *exception)
2992{
2993 Image
2994 *alpha_image,
2995 *color_image,
2996 *image,
2997 *jng_image;
2998
2999 ImageInfo
3000 *alpha_image_info,
3001 *color_image_info;
3002
3003 long
3004 y;
3005
3006 MagickBooleanType
3007 status;
3008
3009 png_uint_32
3010 jng_height,
3011 jng_width;
3012
3013 png_byte
3014 jng_color_type,
3015 jng_image_sample_depth,
3016 jng_image_compression_method,
3017 jng_image_interlace_method,
3018 jng_alpha_sample_depth,
3019 jng_alpha_compression_method,
3020 jng_alpha_filter_method,
3021 jng_alpha_interlace_method;
3022
3023 register const PixelPacket
3024 *s;
3025
3026 register long
3027 i,
3028 x;
3029
3030 register PixelPacket
3031 *q;
3032
3033 register unsigned char
3034 *p;
3035
3036 unsigned int
3037 logging,
3038 read_JSEP,
3039 reading_idat,
3040 skip_to_iend;
3041
3042 unsigned long
3043 length;
3044
3045 jng_alpha_compression_method=0;
3046 jng_alpha_sample_depth=8;
3047 jng_color_type=0;
3048 jng_height=0;
3049 jng_width=0;
3050 alpha_image=(Image *) NULL;
3051 color_image=(Image *) NULL;
3052 alpha_image_info=(ImageInfo *) NULL;
3053 color_image_info=(ImageInfo *) NULL;
3054
3055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3056 " enter ReadOneJNGImage()");
3057
3058 image=mng_info->image;
3059 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3060 {
3061 /*
3062 Allocate next image structure.
3063 */
3064 if (logging != MagickFalse)
3065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3066 " AcquireNextImage()");
3067 AcquireNextImage(image_info,image);
3068 if (GetNextImageInList(image) == (Image *) NULL)
3069 return((Image *) NULL);
3070 image=SyncNextImageInList(image);
3071 }
3072 mng_info->image=image;
3073
3074 /*
3075 Signature bytes have already been read.
3076 */
3077
3078 read_JSEP=MagickFalse;
3079 reading_idat=MagickFalse;
3080 skip_to_iend=MagickFalse;
3081 for (;;)
3082 {
3083 char
3084 type[MaxTextExtent];
3085
3086 unsigned char
3087 *chunk;
3088
3089 unsigned int
3090 count;
3091
3092 /*
3093 Read a new JNG chunk.
3094 */
3095 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3096 2*GetBlobSize(image));
3097 if (status == MagickFalse)
3098 break;
3099 type[0]='\0';
3100 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3101 length=ReadBlobMSBLong(image);
3102 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3103
3104 if (logging != MagickFalse)
3105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3106 " Reading JNG chunk type %c%c%c%c, length: %lu",
3107 type[0],type[1],type[2],type[3],length);
3108
3109 if (length > PNG_UINT_31_MAX || count == 0)
3110 ThrowReaderException(CorruptImageError,"CorruptImage");
3111 p=NULL;
3112 chunk=(unsigned char *) NULL;
3113 if (length)
3114 {
3115 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3116 if (chunk == (unsigned char *) NULL)
3117 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3118 for (i=0; i < (long) length; i++)
3119 chunk[i]=(unsigned char) ReadBlobByte(image);
3120 p=chunk;
3121 }
3122 (void) ReadBlobMSBLong(image); /* read crc word */
3123
3124 if (skip_to_iend)
3125 {
3126 if (length)
3127 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3128 continue;
3129 }
3130
3131 if (memcmp(type,mng_JHDR,4) == 0)
3132 {
3133 if (length == 16)
3134 {
3135 jng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
3136 (p[2] << 8) | p[3]);
3137 jng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
3138 (p[6] << 8) | p[7]);
3139 jng_color_type=p[8];
3140 jng_image_sample_depth=p[9];
3141 jng_image_compression_method=p[10];
3142 jng_image_interlace_method=p[11];
3143 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3144 NoInterlace;
3145 jng_alpha_sample_depth=p[12];
3146 jng_alpha_compression_method=p[13];
3147 jng_alpha_filter_method=p[14];
3148 jng_alpha_interlace_method=p[15];
3149 if (logging != MagickFalse)
3150 {
3151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3152 " jng_width: %16lu",jng_width);
3153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3154 " jng_width: %16lu",jng_height);
3155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3156 " jng_color_type: %16d",jng_color_type);
3157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3158 " jng_image_sample_depth: %3d",
3159 jng_image_sample_depth);
3160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3161 " jng_image_compression_method:%3d",
3162 jng_image_compression_method);
3163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3164 " jng_image_interlace_method: %3d",
3165 jng_image_interlace_method);
3166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3167 " jng_alpha_sample_depth: %3d",
3168 jng_alpha_sample_depth);
3169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3170 " jng_alpha_compression_method:%3d",
3171 jng_alpha_compression_method);
3172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3173 " jng_alpha_filter_method: %3d",
3174 jng_alpha_filter_method);
3175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3176 " jng_alpha_interlace_method: %3d",
3177 jng_alpha_interlace_method);
3178 }
3179 }
3180 if (length)
3181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3182 continue;
3183 }
3184
3185
3186 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3187 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3188 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3189 {
3190 /*
3191 o create color_image
3192 o open color_blob, attached to color_image
3193 o if (color type has alpha)
3194 open alpha_blob, attached to alpha_image
3195 */
3196
3197 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3198 if (color_image_info == (ImageInfo *) NULL)
3199 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3200 GetImageInfo(color_image_info);
3201 color_image=AcquireImage(color_image_info);
3202 if (color_image == (Image *) NULL)
3203 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3204
3205 if (logging != MagickFalse)
3206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3207 " Creating color_blob.");
3208 (void) AcquireUniqueFilename(color_image->filename);
3209 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3210 exception);
3211 if (status == MagickFalse)
3212 return((Image *) NULL);
3213
3214 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3215 {
3216 alpha_image_info=(ImageInfo *)
3217 AcquireMagickMemory(sizeof(ImageInfo));
3218 if (alpha_image_info == (ImageInfo *) NULL)
3219 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3220 GetImageInfo(alpha_image_info);
3221 alpha_image=AcquireImage(alpha_image_info);
3222 if (alpha_image == (Image *) NULL)
3223 {
3224 alpha_image=DestroyImage(alpha_image);
3225 ThrowReaderException(ResourceLimitError,
3226 "MemoryAllocationFailed");
3227 }
3228 if (logging != MagickFalse)
3229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3230 " Creating alpha_blob.");
3231 (void) AcquireUniqueFilename(alpha_image->filename);
3232 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3233 exception);
3234 if (status == MagickFalse)
3235 return((Image *) NULL);
3236 if (jng_alpha_compression_method == 0)
3237 {
3238 unsigned char
3239 data[18];
3240
3241 if (logging != MagickFalse)
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " Writing IHDR chunk to alpha_blob.");
3244 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3245 "\211PNG\r\n\032\n");
3246 (void) WriteBlobMSBULong(alpha_image,13L);
3247 PNGType(data,mng_IHDR);
3248 LogPNGChunk((int) logging,mng_IHDR,13L);
3249 PNGLong(data+4,jng_width);
3250 PNGLong(data+8,jng_height);
3251 data[12]=jng_alpha_sample_depth;
3252 data[13]=0; /* color_type gray */
3253 data[14]=0; /* compression method 0 */
3254 data[15]=0; /* filter_method 0 */
3255 data[16]=0; /* interlace_method 0 */
3256 (void) WriteBlob(alpha_image,17,data);
3257 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3258 }
3259 }
3260 reading_idat=MagickTrue;
3261 }
3262
3263 if (memcmp(type,mng_JDAT,4) == 0)
3264 {
3265 /*
3266 Copy chunk to color_image->blob
3267 */
3268
3269 if (logging != MagickFalse)
3270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3271 " Copying JDAT chunk data to color_blob.");
3272
3273 (void) WriteBlob(color_image,length,chunk);
3274 if (length)
3275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3276 continue;
3277 }
3278
3279 if (memcmp(type,mng_IDAT,4) == 0)
3280 {
3281 png_byte
3282 data[5];
3283
3284 /*
3285 Copy IDAT header and chunk data to alpha_image->blob
3286 */
3287
3288 if (image_info->ping == MagickFalse)
3289 {
3290 if (logging != MagickFalse)
3291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3292 " Copying IDAT chunk data to alpha_blob.");
3293
3294 (void) WriteBlobMSBULong(alpha_image,(unsigned long) length);
3295 PNGType(data,mng_IDAT);
3296 LogPNGChunk((int) logging,mng_IDAT,length);
3297 (void) WriteBlob(alpha_image,4,data);
3298 (void) WriteBlob(alpha_image,length,chunk);
3299 (void) WriteBlobMSBULong(alpha_image,
3300 crc32(crc32(0,data,4),chunk,(uInt) length));
3301 }
3302 if (length)
3303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3304 continue;
3305 }
3306
3307 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3308 {
3309 /*
3310 Copy chunk data to alpha_image->blob
3311 */
3312
3313 if (image_info->ping == MagickFalse)
3314 {
3315 if (logging != MagickFalse)
3316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3317 " Copying JDAA chunk data to alpha_blob.");
3318
3319 (void) WriteBlob(alpha_image,length,chunk);
3320 }
3321 if (length)
3322 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3323 continue;
3324 }
3325
3326 if (memcmp(type,mng_JSEP,4) == 0)
3327 {
3328 read_JSEP=MagickTrue;
3329 if (length)
3330 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3331 continue;
3332 }
3333
3334 if (memcmp(type,mng_bKGD,4) == 0)
3335 {
3336 if (length == 2)
3337 {
3338 image->background_color.red=ScaleCharToQuantum(p[1]);
3339 image->background_color.green=image->background_color.red;
3340 image->background_color.blue=image->background_color.red;
3341 }
3342 if (length == 6)
3343 {
3344 image->background_color.red=ScaleCharToQuantum(p[1]);
3345 image->background_color.green=ScaleCharToQuantum(p[3]);
3346 image->background_color.blue=ScaleCharToQuantum(p[5]);
3347 }
3348 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3349 continue;
3350 }
3351
3352 if (memcmp(type,mng_gAMA,4) == 0)
3353 {
3354 if (length == 4)
3355 image->gamma=((float) mng_get_long(p))*0.00001;
3356 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3357 continue;
3358 }
3359
3360 if (memcmp(type,mng_cHRM,4) == 0)
3361 {
3362 if (length == 32)
3363 {
3364 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3365 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3366 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3367 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3368 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3369 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3370 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3371 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3372 }
3373 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3374 continue;
3375 }
3376
3377 if (memcmp(type,mng_sRGB,4) == 0)
3378 {
3379 if (length == 1)
3380 {
3381 image->rendering_intent=(RenderingIntent) (p[0]+1);
3382 image->gamma=0.45455f;
3383 image->chromaticity.red_primary.x=0.6400f;
3384 image->chromaticity.red_primary.y=0.3300f;
3385 image->chromaticity.green_primary.x=0.3000f;
3386 image->chromaticity.green_primary.y=0.6000f;
3387 image->chromaticity.blue_primary.x=0.1500f;
3388 image->chromaticity.blue_primary.y=0.0600f;
3389 image->chromaticity.white_point.x=0.3127f;
3390 image->chromaticity.white_point.y=0.3290f;
3391 }
3392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3393 continue;
3394 }
3395
3396 if (memcmp(type,mng_oFFs,4) == 0)
3397 {
3398 if (length > 8)
3399 {
3400 image->page.x=mng_get_long(p);
3401 image->page.y=mng_get_long(&p[4]);
3402 if ((int) p[8] != 0)
3403 {
3404 image->page.x/=10000;
3405 image->page.y/=10000;
3406 }
3407 }
3408 if (length)
3409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3410 continue;
3411 }
3412
3413 if (memcmp(type,mng_pHYs,4) == 0)
3414 {
3415 if (length > 8)
3416 {
3417 image->x_resolution=(double) mng_get_long(p);
3418 image->y_resolution=(double) mng_get_long(&p[4]);
3419 if ((int) p[8] == PNG_RESOLUTION_METER)
3420 {
3421 image->units=PixelsPerCentimeterResolution;
3422 image->x_resolution=image->x_resolution/100.0f;
3423 image->y_resolution=image->y_resolution/100.0f;
3424 }
3425 }
3426 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3427 continue;
3428 }
3429
3430#if 0
3431 if (memcmp(type,mng_iCCP,4) == 0)
3432 {
3433 /* To do. */
3434 if (length)
3435 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3436 continue;
3437 }
3438#endif
3439
3440 if (length)
3441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3442
3443 if (memcmp(type,mng_IEND,4))
3444 continue;
3445 break;
3446 }
3447
3448
3449 /* IEND found */
3450
3451 /*
3452 Finish up reading image data:
3453
3454 o read main image from color_blob.
3455
3456 o close color_blob.
3457
3458 o if (color_type has alpha)
3459 if alpha_encoding is PNG
3460 read secondary image from alpha_blob via ReadPNG
3461 if alpha_encoding is JPEG
3462 read secondary image from alpha_blob via ReadJPEG
3463
3464 o close alpha_blob.
3465
3466 o copy intensity of secondary image into
3467 opacity samples of main image.
3468
3469 o destroy the secondary image.
3470 */
3471
3472 (void) CloseBlob(color_image);
3473 if (logging != MagickFalse)
3474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3475 " Reading jng_image from color_blob.");
3476 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3477 color_image->filename);
3478 color_image_info->ping=MagickFalse; /* To do: avoid this */
3479 jng_image=ReadImage(color_image_info,exception);
3480 if (jng_image == (Image *) NULL)
3481 return((Image *) NULL);
3482
3483 (void) RelinquishUniqueFileResource(color_image->filename);
3484 color_image=DestroyImage(color_image);
3485 color_image_info=DestroyImageInfo(color_image_info);
3486
3487 if (jng_image == (Image *) NULL)
3488 return((Image *) NULL);
3489
3490 if (logging != MagickFalse)
3491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3492 " Copying jng_image pixels to main image.");
3493 image->rows=jng_height;
3494 image->columns=jng_width;
3495 length=image->columns*sizeof(PixelPacket);
3496 for (y=0; y < (long) image->rows; y++)
3497 {
3498 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3499 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3500 (void) CopyMagickMemory(q,s,length);
3501 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3502 break;
3503 }
3504 jng_image=DestroyImage(jng_image);
3505 if (image_info->ping == MagickFalse)
3506 {
3507 if (jng_color_type >= 12)
3508 {
3509 if (jng_alpha_compression_method == 0)
3510 {
3511 png_byte
3512 data[5];
3513 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3514 PNGType(data,mng_IEND);
3515 LogPNGChunk((int) logging,mng_IEND,0L);
3516 (void) WriteBlob(alpha_image,4,data);
3517 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3518 }
3519 (void) CloseBlob(alpha_image);
3520 if (logging != MagickFalse)
3521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522 " Reading opacity from alpha_blob.");
3523
3524 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3525 "%s",alpha_image->filename);
3526
3527 jng_image=ReadImage(alpha_image_info,exception);
3528 if (jng_image != (Image *) NULL)
3529 for (y=0; y < (long) image->rows; y++)
3530 {
3531 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3532 &image->exception);
3533 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3534 if (image->matte != MagickFalse)
3535 for (x=(long) image->columns; x != 0; x--,q++,s++)
3536 q->opacity=(Quantum) QuantumRange-s->red;
3537 else
3538 for (x=(long) image->columns; x != 0; x--,q++,s++)
3539 {
3540 q->opacity=(Quantum) QuantumRange-s->red;
3541 if (q->opacity != OpaqueOpacity)
3542 image->matte=MagickTrue;
3543 }
3544 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3545 break;
3546 }
3547 (void) RelinquishUniqueFileResource(alpha_image->filename);
3548 alpha_image=DestroyImage(alpha_image);
3549 alpha_image_info=DestroyImageInfo(alpha_image_info);
3550 if (jng_image != (Image *) NULL)
3551 jng_image=DestroyImage(jng_image);
3552 }
3553 }
3554
3555 /*
3556 Read the JNG image.
3557 */
3558 if (mng_info->mng_type == 0)
3559 {
3560 mng_info->mng_width=jng_width;
3561 mng_info->mng_height=jng_height;
3562 }
3563 if (image->page.width == 0 && image->page.height == 0)
3564 {
3565 image->page.width=jng_width;
3566 image->page.height=jng_height;
3567 }
3568 if (image->page.x == 0 && image->page.y == 0)
3569 {
3570 image->page.x=mng_info->x_off[mng_info->object_id];
3571 image->page.y=mng_info->y_off[mng_info->object_id];
3572 }
3573 else
3574 {
3575 image->page.y=mng_info->y_off[mng_info->object_id];
3576 }
3577 mng_info->image_found++;
3578 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3579 2*GetBlobSize(image));
3580 if (logging != MagickFalse)
3581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3582 " exit ReadOneJNGImage()");
3583 return(image);
3584}
3585
3586/*
3587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3588% %
3589% %
3590% %
3591% R e a d J N G I m a g e %
3592% %
3593% %
3594% %
3595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3596%
3597% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3598% (including the 8-byte signature) and returns it. It allocates the memory
3599% necessary for the new Image structure and returns a pointer to the new
3600% image.
3601%
3602% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3603%
3604% The format of the ReadJNGImage method is:
3605%
3606% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3607% *exception)
3608%
3609% A description of each parameter follows:
3610%
3611% o image_info: the image info.
3612%
3613% o exception: return any errors or warnings in this structure.
3614%
3615*/
3616
3617static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3618{
3619 Image
3620 *image,
3621 *previous;
3622
3623 MagickBooleanType
3624 status;
3625
3626 MngInfo
3627 *mng_info;
3628
3629 char
3630 magic_number[MaxTextExtent];
3631
3632 int
3633 have_mng_structure,
3634 logging;
3635
3636 size_t
3637 count;
3638
3639 /*
3640 Open image file.
3641 */
3642 assert(image_info != (const ImageInfo *) NULL);
3643 assert(image_info->signature == MagickSignature);
3644 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3645 assert(exception != (ExceptionInfo *) NULL);
3646 assert(exception->signature == MagickSignature);
3647 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3648 image=AcquireImage(image_info);
3649 mng_info=(MngInfo *) NULL;
3650 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3651 if (status == MagickFalse)
3652 return((Image *) NULL);
3653 if (LocaleCompare(image_info->magick,"JNG") != 0)
3654 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3655 /*
3656 Verify JNG signature.
3657 */
3658 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3659 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3660 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3661 /*
3662 Allocate a MngInfo structure.
3663 */
3664 have_mng_structure=MagickFalse;
3665 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3666 if (mng_info == (MngInfo *) NULL)
3667 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3668 /*
3669 Initialize members of the MngInfo structure.
3670 */
3671 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3672 have_mng_structure=MagickTrue;
3673
3674 mng_info->image=image;
3675 previous=image;
3676 image=ReadOneJNGImage(mng_info,image_info,exception);
3677 MngInfoFreeStruct(mng_info,&have_mng_structure);
3678 if (image == (Image *) NULL)
3679 {
3680 if (IsImageObject(previous) != MagickFalse)
3681 {
3682 (void) CloseBlob(previous);
3683 (void) DestroyImageList(previous);
3684 }
3685 if (logging != MagickFalse)
3686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3687 "exit ReadJNGImage() with error");
3688 return((Image *) NULL);
3689 }
3690 (void) CloseBlob(image);
3691 if (image->columns == 0 || image->rows == 0)
3692 {
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadJNGImage() with error");
3696 ThrowReaderException(CorruptImageError,"CorruptImage");
3697 }
3698 if (logging != MagickFalse)
3699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3700 return(image);
3701}
3702#endif
3703
3704static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3705{
3706 char
3707 page_geometry[MaxTextExtent];
3708
3709 Image
3710 *image,
3711 *previous;
3712
3713 int
3714 have_mng_structure;
3715
3716 volatile int
3717 first_mng_object,
3718 logging,
3719 object_id,
3720 term_chunk_found,
3721 skip_to_iend;
3722
3723 volatile long
3724 image_count=0;
3725
3726 MagickBooleanType
3727 status;
3728
3729 MagickOffsetType
3730 offset;
3731
3732 MngInfo
3733 *mng_info;
3734
3735 MngBox
3736 default_fb,
3737 fb,
3738 previous_fb;
3739
3740#if defined(MNG_INSERT_LAYERS)
3741 PixelPacket
3742 mng_background_color;
3743#endif
3744
3745 register unsigned char
3746 *p;
3747
3748 register long
3749 i;
3750
3751 size_t
3752 count;
3753
3754 long
3755 loop_level;
3756
3757 volatile short
3758 skipping_loop;
3759
3760#if defined(MNG_INSERT_LAYERS)
3761 unsigned int
3762 mandatory_back=0;
3763#endif
3764
3765 volatile unsigned int
3766#ifdef MNG_OBJECT_BUFFERS
3767 mng_background_object=0,
3768#endif
3769 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3770
3771 unsigned long
3772 default_frame_timeout,
3773 frame_timeout,
3774#if defined(MNG_INSERT_LAYERS)
3775 image_height,
3776 image_width,
3777#endif
3778 length;
3779
3780 volatile unsigned long
3781 default_frame_delay,
3782 final_delay,
3783 final_image_delay,
3784 frame_delay,
3785#if defined(MNG_INSERT_LAYERS)
3786 insert_layers,
3787#endif
3788 mng_iterations=1,
3789 simplicity=0,
3790 subframe_height=0,
3791 subframe_width=0;
3792
3793 previous_fb.top=0;
3794 previous_fb.bottom=0;
3795 previous_fb.left=0;
3796 previous_fb.right=0;
3797 default_fb.top=0;
3798 default_fb.bottom=0;
3799 default_fb.left=0;
3800 default_fb.right=0;
3801
3802 /*
3803 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
3804 following optimizations:
3805
3806 o 16-bit depth is reduced to 8 if all pixels contain samples whose
3807 high byte and low byte are identical.
3808 o Opaque matte channel is removed.
3809 o If matte channel is present but only one transparent color is
3810 present, RGB+tRNS is written instead of RGBA
3811 o Grayscale images are reduced to 1, 2, or 4 bit depth if
3812 this can be done without loss.
3813 o Palette is sorted to remove unused entries and to put a
3814 transparent color first, if PNG_SORT_PALETTE is also defined.
3815 */
3816
3817 /*
3818 Open image file.
3819 */
3820 assert(image_info != (const ImageInfo *) NULL);
3821 assert(image_info->signature == MagickSignature);
3822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3823 assert(exception != (ExceptionInfo *) NULL);
3824 assert(exception->signature == MagickSignature);
3825 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
3826 image=AcquireImage(image_info);
3827 mng_info=(MngInfo *) NULL;
3828 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3829 if (status == MagickFalse)
3830 return((Image *) NULL);
3831 first_mng_object=MagickFalse;
3832 skipping_loop=(-1);
3833 have_mng_structure=MagickFalse;
3834 /*
3835 Allocate a MngInfo structure.
3836 */
3837 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3838 if (mng_info == (MngInfo *) NULL)
3839 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3840 /*
3841 Initialize members of the MngInfo structure.
3842 */
3843 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3844 mng_info->image=image;
3845 have_mng_structure=MagickTrue;
3846#if (MAGICKCORE_QUANTUM_DEPTH == 16)
3847 mng_info->optimize=image_info->type == OptimizeType;
3848#endif
3849
3850 if (LocaleCompare(image_info->magick,"MNG") == 0)
3851 {
3852 char
3853 magic_number[MaxTextExtent];
3854
3855 /*
3856 Verify MNG signature.
3857 */
3858 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3859 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
3860 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3861 /*
3862 Initialize some nonzero members of the MngInfo structure.
3863 */
3864 for (i=0; i < MNG_MAX_OBJECTS; i++)
3865 {
3866 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
3867 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
3868 }
3869 mng_info->exists[0]=MagickTrue;
3870 }
3871 first_mng_object=MagickTrue;
3872 mng_type=0;
3873#if defined(MNG_INSERT_LAYERS)
3874 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
3875#endif
3876 default_frame_delay=0;
3877 default_frame_timeout=0;
3878 frame_delay=0;
3879 final_delay=1;
3880 mng_info->ticks_per_second=1UL*image->ticks_per_second;
3881 object_id=0;
3882 skip_to_iend=MagickFalse;
3883 term_chunk_found=MagickFalse;
3884 mng_info->framing_mode=1;
3885#if defined(MNG_INSERT_LAYERS)
3886 mandatory_back=MagickFalse;
3887#endif
3888#if defined(MNG_INSERT_LAYERS)
3889 mng_background_color=image->background_color;
3890#endif
3891 default_fb=mng_info->frame;
3892 previous_fb=mng_info->frame;
3893 do
3894 {
3895 char
3896 type[MaxTextExtent];
3897
3898 if (LocaleCompare(image_info->magick,"MNG") == 0)
3899 {
3900 unsigned char
3901 *chunk;
3902
3903 /*
3904 Read a new chunk.
3905 */
3906 type[0]='\0';
3907 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3908 length=ReadBlobMSBLong(image);
3909 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
3910
3911 if (logging != MagickFalse)
3912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3913 " Reading MNG chunk type %c%c%c%c, length: %lu",
3914 type[0],type[1],type[2],type[3],length);
3915
3916 if (length > PNG_UINT_31_MAX)
3917 status=MagickFalse;
3918 if (count == 0)
3919 ThrowReaderException(CorruptImageError,"CorruptImage");
3920 p=NULL;
3921 chunk=(unsigned char *) NULL;
3922 if (length)
3923 {
3924 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3925 if (chunk == (unsigned char *) NULL)
3926 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3927 for (i=0; i < (long) length; i++)
3928 chunk[i]=(unsigned char) ReadBlobByte(image);
3929 p=chunk;
3930 }
3931 (void) ReadBlobMSBLong(image); /* read crc word */
3932
3933#if !defined(JNG_SUPPORTED)
3934 if (memcmp(type,mng_JHDR,4) == 0)
3935 {
3936 skip_to_iend=MagickTrue;
3937 if (mng_info->jhdr_warning == 0)
3938 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3939 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
3940 mng_info->jhdr_warning++;
3941 }
3942#endif
3943 if (memcmp(type,mng_DHDR,4) == 0)
3944 {
3945 skip_to_iend=MagickTrue;
3946 if (mng_info->dhdr_warning == 0)
3947 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3948 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
3949 mng_info->dhdr_warning++;
3950 }
3951 if (memcmp(type,mng_MEND,4) == 0)
3952 break;
3953 if (skip_to_iend)
3954 {
3955 if (memcmp(type,mng_IEND,4) == 0)
3956 skip_to_iend=MagickFalse;
3957 if (length)
3958 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3959 if (logging != MagickFalse)
3960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " Skip to IEND.");
3962 continue;
3963 }
3964 if (memcmp(type,mng_MHDR,4) == 0)
3965 {
3966 mng_info->mng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
3967 (p[2] << 8) | p[3]);
3968 mng_info->mng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
3969 (p[6] << 8) | p[7]);
3970 if (logging != MagickFalse)
3971 {
3972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3973 " MNG width: %lu",mng_info->mng_width);
3974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3975 " MNG height: %lu",mng_info->mng_height);
3976 }
3977 p+=8;
3978 mng_info->ticks_per_second=(unsigned long) mng_get_long(p);
3979 if (mng_info->ticks_per_second == 0)
3980 default_frame_delay=0;
3981 else
3982 default_frame_delay=1UL*image->ticks_per_second/
3983 mng_info->ticks_per_second;
3984 frame_delay=default_frame_delay;
3985 simplicity=0;
3986 if (length > 16)
3987 {
3988 p+=16;
3989 simplicity=(unsigned long) mng_get_long(p);
3990 }
3991 mng_type=1; /* Full MNG */
3992 if ((simplicity != 0) && ((simplicity | 11) == 11))
3993 mng_type=2; /* LC */
3994 if ((simplicity != 0) && ((simplicity | 9) == 9))
3995 mng_type=3; /* VLC */
3996#if defined(MNG_INSERT_LAYERS)
3997 if (mng_type != 3)
3998 insert_layers=MagickTrue;
3999#endif
4000 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4001 {
4002 /*
4003 Allocate next image structure.
4004 */
4005 AcquireNextImage(image_info,image);
4006 if (GetNextImageInList(image) == (Image *) NULL)
4007 return((Image *) NULL);
4008 image=SyncNextImageInList(image);
4009 mng_info->image=image;
4010 }
4011
4012 if ((mng_info->mng_width > 65535L) ||
4013 (mng_info->mng_height > 65535L))
4014 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4015 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu+0+0",
4016 mng_info->mng_width,mng_info->mng_height);
4017 mng_info->frame.left=0;
4018 mng_info->frame.right=(long) mng_info->mng_width;
4019 mng_info->frame.top=0;
4020 mng_info->frame.bottom=(long) mng_info->mng_height;
4021 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4022 for (i=0; i < MNG_MAX_OBJECTS; i++)
4023 mng_info->object_clip[i]=mng_info->frame;
4024 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4025 continue;
4026 }
4027
4028 if (memcmp(type,mng_TERM,4) == 0)
4029 {
4030 int
4031 repeat=0;
4032
4033
4034 if (length)
4035 repeat=p[0];
4036 if (repeat == 3)
4037 {
4038 final_delay=(png_uint_32) mng_get_long(&p[2]);
4039 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4040 if (mng_iterations == PNG_UINT_31_MAX)
4041 mng_iterations=0;
4042 image->iterations=mng_iterations;
4043 term_chunk_found=MagickTrue;
4044 }
4045 if (logging != MagickFalse)
4046 {
4047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4048 " repeat=%d",repeat);
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " final_delay=%ld",final_delay);
4051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4052 " image->iterations=%ld",image->iterations);
4053 }
4054 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4055 continue;
4056 }
4057 if (memcmp(type,mng_DEFI,4) == 0)
4058 {
4059 if (mng_type == 3)
4060 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4061 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4062 image->filename);
4063 object_id=(p[0] << 8) | p[1];
4064 if (mng_type == 2 && object_id != 0)
4065 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4066 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4067 image->filename);
4068 if (object_id > MNG_MAX_OBJECTS)
4069 {
4070 /*
4071 Instead ofsuing a warning we should allocate a larger
4072 MngInfo structure and continue.
4073 */
4074 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4075 CoderError,"object id too large","`%s'",image->filename);
4076 object_id=MNG_MAX_OBJECTS;
4077 }
4078 if (mng_info->exists[object_id])
4079 if (mng_info->frozen[object_id])
4080 {
4081 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4082 (void) ThrowMagickException(&image->exception,
4083 GetMagickModule(),CoderError,
4084 "DEFI cannot redefine a frozen MNG object","`%s'",
4085 image->filename);
4086 continue;
4087 }
4088 mng_info->exists[object_id]=MagickTrue;
4089 if (length > 2)
4090 mng_info->invisible[object_id]=p[2];
4091 /*
4092 Extract object offset info.
4093 */
4094 if (length > 11)
4095 {
4096 mng_info->x_off[object_id]=(long) ((p[4] << 24) | (p[5] << 16) |
4097 (p[6] << 8) | p[7]);
4098 mng_info->y_off[object_id]=(long) ((p[8] << 24) | (p[9] << 16) |
4099 (p[10] << 8) | p[11]);
4100 if (logging != MagickFalse)
4101 {
4102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4103 " x_off[%d]: %lu",object_id,mng_info->x_off[object_id]);
4104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4105 " y_off[%d]: %lu",object_id,mng_info->y_off[object_id]);
4106 }
4107 }
4108 /*
4109 Extract object clipping info.
4110 */
4111 if (length > 27)
4112 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4113 &p[12]);
4114 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4115 continue;
4116 }
4117 if (memcmp(type,mng_bKGD,4) == 0)
4118 {
4119 mng_info->have_global_bkgd=MagickFalse;
4120 if (length > 5)
4121 {
4122 mng_info->mng_global_bkgd.red=
4123 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4124 mng_info->mng_global_bkgd.green=
4125 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4126 mng_info->mng_global_bkgd.blue=
4127 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4128 mng_info->have_global_bkgd=MagickTrue;
4129 }
4130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4131 continue;
4132 }
4133 if (memcmp(type,mng_BACK,4) == 0)
4134 {
4135#if defined(MNG_INSERT_LAYERS)
4136 if (length > 6)
4137 mandatory_back=p[6];
4138 else
4139 mandatory_back=0;
4140 if (mandatory_back && length > 5)
4141 {
4142 mng_background_color.red=
4143 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4144 mng_background_color.green=
4145 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4146 mng_background_color.blue=
4147 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4148 mng_background_color.opacity=OpaqueOpacity;
4149 }
4150#ifdef MNG_OBJECT_BUFFERS
4151 if (length > 8)
4152 mng_background_object=(p[7] << 8) | p[8];
4153#endif
4154#endif
4155 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4156 continue;
4157 }
4158 if (memcmp(type,mng_PLTE,4) == 0)
4159 {
4160 /*
4161 Read global PLTE.
4162 */
4163 if (length && (length < 769))
4164 {
4165 if (mng_info->global_plte == (png_colorp) NULL)
4166 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4167 sizeof(*mng_info->global_plte));
4168 for (i=0; i < (long) (length/3); i++)
4169 {
4170 mng_info->global_plte[i].red=p[3*i];
4171 mng_info->global_plte[i].green=p[3*i+1];
4172 mng_info->global_plte[i].blue=p[3*i+2];
4173 }
4174 mng_info->global_plte_length=length/3;
4175 }
4176#ifdef MNG_LOOSE
4177 for ( ; i < 256; i++)
4178 {
4179 mng_info->global_plte[i].red=i;
4180 mng_info->global_plte[i].green=i;
4181 mng_info->global_plte[i].blue=i;
4182 }
4183 if (length)
4184 mng_info->global_plte_length=256;
4185#endif
4186 else
4187 mng_info->global_plte_length=0;
4188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4189 continue;
4190 }
4191 if (memcmp(type,mng_tRNS,4) == 0)
4192 {
4193 /* read global tRNS */
4194
4195 if (length < 257)
4196 for (i=0; i < (long) length; i++)
4197 mng_info->global_trns[i]=p[i];
4198
4199#ifdef MNG_LOOSE
4200 for ( ; i < 256; i++)
4201 mng_info->global_trns[i]=255;
4202#endif
4203 mng_info->global_trns_length=length;
4204 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4205 continue;
4206 }
4207 if (memcmp(type,mng_gAMA,4) == 0)
4208 {
4209 if (length == 4)
4210 {
4211 long
4212 igamma;
4213
4214 igamma=mng_get_long(p);
4215 mng_info->global_gamma=((float) igamma)*0.00001;
4216 mng_info->have_global_gama=MagickTrue;
4217 }
4218 else
4219 mng_info->have_global_gama=MagickFalse;
4220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4221 continue;
4222 }
4223
4224 if (memcmp(type,mng_cHRM,4) == 0)
4225 {
4226 /*
4227 Read global cHRM
4228 */
4229 if (length == 32)
4230 {
4231 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4232 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4233 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4234 mng_info->global_chrm.red_primary.y=0.00001*
4235 mng_get_long(&p[12]);
4236 mng_info->global_chrm.green_primary.x=0.00001*
4237 mng_get_long(&p[16]);
4238 mng_info->global_chrm.green_primary.y=0.00001*
4239 mng_get_long(&p[20]);
4240 mng_info->global_chrm.blue_primary.x=0.00001*
4241 mng_get_long(&p[24]);
4242 mng_info->global_chrm.blue_primary.y=0.00001*
4243 mng_get_long(&p[28]);
4244 mng_info->have_global_chrm=MagickTrue;
4245 }
4246 else
4247 mng_info->have_global_chrm=MagickFalse;
4248 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4249 continue;
4250 }
4251 if (memcmp(type,mng_sRGB,4) == 0)
4252 {
4253 /*
4254 Read global sRGB.
4255 */
4256 if (length)
4257 {
4258 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4259 mng_info->have_global_srgb=MagickTrue;
4260 }
4261 else
4262 mng_info->have_global_srgb=MagickFalse;
4263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4264 continue;
4265 }
4266 if (memcmp(type,mng_iCCP,4) == 0)
4267 {
4268 /* To do. */
4269
4270 /*
4271 Read global iCCP.
4272 */
4273 if (length)
4274 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4275 continue;
4276 }
4277 if (memcmp(type,mng_FRAM,4) == 0)
4278 {
4279 if (mng_type == 3)
4280 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4281 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4282 image->filename);
4283 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4284 image->delay=frame_delay;
4285 frame_delay=default_frame_delay;
4286 frame_timeout=default_frame_timeout;
4287 fb=default_fb;
4288 if (length)
4289 if (p[0])
4290 mng_info->framing_mode=p[0];
4291 if (logging != MagickFalse)
4292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4293 " Framing_mode=%d",mng_info->framing_mode);
4294 if (length > 6)
4295 {
4296 /*
4297 Note the delay and frame clipping boundaries.
4298 */
4299 p++; /* framing mode */
4300 while (*p && ((p-chunk) < (long) length))
4301 p++; /* frame name */
4302 p++; /* frame name terminator */
4303 if ((p-chunk) < (long) (length-4))
4304 {
4305 int
4306 change_delay,
4307 change_timeout,
4308 change_clipping;
4309
4310 change_delay=(*p++);
4311 change_timeout=(*p++);
4312 change_clipping=(*p++);
4313 p++; /* change_sync */
4314 if (change_delay)
4315 {
4316 frame_delay=(1UL*image->ticks_per_second*
4317 (mng_get_long(p))/mng_info->ticks_per_second);
4318 if (change_delay == 2)
4319 default_frame_delay=frame_delay;
4320 p+=4;
4321 if (logging != MagickFalse)
4322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4323 " Framing_delay=%ld",frame_delay);
4324 }
4325 if (change_timeout)
4326 {
4327 frame_timeout=(1UL*image->ticks_per_second*
4328 (mng_get_long(p))/mng_info->ticks_per_second);
4329 if (change_delay == 2)
4330 default_frame_timeout=frame_timeout;
4331 p+=4;
4332 if (logging != MagickFalse)
4333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4334 " Framing_timeout=%ld",frame_timeout);
4335 }
4336 if (change_clipping)
4337 {
4338 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4339 p+=17;
4340 previous_fb=fb;
4341 if (logging != MagickFalse)
4342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4343 " Frame_clipping: L=%ld R=%ld T=%ld B=%ld",
4344 fb.left, fb.right,fb.top,fb.bottom);
4345 if (change_clipping == 2)
4346 default_fb=fb;
4347 }
4348 }
4349 }
4350 mng_info->clip=fb;
4351 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4352 subframe_width=(unsigned long) (mng_info->clip.right
4353 -mng_info->clip.left);
4354 subframe_height=(unsigned long) (mng_info->clip.bottom
4355 -mng_info->clip.top);
4356 /*
4357 Insert a background layer behind the frame if framing_mode is 4.
4358 */
4359#if defined(MNG_INSERT_LAYERS)
4360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362 " subframe_width=%lu, subframe_height=%lu",
4363 subframe_width, subframe_height);
4364 if (insert_layers && (mng_info->framing_mode == 4) &&
4365 (subframe_width) && (subframe_height))
4366 {
4367 /*
4368 Allocate next image structure.
4369 */
4370 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4371 {
4372 AcquireNextImage(image_info,image);
4373 if (GetNextImageInList(image) == (Image *) NULL)
4374 {
4375 image=DestroyImageList(image);
4376 MngInfoFreeStruct(mng_info,&have_mng_structure);
4377 return((Image *) NULL);
4378 }
4379 image=SyncNextImageInList(image);
4380 }
4381 mng_info->image=image;
4382 if (term_chunk_found)
4383 {
4384 image->start_loop=MagickTrue;
4385 image->iterations=mng_iterations;
4386 term_chunk_found=MagickFalse;
4387 }
4388 else
4389 image->start_loop=MagickFalse;
4390 image->columns=subframe_width;
4391 image->rows=subframe_height;
4392 image->page.width=subframe_width;
4393 image->page.height=subframe_height;
4394 image->page.x=mng_info->clip.left;
4395 image->page.y=mng_info->clip.top;
4396 image->background_color=mng_background_color;
4397 image->matte=MagickFalse;
4398 image->delay=0;
4399 (void) SetImageBackgroundColor(image);
4400 if (logging != MagickFalse)
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4402 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
4403 mng_info->clip.left,mng_info->clip.right,
4404 mng_info->clip.top,mng_info->clip.bottom);
4405 }
4406#endif
4407 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4408 continue;
4409 }
4410 if (memcmp(type,mng_CLIP,4) == 0)
4411 {
4412 unsigned int
4413 first_object,
4414 last_object;
4415
4416 /*
4417 Read CLIP.
4418 */
4419 first_object=(p[0] << 8) | p[1];
4420 last_object=(p[2] << 8) | p[3];
4421 for (i=(int) first_object; i <= (int) last_object; i++)
4422 {
4423 if (mng_info->exists[i] && !mng_info->frozen[i])
4424 {
4425 MngBox
4426 box;
4427
4428 box=mng_info->object_clip[i];
4429 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4430 }
4431 }
4432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4433 continue;
4434 }
4435 if (memcmp(type,mng_SAVE,4) == 0)
4436 {
4437 for (i=1; i < MNG_MAX_OBJECTS; i++)
4438 if (mng_info->exists[i])
4439 {
4440 mng_info->frozen[i]=MagickTrue;
4441#ifdef MNG_OBJECT_BUFFERS
4442 if (mng_info->ob[i] != (MngBuffer *) NULL)
4443 mng_info->ob[i]->frozen=MagickTrue;
4444#endif
4445 }
4446 if (length)
4447 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4448 continue;
4449 }
4450
4451 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4452 {
4453 /*
4454 Read DISC or SEEK.
4455 */
4456 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4457 {
4458 for (i=1; i < MNG_MAX_OBJECTS; i++)
4459 MngInfoDiscardObject(mng_info,i);
4460 }
4461 else
4462 {
4463 register long
4464 j;
4465
4466 for (j=0; j < (long) length; j+=2)
4467 {
4468 i=p[j] << 8 | p[j+1];
4469 MngInfoDiscardObject(mng_info,i);
4470 }
4471 }
4472 if (length)
4473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4474 continue;
4475 }
4476 if (memcmp(type,mng_MOVE,4) == 0)
4477 {
4478 unsigned long
4479 first_object,
4480 last_object;
4481
4482 /*
4483 read MOVE
4484 */
4485 first_object=(p[0] << 8) | p[1];
4486 last_object=(p[2] << 8) | p[3];
4487 for (i=(long) first_object; i <= (long) last_object; i++)
4488 {
4489 if (mng_info->exists[i] && !mng_info->frozen[i])
4490 {
4491 MngPair
4492 new_pair;
4493
4494 MngPair
4495 old_pair;
4496
4497 old_pair.a=mng_info->x_off[i];
4498 old_pair.b=mng_info->y_off[i];
4499 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4500 mng_info->x_off[i]=new_pair.a;
4501 mng_info->y_off[i]=new_pair.b;
4502 }
4503 }
4504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4505 continue;
4506 }
4507
4508 if (memcmp(type,mng_LOOP,4) == 0)
4509 {
4510 long loop_iters=1;
4511 loop_level=chunk[0];
4512 mng_info->loop_active[loop_level]=1; /* mark loop active */
4513 /*
4514 Record starting point.
4515 */
4516 loop_iters=mng_get_long(&chunk[1]);
4517 if (logging != MagickFalse)
4518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4519 " LOOP level %ld has %ld iterations ",loop_level,loop_iters);
4520 if (loop_iters == 0)
4521 skipping_loop=loop_level;
4522 else
4523 {
4524 mng_info->loop_jump[loop_level]=TellBlob(image);
4525 mng_info->loop_count[loop_level]=loop_iters;
4526 }
4527 mng_info->loop_iteration[loop_level]=0;
4528 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4529 continue;
4530 }
4531 if (memcmp(type,mng_ENDL,4) == 0)
4532 {
4533 loop_level=chunk[0];
4534 if (skipping_loop > 0)
4535 {
4536 if (skipping_loop == loop_level)
4537 {
4538 /*
4539 Found end of zero-iteration loop.
4540 */
4541 skipping_loop=(-1);
4542 mng_info->loop_active[loop_level]=0;
4543 }
4544 }
4545 else
4546 {
4547 if (mng_info->loop_active[loop_level] == 1)
4548 {
4549 mng_info->loop_count[loop_level]--;
4550 mng_info->loop_iteration[loop_level]++;
4551 if (logging != MagickFalse)
4552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4553 " ENDL: LOOP level %ld has %ld remaining iterations ",
4554 loop_level,mng_info->loop_count[loop_level]);
4555 if (mng_info->loop_count[loop_level] != 0)
4556 {
4557 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4558 SEEK_SET);
4559 if (offset < 0)
4560 ThrowReaderException(CorruptImageError,
4561 "ImproperImageHeader");
4562 }
4563 else
4564 {
4565 short
4566 last_level;
4567
4568 /*
4569 Finished loop.
4570 */
4571 mng_info->loop_active[loop_level]=0;
4572 last_level=(-1);
4573 for (i=0; i < loop_level; i++)
4574 if (mng_info->loop_active[i] == 1)
4575 last_level=(short) i;
4576 loop_level=last_level;
4577 }
4578 }
4579 }
4580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4581 continue;
4582 }
4583 if (memcmp(type,mng_CLON,4) == 0)
4584 {
4585 if (mng_info->clon_warning == 0)
4586 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4587 CoderError,"CLON is not implemented yet","`%s'",
4588 image->filename);
4589 mng_info->clon_warning++;
4590 }
4591 if (memcmp(type,mng_MAGN,4) == 0)
4592 {
4593 png_uint_16
4594 magn_first,
4595 magn_last,
4596 magn_mb,
4597 magn_ml,
4598 magn_mr,
4599 magn_mt,
4600 magn_mx,
4601 magn_my,
4602 magn_methx,
4603 magn_methy;
4604
4605 if (length > 1)
4606 magn_first=(p[0] << 8) | p[1];
4607 else
4608 magn_first=0;
4609 if (length > 3)
4610 magn_last=(p[2] << 8) | p[3];
4611 else
4612 magn_last=magn_first;
4613#ifndef MNG_OBJECT_BUFFERS
4614 if (magn_first || magn_last)
4615 if (mng_info->magn_warning == 0)
4616 {
4617 (void) ThrowMagickException(&image->exception,
4618 GetMagickModule(),CoderError,
4619 "MAGN is not implemented yet for nonzero objects",
4620 "`%s'",image->filename);
4621 mng_info->magn_warning++;
4622 }
4623#endif
4624 if (length > 4)
4625 magn_methx=p[4];
4626 else
4627 magn_methx=0;
4628
4629 if (length > 6)
4630 magn_mx=(p[5] << 8) | p[6];
4631 else
4632 magn_mx=1;
4633 if (magn_mx == 0)
4634 magn_mx=1;
4635
4636 if (length > 8)
4637 magn_my=(p[7] << 8) | p[8];
4638 else
4639 magn_my=magn_mx;
4640 if (magn_my == 0)
4641 magn_my=1;
4642
4643 if (length > 10)
4644 magn_ml=(p[9] << 8) | p[10];
4645 else
4646 magn_ml=magn_mx;
4647 if (magn_ml == 0)
4648 magn_ml=1;
4649
4650 if (length > 12)
4651 magn_mr=(p[11] << 8) | p[12];
4652 else
4653 magn_mr=magn_mx;
4654 if (magn_mr == 0)
4655 magn_mr=1;
4656
4657 if (length > 14)
4658 magn_mt=(p[13] << 8) | p[14];
4659 else
4660 magn_mt=magn_my;
4661 if (magn_mt == 0)
4662 magn_mt=1;
4663
4664 if (length > 16)
4665 magn_mb=(p[15] << 8) | p[16];
4666 else
4667 magn_mb=magn_my;
4668 if (magn_mb == 0)
4669 magn_mb=1;
4670
4671 if (length > 17)
4672 magn_methy=p[17];
4673 else
4674 magn_methy=magn_methx;
4675
4676 if (magn_methx > 5 || magn_methy > 5)
4677 if (mng_info->magn_warning == 0)
4678 {
4679 (void) ThrowMagickException(&image->exception,
4680 GetMagickModule(),CoderError,
4681 "Unknown MAGN method in MNG datastream","`%s'",
4682 image->filename);
4683 mng_info->magn_warning++;
4684 }
4685#ifdef MNG_OBJECT_BUFFERS
4686 /* Magnify existing objects in the range magn_first to magn_last */
4687#endif
4688 if (magn_first == 0 || magn_last == 0)
4689 {
4690 /* Save the magnification factors for object 0 */
4691 mng_info->magn_mb=magn_mb;
4692 mng_info->magn_ml=magn_ml;
4693 mng_info->magn_mr=magn_mr;
4694 mng_info->magn_mt=magn_mt;
4695 mng_info->magn_mx=magn_mx;
4696 mng_info->magn_my=magn_my;
4697 mng_info->magn_methx=magn_methx;
4698 mng_info->magn_methy=magn_methy;
4699 }
4700 }
4701 if (memcmp(type,mng_PAST,4) == 0)
4702 {
4703 if (mng_info->past_warning == 0)
4704 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4705 CoderError,"PAST is not implemented yet","`%s'",
4706 image->filename);
4707 mng_info->past_warning++;
4708 }
4709 if (memcmp(type,mng_SHOW,4) == 0)
4710 {
4711 if (mng_info->show_warning == 0)
4712 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4713 CoderError,"SHOW is not implemented yet","`%s'",
4714 image->filename);
4715 mng_info->show_warning++;
4716 }
4717 if (memcmp(type,mng_sBIT,4) == 0)
4718 {
4719 if (length < 4)
4720 mng_info->have_global_sbit=MagickFalse;
4721 else
4722 {
4723 mng_info->global_sbit.gray=p[0];
4724 mng_info->global_sbit.red=p[0];
4725 mng_info->global_sbit.green=p[1];
4726 mng_info->global_sbit.blue=p[2];
4727 mng_info->global_sbit.alpha=p[3];
4728 mng_info->have_global_sbit=MagickTrue;
4729 }
4730 }
4731 if (memcmp(type,mng_pHYs,4) == 0)
4732 {
4733 if (length > 8)
4734 {
4735 mng_info->global_x_pixels_per_unit=
4736 (unsigned long) mng_get_long(p);
4737 mng_info->global_y_pixels_per_unit=
4738 (unsigned long) mng_get_long(&p[4]);
4739 mng_info->global_phys_unit_type=p[8];
4740 mng_info->have_global_phys=MagickTrue;
4741 }
4742 else
4743 mng_info->have_global_phys=MagickFalse;
4744 }
4745 if (memcmp(type,mng_pHYg,4) == 0)
4746 {
4747 if (mng_info->phyg_warning == 0)
4748 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4749 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4750 mng_info->phyg_warning++;
4751 }
4752 if (memcmp(type,mng_BASI,4) == 0)
4753 {
4754 skip_to_iend=MagickTrue;
4755 if (mng_info->basi_warning == 0)
4756 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4757 CoderError,"BASI is not implemented yet","`%s'",
4758 image->filename);
4759 mng_info->basi_warning++;
4760#ifdef MNG_BASI_SUPPORTED
4761 basi_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
4762 (p[2] << 8) | p[3]);
4763 basi_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
4764 (p[6] << 8) | p[7]);
4765 basi_color_type=p[8];
4766 basi_compression_method=p[9];
4767 basi_filter_type=p[10];
4768 basi_interlace_method=p[11];
4769 if (length > 11)
4770 basi_red=(p[12] << 8) & p[13];
4771 else
4772 basi_red=0;
4773 if (length > 13)
4774 basi_green=(p[14] << 8) & p[15];
4775 else
4776 basi_green=0;
4777 if (length > 15)
4778 basi_blue=(p[16] << 8) & p[17];
4779 else
4780 basi_blue=0;
4781 if (length > 17)
4782 basi_alpha=(p[18] << 8) & p[19];
4783 else
4784 {
4785 if (basi_sample_depth == 16)
4786 basi_alpha=65535L;
4787 else
4788 basi_alpha=255;
4789 }
4790 if (length > 19)
4791 basi_viewable=p[20];
4792 else
4793 basi_viewable=0;
4794#endif
4795 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4796 continue;
4797 }
4798 if (memcmp(type,mng_IHDR,4)
4799#if defined(JNG_SUPPORTED)
4800 && memcmp(type,mng_JHDR,4)
4801#endif
4802 )
4803 {
4804 /* Not an IHDR or JHDR chunk */
4805 if (length)
4806 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4807 continue;
4808 }
4809/* Process IHDR */
4810 if (logging != MagickFalse)
4811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4812 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
4813 mng_info->exists[object_id]=MagickTrue;
4814 mng_info->viewable[object_id]=MagickTrue;
4815 if (mng_info->invisible[object_id])
4816 {
4817 if (logging != MagickFalse)
4818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4819 " Skipping invisible object");
4820 skip_to_iend=MagickTrue;
4821 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4822 continue;
4823 }
4824#if defined(MNG_INSERT_LAYERS)
4825 if (length < 8)
4826 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4827 image_width=(unsigned long) mng_get_long(p);
4828 image_height=(unsigned long) mng_get_long(&p[4]);
4829#endif
4830 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4831
4832 /*
4833 Insert a transparent background layer behind the entire animation
4834 if it is not full screen.
4835 */
4836#if defined(MNG_INSERT_LAYERS)
4837 if (insert_layers && mng_type && first_mng_object)
4838 {
4839 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
4840 (image_width < mng_info->mng_width) ||
4841 (mng_info->clip.right < (long) mng_info->mng_width) ||
4842 (image_height < mng_info->mng_height) ||
4843 (mng_info->clip.bottom < (long) mng_info->mng_height))
4844 {
4845 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4846 {
4847 /*
4848 Allocate next image structure.
4849 */
4850 AcquireNextImage(image_info,image);
4851 if (GetNextImageInList(image) == (Image *) NULL)
4852 {
4853 image=DestroyImageList(image);
4854 MngInfoFreeStruct(mng_info,&have_mng_structure);
4855 return((Image *) NULL);
4856 }
4857 image=SyncNextImageInList(image);
4858 }
4859 mng_info->image=image;
4860 if (term_chunk_found)
4861 {
4862 image->start_loop=MagickTrue;
4863 image->iterations=mng_iterations;
4864 term_chunk_found=MagickFalse;
4865 }
4866 else
4867 image->start_loop=MagickFalse;
4868 /*
4869 Make a background rectangle.
4870 */
4871 image->delay=0;
4872 image->columns=mng_info->mng_width;
4873 image->rows=mng_info->mng_height;
4874 image->page.width=mng_info->mng_width;
4875 image->page.height=mng_info->mng_height;
4876 image->page.x=0;
4877 image->page.y=0;
4878 image->background_color=mng_background_color;
4879 (void) SetImageBackgroundColor(image);
4880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882 " Inserted transparent background layer, W=%lud, H=%lud",
4883 mng_info->mng_width,mng_info->mng_height);
4884 }
4885 }
4886 /*
4887 Insert a background layer behind the upcoming image if
4888 framing_mode is 3, and we haven't already inserted one.
4889 */
4890 if (insert_layers && (mng_info->framing_mode == 3) &&
4891 (subframe_width) && (subframe_height) && (simplicity == 0 ||
4892 (simplicity & 0x08)))
4893 {
4894 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4895 {
4896 /*
4897 Allocate next image structure.
4898 */
4899 AcquireNextImage(image_info,image);
4900 if (GetNextImageInList(image) == (Image *) NULL)
4901 {
4902 image=DestroyImageList(image);
4903 MngInfoFreeStruct(mng_info,&have_mng_structure);
4904 return((Image *) NULL);
4905 }
4906 image=SyncNextImageInList(image);
4907 }
4908 mng_info->image=image;
4909 if (term_chunk_found)
4910 {
4911 image->start_loop=MagickTrue;
4912 image->iterations=mng_iterations;
4913 term_chunk_found=MagickFalse;
4914 }
4915 else
4916 image->start_loop=MagickFalse;
4917 image->delay=0;
4918 image->columns=subframe_width;
4919 image->rows=subframe_height;
4920 image->page.width=subframe_width;
4921 image->page.height=subframe_height;
4922 image->page.x=mng_info->clip.left;
4923 image->page.y=mng_info->clip.top;
4924 image->background_color=mng_background_color;
4925 image->matte=MagickFalse;
4926 (void) SetImageBackgroundColor(image);
4927 if (logging != MagickFalse)
4928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4929 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
4930 mng_info->clip.left,mng_info->clip.right,
4931 mng_info->clip.top,mng_info->clip.bottom);
4932 }
4933#endif /* MNG_INSERT_LAYERS */
4934 first_mng_object=MagickFalse;
4935 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4936 {
4937 /*
4938 Allocate next image structure.
4939 */
4940 AcquireNextImage(image_info,image);
4941 if (GetNextImageInList(image) == (Image *) NULL)
4942 {
4943 image=DestroyImageList(image);
4944 MngInfoFreeStruct(mng_info,&have_mng_structure);
4945 return((Image *) NULL);
4946 }
4947 image=SyncNextImageInList(image);
4948 }
4949 mng_info->image=image;
4950 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4951 GetBlobSize(image));
4952 if (status == MagickFalse)
4953 break;
4954 if (term_chunk_found)
4955 {
4956 image->start_loop=MagickTrue;
4957 term_chunk_found=MagickFalse;
4958 }
4959 else
4960 image->start_loop=MagickFalse;
4961 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
4962 {
4963 image->delay=frame_delay;
4964 frame_delay=default_frame_delay;
4965 }
4966 else
4967 image->delay=0;
4968 image->page.width=mng_info->mng_width;
4969 image->page.height=mng_info->mng_height;
4970 image->page.x=mng_info->x_off[object_id];
4971 image->page.y=mng_info->y_off[object_id];
4972 image->iterations=mng_iterations;
4973 /*
4974 Seek back to the beginning of the IHDR or JHDR chunk's length field.
4975 */
4976 if (logging != MagickFalse)
4977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4978 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
4979 type[2],type[3]);
4980 offset=SeekBlob(image,-((long) length+12),SEEK_CUR);
4981 if (offset < 0)
4982 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4983 }
4984
4985 previous=image;
4986 mng_info->image=image;
4987 mng_info->mng_type=mng_type;
4988 mng_info->object_id=object_id;
4989
4990 if (memcmp(type,mng_IHDR,4) == 0)
4991 image=ReadOnePNGImage(mng_info,image_info,exception);
4992#if defined(JNG_SUPPORTED)
4993 else
4994 image=ReadOneJNGImage(mng_info,image_info,exception);
4995#endif
4996
4997 if (image == (Image *) NULL)
4998 {
4999 if (IsImageObject(previous) != MagickFalse)
5000 {
5001 (void) DestroyImageList(previous);
5002 (void) CloseBlob(previous);
5003 }
5004 MngInfoFreeStruct(mng_info,&have_mng_structure);
5005 return((Image *) NULL);
5006 }
5007 if (image->columns == 0 || image->rows == 0)
5008 {
5009 (void) CloseBlob(image);
5010 image=DestroyImageList(image);
5011 MngInfoFreeStruct(mng_info,&have_mng_structure);
5012 return((Image *) NULL);
5013 }
5014 mng_info->image=image;
5015
5016 if (mng_type)
5017 {
5018 MngBox
5019 crop_box;
5020
5021 if (mng_info->magn_methx || mng_info->magn_methy)
5022 {
5023 png_uint_32
5024 magnified_height,
5025 magnified_width;
5026
5027 if (logging != MagickFalse)
5028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5029 " Processing MNG MAGN chunk");
5030
5031 if (mng_info->magn_methx == 1)
5032 {
5033 magnified_width=mng_info->magn_ml;
5034 if (image->columns > 1)
5035 magnified_width += mng_info->magn_mr;
5036 if (image->columns > 2)
5037 magnified_width += (image->columns-2)*(mng_info->magn_mx);
5038 }
5039 else
5040 {
5041 magnified_width=image->columns;
5042 if (image->columns > 1)
5043 magnified_width += mng_info->magn_ml-1;
5044 if (image->columns > 2)
5045 magnified_width += mng_info->magn_mr-1;
5046 if (image->columns > 3)
5047 magnified_width += (image->columns-3)*(mng_info->magn_mx-1);
5048 }
5049 if (mng_info->magn_methy == 1)
5050 {
5051 magnified_height=mng_info->magn_mt;
5052 if (image->rows > 1)
5053 magnified_height += mng_info->magn_mb;
5054 if (image->rows > 2)
5055 magnified_height += (image->rows-2)*(mng_info->magn_my);
5056 }
5057 else
5058 {
5059 magnified_height=image->rows;
5060 if (image->rows > 1)
5061 magnified_height += mng_info->magn_mt-1;
5062 if (image->rows > 2)
5063 magnified_height += mng_info->magn_mb-1;
5064 if (image->rows > 3)
5065 magnified_height += (image->rows-3)*(mng_info->magn_my-1);
5066 }
5067 if (magnified_height > image->rows ||
5068 magnified_width > image->columns)
5069 {
5070 Image
5071 *large_image;
5072
5073 int
5074 yy;
5075
5076 long
5077 m,
5078 y;
5079
5080 register long
5081 x;
5082
5083 register PixelPacket
5084 *n,
5085 *q;
5086
5087 PixelPacket
5088 *next,
5089 *prev;
5090
5091 png_uint_16
5092 magn_methx,
5093 magn_methy;
5094
5095 /*
5096 Allocate next image structure.
5097 */
5098 if (logging != MagickFalse)
5099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5100 " Allocate magnified image");
5101 AcquireNextImage(image_info,image);
5102 if (GetNextImageInList(image) == (Image *) NULL)
5103 {
5104 image=DestroyImageList(image);
5105 MngInfoFreeStruct(mng_info,&have_mng_structure);
5106 return((Image *) NULL);
5107 }
5108
5109 large_image=SyncNextImageInList(image);
5110
5111 large_image->columns=magnified_width;
5112 large_image->rows=magnified_height;
5113
5114 magn_methx=mng_info->magn_methx;
5115 magn_methy=mng_info->magn_methy;
5116
5117#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5118#define QM unsigned short
5119 if (magn_methx != 1 || magn_methy != 1)
5120 {
5121 /*
5122 Scale pixels to unsigned shorts to prevent
5123 overflow of intermediate values of interpolations
5124 */
5125 for (y=0; y < (long) image->rows; y++)
5126 {
5127 q=GetAuthenticPixels(image,0,y,image->columns,1,
5128 exception);
5129 for (x=(long) image->columns-1; x >= 0; x--)
5130 {
5131 q->red=ScaleQuantumToShort(q->red);
5132 q->green=ScaleQuantumToShort(q->green);
5133 q->blue=ScaleQuantumToShort(q->blue);
5134 q->opacity=ScaleQuantumToShort(q->opacity);
5135 q++;
5136 }
5137 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5138 break;
5139 }
5140 }
5141#else
5142#define QM Quantum
5143#endif
5144
5145 if (image->matte != MagickFalse)
5146 (void) SetImageBackgroundColor(large_image);
5147 else
5148 {
5149 large_image->background_color.opacity=OpaqueOpacity;
5150 (void) SetImageBackgroundColor(large_image);
5151 if (magn_methx == 4)
5152 magn_methx=2;
5153 if (magn_methx == 5)
5154 magn_methx=3;
5155 if (magn_methy == 4)
5156 magn_methy=2;
5157 if (magn_methy == 5)
5158 magn_methy=3;
5159 }
5160
5161 /* magnify the rows into the right side of the large image */
5162
5163 if (logging != MagickFalse)
5164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5165 " Magnify the rows to %lu",large_image->rows);
5166 m=(long) mng_info->magn_mt;
5167 yy=0;
5168 length=(size_t) image->columns;
5169 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5170 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5171 if ((prev == (PixelPacket *) NULL) ||
5172 (next == (PixelPacket *) NULL))
5173 {
5174 image=DestroyImageList(image);
5175 MngInfoFreeStruct(mng_info,&have_mng_structure);
5176 ThrowReaderException(ResourceLimitError,
5177 "MemoryAllocationFailed");
5178 }
5179 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5180 (void) CopyMagickMemory(next,n,length);
5181 for (y=0; y < (long) image->rows; y++)
5182 {
5183 if (y == 0)
5184 m=(long) mng_info->magn_mt;
5185 else if (magn_methy > 1 && y == (long) image->rows-2)
5186 m=(long) mng_info->magn_mb;
5187 else if (magn_methy <= 1 && y == (long) image->rows-1)
5188 m=(long) mng_info->magn_mb;
5189 else if (magn_methy > 1 && y == (long) image->rows-1)
5190 m=1;
5191 else
5192 m=(long) mng_info->magn_my;
5193 n=prev;
5194 prev=next;
5195 next=n;
5196 if (y < (long) image->rows-1)
5197 {
5198 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5199 exception);
5200 (void) CopyMagickMemory(next,n,length);
5201 }
5202 for (i=0; i < m; i++, yy++)
5203 {
5204 register PixelPacket
5205 *pixels;
5206
5207 assert(yy < (long) large_image->rows);
5208 pixels=prev;
5209 n=next;
5210 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5211 1,exception);
5212 q+=(large_image->columns-image->columns);
5213 for (x=(long) image->columns-1; x >= 0; x--)
5214 {
5215 /* TO DO: get color as function of indices[x] */
5216 /*
5217 if (image->storage_class == PseudoClass)
5218 {
5219 }
5220 */
5221
5222 if (magn_methy <= 1)
5223 {
5224 *q=(*pixels); /* replicate previous */
5225 }
5226 else if (magn_methy == 2 || magn_methy == 4)
5227 {
5228 if (i == 0)
5229 *q=(*pixels);
5230 else
5231 {
5232 /* Interpolate */
5233 (*q).red=(QM) (((long) (2*i*((*n).red
5234 -(*pixels).red)+m))/((long) (m*2))
5235 +(*pixels).red);
5236 (*q).green=(QM) (((long) (2*i*((*n).green
5237 -(*pixels).green)+m))/((long) (m*2))
5238 +(*pixels).green);
5239 (*q).blue=(QM) (((long) (2*i*((*n).blue
5240 -(*pixels).blue)+m))/((long) (m*2))
5241 +(*pixels).blue);
5242 if (image->matte != MagickFalse)
5243 (*q).opacity=(QM) (((long)
5244 (2*i*((*n).opacity
5245 -(*pixels).opacity)+m))
5246 /((long) (m*2))+(*pixels).opacity);
5247 }
5248 if (magn_methy == 4)
5249 {
5250 /* Replicate nearest */
5251 if (i <= ((m+1) << 1))
5252 (*q).opacity=(*pixels).opacity+0;
5253 else
5254 (*q).opacity=(*n).opacity+0;
5255 }
5256 }
5257 else /* if (magn_methy == 3 || magn_methy == 5) */
5258 {
5259 /* Replicate nearest */
5260 if (i <= ((m+1) << 1))
5261 *q=(*pixels);
5262 else
5263 *q=(*n);
5264 if (magn_methy == 5)
5265 {
5266 (*q).opacity=(QM) (((long) (2*i*((*n).opacity
5267 -(*pixels).opacity)+m))/((long) (m*2))
5268 +(*pixels).opacity);
5269 }
5270 }
5271 n++;
5272 q++;
5273 pixels++;
5274 } /* x */
5275 if (SyncAuthenticPixels(large_image,exception) == 0)
5276 break;
5277 } /* i */
5278 } /* y */
5279 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5280 next=(PixelPacket *) RelinquishMagickMemory(next);
5281
5282 length=image->columns;
5283
5284 if (logging != MagickFalse)
5285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5286 " Delete original image");
5287
5288 DeleteImageFromList(&image);
5289
5290 image=large_image;
5291
5292 mng_info->image=image;
5293
5294 /* magnify the columns */
5295 if (logging != MagickFalse)
5296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5297 " Magnify the columns to %lu",image->columns);
5298
5299 for (y=0; y < (long) image->rows; y++)
5300 {
5301 register PixelPacket
5302 *pixels;
5303
5304 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5305 pixels=q+(image->columns-length);
5306 n=pixels+1;
5307 for (x=(long) (image->columns-length);
5308 x < (long) image->columns; x++)
5309 {
5310 if (x == (long) (image->columns-length))
5311 m=(long) mng_info->magn_ml;
5312 else if (magn_methx > 1 && x == (long) image->columns-2)
5313 m=(long) mng_info->magn_mr;
5314 else if (magn_methx <= 1 && x == (long) image->columns-1)
5315 m=(long) mng_info->magn_mr;
5316 else if (magn_methx > 1 && x == (long) image->columns-1)
5317 m=1;
5318 else
5319 m=(long) mng_info->magn_mx;
5320 for (i=0; i < m; i++)
5321 {
5322 if (magn_methx <= 1)
5323 {
5324 /* replicate previous */
5325 *q=(*pixels);
5326 }
5327 else if (magn_methx == 2 || magn_methx == 4)
5328 {
5329 if (i == 0)
5330 *q=(*pixels);
5331 else
5332 {
5333 /* Interpolate */
5334 (*q).red=(QM) ((2*i*((*n).red
5335 -(*pixels).red)+m)
5336 /((long) (m*2))+(*pixels).red);
5337 (*q).green=(QM) ((2*i*((*n).green
5338 -(*pixels).green)
5339 +m)/((long) (m*2))+(*pixels).green);
5340 (*q).blue=(QM) ((2*i*((*n).blue
5341 -(*pixels).blue)+m)
5342 /((long) (m*2))+(*pixels).blue);
5343 if (image->matte != MagickFalse)
5344 (*q).opacity=(QM) ((2*i*((*n).opacity
5345 -(*pixels).opacity)+m)/((long) (m*2))
5346 +(*pixels).opacity);
5347 }
5348 if (magn_methx == 4)
5349 {
5350 /* Replicate nearest */
5351 if (i <= ((m+1) << 1))
5352 (*q).opacity=(*pixels).opacity+0;
5353 else
5354 (*q).opacity=(*n).opacity+0;
5355 }
5356 }
5357 else /* if (magn_methx == 3 || magn_methx == 5) */
5358 {
5359 /* Replicate nearest */
5360 if (i <= ((m+1) << 1))
5361 *q=(*pixels);
5362 else
5363 *q=(*n);
5364 if (magn_methx == 5)
5365 {
5366 /* Interpolate */
5367 (*q).opacity=(QM) ((2*i*((*n).opacity
5368 -(*pixels).opacity)+m) /((long) (m*2))
5369 +(*pixels).opacity);
5370 }
5371 }
5372 q++;
5373 }
5374 n++;
5375 p++;
5376 }
5377 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5378 break;
5379 }
5380#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5381 if (magn_methx != 1 || magn_methy != 1)
5382 {
5383 /*
5384 Rescale pixels to Quantum
5385 */
5386 for (y=0; y < (long) image->rows; y++)
5387 {
5388 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5389 for (x=(long) image->columns-1; x >= 0; x--)
5390 {
5391 q->red=ScaleShortToQuantum(q->red);
5392 q->green=ScaleShortToQuantum(q->green);
5393 q->blue=ScaleShortToQuantum(q->blue);
5394 q->opacity=ScaleShortToQuantum(q->opacity);
5395 q++;
5396 }
5397 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5398 break;
5399 }
5400 }
5401#endif
5402 if (logging != MagickFalse)
5403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5404 " Finished MAGN processing");
5405 }
5406 }
5407
5408 /*
5409 Crop_box is with respect to the upper left corner of the MNG.
5410 */
5411 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5412 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5413 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5414 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5415 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5416 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5417 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5418 if ((crop_box.left != (mng_info->image_box.left
5419 +mng_info->x_off[object_id])) ||
5420 (crop_box.right != (mng_info->image_box.right
5421 +mng_info->x_off[object_id])) ||
5422 (crop_box.top != (mng_info->image_box.top
5423 +mng_info->y_off[object_id])) ||
5424 (crop_box.bottom != (mng_info->image_box.bottom
5425 +mng_info->y_off[object_id])))
5426 {
5427 if (logging != MagickFalse)
5428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5429 " Crop the PNG image");
5430 if ((crop_box.left < crop_box.right) &&
5431 (crop_box.top < crop_box.bottom))
5432 {
5433 Image
5434 *im;
5435
5436 RectangleInfo
5437 crop_info;
5438
5439 /*
5440 Crop_info is with respect to the upper left corner of
5441 the image.
5442 */
5443 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5444 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5445 crop_info.width=(unsigned long) (crop_box.right-crop_box.left);
5446 crop_info.height=(unsigned long) (crop_box.bottom-crop_box.top);
5447 image->page.width=image->columns;
5448 image->page.height=image->rows;
5449 image->page.x=0;
5450 image->page.y=0;
5451 im=CropImage(image,&crop_info,exception);
5452 if (im != (Image *) NULL)
5453 {
5454 image->columns=im->columns;
5455 image->rows=im->rows;
5456 im=DestroyImage(im);
5457 image->page.width=image->columns;
5458 image->page.height=image->rows;
5459 image->page.x=crop_box.left;
5460 image->page.y=crop_box.top;
5461 }
5462 }
5463 else
5464 {
5465 /*
5466 No pixels in crop area. The MNG spec still requires
5467 a layer, though, so make a single transparent pixel in
5468 the top left corner.
5469 */
5470 image->columns=1;
5471 image->rows=1;
5472 image->colors=2;
5473 (void) SetImageBackgroundColor(image);
5474 image->page.width=1;
5475 image->page.height=1;
5476 image->page.x=0;
5477 image->page.y=0;
5478 }
5479 }
5480#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5481 image=mng_info->image;
5482#endif
5483 }
5484
5485#if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5486 /* Determine if bit depth can be reduced from 16 to 8.
5487 * Note that the method GetImageDepth doesn't check background
5488 * and doesn't handle PseudoClass specially. Also it uses
5489 * multiplication and division by 257 instead of shifting, so
5490 * might be slower.
5491 */
5492 if (mng_info->optimize && image->depth == 16)
5493 {
5494 int
5495 ok_to_reduce;
5496
5497 const PixelPacket
5498 *p;
5499
5500 ok_to_reduce=(((((unsigned long) image->background_color.red >> 8) &
5501 0xff)
5502 == ((unsigned long) image->background_color.red & 0xff)) &&
5503 ((((unsigned long) image->background_color.green >> 8) & 0xff)
5504 == ((unsigned long) image->background_color.green & 0xff)) &&
5505 ((((unsigned long) image->background_color.blue >> 8) & 0xff)
5506 == ((unsigned long) image->background_color.blue & 0xff)));
5507 if (ok_to_reduce && image->storage_class == PseudoClass)
5508 {
5509 int indx;
5510
5511 for (indx=0; indx < (long) image->colors; indx++)
5512 {
5513 ok_to_reduce=(((((unsigned long) image->colormap[indx].red >>
5514 8) & 0xff)
5515 == ((unsigned long) image->colormap[indx].red & 0xff)) &&
5516 ((((unsigned long) image->colormap[indx].green >> 8) & 0xff)
5517 == ((unsigned long) image->colormap[indx].green & 0xff)) &&
5518 ((((unsigned long) image->colormap[indx].blue >> 8) & 0xff)
5519 == ((unsigned long) image->colormap[indx].blue & 0xff)));
5520 if (ok_to_reduce == MagickFalse)
5521 break;
5522 }
5523 }
5524 if ((ok_to_reduce != MagickFalse) &&
5525 (image->storage_class != PseudoClass))
5526 {
5527 long
5528 y;
5529
5530 register long
5531 x;
5532
5533 for (y=0; y < (long) image->rows; y++)
5534 {
5535 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5536 if (p == (const PixelPacket *) NULL)
5537 break;
5538 for (x=(long) image->columns-1; x >= 0; x--)
5539 {
5540 ok_to_reduce=((
5541 (((unsigned long) p->red >> 8) & 0xff) ==
5542 ((unsigned long) p->red & 0xff)) &&
5543 ((((unsigned long) p->green >> 8) & 0xff) ==
5544 ((unsigned long) p->green & 0xff)) &&
5545 ((((unsigned long) p->blue >> 8) & 0xff) ==
5546 ((unsigned long) p->blue & 0xff)) &&
5547 (((!image->matte ||
5548 (((unsigned long) p->opacity >> 8) & 0xff) ==
5549 ((unsigned long) p->opacity & 0xff)))));
5550 if (ok_to_reduce == 0)
5551 break;
5552 p++;
5553 }
5554 if (x != 0)
5555 break;
5556 }
5557 }
5558 if (ok_to_reduce)
5559 {
5560 image->depth=8;
5561 if (logging != MagickFalse)
5562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5563 " Reducing PNG bit depth to 8 without loss of info");
5564 }
5565 }
5566#endif
5567 GetImageException(image,exception);
5568 if (image_info->number_scenes != 0)
5569 {
5570 if (mng_info->scenes_found >
5571 (long) (image_info->first_scene+image_info->number_scenes))
5572 break;
5573 }
5574 if (logging != MagickFalse)
5575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5576 " Finished reading image datastream.");
5577 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5578 (void) CloseBlob(image);
5579 if (logging != MagickFalse)
5580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5581 " Finished reading all image datastreams.");
5582#if defined(MNG_INSERT_LAYERS)
5583 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5584 (mng_info->mng_height))
5585 {
5586 /*
5587 Insert a background layer if nothing else was found.
5588 */
5589 if (logging != MagickFalse)
5590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5591 " No images found. Inserting a background layer.");
5592 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5593 {
5594 /*
5595 Allocate next image structure.
5596 */
5597 AcquireNextImage(image_info,image);
5598 if (GetNextImageInList(image) == (Image *) NULL)
5599 {
5600 image=DestroyImageList(image);
5601 MngInfoFreeStruct(mng_info,&have_mng_structure);
5602 if (logging != MagickFalse)
5603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5604 " Allocation failed, returning NULL.");
5605 return((Image *) NULL);
5606 }
5607 image=SyncNextImageInList(image);
5608 }
5609 image->columns=mng_info->mng_width;
5610 image->rows=mng_info->mng_height;
5611 image->page.width=mng_info->mng_width;
5612 image->page.height=mng_info->mng_height;
5613 image->page.x=0;
5614 image->page.y=0;
5615 image->background_color=mng_background_color;
5616 image->matte=MagickFalse;
5617 if (image_info->ping == MagickFalse)
5618 (void) SetImageBackgroundColor(image);
5619 mng_info->image_found++;
5620 }
5621#endif
5622 image->iterations=mng_iterations;
5623 if (mng_iterations == 1)
5624 image->start_loop=MagickTrue;
5625 while (GetPreviousImageInList(image) != (Image *) NULL)
5626 {
5627 image_count++;
5628 if (image_count > 10*mng_info->image_found)
5629 {
5630 if (logging != MagickFalse)
5631 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5632 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5633 CoderError,"Linked list is corrupted, beginning of list not found",
5634 "`%s'",image_info->filename);
5635 return((Image *) NULL);
5636 }
5637 image=GetPreviousImageInList(image);
5638 if (GetNextImageInList(image) == (Image *) NULL)
5639 {
5640 if (logging != MagickFalse)
5641 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5642 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5643 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5644 image_info->filename);
5645 }
5646 }
5647 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5648 GetNextImageInList(image) ==
5649 (Image *) NULL)
5650 {
5651 if (logging != MagickFalse)
5652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653 " First image null");
5654 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5655 CoderError,"image->next for first image is NULL but shouldn't be.",
5656 "`%s'",image_info->filename);
5657 }
5658 if (mng_info->image_found == 0)
5659 {
5660 if (logging != MagickFalse)
5661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5662 " No visible images found.");
5663 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5664 CoderError,"No visible images in file","`%s'",image_info->filename);
5665 if (image != (Image *) NULL)
5666 image=DestroyImageList(image);
5667 MngInfoFreeStruct(mng_info,&have_mng_structure);
5668 return((Image *) NULL);
5669 }
5670
5671 if (mng_info->ticks_per_second)
5672 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5673 final_delay/mng_info->ticks_per_second;
5674 else
5675 image->start_loop=MagickTrue;
5676 /* Find final nonzero image delay */
5677 final_image_delay=0;
5678 while (GetNextImageInList(image) != (Image *) NULL)
5679 {
5680 if (image->delay)
5681 final_image_delay=image->delay;
5682 image=GetNextImageInList(image);
5683 }
5684 if (final_delay < final_image_delay)
5685 final_delay=final_image_delay;
5686 image->delay=final_delay;
5687 if (logging != MagickFalse)
5688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5689 " image->delay=%lu, final_delay=%lu",image->delay,final_delay);
5690 if (logging != MagickFalse)
5691 {
5692 int
5693 scene;
5694
5695 scene=0;
5696 image=GetFirstImageInList(image);
5697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5698 " Before coalesce:");
5699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5700 " scene 0 delay=%lu",image->delay);
5701 while (GetNextImageInList(image) != (Image *) NULL)
5702 {
5703 image=GetNextImageInList(image);
5704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5705 " scene %d delay=%lu",++scene,image->delay);
5706 }
5707 }
5708
5709 image=GetFirstImageInList(image);
5710#ifdef MNG_COALESCE_LAYERS
5711 if (insert_layers)
5712 {
5713 Image
5714 *next_image,
5715 *next;
5716
5717 unsigned long
5718 scene;
5719
5720 if (logging != MagickFalse)
5721 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5722 scene=image->scene;
5723 next_image=CoalesceImages(image,&image->exception);
5724 if (next_image == (Image *) NULL)
5725 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5726 image=DestroyImageList(image);
5727 image=next_image;
5728 for (next=image; next != (Image *) NULL; next=next_image)
5729 {
5730 next->page.width=mng_info->mng_width;
5731 next->page.height=mng_info->mng_height;
5732 next->page.x=0;
5733 next->page.y=0;
5734 next->scene=scene++;
5735 next_image=GetNextImageInList(next);
5736 if (next_image == (Image *) NULL)
5737 break;
5738 if (next->delay == 0)
5739 {
5740 scene--;
5741 next_image->previous=GetPreviousImageInList(next);
5742 if (GetPreviousImageInList(next) == (Image *) NULL)
5743 image=next_image;
5744 else
5745 next->previous->next=next_image;
5746 next=DestroyImage(next);
5747 }
5748 }
5749 }
5750#endif
5751
5752 while (GetNextImageInList(image) != (Image *) NULL)
5753 image=GetNextImageInList(image);
5754 image->dispose=BackgroundDispose;
5755
5756 if (logging != MagickFalse)
5757 {
5758 int
5759 scene;
5760
5761 scene=0;
5762 image=GetFirstImageInList(image);
5763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5764 " After coalesce:");
5765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5766 " scene 0 delay=%lu dispose=%d",image->delay,(int) image->dispose);
5767 while (GetNextImageInList(image) != (Image *) NULL)
5768 {
5769 image=GetNextImageInList(image);
5770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5771 " scene %d delay=%lu dispose=%d",++scene,
5772 image->delay,(int) image->dispose);
5773 }
5774 }
5775 image=GetFirstImageInList(image);
5776 MngInfoFreeStruct(mng_info,&have_mng_structure);
5777 have_mng_structure=MagickFalse;
5778 if (logging != MagickFalse)
5779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
5780 return(GetFirstImageInList(image));
5781}
5782#else /* PNG_LIBPNG_VER > 95 */
5783static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5784{
5785 printf("Your PNG library is too old: You have libpng-%s\n",
5786 PNG_LIBPNG_VER_STRING);
5787 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
5788 "PNG library is too old","`%s'",image_info->filename);
5789 return(Image *) NULL;
5790}
5791static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5792{
5793 return(ReadPNGImage(image_info,exception));
5794}
5795#endif /* PNG_LIBPNG_VER > 95 */
5796#endif
5797
5798/*
5799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5800% %
5801% %
5802% %
5803% R e g i s t e r P N G I m a g e %
5804% %
5805% %
5806% %
5807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5808%
5809% RegisterPNGImage() adds properties for the PNG image format to
5810% the list of supported formats. The properties include the image format
5811% tag, a method to read and/or write the format, whether the format
5812% supports the saving of more than one frame to the same file or blob,
5813% whether the format supports native in-memory I/O, and a brief
5814% description of the format.
5815%
5816% The format of the RegisterPNGImage method is:
5817%
5818% unsigned long RegisterPNGImage(void)
5819%
5820*/
5821ModuleExport unsigned long RegisterPNGImage(void)
5822{
5823 char
5824 version[MaxTextExtent];
5825
5826 MagickInfo
5827 *entry;
5828
5829 static const char
5830 *PNGNote=
5831 {
5832 "See http://www.libpng.org/ for details about the PNG format."
5833 },
5834 *JNGNote=
5835 {
5836 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
5837 "format."
5838 },
5839 *MNGNote=
5840 {
5841 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
5842 "format."
5843 };
5844
5845 *version='\0';
5846#if defined(PNG_LIBPNG_VER_STRING)
5847 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
5848 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
5849#if (PNG_LIBPNG_VER > 10005)
5850 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
5851 {
5852 (void) ConcatenateMagickString(version,",",MaxTextExtent);
5853 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
5854 MaxTextExtent);
5855 }
5856#endif
5857#endif
5858 entry=SetMagickInfo("MNG");
5859 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
5860#if defined(MAGICKCORE_PNG_DELEGATE)
5861 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
5862 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
5863#endif
5864 entry->magick=(IsImageFormatHandler *) IsMNG;
5865 entry->description=ConstantString("Multiple-image Network Graphics");
5866 if (*version != '\0')
5867 entry->version=ConstantString(version);
5868 entry->module=ConstantString("PNG");
5869 entry->note=ConstantString(MNGNote);
5870 (void) RegisterMagickInfo(entry);
5871
5872 entry=SetMagickInfo("PNG");
5873#if defined(MAGICKCORE_PNG_DELEGATE)
5874 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5875 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5876#endif
5877 entry->magick=(IsImageFormatHandler *) IsPNG;
5878 entry->adjoin=MagickFalse;
5879 entry->description=ConstantString("Portable Network Graphics");
5880 entry->module=ConstantString("PNG");
5881 if (*version != '\0')
5882 entry->version=ConstantString(version);
5883 entry->note=ConstantString(PNGNote);
5884 (void) RegisterMagickInfo(entry);
5885
5886 entry=SetMagickInfo("PNG8");
5887#if defined(MAGICKCORE_PNG_DELEGATE)
5888 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5889 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5890#endif
5891 entry->magick=(IsImageFormatHandler *) IsPNG;
5892 entry->adjoin=MagickFalse;
5893 entry->description=ConstantString(
5894 "8-bit indexed with optional binary transparency");
5895 entry->module=ConstantString("PNG");
5896 (void) RegisterMagickInfo(entry);
5897
5898 entry=SetMagickInfo("PNG24");
5899 *version='\0';
5900#if defined(ZLIB_VERSION)
5901 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
5902 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
5903 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
5904 {
5905 (void) ConcatenateMagickString(version,",",MaxTextExtent);
5906 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
5907 }
5908#endif
5909 if (*version != '\0')
5910 entry->version=ConstantString(version);
5911#if defined(MAGICKCORE_PNG_DELEGATE)
5912 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5913 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5914#endif
5915 entry->magick=(IsImageFormatHandler *) IsPNG;
5916 entry->adjoin=MagickFalse;
5917 entry->description=ConstantString("opaque 24-bit RGB");
5918 entry->module=ConstantString("PNG");
5919 (void) RegisterMagickInfo(entry);
5920
5921 entry=SetMagickInfo("PNG32");
5922#if defined(MAGICKCORE_PNG_DELEGATE)
5923 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5924 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5925#endif
5926 entry->magick=(IsImageFormatHandler *) IsPNG;
5927 entry->adjoin=MagickFalse;
5928 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
5929 entry->module=ConstantString("PNG");
5930 (void) RegisterMagickInfo(entry);
5931
5932 entry=SetMagickInfo("JNG");
5933#if defined(JNG_SUPPORTED)
5934#if defined(MAGICKCORE_PNG_DELEGATE)
5935 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
5936 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
5937#endif
5938#endif
5939 entry->magick=(IsImageFormatHandler *) IsJNG;
5940 entry->adjoin=MagickFalse;
5941 entry->description=ConstantString("JPEG Network Graphics");
5942 entry->module=ConstantString("PNG");
5943 entry->note=ConstantString(JNGNote);
5944 (void) RegisterMagickInfo(entry);
5945 return(MagickImageCoderSignature);
5946}
5947
5948/*
5949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5950% %
5951% %
5952% %
5953% U n r e g i s t e r P N G I m a g e %
5954% %
5955% %
5956% %
5957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5958%
5959% UnregisterPNGImage() removes format registrations made by the
5960% PNG module from the list of supported formats.
5961%
5962% The format of the UnregisterPNGImage method is:
5963%
5964% UnregisterPNGImage(void)
5965%
5966*/
5967ModuleExport void UnregisterPNGImage(void)
5968{
5969 (void) UnregisterMagickInfo("MNG");
5970 (void) UnregisterMagickInfo("PNG");
5971 (void) UnregisterMagickInfo("PNG8");
5972 (void) UnregisterMagickInfo("PNG24");
5973 (void) UnregisterMagickInfo("PNG32");
5974 (void) UnregisterMagickInfo("JNG");
5975#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
5976 AcquireSemaphoreInfo(&png_semaphore);
5977 RelinquishSemaphoreInfo(png_semaphore);
5978 DestroySemaphoreInfo(&png_semaphore);
5979#endif
5980}
5981
5982#if defined(MAGICKCORE_PNG_DELEGATE)
5983#if PNG_LIBPNG_VER > 95
5984/*
5985%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5986% %
5987% %
5988% %
5989% W r i t e M N G I m a g e %
5990% %
5991% %
5992% %
5993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5994%
5995% WriteMNGImage() writes an image in the Portable Network Graphics
5996% Group's "Multiple-image Network Graphics" encoded image format.
5997%
5998% MNG support written by Glenn Randers-Pehrson, glennrp@image...
5999%
6000% The format of the WriteMNGImage method is:
6001%
6002% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6003%
6004% A description of each parameter follows.
6005%
6006% o image_info: the image info.
6007%
6008% o image: The image.
6009%
6010%
6011% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6012% "To do" under ReadPNGImage):
6013%
6014% Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6015% some GIF animations don't convert properly)
6016%
6017% Preserve all unknown and not-yet-handled known chunks found in input
6018% PNG file and copy them into output PNG files according to the PNG
6019% copying rules.
6020%
6021% Write the iCCP chunk at MNG level when (icc profile length > 0)
6022%
6023% Improve selection of color type (use indexed-colour or indexed-colour
6024% with tRNS when 256 or fewer unique RGBA values are present).
6025%
6026% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6027% This will be complicated if we limit ourselves to generating MNG-LC
6028% files. For now we ignore disposal method 3 and simply overlay the next
6029% image on it.
6030%
6031% Check for identical PLTE's or PLTE/tRNS combinations and use a
6032% global MNG PLTE or PLTE/tRNS combination when appropriate.
6033% [mostly done 15 June 1999 but still need to take care of tRNS]
6034%
6035% Check for identical sRGB and replace with a global sRGB (and remove
6036% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6037% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6038% local gAMA/cHRM with local sRGB if appropriate).
6039%
6040% Check for identical sBIT chunks and write global ones.
6041%
6042% Provide option to skip writing the signature tEXt chunks.
6043%
6044% Use signatures to detect identical objects and reuse the first
6045% instance of such objects instead of writing duplicate objects.
6046%
6047% Use a smaller-than-32k value of compression window size when
6048% appropriate.
6049%
6050% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6051% ancillary text chunks and save profiles.
6052%
6053% Provide an option to force LC files (to ensure exact framing rate)
6054% instead of VLC.
6055%
6056% Provide an option to force VLC files instead of LC, even when offsets
6057% are present. This will involve expanding the embedded images with a
6058% transparent region at the top and/or left.
6059*/
6060
6061#if (PNG_LIBPNG_VER > 99 && PNG_LIBPNG_VER < 10007)
6062/* This function became available in libpng version 1.0.6g. */
6063static void
6064png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
6065{
6066 if (png_ptr->zbuf)
6067 png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf=NULL;
6068 png_ptr->zbuf_size=(png_size_t) size;
6069 png_ptr->zbuf=(png_bytep) png_malloc(png_ptr, size);
6070 if (png_ptr->zbuf == 0)
6071 png_error(png_ptr,"Unable to allocate zbuf");
6072}
6073#endif
6074
6075static void
6076png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6077 png_info *ping_info, unsigned char *profile_type, unsigned char
6078 *profile_description, unsigned char *profile_data, png_uint_32 length)
6079{
6080#if (PNG_LIBPNG_VER > 10005)
6081 png_textp
6082 text;
6083
6084 register long
6085 i;
6086
6087 unsigned char
6088 *sp;
6089
6090 png_charp
6091 dp;
6092
6093 png_uint_32
6094 allocated_length,
6095 description_length;
6096
6097 unsigned char
6098 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6099#endif
6100
6101#if (PNG_LIBPNG_VER <= 10005)
6102 if (image_info->verbose)
6103 (void) printf("Not ");
6104 image_info=image_info;
6105 ping=ping;
6106 ping_info=ping_info;
6107 profile_type=profile_type;
6108 profile_description=profile_description;
6109 profile_data=profile_data;
6110 length=length;
6111#endif
6112 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6113 return;
6114
6115 if (image_info->verbose)
6116 {
6117 (void) printf("writing raw profile: type=%s, length=%lu\n",
6118 (char *) profile_type, length);
6119 }
6120#if (PNG_LIBPNG_VER > 10005)
6121 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6122 description_length=(png_uint_32) strlen((const char *) profile_description);
6123 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6124 + description_length);
6125 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6126 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6127 text[0].key[0]='\0';
6128 (void) ConcatenateMagickString(text[0].key,
6129 "Raw profile type ",MaxTextExtent);
6130 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6131 sp=profile_data;
6132 dp=text[0].text;
6133 *dp++='\n';
6134 (void) CopyMagickString(dp,(const char *) profile_description,
6135 allocated_length);
6136 dp+=description_length;
6137 *dp++='\n';
6138 (void) FormatMagickString(dp,allocated_length-
6139 (png_size_t) (dp-text[0].text),"%8lu ",length);
6140 dp+=8;
6141 for (i=0; i < (long) length; i++)
6142 {
6143 if (i%36 == 0)
6144 *dp++='\n';
6145 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6146 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6147 }
6148 *dp++='\n';
6149 *dp='\0';
6150 text[0].text_length=(png_size_t) (dp-text[0].text);
6151 text[0].compression=image_info->compression == NoCompression ||
6152 (image_info->compression == UndefinedCompression &&
6153 text[0].text_length < 128) ? -1 : 0;
6154 if (text[0].text_length <= allocated_length)
6155 png_set_text(ping,ping_info,text,1);
6156 png_free(ping,text[0].text);
6157 png_free(ping,text[0].key);
6158 png_free(ping,text);
6159#endif
6160}
6161
6162static MagickBooleanType png_write_chunk_from_profile(Image *image,
6163 const char *string, int logging)
6164{
6165 char
6166 *name;
6167
6168 const StringInfo
6169 *profile;
6170
6171 unsigned char
6172 *data;
6173
6174 png_uint_32 length;
6175
6176 ResetImageProfileIterator(image);
6177 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6178 profile=GetImageProfile(image,name);
6179 if (profile != (const StringInfo *) NULL)
6180 {
6181 StringInfo
6182 *png_profile;
6183
6184 if (LocaleNCompare(name,string,11) == 0) {
6185 if (logging != MagickFalse)
6186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6187 " Found %s profile",name);
6188
6189 png_profile=CloneStringInfo(profile);
6190 data=GetStringInfoDatum(png_profile),
6191 length=(png_uint_32) GetStringInfoLength(png_profile);
6192 data[4]=data[3];
6193 data[3]=data[2];
6194 data[2]=data[1];
6195 data[1]=data[0];
6196 (void) WriteBlobMSBULong(image,length-5); /* data length */
6197 (void) WriteBlob(image,length-1,data+1);
6198 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6199 png_profile=DestroyStringInfo(png_profile);
6200 }
6201 }
6202 name=GetNextImageProfile(image);
6203 }
6204 return(MagickTrue);
6205}
6206
6207static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6208 const ImageInfo *image_info,Image *image)
6209{
6210/* Write one PNG image */
6211 char
6212 s[2];
6213
6214 const char
6215 *name,
6216 *property,
6217 *value;
6218
6219 const StringInfo
6220 *profile;
6221
6222
6223 int
6224 image_matte,
6225 num_passes,
6226 pass;
6227
6228 png_colorp
6229 palette;
6230
6231 png_info
6232 *ping_info;
6233
6234 png_struct
6235 *ping;
6236
6237 long
6238 y;
6239
6240 MagickBooleanType
6241 status;
6242
6243 QuantumInfo
6244 *quantum_info;
6245
6246 register IndexPacket
6247 *indices;
6248
6249 register long
6250 i,
6251 x;
6252
6253 unsigned char
6254 *png_pixels;
6255
6256 unsigned int
6257 logging,
6258 matte;
6259
6260 volatile unsigned long
6261 image_colors,
6262 image_depth;
6263
6264 unsigned long
6265 old_bit_depth,
6266 quality,
6267 rowbytes,
6268 save_image_depth;
6269
6270 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6271 " enter WriteOnePNGImage()");
6272
6273#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6274 AcquireSemaphoreInfo(&png_semaphore);
6275#endif
6276
6277 image_colors=image->colors;
6278 image_depth=image->depth;
6279 image_matte=image->matte;
6280
6281 if (image->colorspace != RGBColorspace)
6282 (void) TransformImageColorspace(image,RGBColorspace);
6283 mng_info->IsPalette=image->storage_class == PseudoClass &&
6284 image_colors <= 256;
6285 mng_info->optimize=image_info->type == OptimizeType;
6286
6287 /*
6288 Allocate the PNG structures
6289 */
6290#ifdef PNG_USER_MEM_SUPPORTED
6291 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6292 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6293 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6294#else
6295 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6296 PNGErrorHandler,PNGWarningHandler);
6297#endif
6298 if (ping == (png_struct *) NULL)
6299 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6300 ping_info=png_create_info_struct(ping);
6301 if (ping_info == (png_info *) NULL)
6302 {
6303 png_destroy_write_struct(&ping,(png_info **) NULL);
6304 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6305 }
6306 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6307 png_pixels=(unsigned char *) NULL;
6308
6309 if (setjmp(ping->jmpbuf))
6310 {
6311 /*
6312 PNG write failed.
6313 */
6314#ifdef PNG_DEBUG
6315 if (image_info->verbose)
6316 (void) printf("PNG write has failed.\n");
6317#endif
6318 png_destroy_write_struct(&ping,&ping_info);
6319#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6320 RelinquishSemaphoreInfo(png_semaphore);
6321#endif
6322 return(MagickFalse);
6323 }
6324 /*
6325 Prepare PNG for writing.
6326 */
6327#if defined(PNG_MNG_FEATURES_SUPPORTED)
6328 if (mng_info->write_mng)
6329 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6330#else
6331# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6332 if (mng_info->write_mng)
6333 png_permit_empty_plte(ping,MagickTrue);
6334# endif
6335#endif
6336 x=0;
6337 ping_info->width=image->columns;
6338 ping_info->height=image->rows;
6339 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6340 image_depth=8;
6341 if (mng_info->write_png_depth != 0)
6342 image_depth=mng_info->write_png_depth;
6343 /* Adjust requested depth to next higher valid depth if necessary */
6344 if (image_depth > 8)
6345 image_depth=16;
6346 if ((image_depth > 4) && (image_depth < 8))
6347 image_depth=8;
6348 if (image_depth == 3)
6349 image_depth=4;
6350 if (logging != MagickFalse)
6351 {
6352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6353 " width=%lu",ping_info->width);
6354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6355 " height=%lu",ping_info->height);
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6357 " image_matte=%u",image->matte);
6358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6359 " image_depth=%lu",image->depth);
6360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6361 " requested PNG image_depth=%lu",image->depth);
6362 }
6363 save_image_depth=image_depth;
6364 ping_info->bit_depth=(png_byte) save_image_depth;
6365#if defined(PNG_pHYs_SUPPORTED)
6366 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6367 (!mng_info->write_mng || !mng_info->equal_physs))
6368 {
6369 int
6370 unit_type;
6371
6372 png_uint_32
6373 x_resolution,
6374 y_resolution;
6375
6376 if (image->units == PixelsPerInchResolution)
6377 {
6378 unit_type=PNG_RESOLUTION_METER;
6379 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6380 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6381 }
6382 else if (image->units == PixelsPerCentimeterResolution)
6383 {
6384 unit_type=PNG_RESOLUTION_METER;
6385 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6386 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6387 }
6388 else
6389 {
6390 unit_type=PNG_RESOLUTION_UNKNOWN;
6391 x_resolution=(png_uint_32) image->x_resolution;
6392 y_resolution=(png_uint_32) image->y_resolution;
6393 }
6394 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6395 if (logging != MagickFalse)
6396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6397 " Setting up pHYs chunk");
6398 }
6399#endif
6400#if defined(PNG_oFFs_SUPPORTED)
6401 if (image->page.x || image->page.y)
6402 {
6403 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6404 (png_int_32) image->page.y, 0);
6405 if (logging != MagickFalse)
6406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6407 " Setting up oFFs chunk");
6408 }
6409#endif
6410 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6411 {
6412 png_color_16
6413 background;
6414
6415 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6416 {
6417 unsigned long
6418 maxval;
6419
6420 maxval=(1UL << image_depth)-1;
6421 background.red=(png_uint_16)
6422 (QuantumScale*(maxval*image->background_color.red));
6423 background.green=(png_uint_16)
6424 (QuantumScale*(maxval*image->background_color.green));
6425 background.blue=(png_uint_16)
6426 (QuantumScale*(maxval*image->background_color.blue));
6427 background.gray=(png_uint_16)
6428 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6429 }
6430 else
6431 {
6432 background.red=image->background_color.red;
6433 background.green=image->background_color.green;
6434 background.blue=image->background_color.blue;
6435 background.gray=
6436 (png_uint_16) PixelIntensity(&image->background_color);
6437 }
6438 background.index=(png_byte) background.gray;
6439 if (logging != MagickFalse)
6440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6441 " Setting up bKGd chunk");
6442 png_set_bKGD(ping,ping_info,&background);
6443 }
6444 /*
6445 Select the color type.
6446 */
6447 matte=image_matte;
6448 old_bit_depth=0;
6449 if (mng_info->write_png8)
6450 {
6451 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6452 ping_info->bit_depth=8;
6453 image_depth=ping_info->bit_depth;
6454 {
6455 /* TO DO: make this a function cause it's used twice, except
6456 for reducing the sample depth from 8. */
6457
6458 QuantizeInfo
6459 quantize_info;
6460
6461 unsigned long
6462 number_colors,
6463 save_number_colors;
6464
6465 number_colors=image_colors;
6466 if ((image->storage_class == DirectClass) || (number_colors > 256))
6467 {
6468 GetQuantizeInfo(&quantize_info);
6469 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6470 MagickFalse ? MagickTrue : MagickFalse;
6471 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6472 256UL);
6473 (void) QuantizeImage(&quantize_info,image);
6474 number_colors=image_colors;
6475 (void) SyncImage(image);
6476 if (logging != MagickFalse)
6477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6478 " Colors quantized to %ld",number_colors);
6479 }
6480 if (matte)
6481 ping_info->valid|=PNG_INFO_tRNS;
6482 /*
6483 Set image palette.
6484 */
6485 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6486 ping_info->valid|=PNG_INFO_PLTE;
6487#if defined(PNG_SORT_PALETTE)
6488 save_number_colors=image_colors;
6489 if (CompressColormapTransFirst(image) == MagickFalse)
6490 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6491 number_colors=image_colors;
6492 image_colors=save_number_colors;
6493#endif
6494 palette=(png_color *) AcquireQuantumMemory(257,
6495 sizeof(*palette));
6496 if (palette == (png_color *) NULL)
6497 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6498 if (logging != MagickFalse)
6499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6500 " Setting up PLTE chunk");
6501 for (i=0; i < (long) number_colors; i++)
6502 {
6503 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6504 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6505 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6506 if (logging != MagickFalse)
6507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6508#if MAGICKCORE_QUANTUM_DEPTH == 8
6509 " %3ld (%3d,%3d,%3d)",
6510#else
6511 " %5ld (%5d,%5d,%5d)",
6512#endif
6513 i,palette[i].red,palette[i].green,palette[i].blue);
6514
6515 }
6516 if (matte)
6517 {
6518 number_colors++;
6519 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6520 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6521 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6522 }
6523 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6524#if (PNG_LIBPNG_VER > 10008)
6525 palette=(png_colorp) RelinquishMagickMemory(palette);
6526#endif
6527 image_depth=ping_info->bit_depth;
6528 ping_info->num_trans=0;
6529 if (matte)
6530 {
6531 ExceptionInfo
6532 *exception;
6533
6534 /*
6535 Identify which colormap entry is transparent.
6536 */
6537 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
6538 number_colors,sizeof(*ping_info->trans_alpha));
6539 if (ping_info->trans == (unsigned char *) NULL)
6540 ThrowWriterException(ResourceLimitError,
6541 "MemoryAllocationFailed");
6542 assert(number_colors <= 256);
6543 for (i=0; i < (long) number_colors; i++)
6544 ping_info->trans_alpha[i]=255;
6545 exception=(&image->exception);
6546 for (y=0; y < (long) image->rows; y++)
6547 {
6548 register const PixelPacket
6549 *p;
6550
6551 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6552 if (p == (PixelPacket *) NULL)
6553 break;
6554 indices=GetAuthenticIndexQueue(image);
6555 for (x=0; x < (long) image->columns; x++)
6556 {
6557 if (p->opacity != OpaqueOpacity)
6558 {
6559 indices[x]=(IndexPacket) (number_colors-1);
6560 ping_info->trans_alpha[(long) indices[x]]=(png_byte) (255-
6561 ScaleQuantumToChar(p->opacity));
6562 }
6563 p++;
6564 }
6565 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6566 break;
6567 }
6568 for (i=0; i < (long) number_colors; i++)
6569 if (ping_info->trans_alpha[i] != 255)
6570 ping_info->num_trans=(unsigned short) (i+1);
6571 if (ping_info->num_trans == 0)
6572 ping_info->valid&=(~PNG_INFO_tRNS);
6573 if (!(ping_info->valid & PNG_INFO_tRNS))
6574 ping_info->num_trans=0;
6575 if (ping_info->num_trans == 0)
6576 ping_info->trans_alpha=(unsigned char *)
6577 RelinquishMagickMemory(ping_info->trans_alpha);
6578 }
6579 /*
6580 Identify which colormap entry is the background color.
6581 */
6582 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
6583 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
6584 break;
6585 ping_info->background.index=(png_byte) i;
6586 }
6587 if (image_matte != MagickFalse)
6588 {
6589 /* TO DO: reduce to binary transparency */
6590 }
6591 } /* end of write_png8 */
6592 else if (mng_info->write_png24)
6593 {
6594 image_matte=MagickFalse;
6595 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6596 }
6597 else if (mng_info->write_png32)
6598 {
6599 image_matte=MagickTrue;
6600 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6601 }
6602 else
6603 {
6604 image_depth=ping_info->bit_depth;
6605 if (mng_info->write_png_colortype)
6606 {
6607 ping_info->color_type=(png_byte) mng_info->write_png_colortype-1;
6608 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6609 ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6610 image_matte=MagickTrue;
6611 }
6612 else
6613 {
6614 if (logging != MagickFalse)
6615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6616 "Selecting PNG colortype");
6617 ping_info->color_type=(png_byte) ((matte == MagickTrue)?
6618 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6619 if(image_info->type == TrueColorType)
6620 {
6621 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6622 image_matte=MagickFalse;
6623 }
6624 if(image_info->type == TrueColorMatteType)
6625 {
6626 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6627 image_matte=MagickTrue;
6628 }
6629 if ((image_info->type == UndefinedType ||
6630 image_info->type == OptimizeType ||
6631 image_info->type == GrayscaleType) &&
6632 image_matte == MagickFalse && ImageIsGray(image))
6633 {
6634 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6635 image_matte=MagickFalse;
6636 }
6637 if ((image_info->type == UndefinedType ||
6638 image_info->type == OptimizeType ||
6639 image_info->type == GrayscaleMatteType) &&
6640 image_matte == MagickTrue && ImageIsGray(image))
6641 {
6642 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6643 image_matte=MagickTrue;
6644 }
6645 }
6646 if (logging != MagickFalse)
6647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6648 "Selected PNG colortype=%d",ping_info->color_type);
6649
6650 if (ping_info->bit_depth < 8)
6651 {
6652 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6653 ping_info->color_type == PNG_COLOR_TYPE_RGB ||
6654 ping_info->color_type == PNG_COLOR_TYPE_RGBA)
6655 ping_info->bit_depth=8;
6656 }
6657
6658 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
6659 {
6660 if (image->matte == MagickFalse && image->colors < 256)
6661 {
6662 if (ImageIsMonochrome(image))
6663 {
6664 ping_info->bit_depth=1;
6665 if (ping_info->bit_depth < mng_info->write_png_depth)
6666 ping_info->bit_depth = mng_info->write_png_depth;
6667 }
6668 }
6669 }
6670 if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6671 {
6672 ping_info->bit_depth=1;
6673 while ((int) (1 << ping_info->bit_depth) < (long) image_colors)
6674 ping_info->bit_depth <<= 1;
6675
6676 if (logging != MagickFalse)
6677 {
6678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6679 " Number of colors: %lu",image_colors);
6680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6681 " Tentative PNG bit depth: %d",ping_info->bit_depth);
6682 }
6683 if (mng_info->write_png_depth)
6684 {
6685 old_bit_depth=ping_info->bit_depth;
6686 if (ping_info->bit_depth < mng_info->write_png_depth)
6687 {
6688 ping_info->bit_depth = mng_info->write_png_depth;
6689 if (ping_info->bit_depth > 8)
6690 ping_info->bit_depth = 8;
6691 if (ping_info->bit_depth != old_bit_depth)
6692 {
6693 if (logging != MagickFalse)
6694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6695 " Colors increased to %ld",image_colors);
6696 }
6697 }
6698 }
6699 }
6700 }
6701 image_depth=ping_info->bit_depth;
6702 if (logging != MagickFalse)
6703 {
6704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6705 " Tentative PNG color type: %d",ping_info->color_type);
6706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6707 " image_info->type: %d",image_info->type);
6708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6709 " image_depth: %lu",image_depth);
6710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6711 " ping_info->bit_depth: %d",ping_info->bit_depth);
6712 }
6713
6714 if (matte && (mng_info->optimize || mng_info->IsPalette))
6715 {
6716 register const PixelPacket
6717 *p;
6718
6719 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
6720 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6721 for (y=0; y < (long) image->rows; y++)
6722 {
6723 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6724 if (p == (const PixelPacket *) NULL)
6725 break;
6726 for (x=(long) image->columns-1; x >= 0; x--)
6727 {
6728 if (IsGray(p) == MagickFalse)
6729 {
6730 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6731 break;
6732 }
6733 p++;
6734 }
6735 }
6736 /*
6737 Determine if there is any transparent color.
6738 */
6739 for (y=0; y < (long) image->rows; y++)
6740 {
6741 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6742 if (p == (const PixelPacket *) NULL)
6743 break;
6744 for (x=(long) image->columns-1; x >= 0; x--)
6745 {
6746 if (p->opacity != OpaqueOpacity)
6747 break;
6748 p++;
6749 }
6750 if (x != 0)
6751 break;
6752 }
6753 if ((y == (long) image->rows) && (x == (long) image->columns))
6754 {
6755 /*
6756 No transparent pixels are present. Change 4 or 6 to 0 or 2,
6757 and do not set the PNG_INFO_tRNS flag in ping_info->valid.
6758 */
6759 image_matte=MagickFalse;
6760 ping_info->color_type&=0x03;
6761 }
6762 else
6763 {
6764 unsigned int
6765 mask;
6766
6767 mask=0xffff;
6768 if (ping_info->bit_depth == 8)
6769 mask=0x00ff;
6770 if (ping_info->bit_depth == 4)
6771 mask=0x000f;
6772 if (ping_info->bit_depth == 2)
6773 mask=0x0003;
6774 if (ping_info->bit_depth == 1)
6775 mask=0x0001;
6776 ping_info->valid|=PNG_INFO_tRNS;
6777 ping_info->trans_color.red=(png_uint_16)
6778 (ScaleQuantumToShort(p->red) & mask);
6779 ping_info->trans_color.green=(png_uint_16)
6780 (ScaleQuantumToShort(p->green) & mask);
6781 ping_info->trans_color.blue=(png_uint_16)
6782 (ScaleQuantumToShort(p->blue) & mask);
6783 ping_info->trans_color.gray=(png_uint_16)
6784 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
6785 ping_info->trans_color.index=(png_byte)
6786 (ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity)));
6787 }
6788 if (ping_info->valid & PNG_INFO_tRNS)
6789 {
6790 /*
6791 Determine if there is one and only one transparent color
6792 and if so if it is fully transparent.
6793 */
6794 for (y=0; y < (long) image->rows; y++)
6795 {
6796 p=GetVirtualPixels(image,0,y,image->columns,1,
6797 &image->exception);
6798 x=0;
6799 if (p == (const PixelPacket *) NULL)
6800 break;
6801 for (x=(long) image->columns-1; x >= 0; x--)
6802 {
6803 if (p->opacity != OpaqueOpacity)
6804 {
6805 if (IsPNGColorEqual(ping_info->trans_color,*p) == 0)
6806 {
6807 break; /* Can't use RGB + tRNS for multiple
6808 transparent colors. */
6809 }
6810 if (p->opacity != (Quantum) TransparentOpacity)
6811 {
6812 break; /* Can't use RGB + tRNS for
6813 semitransparency. */
6814 }
6815 }
6816 else
6817 {
6818 if (IsPNGColorEqual(ping_info->trans_color,*p))
6819 break; /* Can't use RGB + tRNS when another pixel
6820 having the same RGB samples is
6821 transparent. */
6822 }
6823 p++;
6824 }
6825 if (x != 0)
6826 break;
6827 }
6828 if (x != 0)
6829 ping_info->valid&=(~PNG_INFO_tRNS);
6830 }
6831 if (ping_info->valid & PNG_INFO_tRNS)
6832 {
6833 ping_info->color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
6834 if (image_depth == 8)
6835 {
6836 ping_info->trans_color.red&=0xff;
6837 ping_info->trans_color.green&=0xff;
6838 ping_info->trans_color.blue&=0xff;
6839 ping_info->trans_color.gray&=0xff;
6840 }
6841 }
6842 }
6843 matte=image_matte;
6844 if (ping_info->valid & PNG_INFO_tRNS)
6845 image_matte=MagickFalse;
6846 if ((mng_info->optimize || mng_info->IsPalette) &&
6847 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
6848 ImageIsGray(image) && (!image_matte || image_depth >= 8))
6849 {
6850 if (image_matte != MagickFalse)
6851 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6852 else
6853 {
6854 ping_info->color_type=PNG_COLOR_TYPE_GRAY;
6855 if (save_image_depth == 16 && image_depth == 8)
6856 ping_info->trans_color.gray*=0x0101;
6857 }
6858 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
6859 image_depth=MAGICKCORE_QUANTUM_DEPTH;
6860 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
6861 image_colors=1 << image_depth;
6862 if (image_depth > 8)
6863 ping_info->bit_depth=16;
6864 else
6865 {
6866 ping_info->bit_depth=8;
6867 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6868 {
6869 if(!mng_info->write_png_depth)
6870 {
6871 ping_info->bit_depth=1;
6872 while ((int) (1 << ping_info->bit_depth)
6873 < (long) image_colors)
6874 ping_info->bit_depth <<= 1;
6875 }
6876 }
6877 else if (mng_info->optimize && ping_info->color_type ==
6878 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
6879 mng_info->IsPalette)
6880 {
6881
6882 /* Check if grayscale is reducible */
6883 int
6884 depth_4_ok=MagickTrue,
6885 depth_2_ok=MagickTrue,
6886 depth_1_ok=MagickTrue;
6887
6888 for (i=0; i < (long) image_colors; i++)
6889 {
6890 unsigned char
6891 intensity;
6892
6893 intensity=ScaleQuantumToChar(image->colormap[i].red);
6894
6895 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
6896 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
6897 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
6898 depth_2_ok=depth_1_ok=MagickFalse;
6899 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
6900 depth_1_ok=MagickFalse;
6901 }
6902 if (depth_1_ok && mng_info->write_png_depth <= 1)
6903 ping_info->bit_depth=1;
6904 else if (depth_2_ok && mng_info->write_png_depth <= 2)
6905 ping_info->bit_depth=2;
6906 else if (depth_4_ok && mng_info->write_png_depth <= 4)
6907 ping_info->bit_depth=4;
6908 }
6909 }
6910 image_depth=ping_info->bit_depth;
6911 }
6912 else
6913 if (mng_info->IsPalette)
6914 {
6915 if (image_depth <= 8)
6916 {
6917 unsigned long
6918 number_colors;
6919
6920 number_colors=image_colors;
6921 if (matte)
6922 ping_info->valid|=PNG_INFO_tRNS;
6923 /*
6924 Set image palette.
6925 */
6926 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6927 ping_info->valid|=PNG_INFO_PLTE;
6928 if (mng_info->have_write_global_plte && !matte)
6929 {
6930 png_set_PLTE(ping,ping_info,NULL,0);
6931 if (logging)
6932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6933 " Setting up empty PLTE chunk");
6934 }
6935 else
6936 {
6937#if defined(PNG_SORT_PALETTE)
6938 unsigned long
6939 save_number_colors;
6940
6941 if (mng_info->optimize)
6942 {
6943 save_number_colors=image_colors;
6944 if (CompressColormapTransFirst(image) == MagickFalse)
6945 ThrowWriterException(ResourceLimitError,
6946 "MemoryAllocationFailed");
6947 number_colors=image_colors;
6948 image_colors=save_number_colors;
6949 }
6950#endif
6951 palette=(png_color *) AcquireQuantumMemory(257,
6952 sizeof(*palette));
6953 if (palette == (png_color *) NULL)
6954 ThrowWriterException(ResourceLimitError,
6955 "MemoryAllocationFailed");
6956 for (i=0; i < (long) number_colors; i++)
6957 {
6958 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6959 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6960 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6961 }
6962 if (logging)
6963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6964 " Setting up PLTE chunk");
6965 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6966#if (PNG_LIBPNG_VER > 10008)
6967 palette=(png_colorp) RelinquishMagickMemory(palette);
6968#endif
6969 }
6970 /* color_type is PNG_COLOR_TYPE_PALETTE */
6971 if (!mng_info->write_png_depth)
6972 {
6973 ping_info->bit_depth=1;
6974 while ((1UL << ping_info->bit_depth) < number_colors)
6975 ping_info->bit_depth <<= 1;
6976 }
6977 ping_info->num_trans=0;
6978 if (matte)
6979 {
6980 ExceptionInfo
6981 *exception;
6982
6983 register const PixelPacket
6984 *p;
6985
6986 int
6987 trans[256];
6988
6989 register const IndexPacket
6990 *packet_indices;
6991
6992 /*
6993 Identify which colormap entry is transparent.
6994 */
6995 assert(number_colors <= 256);
6996 for (i=0; i < (long) number_colors; i++)
6997 trans[i]=256;
6998 exception=(&image->exception);
6999 for (y=0; y < (long) image->rows; y++)
7000 {
7001 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7002 if (p == (const PixelPacket *) NULL)
7003 break;
7004 packet_indices=GetVirtualIndexQueue(image);
7005 for (x=0; x < (long) image->columns; x++)
7006 {
7007 if (p->opacity != OpaqueOpacity)
7008 {
7009 IndexPacket
7010 packet_index;
7011
7012 packet_index=packet_indices[x];
7013 assert((unsigned long) packet_index < number_colors);
7014 if (trans[(long) packet_index] != 256)
7015 {
7016 if (trans[(long) packet_index] != (png_byte) (255-
7017 ScaleQuantumToChar(p->opacity)))
7018 {
7019 ping_info->color_type=(png_byte)
7020 PNG_COLOR_TYPE_RGB_ALPHA;
7021 break;
7022 }
7023 }
7024 trans[(long) packet_index]=(png_byte) (255-
7025 ScaleQuantumToChar(p->opacity));
7026 }
7027 p++;
7028 }
7029 if ((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7030 {
7031 ping_info->num_trans=0;
7032 ping_info->valid&=(~PNG_INFO_tRNS);
7033 ping_info->valid&=(~PNG_INFO_PLTE);
7034 mng_info->IsPalette=MagickFalse;
7035 (void) SyncImage(image);
7036 if (logging)
7037 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7038 " Cannot write image as indexed PNG, writing RGBA.");
7039 break;
7040 }
7041 }
7042 if ((ping_info->valid & PNG_INFO_tRNS))
7043 {
7044 for (i=0; i < (long) number_colors; i++)
7045 {
7046 if (trans[i] == 256)
7047 trans[i]=255;
7048 if (trans[i] != 255)
7049 ping_info->num_trans=(unsigned short) (i+1);
7050 }
7051 }
7052 if (ping_info->num_trans == 0)
7053 ping_info->valid&=(~PNG_INFO_tRNS);
7054 if (!(ping_info->valid & PNG_INFO_tRNS))
7055 ping_info->num_trans=0;
7056 if (ping_info->num_trans != 0)
7057 {
7058 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
7059 number_colors,sizeof(*ping_info->trans_alpha));
7060 if (ping_info->trans_alpha == (unsigned char *) NULL)
7061 ThrowWriterException(ResourceLimitError,
7062 "MemoryAllocationFailed");
7063 for (i=0; i < (long) number_colors; i++)
7064 ping_info->trans_alpha[i]=(png_byte) trans[i];
7065 }
7066 }
7067
7068 /*
7069 Identify which colormap entry is the background color.
7070 */
7071 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
7072 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
7073 break;
7074 ping_info->background.index=(png_byte) i;
7075 }
7076 }
7077 else
7078 {
7079 if (image_depth < 8)
7080 image_depth=8;
7081 if ((save_image_depth == 16) && (image_depth == 8))
7082 {
7083 ping_info->trans_color.red*=0x0101;
7084 ping_info->trans_color.green*=0x0101;
7085 ping_info->trans_color.blue*=0x0101;
7086 ping_info->trans_color.gray*=0x0101;
7087 }
7088 }
7089
7090 /*
7091 Adjust background and transparency samples in sub-8-bit grayscale files.
7092 */
7093 if (ping_info->bit_depth < 8 && ping_info->color_type ==
7094 PNG_COLOR_TYPE_GRAY)
7095 {
7096 png_uint_16
7097 maxval;
7098
7099 png_color_16
7100 background;
7101
7102 maxval=(png_uint_16) ((1 << ping_info->bit_depth)-1);
7103
7104
7105 background.gray=(png_uint_16)
7106 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7107
7108 if (logging != MagickFalse)
7109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7110 " Setting up bKGD chunk");
7111 png_set_bKGD(ping,ping_info,&background);
7112
7113 ping_info->trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7114 ping_info->trans_color.gray));
7115 }
7116 if (logging != MagickFalse)
7117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7118 " PNG color type: %d",ping_info->color_type);
7119 /*
7120 Initialize compression level and filtering.
7121 */
7122 if (logging != MagickFalse)
7123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7124 " Setting up deflate compression");
7125#if (PNG_LIBPNG_VER > 99)
7126 if (logging != MagickFalse)
7127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7128 " Compression buffer size: 32768");
7129 png_set_compression_buffer_size(ping,32768L);
7130#endif
7131 if (logging != MagickFalse)
7132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7133 " Compression mem level: 9");
7134 png_set_compression_mem_level(ping, 9);
7135 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7136 image->quality;
7137 if (quality > 9)
7138 {
7139 int
7140 level;
7141
7142 level=(int) MagickMin((long) quality/10,9);
7143 if (logging != MagickFalse)
7144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145 " Compression level: %d",level);
7146 png_set_compression_level(ping,level);
7147 }
7148 else
7149 {
7150 if (logging != MagickFalse)
7151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7152 " Compression strategy: Z_HUFFMAN_ONLY");
7153 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7154 }
7155 if (logging != MagickFalse)
7156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7157 " Setting up filtering");
7158#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7159
7160 /* This became available in libpng-1.0.9. Output must be a MNG. */
7161 if (mng_info->write_mng && ((quality % 10) == 7))
7162 {
7163 if (logging != MagickFalse)
7164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7165 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7166 ping_info->filter_type=PNG_INTRAPIXEL_DIFFERENCING;
7167 }
7168 else
7169 if (logging != MagickFalse)
7170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7171 " Filter_type: 0");
7172#endif
7173 {
7174 int
7175 base_filter;
7176
7177 if ((quality % 10) > 5)
7178 base_filter=PNG_ALL_FILTERS;
7179 else
7180 if ((quality % 10) != 5)
7181 base_filter=(int) quality % 10;
7182 else
7183 if (((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY) ||
7184 ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
7185 (quality < 50))
7186 base_filter=PNG_NO_FILTERS;
7187 else
7188 base_filter=PNG_ALL_FILTERS;
7189 if (logging != MagickFalse)
7190 {
7191 if (base_filter == PNG_ALL_FILTERS)
7192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7193 " Base filter method: ADAPTIVE");
7194 else
7195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7196 " Base filter method: NONE");
7197 }
7198 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7199 }
7200
7201 ResetImageProfileIterator(image);
7202 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7203 {
7204 profile=GetImageProfile(image,name);
7205 if (profile != (StringInfo *) NULL)
7206 {
7207#if (PNG_LIBPNG_VER > 10008) && defined(PNG_WRITE_iCCP_SUPPORTED)
7208 if ((LocaleCompare(name,"ICC") == 0) ||
7209 (LocaleCompare(name,"ICM") == 0))
7210 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7211 GetStringInfoDatum(profile),
7212 (png_uint_32) GetStringInfoLength(profile));
7213 else
7214#endif
7215 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7216 name,(unsigned char *) name,GetStringInfoDatum(profile),
7217 (png_uint_32) GetStringInfoLength(profile));
7218 }
7219 if (logging != MagickFalse)
7220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221 " Setting up text chunk with %s profile",name);
7222 name=GetNextImageProfile(image);
7223 }
7224
7225#if defined(PNG_WRITE_sRGB_SUPPORTED)
7226 if ((mng_info->have_write_global_srgb == 0) &&
7227 ((image->rendering_intent != UndefinedIntent) ||
7228 (image->colorspace == sRGBColorspace)))
7229 {
7230 /*
7231 Note image rendering intent.
7232 */
7233 if (logging != MagickFalse)
7234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7235 " Setting up sRGB chunk");
7236 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7237 png_set_gAMA(ping,ping_info,0.45455);
7238 }
7239 if ((!mng_info->write_mng) || !(ping_info->valid & PNG_INFO_sRGB))
7240#endif
7241 {
7242 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7243 {
7244 /*
7245 Note image gamma.
7246 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7247 */
7248 if (logging != MagickFalse)
7249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7250 " Setting up gAMA chunk");
7251 png_set_gAMA(ping,ping_info,image->gamma);
7252 }
7253 if ((mng_info->have_write_global_chrm == 0) &&
7254 (image->chromaticity.red_primary.x != 0.0))
7255 {
7256 /*
7257 Note image chromaticity.
7258 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7259 */
7260 PrimaryInfo
7261 bp,
7262 gp,
7263 rp,
7264 wp;
7265
7266 wp=image->chromaticity.white_point;
7267 rp=image->chromaticity.red_primary;
7268 gp=image->chromaticity.green_primary;
7269 bp=image->chromaticity.blue_primary;
7270
7271 if (logging != MagickFalse)
7272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7273 " Setting up cHRM chunk");
7274 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7275 bp.x,bp.y);
7276 }
7277 }
7278 ping_info->interlace_type=image_info->interlace != NoInterlace;
7279
7280 if (mng_info->write_mng)
7281 png_set_sig_bytes(ping,8);
7282
7283 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7284
7285 if (mng_info->write_png_colortype)
7286 {
7287 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7288 if (ImageIsGray(image) == MagickFalse)
7289 {
7290 ping_info->color_type = PNG_COLOR_TYPE_RGB;
7291 if (ping_info->bit_depth < 8)
7292 ping_info->bit_depth=8;
7293 }
7294
7295 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7296 if (ImageIsGray(image) == MagickFalse)
7297 ping_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7298 }
7299
7300 if ((mng_info->write_png_depth &&
7301 mng_info->write_png_depth != ping_info->bit_depth) ||
7302 (mng_info->write_png_colortype &&
7303 mng_info->write_png_colortype-1 != ping_info->color_type))
7304 {
7305 if (logging != MagickFalse)
7306 {
7307 if (mng_info->write_png_depth)
7308 {
7309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7310 " Defined PNG:bit-depth=%u, Computed depth=%u",
7311 mng_info->write_png_depth,
7312 ping_info->bit_depth);
7313 }
7314 if (mng_info->write_png_colortype)
7315 {
7316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7317 " Defined PNG:color-type=%u, Computed color type=%u",
7318 mng_info->write_png_colortype-1,
7319 ping_info->color_type);
7320 }
7321 }
7322 png_error(ping,
7323 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7324 }
7325
7326 if (image_matte && !image->matte)
7327 {
7328 /* Add an opaque matte channel */
7329 image->matte = MagickTrue;
7330 (void) SetImageOpacity(image,0);
7331 }
7332
7333 if (logging != MagickFalse)
7334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7335 " Writing PNG header chunks");
7336
7337 png_write_info_before_PLTE(ping, ping_info);
7338 /* write any png-chunk-b profiles */
7339 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7340 png_write_info(ping,ping_info);
7341 /* write any PNG-chunk-m profiles */
7342 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7343
7344 if (image->page.width || image->page.height)
7345 {
7346 unsigned char
7347 chunk[14];
7348
7349 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7350 PNGType(chunk,mng_vpAg);
7351 LogPNGChunk((int) logging,mng_vpAg,9L);
7352 PNGLong(chunk+4,(png_uint_32) image->page.width);
7353 PNGLong(chunk+8,(png_uint_32) image->page.height);
7354 chunk[12]=0; /* unit = pixels */
7355 (void) WriteBlob(image,13,chunk);
7356 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7357 }
7358
7359#if (PNG_LIBPNG_VER == 10206)
7360 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7361#define PNG_HAVE_IDAT 0x04
7362 ping->mode |= PNG_HAVE_IDAT;
7363#undef PNG_HAVE_IDAT
7364#endif
7365
7366 png_set_packing(ping);
7367 /*
7368 Allocate memory.
7369 */
7370 rowbytes=image->columns;
7371 if (image_depth <= 8)
7372 {
7373 if (mng_info->write_png24 || (mng_info->write_png_depth == 8 &&
7374 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB))
7375 rowbytes*=3;
7376 else if (mng_info->write_png32 || (mng_info->write_png_depth == 8 &&
7377 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB_ALPHA))
7378 rowbytes*=4;
7379 else if ((!mng_info->write_png8 ||
7380 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY ||
7381 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA )&&
7382 ((mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image)))
7383 rowbytes*=(image_matte ? 2 : 1);
7384 else
7385 {
7386 if (!mng_info->IsPalette)
7387 rowbytes*=(image_matte ? 4 : 3);
7388 }
7389 }
7390 else
7391 {
7392 if ((mng_info->optimize || mng_info->IsPalette) &&
7393 ImageIsGray(image))
7394 rowbytes*=(image_matte ? 4 : 2);
7395 else
7396 rowbytes*=(image_matte ? 8 : 6);
7397 }
7398 if (logging)
7399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7400 " Allocating %lu bytes of memory for pixels",rowbytes);
7401 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7402 sizeof(*png_pixels));
7403 if (png_pixels == (unsigned char *) NULL)
7404 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7405 /*
7406 Initialize image scanlines.
7407 */
7408 quantum_info=AcquireQuantumInfo(image_info,image);
7409 if (quantum_info == (QuantumInfo *) NULL)
7410 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7411 if (setjmp(ping->jmpbuf))
7412 {
7413 /*
7414 PNG write failed.
7415 */
7416#ifdef PNG_DEBUG
7417 if (image_info->verbose)
7418 (void) printf("PNG write has failed.\n");
7419#endif
7420 png_destroy_write_struct(&ping,&ping_info);
7421 if (quantum_info != (QuantumInfo *) NULL)
7422 quantum_info=DestroyQuantumInfo(quantum_info);
7423 if (png_pixels != (unsigned char *) NULL)
7424 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7425#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7426 RelinquishSemaphoreInfo(png_semaphore);
7427#endif
7428 return(MagickFalse);
7429 }
7430 quantum_info->format=UndefinedQuantumFormat;
7431 quantum_info->depth=image_depth;
7432 num_passes=png_set_interlace_handling(ping);
7433 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7434 !mng_info->write_png32) &&
7435 (mng_info->optimize || mng_info->IsPalette ||
7436 (image_info->type == BilevelType)) &&
7437 !image_matte && ImageIsMonochrome(image))
7438 {
7439 register const PixelPacket
7440 *p;
7441
7442 quantum_info->depth=8;
7443 for (pass=0; pass < num_passes; pass++)
7444 {
7445 /*
7446 Convert PseudoClass image to a PNG monochrome image.
7447 */
7448 for (y=0; y < (long) image->rows; y++)
7449 {
7450 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7451 if (p == (const PixelPacket *) NULL)
7452 break;
7453 if (mng_info->IsPalette)
7454 {
7455 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7456 quantum_info,GrayQuantum,png_pixels,&image->exception);
7457 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7458 mng_info->write_png_depth &&
7459 mng_info->write_png_depth != old_bit_depth)
7460 {
7461 /* Undo pixel scaling */
7462 for (i=0; i < (long) image->columns; i++)
7463 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7464 >> (8-old_bit_depth));
7465 }
7466 }
7467 else
7468 {
7469 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7470 quantum_info,RedQuantum,png_pixels,&image->exception);
7471 }
7472 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7473 for (i=0; i < (long) image->columns; i++)
7474 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7475 255 : 0);
7476 png_write_row(ping,png_pixels);
7477 }
7478 if (image->previous == (Image *) NULL)
7479 {
7480 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7481 if (status == MagickFalse)
7482 break;
7483 }
7484 }
7485 }
7486 else
7487 for (pass=0; pass < num_passes; pass++)
7488 {
7489 register const PixelPacket
7490 *p;
7491
7492 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7493 !mng_info->write_png32) &&
7494 (image_matte ||
7495 (ping_info->bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7496 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7497 {
7498 for (y=0; y < (long) image->rows; y++)
7499 {
7500 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7501 if (p == (const PixelPacket *) NULL)
7502 break;
7503 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7504 {
7505 if (mng_info->IsPalette)
7506 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7507 quantum_info,GrayQuantum,png_pixels,&image->exception);
7508 else
7509 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7510 quantum_info,RedQuantum,png_pixels,&image->exception);
7511 }
7512 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7513 {
7514 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7515 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7516 }
7517 png_write_row(ping,png_pixels);
7518 }
7519 if (image->previous == (Image *) NULL)
7520 {
7521 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7522 if (status == MagickFalse)
7523 break;
7524 }
7525 }
7526 else
7527 for (pass=0; pass < num_passes; pass++)
7528 {
7529 if ((image_depth > 8) || (mng_info->write_png24 ||
7530 mng_info->write_png32 ||
7531 (!mng_info->write_png8 && !mng_info->IsPalette)))
7532 for (y=0; y < (long) image->rows; y++)
7533 {
7534 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7535 if (p == (const PixelPacket *) NULL)
7536 break;
7537 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7538 {
7539 if (image->storage_class == DirectClass)
7540 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7541 quantum_info,RedQuantum,png_pixels,&image->exception);
7542 else
7543 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7544 quantum_info,GrayQuantum,png_pixels,&image->exception);
7545 }
7546 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7547 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7548 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7549 else if (image_matte != MagickFalse)
7550 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7551 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7552 else
7553 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7554 quantum_info,RGBQuantum,png_pixels,&image->exception);
7555 png_write_row(ping,png_pixels);
7556 }
7557 else
7558 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7559 mng_info->write_png32 ||
7560 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7561 {
7562 if ((ping_info->color_type != PNG_COLOR_TYPE_GRAY) &&
7563 (ping_info->color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7564 {
7565 if (logging)
7566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7567 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7568 quantum_info->depth=8;
7569 image_depth=8;
7570 }
7571 for (y=0; y < (long) image->rows; y++)
7572 {
7573 if (logging)
7574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7575 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7576 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7577 if (p == (const PixelPacket *) NULL)
7578 break;
7579 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7580 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7581 quantum_info,GrayQuantum,png_pixels,&image->exception);
7582 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7583 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7584 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7585 else
7586 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7587 quantum_info,IndexQuantum,png_pixels,&image->exception);
7588 png_write_row(ping,png_pixels);
7589 }
7590 }
7591 if (image->previous == (Image *) NULL)
7592 {
7593 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7594 if (status == MagickFalse)
7595 break;
7596 }
7597 }
7598 }
cristyb32b90a2009-09-07 21:45:48 +00007599 if (quantum_info != (QuantumInfo *) NULL)
7600 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007601
7602 if (logging != MagickFalse)
7603 {
7604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7605 " Writing PNG image data");
7606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607 " Width: %lu",ping_info->width);
7608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609 " Height: %lu",ping_info->height);
7610 if (mng_info->write_png_depth)
7611 {
7612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7613 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7614 }
7615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7616 " PNG bit-depth written: %d",ping_info->bit_depth);
7617 if (mng_info->write_png_colortype)
7618 {
7619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7620 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7621 }
7622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7623 " PNG color-type written: %d",ping_info->color_type);
7624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7625 " PNG Interlace method: %d",ping_info->interlace_type);
7626 }
7627 /*
7628 Generate text chunks.
7629 */
7630#if (PNG_LIBPNG_VER <= 10005)
7631 ping_info->num_text=0;
7632#endif
7633 ResetImagePropertyIterator(image);
7634 property=GetNextImageProperty(image);
7635 while (property != (const char *) NULL)
7636 {
7637#if (PNG_LIBPNG_VER > 10005)
7638 png_textp
7639 text;
7640#endif
7641
7642 value=GetImageProperty(image,property);
7643 if (value != (const char *) NULL)
7644 {
7645#if (PNG_LIBPNG_VER > 10005)
7646 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7647 text[0].key=(char *) property;
7648 text[0].text=(char *) value;
7649 text[0].text_length=strlen(value);
7650 text[0].compression=image_info->compression == NoCompression ||
7651 (image_info->compression == UndefinedCompression &&
7652 text[0].text_length < 128) ? -1 : 0;
7653 if (logging != MagickFalse)
7654 {
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " Setting up text chunk");
7657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7658 " keyword: %s",text[0].key);
7659 }
7660 png_set_text(ping,ping_info,text,1);
7661 png_free(ping,text);
7662#else
7663/* Work directly with ping_info struct; png_set_text before libpng version
7664 * 1.0.5a is leaky */
7665 if (ping_info->num_text == 0)
7666 {
7667 ping_info->text=(png_text *) AcquireQuantumMemory(256,
7668 sizeof(*ping_info->text));
7669 if (ping_info->text == (png_text *) NULL)
7670 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7671 ResourceLimitError,"MemoryAllocationFailed","`%s'",
7672 image->filename);
7673 }
7674 i=ping_info->num_text++;
7675 if (i > 255)
7676 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7677 ResourceLimitError,"Cannot write more than 256 PNG text chunks",
7678 "`%s'",image->filename);
7679 ping_info->text[i].key=(char *) property;
7680 ping_info->text[i].text=(char *) value;
7681 ping_info->text[i].text_length=strlen(value);
7682 ping_info->text[i].compression=
7683 image_info->compression == NoCompression ||
7684 (image_info->compression == UndefinedCompression &&
7685 ping_info->text[i].text_length < 128) ? -1 : 0;
7686 if (logging != MagickFalse)
7687 {
7688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7689 " Setting up text chunk");
7690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7691 " keyword: %s",ping_info->text[i].key);
7692 }
7693#endif
7694 }
7695 property=GetNextImageProperty(image);
7696 }
7697
7698 /* write any PNG-chunk-e profiles */
7699 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7700
7701 if (logging != MagickFalse)
7702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7703 " Writing PNG end info");
7704 png_write_end(ping,ping_info);
7705 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7706 {
7707 if (mng_info->page.x || mng_info->page.y ||
7708 (ping_info->width != mng_info->page.width) ||
7709 (ping_info->height != mng_info->page.height))
7710 {
7711 unsigned char
7712 chunk[32];
7713
7714 /*
7715 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7716 */
7717 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7718 PNGType(chunk,mng_FRAM);
7719 LogPNGChunk((int) logging,mng_FRAM,27L);
7720 chunk[4]=4;
7721 chunk[5]=0; /* frame name separator (no name) */
7722 chunk[6]=1; /* flag for changing delay, for next frame only */
7723 chunk[7]=0; /* flag for changing frame timeout */
7724 chunk[8]=1; /* flag for changing frame clipping for next frame */
7725 chunk[9]=0; /* flag for changing frame sync_id */
7726 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7727 chunk[14]=0; /* clipping boundaries delta type */
7728 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7729 PNGLong(chunk+19,
7730 (png_uint_32) (mng_info->page.x + ping_info->width));
7731 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7732 PNGLong(chunk+27,
7733 (png_uint_32) (mng_info->page.y + ping_info->height));
7734 (void) WriteBlob(image,31,chunk);
7735 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7736 mng_info->old_framing_mode=4;
7737 mng_info->framing_mode=1;
7738 }
7739 else
7740 mng_info->framing_mode=3;
7741 }
7742 if (mng_info->write_mng && !mng_info->need_fram &&
7743 ((int) image->dispose == 3))
7744 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7745 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7746 "`%s'",image->filename);
7747 image_depth=save_image_depth;
7748
7749 /* Save depth actually written */
7750
7751 s[0]=(char) ping_info->bit_depth;
7752 s[1]='\0';
7753
7754 (void) SetImageProperty(image,"png:bit-depth-written",s);
7755
7756 /*
7757 Free PNG resources.
7758 */
7759#if (PNG_LIBPNG_VER < 10007)
7760 if (ping_info->valid & PNG_INFO_PLTE)
7761 {
7762 ping_info->palette=(png_colorp)
7763 RelinquishMagickMemory(ping_info->palette);
7764 ping_info->valid&=(~PNG_INFO_PLTE);
7765 }
7766 if (ping_info->valid & PNG_INFO_tRNS)
7767 {
7768 ping_info->trans_alpha=(unsigned char *) RelinquishMagickMemory(
7769 ping_info->trans_alpha);
7770 ping_info->valid&=(~PNG_INFO_tRNS);
7771 }
7772#endif
7773
7774 png_destroy_write_struct(&ping,&ping_info);
7775
7776 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7777
7778#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7779 RelinquishSemaphoreInfo(png_semaphore);
7780#endif
7781
7782 if (logging != MagickFalse)
7783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7784 " exit WriteOnePNGImage()");
7785 return(MagickTrue);
7786/* End write one PNG image */
7787}
7788
7789/*
7790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7791% %
7792% %
7793% %
7794% W r i t e P N G I m a g e %
7795% %
7796% %
7797% %
7798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7799%
7800% WritePNGImage() writes a Portable Network Graphics (PNG) or
7801% Multiple-image Network Graphics (MNG) image file.
7802%
7803% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7804%
7805% The format of the WritePNGImage method is:
7806%
7807% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
7808%
7809% A description of each parameter follows:
7810%
7811% o image_info: the image info.
7812%
7813% o image: The image.
7814%
7815% Returns MagickTrue on success, MagickFalse on failure.
7816%
7817% Communicating with the PNG encoder:
7818%
7819% While the datastream written is always in PNG format and normally would
7820% be given the "png" file extension, this method also writes the following
7821% pseudo-formats which are subsets of PNG:
7822%
7823% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
7824% is present, the tRNS chunk must only have values 0 and 255
7825% (i.e., transparency is binary: fully opaque or fully
7826% transparent). The pixels contain 8-bit indices even if
7827% they could be represented with 1, 2, or 4 bits. Note: grayscale
7828% images will be written as indexed PNG files even though the
7829% PNG grayscale type might be slightly more efficient.
7830%
7831% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
7832% chunk can be present to convey binary transparency by naming
7833% one of the colors as transparent.
7834%
7835% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
7836% transparency is permitted, i.e., the alpha sample for
7837% each pixel can have any value from 0 to 255. The alpha
7838% channel is present even if the image is fully opaque.
7839%
7840% o -define: For more precise control of the PNG output, you can use the
7841% Image options "png:bit-depth" and "png:color-type". These
7842% can be set from the commandline with "-define" and also
7843% from the application programming interfaces.
7844%
7845% png:color-type can be 0, 2, 3, 4, or 6.
7846%
7847% When png:color-type is 0 (Grayscale), png:bit-depth can
7848% be 1, 2, 4, 8, or 16.
7849%
7850% When png:color-type is 2 (RGB), png:bit-depth can
7851% be 8 or 16.
7852%
7853% When png:color-type is 3 (Indexed), png:bit-depth can
7854% be 1, 2, 4, or 8. This refers to the number of bits
7855% used to store the index. The color samples always have
7856% bit-depth 8 in indexed PNG files.
7857%
7858% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
7859% png:bit-depth can be 8 or 16.
7860%
7861% If the image cannot be written without loss in the requested PNG8, PNG24,
7862% or PNG32 format or with the requested bit-depth and color-type without loss,
7863% a PNG file will not be written, and the encoder will return MagickFalse.
7864% Since image encoders should not be responsible for the "heavy lifting",
7865% the user should make sure that ImageMagick has already reduced the
7866% image depth and number of colors and limit transparency to binary
7867% transparency prior to attempting to write the image in a format that
7868% is subject to depth, color, or transparency limitations.
7869%
7870% TODO: Enforce the previous paragraph.
7871%
7872% TODO: Allow all other PNG subformats to be requested via new
7873% "-define png:bit-depth -define png:color-type" options.
7874%
7875% Note that another definition, "png:bit-depth-written" exists, but it
7876% is not intended for external use. It is only used internally by the
7877% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
7878%
7879% It is possible to request that the PNG encoder write previously-formatted
7880% ancillary chunks in the output PNG file, using the "-profile" commandline
7881% option as shown below or by setting the profile via a programming
7882% interface:
7883%
7884% -profile PNG-chunk-x:<file>
7885%
7886% where x is a location flag and <file> is a file containing the chunk
7887% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
7888%
7889% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
7890% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
7891% of the same type, then add a short unique string after the "x" to prevent
7892% subsequent profiles from overwriting the preceding ones:
7893%
7894% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
7895%
7896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7897*/
7898static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
7899 Image *image)
7900{
7901 MagickBooleanType
7902 status;
7903
7904 MngInfo
7905 *mng_info;
7906
7907 const char
7908 *value;
7909
7910 int
7911 have_mng_structure;
7912
7913 unsigned int
7914 logging;
7915
7916 /*
7917 Open image file.
7918 */
7919 assert(image_info != (const ImageInfo *) NULL);
7920 assert(image_info->signature == MagickSignature);
7921 assert(image != (Image *) NULL);
7922 assert(image->signature == MagickSignature);
7923 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
7924 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
7925 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
7926 if (status == MagickFalse)
7927 return(MagickFalse);
7928 /*
7929 Allocate a MngInfo structure.
7930 */
7931 have_mng_structure=MagickFalse;
7932 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7933 if (mng_info == (MngInfo *) NULL)
7934 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7935 /*
7936 Initialize members of the MngInfo structure.
7937 */
7938 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7939 mng_info->image=image;
7940 have_mng_structure=MagickTrue;
7941
7942 /* See if user has requested a specific PNG subformat */
7943
7944 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
7945 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
7946 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
7947
7948 if (mng_info->write_png8)
7949 {
7950 mng_info->write_png_colortype = /* 3 */ 4;
7951 mng_info->write_png_depth = 8;
7952 image->depth = 8;
7953#if 0 /* this does not work */
7954 if (image->matte == MagickTrue)
7955 (void) SetImageType(image,PaletteMatteType);
7956 else
7957 (void) SetImageType(image,PaletteType);
7958 (void) SyncImage(image);
7959#endif
7960 }
7961
7962 if (mng_info->write_png24)
7963 {
7964 mng_info->write_png_colortype = /* 2 */ 3;
7965 mng_info->write_png_depth = 8;
7966 image->depth = 8;
7967 if (image->matte == MagickTrue)
7968 (void) SetImageType(image,TrueColorMatteType);
7969 else
7970 (void) SetImageType(image,TrueColorType);
7971 (void) SyncImage(image);
7972 }
7973
7974 if (mng_info->write_png32)
7975 {
7976 mng_info->write_png_colortype = /* 6 */ 7;
7977 mng_info->write_png_depth = 8;
7978 image->depth = 8;
7979 if (image->matte == MagickTrue)
7980 (void) SetImageType(image,TrueColorMatteType);
7981 else
7982 (void) SetImageType(image,TrueColorType);
7983 (void) SyncImage(image);
7984 }
7985
7986 value=GetImageOption(image_info,"png:bit-depth");
7987 if (value != (char *) NULL)
7988 {
7989 if (LocaleCompare(value,"1") == 0)
7990 mng_info->write_png_depth = 1;
7991 else if (LocaleCompare(value,"2") == 0)
7992 mng_info->write_png_depth = 2;
7993 else if (LocaleCompare(value,"4") == 0)
7994 mng_info->write_png_depth = 4;
7995 else if (LocaleCompare(value,"8") == 0)
7996 mng_info->write_png_depth = 8;
7997 else if (LocaleCompare(value,"16") == 0)
7998 mng_info->write_png_depth = 16;
7999 if (logging != MagickFalse)
8000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8001 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8002 }
8003 value=GetImageOption(image_info,"png:color-type");
8004 if (value != (char *) NULL)
8005 {
8006 /* We must store colortype+1 because 0 is a valid colortype */
8007 if (LocaleCompare(value,"0") == 0)
8008 mng_info->write_png_colortype = 1;
8009 else if (LocaleCompare(value,"2") == 0)
8010 mng_info->write_png_colortype = 3;
8011 else if (LocaleCompare(value,"3") == 0)
8012 mng_info->write_png_colortype = 4;
8013 else if (LocaleCompare(value,"4") == 0)
8014 mng_info->write_png_colortype = 5;
8015 else if (LocaleCompare(value,"6") == 0)
8016 mng_info->write_png_colortype = 7;
8017 if (logging != MagickFalse)
8018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8020 }
8021
8022 status=WriteOnePNGImage(mng_info,image_info,image);
8023
8024 (void) CloseBlob(image);
8025
8026 MngInfoFreeStruct(mng_info,&have_mng_structure);
8027 if (logging != MagickFalse)
8028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8029 return(status);
8030}
8031
8032#if defined(JNG_SUPPORTED)
8033
8034/* Write one JNG image */
8035static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8036 const ImageInfo *image_info,Image *image)
8037{
8038 Image
8039 *jpeg_image;
8040
8041 ImageInfo
8042 *jpeg_image_info;
8043
8044 MagickBooleanType
8045 status;
8046
8047 size_t
8048 length;
8049
8050 unsigned char
8051 *blob,
8052 chunk[80],
8053 *p;
8054
8055 unsigned int
8056 jng_alpha_compression_method,
8057 jng_alpha_sample_depth,
8058 jng_color_type,
8059 logging,
8060 transparent;
8061
8062 unsigned long
8063 jng_quality;
8064
8065 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8066 " enter WriteOneJNGImage()");
8067
8068 blob=(unsigned char *) NULL;
8069 jpeg_image=(Image *) NULL;
8070 jpeg_image_info=(ImageInfo *) NULL;
8071
8072 status=MagickTrue;
8073 transparent=image_info->type==GrayscaleMatteType ||
8074 image_info->type==TrueColorMatteType;
8075 jng_color_type=10;
8076 jng_alpha_sample_depth=0;
8077 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8078 jng_alpha_compression_method=0;
8079
8080 if (image->matte != MagickFalse)
8081 {
8082 /* if any pixels are transparent */
8083 transparent=MagickTrue;
8084 if (image_info->compression==JPEGCompression)
8085 jng_alpha_compression_method=8;
8086 }
8087
8088 if (transparent)
8089 {
8090 jng_color_type=14;
8091 /* Create JPEG blob, image, and image_info */
8092 if (logging != MagickFalse)
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " Creating jpeg_image_info for opacity.");
8095 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8096 if (jpeg_image_info == (ImageInfo *) NULL)
8097 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8098 if (logging != MagickFalse)
8099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8100 " Creating jpeg_image.");
8101 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8102 if (jpeg_image == (Image *) NULL)
8103 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8104 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8105 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8106 status=NegateImage(jpeg_image,MagickFalse);
8107 jpeg_image->matte=MagickFalse;
8108 if (jng_quality >= 1000)
8109 jpeg_image_info->quality=jng_quality/1000;
8110 else
8111 jpeg_image_info->quality=jng_quality;
8112 jpeg_image_info->type=GrayscaleType;
8113 (void) SetImageType(jpeg_image,GrayscaleType);
8114 (void) AcquireUniqueFilename(jpeg_image->filename);
8115 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8116 "%s",jpeg_image->filename);
8117 }
8118
8119 /* To do: check bit depth of PNG alpha channel */
8120
8121 /* Check if image is grayscale. */
8122 if (image_info->type != TrueColorMatteType && image_info->type !=
8123 TrueColorType && ImageIsGray(image))
8124 jng_color_type-=2;
8125
8126 if (transparent)
8127 {
8128 if (jng_alpha_compression_method==0)
8129 {
8130 const char
8131 *value;
8132
8133 /* Encode opacity as a grayscale PNG blob */
8134 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8135 &image->exception);
8136 if (logging != MagickFalse)
8137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8138 " Creating PNG blob.");
8139 length=0;
8140
8141 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8142 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8143 jpeg_image_info->interlace=NoInterlace;
8144
8145 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8146 &image->exception);
8147
8148 /* Retrieve sample depth used */
8149 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8150 if (value != (char *) NULL)
8151 jng_alpha_sample_depth= (unsigned int) value[0];
8152 }
8153 else
8154 {
8155 /* Encode opacity as a grayscale JPEG blob */
8156
8157 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8158 &image->exception);
8159
8160 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8161 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8162 jpeg_image_info->interlace=NoInterlace;
8163 if (logging != MagickFalse)
8164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8165 " Creating blob.");
8166 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8167 &image->exception);
8168 jng_alpha_sample_depth=8;
8169 if (logging != MagickFalse)
8170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8171 " Successfully read jpeg_image into a blob, length=%lu.",
8172 (unsigned long) length);
8173
8174 }
8175 /* Destroy JPEG image and image_info */
8176 jpeg_image=DestroyImage(jpeg_image);
8177 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8178 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8179 }
8180
8181 /* Write JHDR chunk */
8182 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8183 PNGType(chunk,mng_JHDR);
8184 LogPNGChunk((int) logging,mng_JHDR,16L);
8185 PNGLong(chunk+4,image->columns);
8186 PNGLong(chunk+8,image->rows);
8187 chunk[12]=jng_color_type;
8188 chunk[13]=8; /* sample depth */
8189 chunk[14]=8; /*jng_image_compression_method */
8190 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8191 chunk[16]=jng_alpha_sample_depth;
8192 chunk[17]=jng_alpha_compression_method;
8193 chunk[18]=0; /*jng_alpha_filter_method */
8194 chunk[19]=0; /*jng_alpha_interlace_method */
8195 (void) WriteBlob(image,20,chunk);
8196 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8197 if (logging != MagickFalse)
8198 {
8199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8200 " JNG width:%15lu",image->columns);
8201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 " JNG height:%14lu",image->rows);
8203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204 " JNG color type:%10d",jng_color_type);
8205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8206 " JNG sample depth:%8d",8);
8207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8208 " JNG compression:%9d",8);
8209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8210 " JNG interlace:%11d",0);
8211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8212 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " JNG alpha compression:%3d",jng_alpha_compression_method);
8215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8216 " JNG alpha filter:%8d",0);
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " JNG alpha interlace:%5d",0);
8219 }
8220
8221 /* Write any JNG-chunk-b profiles */
8222 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8223
8224 /*
8225 Write leading ancillary chunks
8226 */
8227
8228 if (transparent)
8229 {
8230 /*
8231 Write JNG bKGD chunk
8232 */
8233
8234 unsigned char
8235 blue,
8236 green,
8237 red;
8238
8239 long
8240 num_bytes;
8241
8242 if (jng_color_type == 8 || jng_color_type == 12)
8243 num_bytes=6L;
8244 else
8245 num_bytes=10L;
8246 (void) WriteBlobMSBULong(image,(unsigned long) (num_bytes-4L));
8247 PNGType(chunk,mng_bKGD);
8248 LogPNGChunk((int) logging,mng_bKGD,(unsigned long) (num_bytes-4L));
8249 red=ScaleQuantumToChar(image->background_color.red);
8250 green=ScaleQuantumToChar(image->background_color.green);
8251 blue=ScaleQuantumToChar(image->background_color.blue);
8252 *(chunk+4)=0;
8253 *(chunk+5)=red;
8254 *(chunk+6)=0;
8255 *(chunk+7)=green;
8256 *(chunk+8)=0;
8257 *(chunk+9)=blue;
8258 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8259 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8260 }
8261
8262 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8263 {
8264 /*
8265 Write JNG sRGB chunk
8266 */
8267 (void) WriteBlobMSBULong(image,1L);
8268 PNGType(chunk,mng_sRGB);
8269 LogPNGChunk((int) logging,mng_sRGB,1L);
8270 if (image->rendering_intent != UndefinedIntent)
8271 chunk[4]=(unsigned char) (image->rendering_intent-1);
8272 else
8273 chunk[4]=(unsigned char) (PerceptualIntent-1);
8274 (void) WriteBlob(image,5,chunk);
8275 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8276 }
8277 else
8278 {
8279 if (image->gamma != 0.0)
8280 {
8281 /*
8282 Write JNG gAMA chunk
8283 */
8284 (void) WriteBlobMSBULong(image,4L);
8285 PNGType(chunk,mng_gAMA);
8286 LogPNGChunk((int) logging,mng_gAMA,4L);
8287 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
8288 (void) WriteBlob(image,8,chunk);
8289 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8290 }
8291 if ((mng_info->equal_chrms == MagickFalse) &&
8292 (image->chromaticity.red_primary.x != 0.0))
8293 {
8294 PrimaryInfo
8295 primary;
8296
8297 /*
8298 Write JNG cHRM chunk
8299 */
8300 (void) WriteBlobMSBULong(image,32L);
8301 PNGType(chunk,mng_cHRM);
8302 LogPNGChunk((int) logging,mng_cHRM,32L);
8303 primary=image->chromaticity.white_point;
8304 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
8305 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
8306 primary=image->chromaticity.red_primary;
8307 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
8308 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
8309 primary=image->chromaticity.green_primary;
8310 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
8311 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
8312 primary=image->chromaticity.blue_primary;
8313 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
8314 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
8315 (void) WriteBlob(image,36,chunk);
8316 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8317 }
8318 }
8319 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8320 {
8321 /*
8322 Write JNG pHYs chunk
8323 */
8324 (void) WriteBlobMSBULong(image,9L);
8325 PNGType(chunk,mng_pHYs);
8326 LogPNGChunk((int) logging,mng_pHYs,9L);
8327 if (image->units == PixelsPerInchResolution)
8328 {
8329 PNGLong(chunk+4,(unsigned long)
8330 (image->x_resolution*100.0/2.54+0.5));
8331 PNGLong(chunk+8,(unsigned long)
8332 (image->y_resolution*100.0/2.54+0.5));
8333 chunk[12]=1;
8334 }
8335 else
8336 {
8337 if (image->units == PixelsPerCentimeterResolution)
8338 {
8339 PNGLong(chunk+4,(unsigned long)
8340 (image->x_resolution*100.0+0.5));
8341 PNGLong(chunk+8,(unsigned long)
8342 (image->y_resolution*100.0+0.5));
8343 chunk[12]=1;
8344 }
8345 else
8346 {
8347 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
8348 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
8349 chunk[12]=0;
8350 }
8351 }
8352 (void) WriteBlob(image,13,chunk);
8353 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8354 }
8355
8356 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8357 {
8358 /*
8359 Write JNG oFFs chunk
8360 */
8361 (void) WriteBlobMSBULong(image,9L);
8362 PNGType(chunk,mng_oFFs);
8363 LogPNGChunk((int) logging,mng_oFFs,9L);
8364 PNGsLong(chunk+4,(long) (image->page.x));
8365 PNGsLong(chunk+8,(long) (image->page.y));
8366 chunk[12]=0;
8367 (void) WriteBlob(image,13,chunk);
8368 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8369 }
8370 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8371 {
8372 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8373 PNGType(chunk,mng_vpAg);
8374 LogPNGChunk((int) logging,mng_vpAg,9L);
8375 PNGLong(chunk+4,(png_uint_32) image->page.width);
8376 PNGLong(chunk+8,(png_uint_32) image->page.height);
8377 chunk[12]=0; /* unit = pixels */
8378 (void) WriteBlob(image,13,chunk);
8379 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8380 }
8381
8382
8383 if (transparent)
8384 {
8385 if (jng_alpha_compression_method==0)
8386 {
8387 register long
8388 i;
8389
8390 long
8391 len;
8392
8393 /* Write IDAT chunk header */
8394 if (logging != MagickFalse)
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Write IDAT chunks from blob, length=%lu.",
8397 (unsigned long) length);
8398
8399 /* Copy IDAT chunks */
8400 len=0;
8401 p=blob+8;
8402 for (i=8; i<(long) length; i+=len+12)
8403 {
8404 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8405 p+=4;
8406 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8407 {
8408 /* Found an IDAT chunk. */
8409 (void) WriteBlobMSBULong(image,(unsigned long) len);
8410 LogPNGChunk((int) logging,mng_IDAT,(unsigned long) len);
8411 (void) WriteBlob(image,(size_t) len+4,p);
8412 (void) WriteBlobMSBULong(image,
8413 crc32(0,p,(uInt) len+4));
8414 }
8415 else
8416 {
8417 if (logging != MagickFalse)
8418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8419 " Skipping %c%c%c%c chunk, length=%lu.",
8420 *(p),*(p+1),*(p+2),*(p+3),len);
8421 }
8422 p+=(8+len);
8423 }
8424 }
8425 else
8426 {
8427 /* Write JDAA chunk header */
8428 if (logging != MagickFalse)
8429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8430 " Write JDAA chunk, length=%lu.",
8431 (unsigned long) length);
8432 (void) WriteBlobMSBULong(image,(unsigned long) length);
8433 PNGType(chunk,mng_JDAA);
8434 LogPNGChunk((int) logging,mng_JDAA,length);
8435 /* Write JDAT chunk(s) data */
8436 (void) WriteBlob(image,4,chunk);
8437 (void) WriteBlob(image,length,blob);
8438 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8439 (uInt) length));
8440 }
8441 blob=(unsigned char *) RelinquishMagickMemory(blob);
8442 }
8443
8444 /* Encode image as a JPEG blob */
8445 if (logging != MagickFalse)
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " Creating jpeg_image_info.");
8448 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8449 if (jpeg_image_info == (ImageInfo *) NULL)
8450 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8451
8452 if (logging != MagickFalse)
8453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8454 " Creating jpeg_image.");
8455
8456 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8457 if (jpeg_image == (Image *) NULL)
8458 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8459 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8460
8461 (void) AcquireUniqueFilename(jpeg_image->filename);
8462 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8463 jpeg_image->filename);
8464
8465 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8466 &image->exception);
8467
8468 if (logging != MagickFalse)
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470 " Created jpeg_image, %lu x %lu.",jpeg_image->columns,
8471 jpeg_image->rows);
8472
8473 if (jng_color_type == 8 || jng_color_type == 12)
8474 jpeg_image_info->type=GrayscaleType;
8475 jpeg_image_info->quality=jng_quality % 1000;
8476 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8477 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8478 if (logging != MagickFalse)
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " Creating blob.");
8481 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8482 if (logging != MagickFalse)
8483 {
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Successfully read jpeg_image into a blob, length=%lu.",
8486 (unsigned long) length);
8487
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Write JDAT chunk, length=%lu.",
8490 (unsigned long) length);
8491 }
8492 /* Write JDAT chunk(s) */
8493 (void) WriteBlobMSBULong(image,(unsigned long) length);
8494 PNGType(chunk,mng_JDAT);
8495 LogPNGChunk((int) logging,mng_JDAT,length);
8496 (void) WriteBlob(image,4,chunk);
8497 (void) WriteBlob(image,length,blob);
8498 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8499
8500 jpeg_image=DestroyImage(jpeg_image);
8501 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8502 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8503 blob=(unsigned char *) RelinquishMagickMemory(blob);
8504
8505 /* Write any JNG-chunk-e profiles */
8506 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8507
8508 /* Write IEND chunk */
8509 (void) WriteBlobMSBULong(image,0L);
8510 PNGType(chunk,mng_IEND);
8511 LogPNGChunk((int) logging,mng_IEND,0);
8512 (void) WriteBlob(image,4,chunk);
8513 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8514
8515 if (logging != MagickFalse)
8516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517 " exit WriteOneJNGImage()");
8518 return(status);
8519}
8520
8521
8522/*
8523%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8524% %
8525% %
8526% %
8527% W r i t e J N G I m a g e %
8528% %
8529% %
8530% %
8531%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8532%
8533% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8534%
8535% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8536%
8537% The format of the WriteJNGImage method is:
8538%
8539% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8540%
8541% A description of each parameter follows:
8542%
8543% o image_info: the image info.
8544%
8545% o image: The image.
8546%
8547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8548*/
8549static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8550{
8551 MagickBooleanType
8552 status;
8553
8554 MngInfo
8555 *mng_info;
8556
8557 int
8558 have_mng_structure;
8559
8560 unsigned int
8561 logging;
8562
8563 /*
8564 Open image file.
8565 */
8566 assert(image_info != (const ImageInfo *) NULL);
8567 assert(image_info->signature == MagickSignature);
8568 assert(image != (Image *) NULL);
8569 assert(image->signature == MagickSignature);
8570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8571 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8572 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8573 if (status == MagickFalse)
8574 return(status);
8575
8576 /*
8577 Allocate a MngInfo structure.
8578 */
8579 have_mng_structure=MagickFalse;
8580 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8581 if (mng_info == (MngInfo *) NULL)
8582 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8583 /*
8584 Initialize members of the MngInfo structure.
8585 */
8586 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8587 mng_info->image=image;
8588 have_mng_structure=MagickTrue;
8589
8590 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8591
8592 status=WriteOneJNGImage(mng_info,image_info,image);
8593 (void) CloseBlob(image);
8594
8595 (void) CatchImageException(image);
8596 MngInfoFreeStruct(mng_info,&have_mng_structure);
8597 if (logging != MagickFalse)
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8599 return(status);
8600}
8601#endif
8602
8603
8604
8605static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8606{
8607 const char
8608 *option;
8609
8610 Image
8611 *next_image;
8612
8613 MagickBooleanType
8614 status;
8615
8616 MngInfo
8617 *mng_info;
8618
8619 int
8620 have_mng_structure,
8621 image_count,
8622 need_iterations,
8623 need_matte;
8624
8625 volatile int
8626#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8627 defined(PNG_MNG_FEATURES_SUPPORTED)
8628 need_local_plte,
8629#endif
8630 all_images_are_gray,
8631 logging,
8632 need_defi,
8633 optimize,
8634 use_global_plte;
8635
8636 register long
8637 i;
8638
8639 unsigned char
8640 chunk[800];
8641
8642 volatile unsigned int
8643 write_jng,
8644 write_mng;
8645
8646 volatile unsigned long
8647 scene;
8648
8649 unsigned long
8650 final_delay=0,
8651 initial_delay;
8652
8653#if (PNG_LIBPNG_VER < 10007)
8654 if (image_info->verbose)
8655 printf("Your PNG library (libpng-%s) is rather old.\n",
8656 PNG_LIBPNG_VER_STRING);
8657#endif
8658
8659 /*
8660 Open image file.
8661 */
8662 assert(image_info != (const ImageInfo *) NULL);
8663 assert(image_info->signature == MagickSignature);
8664 assert(image != (Image *) NULL);
8665 assert(image->signature == MagickSignature);
8666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8667 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8668 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8669 if (status == MagickFalse)
8670 return(status);
8671
8672 /*
8673 Allocate a MngInfo structure.
8674 */
8675 have_mng_structure=MagickFalse;
8676 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8677 if (mng_info == (MngInfo *) NULL)
8678 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8679 /*
8680 Initialize members of the MngInfo structure.
8681 */
8682 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8683 mng_info->image=image;
8684 have_mng_structure=MagickTrue;
8685 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8686
8687 /*
8688 * See if user has requested a specific PNG subformat to be used
8689 * for all of the PNGs in the MNG being written, e.g.,
8690 *
8691 * convert *.png png8:animation.mng
8692 *
8693 * To do: check -define png:bit_depth and png:color_type as well,
8694 * or perhaps use mng:bit_depth and mng:color_type instead for
8695 * global settings.
8696 */
8697
8698 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8699 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8700 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8701
8702 write_jng=MagickFalse;
8703 if (image_info->compression == JPEGCompression)
8704 write_jng=MagickTrue;
8705
8706 mng_info->adjoin=image_info->adjoin &&
8707 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8708
8709 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8710 optimize=MagickFalse;
8711 else
8712 optimize=(image_info->type == OptimizeType || image_info->type ==
8713 UndefinedType);
8714
8715 if (logging != MagickFalse)
8716 {
8717 /* Log some info about the input */
8718 Image
8719 *p;
8720
8721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8722 " Checking input image(s)");
8723 if (optimize)
8724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8725 " Optimize: TRUE");
8726 else
8727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8728 " Optimize: FALSE");
8729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8730 " Image_info depth: %ld",image_info->depth);
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Type: %d",image_info->type);
8733
8734 scene=0;
8735 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8736 {
8737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8738 " Scene: %ld",scene++);
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740 " Image depth: %lu",p->depth);
8741 if (p->matte)
8742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743 " Matte: True");
8744 else
8745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8746 " Matte: False");
8747 if (p->storage_class == PseudoClass)
8748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8749 " Storage class: PseudoClass");
8750 else
8751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8752 " Storage class: DirectClass");
8753 if (p->colors)
8754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8755 " Number of colors: %lu",p->colors);
8756 else
8757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8758 " Number of colors: unspecified");
8759 if (mng_info->adjoin == MagickFalse)
8760 break;
8761 }
8762 }
8763
8764 /*
8765 Sometimes we get PseudoClass images whose RGB values don't match
8766 the colors in the colormap. This code syncs the RGB values.
8767 */
8768 {
8769 Image
8770 *p;
8771
8772 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8773 {
8774 if (p->taint && p->storage_class == PseudoClass)
8775 (void) SyncImage(p);
8776 if (mng_info->adjoin == MagickFalse)
8777 break;
8778 }
8779 }
8780
8781#ifdef PNG_BUILD_PALETTE
8782 if (optimize)
8783 {
8784 /*
8785 Sometimes we get DirectClass images that have 256 colors or fewer.
8786 This code will convert them to PseudoClass and build a colormap.
8787 */
8788 Image
8789 *p;
8790
8791 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8792 {
8793 if (p->storage_class != PseudoClass)
8794 {
8795 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
8796 if (p->colors <= 256)
8797 {
8798 p->colors=0;
8799 if (p->matte != MagickFalse)
8800 (void) SetImageType(p,PaletteMatteType);
8801 else
8802 (void) SetImageType(p,PaletteType);
8803 }
8804 }
8805 if (mng_info->adjoin == MagickFalse)
8806 break;
8807 }
8808 }
8809#endif
8810
8811 use_global_plte=MagickFalse;
8812 all_images_are_gray=MagickFalse;
8813#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8814 need_local_plte=MagickTrue;
8815#endif
8816 need_defi=MagickFalse;
8817 need_matte=MagickFalse;
8818 mng_info->framing_mode=1;
8819 mng_info->old_framing_mode=1;
8820
8821 if (write_mng)
8822 if (image_info->page != (char *) NULL)
8823 {
8824 /*
8825 Determine image bounding box.
8826 */
8827 SetGeometry(image,&mng_info->page);
8828 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
8829 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
8830 }
8831 if (write_mng)
8832 {
8833 unsigned int
8834 need_geom;
8835
8836 unsigned short
8837 red,
8838 green,
8839 blue;
8840
8841 mng_info->page=image->page;
8842 need_geom=MagickTrue;
8843 if (mng_info->page.width || mng_info->page.height)
8844 need_geom=MagickFalse;
8845 /*
8846 Check all the scenes.
8847 */
8848 initial_delay=image->delay;
8849 need_iterations=MagickFalse;
8850 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
8851 mng_info->equal_physs=MagickTrue,
8852 mng_info->equal_gammas=MagickTrue;
8853 mng_info->equal_srgbs=MagickTrue;
8854 mng_info->equal_backgrounds=MagickTrue;
8855 image_count=0;
8856#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8857 defined(PNG_MNG_FEATURES_SUPPORTED)
8858 all_images_are_gray=MagickTrue;
8859 mng_info->equal_palettes=MagickFalse;
8860 need_local_plte=MagickFalse;
8861#endif
8862 for (next_image=image; next_image != (Image *) NULL; )
8863 {
8864 if (need_geom)
8865 {
8866 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
8867 mng_info->page.width=next_image->columns+next_image->page.x;
8868 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
8869 mng_info->page.height=next_image->rows+next_image->page.y;
8870 }
8871 if (next_image->page.x || next_image->page.y)
8872 need_defi=MagickTrue;
8873 if (next_image->matte)
8874 need_matte=MagickTrue;
8875 if ((int) next_image->dispose >= BackgroundDispose)
8876 if (next_image->matte || next_image->page.x || next_image->page.y ||
8877 ((next_image->columns < mng_info->page.width) &&
8878 (next_image->rows < mng_info->page.height)))
8879 mng_info->need_fram=MagickTrue;
8880 if (next_image->iterations)
8881 need_iterations=MagickTrue;
8882 final_delay=next_image->delay;
8883 if (final_delay != initial_delay || final_delay > 1UL*
8884 next_image->ticks_per_second)
8885 mng_info->need_fram=1;
8886#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8887 defined(PNG_MNG_FEATURES_SUPPORTED)
8888 /*
8889 check for global palette possibility.
8890 */
8891 if (image->matte != MagickFalse)
8892 need_local_plte=MagickTrue;
8893 if (need_local_plte == 0)
8894 {
8895 if (ImageIsGray(image) == MagickFalse)
8896 all_images_are_gray=MagickFalse;
8897 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
8898 if (use_global_plte == 0)
8899 use_global_plte=mng_info->equal_palettes;
8900 need_local_plte=!mng_info->equal_palettes;
8901 }
8902#endif
8903 if (GetNextImageInList(next_image) != (Image *) NULL)
8904 {
8905 if (next_image->background_color.red !=
8906 next_image->next->background_color.red ||
8907 next_image->background_color.green !=
8908 next_image->next->background_color.green ||
8909 next_image->background_color.blue !=
8910 next_image->next->background_color.blue)
8911 mng_info->equal_backgrounds=MagickFalse;
8912 if (next_image->gamma != next_image->next->gamma)
8913 mng_info->equal_gammas=MagickFalse;
8914 if (next_image->rendering_intent !=
8915 next_image->next->rendering_intent)
8916 mng_info->equal_srgbs=MagickFalse;
8917 if ((next_image->units != next_image->next->units) ||
8918 (next_image->x_resolution != next_image->next->x_resolution) ||
8919 (next_image->y_resolution != next_image->next->y_resolution))
8920 mng_info->equal_physs=MagickFalse;
8921 if (mng_info->equal_chrms)
8922 {
8923 if (next_image->chromaticity.red_primary.x !=
8924 next_image->next->chromaticity.red_primary.x ||
8925 next_image->chromaticity.red_primary.y !=
8926 next_image->next->chromaticity.red_primary.y ||
8927 next_image->chromaticity.green_primary.x !=
8928 next_image->next->chromaticity.green_primary.x ||
8929 next_image->chromaticity.green_primary.y !=
8930 next_image->next->chromaticity.green_primary.y ||
8931 next_image->chromaticity.blue_primary.x !=
8932 next_image->next->chromaticity.blue_primary.x ||
8933 next_image->chromaticity.blue_primary.y !=
8934 next_image->next->chromaticity.blue_primary.y ||
8935 next_image->chromaticity.white_point.x !=
8936 next_image->next->chromaticity.white_point.x ||
8937 next_image->chromaticity.white_point.y !=
8938 next_image->next->chromaticity.white_point.y)
8939 mng_info->equal_chrms=MagickFalse;
8940 }
8941 }
8942 image_count++;
8943 next_image=GetNextImageInList(next_image);
8944 }
8945 if (image_count < 2)
8946 {
8947 mng_info->equal_backgrounds=MagickFalse;
8948 mng_info->equal_chrms=MagickFalse;
8949 mng_info->equal_gammas=MagickFalse;
8950 mng_info->equal_srgbs=MagickFalse;
8951 mng_info->equal_physs=MagickFalse;
8952 use_global_plte=MagickFalse;
8953#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8954 need_local_plte=MagickTrue;
8955#endif
8956 need_iterations=MagickFalse;
8957 }
8958 if (mng_info->need_fram == MagickFalse)
8959 {
8960 /*
8961 Only certain framing rates 100/n are exactly representable without
8962 the FRAM chunk but we'll allow some slop in VLC files
8963 */
8964 if (final_delay == 0)
8965 {
8966 if (need_iterations != MagickFalse)
8967 {
8968 /*
8969 It's probably a GIF with loop; don't run it *too* fast.
8970 */
8971 final_delay=10;
8972 (void) ThrowMagickException(&image->exception,
8973 GetMagickModule(),CoderError,
8974 "input has zero delay between all frames; assuming 10 cs",
8975 "`%s'","");
8976 }
8977 else
8978 mng_info->ticks_per_second=0;
8979 }
8980 if (final_delay != 0)
8981 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
8982 if (final_delay > 50)
8983 mng_info->ticks_per_second=2;
8984 if (final_delay > 75)
8985 mng_info->ticks_per_second=1;
8986 if (final_delay > 125)
8987 mng_info->need_fram=MagickTrue;
8988 if (need_defi && final_delay > 2 && (final_delay != 4) &&
8989 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
8990 (final_delay != 25) && (final_delay != 50) && (final_delay !=
8991 1UL*image->ticks_per_second))
8992 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
8993 }
8994 if (mng_info->need_fram != MagickFalse)
8995 mng_info->ticks_per_second=1UL*image->ticks_per_second;
8996 /*
8997 If pseudocolor, we should also check to see if all the
8998 palettes are identical and write a global PLTE if they are.
8999 ../glennrp Feb 99.
9000 */
9001 /*
9002 Write the MNG version 1.0 signature and MHDR chunk.
9003 */
9004 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9005 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9006 PNGType(chunk,mng_MHDR);
9007 LogPNGChunk((int) logging,mng_MHDR,28L);
9008 PNGLong(chunk+4,mng_info->page.width);
9009 PNGLong(chunk+8,mng_info->page.height);
9010 PNGLong(chunk+12,mng_info->ticks_per_second);
9011 PNGLong(chunk+16,0L); /* layer count=unknown */
9012 PNGLong(chunk+20,0L); /* frame count=unknown */
9013 PNGLong(chunk+24,0L); /* play time=unknown */
9014 if (write_jng)
9015 {
9016 if (need_matte)
9017 {
9018 if (need_defi || mng_info->need_fram || use_global_plte)
9019 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9020 else
9021 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9022 }
9023 else
9024 {
9025 if (need_defi || mng_info->need_fram || use_global_plte)
9026 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9027 else
9028 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9029 }
9030 }
9031 else
9032 {
9033 if (need_matte)
9034 {
9035 if (need_defi || mng_info->need_fram || use_global_plte)
9036 PNGLong(chunk+28,11L); /* simplicity=LC */
9037 else
9038 PNGLong(chunk+28,9L); /* simplicity=VLC */
9039 }
9040 else
9041 {
9042 if (need_defi || mng_info->need_fram || use_global_plte)
9043 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9044 else
9045 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9046 }
9047 }
9048 (void) WriteBlob(image,32,chunk);
9049 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9050 option=GetImageOption(image_info,"mng:need-cacheoff");
9051 if (option != (const char *) NULL)
9052 {
9053 size_t
9054 length;
9055
9056 /*
9057 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9058 */
9059 PNGType(chunk,mng_nEED);
9060 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9061 (void) WriteBlobMSBULong(image,(unsigned long) length);
9062 LogPNGChunk((int) logging,mng_nEED,(unsigned long) length);
9063 length+=4;
9064 (void) WriteBlob(image,length,chunk);
9065 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9066 }
9067 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9068 (GetNextImageInList(image) != (Image *) NULL) &&
9069 (image->iterations != 1))
9070 {
9071 /*
9072 Write MNG TERM chunk
9073 */
9074 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9075 PNGType(chunk,mng_TERM);
9076 LogPNGChunk((int) logging,mng_TERM,10L);
9077 chunk[4]=3; /* repeat animation */
9078 chunk[5]=0; /* show last frame when done */
9079 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9080 final_delay/MagickMax(image->ticks_per_second,1)));
9081 if (image->iterations == 0)
9082 PNGLong(chunk+10,PNG_UINT_31_MAX);
9083 else
9084 PNGLong(chunk+10,(png_uint_32) image->iterations);
9085 if (logging != MagickFalse)
9086 {
9087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9088 " TERM delay: %lu",
9089 (png_uint_32) (mng_info->ticks_per_second*
9090 final_delay/MagickMax(image->ticks_per_second,1)));
9091 if (image->iterations == 0)
9092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9093 " TERM iterations: %lu",PNG_UINT_31_MAX);
9094 else
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096 " Image iterations: %lu",image->iterations);
9097 }
9098 (void) WriteBlob(image,14,chunk);
9099 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9100 }
9101 /*
9102 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9103 */
9104 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9105 mng_info->equal_srgbs)
9106 {
9107 /*
9108 Write MNG sRGB chunk
9109 */
9110 (void) WriteBlobMSBULong(image,1L);
9111 PNGType(chunk,mng_sRGB);
9112 LogPNGChunk((int) logging,mng_sRGB,1L);
9113 if (image->rendering_intent != UndefinedIntent)
9114 chunk[4]=(unsigned char) (image->rendering_intent-1);
9115 else
9116 chunk[4]=(unsigned char) (PerceptualIntent-1);
9117 (void) WriteBlob(image,5,chunk);
9118 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9119 mng_info->have_write_global_srgb=MagickTrue;
9120 }
9121 else
9122 {
9123 if (image->gamma && mng_info->equal_gammas)
9124 {
9125 /*
9126 Write MNG gAMA chunk
9127 */
9128 (void) WriteBlobMSBULong(image,4L);
9129 PNGType(chunk,mng_gAMA);
9130 LogPNGChunk((int) logging,mng_gAMA,4L);
9131 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
9132 (void) WriteBlob(image,8,chunk);
9133 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9134 mng_info->have_write_global_gama=MagickTrue;
9135 }
9136 if (mng_info->equal_chrms)
9137 {
9138 PrimaryInfo
9139 primary;
9140
9141 /*
9142 Write MNG cHRM chunk
9143 */
9144 (void) WriteBlobMSBULong(image,32L);
9145 PNGType(chunk,mng_cHRM);
9146 LogPNGChunk((int) logging,mng_cHRM,32L);
9147 primary=image->chromaticity.white_point;
9148 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
9149 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
9150 primary=image->chromaticity.red_primary;
9151 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
9152 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
9153 primary=image->chromaticity.green_primary;
9154 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
9155 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
9156 primary=image->chromaticity.blue_primary;
9157 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
9158 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
9159 (void) WriteBlob(image,36,chunk);
9160 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9161 mng_info->have_write_global_chrm=MagickTrue;
9162 }
9163 }
9164 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9165 {
9166 /*
9167 Write MNG pHYs chunk
9168 */
9169 (void) WriteBlobMSBULong(image,9L);
9170 PNGType(chunk,mng_pHYs);
9171 LogPNGChunk((int) logging,mng_pHYs,9L);
9172 if (image->units == PixelsPerInchResolution)
9173 {
9174 PNGLong(chunk+4,(unsigned long)
9175 (image->x_resolution*100.0/2.54+0.5));
9176 PNGLong(chunk+8,(unsigned long)
9177 (image->y_resolution*100.0/2.54+0.5));
9178 chunk[12]=1;
9179 }
9180 else
9181 {
9182 if (image->units == PixelsPerCentimeterResolution)
9183 {
9184 PNGLong(chunk+4,(unsigned long)
9185 (image->x_resolution*100.0+0.5));
9186 PNGLong(chunk+8,(unsigned long)
9187 (image->y_resolution*100.0+0.5));
9188 chunk[12]=1;
9189 }
9190 else
9191 {
9192 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
9193 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
9194 chunk[12]=0;
9195 }
9196 }
9197 (void) WriteBlob(image,13,chunk);
9198 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9199 }
9200 /*
9201 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9202 or does not cover the entire frame.
9203 */
9204 if (write_mng && (image->matte || image->page.x > 0 ||
9205 image->page.y > 0 || (image->page.width &&
9206 (image->page.width+image->page.x < mng_info->page.width))
9207 || (image->page.height && (image->page.height+image->page.y
9208 < mng_info->page.height))))
9209 {
9210 (void) WriteBlobMSBULong(image,6L);
9211 PNGType(chunk,mng_BACK);
9212 LogPNGChunk((int) logging,mng_BACK,6L);
9213 red=ScaleQuantumToShort(image->background_color.red);
9214 green=ScaleQuantumToShort(image->background_color.green);
9215 blue=ScaleQuantumToShort(image->background_color.blue);
9216 PNGShort(chunk+4,red);
9217 PNGShort(chunk+6,green);
9218 PNGShort(chunk+8,blue);
9219 (void) WriteBlob(image,10,chunk);
9220 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9221 if (mng_info->equal_backgrounds)
9222 {
9223 (void) WriteBlobMSBULong(image,6L);
9224 PNGType(chunk,mng_bKGD);
9225 LogPNGChunk((int) logging,mng_bKGD,6L);
9226 (void) WriteBlob(image,10,chunk);
9227 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9228 }
9229 }
9230
9231#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9232 if ((need_local_plte == MagickFalse) &&
9233 (image->storage_class == PseudoClass) &&
9234 (all_images_are_gray == MagickFalse))
9235 {
9236 unsigned long
9237 data_length;
9238
9239 /*
9240 Write MNG PLTE chunk
9241 */
9242 data_length=3*image->colors;
9243 (void) WriteBlobMSBULong(image,data_length);
9244 PNGType(chunk,mng_PLTE);
9245 LogPNGChunk((int) logging,mng_PLTE,data_length);
9246 for (i=0; i < (long) image->colors; i++)
9247 {
9248 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9249 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9250 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9251 }
9252 (void) WriteBlob(image,data_length+4,chunk);
9253 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9254 mng_info->have_write_global_plte=MagickTrue;
9255 }
9256#endif
9257 }
9258 scene=0;
9259 mng_info->delay=0;
9260#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9261 defined(PNG_MNG_FEATURES_SUPPORTED)
9262 mng_info->equal_palettes=MagickFalse;
9263#endif
9264 do
9265 {
9266 if (mng_info->adjoin)
9267 {
9268#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9269 defined(PNG_MNG_FEATURES_SUPPORTED)
9270 /*
9271 If we aren't using a global palette for the entire MNG, check to
9272 see if we can use one for two or more consecutive images.
9273 */
9274 if (need_local_plte && use_global_plte && !all_images_are_gray)
9275 {
9276 if (mng_info->IsPalette)
9277 {
9278 /*
9279 When equal_palettes is true, this image has the same palette
9280 as the previous PseudoClass image
9281 */
9282 mng_info->have_write_global_plte=mng_info->equal_palettes;
9283 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9284 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9285 {
9286 /*
9287 Write MNG PLTE chunk
9288 */
9289 unsigned long
9290 data_length;
9291
9292 data_length=3*image->colors;
9293 (void) WriteBlobMSBULong(image,data_length);
9294 PNGType(chunk,mng_PLTE);
9295 LogPNGChunk((int) logging,mng_PLTE,data_length);
9296 for (i=0; i < (long) image->colors; i++)
9297 {
9298 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9299 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9300 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9301 }
9302 (void) WriteBlob(image,data_length+4,chunk);
9303 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9304 (uInt) (data_length+4)));
9305 mng_info->have_write_global_plte=MagickTrue;
9306 }
9307 }
9308 else
9309 mng_info->have_write_global_plte=MagickFalse;
9310 }
9311#endif
9312 if (need_defi)
9313 {
9314 long
9315 previous_x,
9316 previous_y;
9317
9318 if (scene)
9319 {
9320 previous_x=mng_info->page.x;
9321 previous_y=mng_info->page.y;
9322 }
9323 else
9324 {
9325 previous_x=0;
9326 previous_y=0;
9327 }
9328 mng_info->page=image->page;
9329 if ((mng_info->page.x != previous_x) ||
9330 (mng_info->page.y != previous_y))
9331 {
9332 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9333 PNGType(chunk,mng_DEFI);
9334 LogPNGChunk((int) logging,mng_DEFI,12L);
9335 chunk[4]=0; /* object 0 MSB */
9336 chunk[5]=0; /* object 0 LSB */
9337 chunk[6]=0; /* visible */
9338 chunk[7]=0; /* abstract */
9339 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9340 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9341 (void) WriteBlob(image,16,chunk);
9342 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9343 }
9344 }
9345 }
9346
9347 mng_info->write_mng=write_mng;
9348
9349 if ((int) image->dispose >= 3)
9350 mng_info->framing_mode=3;
9351
9352 if (mng_info->need_fram && mng_info->adjoin &&
9353 ((image->delay != mng_info->delay) ||
9354 (mng_info->framing_mode != mng_info->old_framing_mode)))
9355 {
9356 if (image->delay == mng_info->delay)
9357 {
9358 /*
9359 Write a MNG FRAM chunk with the new framing mode.
9360 */
9361 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9362 PNGType(chunk,mng_FRAM);
9363 LogPNGChunk((int) logging,mng_FRAM,1L);
9364 chunk[4]=(unsigned char) mng_info->framing_mode;
9365 (void) WriteBlob(image,5,chunk);
9366 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9367 }
9368 else
9369 {
9370 /*
9371 Write a MNG FRAM chunk with the delay.
9372 */
9373 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9374 PNGType(chunk,mng_FRAM);
9375 LogPNGChunk((int) logging,mng_FRAM,10L);
9376 chunk[4]=(unsigned char) mng_info->framing_mode;
9377 chunk[5]=0; /* frame name separator (no name) */
9378 chunk[6]=2; /* flag for changing default delay */
9379 chunk[7]=0; /* flag for changing frame timeout */
9380 chunk[8]=0; /* flag for changing frame clipping */
9381 chunk[9]=0; /* flag for changing frame sync_id */
9382 PNGLong(chunk+10,(png_uint_32)
9383 ((mng_info->ticks_per_second*
9384 image->delay)/MagickMax(image->ticks_per_second,1)));
9385 (void) WriteBlob(image,14,chunk);
9386 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9387 mng_info->delay=image->delay;
9388 }
9389 mng_info->old_framing_mode=mng_info->framing_mode;
9390 }
9391
9392#if defined(JNG_SUPPORTED)
9393 if (image_info->compression == JPEGCompression)
9394 {
9395 ImageInfo
9396 *write_info;
9397
9398 if (logging != MagickFalse)
9399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400 " Writing JNG object.");
9401 /* To do: specify the desired alpha compression method. */
9402 write_info=CloneImageInfo(image_info);
9403 write_info->compression=UndefinedCompression;
9404 status=WriteOneJNGImage(mng_info,write_info,image);
9405 write_info=DestroyImageInfo(write_info);
9406 }
9407 else
9408#endif
9409 {
9410 if (logging != MagickFalse)
9411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9412 " Writing PNG object.");
9413 status=WriteOnePNGImage(mng_info,image_info,image);
9414 }
9415
9416 if (status == MagickFalse)
9417 {
9418 MngInfoFreeStruct(mng_info,&have_mng_structure);
9419 (void) CloseBlob(image);
9420 return(MagickFalse);
9421 }
9422 (void) CatchImageException(image);
9423 if (GetNextImageInList(image) == (Image *) NULL)
9424 break;
9425 image=SyncNextImageInList(image);
9426 status=SetImageProgress(image,SaveImagesTag,scene++,
9427 GetImageListLength(image));
9428 if (status == MagickFalse)
9429 break;
9430 } while (mng_info->adjoin);
9431 if (write_mng)
9432 {
9433 while (GetPreviousImageInList(image) != (Image *) NULL)
9434 image=GetPreviousImageInList(image);
9435 /*
9436 Write the MEND chunk.
9437 */
9438 (void) WriteBlobMSBULong(image,0x00000000L);
9439 PNGType(chunk,mng_MEND);
9440 LogPNGChunk((int) logging,mng_MEND,0L);
9441 (void) WriteBlob(image,4,chunk);
9442 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9443 }
9444 /*
9445 Relinquish resources.
9446 */
9447 (void) CloseBlob(image);
9448 MngInfoFreeStruct(mng_info,&have_mng_structure);
9449 if (logging != MagickFalse)
9450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9451 return(MagickTrue);
9452}
9453#else /* PNG_LIBPNG_VER > 95 */
9454static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9455{
9456 image=image;
9457 printf("Your PNG library is too old: You have libpng-%s\n",
9458 PNG_LIBPNG_VER_STRING);
9459 ThrowBinaryException(CoderError,"PNG library is too old",
9460 image_info->filename);
9461}
9462static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9463{
9464 return(WritePNGImage(image_info,image));
9465}
9466#endif /* PNG_LIBPNG_VER > 95 */
9467#endif