2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
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. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "magick/studio.h"
45 #include "magick/attribute.h"
46 #include "magick/blob.h"
47 #include "magick/blob-private.h"
48 #include "magick/cache.h"
49 #include "magick/color.h"
50 #include "magick/color-private.h"
51 #include "magick/colormap.h"
52 #include "magick/colorspace.h"
53 #include "magick/constitute.h"
54 #include "magick/enhance.h"
55 #include "magick/exception.h"
56 #include "magick/exception-private.h"
57 #include "magick/geometry.h"
58 #include "magick/histogram.h"
59 #include "magick/image.h"
60 #include "magick/image-private.h"
61 #include "magick/layer.h"
62 #include "magick/list.h"
63 #include "magick/log.h"
64 #include "magick/magick.h"
65 #include "magick/memory_.h"
66 #include "magick/module.h"
67 #include "magick/monitor.h"
68 #include "magick/monitor-private.h"
69 #include "magick/option.h"
70 #include "magick/quantum-private.h"
71 #include "magick/profile.h"
72 #include "magick/property.h"
73 #include "magick/quantize.h"
74 #include "magick/resource_.h"
75 #include "magick/semaphore.h"
76 #include "magick/quantum-private.h"
77 #include "magick/static.h"
78 #include "magick/statistic.h"
79 #include "magick/string_.h"
80 #include "magick/string-private.h"
81 #include "magick/transform.h"
82 #include "magick/utility.h"
83 #if defined(MAGICKCORE_PNG_DELEGATE)
85 /* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
87 * migration to libpng-1.5, remove these defines and then
88 * fix any code that generates warnings.
90 /* #define PNG_DEPRECATED Use of this function is deprecated */
91 /* #define PNG_USE_RESULT The result of this function must be checked */
92 /* #define PNG_NORETURN This function does not return */
93 /* #define PNG_ALLOCATED The result of the function is new memory */
94 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
99 /* ImageMagick differences */
100 #define first_scene scene
102 #if PNG_LIBPNG_VER > 10011
104 Optional declarations. Define or undefine them as you like.
106 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
109 Features under construction. Define these to work on them.
111 #undef MNG_OBJECT_BUFFERS
112 #undef MNG_BASI_SUPPORTED
113 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
114 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
115 #define PNG_BUILD_PALETTE /* This works as of 5.4.3. */
116 #define PNG_SORT_PALETTE /* This works as of 5.4.0. */
117 #if defined(MAGICKCORE_JPEG_DELEGATE)
118 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120 #if !defined(RGBColorMatchExact)
121 #define IsPNGColorEqual(color,target) \
122 (((color).red == (target).red) && \
123 ((color).green == (target).green) && \
124 ((color).blue == (target).blue))
128 Establish thread safety.
129 setjmp/longjmp is claimed to be safe on these platforms:
130 setjmp/longjmp is alleged to be unsafe on these platforms:
132 #ifndef SETJMP_IS_THREAD_SAFE
133 #define PNG_SETJMP_NOT_THREAD_SAFE
136 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
138 *png_semaphore = (SemaphoreInfo *) NULL;
142 This temporary until I set up malloc'ed object attributes array.
143 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
146 #define MNG_MAX_OBJECTS 256
149 If this not defined, spec is interpreted strictly. If it is
150 defined, an attempt will be made to recover from some errors,
152 o global PLTE too short
157 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
158 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
159 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
160 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
161 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
162 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
163 will be enabled by default in libpng-1.2.0.
165 #ifdef PNG_MNG_FEATURES_SUPPORTED
166 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
167 # define PNG_READ_EMPTY_PLTE_SUPPORTED
169 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
170 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
175 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
176 This macro is only defined in libpng-1.0.3 and later.
177 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
179 #ifndef PNG_UINT_31_MAX
180 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
184 Constant strings for known chunk types. If you need to add a chunk,
185 add a string holding the name here. To make the code more
186 portable, we use ASCII numbers like this, not characters.
189 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
190 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
191 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
192 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
193 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
194 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
195 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
196 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
197 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
198 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
199 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
200 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
201 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
202 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
203 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
204 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
205 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
206 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
207 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
208 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
209 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
210 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
211 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
212 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
213 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
214 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
215 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
216 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
217 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
218 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
219 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
220 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
221 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
222 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
224 #if defined(JNG_SUPPORTED)
225 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
226 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
227 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
228 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
229 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
230 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
234 Other known chunks that are not yet supported by ImageMagick:
235 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
236 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
237 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
238 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
239 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
240 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
241 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
242 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
245 typedef struct _UShortPixelPacket
255 typedef struct _MngBox
264 typedef struct _MngPair
271 #ifdef MNG_OBJECT_BUFFERS
272 typedef struct _MngBuffer
304 typedef struct _MngInfo
307 #ifdef MNG_OBJECT_BUFFERS
309 *ob[MNG_MAX_OBJECTS];
320 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
321 bytes_in_read_buffer,
327 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
328 defined(PNG_MNG_FEATURES_SUPPORTED)
340 have_saved_bkgd_index,
341 have_write_global_chrm,
342 have_write_global_gama,
343 have_write_global_plte,
344 have_write_global_srgb,
359 x_off[MNG_MAX_OBJECTS],
360 y_off[MNG_MAX_OBJECTS];
366 object_clip[MNG_MAX_OBJECTS];
369 /* These flags could be combined into one byte */
370 exists[MNG_MAX_OBJECTS],
371 frozen[MNG_MAX_OBJECTS],
373 invisible[MNG_MAX_OBJECTS],
374 viewable[MNG_MAX_OBJECTS];
386 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
404 global_x_pixels_per_unit,
405 global_y_pixels_per_unit,
412 global_phys_unit_type,
431 #ifdef MNG_BASI_SUPPORTED
439 basi_compression_method,
441 basi_interlace_method,
468 Forward declarations.
470 static MagickBooleanType
471 WritePNGImage(const ImageInfo *,Image *);
472 static MagickBooleanType
473 WriteMNGImage(const ImageInfo *,Image *);
474 #if defined(JNG_SUPPORTED)
475 static MagickBooleanType
476 WriteJNGImage(const ImageInfo *,Image *);
479 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
485 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
492 #if PNG_LIBPNG_VER > 10011
493 #if defined(PNG_SORT_PALETTE)
495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
499 % 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 %
503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 % CompressColormapTransFirst compresses an image colormap removing
506 % any duplicate and unused color entries and putting the transparent colors
507 % first. Returns MagickTrue on success, MagickFalse on error.
509 % The format of the CompressColormapTransFirst method is:
511 % unsigned int CompressColormapTransFirst(Image *image)
513 % A description of each parameter follows:
515 % o image: the address of a structure of type Image.
516 % This function updates image->colors and image->colormap.
519 static MagickBooleanType CompressColormapTransFirst(Image *image)
534 register const IndexPacket
537 register const PixelPacket
556 Determine if colormap can be compressed.
558 assert(image != (Image *) NULL);
559 assert(image->signature == MagickSignature);
560 if (image->debug != MagickFalse)
561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
562 " CompressColorMapTransFirst %s (%.20g colors)",image->filename,
563 (double) image->colors);
564 if (image->storage_class != PseudoClass || image->colors > 256 ||
567 if (image->debug != MagickFalse)
569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
570 " Could not compress colormap");
571 if (image->colors > 256 || image->colors == 0)
577 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
578 if (marker == (unsigned char *) NULL)
579 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
581 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
582 if (opacity == (IndexPacket *) NULL)
584 marker=(unsigned char *) RelinquishMagickMemory(marker);
585 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
589 Mark colors that are present.
591 number_colors=(ssize_t) image->colors;
592 for (i=0; i < number_colors; i++)
594 marker[i]=MagickFalse;
595 opacity[i]=OpaqueOpacity;
598 for (y=0; y < (ssize_t) image->rows; y++)
600 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
601 if (p == (const PixelPacket *) NULL)
603 indexes=GetVirtualIndexQueue(image);
604 if (image->matte != MagickFalse)
605 for (x=0; x < (ssize_t) image->columns; x++)
607 marker[(int) indexes[x]]=MagickTrue;
608 opacity[(int) indexes[x]]=GetOpacityPixelComponent(p);
609 if (indexes[x] > top_used)
614 for (x=0; x < (ssize_t) image->columns; x++)
616 marker[(int) indexes[x]]=MagickTrue;
617 if (indexes[x] > top_used)
622 if (image->matte != MagickFalse)
625 Mark background color, topmost occurrence if more than one.
627 for (i=number_colors-1; i; i--)
629 if (IsColorEqual(image->colormap+i,&image->background_color))
631 marker[i]=MagickTrue;
639 for (i=0; i < number_colors-1; i++)
642 for (j=i+1; j < number_colors; j++)
643 if ((opacity[i] == opacity[j]) &&
644 (IsColorEqual(image->colormap+i,image->colormap+j)))
645 marker[j]=MagickFalse;
648 Count colors that still remain.
650 have_transparency=MagickFalse;
652 for (i=0; i < number_colors; i++)
656 if (opacity[i] != OpaqueOpacity)
657 have_transparency=MagickTrue;
659 if ((!have_transparency || (marker[0] &&
660 (opacity[0] == (Quantum) TransparentOpacity)))
661 && (new_number_colors == number_colors))
664 No duplicate or unused entries, and transparency-swap not needed.
666 marker=(unsigned char *) RelinquishMagickMemory(marker);
667 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
671 remap_needed=MagickFalse;
672 if ((ssize_t) top_used >= new_number_colors)
673 remap_needed=MagickTrue;
679 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
681 if (colormap == (PixelPacket *) NULL)
683 marker=(unsigned char *) RelinquishMagickMemory(marker);
684 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
685 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
689 Eliminate unused colormap entries.
691 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
693 if (map == (IndexPacket *) NULL)
695 marker=(unsigned char *) RelinquishMagickMemory(marker);
696 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
697 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
698 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
702 for (i=0; i < number_colors; i++)
704 map[i]=(IndexPacket) k;
707 for (j=i+1; j < number_colors; j++)
709 if ((opacity[i] == opacity[j]) &&
710 (IsColorEqual(image->colormap+i,image->colormap+j)))
712 map[j]=(IndexPacket) k;
713 marker[j]=MagickFalse;
720 for (i=0; i < number_colors; i++)
724 colormap[j]=image->colormap[i];
728 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
731 Move the first transparent color to palette entry 0.
733 for (i=1; i < number_colors; i++)
735 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
740 temp_colormap=colormap[0];
741 colormap[0]=colormap[(int) map[i]];
742 colormap[(ssize_t) map[i]]=temp_colormap;
743 for (j=0; j < number_colors; j++)
747 else if (map[j] == map[i])
750 remap_needed=MagickTrue;
756 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
757 marker=(unsigned char *) RelinquishMagickMemory(marker);
773 exception=(&image->exception);
774 for (y=0; y < (ssize_t) image->rows; y++)
776 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
777 if (q == (PixelPacket *) NULL)
779 pixels=GetAuthenticIndexQueue(image);
780 for (x=0; x < (ssize_t) image->columns; x++)
785 if (SyncAuthenticPixels(image,exception) == MagickFalse)
788 for (i=0; i < new_number_colors; i++)
789 image->colormap[i]=colormap[i];
791 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
792 image->colors=(size_t) new_number_colors;
793 map=(IndexPacket *) RelinquishMagickMemory(map);
799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 % I m a g e I s G r a y %
807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809 % Like IsGrayImage except does not change DirectClass to PseudoClass %
811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 static MagickBooleanType ImageIsGray(Image *image)
815 register const PixelPacket
823 assert(image != (Image *) NULL);
824 assert(image->signature == MagickSignature);
825 if (image->debug != MagickFalse)
826 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
828 if (image->storage_class == PseudoClass)
830 for (i=0; i < (ssize_t) image->colors; i++)
831 if (IsGray(image->colormap+i) == MagickFalse)
835 for (y=0; y < (ssize_t) image->rows; y++)
837 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
838 if (p == (const PixelPacket *) NULL)
840 for (x=(ssize_t) image->columns-1; x >= 0; x--)
842 if (IsGray(p) == MagickFalse)
851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 % I m a g e I s M o n o c h r o m e %
859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861 % Like IsMonochromeImage except does not change DirectClass to PseudoClass %
862 % and is more accurate. %
864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866 static MagickBooleanType ImageIsMonochrome(Image *image)
868 register const PixelPacket
876 assert(image != (Image *) NULL);
877 assert(image->signature == MagickSignature);
878 if (image->debug != MagickFalse)
879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
880 if (image->storage_class == PseudoClass)
882 for (i=0; i < (ssize_t) image->colors; i++)
884 if ((IsGray(image->colormap+i) == MagickFalse) ||
885 ((image->colormap[i].red != 0) &&
886 (image->colormap[i].red != (Quantum) QuantumRange)))
891 for (y=0; y < (ssize_t) image->rows; y++)
893 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
894 if (p == (const PixelPacket *) NULL)
896 for (x=(ssize_t) image->columns-1; x >= 0; x--)
898 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
900 if (IsGray(p) == MagickFalse)
902 if ((p->opacity != OpaqueOpacity) &&
903 (p->opacity != (Quantum) TransparentOpacity))
910 #endif /* PNG_LIBPNG_VER > 10011 */
911 #endif /* MAGICKCORE_PNG_DELEGATE */
914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924 % IsMNG() returns MagickTrue if the image format type, identified by the
925 % magick string, is MNG.
927 % The format of the IsMNG method is:
929 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
931 % A description of each parameter follows:
933 % o magick: compare image format pattern against these bytes.
935 % o length: Specifies the length of the magick string.
939 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
943 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959 % IsJNG() returns MagickTrue if the image format type, identified by the
960 % magick string, is JNG.
962 % The format of the IsJNG method is:
964 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
966 % A description of each parameter follows:
968 % o magick: compare image format pattern against these bytes.
970 % o length: Specifies the length of the magick string.
974 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
978 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994 % IsPNG() returns MagickTrue if the image format type, identified by the
995 % magick string, is PNG.
997 % The format of the IsPNG method is:
999 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1001 % A description of each parameter follows:
1003 % o magick: compare image format pattern against these bytes.
1005 % o length: Specifies the length of the magick string.
1008 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1011 return(MagickFalse);
1012 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1014 return(MagickFalse);
1017 #if defined(MAGICKCORE_PNG_DELEGATE)
1018 #if defined(__cplusplus) || defined(c_plusplus)
1022 #if (PNG_LIBPNG_VER > 10011)
1023 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1028 assert(image != (Image *) NULL);
1029 assert(image->signature == MagickSignature);
1030 buffer[0]=(unsigned char) (value >> 24);
1031 buffer[1]=(unsigned char) (value >> 16);
1032 buffer[2]=(unsigned char) (value >> 8);
1033 buffer[3]=(unsigned char) value;
1034 return((size_t) WriteBlob(image,4,buffer));
1037 static void PNGLong(png_bytep p,png_uint_32 value)
1039 *p++=(png_byte) ((value >> 24) & 0xff);
1040 *p++=(png_byte) ((value >> 16) & 0xff);
1041 *p++=(png_byte) ((value >> 8) & 0xff);
1042 *p++=(png_byte) (value & 0xff);
1045 static void PNGsLong(png_bytep p,png_int_32 value)
1047 *p++=(png_byte) ((value >> 24) & 0xff);
1048 *p++=(png_byte) ((value >> 16) & 0xff);
1049 *p++=(png_byte) ((value >> 8) & 0xff);
1050 *p++=(png_byte) (value & 0xff);
1053 static void PNGShort(png_bytep p,png_uint_16 value)
1055 *p++=(png_byte) ((value >> 8) & 0xff);
1056 *p++=(png_byte) (value & 0xff);
1059 static void PNGType(png_bytep p,png_bytep type)
1061 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1064 static void LogPNGChunk(int logging, png_bytep type, size_t length)
1066 if (logging != MagickFalse)
1067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1068 " Writing %c%c%c%c chunk, length: %.20g",
1069 type[0],type[1],type[2],type[3],(double) length);
1071 #endif /* PNG_LIBPNG_VER > 10011 */
1073 #if defined(__cplusplus) || defined(c_plusplus)
1077 #if PNG_LIBPNG_VER > 10011
1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083 % R e a d P N G I m a g e %
1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1090 % Multiple-image Network Graphics (MNG) image file and returns it. It
1091 % allocates the memory necessary for the new Image structure and returns a
1092 % pointer to the new image or set of images.
1094 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1096 % The format of the ReadPNGImage method is:
1098 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1100 % A description of each parameter follows:
1102 % o image_info: the image info.
1104 % o exception: return any errors or warnings in this structure.
1106 % To do, more or less in chronological order (as of version 5.5.2,
1107 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1109 % Get 16-bit cheap transparency working.
1111 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1113 % Preserve all unknown and not-yet-handled known chunks found in input
1114 % PNG file and copy them into output PNG files according to the PNG
1117 % (At this point, PNG encoding should be in full MNG compliance)
1119 % Provide options for choice of background to use when the MNG BACK
1120 % chunk is not present or is not mandatory (i.e., leave transparent,
1121 % user specified, MNG BACK, PNG bKGD)
1123 % Implement LOOP/ENDL [done, but could do discretionary loops more
1124 % efficiently by linking in the duplicate frames.].
1126 % Decode and act on the MHDR simplicity profile (offer option to reject
1127 % files or attempt to process them anyway when the profile isn't LC or VLC).
1129 % Upgrade to full MNG without Delta-PNG.
1131 % o BACK [done a while ago except for background image ID]
1132 % o MOVE [done 15 May 1999]
1133 % o CLIP [done 15 May 1999]
1134 % o DISC [done 19 May 1999]
1135 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1136 % o SEEK [partially done 19 May 1999 (discard function only)]
1140 % o MNG-level tEXt/iTXt/zTXt
1145 % o iTXt (wait for libpng implementation).
1147 % Use the scene signature to discover when an identical scene is
1148 % being reused, and just point to the original image->exception instead
1149 % of storing another set of pixels. This not specific to MNG
1150 % but could be applied generally.
1152 % Upgrade to full MNG with Delta-PNG.
1154 % JNG tEXt/iTXt/zTXt
1156 % We will not attempt to read files containing the CgBI chunk.
1157 % They are really Xcode files meant for display on the iPhone.
1158 % These are not valid PNG files and it is impossible to recover
1159 % the orginal PNG from files that have been converted to Xcode-PNG,
1160 % since irretrievable loss of color data has occurred due to the
1161 % use of premultiplied alpha.
1164 #if defined(__cplusplus) || defined(c_plusplus)
1169 This the function that does the actual reading of data. It is
1170 the same as the one supplied in libpng, except that it receives the
1171 datastream from the ReadBlob() function instead of standard input.
1173 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1178 image=(Image *) png_get_io_ptr(png_ptr);
1184 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1185 if (check != length)
1190 (void) FormatMagickString(msg,MaxTextExtent,
1191 "Expected %.20g bytes; found %.20g bytes",(double) length,
1193 png_warning(png_ptr,msg);
1194 png_error(png_ptr,"Read Exception");
1199 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1200 !defined(PNG_MNG_FEATURES_SUPPORTED)
1201 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1202 * older than libpng-1.0.3a, which was the first to allow the empty
1203 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1204 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1205 * encountered after an empty PLTE, so we have to look ahead for bKGD
1206 * chunks and remove them from the datastream that is passed to libpng,
1207 * and store their contents for later use.
1209 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1224 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1225 image=(Image *) mng_info->image;
1226 while (mng_info->bytes_in_read_buffer && length)
1228 data[i]=mng_info->read_buffer[i];
1229 mng_info->bytes_in_read_buffer--;
1235 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1236 if (check != length)
1237 png_error(png_ptr,"Read Exception");
1240 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1243 check=(png_size_t) ReadBlob(image,(size_t) length,
1244 (char *) mng_info->read_buffer);
1245 mng_info->read_buffer[4]=0;
1246 mng_info->bytes_in_read_buffer=4;
1247 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1248 mng_info->found_empty_plte=MagickTrue;
1249 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1251 mng_info->found_empty_plte=MagickFalse;
1252 mng_info->have_saved_bkgd_index=MagickFalse;
1255 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1258 check=(png_size_t) ReadBlob(image,(size_t) length,
1259 (char *) mng_info->read_buffer);
1260 mng_info->read_buffer[4]=0;
1261 mng_info->bytes_in_read_buffer=4;
1262 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1263 if (mng_info->found_empty_plte)
1266 Skip the bKGD data byte and CRC.
1269 ReadBlob(image,5,(char *) mng_info->read_buffer);
1270 check=(png_size_t) ReadBlob(image,(size_t) length,
1271 (char *) mng_info->read_buffer);
1272 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1273 mng_info->have_saved_bkgd_index=MagickTrue;
1274 mng_info->bytes_in_read_buffer=0;
1282 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1287 image=(Image *) png_get_io_ptr(png_ptr);
1293 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1294 if (check != length)
1295 png_error(png_ptr,"WriteBlob Failed");
1299 static void png_flush_data(png_structp png_ptr)
1304 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1305 static int PalettesAreEqual(Image *a,Image *b)
1310 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1311 return((int) MagickFalse);
1312 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1313 return((int) MagickFalse);
1314 if (a->colors != b->colors)
1315 return((int) MagickFalse);
1316 for (i=0; i < (ssize_t) a->colors; i++)
1318 if ((a->colormap[i].red != b->colormap[i].red) ||
1319 (a->colormap[i].green != b->colormap[i].green) ||
1320 (a->colormap[i].blue != b->colormap[i].blue))
1321 return((int) MagickFalse);
1323 return((int) MagickTrue);
1327 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1329 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1330 mng_info->exists[i] && !mng_info->frozen[i])
1332 #ifdef MNG_OBJECT_BUFFERS
1333 if (mng_info->ob[i] != (MngBuffer *) NULL)
1335 if (mng_info->ob[i]->reference_count > 0)
1336 mng_info->ob[i]->reference_count--;
1337 if (mng_info->ob[i]->reference_count == 0)
1339 if (mng_info->ob[i]->image != (Image *) NULL)
1340 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1341 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1344 mng_info->ob[i]=(MngBuffer *) NULL;
1346 mng_info->exists[i]=MagickFalse;
1347 mng_info->invisible[i]=MagickFalse;
1348 mng_info->viewable[i]=MagickFalse;
1349 mng_info->frozen[i]=MagickFalse;
1350 mng_info->x_off[i]=0;
1351 mng_info->y_off[i]=0;
1352 mng_info->object_clip[i].left=0;
1353 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1354 mng_info->object_clip[i].top=0;
1355 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1359 static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1361 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1366 for (i=1; i < MNG_MAX_OBJECTS; i++)
1367 MngInfoDiscardObject(mng_info,i);
1368 if (mng_info->global_plte != (png_colorp) NULL)
1369 mng_info->global_plte=(png_colorp)
1370 RelinquishMagickMemory(mng_info->global_plte);
1371 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1372 *have_mng_structure=MagickFalse;
1376 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1382 if (box.left < box2.left)
1384 if (box.top < box2.top)
1386 if (box.right > box2.right)
1387 box.right=box2.right;
1388 if (box.bottom > box2.bottom)
1389 box.bottom=box2.bottom;
1393 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1399 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1401 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1402 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1403 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1404 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1405 if (delta_type != 0)
1407 box.left+=previous_box.left;
1408 box.right+=previous_box.right;
1409 box.top+=previous_box.top;
1410 box.bottom+=previous_box.bottom;
1415 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1421 Read two ssize_ts from CLON, MOVE or PAST chunk
1423 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1424 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1425 if (delta_type != 0)
1427 pair.a+=previous_pair.a;
1428 pair.b+=previous_pair.b;
1433 static long mng_get_long(unsigned char *p)
1435 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1438 static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1443 image=(Image *) png_get_error_ptr(ping);
1444 if (image->debug != MagickFalse)
1445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1446 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1447 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1448 message,"`%s'",image->filename);
1449 #if PNG_LIBPNG_VER < 10500
1450 longjmp(ping->jmpbuf,1);
1452 png_longjmp(ping,1);
1456 static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1461 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1462 png_error(ping, message);
1463 image=(Image *) png_get_error_ptr(ping);
1464 if (image->debug != MagickFalse)
1465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1466 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1467 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1468 message,"`%s'",image->filename);
1471 #ifdef PNG_USER_MEM_SUPPORTED
1472 static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1474 #if (PNG_LIBPNG_VER < 10011)
1479 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1481 png_error("Insufficient memory.");
1485 return((png_voidp) AcquireMagickMemory((size_t) size));
1490 Free a pointer. It is removed from the list at the same time.
1492 static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1495 ptr=RelinquishMagickMemory(ptr);
1496 return((png_free_ptr) NULL);
1500 #if defined(__cplusplus) || defined(c_plusplus)
1505 png_read_raw_profile(Image *image, const ImageInfo *image_info,
1506 png_textp text,int ii)
1511 register unsigned char
1525 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1526 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1527 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1528 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1529 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1533 /* look for newline */
1536 /* look for length */
1537 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1539 length=(png_uint_32) StringToLong(sp);
1540 while (*sp != ' ' && *sp != '\n')
1542 /* allocate space */
1545 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1546 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1547 return(MagickFalse);
1549 profile=AcquireStringInfo(length);
1550 if (profile == (StringInfo *) NULL)
1552 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1553 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1554 "unable to copy profile");
1555 return(MagickFalse);
1557 /* copy profile, skipping white space and column 1 "=" signs */
1558 dp=GetStringInfoDatum(profile);
1560 for (i=0; i < (ssize_t) nibbles; i++)
1562 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1566 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1567 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1568 profile=DestroyStringInfo(profile);
1569 return(MagickFalse);
1574 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1576 (*dp++)+=unhex[(int) *sp++];
1579 We have already read "Raw profile type.
1581 (void) SetImageProfile(image,&text[ii].key[17],profile);
1582 profile=DestroyStringInfo(profile);
1583 if (image_info->verbose)
1584 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1588 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1589 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1595 /* The unknown chunk structure contains the chunk data:
1600 Note that libpng has already taken care of the CRC handling.
1604 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1605 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1606 return(0); /* Did not recognize */
1608 /* recognized vpAg */
1610 if (chunk->size != 9)
1611 return(-1); /* Error return */
1613 if (chunk->data[8] != 0)
1614 return(0); /* ImageMagick requires pixel units */
1616 image=(Image *) png_get_user_chunk_ptr(ping);
1618 image->page.width=(size_t) ((chunk->data[0] << 24) |
1619 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1620 image->page.height=(size_t) ((chunk->data[4] << 24) |
1621 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1623 /* Return one of the following: */
1624 /* return(-n); chunk had an error */
1625 /* return(0); did not recognize */
1626 /* return(n); success */
1634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638 % R e a d O n e P N G I m a g e %
1642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1644 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1645 % (minus the 8-byte signature) and returns it. It allocates the memory
1646 % necessary for the new Image structure and returns a pointer to the new
1649 % The format of the ReadOnePNGImage method is:
1651 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1652 % ExceptionInfo *exception)
1654 % A description of each parameter follows:
1656 % o mng_info: Specifies a pointer to a MngInfo structure.
1658 % o image_info: the image info.
1660 % o exception: return any errors or warnings in this structure.
1663 static Image *ReadOnePNGImage(MngInfo *mng_info,
1664 const ImageInfo *image_info, ExceptionInfo *exception)
1666 /* Read one PNG image */
1678 ping_interlace_method,
1679 ping_compression_method,
1720 register unsigned char
1723 register IndexPacket
1730 register PixelPacket
1739 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1740 png_byte unused_chunks[]=
1742 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1743 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1744 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1745 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1746 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1747 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1751 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1752 " enter ReadOnePNGImage()");
1754 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1755 LockSemaphoreInfo(png_semaphore);
1758 #if (PNG_LIBPNG_VER < 10200)
1759 if (image_info->verbose)
1760 printf("Your PNG library (libpng-%s) is rather old.\n",
1761 PNG_LIBPNG_VER_STRING);
1764 #if (PNG_LIBPNG_VER >= 10400)
1765 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1766 if (image_info->verbose)
1768 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1769 PNG_LIBPNG_VER_STRING);
1770 printf("Please update it.\n");
1776 quantum_info = (QuantumInfo *) NULL;
1777 image=mng_info->image;
1780 Allocate the PNG structures
1782 #ifdef PNG_USER_MEM_SUPPORTED
1783 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1784 PNGErrorHandler,PNGWarningHandler, NULL,
1785 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1787 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1788 PNGErrorHandler,PNGWarningHandler);
1790 if (ping == (png_struct *) NULL)
1791 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1792 ping_info=png_create_info_struct(ping);
1793 if (ping_info == (png_info *) NULL)
1795 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1796 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1798 end_info=png_create_info_struct(ping);
1799 if (end_info == (png_info *) NULL)
1801 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1802 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1804 png_pixels=(unsigned char *) NULL;
1805 if (setjmp(png_jmpbuf(ping)))
1808 PNG image is corrupt.
1810 png_destroy_read_struct(&ping,&ping_info,&end_info);
1811 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1812 UnlockSemaphoreInfo(png_semaphore);
1814 if (logging != MagickFalse)
1815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1816 " exit ReadOnePNGImage() with error.");
1817 if (image != (Image *) NULL)
1819 InheritException(exception,&image->exception);
1822 return(GetFirstImageInList(image));
1825 Prepare PNG for reading.
1828 mng_info->image_found++;
1829 png_set_sig_bytes(ping,8);
1830 if (LocaleCompare(image_info->magick,"MNG") == 0)
1832 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1833 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1834 png_set_read_fn(ping,image,png_get_data);
1836 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1837 png_permit_empty_plte(ping,MagickTrue);
1838 png_set_read_fn(ping,image,png_get_data);
1840 mng_info->image=image;
1841 mng_info->bytes_in_read_buffer=0;
1842 mng_info->found_empty_plte=MagickFalse;
1843 mng_info->have_saved_bkgd_index=MagickFalse;
1844 png_set_read_fn(ping,mng_info,mng_get_data);
1849 png_set_read_fn(ping,image,png_get_data);
1851 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1852 /* Ignore unused chunks and all unknown chunks except for vpAg */
1853 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1854 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1855 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1856 (int)sizeof(unused_chunks)/5);
1857 /* Callback for other unknown chunks */
1858 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1861 #if (PNG_LIBPNG_VER < 10400)
1862 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1863 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1864 /* Disable thread-unsafe features of pnggccrd */
1865 if (png_access_version_number() >= 10200)
1867 png_uint_32 mmx_disable_mask=0;
1868 png_uint_32 asm_flags;
1870 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1871 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1872 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1873 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1874 asm_flags=png_get_asm_flags(ping);
1875 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1880 png_read_info(ping,ping_info);
1882 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1883 &ping_bit_depth,&ping_color_type,
1884 &ping_interlace_method,&ping_compression_method,
1885 &ping_filter_method);
1887 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1890 (void) png_get_bKGD(ping, ping_info, &ping_background);
1892 if (ping_bit_depth < 8)
1894 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1896 png_set_packing(ping);
1901 image->depth=ping_bit_depth;
1902 image->depth=GetImageQuantumDepth(image,MagickFalse);
1903 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1904 if (logging != MagickFalse)
1906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1907 " PNG width: %.20g, height: %.20g",
1908 (double) ping_width, (double) ping_height);
1909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1910 " PNG color_type: %d, bit_depth: %d",
1911 ping_color_type, ping_bit_depth);
1912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1913 " PNG compression_method: %d",
1914 ping_compression_method);
1915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1916 " PNG interlace_method: %d, filter_method: %d",
1917 ping_interlace_method,ping_filter_method);
1920 #ifdef PNG_READ_iCCP_SUPPORTED
1921 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1933 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1935 if (profile_length != 0)
1940 if (logging != MagickFalse)
1941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1942 " Reading PNG iCCP chunk.");
1943 profile=AcquireStringInfo(profile_length);
1944 SetStringInfoDatum(profile,(const unsigned char *) info);
1945 (void) SetImageProfile(image,"icc",profile);
1946 profile=DestroyStringInfo(profile);
1950 #if defined(PNG_READ_sRGB_SUPPORTED)
1955 if (mng_info->have_global_srgb)
1956 image->rendering_intent=(RenderingIntent)
1957 (mng_info->global_srgb_intent+1);
1958 if (png_get_sRGB(ping,ping_info,&intent))
1960 image->rendering_intent=(RenderingIntent) (intent+1);
1961 if (logging != MagickFalse)
1962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1963 " Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1971 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1972 if (mng_info->have_global_gama)
1973 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1974 if (png_get_gAMA(ping,ping_info,&file_gamma))
1976 image->gamma=(float) file_gamma;
1977 if (logging != MagickFalse)
1978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1979 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1982 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1984 if (mng_info->have_global_chrm != MagickFalse)
1986 (void) png_set_cHRM(ping,ping_info,
1987 mng_info->global_chrm.white_point.x,
1988 mng_info->global_chrm.white_point.y,
1989 mng_info->global_chrm.red_primary.x,
1990 mng_info->global_chrm.red_primary.y,
1991 mng_info->global_chrm.green_primary.x,
1992 mng_info->global_chrm.green_primary.y,
1993 mng_info->global_chrm.blue_primary.x,
1994 mng_info->global_chrm.blue_primary.y);
1997 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1999 (void) png_get_cHRM(ping,ping_info,
2000 &image->chromaticity.white_point.x,
2001 &image->chromaticity.white_point.y,
2002 &image->chromaticity.red_primary.x,
2003 &image->chromaticity.red_primary.y,
2004 &image->chromaticity.green_primary.x,
2005 &image->chromaticity.green_primary.y,
2006 &image->chromaticity.blue_primary.x,
2007 &image->chromaticity.blue_primary.y);
2008 if (logging != MagickFalse)
2009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2010 " Reading PNG cHRM chunk.");
2012 if (image->rendering_intent)
2014 png_set_sRGB(ping,ping_info,image->rendering_intent-1);
2015 png_set_gAMA(ping,ping_info,0.45455f);
2016 png_set_cHRM(ping,ping_info,
2017 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2018 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2020 #if defined(PNG_oFFs_SUPPORTED)
2021 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2023 image->page.x=png_get_x_offset_pixels(ping, ping_info);
2024 image->page.y=png_get_y_offset_pixels(ping, ping_info);
2025 if (logging != MagickFalse)
2026 if (image->page.x || image->page.y)
2027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2028 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2029 image->page.x,(double) image->page.y);
2032 #if defined(PNG_pHYs_SUPPORTED)
2033 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2035 if (mng_info->have_global_phys)
2037 png_set_pHYs(ping,ping_info,
2038 mng_info->global_x_pixels_per_unit,
2039 mng_info->global_y_pixels_per_unit,
2040 mng_info->global_phys_unit_type);
2044 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2054 Set image resolution.
2056 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2058 image->x_resolution=(double) x_resolution;
2059 image->y_resolution=(double) y_resolution;
2060 if (unit_type == PNG_RESOLUTION_METER)
2062 image->units=PixelsPerCentimeterResolution;
2063 image->x_resolution=(double) x_resolution/100.0;
2064 image->y_resolution=(double) y_resolution/100.0;
2066 if (logging != MagickFalse)
2067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2068 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2069 (double) x_resolution,(double) y_resolution,unit_type);
2072 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2080 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2081 if ((number_colors == 0) &&
2082 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2084 if (mng_info->global_plte_length)
2086 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2087 (int) mng_info->global_plte_length);
2088 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2089 if (mng_info->global_trns_length)
2091 if (mng_info->global_trns_length >
2092 mng_info->global_plte_length)
2093 (void) ThrowMagickException(&image->exception,
2094 GetMagickModule(),CoderError,
2095 "global tRNS has more entries than global PLTE",
2096 "`%s'",image_info->filename);
2097 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2098 (int) mng_info->global_trns_length,NULL);
2100 #if defined(PNG_READ_bKGD_SUPPORTED)
2102 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2103 mng_info->have_saved_bkgd_index ||
2105 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2110 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2111 if (mng_info->have_saved_bkgd_index)
2112 background.index=mng_info->saved_bkgd_index;
2114 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2115 background.index=ping_background->index;
2116 background.red=(png_uint_16)
2117 mng_info->global_plte[background.index].red;
2118 background.green=(png_uint_16)
2119 mng_info->global_plte[background.index].green;
2120 background.blue=(png_uint_16)
2121 mng_info->global_plte[background.index].blue;
2122 png_set_bKGD(ping,ping_info,&background);
2127 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2128 CoderError,"No global PLTE in file","`%s'",
2129 image_info->filename);
2133 #if defined(PNG_READ_bKGD_SUPPORTED)
2134 if (mng_info->have_global_bkgd &&
2135 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2136 image->background_color=mng_info->mng_global_bkgd;
2137 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2140 Set image background color.
2142 if (logging != MagickFalse)
2143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2144 " Reading PNG bKGD chunk.");
2145 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2147 image->background_color.red=ping_background->red;
2148 image->background_color.green=ping_background->green;
2149 image->background_color.blue=ping_background->blue;
2151 else /* Scale background components to 16-bit */
2156 if (logging != MagickFalse)
2157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2158 " raw ping_background=(%d,%d,%d).",ping_background->red,
2159 ping_background->green,ping_background->blue);
2162 if (ping_bit_depth == 1)
2164 else if (ping_bit_depth == 2)
2166 else if (ping_bit_depth == 4)
2168 if (ping_bit_depth <= 8)
2171 ping_background->red *= bkgd_scale;
2172 ping_background->green *= bkgd_scale;
2173 ping_background->blue *= bkgd_scale;
2175 if (logging != MagickFalse)
2177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2178 " bkgd_scale=%d.",bkgd_scale);
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " ping_background=(%d,%d,%d).",ping_background->red,
2181 ping_background->green,ping_background->blue);
2184 image->background_color.red=
2185 ScaleShortToQuantum(ping_background->red);
2186 image->background_color.green=
2187 ScaleShortToQuantum(ping_background->green);
2188 image->background_color.blue=
2189 ScaleShortToQuantum(ping_background->blue);
2191 if (logging != MagickFalse)
2192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2193 " image->background_color=(%.20g,%.20g,%.20g).",
2194 (double) image->background_color.red,
2195 (double) image->background_color.green,
2196 (double) image->background_color.blue);
2200 transparent_color.red=0;
2201 transparent_color.green=0;
2202 transparent_color.blue=0;
2203 transparent_color.opacity=0;
2204 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2207 Image has a transparent background.
2215 if (logging != MagickFalse)
2216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2217 " Reading PNG tRNS chunk.");
2219 max_sample = (int) ((one << ping_bit_depth) - 1);
2221 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2222 (int)ping_trans_color->gray > max_sample) ||
2223 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2224 ((int)ping_trans_color->red > max_sample ||
2225 (int)ping_trans_color->green > max_sample ||
2226 (int)ping_trans_color->blue > max_sample)))
2228 if (logging != MagickFalse)
2229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2230 " Ignoring PNG tRNS chunk with out-of-range sample.");
2231 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2232 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2233 image->matte=MagickFalse;
2237 transparent_color.red= (unsigned short)(ping_trans_color->red);
2238 transparent_color.green= (unsigned short) (ping_trans_color->green);
2239 transparent_color.blue= (unsigned short) (ping_trans_color->blue);
2240 transparent_color.opacity= (unsigned short) (ping_trans_color->gray);
2242 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2244 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2245 if (ping_bit_depth < MAGICKCORE_QUANTUM_DEPTH)
2247 transparent_color.opacity=(unsigned short) (
2248 ping_trans_color->gray *
2249 (65535L/((1UL << ping_bit_depth)-1)));
2251 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2253 transparent_color.opacity=(unsigned short) (
2254 (ping_trans_color->gray * 65535L)/
2255 ((1UL << ping_bit_depth)-1));
2257 if (logging != MagickFalse)
2259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2262 " scaled graylevel is %d.",transparent_color.opacity);
2264 transparent_color.red=transparent_color.opacity;
2265 transparent_color.green=transparent_color.opacity;
2266 transparent_color.blue=transparent_color.opacity;
2270 #if defined(PNG_READ_sBIT_SUPPORTED)
2271 if (mng_info->have_global_sbit)
2273 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2274 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2277 num_passes=png_set_interlace_handling(ping);
2279 png_read_update_info(ping,ping_info);
2281 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2284 Initialize image structure.
2286 mng_info->image_box.left=0;
2287 mng_info->image_box.right=(ssize_t) ping_width;
2288 mng_info->image_box.top=0;
2289 mng_info->image_box.bottom=(ssize_t) ping_height;
2290 if (mng_info->mng_type == 0)
2292 mng_info->mng_width=ping_width;
2293 mng_info->mng_height=ping_height;
2294 mng_info->frame=mng_info->image_box;
2295 mng_info->clip=mng_info->image_box;
2299 image->page.y=mng_info->y_off[mng_info->object_id];
2301 image->compression=ZipCompression;
2302 image->columns=ping_width;
2303 image->rows=ping_height;
2304 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2305 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2306 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2311 image->storage_class=PseudoClass;
2313 image->colors=one << ping_bit_depth;
2314 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2315 if (image->colors > 256)
2318 if (image->colors > 65536L)
2319 image->colors=65536L;
2321 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2329 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2330 image->colors=(size_t) number_colors;
2331 if (logging != MagickFalse)
2332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2333 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2337 if (image->storage_class == PseudoClass)
2340 Initialize image colormap.
2342 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2343 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2344 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2352 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2353 for (i=0; i < (ssize_t) image->colors; i++)
2355 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2356 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2357 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2365 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2368 for (i=0; i < (ssize_t) image->colors; i++)
2370 image->colormap[i].red=(Quantum) (i*scale);
2371 image->colormap[i].green=(Quantum) (i*scale);
2372 image->colormap[i].blue=(Quantum) (i*scale);
2377 Read image scanlines.
2379 if (image->delay != 0)
2380 mng_info->scenes_found++;
2381 if ((mng_info->mng_type != 0) || (
2382 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2383 (image_info->first_scene+image_info->number_scenes))))
2385 if (logging != MagickFalse)
2386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2387 " Skipping PNG image data for scene %.20g",(double)
2388 mng_info->scenes_found-1);
2389 png_destroy_read_struct(&ping,&ping_info,&end_info);
2390 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2391 UnlockSemaphoreInfo(png_semaphore);
2393 if (logging != MagickFalse)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2395 " exit ReadOnePNGImage().");
2398 if (logging != MagickFalse)
2399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2400 " Reading PNG IDAT chunk(s)");
2402 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2403 ping_rowbytes*sizeof(*png_pixels));
2405 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2406 sizeof(*png_pixels));
2407 if (png_pixels == (unsigned char *) NULL)
2408 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2409 if (logging != MagickFalse)
2410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2411 " Converting PNG pixels to pixel packets");
2413 Convert PNG pixels to pixel packets.
2415 if (setjmp(png_jmpbuf(ping)))
2418 PNG image is corrupt.
2420 png_destroy_read_struct(&ping,&ping_info,&end_info);
2421 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2422 UnlockSemaphoreInfo(png_semaphore);
2424 if (quantum_info != (QuantumInfo *) NULL)
2425 quantum_info = DestroyQuantumInfo(quantum_info);
2426 if (png_pixels != (unsigned char *) NULL)
2427 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2430 " exit ReadOnePNGImage() with error.");
2431 if (image != (Image *) NULL)
2433 InheritException(exception,&image->exception);
2436 return(GetFirstImageInList(image));
2438 quantum_info=AcquireQuantumInfo(image_info,image);
2439 if (quantum_info == (QuantumInfo *) NULL)
2440 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2441 if (image->storage_class == DirectClass)
2442 for (pass=0; pass < num_passes; pass++)
2445 Convert image to DirectClass pixel packets.
2447 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2451 depth=(ssize_t) ping_bit_depth;
2453 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2454 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2455 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2456 MagickTrue : MagickFalse;
2458 for (y=0; y < (ssize_t) image->rows; y++)
2461 row_offset=ping_rowbytes*y;
2464 png_read_row(ping,png_pixels+row_offset,NULL);
2465 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2466 if (q == (PixelPacket *) NULL)
2468 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2475 r=png_pixels+row_offset;
2477 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2479 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2483 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
2484 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2486 /* Cheap transparency */
2487 *r++=TransparentOpacity;
2493 else if (ping_color_type == PNG_COLOR_TYPE_RGB)
2495 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2496 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2504 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2505 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2506 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2508 /* Cheap transparency */
2509 *r++=TransparentOpacity;
2515 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2526 else if (ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2527 for (x=(ssize_t) (4*image->columns); x != 0; x--)
2532 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2533 for (x=(ssize_t) (2*image->columns); x != 0; x--)
2539 if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_GRAY)
2540 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2541 GrayQuantum,png_pixels+row_offset);
2542 if (ping_color_type == PNG_COLOR_TYPE_GRAY ||
2543 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2545 quantum_info->depth=8;
2546 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2547 GrayAlphaQuantum,png_pixels+row_offset);
2549 else if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_RGB)
2550 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2551 RGBQuantum,png_pixels+row_offset);
2552 else if (ping_color_type == PNG_COLOR_TYPE_RGB ||
2553 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2555 quantum_info->depth=8;
2556 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2557 RGBAQuantum,png_pixels+row_offset);
2559 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
2560 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2561 IndexQuantum,png_pixels+row_offset);
2562 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2563 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2564 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2565 GrayQuantum,png_pixels+row_offset,exception);
2566 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2567 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2568 GrayAlphaQuantum,png_pixels+row_offset,exception);
2569 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2570 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2571 RGBAQuantum,png_pixels+row_offset,exception);
2572 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2573 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2574 IndexQuantum,png_pixels+row_offset,exception);
2576 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2577 RGBQuantum,png_pixels+row_offset,exception);
2579 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2581 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2583 if (status == MagickFalse)
2586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2589 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2591 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2592 if (status == MagickFalse)
2596 else /* image->storage_class != DirectClass */
2597 for (pass=0; pass < num_passes; pass++)
2606 Convert grayscale image to PseudoClass pixel packets.
2608 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2609 MagickTrue : MagickFalse;
2610 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2611 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2612 if (quantum_scanline == (Quantum *) NULL)
2613 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2614 for (y=0; y < (ssize_t) image->rows; y++)
2617 row_offset=ping_rowbytes*y;
2620 png_read_row(ping,png_pixels+row_offset,NULL);
2621 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2622 if (q == (PixelPacket *) NULL)
2624 indexes=GetAuthenticIndexQueue(image);
2625 p=png_pixels+row_offset;
2627 switch (ping_bit_depth)
2634 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2636 for (bit=7; bit >= 0; bit--)
2637 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2640 if ((image->columns % 8) != 0)
2642 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2643 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2649 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2651 *r++=(*p >> 6) & 0x03;
2652 *r++=(*p >> 4) & 0x03;
2653 *r++=(*p >> 2) & 0x03;
2656 if ((image->columns % 4) != 0)
2658 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2659 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2665 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2667 *r++=(*p >> 4) & 0x0f;
2670 if ((image->columns % 2) != 0)
2671 *r++=(*p++ >> 4) & 0x0f;
2676 if (ping_color_type == 4)
2677 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2680 /* In image.h, OpaqueOpacity is 0
2681 * TransparentOpacity is QuantumRange
2682 * In a PNG datastream, Opaque is QuantumRange
2683 * and Transparent is 0.
2685 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2689 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2697 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2701 if (image->colors > 256)
2707 *r=(Quantum) quantum;
2709 if (ping_color_type == 4)
2711 quantum=((*p++) << 8);
2713 q->opacity=(Quantum) (QuantumRange-quantum);
2717 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2721 if (image->colors > 256)
2729 if (ping_color_type == 4)
2731 q->opacity=(*p << 8) | *(p+1);
2733 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2737 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2739 p++; /* strip low byte */
2740 if (ping_color_type == 4)
2742 q->opacity=(Quantum) (QuantumRange-(*p++));
2755 Transfer image scanline.
2758 for (x=0; x < (ssize_t) image->columns; x++)
2759 indexes[x]=(IndexPacket) (*r++);
2760 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2762 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2764 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2766 if (status == MagickFalse)
2770 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2772 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2773 if (status == MagickFalse)
2776 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2778 if (quantum_info != (QuantumInfo *) NULL)
2779 quantum_info=DestroyQuantumInfo(quantum_info);
2780 if (image->storage_class == PseudoClass)
2786 image->matte=MagickFalse;
2787 (void) SyncImage(image);
2790 png_read_end(ping,ping_info);
2792 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2793 (ssize_t) image_info->first_scene && image->delay != 0)
2795 png_destroy_read_struct(&ping,&ping_info,&end_info);
2796 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2798 (void) SetImageBackgroundColor(image);
2799 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2800 UnlockSemaphoreInfo(png_semaphore);
2802 if (logging != MagickFalse)
2803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804 " exit ReadOnePNGImage() early.");
2807 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2813 Image has a transparent background.
2815 storage_class=image->storage_class;
2816 image->matte=MagickTrue;
2818 #if 1 /* balfour fix */
2819 /* From imagemagick discourse server, 5 Feb 2010 */
2821 if (storage_class == PseudoClass)
2823 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2825 for (x=0; x < ping_num_trans; x++)
2827 image->colormap[x].opacity =
2828 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2831 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2833 for (x=0; x < (int) image->colors; x++)
2835 if (ScaleQuantumToShort(image->colormap[x].red) ==
2836 transparent_color.opacity)
2838 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2842 (void) SyncImage(image);
2847 for (y=0; y < (ssize_t) image->rows; y++)
2849 image->storage_class=storage_class;
2850 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2851 if (q == (PixelPacket *) NULL)
2853 indexes=GetAuthenticIndexQueue(image);
2855 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2857 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2858 ScaleQuantumToShort(q->green) == transparent_color.green &&
2859 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2860 q->opacity=(Quantum) TransparentOpacity;
2862 SetOpacityPixelComponent(q,OpaqueOpacity);
2865 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2870 #else /* not balfour */
2873 for (y=0; y < (ssize_t) image->rows; y++)
2875 image->storage_class=storage_class;
2876 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2877 if (q == (PixelPacket *) NULL)
2879 indexes=GetAuthenticIndexQueue(image);
2881 if (storage_class == PseudoClass)
2886 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2887 for (x=0; x < (ssize_t) image->columns; x++)
2889 indexpacket=indexes[x];
2890 if (indexpacket < ping_num_trans)
2891 q->opacity=ScaleCharToQuantum((unsigned char)
2892 (255-ping_trans_alpha[(ssize_t) indexpacket]));
2894 SetOpacityPixelComponent(q,OpaqueOpacity);
2897 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2898 for (x=0; x < (ssize_t) image->columns; x++)
2900 indexpacket=indexes[x];
2901 q->red=image->colormap[(ssize_t) indexpacket].red;
2904 if (ScaleQuantomToShort(q->red) == transparent_color.opacity)
2905 q->opacity=(Quantum) TransparentOpacity;
2907 SetOpacityPixelComponent(q,OpaqueOpacity);
2912 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2914 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2915 ScaleQuantumToShort(q->green) == transparent_color.green &&
2916 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2917 q->opacity=(Quantum) TransparentOpacity;
2919 SetOpacityPixelComponent(q,OpaqueOpacity);
2922 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2925 #endif /* not balfour */
2926 image->storage_class=DirectClass;
2928 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2929 if (image->depth > 8)
2932 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2933 for (i=0; i < (ssize_t) num_text; i++)
2935 /* Check for a profile */
2937 if (logging != MagickFalse)
2938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2939 " Reading PNG text chunk");
2940 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2941 (void) png_read_raw_profile(image,image_info,text,(int) i);
2947 length=text[i].text_length;
2948 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2950 if (value == (char *) NULL)
2952 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2953 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2958 (void) ConcatenateMagickString(value,text[i].text,length+2);
2959 (void) SetImageProperty(image,text[i].key,value);
2960 if (logging != MagickFalse)
2961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2962 " Keyword: %s",text[i].key);
2963 value=DestroyString(value);
2966 #ifdef MNG_OBJECT_BUFFERS
2968 Store the object if necessary.
2970 if (object_id && !mng_info->frozen[object_id])
2972 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2975 create a new object buffer.
2977 mng_info->ob[object_id]=(MngBuffer *)
2978 AcquireAlignedMemory(1,sizeof(MngBuffer));
2979 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2981 mng_info->ob[object_id]->image=(Image *) NULL;
2982 mng_info->ob[object_id]->reference_count=1;
2985 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2986 mng_info->ob[object_id]->frozen)
2988 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2989 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2990 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2992 if (mng_info->ob[object_id]->frozen)
2993 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2994 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2995 "`%s'",image->filename);
3000 if (mng_info->ob[object_id]->image != (Image *) NULL)
3001 mng_info->ob[object_id]->image=DestroyImage
3002 (mng_info->ob[object_id]->image);
3003 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3005 if (mng_info->ob[object_id]->image != (Image *) NULL)
3006 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3008 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3009 ResourceLimitError,"Cloning image for object buffer failed",
3010 "`%s'",image->filename);
3011 if (ping_width > 250000L || ping_height > 250000L)
3012 png_error(ping,"PNG Image dimensions are too large.");
3013 mng_info->ob[object_id]->width=ping_width;
3014 mng_info->ob[object_id]->height=ping_height;
3015 mng_info->ob[object_id]->color_type=ping_color_type;
3016 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3017 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3018 mng_info->ob[object_id]->compression_method=
3019 ping_compression_method;
3020 mng_info->ob[object_id]->filter_method=ping_filter_method;
3021 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3030 Copy the PLTE to the object buffer.
3032 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3033 mng_info->ob[object_id]->plte_length=number_colors;
3034 for (i=0; i < number_colors; i++)
3036 mng_info->ob[object_id]->plte[i]=plte[i];
3040 mng_info->ob[object_id]->plte_length=0;
3045 Relinquish resources.
3047 png_destroy_read_struct(&ping,&ping_info,&end_info);
3049 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
3050 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3051 UnlockSemaphoreInfo(png_semaphore);
3054 if (logging != MagickFalse)
3055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3056 " exit ReadOnePNGImage()");
3059 /* end of reading one PNG image */
3062 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3075 magic_number[MaxTextExtent];
3087 assert(image_info != (const ImageInfo *) NULL);
3088 assert(image_info->signature == MagickSignature);
3089 if (image_info->debug != MagickFalse)
3090 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3091 image_info->filename);
3092 assert(exception != (ExceptionInfo *) NULL);
3093 assert(exception->signature == MagickSignature);
3094 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3095 image=AcquireImage(image_info);
3096 mng_info=(MngInfo *) NULL;
3097 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3098 if (status == MagickFalse)
3099 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3101 Verify PNG signature.
3103 count=ReadBlob(image,8,(unsigned char *) magic_number);
3104 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3105 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3107 Allocate a MngInfo structure.
3109 have_mng_structure=MagickFalse;
3110 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3111 if (mng_info == (MngInfo *) NULL)
3112 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3114 Initialize members of the MngInfo structure.
3116 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3117 mng_info->image=image;
3118 have_mng_structure=MagickTrue;
3121 image=ReadOnePNGImage(mng_info,image_info,exception);
3122 MngInfoFreeStruct(mng_info,&have_mng_structure);
3123 if (image == (Image *) NULL)
3125 if (previous != (Image *) NULL)
3127 if (previous->signature != MagickSignature)
3128 ThrowReaderException(CorruptImageError,"CorruptImage");
3129 (void) CloseBlob(previous);
3130 (void) DestroyImageList(previous);
3132 if (logging != MagickFalse)
3133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134 "exit ReadPNGImage() with error");
3135 return((Image *) NULL);
3137 (void) CloseBlob(image);
3138 if ((image->columns == 0) || (image->rows == 0))
3140 if (logging != MagickFalse)
3141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3142 "exit ReadPNGImage() with error.");
3143 ThrowReaderException(CorruptImageError,"CorruptImage");
3145 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3147 (void) SetImageType(image,PaletteType);
3148 if (image->matte != MagickFalse)
3150 /* To do: Reduce to binary transparency */
3153 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3155 (void) SetImageType(image,TrueColorType);
3156 image->matte=MagickFalse;
3158 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3159 (void) SetImageType(image,TrueColorMatteType);
3160 if (logging != MagickFalse)
3161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3167 #if defined(JNG_SUPPORTED)
3169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3173 % R e a d O n e J N G I m a g e %
3177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3179 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3180 % (minus the 8-byte signature) and returns it. It allocates the memory
3181 % necessary for the new Image structure and returns a pointer to the new
3184 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3186 % The format of the ReadOneJNGImage method is:
3188 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3189 % ExceptionInfo *exception)
3191 % A description of each parameter follows:
3193 % o mng_info: Specifies a pointer to a MngInfo structure.
3195 % o image_info: the image info.
3197 % o exception: return any errors or warnings in this structure.
3200 static Image *ReadOneJNGImage(MngInfo *mng_info,
3201 const ImageInfo *image_info, ExceptionInfo *exception)
3225 jng_image_sample_depth,
3226 jng_image_compression_method,
3227 jng_image_interlace_method,
3228 jng_alpha_sample_depth,
3229 jng_alpha_compression_method,
3230 jng_alpha_filter_method,
3231 jng_alpha_interlace_method;
3233 register const PixelPacket
3240 register PixelPacket
3243 register unsigned char
3255 jng_alpha_compression_method=0;
3256 jng_alpha_sample_depth=8;
3260 alpha_image=(Image *) NULL;
3261 color_image=(Image *) NULL;
3262 alpha_image_info=(ImageInfo *) NULL;
3263 color_image_info=(ImageInfo *) NULL;
3265 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3266 " enter ReadOneJNGImage()");
3268 image=mng_info->image;
3269 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3272 Allocate next image structure.
3274 if (logging != MagickFalse)
3275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3276 " AcquireNextImage()");
3277 AcquireNextImage(image_info,image);
3278 if (GetNextImageInList(image) == (Image *) NULL)
3279 return((Image *) NULL);
3280 image=SyncNextImageInList(image);
3282 mng_info->image=image;
3285 Signature bytes have already been read.
3288 read_JSEP=MagickFalse;
3289 reading_idat=MagickFalse;
3290 skip_to_iend=MagickFalse;
3294 type[MaxTextExtent];
3303 Read a new JNG chunk.
3305 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3306 2*GetBlobSize(image));
3307 if (status == MagickFalse)
3310 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3311 length=ReadBlobMSBLong(image);
3312 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3314 if (logging != MagickFalse)
3315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3316 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3317 type[0],type[1],type[2],type[3],(double) length);
3319 if (length > PNG_UINT_31_MAX || count == 0)
3320 ThrowReaderException(CorruptImageError,"CorruptImage");
3322 chunk=(unsigned char *) NULL;
3325 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3326 if (chunk == (unsigned char *) NULL)
3327 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3328 for (i=0; i < (ssize_t) length; i++)
3329 chunk[i]=(unsigned char) ReadBlobByte(image);
3332 (void) ReadBlobMSBLong(image); /* read crc word */
3337 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3341 if (memcmp(type,mng_JHDR,4) == 0)
3345 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3346 (p[2] << 8) | p[3]);
3347 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3348 (p[6] << 8) | p[7]);
3349 jng_color_type=p[8];
3350 jng_image_sample_depth=p[9];
3351 jng_image_compression_method=p[10];
3352 jng_image_interlace_method=p[11];
3353 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3355 jng_alpha_sample_depth=p[12];
3356 jng_alpha_compression_method=p[13];
3357 jng_alpha_filter_method=p[14];
3358 jng_alpha_interlace_method=p[15];
3359 if (logging != MagickFalse)
3361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3362 " jng_width: %16lu",(unsigned long) jng_width);
3363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3364 " jng_width: %16lu",(unsigned long) jng_height);
3365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366 " jng_color_type: %16d",jng_color_type);
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 " jng_image_sample_depth: %3d",
3369 jng_image_sample_depth);
3370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3371 " jng_image_compression_method:%3d",
3372 jng_image_compression_method);
3373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3374 " jng_image_interlace_method: %3d",
3375 jng_image_interlace_method);
3376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3377 " jng_alpha_sample_depth: %3d",
3378 jng_alpha_sample_depth);
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380 " jng_alpha_compression_method:%3d",
3381 jng_alpha_compression_method);
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " jng_alpha_filter_method: %3d",
3384 jng_alpha_filter_method);
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " jng_alpha_interlace_method: %3d",
3387 jng_alpha_interlace_method);
3391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3396 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3397 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3398 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3401 o create color_image
3402 o open color_blob, attached to color_image
3403 o if (color type has alpha)
3404 open alpha_blob, attached to alpha_image
3407 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
3408 if (color_image_info == (ImageInfo *) NULL)
3409 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3410 GetImageInfo(color_image_info);
3411 color_image=AcquireImage(color_image_info);
3412 if (color_image == (Image *) NULL)
3413 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3415 if (logging != MagickFalse)
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3417 " Creating color_blob.");
3418 (void) AcquireUniqueFilename(color_image->filename);
3419 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3421 if (status == MagickFalse)
3422 return((Image *) NULL);
3424 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3426 alpha_image_info=(ImageInfo *)
3427 AcquireAlignedMemory(1,sizeof(ImageInfo));
3428 if (alpha_image_info == (ImageInfo *) NULL)
3429 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3430 GetImageInfo(alpha_image_info);
3431 alpha_image=AcquireImage(alpha_image_info);
3432 if (alpha_image == (Image *) NULL)
3434 alpha_image=DestroyImage(alpha_image);
3435 ThrowReaderException(ResourceLimitError,
3436 "MemoryAllocationFailed");
3438 if (logging != MagickFalse)
3439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3440 " Creating alpha_blob.");
3441 (void) AcquireUniqueFilename(alpha_image->filename);
3442 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3444 if (status == MagickFalse)
3445 return((Image *) NULL);
3446 if (jng_alpha_compression_method == 0)
3451 if (logging != MagickFalse)
3452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453 " Writing IHDR chunk to alpha_blob.");
3454 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3455 "\211PNG\r\n\032\n");
3456 (void) WriteBlobMSBULong(alpha_image,13L);
3457 PNGType(data,mng_IHDR);
3458 LogPNGChunk((int) logging,mng_IHDR,13L);
3459 PNGLong(data+4,jng_width);
3460 PNGLong(data+8,jng_height);
3461 data[12]=jng_alpha_sample_depth;
3462 data[13]=0; /* color_type gray */
3463 data[14]=0; /* compression method 0 */
3464 data[15]=0; /* filter_method 0 */
3465 data[16]=0; /* interlace_method 0 */
3466 (void) WriteBlob(alpha_image,17,data);
3467 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3470 reading_idat=MagickTrue;
3473 if (memcmp(type,mng_JDAT,4) == 0)
3476 Copy chunk to color_image->blob
3479 if (logging != MagickFalse)
3480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3481 " Copying JDAT chunk data to color_blob.");
3483 (void) WriteBlob(color_image,length,chunk);
3485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3489 if (memcmp(type,mng_IDAT,4) == 0)
3495 Copy IDAT header and chunk data to alpha_image->blob
3498 if (image_info->ping == MagickFalse)
3500 if (logging != MagickFalse)
3501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3502 " Copying IDAT chunk data to alpha_blob.");
3504 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3505 PNGType(data,mng_IDAT);
3506 LogPNGChunk((int) logging,mng_IDAT,length);
3507 (void) WriteBlob(alpha_image,4,data);
3508 (void) WriteBlob(alpha_image,length,chunk);
3509 (void) WriteBlobMSBULong(alpha_image,
3510 crc32(crc32(0,data,4),chunk,(uInt) length));
3513 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3517 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3520 Copy chunk data to alpha_image->blob
3523 if (image_info->ping == MagickFalse)
3525 if (logging != MagickFalse)
3526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527 " Copying JDAA chunk data to alpha_blob.");
3529 (void) WriteBlob(alpha_image,length,chunk);
3532 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3536 if (memcmp(type,mng_JSEP,4) == 0)
3538 read_JSEP=MagickTrue;
3540 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3544 if (memcmp(type,mng_bKGD,4) == 0)
3548 image->background_color.red=ScaleCharToQuantum(p[1]);
3549 image->background_color.green=image->background_color.red;
3550 image->background_color.blue=image->background_color.red;
3554 image->background_color.red=ScaleCharToQuantum(p[1]);
3555 image->background_color.green=ScaleCharToQuantum(p[3]);
3556 image->background_color.blue=ScaleCharToQuantum(p[5]);
3558 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3562 if (memcmp(type,mng_gAMA,4) == 0)
3565 image->gamma=((float) mng_get_long(p))*0.00001;
3566 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3570 if (memcmp(type,mng_cHRM,4) == 0)
3574 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3575 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3576 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3577 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3578 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3579 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3580 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3581 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3583 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3587 if (memcmp(type,mng_sRGB,4) == 0)
3591 image->rendering_intent=(RenderingIntent) (p[0]+1);
3592 image->gamma=0.45455f;
3593 image->chromaticity.red_primary.x=0.6400f;
3594 image->chromaticity.red_primary.y=0.3300f;
3595 image->chromaticity.green_primary.x=0.3000f;
3596 image->chromaticity.green_primary.y=0.6000f;
3597 image->chromaticity.blue_primary.x=0.1500f;
3598 image->chromaticity.blue_primary.y=0.0600f;
3599 image->chromaticity.white_point.x=0.3127f;
3600 image->chromaticity.white_point.y=0.3290f;
3602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3606 if (memcmp(type,mng_oFFs,4) == 0)
3610 image->page.x=mng_get_long(p);
3611 image->page.y=mng_get_long(&p[4]);
3612 if ((int) p[8] != 0)
3614 image->page.x/=10000;
3615 image->page.y/=10000;
3619 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3623 if (memcmp(type,mng_pHYs,4) == 0)
3627 image->x_resolution=(double) mng_get_long(p);
3628 image->y_resolution=(double) mng_get_long(&p[4]);
3629 if ((int) p[8] == PNG_RESOLUTION_METER)
3631 image->units=PixelsPerCentimeterResolution;
3632 image->x_resolution=image->x_resolution/100.0f;
3633 image->y_resolution=image->y_resolution/100.0f;
3636 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3641 if (memcmp(type,mng_iCCP,4) == 0)
3645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3651 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3653 if (memcmp(type,mng_IEND,4))
3662 Finish up reading image data:
3664 o read main image from color_blob.
3668 o if (color_type has alpha)
3669 if alpha_encoding is PNG
3670 read secondary image from alpha_blob via ReadPNG
3671 if alpha_encoding is JPEG
3672 read secondary image from alpha_blob via ReadJPEG
3676 o copy intensity of secondary image into
3677 opacity samples of main image.
3679 o destroy the secondary image.
3682 (void) CloseBlob(color_image);
3683 if (logging != MagickFalse)
3684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3685 " Reading jng_image from color_blob.");
3686 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3687 color_image->filename);
3688 color_image_info->ping=MagickFalse; /* To do: avoid this */
3689 jng_image=ReadImage(color_image_info,exception);
3690 if (jng_image == (Image *) NULL)
3691 return((Image *) NULL);
3693 (void) RelinquishUniqueFileResource(color_image->filename);
3694 color_image=DestroyImage(color_image);
3695 color_image_info=DestroyImageInfo(color_image_info);
3697 if (jng_image == (Image *) NULL)
3698 return((Image *) NULL);
3700 if (logging != MagickFalse)
3701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3702 " Copying jng_image pixels to main image.");
3703 image->rows=jng_height;
3704 image->columns=jng_width;
3705 length=image->columns*sizeof(PixelPacket);
3706 for (y=0; y < (ssize_t) image->rows; y++)
3708 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3709 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3710 (void) CopyMagickMemory(q,s,length);
3711 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3714 jng_image=DestroyImage(jng_image);
3715 if (image_info->ping == MagickFalse)
3717 if (jng_color_type >= 12)
3719 if (jng_alpha_compression_method == 0)
3723 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3724 PNGType(data,mng_IEND);
3725 LogPNGChunk((int) logging,mng_IEND,0L);
3726 (void) WriteBlob(alpha_image,4,data);
3727 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3729 (void) CloseBlob(alpha_image);
3730 if (logging != MagickFalse)
3731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3732 " Reading opacity from alpha_blob.");
3734 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3735 "%s",alpha_image->filename);
3737 jng_image=ReadImage(alpha_image_info,exception);
3738 if (jng_image != (Image *) NULL)
3739 for (y=0; y < (ssize_t) image->rows; y++)
3741 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3743 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3744 if (image->matte != MagickFalse)
3745 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3746 q->opacity=(Quantum) QuantumRange-s->red;
3748 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3750 q->opacity=(Quantum) QuantumRange-s->red;
3751 if (q->opacity != OpaqueOpacity)
3752 image->matte=MagickTrue;
3754 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3757 (void) RelinquishUniqueFileResource(alpha_image->filename);
3758 alpha_image=DestroyImage(alpha_image);
3759 alpha_image_info=DestroyImageInfo(alpha_image_info);
3760 if (jng_image != (Image *) NULL)
3761 jng_image=DestroyImage(jng_image);
3768 if (mng_info->mng_type == 0)
3770 mng_info->mng_width=jng_width;
3771 mng_info->mng_height=jng_height;
3773 if (image->page.width == 0 && image->page.height == 0)
3775 image->page.width=jng_width;
3776 image->page.height=jng_height;
3778 if (image->page.x == 0 && image->page.y == 0)
3780 image->page.x=mng_info->x_off[mng_info->object_id];
3781 image->page.y=mng_info->y_off[mng_info->object_id];
3785 image->page.y=mng_info->y_off[mng_info->object_id];
3787 mng_info->image_found++;
3788 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3789 2*GetBlobSize(image));
3790 if (logging != MagickFalse)
3791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3792 " exit ReadOneJNGImage()");
3797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3801 % R e a d J N G I m a g e %
3805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3807 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3808 % (including the 8-byte signature) and returns it. It allocates the memory
3809 % necessary for the new Image structure and returns a pointer to the new
3812 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3814 % The format of the ReadJNGImage method is:
3816 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3819 % A description of each parameter follows:
3821 % o image_info: the image info.
3823 % o exception: return any errors or warnings in this structure.
3827 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3840 magic_number[MaxTextExtent];
3852 assert(image_info != (const ImageInfo *) NULL);
3853 assert(image_info->signature == MagickSignature);
3854 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3855 assert(exception != (ExceptionInfo *) NULL);
3856 assert(exception->signature == MagickSignature);
3857 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3858 image=AcquireImage(image_info);
3859 mng_info=(MngInfo *) NULL;
3860 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3861 if (status == MagickFalse)
3862 return((Image *) NULL);
3863 if (LocaleCompare(image_info->magick,"JNG") != 0)
3864 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3866 Verify JNG signature.
3868 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3869 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3870 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3872 Allocate a MngInfo structure.
3874 have_mng_structure=MagickFalse;
3875 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
3876 if (mng_info == (MngInfo *) NULL)
3877 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3879 Initialize members of the MngInfo structure.
3881 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3882 have_mng_structure=MagickTrue;
3884 mng_info->image=image;
3886 image=ReadOneJNGImage(mng_info,image_info,exception);
3887 MngInfoFreeStruct(mng_info,&have_mng_structure);
3888 if (image == (Image *) NULL)
3890 if (IsImageObject(previous) != MagickFalse)
3892 (void) CloseBlob(previous);
3893 (void) DestroyImageList(previous);
3895 if (logging != MagickFalse)
3896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3897 "exit ReadJNGImage() with error");
3898 return((Image *) NULL);
3900 (void) CloseBlob(image);
3901 if (image->columns == 0 || image->rows == 0)
3903 if (logging != MagickFalse)
3904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3905 "exit ReadJNGImage() with error");
3906 ThrowReaderException(CorruptImageError,"CorruptImage");
3908 if (logging != MagickFalse)
3909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3914 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3917 page_geometry[MaxTextExtent];
3950 #if defined(MNG_INSERT_LAYERS)
3952 mng_background_color;
3955 register unsigned char
3970 #if defined(MNG_INSERT_LAYERS)
3975 volatile unsigned int
3976 #ifdef MNG_OBJECT_BUFFERS
3977 mng_background_object=0,
3979 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3982 default_frame_timeout,
3984 #if defined(MNG_INSERT_LAYERS)
3990 /* These delays are all measured in image ticks_per_second,
3991 * not in MNG ticks_per_second
3994 default_frame_delay,
3998 #if defined(MNG_INSERT_LAYERS)
4007 previous_fb.bottom=0;
4009 previous_fb.right=0;
4011 default_fb.bottom=0;
4016 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
4017 following optimizations:
4019 o 16-bit depth is reduced to 8 if all pixels contain samples whose
4020 high byte and low byte are identical.
4021 o Opaque matte channel is removed.
4022 o If matte channel is present but only one transparent color is
4023 present, RGB+tRNS is written instead of RGBA
4024 o Grayscale images are reduced to 1, 2, or 4 bit depth if
4025 this can be done without loss.
4026 o Palette is sorted to remove unused entries and to put a
4027 transparent color first, if PNG_SORT_PALETTE is also defined.
4033 assert(image_info != (const ImageInfo *) NULL);
4034 assert(image_info->signature == MagickSignature);
4035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4036 assert(exception != (ExceptionInfo *) NULL);
4037 assert(exception->signature == MagickSignature);
4038 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4039 image=AcquireImage(image_info);
4040 mng_info=(MngInfo *) NULL;
4041 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4042 if (status == MagickFalse)
4043 return((Image *) NULL);
4044 first_mng_object=MagickFalse;
4046 have_mng_structure=MagickFalse;
4048 Allocate a MngInfo structure.
4050 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
4051 if (mng_info == (MngInfo *) NULL)
4052 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4054 Initialize members of the MngInfo structure.
4056 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4057 mng_info->image=image;
4058 have_mng_structure=MagickTrue;
4059 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
4060 mng_info->optimize=image_info->type == OptimizeType;
4063 if (LocaleCompare(image_info->magick,"MNG") == 0)
4066 magic_number[MaxTextExtent];
4069 Verify MNG signature.
4071 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4072 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4073 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4075 Initialize some nonzero members of the MngInfo structure.
4077 for (i=0; i < MNG_MAX_OBJECTS; i++)
4079 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4080 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4082 mng_info->exists[0]=MagickTrue;
4084 first_mng_object=MagickTrue;
4086 #if defined(MNG_INSERT_LAYERS)
4087 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4089 default_frame_delay=0;
4090 default_frame_timeout=0;
4093 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4095 skip_to_iend=MagickFalse;
4096 term_chunk_found=MagickFalse;
4097 mng_info->framing_mode=1;
4098 #if defined(MNG_INSERT_LAYERS)
4099 mandatory_back=MagickFalse;
4101 #if defined(MNG_INSERT_LAYERS)
4102 mng_background_color=image->background_color;
4104 default_fb=mng_info->frame;
4105 previous_fb=mng_info->frame;
4109 type[MaxTextExtent];
4111 if (LocaleCompare(image_info->magick,"MNG") == 0)
4120 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4121 length=ReadBlobMSBLong(image);
4122 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4124 if (logging != MagickFalse)
4125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4126 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4127 type[0],type[1],type[2],type[3],(double) length);
4129 if (length > PNG_UINT_31_MAX)
4132 ThrowReaderException(CorruptImageError,"CorruptImage");
4134 chunk=(unsigned char *) NULL;
4137 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4138 if (chunk == (unsigned char *) NULL)
4139 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4140 for (i=0; i < (ssize_t) length; i++)
4141 chunk[i]=(unsigned char) ReadBlobByte(image);
4144 (void) ReadBlobMSBLong(image); /* read crc word */
4146 #if !defined(JNG_SUPPORTED)
4147 if (memcmp(type,mng_JHDR,4) == 0)
4149 skip_to_iend=MagickTrue;
4150 if (mng_info->jhdr_warning == 0)
4151 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4152 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4153 mng_info->jhdr_warning++;
4156 if (memcmp(type,mng_DHDR,4) == 0)
4158 skip_to_iend=MagickTrue;
4159 if (mng_info->dhdr_warning == 0)
4160 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4161 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4162 mng_info->dhdr_warning++;
4164 if (memcmp(type,mng_MEND,4) == 0)
4168 if (memcmp(type,mng_IEND,4) == 0)
4169 skip_to_iend=MagickFalse;
4171 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4172 if (logging != MagickFalse)
4173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4177 if (memcmp(type,mng_MHDR,4) == 0)
4179 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4180 (p[2] << 8) | p[3]);
4181 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4182 (p[6] << 8) | p[7]);
4183 if (logging != MagickFalse)
4185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4186 " MNG width: %.20g",(double) mng_info->mng_width);
4187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4188 " MNG height: %.20g",(double) mng_info->mng_height);
4191 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4192 if (mng_info->ticks_per_second == 0)
4193 default_frame_delay=0;
4195 default_frame_delay=1UL*image->ticks_per_second/
4196 mng_info->ticks_per_second;
4197 frame_delay=default_frame_delay;
4202 simplicity=(size_t) mng_get_long(p);
4204 mng_type=1; /* Full MNG */
4205 if ((simplicity != 0) && ((simplicity | 11) == 11))
4206 mng_type=2; /* LC */
4207 if ((simplicity != 0) && ((simplicity | 9) == 9))
4208 mng_type=3; /* VLC */
4209 #if defined(MNG_INSERT_LAYERS)
4211 insert_layers=MagickTrue;
4213 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4216 Allocate next image structure.
4218 AcquireNextImage(image_info,image);
4219 if (GetNextImageInList(image) == (Image *) NULL)
4220 return((Image *) NULL);
4221 image=SyncNextImageInList(image);
4222 mng_info->image=image;
4225 if ((mng_info->mng_width > 65535L) ||
4226 (mng_info->mng_height > 65535L))
4227 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4228 (void) FormatMagickString(page_geometry,MaxTextExtent,
4229 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4230 mng_info->mng_height);
4231 mng_info->frame.left=0;
4232 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4233 mng_info->frame.top=0;
4234 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4235 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4236 for (i=0; i < MNG_MAX_OBJECTS; i++)
4237 mng_info->object_clip[i]=mng_info->frame;
4238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4242 if (memcmp(type,mng_TERM,4) == 0)
4252 final_delay=(png_uint_32) mng_get_long(&p[2]);
4253 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4254 if (mng_iterations == PNG_UINT_31_MAX)
4256 image->iterations=mng_iterations;
4257 term_chunk_found=MagickTrue;
4259 if (logging != MagickFalse)
4261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4262 " repeat=%d",repeat);
4263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4264 " final_delay=%.20g",(double) final_delay);
4265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4266 " image->iterations=%.20g",(double) image->iterations);
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4271 if (memcmp(type,mng_DEFI,4) == 0)
4274 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4275 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4277 object_id=(p[0] << 8) | p[1];
4278 if (mng_type == 2 && object_id != 0)
4279 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4280 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4282 if (object_id > MNG_MAX_OBJECTS)
4285 Instead ofsuing a warning we should allocate a larger
4286 MngInfo structure and continue.
4288 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4289 CoderError,"object id too large","`%s'",image->filename);
4290 object_id=MNG_MAX_OBJECTS;
4292 if (mng_info->exists[object_id])
4293 if (mng_info->frozen[object_id])
4295 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4296 (void) ThrowMagickException(&image->exception,
4297 GetMagickModule(),CoderError,
4298 "DEFI cannot redefine a frozen MNG object","`%s'",
4302 mng_info->exists[object_id]=MagickTrue;
4304 mng_info->invisible[object_id]=p[2];
4306 Extract object offset info.
4310 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
4311 (p[6] << 8) | p[7]);
4312 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
4313 (p[10] << 8) | p[11]);
4314 if (logging != MagickFalse)
4316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4317 " x_off[%d]: %.20g",object_id,(double)
4318 mng_info->x_off[object_id]);
4319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4320 " y_off[%d]: %.20g",object_id,(double)
4321 mng_info->y_off[object_id]);
4325 Extract object clipping info.
4328 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4330 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4333 if (memcmp(type,mng_bKGD,4) == 0)
4335 mng_info->have_global_bkgd=MagickFalse;
4338 mng_info->mng_global_bkgd.red=
4339 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4340 mng_info->mng_global_bkgd.green=
4341 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4342 mng_info->mng_global_bkgd.blue=
4343 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4344 mng_info->have_global_bkgd=MagickTrue;
4346 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4349 if (memcmp(type,mng_BACK,4) == 0)
4351 #if defined(MNG_INSERT_LAYERS)
4353 mandatory_back=p[6];
4356 if (mandatory_back && length > 5)
4358 mng_background_color.red=
4359 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4360 mng_background_color.green=
4361 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4362 mng_background_color.blue=
4363 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4364 mng_background_color.opacity=OpaqueOpacity;
4366 #ifdef MNG_OBJECT_BUFFERS
4368 mng_background_object=(p[7] << 8) | p[8];
4371 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4374 if (memcmp(type,mng_PLTE,4) == 0)
4379 if (length && (length < 769))
4381 if (mng_info->global_plte == (png_colorp) NULL)
4382 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4383 sizeof(*mng_info->global_plte));
4384 for (i=0; i < (ssize_t) (length/3); i++)
4386 mng_info->global_plte[i].red=p[3*i];
4387 mng_info->global_plte[i].green=p[3*i+1];
4388 mng_info->global_plte[i].blue=p[3*i+2];
4390 mng_info->global_plte_length=(unsigned int) (length/3);
4393 for ( ; i < 256; i++)
4395 mng_info->global_plte[i].red=i;
4396 mng_info->global_plte[i].green=i;
4397 mng_info->global_plte[i].blue=i;
4400 mng_info->global_plte_length=256;
4403 mng_info->global_plte_length=0;
4404 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4407 if (memcmp(type,mng_tRNS,4) == 0)
4409 /* read global tRNS */
4412 for (i=0; i < (ssize_t) length; i++)
4413 mng_info->global_trns[i]=p[i];
4416 for ( ; i < 256; i++)
4417 mng_info->global_trns[i]=255;
4419 mng_info->global_trns_length=(unsigned int) length;
4420 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4423 if (memcmp(type,mng_gAMA,4) == 0)
4430 igamma=mng_get_long(p);
4431 mng_info->global_gamma=((float) igamma)*0.00001;
4432 mng_info->have_global_gama=MagickTrue;
4435 mng_info->have_global_gama=MagickFalse;
4436 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4440 if (memcmp(type,mng_cHRM,4) == 0)
4447 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4448 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4449 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4450 mng_info->global_chrm.red_primary.y=0.00001*
4451 mng_get_long(&p[12]);
4452 mng_info->global_chrm.green_primary.x=0.00001*
4453 mng_get_long(&p[16]);
4454 mng_info->global_chrm.green_primary.y=0.00001*
4455 mng_get_long(&p[20]);
4456 mng_info->global_chrm.blue_primary.x=0.00001*
4457 mng_get_long(&p[24]);
4458 mng_info->global_chrm.blue_primary.y=0.00001*
4459 mng_get_long(&p[28]);
4460 mng_info->have_global_chrm=MagickTrue;
4463 mng_info->have_global_chrm=MagickFalse;
4464 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4467 if (memcmp(type,mng_sRGB,4) == 0)
4474 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4475 mng_info->have_global_srgb=MagickTrue;
4478 mng_info->have_global_srgb=MagickFalse;
4479 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4482 if (memcmp(type,mng_iCCP,4) == 0)
4490 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4493 if (memcmp(type,mng_FRAM,4) == 0)
4496 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4497 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4499 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4500 image->delay=frame_delay;
4501 frame_delay=default_frame_delay;
4502 frame_timeout=default_frame_timeout;
4506 mng_info->framing_mode=p[0];
4507 if (logging != MagickFalse)
4508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4509 " Framing_mode=%d",mng_info->framing_mode);
4513 Note the delay and frame clipping boundaries.
4515 p++; /* framing mode */
4516 while (*p && ((p-chunk) < (ssize_t) length))
4517 p++; /* frame name */
4518 p++; /* frame name terminator */
4519 if ((p-chunk) < (ssize_t) (length-4))
4526 change_delay=(*p++);
4527 change_timeout=(*p++);
4528 change_clipping=(*p++);
4529 p++; /* change_sync */
4532 frame_delay=1UL*image->ticks_per_second*
4534 if (mng_info->ticks_per_second != 0)
4535 frame_delay/=mng_info->ticks_per_second;
4537 frame_delay=PNG_UINT_31_MAX;
4538 if (change_delay == 2)
4539 default_frame_delay=frame_delay;
4541 if (logging != MagickFalse)
4542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4543 " Framing_delay=%.20g",(double) frame_delay);
4547 frame_timeout=1UL*image->ticks_per_second*
4549 if (mng_info->ticks_per_second != 0)
4550 frame_timeout/=mng_info->ticks_per_second;
4552 frame_timeout=PNG_UINT_31_MAX;
4553 if (change_delay == 2)
4554 default_frame_timeout=frame_timeout;
4556 if (logging != MagickFalse)
4557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4558 " Framing_timeout=%.20g",(double) frame_timeout);
4560 if (change_clipping)
4562 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4565 if (logging != MagickFalse)
4566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4567 " Frame_clipping: L=%.20g R=%.20g T=%.20g B=%.20g",
4568 (double) fb.left,(double) fb.right,(double) fb.top,
4569 (double) fb.bottom);
4570 if (change_clipping == 2)
4576 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4577 subframe_width=(size_t) (mng_info->clip.right
4578 -mng_info->clip.left);
4579 subframe_height=(size_t) (mng_info->clip.bottom
4580 -mng_info->clip.top);
4582 Insert a background layer behind the frame if framing_mode is 4.
4584 #if defined(MNG_INSERT_LAYERS)
4585 if (logging != MagickFalse)
4586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4587 " subframe_width=%.20g, subframe_height=%.20g",(double)
4588 subframe_width,(double) subframe_height);
4589 if (insert_layers && (mng_info->framing_mode == 4) &&
4590 (subframe_width) && (subframe_height))
4593 Allocate next image structure.
4595 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4597 AcquireNextImage(image_info,image);
4598 if (GetNextImageInList(image) == (Image *) NULL)
4600 image=DestroyImageList(image);
4601 MngInfoFreeStruct(mng_info,&have_mng_structure);
4602 return((Image *) NULL);
4604 image=SyncNextImageInList(image);
4606 mng_info->image=image;
4607 if (term_chunk_found)
4609 image->start_loop=MagickTrue;
4610 image->iterations=mng_iterations;
4611 term_chunk_found=MagickFalse;
4614 image->start_loop=MagickFalse;
4615 image->columns=subframe_width;
4616 image->rows=subframe_height;
4617 image->page.width=subframe_width;
4618 image->page.height=subframe_height;
4619 image->page.x=mng_info->clip.left;
4620 image->page.y=mng_info->clip.top;
4621 image->background_color=mng_background_color;
4622 image->matte=MagickFalse;
4624 (void) SetImageBackgroundColor(image);
4625 if (logging != MagickFalse)
4626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4627 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4628 (double) mng_info->clip.left,(double) mng_info->clip.right,
4629 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4635 if (memcmp(type,mng_CLIP,4) == 0)
4644 first_object=(p[0] << 8) | p[1];
4645 last_object=(p[2] << 8) | p[3];
4646 for (i=(int) first_object; i <= (int) last_object; i++)
4648 if (mng_info->exists[i] && !mng_info->frozen[i])
4653 box=mng_info->object_clip[i];
4654 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4657 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4660 if (memcmp(type,mng_SAVE,4) == 0)
4662 for (i=1; i < MNG_MAX_OBJECTS; i++)
4663 if (mng_info->exists[i])
4665 mng_info->frozen[i]=MagickTrue;
4666 #ifdef MNG_OBJECT_BUFFERS
4667 if (mng_info->ob[i] != (MngBuffer *) NULL)
4668 mng_info->ob[i]->frozen=MagickTrue;
4672 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4676 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4681 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4683 for (i=1; i < MNG_MAX_OBJECTS; i++)
4684 MngInfoDiscardObject(mng_info,i);
4691 for (j=0; j < (ssize_t) length; j+=2)
4693 i=p[j] << 8 | p[j+1];
4694 MngInfoDiscardObject(mng_info,i);
4698 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701 if (memcmp(type,mng_MOVE,4) == 0)
4710 first_object=(p[0] << 8) | p[1];
4711 last_object=(p[2] << 8) | p[3];
4712 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4714 if (mng_info->exists[i] && !mng_info->frozen[i])
4722 old_pair.a=mng_info->x_off[i];
4723 old_pair.b=mng_info->y_off[i];
4724 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4725 mng_info->x_off[i]=new_pair.a;
4726 mng_info->y_off[i]=new_pair.b;
4729 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4733 if (memcmp(type,mng_LOOP,4) == 0)
4735 ssize_t loop_iters=1;
4736 loop_level=chunk[0];
4737 mng_info->loop_active[loop_level]=1; /* mark loop active */
4739 Record starting point.
4741 loop_iters=mng_get_long(&chunk[1]);
4742 if (logging != MagickFalse)
4743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4744 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4745 (double) loop_iters);
4746 if (loop_iters == 0)
4747 skipping_loop=loop_level;
4750 mng_info->loop_jump[loop_level]=TellBlob(image);
4751 mng_info->loop_count[loop_level]=loop_iters;
4753 mng_info->loop_iteration[loop_level]=0;
4754 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4757 if (memcmp(type,mng_ENDL,4) == 0)
4759 loop_level=chunk[0];
4760 if (skipping_loop > 0)
4762 if (skipping_loop == loop_level)
4765 Found end of zero-iteration loop.
4768 mng_info->loop_active[loop_level]=0;
4773 if (mng_info->loop_active[loop_level] == 1)
4775 mng_info->loop_count[loop_level]--;
4776 mng_info->loop_iteration[loop_level]++;
4777 if (logging != MagickFalse)
4778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4779 " ENDL: LOOP level %.20g has %.20g remaining iterations ",
4780 (double) loop_level,(double)
4781 mng_info->loop_count[loop_level]);
4782 if (mng_info->loop_count[loop_level] != 0)
4784 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4787 ThrowReaderException(CorruptImageError,
4788 "ImproperImageHeader");
4798 mng_info->loop_active[loop_level]=0;
4800 for (i=0; i < loop_level; i++)
4801 if (mng_info->loop_active[i] == 1)
4802 last_level=(short) i;
4803 loop_level=last_level;
4807 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4810 if (memcmp(type,mng_CLON,4) == 0)
4812 if (mng_info->clon_warning == 0)
4813 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4814 CoderError,"CLON is not implemented yet","`%s'",
4816 mng_info->clon_warning++;
4818 if (memcmp(type,mng_MAGN,4) == 0)
4833 magn_first=(p[0] << 8) | p[1];
4837 magn_last=(p[2] << 8) | p[3];
4839 magn_last=magn_first;
4840 #ifndef MNG_OBJECT_BUFFERS
4841 if (magn_first || magn_last)
4842 if (mng_info->magn_warning == 0)
4844 (void) ThrowMagickException(&image->exception,
4845 GetMagickModule(),CoderError,
4846 "MAGN is not implemented yet for nonzero objects",
4847 "`%s'",image->filename);
4848 mng_info->magn_warning++;
4857 magn_mx=(p[5] << 8) | p[6];
4864 magn_my=(p[7] << 8) | p[8];
4871 magn_ml=(p[9] << 8) | p[10];
4878 magn_mr=(p[11] << 8) | p[12];
4885 magn_mt=(p[13] << 8) | p[14];
4892 magn_mb=(p[15] << 8) | p[16];
4901 magn_methy=magn_methx;
4903 if (magn_methx > 5 || magn_methy > 5)
4904 if (mng_info->magn_warning == 0)
4906 (void) ThrowMagickException(&image->exception,
4907 GetMagickModule(),CoderError,
4908 "Unknown MAGN method in MNG datastream","`%s'",
4910 mng_info->magn_warning++;
4912 #ifdef MNG_OBJECT_BUFFERS
4913 /* Magnify existing objects in the range magn_first to magn_last */
4915 if (magn_first == 0 || magn_last == 0)
4917 /* Save the magnification factors for object 0 */
4918 mng_info->magn_mb=magn_mb;
4919 mng_info->magn_ml=magn_ml;
4920 mng_info->magn_mr=magn_mr;
4921 mng_info->magn_mt=magn_mt;
4922 mng_info->magn_mx=magn_mx;
4923 mng_info->magn_my=magn_my;
4924 mng_info->magn_methx=magn_methx;
4925 mng_info->magn_methy=magn_methy;
4928 if (memcmp(type,mng_PAST,4) == 0)
4930 if (mng_info->past_warning == 0)
4931 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4932 CoderError,"PAST is not implemented yet","`%s'",
4934 mng_info->past_warning++;
4936 if (memcmp(type,mng_SHOW,4) == 0)
4938 if (mng_info->show_warning == 0)
4939 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4940 CoderError,"SHOW is not implemented yet","`%s'",
4942 mng_info->show_warning++;
4944 if (memcmp(type,mng_sBIT,4) == 0)
4947 mng_info->have_global_sbit=MagickFalse;
4950 mng_info->global_sbit.gray=p[0];
4951 mng_info->global_sbit.red=p[0];
4952 mng_info->global_sbit.green=p[1];
4953 mng_info->global_sbit.blue=p[2];
4954 mng_info->global_sbit.alpha=p[3];
4955 mng_info->have_global_sbit=MagickTrue;
4958 if (memcmp(type,mng_pHYs,4) == 0)
4962 mng_info->global_x_pixels_per_unit=
4963 (size_t) mng_get_long(p);
4964 mng_info->global_y_pixels_per_unit=
4965 (size_t) mng_get_long(&p[4]);
4966 mng_info->global_phys_unit_type=p[8];
4967 mng_info->have_global_phys=MagickTrue;
4970 mng_info->have_global_phys=MagickFalse;
4972 if (memcmp(type,mng_pHYg,4) == 0)
4974 if (mng_info->phyg_warning == 0)
4975 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4976 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4977 mng_info->phyg_warning++;
4979 if (memcmp(type,mng_BASI,4) == 0)
4981 skip_to_iend=MagickTrue;
4982 if (mng_info->basi_warning == 0)
4983 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4984 CoderError,"BASI is not implemented yet","`%s'",
4986 mng_info->basi_warning++;
4987 #ifdef MNG_BASI_SUPPORTED
4988 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4989 (p[2] << 8) | p[3]);
4990 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4991 (p[6] << 8) | p[7]);
4992 basi_color_type=p[8];
4993 basi_compression_method=p[9];
4994 basi_filter_type=p[10];
4995 basi_interlace_method=p[11];
4997 basi_red=(p[12] << 8) & p[13];
5001 basi_green=(p[14] << 8) & p[15];
5005 basi_blue=(p[16] << 8) & p[17];
5009 basi_alpha=(p[18] << 8) & p[19];
5012 if (basi_sample_depth == 16)
5018 basi_viewable=p[20];
5022 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5025 if (memcmp(type,mng_IHDR,4)
5026 #if defined(JNG_SUPPORTED)
5027 && memcmp(type,mng_JHDR,4)
5031 /* Not an IHDR or JHDR chunk */
5033 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5037 if (logging != MagickFalse)
5038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5039 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5040 mng_info->exists[object_id]=MagickTrue;
5041 mng_info->viewable[object_id]=MagickTrue;
5042 if (mng_info->invisible[object_id])
5044 if (logging != MagickFalse)
5045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5046 " Skipping invisible object");
5047 skip_to_iend=MagickTrue;
5048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5051 #if defined(MNG_INSERT_LAYERS)
5053 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5054 image_width=(size_t) mng_get_long(p);
5055 image_height=(size_t) mng_get_long(&p[4]);
5057 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5060 Insert a transparent background layer behind the entire animation
5061 if it is not full screen.
5063 #if defined(MNG_INSERT_LAYERS)
5064 if (insert_layers && mng_type && first_mng_object)
5066 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5067 (image_width < mng_info->mng_width) ||
5068 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5069 (image_height < mng_info->mng_height) ||
5070 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5072 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5075 Allocate next image structure.
5077 AcquireNextImage(image_info,image);
5078 if (GetNextImageInList(image) == (Image *) NULL)
5080 image=DestroyImageList(image);
5081 MngInfoFreeStruct(mng_info,&have_mng_structure);
5082 return((Image *) NULL);
5084 image=SyncNextImageInList(image);
5086 mng_info->image=image;
5087 if (term_chunk_found)
5089 image->start_loop=MagickTrue;
5090 image->iterations=mng_iterations;
5091 term_chunk_found=MagickFalse;
5094 image->start_loop=MagickFalse;
5096 Make a background rectangle.
5099 image->columns=mng_info->mng_width;
5100 image->rows=mng_info->mng_height;
5101 image->page.width=mng_info->mng_width;
5102 image->page.height=mng_info->mng_height;
5105 image->background_color=mng_background_color;
5106 (void) SetImageBackgroundColor(image);
5107 if (logging != MagickFalse)
5108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5109 " Inserted transparent background layer, W=%.20g, H=%.20g",
5110 (double) mng_info->mng_width,(double) mng_info->mng_height);
5114 Insert a background layer behind the upcoming image if
5115 framing_mode is 3, and we haven't already inserted one.
5117 if (insert_layers && (mng_info->framing_mode == 3) &&
5118 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5119 (simplicity & 0x08)))
5121 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5124 Allocate next image structure.
5126 AcquireNextImage(image_info,image);
5127 if (GetNextImageInList(image) == (Image *) NULL)
5129 image=DestroyImageList(image);
5130 MngInfoFreeStruct(mng_info,&have_mng_structure);
5131 return((Image *) NULL);
5133 image=SyncNextImageInList(image);
5135 mng_info->image=image;
5136 if (term_chunk_found)
5138 image->start_loop=MagickTrue;
5139 image->iterations=mng_iterations;
5140 term_chunk_found=MagickFalse;
5143 image->start_loop=MagickFalse;
5145 image->columns=subframe_width;
5146 image->rows=subframe_height;
5147 image->page.width=subframe_width;
5148 image->page.height=subframe_height;
5149 image->page.x=mng_info->clip.left;
5150 image->page.y=mng_info->clip.top;
5151 image->background_color=mng_background_color;
5152 image->matte=MagickFalse;
5153 (void) SetImageBackgroundColor(image);
5154 if (logging != MagickFalse)
5155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5156 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5157 (double) mng_info->clip.left,(double) mng_info->clip.right,
5158 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5160 #endif /* MNG_INSERT_LAYERS */
5161 first_mng_object=MagickFalse;
5162 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5165 Allocate next image structure.
5167 AcquireNextImage(image_info,image);
5168 if (GetNextImageInList(image) == (Image *) NULL)
5170 image=DestroyImageList(image);
5171 MngInfoFreeStruct(mng_info,&have_mng_structure);
5172 return((Image *) NULL);
5174 image=SyncNextImageInList(image);
5176 mng_info->image=image;
5177 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5178 GetBlobSize(image));
5179 if (status == MagickFalse)
5181 if (term_chunk_found)
5183 image->start_loop=MagickTrue;
5184 term_chunk_found=MagickFalse;
5187 image->start_loop=MagickFalse;
5188 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5190 image->delay=frame_delay;
5191 frame_delay=default_frame_delay;
5195 image->page.width=mng_info->mng_width;
5196 image->page.height=mng_info->mng_height;
5197 image->page.x=mng_info->x_off[object_id];
5198 image->page.y=mng_info->y_off[object_id];
5199 image->iterations=mng_iterations;
5201 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5203 if (logging != MagickFalse)
5204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5205 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5207 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5209 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5213 mng_info->image=image;
5214 mng_info->mng_type=mng_type;
5215 mng_info->object_id=object_id;
5217 if (memcmp(type,mng_IHDR,4) == 0)
5218 image=ReadOnePNGImage(mng_info,image_info,exception);
5219 #if defined(JNG_SUPPORTED)
5221 image=ReadOneJNGImage(mng_info,image_info,exception);
5224 if (image == (Image *) NULL)
5226 if (IsImageObject(previous) != MagickFalse)
5228 (void) DestroyImageList(previous);
5229 (void) CloseBlob(previous);
5231 MngInfoFreeStruct(mng_info,&have_mng_structure);
5232 return((Image *) NULL);
5234 if (image->columns == 0 || image->rows == 0)
5236 (void) CloseBlob(image);
5237 image=DestroyImageList(image);
5238 MngInfoFreeStruct(mng_info,&have_mng_structure);
5239 return((Image *) NULL);
5241 mng_info->image=image;
5248 if (mng_info->magn_methx || mng_info->magn_methy)
5254 if (logging != MagickFalse)
5255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5256 " Processing MNG MAGN chunk");
5258 if (mng_info->magn_methx == 1)
5260 magnified_width=mng_info->magn_ml;
5261 if (image->columns > 1)
5262 magnified_width += mng_info->magn_mr;
5263 if (image->columns > 2)
5264 magnified_width += (png_uint_32) ((image->columns-2)*(mng_info->magn_mx));
5268 magnified_width=(png_uint_32) image->columns;
5269 if (image->columns > 1)
5270 magnified_width += mng_info->magn_ml-1;
5271 if (image->columns > 2)
5272 magnified_width += mng_info->magn_mr-1;
5273 if (image->columns > 3)
5274 magnified_width += (png_uint_32) ((image->columns-3)*(mng_info->magn_mx-1));
5276 if (mng_info->magn_methy == 1)
5278 magnified_height=mng_info->magn_mt;
5279 if (image->rows > 1)
5280 magnified_height += mng_info->magn_mb;
5281 if (image->rows > 2)
5282 magnified_height += (image->rows-2)*(mng_info->magn_my);
5286 magnified_height=(png_uint_32) image->rows;
5287 if (image->rows > 1)
5288 magnified_height += mng_info->magn_mt-1;
5289 if (image->rows > 2)
5290 magnified_height += mng_info->magn_mb-1;
5291 if (image->rows > 3)
5292 magnified_height += (png_uint_32) ((image->rows-3)*(mng_info->magn_my-1));
5294 if (magnified_height > image->rows ||
5295 magnified_width > image->columns)
5310 register PixelPacket
5323 Allocate next image structure.
5325 if (logging != MagickFalse)
5326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5327 " Allocate magnified image");
5328 AcquireNextImage(image_info,image);
5329 if (GetNextImageInList(image) == (Image *) NULL)
5331 image=DestroyImageList(image);
5332 MngInfoFreeStruct(mng_info,&have_mng_structure);
5333 return((Image *) NULL);
5336 large_image=SyncNextImageInList(image);
5338 large_image->columns=magnified_width;
5339 large_image->rows=magnified_height;
5341 magn_methx=mng_info->magn_methx;
5342 magn_methy=mng_info->magn_methy;
5344 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5345 #define QM unsigned short
5346 if (magn_methx != 1 || magn_methy != 1)
5349 Scale pixels to unsigned shorts to prevent
5350 overflow of intermediate values of interpolations
5352 for (y=0; y < (ssize_t) image->rows; y++)
5354 q=GetAuthenticPixels(image,0,y,image->columns,1,
5356 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5358 q->red=ScaleQuantumToShort(q->red);
5359 q->green=ScaleQuantumToShort(q->green);
5360 q->blue=ScaleQuantumToShort(q->blue);
5361 q->opacity=ScaleQuantumToShort(q->opacity);
5364 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5372 if (image->matte != MagickFalse)
5373 (void) SetImageBackgroundColor(large_image);
5376 large_image->background_color.opacity=OpaqueOpacity;
5377 (void) SetImageBackgroundColor(large_image);
5378 if (magn_methx == 4)
5380 if (magn_methx == 5)
5382 if (magn_methy == 4)
5384 if (magn_methy == 5)
5388 /* magnify the rows into the right side of the large image */
5390 if (logging != MagickFalse)
5391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5392 " Magnify the rows to %.20g",(double) large_image->rows);
5393 m=(ssize_t) mng_info->magn_mt;
5395 length=(size_t) image->columns;
5396 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5397 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5398 if ((prev == (PixelPacket *) NULL) ||
5399 (next == (PixelPacket *) NULL))
5401 image=DestroyImageList(image);
5402 MngInfoFreeStruct(mng_info,&have_mng_structure);
5403 ThrowReaderException(ResourceLimitError,
5404 "MemoryAllocationFailed");
5406 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5407 (void) CopyMagickMemory(next,n,length);
5408 for (y=0; y < (ssize_t) image->rows; y++)
5411 m=(ssize_t) mng_info->magn_mt;
5412 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5413 m=(ssize_t) mng_info->magn_mb;
5414 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5415 m=(ssize_t) mng_info->magn_mb;
5416 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5419 m=(ssize_t) mng_info->magn_my;
5423 if (y < (ssize_t) image->rows-1)
5425 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5427 (void) CopyMagickMemory(next,n,length);
5429 for (i=0; i < m; i++, yy++)
5431 register PixelPacket
5434 assert(yy < (ssize_t) large_image->rows);
5437 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5439 q+=(large_image->columns-image->columns);
5440 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5442 /* TO DO: get color as function of indexes[x] */
5444 if (image->storage_class == PseudoClass)
5449 if (magn_methy <= 1)
5451 *q=(*pixels); /* replicate previous */
5453 else if (magn_methy == 2 || magn_methy == 4)
5460 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5461 -(*pixels).red)+m))/((ssize_t) (m*2))
5463 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5464 -(*pixels).green)+m))/((ssize_t) (m*2))
5466 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5467 -(*pixels).blue)+m))/((ssize_t) (m*2))
5469 if (image->matte != MagickFalse)
5470 (*q).opacity=(QM) (((ssize_t)
5472 -(*pixels).opacity)+m))
5473 /((ssize_t) (m*2))+(*pixels).opacity);
5475 if (magn_methy == 4)
5477 /* Replicate nearest */
5478 if (i <= ((m+1) << 1))
5479 (*q).opacity=(*pixels).opacity+0;
5481 (*q).opacity=(*n).opacity+0;
5484 else /* if (magn_methy == 3 || magn_methy == 5) */
5486 /* Replicate nearest */
5487 if (i <= ((m+1) << 1))
5491 if (magn_methy == 5)
5493 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5494 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5495 +(*pixels).opacity);
5502 if (SyncAuthenticPixels(large_image,exception) == 0)
5506 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5507 next=(PixelPacket *) RelinquishMagickMemory(next);
5509 length=image->columns;
5511 if (logging != MagickFalse)
5512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5513 " Delete original image");
5515 DeleteImageFromList(&image);
5519 mng_info->image=image;
5521 /* magnify the columns */
5522 if (logging != MagickFalse)
5523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5524 " Magnify the columns to %.20g",(double) image->columns);
5526 for (y=0; y < (ssize_t) image->rows; y++)
5528 register PixelPacket
5531 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5532 pixels=q+(image->columns-length);
5534 for (x=(ssize_t) (image->columns-length);
5535 x < (ssize_t) image->columns; x++)
5537 if (x == (ssize_t) (image->columns-length))
5538 m=(ssize_t) mng_info->magn_ml;
5539 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5540 m=(ssize_t) mng_info->magn_mr;
5541 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5542 m=(ssize_t) mng_info->magn_mr;
5543 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5546 m=(ssize_t) mng_info->magn_mx;
5547 for (i=0; i < m; i++)
5549 if (magn_methx <= 1)
5551 /* replicate previous */
5554 else if (magn_methx == 2 || magn_methx == 4)
5561 (*q).red=(QM) ((2*i*((*n).red
5563 /((ssize_t) (m*2))+(*pixels).red);
5564 (*q).green=(QM) ((2*i*((*n).green
5566 +m)/((ssize_t) (m*2))+(*pixels).green);
5567 (*q).blue=(QM) ((2*i*((*n).blue
5569 /((ssize_t) (m*2))+(*pixels).blue);
5570 if (image->matte != MagickFalse)
5571 (*q).opacity=(QM) ((2*i*((*n).opacity
5572 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5573 +(*pixels).opacity);
5575 if (magn_methx == 4)
5577 /* Replicate nearest */
5578 if (i <= ((m+1) << 1))
5579 (*q).opacity=(*pixels).opacity+0;
5581 (*q).opacity=(*n).opacity+0;
5584 else /* if (magn_methx == 3 || magn_methx == 5) */
5586 /* Replicate nearest */
5587 if (i <= ((m+1) << 1))
5591 if (magn_methx == 5)
5594 (*q).opacity=(QM) ((2*i*((*n).opacity
5595 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5596 +(*pixels).opacity);
5604 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5607 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5608 if (magn_methx != 1 || magn_methy != 1)
5611 Rescale pixels to Quantum
5613 for (y=0; y < (ssize_t) image->rows; y++)
5615 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5616 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5618 q->red=ScaleShortToQuantum(q->red);
5619 q->green=ScaleShortToQuantum(q->green);
5620 q->blue=ScaleShortToQuantum(q->blue);
5621 q->opacity=ScaleShortToQuantum(q->opacity);
5624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5629 if (logging != MagickFalse)
5630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5631 " Finished MAGN processing");
5636 Crop_box is with respect to the upper left corner of the MNG.
5638 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5639 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5640 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5641 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5642 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5643 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5644 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5645 if ((crop_box.left != (mng_info->image_box.left
5646 +mng_info->x_off[object_id])) ||
5647 (crop_box.right != (mng_info->image_box.right
5648 +mng_info->x_off[object_id])) ||
5649 (crop_box.top != (mng_info->image_box.top
5650 +mng_info->y_off[object_id])) ||
5651 (crop_box.bottom != (mng_info->image_box.bottom
5652 +mng_info->y_off[object_id])))
5654 if (logging != MagickFalse)
5655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5656 " Crop the PNG image");
5657 if ((crop_box.left < crop_box.right) &&
5658 (crop_box.top < crop_box.bottom))
5667 Crop_info is with respect to the upper left corner of
5670 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5671 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5672 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5673 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5674 image->page.width=image->columns;
5675 image->page.height=image->rows;
5678 im=CropImage(image,&crop_info,exception);
5679 if (im != (Image *) NULL)
5681 image->columns=im->columns;
5682 image->rows=im->rows;
5683 im=DestroyImage(im);
5684 image->page.width=image->columns;
5685 image->page.height=image->rows;
5686 image->page.x=crop_box.left;
5687 image->page.y=crop_box.top;
5693 No pixels in crop area. The MNG spec still requires
5694 a layer, though, so make a single transparent pixel in
5695 the top left corner.
5700 (void) SetImageBackgroundColor(image);
5701 image->page.width=1;
5702 image->page.height=1;
5707 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5708 image=mng_info->image;
5712 #if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5713 /* Determine if bit depth can be reduced from 16 to 8.
5714 * Note that the method GetImageDepth doesn't check background
5715 * and doesn't handle PseudoClass specially. Also it uses
5716 * multiplication and division by 257 instead of shifting, so
5719 if (mng_info->optimize && image->depth == 16)
5727 ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
5729 == ((size_t) image->background_color.red & 0xff)) &&
5730 ((((size_t) image->background_color.green >> 8) & 0xff)
5731 == ((size_t) image->background_color.green & 0xff)) &&
5732 ((((size_t) image->background_color.blue >> 8) & 0xff)
5733 == ((size_t) image->background_color.blue & 0xff)));
5734 if (ok_to_reduce && image->storage_class == PseudoClass)
5738 for (indx=0; indx < (ssize_t) image->colors; indx++)
5740 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
5742 == ((size_t) image->colormap[indx].red & 0xff)) &&
5743 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5744 == ((size_t) image->colormap[indx].green & 0xff)) &&
5745 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5746 == ((size_t) image->colormap[indx].blue & 0xff)));
5747 if (ok_to_reduce == MagickFalse)
5751 if ((ok_to_reduce != MagickFalse) &&
5752 (image->storage_class != PseudoClass))
5760 for (y=0; y < (ssize_t) image->rows; y++)
5762 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5763 if (p == (const PixelPacket *) NULL)
5765 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5768 (((size_t) p->red >> 8) & 0xff) ==
5769 ((size_t) p->red & 0xff)) &&
5770 ((((size_t) p->green >> 8) & 0xff) ==
5771 ((size_t) p->green & 0xff)) &&
5772 ((((size_t) p->blue >> 8) & 0xff) ==
5773 ((size_t) p->blue & 0xff)) &&
5775 (((size_t) p->opacity >> 8) & 0xff) ==
5776 ((size_t) p->opacity & 0xff)))));
5777 if (ok_to_reduce == 0)
5788 if (logging != MagickFalse)
5789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5790 " Reducing PNG bit depth to 8 without loss of info");
5794 GetImageException(image,exception);
5795 if (image_info->number_scenes != 0)
5797 if (mng_info->scenes_found >
5798 (ssize_t) (image_info->first_scene+image_info->number_scenes))
5801 if (logging != MagickFalse)
5802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5803 " Finished reading image datastream.");
5804 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5805 (void) CloseBlob(image);
5806 if (logging != MagickFalse)
5807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5808 " Finished reading all image datastreams.");
5809 #if defined(MNG_INSERT_LAYERS)
5810 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5811 (mng_info->mng_height))
5814 Insert a background layer if nothing else was found.
5816 if (logging != MagickFalse)
5817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5818 " No images found. Inserting a background layer.");
5819 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5822 Allocate next image structure.
5824 AcquireNextImage(image_info,image);
5825 if (GetNextImageInList(image) == (Image *) NULL)
5827 image=DestroyImageList(image);
5828 MngInfoFreeStruct(mng_info,&have_mng_structure);
5829 if (logging != MagickFalse)
5830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5831 " Allocation failed, returning NULL.");
5832 return((Image *) NULL);
5834 image=SyncNextImageInList(image);
5836 image->columns=mng_info->mng_width;
5837 image->rows=mng_info->mng_height;
5838 image->page.width=mng_info->mng_width;
5839 image->page.height=mng_info->mng_height;
5842 image->background_color=mng_background_color;
5843 image->matte=MagickFalse;
5844 if (image_info->ping == MagickFalse)
5845 (void) SetImageBackgroundColor(image);
5846 mng_info->image_found++;
5849 image->iterations=mng_iterations;
5850 if (mng_iterations == 1)
5851 image->start_loop=MagickTrue;
5852 while (GetPreviousImageInList(image) != (Image *) NULL)
5855 if (image_count > 10*mng_info->image_found)
5857 if (logging != MagickFalse)
5858 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5859 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5860 CoderError,"Linked list is corrupted, beginning of list not found",
5861 "`%s'",image_info->filename);
5862 return((Image *) NULL);
5864 image=GetPreviousImageInList(image);
5865 if (GetNextImageInList(image) == (Image *) NULL)
5867 if (logging != MagickFalse)
5868 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5869 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5870 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5871 image_info->filename);
5874 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5875 GetNextImageInList(image) ==
5878 if (logging != MagickFalse)
5879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5880 " First image null");
5881 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5882 CoderError,"image->next for first image is NULL but shouldn't be.",
5883 "`%s'",image_info->filename);
5885 if (mng_info->image_found == 0)
5887 if (logging != MagickFalse)
5888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5889 " No visible images found.");
5890 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5891 CoderError,"No visible images in file","`%s'",image_info->filename);
5892 if (image != (Image *) NULL)
5893 image=DestroyImageList(image);
5894 MngInfoFreeStruct(mng_info,&have_mng_structure);
5895 return((Image *) NULL);
5898 if (mng_info->ticks_per_second)
5899 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5900 final_delay/mng_info->ticks_per_second;
5902 image->start_loop=MagickTrue;
5903 /* Find final nonzero image delay */
5904 final_image_delay=0;
5905 while (GetNextImageInList(image) != (Image *) NULL)
5908 final_image_delay=image->delay;
5909 image=GetNextImageInList(image);
5911 if (final_delay < final_image_delay)
5912 final_delay=final_image_delay;
5913 image->delay=final_delay;
5914 if (logging != MagickFalse)
5915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5916 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
5917 (double) final_delay);
5918 if (logging != MagickFalse)
5924 image=GetFirstImageInList(image);
5925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5926 " Before coalesce:");
5927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5928 " scene 0 delay=%.20g",(double) image->delay);
5929 while (GetNextImageInList(image) != (Image *) NULL)
5931 image=GetNextImageInList(image);
5932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5933 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
5937 image=GetFirstImageInList(image);
5938 #ifdef MNG_COALESCE_LAYERS
5948 if (logging != MagickFalse)
5949 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5951 next_image=CoalesceImages(image,&image->exception);
5952 if (next_image == (Image *) NULL)
5953 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5954 image=DestroyImageList(image);
5956 for (next=image; next != (Image *) NULL; next=next_image)
5958 next->page.width=mng_info->mng_width;
5959 next->page.height=mng_info->mng_height;
5962 next->scene=scene++;
5963 next_image=GetNextImageInList(next);
5964 if (next_image == (Image *) NULL)
5966 if (next->delay == 0)
5969 next_image->previous=GetPreviousImageInList(next);
5970 if (GetPreviousImageInList(next) == (Image *) NULL)
5973 next->previous->next=next_image;
5974 next=DestroyImage(next);
5980 while (GetNextImageInList(image) != (Image *) NULL)
5981 image=GetNextImageInList(image);
5982 image->dispose=BackgroundDispose;
5984 if (logging != MagickFalse)
5990 image=GetFirstImageInList(image);
5991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5992 " After coalesce:");
5993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5994 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
5995 (double) image->dispose);
5996 while (GetNextImageInList(image) != (Image *) NULL)
5998 image=GetNextImageInList(image);
5999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6000 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6001 (double) image->delay,(double) image->dispose);
6004 image=GetFirstImageInList(image);
6005 MngInfoFreeStruct(mng_info,&have_mng_structure);
6006 have_mng_structure=MagickFalse;
6007 if (logging != MagickFalse)
6008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6009 return(GetFirstImageInList(image));
6011 #else /* PNG_LIBPNG_VER > 10011 */
6012 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6014 printf("Your PNG library is too old: You have libpng-%s\n",
6015 PNG_LIBPNG_VER_STRING);
6016 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6017 "PNG library is too old","`%s'",image_info->filename);
6018 return(Image *) NULL;
6020 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6022 return(ReadPNGImage(image_info,exception));
6024 #endif /* PNG_LIBPNG_VER > 10011 */
6028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6032 % R e g i s t e r P N G I m a g e %
6036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6038 % RegisterPNGImage() adds properties for the PNG image format to
6039 % the list of supported formats. The properties include the image format
6040 % tag, a method to read and/or write the format, whether the format
6041 % supports the saving of more than one frame to the same file or blob,
6042 % whether the format supports native in-memory I/O, and a brief
6043 % description of the format.
6045 % The format of the RegisterPNGImage method is:
6047 % size_t RegisterPNGImage(void)
6050 ModuleExport size_t RegisterPNGImage(void)
6053 version[MaxTextExtent];
6061 "See http://www.libpng.org/ for details about the PNG format."
6065 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6070 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6075 #if defined(PNG_LIBPNG_VER_STRING)
6076 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6077 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6078 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6080 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6081 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6085 entry=SetMagickInfo("MNG");
6086 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6087 #if defined(MAGICKCORE_PNG_DELEGATE)
6088 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6089 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6091 entry->magick=(IsImageFormatHandler *) IsMNG;
6092 entry->description=ConstantString("Multiple-image Network Graphics");
6093 if (*version != '\0')
6094 entry->version=ConstantString(version);
6095 entry->module=ConstantString("PNG");
6096 entry->note=ConstantString(MNGNote);
6097 (void) RegisterMagickInfo(entry);
6099 entry=SetMagickInfo("PNG");
6100 #if defined(MAGICKCORE_PNG_DELEGATE)
6101 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6102 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6104 entry->magick=(IsImageFormatHandler *) IsPNG;
6105 entry->adjoin=MagickFalse;
6106 entry->description=ConstantString("Portable Network Graphics");
6107 entry->module=ConstantString("PNG");
6108 if (*version != '\0')
6109 entry->version=ConstantString(version);
6110 entry->note=ConstantString(PNGNote);
6111 (void) RegisterMagickInfo(entry);
6113 entry=SetMagickInfo("PNG8");
6114 #if defined(MAGICKCORE_PNG_DELEGATE)
6115 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6116 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6118 entry->magick=(IsImageFormatHandler *) IsPNG;
6119 entry->adjoin=MagickFalse;
6120 entry->description=ConstantString(
6121 "8-bit indexed with optional binary transparency");
6122 entry->module=ConstantString("PNG");
6123 (void) RegisterMagickInfo(entry);
6125 entry=SetMagickInfo("PNG24");
6127 #if defined(ZLIB_VERSION)
6128 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6129 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6130 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6132 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6133 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6136 if (*version != '\0')
6137 entry->version=ConstantString(version);
6138 #if defined(MAGICKCORE_PNG_DELEGATE)
6139 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6140 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6142 entry->magick=(IsImageFormatHandler *) IsPNG;
6143 entry->adjoin=MagickFalse;
6144 entry->description=ConstantString("opaque 24-bit RGB");
6145 entry->module=ConstantString("PNG");
6146 (void) RegisterMagickInfo(entry);
6148 entry=SetMagickInfo("PNG32");
6149 #if defined(MAGICKCORE_PNG_DELEGATE)
6150 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6151 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6153 entry->magick=(IsImageFormatHandler *) IsPNG;
6154 entry->adjoin=MagickFalse;
6155 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6156 entry->module=ConstantString("PNG");
6157 (void) RegisterMagickInfo(entry);
6159 entry=SetMagickInfo("JNG");
6160 #if defined(JNG_SUPPORTED)
6161 #if defined(MAGICKCORE_PNG_DELEGATE)
6162 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6163 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6166 entry->magick=(IsImageFormatHandler *) IsJNG;
6167 entry->adjoin=MagickFalse;
6168 entry->description=ConstantString("JPEG Network Graphics");
6169 entry->module=ConstantString("PNG");
6170 entry->note=ConstantString(JNGNote);
6171 (void) RegisterMagickInfo(entry);
6172 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6173 png_semaphore=AllocateSemaphoreInfo();
6175 return(MagickImageCoderSignature);
6179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6183 % U n r e g i s t e r P N G I m a g e %
6187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6189 % UnregisterPNGImage() removes format registrations made by the
6190 % PNG module from the list of supported formats.
6192 % The format of the UnregisterPNGImage method is:
6194 % UnregisterPNGImage(void)
6197 ModuleExport void UnregisterPNGImage(void)
6199 (void) UnregisterMagickInfo("MNG");
6200 (void) UnregisterMagickInfo("PNG");
6201 (void) UnregisterMagickInfo("PNG8");
6202 (void) UnregisterMagickInfo("PNG24");
6203 (void) UnregisterMagickInfo("PNG32");
6204 (void) UnregisterMagickInfo("JNG");
6205 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6206 if (png_semaphore != (SemaphoreInfo *) NULL)
6207 DestroySemaphoreInfo(&png_semaphore);
6211 #if defined(MAGICKCORE_PNG_DELEGATE)
6212 #if PNG_LIBPNG_VER > 10011
6214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6218 % W r i t e M N G I m a g e %
6222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6224 % WriteMNGImage() writes an image in the Portable Network Graphics
6225 % Group's "Multiple-image Network Graphics" encoded image format.
6227 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6229 % The format of the WriteMNGImage method is:
6231 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6233 % A description of each parameter follows.
6235 % o image_info: the image info.
6237 % o image: The image.
6240 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6241 % "To do" under ReadPNGImage):
6243 % Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6244 % some GIF animations don't convert properly)
6246 % Preserve all unknown and not-yet-handled known chunks found in input
6247 % PNG file and copy them into output PNG files according to the PNG
6250 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6252 % Improve selection of color type (use indexed-colour or indexed-colour
6253 % with tRNS when 256 or fewer unique RGBA values are present).
6255 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6256 % This will be complicated if we limit ourselves to generating MNG-LC
6257 % files. For now we ignore disposal method 3 and simply overlay the next
6260 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6261 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6262 % [mostly done 15 June 1999 but still need to take care of tRNS]
6264 % Check for identical sRGB and replace with a global sRGB (and remove
6265 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6266 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6267 % local gAMA/cHRM with local sRGB if appropriate).
6269 % Check for identical sBIT chunks and write global ones.
6271 % Provide option to skip writing the signature tEXt chunks.
6273 % Use signatures to detect identical objects and reuse the first
6274 % instance of such objects instead of writing duplicate objects.
6276 % Use a smaller-than-32k value of compression window size when
6279 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6280 % ancillary text chunks and save profiles.
6282 % Provide an option to force LC files (to ensure exact framing rate)
6285 % Provide an option to force VLC files instead of LC, even when offsets
6286 % are present. This will involve expanding the embedded images with a
6287 % transparent region at the top and/or left.
6291 png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6292 png_info *ping_info, unsigned char *profile_type, unsigned char
6293 *profile_description, unsigned char *profile_data, png_uint_32 length)
6312 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6314 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6317 if (image_info->verbose)
6319 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6320 (char *) profile_type, (double) length);
6322 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6323 description_length=(png_uint_32) strlen((const char *) profile_description);
6324 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6325 + description_length);
6326 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6327 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6328 text[0].key[0]='\0';
6329 (void) ConcatenateMagickString(text[0].key,
6330 "Raw profile type ",MaxTextExtent);
6331 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6335 (void) CopyMagickString(dp,(const char *) profile_description,
6337 dp+=description_length;
6339 (void) FormatMagickString(dp,allocated_length-
6340 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6342 for (i=0; i < (ssize_t) length; i++)
6346 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6347 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6351 text[0].text_length=(png_size_t) (dp-text[0].text);
6352 text[0].compression=image_info->compression == NoCompression ||
6353 (image_info->compression == UndefinedCompression &&
6354 text[0].text_length < 128) ? -1 : 0;
6355 if (text[0].text_length <= allocated_length)
6356 png_set_text(ping,ping_info,text,1);
6357 png_free(ping,text[0].text);
6358 png_free(ping,text[0].key);
6359 png_free(ping,text);
6362 static MagickBooleanType png_write_chunk_from_profile(Image *image,
6363 const char *string, int logging)
6376 ResetImageProfileIterator(image);
6377 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6378 profile=GetImageProfile(image,name);
6379 if (profile != (const StringInfo *) NULL)
6384 if (LocaleNCompare(name,string,11) == 0) {
6385 if (logging != MagickFalse)
6386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6387 " Found %s profile",name);
6389 png_profile=CloneStringInfo(profile);
6390 data=GetStringInfoDatum(png_profile),
6391 length=(png_uint_32) GetStringInfoLength(png_profile);
6396 (void) WriteBlobMSBULong(image,length-5); /* data length */
6397 (void) WriteBlob(image,length-1,data+1);
6398 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6399 png_profile=DestroyStringInfo(png_profile);
6402 name=GetNextImageProfile(image);
6407 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6408 const ImageInfo *image_info,Image *image)
6410 /* Write one PNG image */
6429 ping_trans_alpha[256];
6457 register IndexPacket
6474 ping_interlace_method,
6475 ping_compression_method,
6489 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6490 " enter WriteOnePNGImage()");
6492 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6493 LockSemaphoreInfo(png_semaphore);
6496 /* Initialize some stuff */
6499 ping_interlace_method=0,
6500 ping_compression_method=0,
6501 ping_filter_method=0,
6504 ping_background.red = 0;
6505 ping_background.green = 0;
6506 ping_background.blue = 0;
6507 ping_background.gray = 0;
6508 ping_background.index = 0;
6510 ping_trans_color.red=0;
6511 ping_trans_color.green=0;
6512 ping_trans_color.blue=0;
6513 ping_trans_color.gray=0;
6515 quantum_info = (QuantumInfo *) NULL;
6516 image_colors=image->colors;
6517 image_depth=image->depth;
6518 image_matte=image->matte;
6520 if (image->colorspace != RGBColorspace)
6521 (void) TransformImageColorspace(image,RGBColorspace);
6522 mng_info->IsPalette=image->storage_class == PseudoClass &&
6523 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
6524 mng_info->optimize=image_info->type == OptimizeType;
6527 Allocate the PNG structures
6529 #ifdef PNG_USER_MEM_SUPPORTED
6530 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6531 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6532 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6534 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6535 PNGErrorHandler,PNGWarningHandler);
6537 if (ping == (png_struct *) NULL)
6538 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6539 ping_info=png_create_info_struct(ping);
6540 if (ping_info == (png_info *) NULL)
6542 png_destroy_write_struct(&ping,(png_info **) NULL);
6543 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6545 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6546 png_pixels=(unsigned char *) NULL;
6548 if (setjmp(png_jmpbuf(ping)))
6554 if (image_info->verbose)
6555 (void) printf("PNG write has failed.\n");
6557 png_destroy_write_struct(&ping,&ping_info);
6558 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6559 UnlockSemaphoreInfo(png_semaphore);
6561 return(MagickFalse);
6564 Prepare PNG for writing.
6566 #if defined(PNG_MNG_FEATURES_SUPPORTED)
6567 if (mng_info->write_mng)
6568 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6570 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6571 if (mng_info->write_mng)
6572 png_permit_empty_plte(ping,MagickTrue);
6576 ping_width=(png_uint_32) image->columns;
6577 ping_height=(png_uint_32) image->rows;
6578 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6580 if (mng_info->write_png_depth != 0)
6581 image_depth=mng_info->write_png_depth;
6582 /* Adjust requested depth to next higher valid depth if necessary */
6583 if (image_depth > 8)
6585 if ((image_depth > 4) && (image_depth < 8))
6587 if (image_depth == 3)
6589 if (logging != MagickFalse)
6591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6592 " width=%.20g",(double) ping_width);
6593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6594 " height=%.20g",(double) ping_height);
6595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6596 " image_matte=%.20g",(double) image->matte);
6597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6598 " image_depth=%.20g",(double) image->depth);
6599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6600 " requested PNG image_depth=%.20g",(double) image->depth);
6602 save_image_depth=image_depth;
6603 ping_bit_depth=(png_byte) save_image_depth;
6604 #if defined(PNG_pHYs_SUPPORTED)
6605 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6606 (!mng_info->write_mng || !mng_info->equal_physs))
6615 if (image->units == PixelsPerInchResolution)
6617 unit_type=PNG_RESOLUTION_METER;
6618 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6619 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6621 else if (image->units == PixelsPerCentimeterResolution)
6623 unit_type=PNG_RESOLUTION_METER;
6624 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6625 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6629 unit_type=PNG_RESOLUTION_UNKNOWN;
6630 x_resolution=(png_uint_32) image->x_resolution;
6631 y_resolution=(png_uint_32) image->y_resolution;
6633 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6634 if (logging != MagickFalse)
6635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636 " Setting up pHYs chunk");
6639 #if defined(PNG_oFFs_SUPPORTED)
6640 if (image->page.x || image->page.y)
6642 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6643 (png_int_32) image->page.y, 0);
6644 if (logging != MagickFalse)
6645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6646 " Setting up oFFs chunk");
6649 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6654 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6659 maxval=(1UL << image_depth)-1;
6660 background.red=(png_uint_16)
6661 (QuantumScale*(maxval*image->background_color.red));
6662 background.green=(png_uint_16)
6663 (QuantumScale*(maxval*image->background_color.green));
6664 background.blue=(png_uint_16)
6665 (QuantumScale*(maxval*image->background_color.blue));
6666 background.gray=(png_uint_16)
6667 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6671 background.red=image->background_color.red;
6672 background.green=image->background_color.green;
6673 background.blue=image->background_color.blue;
6675 (png_uint_16) PixelIntensity(&image->background_color);
6677 background.index=(png_byte) background.gray;
6678 if (logging != MagickFalse)
6679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6680 " Setting up bKGd chunk");
6681 png_set_bKGD(ping,ping_info,&background);
6684 Select the color type.
6688 if ((mng_info->write_png_colortype-1) == PNG_COLOR_TYPE_PALETTE)
6689 mng_info->write_png8=MagickTrue;
6690 if (mng_info->write_png8)
6692 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6694 image_depth=ping_bit_depth;
6696 /* TO DO: make this a function cause it's used twice, except
6697 for reducing the sample depth from 8. */
6706 number_colors=image_colors;
6707 if ((image->storage_class == DirectClass) || (number_colors > 256))
6709 GetQuantizeInfo(&quantize_info);
6710 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6711 MagickFalse ? MagickTrue : MagickFalse;
6712 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6714 (void) QuantizeImage(&quantize_info,image);
6715 number_colors=image_colors;
6716 (void) SyncImage(image);
6717 if (logging != MagickFalse)
6718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6719 " Colors quantized to %.20g",(double) number_colors);
6722 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6726 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6727 #if defined(PNG_SORT_PALETTE)
6728 save_number_colors=image_colors;
6729 if (CompressColormapTransFirst(image) == MagickFalse)
6730 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6731 number_colors=image->colors;
6732 image_colors=save_number_colors;
6734 palette=(png_color *) AcquireQuantumMemory(257,
6736 if (palette == (png_color *) NULL)
6737 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6738 if (logging != MagickFalse)
6739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6740 " Setting up PLTE chunk with %d colors",
6741 (int) number_colors);
6742 for (i=0; i < (ssize_t) number_colors; i++)
6744 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6745 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6746 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6747 if (logging != MagickFalse)
6748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6749 #if MAGICKCORE_QUANTUM_DEPTH == 8
6750 " %3ld (%3d,%3d,%3d)",
6752 " %5ld (%5d,%5d,%5d)",
6754 (long) i,palette[i].red,palette[i].green,palette[i].blue);
6760 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6761 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6762 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6764 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6765 palette=(png_colorp) RelinquishMagickMemory(palette);
6766 image_depth=ping_bit_depth;
6777 Identify which colormap entry is transparent.
6779 assert(number_colors <= 256);
6780 for (i=0; i < (ssize_t) number_colors; i++)
6782 exception=(&image->exception);
6783 for (y=0; y < (ssize_t) image->rows; y++)
6785 register const PixelPacket
6788 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6789 if (p == (PixelPacket *) NULL)
6791 indexes=GetAuthenticIndexQueue(image);
6792 for (x=0; x < (ssize_t) image->columns; x++)
6794 if (p->opacity != OpaqueOpacity)
6796 indexes[x]=(IndexPacket) (number_colors-1);
6797 trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
6798 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
6802 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6805 for (i=0; i < (ssize_t) number_colors; i++)
6806 if (trans_alpha[i] != 255)
6807 ping_num_trans=(unsigned short) (i+1);
6809 if (ping_num_trans == 0)
6810 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6811 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6813 if (ping_num_trans != 0)
6815 for (i=0; i<256; i++)
6816 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6819 (void) png_set_tRNS(ping, ping_info,
6825 Identify which colormap entry is the background color.
6827 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
6828 if (IsPNGColorEqual(ping_background,image->colormap[i]))
6830 ping_background.index=(png_byte) i;
6832 if (image_matte != MagickFalse)
6834 /* TO DO: reduce to binary transparency */
6836 } /* end of write_png8 */
6837 else if (mng_info->write_png24)
6839 image_matte=MagickFalse;
6840 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6842 else if (mng_info->write_png32)
6844 image_matte=MagickTrue;
6845 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6849 image_depth=ping_bit_depth;
6850 if (mng_info->write_png_colortype)
6852 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6853 image_matte=MagickFalse;
6854 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6855 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6856 image_matte=MagickTrue;
6860 if (logging != MagickFalse)
6861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6862 "Selecting PNG colortype");
6863 ping_color_type=(png_byte) ((matte == MagickTrue)?
6864 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6865 if(image_info->type == TrueColorType)
6867 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6868 image_matte=MagickFalse;
6870 if(image_info->type == TrueColorMatteType)
6872 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6873 image_matte=MagickTrue;
6875 if ((image_info->type == UndefinedType ||
6876 image_info->type == OptimizeType ||
6877 image_info->type == GrayscaleType) &&
6878 image_matte == MagickFalse && ImageIsGray(image))
6880 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6881 image_matte=MagickFalse;
6883 if ((image_info->type == UndefinedType ||
6884 image_info->type == OptimizeType ||
6885 image_info->type == GrayscaleMatteType) &&
6886 image_matte == MagickTrue && ImageIsGray(image))
6888 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6889 image_matte=MagickTrue;
6892 if (logging != MagickFalse)
6893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6894 "Selected PNG colortype=%d",ping_color_type);
6896 if (ping_bit_depth < 8)
6898 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6899 ping_color_type == PNG_COLOR_TYPE_RGB ||
6900 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6904 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
6906 if (image->matte == MagickFalse && image->colors < 256)
6908 if (ImageIsMonochrome(image))
6911 if (ping_bit_depth < (int)mng_info->write_png_depth)
6912 ping_bit_depth = mng_info->write_png_depth;
6916 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6921 if (image->colors == 0)
6924 (void) ThrowMagickException(&image->exception,
6925 GetMagickModule(),CoderError,
6926 "image has 0 colors", "`%s'","");
6929 if (logging != MagickFalse)
6930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
6933 ping_bit_depth <<= 1;
6935 if (logging != MagickFalse)
6937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6938 " Number of colors: %.20g",(double) image_colors);
6939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6940 " Tentative PNG bit depth: %d",ping_bit_depth);
6942 if (mng_info->write_png_depth)
6944 old_bit_depth=ping_bit_depth;
6945 if (ping_bit_depth < (int)mng_info->write_png_depth)
6947 ping_bit_depth = mng_info->write_png_depth;
6948 if (ping_bit_depth > 8)
6950 if (ping_bit_depth != (int) old_bit_depth)
6952 if (logging != MagickFalse)
6953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6954 " Colors increased to %.20g",(double)
6961 image_depth=ping_bit_depth;
6962 if (logging != MagickFalse)
6964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6965 " Tentative PNG color type: %.20g",(double) ping_color_type);
6966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967 " image_info->type: %.20g",(double) image_info->type);
6968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6969 " image_depth: %.20g",(double) image_depth);
6970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6971 " ping_bit_depth: %.20g",(double) ping_bit_depth);
6974 if (matte && (mng_info->optimize || mng_info->IsPalette))
6976 register const PixelPacket
6979 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
6980 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6981 for (y=0; y < (ssize_t) image->rows; y++)
6983 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6984 if (p == (const PixelPacket *) NULL)
6986 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6988 if (IsGray(p) == MagickFalse)
6990 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6997 Determine if there is any transparent color.
6999 for (y=0; y < (ssize_t) image->rows; y++)
7001 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7002 if (p == (const PixelPacket *) NULL)
7004 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7006 if (p->opacity != OpaqueOpacity)
7013 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
7016 No transparent pixels are present. Change 4 or 6 to 0 or 2.
7018 image_matte=MagickFalse;
7019 ping_color_type&=0x03;
7027 if (ping_bit_depth == 8)
7029 if (ping_bit_depth == 4)
7031 if (ping_bit_depth == 2)
7033 if (ping_bit_depth == 1)
7035 ping_trans_color.red=(png_uint_16)
7036 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
7037 ping_trans_color.green=(png_uint_16)
7038 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
7039 ping_trans_color.blue=(png_uint_16)
7040 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
7041 ping_trans_color.gray=(png_uint_16)
7042 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
7043 ping_trans_color.index=(png_byte)
7044 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
7045 (void) png_set_tRNS(ping, ping_info, NULL, 0,
7048 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7051 Determine if there is one and only one transparent color
7052 and if so if it is fully transparent.
7054 for (y=0; y < (ssize_t) image->rows; y++)
7056 p=GetVirtualPixels(image,0,y,image->columns,1,
7059 if (p == (const PixelPacket *) NULL)
7061 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7063 if (p->opacity != OpaqueOpacity)
7065 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
7067 break; /* Can't use RGB + tRNS for multiple
7068 transparent colors. */
7070 if (p->opacity != (Quantum) TransparentOpacity)
7072 break; /* Can't use RGB + tRNS for
7073 semitransparency. */
7078 if (IsPNGColorEqual(ping_trans_color,*p))
7079 break; /* Can't use RGB + tRNS when another pixel
7080 having the same RGB samples is
7089 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7091 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7093 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
7094 if (image_depth == 8)
7096 ping_trans_color.red&=0xff;
7097 ping_trans_color.green&=0xff;
7098 ping_trans_color.blue&=0xff;
7099 ping_trans_color.gray&=0xff;
7104 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7105 image_matte=MagickFalse;
7106 if ((mng_info->optimize || mng_info->IsPalette) &&
7107 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7108 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7111 if (image_matte != MagickFalse)
7112 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7115 ping_color_type=PNG_COLOR_TYPE_GRAY;
7116 if (save_image_depth == 16 && image_depth == 8)
7117 ping_trans_color.gray*=0x0101;
7119 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7120 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7121 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7122 image_colors=one << image_depth;
7123 if (image_depth > 8)
7128 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7130 if(!mng_info->write_png_depth)
7133 while ((int) (one << ping_bit_depth)
7134 < (ssize_t) image_colors)
7135 ping_bit_depth <<= 1;
7138 else if (mng_info->optimize && ping_color_type ==
7139 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7140 mng_info->IsPalette)
7143 /* Check if grayscale is reducible */
7145 depth_4_ok=MagickTrue,
7146 depth_2_ok=MagickTrue,
7147 depth_1_ok=MagickTrue;
7149 for (i=0; i < (ssize_t) image_colors; i++)
7154 intensity=ScaleQuantumToChar(image->colormap[i].red);
7156 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7157 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7158 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7159 depth_2_ok=depth_1_ok=MagickFalse;
7160 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7161 depth_1_ok=MagickFalse;
7163 if (depth_1_ok && mng_info->write_png_depth <= 1)
7165 else if (depth_2_ok && mng_info->write_png_depth <= 2)
7167 else if (depth_4_ok && mng_info->write_png_depth <= 4)
7171 image_depth=ping_bit_depth;
7174 if (mng_info->IsPalette)
7179 number_colors=image_colors;
7181 if (image_depth <= 8)
7186 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7187 if (mng_info->have_write_global_plte && !matte)
7189 png_set_PLTE(ping,ping_info,NULL,0);
7191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7192 " Setting up empty PLTE chunk");
7196 #if defined(PNG_SORT_PALETTE)
7200 if (mng_info->optimize)
7202 save_number_colors=image_colors;
7203 if (CompressColormapTransFirst(image) == MagickFalse)
7204 ThrowWriterException(ResourceLimitError,
7205 "MemoryAllocationFailed");
7206 number_colors=image->colors;
7207 image_colors=save_number_colors;
7210 palette=(png_color *) AcquireQuantumMemory(257,
7212 if (palette == (png_color *) NULL)
7213 ThrowWriterException(ResourceLimitError,
7214 "MemoryAllocationFailed");
7215 for (i=0; i < (ssize_t) number_colors; i++)
7217 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7218 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7219 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7223 " Setting up PLTE chunk with %d colors",
7224 (int) number_colors);
7225 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
7226 palette=(png_colorp) RelinquishMagickMemory(palette);
7228 /* color_type is PNG_COLOR_TYPE_PALETTE */
7229 if (!mng_info->write_png_depth)
7236 while ((one << ping_bit_depth) < number_colors)
7237 ping_bit_depth <<= 1;
7245 register const PixelPacket
7251 register const IndexPacket
7255 Identify which colormap entry is transparent.
7257 assert(number_colors <= 256);
7258 for (i=0; i < (ssize_t) number_colors; i++)
7260 exception=(&image->exception);
7261 for (y=0; y < (ssize_t) image->rows; y++)
7263 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7264 if (p == (const PixelPacket *) NULL)
7266 packet_indexes=GetVirtualIndexQueue(image);
7267 for (x=0; x < (ssize_t) image->columns; x++)
7269 if (p->opacity != OpaqueOpacity)
7274 packet_index=packet_indexes[x];
7275 assert((size_t) packet_index < number_colors);
7276 if (trans[(ssize_t) packet_index] != 256)
7278 if (trans[(ssize_t) packet_index] != (png_byte) (255-
7279 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
7281 ping_color_type=(png_byte)
7282 PNG_COLOR_TYPE_RGB_ALPHA;
7286 trans[(ssize_t) packet_index]=(png_byte) (255-
7287 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
7291 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7294 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7295 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
7296 mng_info->IsPalette=MagickFalse;
7297 (void) SyncImage(image);
7299 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7300 " Cannot write image as indexed PNG, writing RGBA.");
7304 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7306 for (i=0; i < (ssize_t) number_colors; i++)
7308 if (trans[i] == 256)
7310 if (trans[i] != 255)
7311 ping_num_trans=(unsigned short) (i+1);
7314 if (ping_num_trans == 0)
7315 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7316 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7318 if (ping_num_trans != 0)
7320 for (i=0; i < (ssize_t) number_colors; i++)
7321 ping_trans_alpha[i]=(png_byte) trans[i];
7329 if (image_depth < 8)
7331 if ((save_image_depth == 16) && (image_depth == 8))
7333 ping_trans_color.red*=0x0101;
7334 ping_trans_color.green*=0x0101;
7335 ping_trans_color.blue*=0x0101;
7336 ping_trans_color.gray*=0x0101;
7341 Adjust background and transparency samples in sub-8-bit grayscale files.
7343 if (ping_bit_depth < 8 && ping_color_type ==
7344 PNG_COLOR_TYPE_GRAY)
7355 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
7358 background.gray=(png_uint_16)
7359 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7361 if (logging != MagickFalse)
7362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363 " Setting up bKGD chunk");
7364 png_set_bKGD(ping,ping_info,&background);
7366 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7367 ping_trans_color.gray));
7370 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7373 Identify which colormap entry is the background color.
7379 number_colors=image_colors;
7381 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
7382 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7385 ping_background.index=(png_byte) i;
7388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389 " Setting up bKGD chunk with index=%d",(int) i);
7391 png_set_bKGD(ping,ping_info,&ping_background);
7394 if (logging != MagickFalse)
7395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7396 " PNG color type: %d",ping_color_type);
7398 Initialize compression level and filtering.
7400 if (logging != MagickFalse)
7401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7402 " Setting up deflate compression");
7403 if (logging != MagickFalse)
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " Compression buffer size: 32768");
7406 png_set_compression_buffer_size(ping,32768L);
7407 if (logging != MagickFalse)
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " Compression mem level: 9");
7410 png_set_compression_mem_level(ping, 9);
7411 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7418 level=(int) MagickMin((ssize_t) quality/10,9);
7419 if (logging != MagickFalse)
7420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7421 " Compression level: %d",level);
7422 png_set_compression_level(ping,level);
7426 if (logging != MagickFalse)
7427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428 " Compression strategy: Z_HUFFMAN_ONLY");
7429 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7431 if (logging != MagickFalse)
7432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433 " Setting up filtering");
7434 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7436 /* This became available in libpng-1.0.9. Output must be a MNG. */
7437 if (mng_info->write_mng && ((quality % 10) == 7))
7439 if (logging != MagickFalse)
7440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7441 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7442 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
7445 if (logging != MagickFalse)
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453 if ((quality % 10) > 5)
7454 base_filter=PNG_ALL_FILTERS;
7456 if ((quality % 10) != 5)
7457 base_filter=(int) quality % 10;
7459 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7460 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
7462 base_filter=PNG_NO_FILTERS;
7464 base_filter=PNG_ALL_FILTERS;
7465 if (logging != MagickFalse)
7467 if (base_filter == PNG_ALL_FILTERS)
7468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7469 " Base filter method: ADAPTIVE");
7471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472 " Base filter method: NONE");
7474 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7477 ResetImageProfileIterator(image);
7478 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7480 profile=GetImageProfile(image,name);
7481 if (profile != (StringInfo *) NULL)
7483 #ifdef PNG_WRITE_iCCP_SUPPORTED
7484 if ((LocaleCompare(name,"ICC") == 0) ||
7485 (LocaleCompare(name,"ICM") == 0))
7486 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7487 GetStringInfoDatum(profile),
7488 (png_uint_32) GetStringInfoLength(profile));
7491 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7492 name,(unsigned char *) name,GetStringInfoDatum(profile),
7493 (png_uint_32) GetStringInfoLength(profile));
7495 if (logging != MagickFalse)
7496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7497 " Setting up text chunk with %s profile",name);
7498 name=GetNextImageProfile(image);
7501 #if defined(PNG_WRITE_sRGB_SUPPORTED)
7502 if ((mng_info->have_write_global_srgb == 0) &&
7503 ((image->rendering_intent != UndefinedIntent) ||
7504 (image->colorspace == sRGBColorspace)))
7507 Note image rendering intent.
7509 if (logging != MagickFalse)
7510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7511 " Setting up sRGB chunk");
7512 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7513 png_set_gAMA(ping,ping_info,0.45455);
7515 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
7518 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7522 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7524 if (logging != MagickFalse)
7525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7526 " Setting up gAMA chunk");
7527 png_set_gAMA(ping,ping_info,image->gamma);
7529 if ((mng_info->have_write_global_chrm == 0) &&
7530 (image->chromaticity.red_primary.x != 0.0))
7533 Note image chromaticity.
7534 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7542 wp=image->chromaticity.white_point;
7543 rp=image->chromaticity.red_primary;
7544 gp=image->chromaticity.green_primary;
7545 bp=image->chromaticity.blue_primary;
7547 if (logging != MagickFalse)
7548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7549 " Setting up cHRM chunk");
7550 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7554 ping_interlace_method=image_info->interlace != NoInterlace;
7556 if (mng_info->write_mng)
7557 png_set_sig_bytes(ping,8);
7559 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7561 if (mng_info->write_png_colortype)
7563 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7564 if (ImageIsGray(image) == MagickFalse)
7566 ping_color_type = PNG_COLOR_TYPE_RGB;
7567 if (ping_bit_depth < 8)
7571 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7572 if (ImageIsGray(image) == MagickFalse)
7573 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7576 if ((mng_info->write_png_depth &&
7577 (int) mng_info->write_png_depth != ping_bit_depth) ||
7578 (mng_info->write_png_colortype &&
7579 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
7580 mng_info->write_png_colortype != 7 &&
7581 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
7583 if (logging != MagickFalse)
7585 if (mng_info->write_png_depth)
7587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7588 " Defined PNG:bit-depth=%u, Computed depth=%u",
7589 mng_info->write_png_depth,
7592 if (mng_info->write_png_colortype)
7594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " Defined PNG:color-type=%u, Computed color type=%u",
7596 mng_info->write_png_colortype-1,
7600 if (0) png_error(ping,
7601 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7604 if (image_matte && !image->matte)
7606 /* Add an opaque matte channel */
7607 image->matte = MagickTrue;
7608 (void) SetImageOpacity(image,0);
7609 if (logging != MagickFalse)
7610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7611 " Added an opaque matte channel");
7614 if (image->matte == MagickTrue && ping_color_type < 4)
7616 if (ping_color_type == 3 && ping_num_trans == 0)
7618 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7619 if (logging != MagickFalse)
7620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7621 " Ignoring request to write tRNS chunk with num_trans==0");
7624 (void) png_set_tRNS(ping, ping_info,
7630 if (logging != MagickFalse)
7631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7632 " Writing PNG header chunks");
7634 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7635 ping_bit_depth,ping_color_type,
7636 ping_interlace_method,ping_compression_method,
7637 ping_filter_method);
7639 png_write_info_before_PLTE(ping, ping_info);
7640 /* write any png-chunk-b profiles */
7641 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7642 png_write_info(ping,ping_info);
7643 /* write any PNG-chunk-m profiles */
7644 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7646 if (image->page.width || image->page.height)
7651 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7652 PNGType(chunk,mng_vpAg);
7653 LogPNGChunk((int) logging,mng_vpAg,9L);
7654 PNGLong(chunk+4,(png_uint_32) image->page.width);
7655 PNGLong(chunk+8,(png_uint_32) image->page.height);
7656 chunk[12]=0; /* unit = pixels */
7657 (void) WriteBlob(image,13,chunk);
7658 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7661 #if (PNG_LIBPNG_VER == 10206)
7662 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7663 #define PNG_HAVE_IDAT 0x04
7664 ping->mode |= PNG_HAVE_IDAT;
7665 #undef PNG_HAVE_IDAT
7668 png_set_packing(ping);
7672 rowbytes=image->columns;
7673 if (image_depth > 8)
7675 switch (ping_color_type)
7677 case PNG_COLOR_TYPE_RGB:
7680 case PNG_COLOR_TYPE_GRAY_ALPHA:
7683 case PNG_COLOR_TYPE_RGBA:
7691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7692 " Writing PNG image data");
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
7696 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7697 sizeof(*png_pixels));
7698 if (png_pixels == (unsigned char *) NULL)
7699 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7701 Initialize image scanlines.
7703 if (setjmp(png_jmpbuf(ping)))
7709 if (image_info->verbose)
7710 (void) printf("PNG write has failed.\n");
7712 png_destroy_write_struct(&ping,&ping_info);
7713 if (quantum_info != (QuantumInfo *) NULL)
7714 quantum_info=DestroyQuantumInfo(quantum_info);
7715 if (png_pixels != (unsigned char *) NULL)
7716 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7717 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7718 UnlockSemaphoreInfo(png_semaphore);
7720 return(MagickFalse);
7722 quantum_info=AcquireQuantumInfo(image_info,image);
7723 if (quantum_info == (QuantumInfo *) NULL)
7724 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7725 quantum_info->format=UndefinedQuantumFormat;
7726 quantum_info->depth=image_depth;
7727 num_passes=png_set_interlace_handling(ping);
7728 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7729 !mng_info->write_png32) &&
7730 (mng_info->optimize || mng_info->IsPalette ||
7731 (image_info->type == BilevelType)) &&
7732 !image_matte && ImageIsMonochrome(image))
7734 register const PixelPacket
7737 quantum_info->depth=8;
7738 for (pass=0; pass < num_passes; pass++)
7741 Convert PseudoClass image to a PNG monochrome image.
7743 for (y=0; y < (ssize_t) image->rows; y++)
7745 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7746 if (p == (const PixelPacket *) NULL)
7748 if (mng_info->IsPalette)
7750 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7751 quantum_info,GrayQuantum,png_pixels,&image->exception);
7752 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7753 mng_info->write_png_depth &&
7754 mng_info->write_png_depth != old_bit_depth)
7756 /* Undo pixel scaling */
7757 for (i=0; i < (ssize_t) image->columns; i++)
7758 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7759 >> (8-old_bit_depth));
7764 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7765 quantum_info,RedQuantum,png_pixels,&image->exception);
7767 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7768 for (i=0; i < (ssize_t) image->columns; i++)
7769 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7771 if (logging && y == 0)
7772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7773 " Writing row of pixels (1)");
7774 png_write_row(ping,png_pixels);
7776 if (image->previous == (Image *) NULL)
7778 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7779 if (status == MagickFalse)
7785 for (pass=0; pass < num_passes; pass++)
7787 register const PixelPacket
7790 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7791 !mng_info->write_png32) &&
7793 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7794 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7796 for (y=0; y < (ssize_t) image->rows; y++)
7798 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7799 if (p == (const PixelPacket *) NULL)
7801 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7803 if (mng_info->IsPalette)
7804 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7805 quantum_info,GrayQuantum,png_pixels,&image->exception);
7807 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7808 quantum_info,RedQuantum,png_pixels,&image->exception);
7809 if (logging && y == 0)
7810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7811 " Writing GRAY PNG pixels (2)");
7813 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7815 if (logging && y == 0)
7816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7817 " Writing GRAY_ALPHA PNG pixels (2)");
7818 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7819 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7821 if (logging && y == 0)
7822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7823 " Writing row of pixels (2)");
7824 png_write_row(ping,png_pixels);
7826 if (image->previous == (Image *) NULL)
7828 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7829 if (status == MagickFalse)
7834 for (pass=0; pass < num_passes; pass++)
7836 if ((image_depth > 8) || (mng_info->write_png24 ||
7837 mng_info->write_png32 ||
7838 (!mng_info->write_png8 && !mng_info->IsPalette)))
7839 for (y=0; y < (ssize_t) image->rows; y++)
7841 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7842 if (p == (const PixelPacket *) NULL)
7844 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7846 if (image->storage_class == DirectClass)
7847 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7848 quantum_info,RedQuantum,png_pixels,&image->exception);
7850 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7851 quantum_info,GrayQuantum,png_pixels,&image->exception);
7853 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7855 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7856 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7857 if (logging && y == 0)
7858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7859 " Writing GRAY_ALPHA PNG pixels (3)");
7861 else if (image_matte != MagickFalse)
7862 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7863 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7865 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7866 quantum_info,RGBQuantum,png_pixels,&image->exception);
7867 if (logging && y == 0)
7868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7869 " Writing row of pixels (3)");
7870 png_write_row(ping,png_pixels);
7873 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7874 mng_info->write_png32 ||
7875 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7877 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7878 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7882 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7883 quantum_info->depth=8;
7886 for (y=0; y < (ssize_t) image->rows; y++)
7888 if (logging && y == 0)
7889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7890 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7891 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7892 if (p == (const PixelPacket *) NULL)
7894 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7895 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7896 quantum_info,GrayQuantum,png_pixels,&image->exception);
7897 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7899 if (logging && y == 0)
7900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7901 " Writing GRAY_ALPHA PNG pixels (4)");
7902 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7903 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7906 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7907 quantum_info,IndexQuantum,png_pixels,&image->exception);
7908 if (logging && y == 0)
7909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7910 " Writing row of pixels (4)");
7911 png_write_row(ping,png_pixels);
7914 if (image->previous == (Image *) NULL)
7916 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7917 if (status == MagickFalse)
7922 if (quantum_info != (QuantumInfo *) NULL)
7923 quantum_info=DestroyQuantumInfo(quantum_info);
7925 if (logging != MagickFalse)
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928 " Wrote PNG image data");
7929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7930 " Width: %.20g",(double) ping_width);
7931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932 " Height: %.20g",(double) ping_height);
7933 if (mng_info->write_png_depth)
7935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7939 " PNG bit-depth written: %d",ping_bit_depth);
7940 if (mng_info->write_png_colortype)
7942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7943 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946 " PNG color-type written: %d",ping_color_type);
7947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7948 " PNG Interlace method: %d",ping_interlace_method);
7951 Generate text chunks.
7953 ResetImagePropertyIterator(image);
7954 property=GetNextImageProperty(image);
7955 while (property != (const char *) NULL)
7960 value=GetImageProperty(image,property);
7961 if (value != (const char *) NULL)
7963 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7964 text[0].key=(char *) property;
7965 text[0].text=(char *) value;
7966 text[0].text_length=strlen(value);
7967 text[0].compression=image_info->compression == NoCompression ||
7968 (image_info->compression == UndefinedCompression &&
7969 text[0].text_length < 128) ? -1 : 0;
7970 if (logging != MagickFalse)
7972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7973 " Setting up text chunk");
7974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7975 " keyword: %s",text[0].key);
7977 png_set_text(ping,ping_info,text,1);
7978 png_free(ping,text);
7980 property=GetNextImageProperty(image);
7983 /* write any PNG-chunk-e profiles */
7984 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7986 if (logging != MagickFalse)
7987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7988 " Writing PNG end info");
7989 png_write_end(ping,ping_info);
7990 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7992 if (mng_info->page.x || mng_info->page.y ||
7993 (ping_width != mng_info->page.width) ||
7994 (ping_height != mng_info->page.height))
8000 Write FRAM 4 with clipping boundaries followed by FRAM 1.
8002 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
8003 PNGType(chunk,mng_FRAM);
8004 LogPNGChunk((int) logging,mng_FRAM,27L);
8006 chunk[5]=0; /* frame name separator (no name) */
8007 chunk[6]=1; /* flag for changing delay, for next frame only */
8008 chunk[7]=0; /* flag for changing frame timeout */
8009 chunk[8]=1; /* flag for changing frame clipping for next frame */
8010 chunk[9]=0; /* flag for changing frame sync_id */
8011 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
8012 chunk[14]=0; /* clipping boundaries delta type */
8013 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
8015 (png_uint_32) (mng_info->page.x + ping_width));
8016 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
8018 (png_uint_32) (mng_info->page.y + ping_height));
8019 (void) WriteBlob(image,31,chunk);
8020 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
8021 mng_info->old_framing_mode=4;
8022 mng_info->framing_mode=1;
8025 mng_info->framing_mode=3;
8027 if (mng_info->write_mng && !mng_info->need_fram &&
8028 ((int) image->dispose == 3))
8029 (void) ThrowMagickException(&image->exception,GetMagickModule(),
8030 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
8031 "`%s'",image->filename);
8032 image_depth=save_image_depth;
8034 /* Save depth actually written */
8036 s[0]=(char) ping_bit_depth;
8039 (void) SetImageProperty(image,"png:bit-depth-written",s);
8045 png_destroy_write_struct(&ping,&ping_info);
8047 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8049 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8050 UnlockSemaphoreInfo(png_semaphore);
8053 if (logging != MagickFalse)
8054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " exit WriteOnePNGImage()");
8057 /* End write one PNG image */
8061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8065 % W r i t e P N G I m a g e %
8069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8071 % WritePNGImage() writes a Portable Network Graphics (PNG) or
8072 % Multiple-image Network Graphics (MNG) image file.
8074 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8076 % The format of the WritePNGImage method is:
8078 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8080 % A description of each parameter follows:
8082 % o image_info: the image info.
8084 % o image: The image.
8086 % Returns MagickTrue on success, MagickFalse on failure.
8088 % Communicating with the PNG encoder:
8090 % While the datastream written is always in PNG format and normally would
8091 % be given the "png" file extension, this method also writes the following
8092 % pseudo-formats which are subsets of PNG:
8094 % o PNG8: An 8-bit indexed PNG datastream is written. If transparency
8095 % is present, the tRNS chunk must only have values 0 and 255
8096 % (i.e., transparency is binary: fully opaque or fully
8097 % transparent). The pixels contain 8-bit indices even if
8098 % they could be represented with 1, 2, or 4 bits. Note: grayscale
8099 % images will be written as indexed PNG files even though the
8100 % PNG grayscale type might be slightly more efficient.
8102 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
8103 % chunk can be present to convey binary transparency by naming
8104 % one of the colors as transparent.
8106 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
8107 % transparency is permitted, i.e., the alpha sample for
8108 % each pixel can have any value from 0 to 255. The alpha
8109 % channel is present even if the image is fully opaque.
8111 % o -define: For more precise control of the PNG output, you can use the
8112 % Image options "png:bit-depth" and "png:color-type". These
8113 % can be set from the commandline with "-define" and also
8114 % from the application programming interfaces.
8116 % png:color-type can be 0, 2, 3, 4, or 6.
8118 % When png:color-type is 0 (Grayscale), png:bit-depth can
8119 % be 1, 2, 4, 8, or 16.
8121 % When png:color-type is 2 (RGB), png:bit-depth can
8124 % When png:color-type is 3 (Indexed), png:bit-depth can
8125 % be 1, 2, 4, or 8. This refers to the number of bits
8126 % used to store the index. The color samples always have
8127 % bit-depth 8 in indexed PNG files.
8129 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8130 % png:bit-depth can be 8 or 16.
8132 % If the image cannot be written without loss in the requested PNG8, PNG24,
8133 % or PNG32 format or with the requested bit-depth and color-type without loss,
8134 % a PNG file will not be written, and the encoder will return MagickFalse.
8135 % Since image encoders should not be responsible for the "heavy lifting",
8136 % the user should make sure that ImageMagick has already reduced the
8137 % image depth and number of colors and limit transparency to binary
8138 % transparency prior to attempting to write the image in a format that
8139 % is subject to depth, color, or transparency limitations.
8141 % TODO: Enforce the previous paragraph.
8143 % TODO: Allow all other PNG subformats to be requested via new
8144 % "-define png:bit-depth -define png:color-type" options.
8146 % Note that another definition, "png:bit-depth-written" exists, but it
8147 % is not intended for external use. It is only used internally by the
8148 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8150 % It is possible to request that the PNG encoder write previously-formatted
8151 % ancillary chunks in the output PNG file, using the "-profile" commandline
8152 % option as shown below or by setting the profile via a programming
8155 % -profile PNG-chunk-x:<file>
8157 % where x is a location flag and <file> is a file containing the chunk
8158 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8160 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8161 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8162 % of the same type, then add a short unique string after the "x" to prevent
8163 % subsequent profiles from overwriting the preceding ones:
8165 % -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8169 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8190 assert(image_info != (const ImageInfo *) NULL);
8191 assert(image_info->signature == MagickSignature);
8192 assert(image != (Image *) NULL);
8193 assert(image->signature == MagickSignature);
8194 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8195 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8196 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8197 if (status == MagickFalse)
8198 return(MagickFalse);
8200 Allocate a MngInfo structure.
8202 have_mng_structure=MagickFalse;
8203 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8204 if (mng_info == (MngInfo *) NULL)
8205 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8207 Initialize members of the MngInfo structure.
8209 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8210 mng_info->image=image;
8211 have_mng_structure=MagickTrue;
8213 /* See if user has requested a specific PNG subformat */
8215 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8216 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8217 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8219 if (mng_info->write_png8)
8221 mng_info->write_png_colortype = /* 3 */ 4;
8222 mng_info->write_png_depth = 8;
8224 #if 0 /* this does not work */
8225 if (image->matte == MagickTrue)
8226 (void) SetImageType(image,PaletteMatteType);
8228 (void) SetImageType(image,PaletteType);
8229 (void) SyncImage(image);
8233 if (mng_info->write_png24)
8235 mng_info->write_png_colortype = /* 2 */ 3;
8236 mng_info->write_png_depth = 8;
8238 if (image->matte == MagickTrue)
8239 (void) SetImageType(image,TrueColorMatteType);
8241 (void) SetImageType(image,TrueColorType);
8242 (void) SyncImage(image);
8245 if (mng_info->write_png32)
8247 mng_info->write_png_colortype = /* 6 */ 7;
8248 mng_info->write_png_depth = 8;
8250 if (image->matte == MagickTrue)
8251 (void) SetImageType(image,TrueColorMatteType);
8253 (void) SetImageType(image,TrueColorType);
8254 (void) SyncImage(image);
8257 value=GetImageOption(image_info,"png:bit-depth");
8258 if (value != (char *) NULL)
8260 if (LocaleCompare(value,"1") == 0)
8261 mng_info->write_png_depth = 1;
8262 else if (LocaleCompare(value,"2") == 0)
8263 mng_info->write_png_depth = 2;
8264 else if (LocaleCompare(value,"4") == 0)
8265 mng_info->write_png_depth = 4;
8266 else if (LocaleCompare(value,"8") == 0)
8267 mng_info->write_png_depth = 8;
8268 else if (LocaleCompare(value,"16") == 0)
8269 mng_info->write_png_depth = 16;
8270 if (logging != MagickFalse)
8271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8272 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8274 value=GetImageOption(image_info,"png:color-type");
8275 if (value != (char *) NULL)
8277 /* We must store colortype+1 because 0 is a valid colortype */
8278 if (LocaleCompare(value,"0") == 0)
8279 mng_info->write_png_colortype = 1;
8280 else if (LocaleCompare(value,"2") == 0)
8281 mng_info->write_png_colortype = 3;
8282 else if (LocaleCompare(value,"3") == 0)
8283 mng_info->write_png_colortype = 4;
8284 else if (LocaleCompare(value,"4") == 0)
8285 mng_info->write_png_colortype = 5;
8286 else if (LocaleCompare(value,"6") == 0)
8287 mng_info->write_png_colortype = 7;
8288 if (logging != MagickFalse)
8289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8290 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8293 status=WriteOnePNGImage(mng_info,image_info,image);
8295 (void) CloseBlob(image);
8297 MngInfoFreeStruct(mng_info,&have_mng_structure);
8298 if (logging != MagickFalse)
8299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8303 #if defined(JNG_SUPPORTED)
8305 /* Write one JNG image */
8306 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8307 const ImageInfo *image_info,Image *image)
8327 jng_alpha_compression_method,
8328 jng_alpha_sample_depth,
8336 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " enter WriteOneJNGImage()");
8339 blob=(unsigned char *) NULL;
8340 jpeg_image=(Image *) NULL;
8341 jpeg_image_info=(ImageInfo *) NULL;
8344 transparent=image_info->type==GrayscaleMatteType ||
8345 image_info->type==TrueColorMatteType;
8347 jng_alpha_sample_depth=0;
8348 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8349 jng_alpha_compression_method=0;
8351 if (image->matte != MagickFalse)
8353 /* if any pixels are transparent */
8354 transparent=MagickTrue;
8355 if (image_info->compression==JPEGCompression)
8356 jng_alpha_compression_method=8;
8362 /* Create JPEG blob, image, and image_info */
8363 if (logging != MagickFalse)
8364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " Creating jpeg_image_info for opacity.");
8366 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8367 if (jpeg_image_info == (ImageInfo *) NULL)
8368 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8369 if (logging != MagickFalse)
8370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371 " Creating jpeg_image.");
8372 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8373 if (jpeg_image == (Image *) NULL)
8374 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8375 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8376 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8377 status=NegateImage(jpeg_image,MagickFalse);
8378 jpeg_image->matte=MagickFalse;
8379 if (jng_quality >= 1000)
8380 jpeg_image_info->quality=jng_quality/1000;
8382 jpeg_image_info->quality=jng_quality;
8383 jpeg_image_info->type=GrayscaleType;
8384 (void) SetImageType(jpeg_image,GrayscaleType);
8385 (void) AcquireUniqueFilename(jpeg_image->filename);
8386 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8387 "%s",jpeg_image->filename);
8390 /* To do: check bit depth of PNG alpha channel */
8392 /* Check if image is grayscale. */
8393 if (image_info->type != TrueColorMatteType && image_info->type !=
8394 TrueColorType && ImageIsGray(image))
8399 if (jng_alpha_compression_method==0)
8404 /* Encode opacity as a grayscale PNG blob */
8405 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8407 if (logging != MagickFalse)
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " Creating PNG blob.");
8412 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8413 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8414 jpeg_image_info->interlace=NoInterlace;
8416 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8419 /* Retrieve sample depth used */
8420 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8421 if (value != (char *) NULL)
8422 jng_alpha_sample_depth= (unsigned int) value[0];
8426 /* Encode opacity as a grayscale JPEG blob */
8428 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8431 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8432 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8433 jpeg_image_info->interlace=NoInterlace;
8434 if (logging != MagickFalse)
8435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8437 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8439 jng_alpha_sample_depth=8;
8440 if (logging != MagickFalse)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " Successfully read jpeg_image into a blob, length=%.20g.",
8446 /* Destroy JPEG image and image_info */
8447 jpeg_image=DestroyImage(jpeg_image);
8448 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8449 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8452 /* Write JHDR chunk */
8453 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8454 PNGType(chunk,mng_JHDR);
8455 LogPNGChunk((int) logging,mng_JHDR,16L);
8456 PNGLong(chunk+4,(png_uint_32) image->columns);
8457 PNGLong(chunk+8,(png_uint_32) image->rows);
8458 chunk[12]=jng_color_type;
8459 chunk[13]=8; /* sample depth */
8460 chunk[14]=8; /*jng_image_compression_method */
8461 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8462 chunk[16]=jng_alpha_sample_depth;
8463 chunk[17]=jng_alpha_compression_method;
8464 chunk[18]=0; /*jng_alpha_filter_method */
8465 chunk[19]=0; /*jng_alpha_interlace_method */
8466 (void) WriteBlob(image,20,chunk);
8467 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8468 if (logging != MagickFalse)
8470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8471 " JNG width:%15lu",(unsigned long) image->columns);
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " JNG height:%14lu",(unsigned long) image->rows);
8474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8475 " JNG color type:%10d",jng_color_type);
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " JNG sample depth:%8d",8);
8478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8479 " JNG compression:%9d",8);
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " JNG interlace:%11d",0);
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " JNG alpha compression:%3d",jng_alpha_compression_method);
8486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8487 " JNG alpha filter:%8d",0);
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " JNG alpha interlace:%5d",0);
8492 /* Write any JNG-chunk-b profiles */
8493 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8496 Write leading ancillary chunks
8502 Write JNG bKGD chunk
8513 if (jng_color_type == 8 || jng_color_type == 12)
8517 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
8518 PNGType(chunk,mng_bKGD);
8519 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
8520 red=ScaleQuantumToChar(image->background_color.red);
8521 green=ScaleQuantumToChar(image->background_color.green);
8522 blue=ScaleQuantumToChar(image->background_color.blue);
8529 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8530 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8533 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8536 Write JNG sRGB chunk
8538 (void) WriteBlobMSBULong(image,1L);
8539 PNGType(chunk,mng_sRGB);
8540 LogPNGChunk((int) logging,mng_sRGB,1L);
8541 if (image->rendering_intent != UndefinedIntent)
8542 chunk[4]=(unsigned char) (image->rendering_intent-1);
8544 chunk[4]=(unsigned char) (PerceptualIntent-1);
8545 (void) WriteBlob(image,5,chunk);
8546 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8550 if (image->gamma != 0.0)
8553 Write JNG gAMA chunk
8555 (void) WriteBlobMSBULong(image,4L);
8556 PNGType(chunk,mng_gAMA);
8557 LogPNGChunk((int) logging,mng_gAMA,4L);
8558 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
8559 (void) WriteBlob(image,8,chunk);
8560 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8562 if ((mng_info->equal_chrms == MagickFalse) &&
8563 (image->chromaticity.red_primary.x != 0.0))
8569 Write JNG cHRM chunk
8571 (void) WriteBlobMSBULong(image,32L);
8572 PNGType(chunk,mng_cHRM);
8573 LogPNGChunk((int) logging,mng_cHRM,32L);
8574 primary=image->chromaticity.white_point;
8575 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8576 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
8577 primary=image->chromaticity.red_primary;
8578 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8579 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
8580 primary=image->chromaticity.green_primary;
8581 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8582 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
8583 primary=image->chromaticity.blue_primary;
8584 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8585 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
8586 (void) WriteBlob(image,36,chunk);
8587 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8590 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8593 Write JNG pHYs chunk
8595 (void) WriteBlobMSBULong(image,9L);
8596 PNGType(chunk,mng_pHYs);
8597 LogPNGChunk((int) logging,mng_pHYs,9L);
8598 if (image->units == PixelsPerInchResolution)
8600 PNGLong(chunk+4,(png_uint_32)
8601 (image->x_resolution*100.0/2.54+0.5));
8602 PNGLong(chunk+8,(png_uint_32)
8603 (image->y_resolution*100.0/2.54+0.5));
8608 if (image->units == PixelsPerCentimeterResolution)
8610 PNGLong(chunk+4,(png_uint_32)
8611 (image->x_resolution*100.0+0.5));
8612 PNGLong(chunk+8,(png_uint_32)
8613 (image->y_resolution*100.0+0.5));
8618 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8619 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
8623 (void) WriteBlob(image,13,chunk);
8624 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8627 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8630 Write JNG oFFs chunk
8632 (void) WriteBlobMSBULong(image,9L);
8633 PNGType(chunk,mng_oFFs);
8634 LogPNGChunk((int) logging,mng_oFFs,9L);
8635 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8636 PNGsLong(chunk+8,(ssize_t) (image->page.y));
8638 (void) WriteBlob(image,13,chunk);
8639 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8641 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8643 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8644 PNGType(chunk,mng_vpAg);
8645 LogPNGChunk((int) logging,mng_vpAg,9L);
8646 PNGLong(chunk+4,(png_uint_32) image->page.width);
8647 PNGLong(chunk+8,(png_uint_32) image->page.height);
8648 chunk[12]=0; /* unit = pixels */
8649 (void) WriteBlob(image,13,chunk);
8650 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8656 if (jng_alpha_compression_method==0)
8664 /* Write IDAT chunk header */
8665 if (logging != MagickFalse)
8666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8667 " Write IDAT chunks from blob, length=%.20g.",(double)
8670 /* Copy IDAT chunks */
8673 for (i=8; i<(ssize_t) length; i+=len+12)
8675 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8677 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8679 /* Found an IDAT chunk. */
8680 (void) WriteBlobMSBULong(image,(size_t) len);
8681 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
8682 (void) WriteBlob(image,(size_t) len+4,p);
8683 (void) WriteBlobMSBULong(image,
8684 crc32(0,p,(uInt) len+4));
8688 if (logging != MagickFalse)
8689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8690 " Skipping %c%c%c%c chunk, length=%.20g.",
8691 *(p),*(p+1),*(p+2),*(p+3),(double) len);
8698 /* Write JDAA chunk header */
8699 if (logging != MagickFalse)
8700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8701 " Write JDAA chunk, length=%.20g.",(double) length);
8702 (void) WriteBlobMSBULong(image,(size_t) length);
8703 PNGType(chunk,mng_JDAA);
8704 LogPNGChunk((int) logging,mng_JDAA,length);
8705 /* Write JDAT chunk(s) data */
8706 (void) WriteBlob(image,4,chunk);
8707 (void) WriteBlob(image,length,blob);
8708 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8711 blob=(unsigned char *) RelinquishMagickMemory(blob);
8714 /* Encode image as a JPEG blob */
8715 if (logging != MagickFalse)
8716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8717 " Creating jpeg_image_info.");
8718 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8719 if (jpeg_image_info == (ImageInfo *) NULL)
8720 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8722 if (logging != MagickFalse)
8723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8724 " Creating jpeg_image.");
8726 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8727 if (jpeg_image == (Image *) NULL)
8728 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8729 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8731 (void) AcquireUniqueFilename(jpeg_image->filename);
8732 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8733 jpeg_image->filename);
8735 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8738 if (logging != MagickFalse)
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8741 (double) jpeg_image->rows);
8743 if (jng_color_type == 8 || jng_color_type == 12)
8744 jpeg_image_info->type=GrayscaleType;
8745 jpeg_image_info->quality=jng_quality % 1000;
8746 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8747 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8748 if (logging != MagickFalse)
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8751 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8752 if (logging != MagickFalse)
8754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8755 " Successfully read jpeg_image into a blob, length=%.20g.",
8758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8759 " Write JDAT chunk, length=%.20g.",(double) length);
8761 /* Write JDAT chunk(s) */
8762 (void) WriteBlobMSBULong(image,(size_t) length);
8763 PNGType(chunk,mng_JDAT);
8764 LogPNGChunk((int) logging,mng_JDAT,length);
8765 (void) WriteBlob(image,4,chunk);
8766 (void) WriteBlob(image,length,blob);
8767 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8769 jpeg_image=DestroyImage(jpeg_image);
8770 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8771 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8772 blob=(unsigned char *) RelinquishMagickMemory(blob);
8774 /* Write any JNG-chunk-e profiles */
8775 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8777 /* Write IEND chunk */
8778 (void) WriteBlobMSBULong(image,0L);
8779 PNGType(chunk,mng_IEND);
8780 LogPNGChunk((int) logging,mng_IEND,0);
8781 (void) WriteBlob(image,4,chunk);
8782 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8784 if (logging != MagickFalse)
8785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8786 " exit WriteOneJNGImage()");
8792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8796 % W r i t e J N G I m a g e %
8800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8802 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8804 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
8806 % The format of the WriteJNGImage method is:
8808 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8810 % A description of each parameter follows:
8812 % o image_info: the image info.
8814 % o image: The image.
8816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8818 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8835 assert(image_info != (const ImageInfo *) NULL);
8836 assert(image_info->signature == MagickSignature);
8837 assert(image != (Image *) NULL);
8838 assert(image->signature == MagickSignature);
8839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8840 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8841 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8842 if (status == MagickFalse)
8846 Allocate a MngInfo structure.
8848 have_mng_structure=MagickFalse;
8849 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8850 if (mng_info == (MngInfo *) NULL)
8851 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8853 Initialize members of the MngInfo structure.
8855 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8856 mng_info->image=image;
8857 have_mng_structure=MagickTrue;
8859 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8861 status=WriteOneJNGImage(mng_info,image_info,image);
8862 (void) CloseBlob(image);
8864 (void) CatchImageException(image);
8865 MngInfoFreeStruct(mng_info,&have_mng_structure);
8866 if (logging != MagickFalse)
8867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8874 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8895 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8896 defined(PNG_MNG_FEATURES_SUPPORTED)
8899 all_images_are_gray,
8911 volatile unsigned int
8922 #if (PNG_LIBPNG_VER < 10200)
8923 if (image_info->verbose)
8924 printf("Your PNG library (libpng-%s) is rather old.\n",
8925 PNG_LIBPNG_VER_STRING);
8931 assert(image_info != (const ImageInfo *) NULL);
8932 assert(image_info->signature == MagickSignature);
8933 assert(image != (Image *) NULL);
8934 assert(image->signature == MagickSignature);
8935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8936 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8937 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8938 if (status == MagickFalse)
8942 Allocate a MngInfo structure.
8944 have_mng_structure=MagickFalse;
8945 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8946 if (mng_info == (MngInfo *) NULL)
8947 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8949 Initialize members of the MngInfo structure.
8951 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8952 mng_info->image=image;
8953 have_mng_structure=MagickTrue;
8954 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8957 * See if user has requested a specific PNG subformat to be used
8958 * for all of the PNGs in the MNG being written, e.g.,
8960 * convert *.png png8:animation.mng
8962 * To do: check -define png:bit_depth and png:color_type as well,
8963 * or perhaps use mng:bit_depth and mng:color_type instead for
8967 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8968 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8969 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8971 write_jng=MagickFalse;
8972 if (image_info->compression == JPEGCompression)
8973 write_jng=MagickTrue;
8975 mng_info->adjoin=image_info->adjoin &&
8976 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8978 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8979 optimize=MagickFalse;
8981 optimize=(image_info->type == OptimizeType || image_info->type ==
8984 if (logging != MagickFalse)
8986 /* Log some info about the input */
8990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8991 " Checking input image(s)");
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8997 " Optimize: FALSE");
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Image_info depth: %.20g",(double) image_info->depth);
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " Type: %d",image_info->type);
9004 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " Scene: %.20g",(double) scene++);
9008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9009 " Image depth: %.20g",(double) p->depth);
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 if (p->storage_class == PseudoClass)
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " Storage class: PseudoClass");
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " Storage class: DirectClass");
9023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9024 " Number of colors: %.20g",(double) p->colors);
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Number of colors: unspecified");
9028 if (mng_info->adjoin == MagickFalse)
9034 Sometimes we get PseudoClass images whose RGB values don't match
9035 the colors in the colormap. This code syncs the RGB values.
9041 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9043 if (p->taint && p->storage_class == PseudoClass)
9044 (void) SyncImage(p);
9045 if (mng_info->adjoin == MagickFalse)
9050 #ifdef PNG_BUILD_PALETTE
9054 Sometimes we get DirectClass images that have 256 colors or fewer.
9055 This code will convert them to PseudoClass and build a colormap.
9060 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9062 if (p->storage_class != PseudoClass)
9064 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9065 if (p->colors <= 256)
9068 if (p->matte != MagickFalse)
9069 (void) SetImageType(p,PaletteMatteType);
9071 (void) SetImageType(p,PaletteType);
9074 if (mng_info->adjoin == MagickFalse)
9080 use_global_plte=MagickFalse;
9081 all_images_are_gray=MagickFalse;
9082 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9083 need_local_plte=MagickTrue;
9085 need_defi=MagickFalse;
9086 need_matte=MagickFalse;
9087 mng_info->framing_mode=1;
9088 mng_info->old_framing_mode=1;
9091 if (image_info->page != (char *) NULL)
9094 Determine image bounding box.
9096 SetGeometry(image,&mng_info->page);
9097 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9098 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9110 mng_info->page=image->page;
9111 need_geom=MagickTrue;
9112 if (mng_info->page.width || mng_info->page.height)
9113 need_geom=MagickFalse;
9115 Check all the scenes.
9117 initial_delay=image->delay;
9118 need_iterations=MagickFalse;
9119 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9120 mng_info->equal_physs=MagickTrue,
9121 mng_info->equal_gammas=MagickTrue;
9122 mng_info->equal_srgbs=MagickTrue;
9123 mng_info->equal_backgrounds=MagickTrue;
9125 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9126 defined(PNG_MNG_FEATURES_SUPPORTED)
9127 all_images_are_gray=MagickTrue;
9128 mng_info->equal_palettes=MagickFalse;
9129 need_local_plte=MagickFalse;
9131 for (next_image=image; next_image != (Image *) NULL; )
9135 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9136 mng_info->page.width=next_image->columns+next_image->page.x;
9137 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9138 mng_info->page.height=next_image->rows+next_image->page.y;
9140 if (next_image->page.x || next_image->page.y)
9141 need_defi=MagickTrue;
9142 if (next_image->matte)
9143 need_matte=MagickTrue;
9144 if ((int) next_image->dispose >= BackgroundDispose)
9145 if (next_image->matte || next_image->page.x || next_image->page.y ||
9146 ((next_image->columns < mng_info->page.width) &&
9147 (next_image->rows < mng_info->page.height)))
9148 mng_info->need_fram=MagickTrue;
9149 if (next_image->iterations)
9150 need_iterations=MagickTrue;
9151 final_delay=next_image->delay;
9152 if (final_delay != initial_delay || final_delay > 1UL*
9153 next_image->ticks_per_second)
9154 mng_info->need_fram=1;
9155 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9156 defined(PNG_MNG_FEATURES_SUPPORTED)
9158 check for global palette possibility.
9160 if (image->matte != MagickFalse)
9161 need_local_plte=MagickTrue;
9162 if (need_local_plte == 0)
9164 if (ImageIsGray(image) == MagickFalse)
9165 all_images_are_gray=MagickFalse;
9166 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9167 if (use_global_plte == 0)
9168 use_global_plte=mng_info->equal_palettes;
9169 need_local_plte=!mng_info->equal_palettes;
9172 if (GetNextImageInList(next_image) != (Image *) NULL)
9174 if (next_image->background_color.red !=
9175 next_image->next->background_color.red ||
9176 next_image->background_color.green !=
9177 next_image->next->background_color.green ||
9178 next_image->background_color.blue !=
9179 next_image->next->background_color.blue)
9180 mng_info->equal_backgrounds=MagickFalse;
9181 if (next_image->gamma != next_image->next->gamma)
9182 mng_info->equal_gammas=MagickFalse;
9183 if (next_image->rendering_intent !=
9184 next_image->next->rendering_intent)
9185 mng_info->equal_srgbs=MagickFalse;
9186 if ((next_image->units != next_image->next->units) ||
9187 (next_image->x_resolution != next_image->next->x_resolution) ||
9188 (next_image->y_resolution != next_image->next->y_resolution))
9189 mng_info->equal_physs=MagickFalse;
9190 if (mng_info->equal_chrms)
9192 if (next_image->chromaticity.red_primary.x !=
9193 next_image->next->chromaticity.red_primary.x ||
9194 next_image->chromaticity.red_primary.y !=
9195 next_image->next->chromaticity.red_primary.y ||
9196 next_image->chromaticity.green_primary.x !=
9197 next_image->next->chromaticity.green_primary.x ||
9198 next_image->chromaticity.green_primary.y !=
9199 next_image->next->chromaticity.green_primary.y ||
9200 next_image->chromaticity.blue_primary.x !=
9201 next_image->next->chromaticity.blue_primary.x ||
9202 next_image->chromaticity.blue_primary.y !=
9203 next_image->next->chromaticity.blue_primary.y ||
9204 next_image->chromaticity.white_point.x !=
9205 next_image->next->chromaticity.white_point.x ||
9206 next_image->chromaticity.white_point.y !=
9207 next_image->next->chromaticity.white_point.y)
9208 mng_info->equal_chrms=MagickFalse;
9212 next_image=GetNextImageInList(next_image);
9214 if (image_count < 2)
9216 mng_info->equal_backgrounds=MagickFalse;
9217 mng_info->equal_chrms=MagickFalse;
9218 mng_info->equal_gammas=MagickFalse;
9219 mng_info->equal_srgbs=MagickFalse;
9220 mng_info->equal_physs=MagickFalse;
9221 use_global_plte=MagickFalse;
9222 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9223 need_local_plte=MagickTrue;
9225 need_iterations=MagickFalse;
9227 if (mng_info->need_fram == MagickFalse)
9230 Only certain framing rates 100/n are exactly representable without
9231 the FRAM chunk but we'll allow some slop in VLC files
9233 if (final_delay == 0)
9235 if (need_iterations != MagickFalse)
9238 It's probably a GIF with loop; don't run it *too* fast.
9241 (void) ThrowMagickException(&image->exception,
9242 GetMagickModule(),CoderError,
9243 "input has zero delay between all frames; assuming 10 cs",
9247 mng_info->ticks_per_second=0;
9249 if (final_delay != 0)
9250 mng_info->ticks_per_second=(png_uint_32) (image->ticks_per_second/final_delay);
9251 if (final_delay > 50)
9252 mng_info->ticks_per_second=2;
9253 if (final_delay > 75)
9254 mng_info->ticks_per_second=1;
9255 if (final_delay > 125)
9256 mng_info->need_fram=MagickTrue;
9257 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9258 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9259 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9260 1UL*image->ticks_per_second))
9261 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9263 if (mng_info->need_fram != MagickFalse)
9264 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9266 If pseudocolor, we should also check to see if all the
9267 palettes are identical and write a global PLTE if they are.
9271 Write the MNG version 1.0 signature and MHDR chunk.
9273 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9274 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9275 PNGType(chunk,mng_MHDR);
9276 LogPNGChunk((int) logging,mng_MHDR,28L);
9277 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
9278 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
9279 PNGLong(chunk+12,mng_info->ticks_per_second);
9280 PNGLong(chunk+16,0L); /* layer count=unknown */
9281 PNGLong(chunk+20,0L); /* frame count=unknown */
9282 PNGLong(chunk+24,0L); /* play time=unknown */
9287 if (need_defi || mng_info->need_fram || use_global_plte)
9288 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9290 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9294 if (need_defi || mng_info->need_fram || use_global_plte)
9295 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9297 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9304 if (need_defi || mng_info->need_fram || use_global_plte)
9305 PNGLong(chunk+28,11L); /* simplicity=LC */
9307 PNGLong(chunk+28,9L); /* simplicity=VLC */
9311 if (need_defi || mng_info->need_fram || use_global_plte)
9312 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9314 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9317 (void) WriteBlob(image,32,chunk);
9318 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9319 option=GetImageOption(image_info,"mng:need-cacheoff");
9320 if (option != (const char *) NULL)
9326 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9328 PNGType(chunk,mng_nEED);
9329 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9330 (void) WriteBlobMSBULong(image,(size_t) length);
9331 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
9333 (void) WriteBlob(image,length,chunk);
9334 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9336 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9337 (GetNextImageInList(image) != (Image *) NULL) &&
9338 (image->iterations != 1))
9341 Write MNG TERM chunk
9343 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9344 PNGType(chunk,mng_TERM);
9345 LogPNGChunk((int) logging,mng_TERM,10L);
9346 chunk[4]=3; /* repeat animation */
9347 chunk[5]=0; /* show last frame when done */
9348 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9349 final_delay/MagickMax(image->ticks_per_second,1)));
9350 if (image->iterations == 0)
9351 PNGLong(chunk+10,PNG_UINT_31_MAX);
9353 PNGLong(chunk+10,(png_uint_32) image->iterations);
9354 if (logging != MagickFalse)
9356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9357 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9358 final_delay/MagickMax(image->ticks_per_second,1)));
9359 if (image->iterations == 0)
9360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9361 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
9363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9364 " Image iterations: %.20g",(double) image->iterations);
9366 (void) WriteBlob(image,14,chunk);
9367 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9370 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9372 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9373 mng_info->equal_srgbs)
9376 Write MNG sRGB chunk
9378 (void) WriteBlobMSBULong(image,1L);
9379 PNGType(chunk,mng_sRGB);
9380 LogPNGChunk((int) logging,mng_sRGB,1L);
9381 if (image->rendering_intent != UndefinedIntent)
9382 chunk[4]=(unsigned char) (image->rendering_intent-1);
9384 chunk[4]=(unsigned char) (PerceptualIntent-1);
9385 (void) WriteBlob(image,5,chunk);
9386 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9387 mng_info->have_write_global_srgb=MagickTrue;
9391 if (image->gamma && mng_info->equal_gammas)
9394 Write MNG gAMA chunk
9396 (void) WriteBlobMSBULong(image,4L);
9397 PNGType(chunk,mng_gAMA);
9398 LogPNGChunk((int) logging,mng_gAMA,4L);
9399 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
9400 (void) WriteBlob(image,8,chunk);
9401 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9402 mng_info->have_write_global_gama=MagickTrue;
9404 if (mng_info->equal_chrms)
9410 Write MNG cHRM chunk
9412 (void) WriteBlobMSBULong(image,32L);
9413 PNGType(chunk,mng_cHRM);
9414 LogPNGChunk((int) logging,mng_cHRM,32L);
9415 primary=image->chromaticity.white_point;
9416 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9417 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
9418 primary=image->chromaticity.red_primary;
9419 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9420 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
9421 primary=image->chromaticity.green_primary;
9422 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9423 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
9424 primary=image->chromaticity.blue_primary;
9425 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9426 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
9427 (void) WriteBlob(image,36,chunk);
9428 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9429 mng_info->have_write_global_chrm=MagickTrue;
9432 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9435 Write MNG pHYs chunk
9437 (void) WriteBlobMSBULong(image,9L);
9438 PNGType(chunk,mng_pHYs);
9439 LogPNGChunk((int) logging,mng_pHYs,9L);
9440 if (image->units == PixelsPerInchResolution)
9442 PNGLong(chunk+4,(png_uint_32)
9443 (image->x_resolution*100.0/2.54+0.5));
9444 PNGLong(chunk+8,(png_uint_32)
9445 (image->y_resolution*100.0/2.54+0.5));
9450 if (image->units == PixelsPerCentimeterResolution)
9452 PNGLong(chunk+4,(png_uint_32)
9453 (image->x_resolution*100.0+0.5));
9454 PNGLong(chunk+8,(png_uint_32)
9455 (image->y_resolution*100.0+0.5));
9460 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9461 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
9465 (void) WriteBlob(image,13,chunk);
9466 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9469 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9470 or does not cover the entire frame.
9472 if (write_mng && (image->matte || image->page.x > 0 ||
9473 image->page.y > 0 || (image->page.width &&
9474 (image->page.width+image->page.x < mng_info->page.width))
9475 || (image->page.height && (image->page.height+image->page.y
9476 < mng_info->page.height))))
9478 (void) WriteBlobMSBULong(image,6L);
9479 PNGType(chunk,mng_BACK);
9480 LogPNGChunk((int) logging,mng_BACK,6L);
9481 red=ScaleQuantumToShort(image->background_color.red);
9482 green=ScaleQuantumToShort(image->background_color.green);
9483 blue=ScaleQuantumToShort(image->background_color.blue);
9484 PNGShort(chunk+4,red);
9485 PNGShort(chunk+6,green);
9486 PNGShort(chunk+8,blue);
9487 (void) WriteBlob(image,10,chunk);
9488 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9489 if (mng_info->equal_backgrounds)
9491 (void) WriteBlobMSBULong(image,6L);
9492 PNGType(chunk,mng_bKGD);
9493 LogPNGChunk((int) logging,mng_bKGD,6L);
9494 (void) WriteBlob(image,10,chunk);
9495 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9499 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9500 if ((need_local_plte == MagickFalse) &&
9501 (image->storage_class == PseudoClass) &&
9502 (all_images_are_gray == MagickFalse))
9508 Write MNG PLTE chunk
9510 data_length=3*image->colors;
9511 (void) WriteBlobMSBULong(image,data_length);
9512 PNGType(chunk,mng_PLTE);
9513 LogPNGChunk((int) logging,mng_PLTE,data_length);
9514 for (i=0; i < (ssize_t) image->colors; i++)
9516 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9517 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9518 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9520 (void) WriteBlob(image,data_length+4,chunk);
9521 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9522 mng_info->have_write_global_plte=MagickTrue;
9528 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9529 defined(PNG_MNG_FEATURES_SUPPORTED)
9530 mng_info->equal_palettes=MagickFalse;
9534 if (mng_info->adjoin)
9536 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9537 defined(PNG_MNG_FEATURES_SUPPORTED)
9539 If we aren't using a global palette for the entire MNG, check to
9540 see if we can use one for two or more consecutive images.
9542 if (need_local_plte && use_global_plte && !all_images_are_gray)
9544 if (mng_info->IsPalette)
9547 When equal_palettes is true, this image has the same palette
9548 as the previous PseudoClass image
9550 mng_info->have_write_global_plte=mng_info->equal_palettes;
9551 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9552 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9555 Write MNG PLTE chunk
9560 data_length=3*image->colors;
9561 (void) WriteBlobMSBULong(image,data_length);
9562 PNGType(chunk,mng_PLTE);
9563 LogPNGChunk((int) logging,mng_PLTE,data_length);
9564 for (i=0; i < (ssize_t) image->colors; i++)
9566 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9567 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9568 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9570 (void) WriteBlob(image,data_length+4,chunk);
9571 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9572 (uInt) (data_length+4)));
9573 mng_info->have_write_global_plte=MagickTrue;
9577 mng_info->have_write_global_plte=MagickFalse;
9588 previous_x=mng_info->page.x;
9589 previous_y=mng_info->page.y;
9596 mng_info->page=image->page;
9597 if ((mng_info->page.x != previous_x) ||
9598 (mng_info->page.y != previous_y))
9600 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9601 PNGType(chunk,mng_DEFI);
9602 LogPNGChunk((int) logging,mng_DEFI,12L);
9603 chunk[4]=0; /* object 0 MSB */
9604 chunk[5]=0; /* object 0 LSB */
9605 chunk[6]=0; /* visible */
9606 chunk[7]=0; /* abstract */
9607 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9608 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9609 (void) WriteBlob(image,16,chunk);
9610 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9615 mng_info->write_mng=write_mng;
9617 if ((int) image->dispose >= 3)
9618 mng_info->framing_mode=3;
9620 if (mng_info->need_fram && mng_info->adjoin &&
9621 ((image->delay != mng_info->delay) ||
9622 (mng_info->framing_mode != mng_info->old_framing_mode)))
9624 if (image->delay == mng_info->delay)
9627 Write a MNG FRAM chunk with the new framing mode.
9629 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9630 PNGType(chunk,mng_FRAM);
9631 LogPNGChunk((int) logging,mng_FRAM,1L);
9632 chunk[4]=(unsigned char) mng_info->framing_mode;
9633 (void) WriteBlob(image,5,chunk);
9634 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9639 Write a MNG FRAM chunk with the delay.
9641 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9642 PNGType(chunk,mng_FRAM);
9643 LogPNGChunk((int) logging,mng_FRAM,10L);
9644 chunk[4]=(unsigned char) mng_info->framing_mode;
9645 chunk[5]=0; /* frame name separator (no name) */
9646 chunk[6]=2; /* flag for changing default delay */
9647 chunk[7]=0; /* flag for changing frame timeout */
9648 chunk[8]=0; /* flag for changing frame clipping */
9649 chunk[9]=0; /* flag for changing frame sync_id */
9650 PNGLong(chunk+10,(png_uint_32)
9651 ((mng_info->ticks_per_second*
9652 image->delay)/MagickMax(image->ticks_per_second,1)));
9653 (void) WriteBlob(image,14,chunk);
9654 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9655 mng_info->delay=(png_uint_32) image->delay;
9657 mng_info->old_framing_mode=mng_info->framing_mode;
9660 #if defined(JNG_SUPPORTED)
9661 if (image_info->compression == JPEGCompression)
9666 if (logging != MagickFalse)
9667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668 " Writing JNG object.");
9669 /* To do: specify the desired alpha compression method. */
9670 write_info=CloneImageInfo(image_info);
9671 write_info->compression=UndefinedCompression;
9672 status=WriteOneJNGImage(mng_info,write_info,image);
9673 write_info=DestroyImageInfo(write_info);
9678 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " Writing PNG object.");
9681 status=WriteOnePNGImage(mng_info,image_info,image);
9684 if (status == MagickFalse)
9686 MngInfoFreeStruct(mng_info,&have_mng_structure);
9687 (void) CloseBlob(image);
9688 return(MagickFalse);
9690 (void) CatchImageException(image);
9691 if (GetNextImageInList(image) == (Image *) NULL)
9693 image=SyncNextImageInList(image);
9694 status=SetImageProgress(image,SaveImagesTag,scene++,
9695 GetImageListLength(image));
9696 if (status == MagickFalse)
9698 } while (mng_info->adjoin);
9701 while (GetPreviousImageInList(image) != (Image *) NULL)
9702 image=GetPreviousImageInList(image);
9704 Write the MEND chunk.
9706 (void) WriteBlobMSBULong(image,0x00000000L);
9707 PNGType(chunk,mng_MEND);
9708 LogPNGChunk((int) logging,mng_MEND,0L);
9709 (void) WriteBlob(image,4,chunk);
9710 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9713 Relinquish resources.
9715 (void) CloseBlob(image);
9716 MngInfoFreeStruct(mng_info,&have_mng_structure);
9717 if (logging != MagickFalse)
9718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9721 #else /* PNG_LIBPNG_VER > 10011 */
9722 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9725 printf("Your PNG library is too old: You have libpng-%s\n",
9726 PNG_LIBPNG_VER_STRING);
9727 ThrowBinaryException(CoderError,"PNG library is too old",
9728 image_info->filename);
9730 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9732 return(WritePNGImage(image_info,image));
9734 #endif /* PNG_LIBPNG_VER > 10011 */