2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2011 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/artifact.h"
46 #include "magick/attribute.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/cache.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/colormap.h"
53 #include "magick/colorspace.h"
54 #include "magick/constitute.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/geometry.h"
59 #include "magick/histogram.h"
60 #include "magick/image.h"
61 #include "magick/image-private.h"
62 #include "magick/layer.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/magick.h"
66 #include "magick/memory_.h"
67 #include "magick/module.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/option.h"
71 #include "magick/quantum-private.h"
72 #include "magick/profile.h"
73 #include "magick/property.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 #if defined(MAGICKCORE_JPEG_DELEGATE)
116 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
118 #if !defined(RGBColorMatchExact)
119 #define IsPNGColorEqual(color,target) \
120 (((color).red == (target).red) && \
121 ((color).green == (target).green) && \
122 ((color).blue == (target).blue))
126 Establish thread safety.
127 setjmp/longjmp is claimed to be safe on these platforms:
128 setjmp/longjmp is alleged to be unsafe on these platforms:
130 #ifndef SETJMP_IS_THREAD_SAFE
131 #define PNG_SETJMP_NOT_THREAD_SAFE
134 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
136 *ping_semaphore = (SemaphoreInfo *) NULL;
140 This temporary until I set up malloc'ed object attributes array.
141 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
144 #define MNG_MAX_OBJECTS 256
147 If this not defined, spec is interpreted strictly. If it is
148 defined, an attempt will be made to recover from some errors,
150 o global PLTE too short
155 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
156 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
157 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
158 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
159 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
160 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
161 will be enabled by default in libpng-1.2.0.
163 #ifdef PNG_MNG_FEATURES_SUPPORTED
164 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
165 # define PNG_READ_EMPTY_PLTE_SUPPORTED
167 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
168 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
173 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
174 This macro is only defined in libpng-1.0.3 and later.
175 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
177 #ifndef PNG_UINT_31_MAX
178 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
182 Constant strings for known chunk types. If you need to add a chunk,
183 add a string holding the name here. To make the code more
184 portable, we use ASCII numbers like this, not characters.
187 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
188 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
189 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
190 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
191 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
192 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
193 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
194 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
195 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
196 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
197 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
198 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
199 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
200 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
201 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
202 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
203 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
204 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
205 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
206 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
207 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
208 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
209 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
210 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
211 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
212 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
213 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
214 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
215 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
216 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
217 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
218 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
219 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
220 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
222 #if defined(JNG_SUPPORTED)
223 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
224 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
225 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
226 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
227 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
228 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
232 Other known chunks that are not yet supported by ImageMagick:
233 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
234 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
235 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
236 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
237 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
238 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
239 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
240 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
243 typedef struct _MngBox
252 typedef struct _MngPair
259 #ifdef MNG_OBJECT_BUFFERS
260 typedef struct _MngBuffer
292 typedef struct _MngInfo
295 #ifdef MNG_OBJECT_BUFFERS
297 *ob[MNG_MAX_OBJECTS];
308 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
309 bytes_in_read_buffer,
315 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
316 defined(PNG_MNG_FEATURES_SUPPORTED)
328 have_saved_bkgd_index,
329 have_write_global_chrm,
330 have_write_global_gama,
331 have_write_global_plte,
332 have_write_global_srgb,
346 x_off[MNG_MAX_OBJECTS],
347 y_off[MNG_MAX_OBJECTS];
353 object_clip[MNG_MAX_OBJECTS];
356 /* These flags could be combined into one byte */
357 exists[MNG_MAX_OBJECTS],
358 frozen[MNG_MAX_OBJECTS],
360 invisible[MNG_MAX_OBJECTS],
361 viewable[MNG_MAX_OBJECTS];
373 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
391 global_x_pixels_per_unit,
392 global_y_pixels_per_unit,
402 global_phys_unit_type,
421 #ifdef MNG_BASI_SUPPORTED
429 basi_compression_method,
431 basi_interlace_method,
454 /* Added at version 6.6.6-7 */
461 /* ping_exclude_iTXt, */
468 ping_exclude_zCCP, /* hex-encoded iCCP */
475 Forward declarations.
477 static MagickBooleanType
478 WritePNGImage(const ImageInfo *,Image *);
480 static MagickBooleanType
481 WriteMNGImage(const ImageInfo *,Image *);
483 #if defined(JNG_SUPPORTED)
484 static MagickBooleanType
485 WriteJNGImage(const ImageInfo *,Image *);
488 #if PNG_LIBPNG_VER > 10011
491 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
492 static MagickBooleanType
493 LosslessReduceDepthOK(Image *image)
496 ok_to_reduce=MagickFalse;
498 /* Reduce bit depth if it can be reduced losslessly from 16 to 8.
499 * Note that the method GetImageDepth doesn't check background
500 * and doesn't handle PseudoClass specially. Also it uses
501 * multiplication and division by 257 instead of shifting, so
505 if (image->depth == 16)
512 (((((size_t) image->background_color.red >> 8) & 0xff)
513 == ((size_t) image->background_color.red & 0xff)) &&
514 ((((size_t) image->background_color.green >> 8) & 0xff)
515 == ((size_t) image->background_color.green & 0xff)) &&
516 ((((size_t) image->background_color.blue >> 8) & 0xff)
517 == ((size_t) image->background_color.blue & 0xff))) ? MagickTrue :
520 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
524 for (indx=0; indx < (ssize_t) image->colors; indx++)
526 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
528 == ((size_t) image->colormap[indx].red & 0xff)) &&
529 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
530 == ((size_t) image->colormap[indx].green & 0xff)) &&
531 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
532 == ((size_t) image->colormap[indx].blue & 0xff)) &&
533 (image->matte == MagickFalse ||
534 (((size_t) image->colormap[indx].opacity >> 8) & 0xff)
535 == ((size_t) image->colormap[indx].opacity & 0xff))) ?
536 MagickTrue : MagickFalse;
537 if (ok_to_reduce == MagickFalse)
542 if ((ok_to_reduce != MagickFalse) &&
543 (image->storage_class != PseudoClass))
551 for (y=0; y < (ssize_t) image->rows; y++)
553 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
555 if (p == (const PixelPacket *) NULL)
557 ok_to_reduce = MagickFalse;
561 for (x=(ssize_t) image->columns-1; x >= 0; x--)
564 (((size_t) p->red >> 8) & 0xff) ==
565 ((size_t) p->red & 0xff)) &&
566 ((((size_t) p->green >> 8) & 0xff) ==
567 ((size_t) p->green & 0xff)) &&
568 ((((size_t) p->blue >> 8) & 0xff) ==
569 ((size_t) p->blue & 0xff)) &&
570 (((image->matte == MagickFalse ||
571 (((size_t) p->opacity >> 8) & 0xff) ==
572 ((size_t) p->opacity & 0xff))))) ? MagickTrue : MagickFalse;
574 if (ok_to_reduce == MagickFalse)
584 if (ok_to_reduce != MagickFalse)
586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
587 " OK to reduce PNG bit depth to 8 without loss of info");
591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
592 " Not OK to reduce PNG bit depth to 8 without loss of info");
598 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
601 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
605 case PerceptualIntent:
611 case SaturationIntent:
622 static RenderingIntent
623 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
628 return PerceptualIntent;
631 return RelativeIntent;
634 return SaturationIntent;
637 return AbsoluteIntent;
640 return UndefinedIntent;
644 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
652 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
666 % I m a g e I s G r a y %
670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 % Like IsGrayImage except does not change DirectClass to PseudoClass %
674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 static MagickBooleanType ImageIsGray(Image *image)
678 register const PixelPacket
686 assert(image != (Image *) NULL);
687 assert(image->signature == MagickSignature);
688 if (image->debug != MagickFalse)
689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
691 if (image->storage_class == PseudoClass)
693 for (i=0; i < (ssize_t) image->colors; i++)
694 if (IsGray(image->colormap+i) == MagickFalse)
698 for (y=0; y < (ssize_t) image->rows; y++)
700 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
701 if (p == (const PixelPacket *) NULL)
703 for (x=(ssize_t) image->columns-1; x >= 0; x--)
705 if (IsGray(p) == MagickFalse)
712 #endif /* PNG_LIBPNG_VER > 10011 */
713 #endif /* MAGICKCORE_PNG_DELEGATE */
716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726 % IsMNG() returns MagickTrue if the image format type, identified by the
727 % magick string, is MNG.
729 % The format of the IsMNG method is:
731 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
733 % A description of each parameter follows:
735 % o magick: compare image format pattern against these bytes.
737 % o length: Specifies the length of the magick string.
741 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
746 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763 % IsJNG() returns MagickTrue if the image format type, identified by the
764 % magick string, is JNG.
766 % The format of the IsJNG method is:
768 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
770 % A description of each parameter follows:
772 % o magick: compare image format pattern against these bytes.
774 % o length: Specifies the length of the magick string.
778 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
783 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
800 % IsPNG() returns MagickTrue if the image format type, identified by the
801 % magick string, is PNG.
803 % The format of the IsPNG method is:
805 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
807 % A description of each parameter follows:
809 % o magick: compare image format pattern against these bytes.
811 % o length: Specifies the length of the magick string.
814 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
819 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
825 #if defined(MAGICKCORE_PNG_DELEGATE)
826 #if defined(__cplusplus) || defined(c_plusplus)
830 #if (PNG_LIBPNG_VER > 10011)
831 static size_t WriteBlobMSBULong(Image *image,const size_t value)
836 assert(image != (Image *) NULL);
837 assert(image->signature == MagickSignature);
838 buffer[0]=(unsigned char) (value >> 24);
839 buffer[1]=(unsigned char) (value >> 16);
840 buffer[2]=(unsigned char) (value >> 8);
841 buffer[3]=(unsigned char) value;
842 return((size_t) WriteBlob(image,4,buffer));
845 static void PNGLong(png_bytep p,png_uint_32 value)
847 *p++=(png_byte) ((value >> 24) & 0xff);
848 *p++=(png_byte) ((value >> 16) & 0xff);
849 *p++=(png_byte) ((value >> 8) & 0xff);
850 *p++=(png_byte) (value & 0xff);
853 #if defined(JNG_SUPPORTED)
854 static void PNGsLong(png_bytep p,png_int_32 value)
856 *p++=(png_byte) ((value >> 24) & 0xff);
857 *p++=(png_byte) ((value >> 16) & 0xff);
858 *p++=(png_byte) ((value >> 8) & 0xff);
859 *p++=(png_byte) (value & 0xff);
863 static void PNGShort(png_bytep p,png_uint_16 value)
865 *p++=(png_byte) ((value >> 8) & 0xff);
866 *p++=(png_byte) (value & 0xff);
869 static void PNGType(png_bytep p,png_bytep type)
871 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
874 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
877 if (logging != MagickFalse)
878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
879 " Writing %c%c%c%c chunk, length: %.20g",
880 type[0],type[1],type[2],type[3],(double) length);
882 #endif /* PNG_LIBPNG_VER > 10011 */
884 #if defined(__cplusplus) || defined(c_plusplus)
888 #if PNG_LIBPNG_VER > 10011
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 % R e a d P N G I m a g e %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
901 % Multiple-image Network Graphics (MNG) image file and returns it. It
902 % allocates the memory necessary for the new Image structure and returns a
903 % pointer to the new image or set of images.
905 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
907 % The format of the ReadPNGImage method is:
909 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
911 % A description of each parameter follows:
913 % o image_info: the image info.
915 % o exception: return any errors or warnings in this structure.
917 % To do, more or less in chronological order (as of version 5.5.2,
918 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
920 % Get 16-bit cheap transparency working.
922 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
924 % Preserve all unknown and not-yet-handled known chunks found in input
925 % PNG file and copy them into output PNG files according to the PNG
928 % (At this point, PNG encoding should be in full MNG compliance)
930 % Provide options for choice of background to use when the MNG BACK
931 % chunk is not present or is not mandatory (i.e., leave transparent,
932 % user specified, MNG BACK, PNG bKGD)
934 % Implement LOOP/ENDL [done, but could do discretionary loops more
935 % efficiently by linking in the duplicate frames.].
937 % Decode and act on the MHDR simplicity profile (offer option to reject
938 % files or attempt to process them anyway when the profile isn't LC or VLC).
940 % Upgrade to full MNG without Delta-PNG.
942 % o BACK [done a while ago except for background image ID]
943 % o MOVE [done 15 May 1999]
944 % o CLIP [done 15 May 1999]
945 % o DISC [done 19 May 1999]
946 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
947 % o SEEK [partially done 19 May 1999 (discard function only)]
951 % o MNG-level tEXt/iTXt/zTXt
956 % o iTXt (wait for libpng implementation).
958 % Use the scene signature to discover when an identical scene is
959 % being reused, and just point to the original image->exception instead
960 % of storing another set of pixels. This not specific to MNG
961 % but could be applied generally.
963 % Upgrade to full MNG with Delta-PNG.
967 % We will not attempt to read files containing the CgBI chunk.
968 % They are really Xcode files meant for display on the iPhone.
969 % These are not valid PNG files and it is impossible to recover
970 % the orginal PNG from files that have been converted to Xcode-PNG,
971 % since irretrievable loss of color data has occurred due to the
972 % use of premultiplied alpha.
975 #if defined(__cplusplus) || defined(c_plusplus)
980 This the function that does the actual reading of data. It is
981 the same as the one supplied in libpng, except that it receives the
982 datastream from the ReadBlob() function instead of standard input.
984 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
989 image=(Image *) png_get_io_ptr(png_ptr);
995 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1001 (void) FormatMagickString(msg,MaxTextExtent,
1002 "Expected %.20g bytes; found %.20g bytes",(double) length,
1004 png_warning(png_ptr,msg);
1005 png_error(png_ptr,"Read Exception");
1010 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1011 !defined(PNG_MNG_FEATURES_SUPPORTED)
1012 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1013 * older than libpng-1.0.3a, which was the first to allow the empty
1014 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1015 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1016 * encountered after an empty PLTE, so we have to look ahead for bKGD
1017 * chunks and remove them from the datastream that is passed to libpng,
1018 * and store their contents for later use.
1020 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1035 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1036 image=(Image *) mng_info->image;
1037 while (mng_info->bytes_in_read_buffer && length)
1039 data[i]=mng_info->read_buffer[i];
1040 mng_info->bytes_in_read_buffer--;
1046 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1048 if (check != length)
1049 png_error(png_ptr,"Read Exception");
1053 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1056 check=(png_size_t) ReadBlob(image,(size_t) length,
1057 (char *) mng_info->read_buffer);
1058 mng_info->read_buffer[4]=0;
1059 mng_info->bytes_in_read_buffer=4;
1060 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1061 mng_info->found_empty_plte=MagickTrue;
1062 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1064 mng_info->found_empty_plte=MagickFalse;
1065 mng_info->have_saved_bkgd_index=MagickFalse;
1069 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1072 check=(png_size_t) ReadBlob(image,(size_t) length,
1073 (char *) mng_info->read_buffer);
1074 mng_info->read_buffer[4]=0;
1075 mng_info->bytes_in_read_buffer=4;
1076 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1077 if (mng_info->found_empty_plte)
1080 Skip the bKGD data byte and CRC.
1083 ReadBlob(image,5,(char *) mng_info->read_buffer);
1084 check=(png_size_t) ReadBlob(image,(size_t) length,
1085 (char *) mng_info->read_buffer);
1086 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1087 mng_info->have_saved_bkgd_index=MagickTrue;
1088 mng_info->bytes_in_read_buffer=0;
1096 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1101 image=(Image *) png_get_io_ptr(png_ptr);
1107 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1109 if (check != length)
1110 png_error(png_ptr,"WriteBlob Failed");
1114 static void png_flush_data(png_structp png_ptr)
1119 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1120 static int PalettesAreEqual(Image *a,Image *b)
1125 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1126 return((int) MagickFalse);
1128 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1129 return((int) MagickFalse);
1131 if (a->colors != b->colors)
1132 return((int) MagickFalse);
1134 for (i=0; i < (ssize_t) a->colors; i++)
1136 if ((a->colormap[i].red != b->colormap[i].red) ||
1137 (a->colormap[i].green != b->colormap[i].green) ||
1138 (a->colormap[i].blue != b->colormap[i].blue))
1139 return((int) MagickFalse);
1142 return((int) MagickTrue);
1146 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1148 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1149 mng_info->exists[i] && !mng_info->frozen[i])
1151 #ifdef MNG_OBJECT_BUFFERS
1152 if (mng_info->ob[i] != (MngBuffer *) NULL)
1154 if (mng_info->ob[i]->reference_count > 0)
1155 mng_info->ob[i]->reference_count--;
1157 if (mng_info->ob[i]->reference_count == 0)
1159 if (mng_info->ob[i]->image != (Image *) NULL)
1160 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1162 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1165 mng_info->ob[i]=(MngBuffer *) NULL;
1167 mng_info->exists[i]=MagickFalse;
1168 mng_info->invisible[i]=MagickFalse;
1169 mng_info->viewable[i]=MagickFalse;
1170 mng_info->frozen[i]=MagickFalse;
1171 mng_info->x_off[i]=0;
1172 mng_info->y_off[i]=0;
1173 mng_info->object_clip[i].left=0;
1174 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1175 mng_info->object_clip[i].top=0;
1176 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1180 static void MngInfoFreeStruct(MngInfo *mng_info,
1181 MagickBooleanType *have_mng_structure)
1183 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1188 for (i=1; i < MNG_MAX_OBJECTS; i++)
1189 MngInfoDiscardObject(mng_info,i);
1191 if (mng_info->global_plte != (png_colorp) NULL)
1192 mng_info->global_plte=(png_colorp)
1193 RelinquishMagickMemory(mng_info->global_plte);
1195 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1196 *have_mng_structure=MagickFalse;
1200 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1206 if (box.left < box2.left)
1209 if (box.top < box2.top)
1212 if (box.right > box2.right)
1213 box.right=box2.right;
1215 if (box.bottom > box2.bottom)
1216 box.bottom=box2.bottom;
1221 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1227 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1229 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1230 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1231 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1232 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1233 if (delta_type != 0)
1235 box.left+=previous_box.left;
1236 box.right+=previous_box.right;
1237 box.top+=previous_box.top;
1238 box.bottom+=previous_box.bottom;
1244 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1250 Read two ssize_ts from CLON, MOVE or PAST chunk
1252 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1253 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1255 if (delta_type != 0)
1257 pair.a+=previous_pair.a;
1258 pair.b+=previous_pair.b;
1264 static long mng_get_long(unsigned char *p)
1266 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1269 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1274 image=(Image *) png_get_error_ptr(ping);
1276 if (image->debug != MagickFalse)
1277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1278 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1280 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1281 message,"`%s'",image->filename);
1283 #if (PNG_LIBPNG_VER < 10500)
1284 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1285 * are building with libpng-1.4.x and can be ignored.
1287 longjmp(ping->jmpbuf,1);
1289 png_longjmp(ping,1);
1293 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1298 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1299 png_error(ping, message);
1301 image=(Image *) png_get_error_ptr(ping);
1302 if (image->debug != MagickFalse)
1303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1304 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1306 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1307 message,"`%s'",image->filename);
1310 #ifdef PNG_USER_MEM_SUPPORTED
1311 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1313 #if (PNG_LIBPNG_VER < 10011)
1318 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1321 png_error("Insufficient memory.");
1326 return((png_voidp) AcquireMagickMemory((size_t) size));
1331 Free a pointer. It is removed from the list at the same time.
1333 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1336 ptr=RelinquishMagickMemory(ptr);
1337 return((png_free_ptr) NULL);
1341 #if defined(__cplusplus) || defined(c_plusplus)
1346 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1347 png_textp text,int ii)
1352 register unsigned char
1366 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1367 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1368 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1374 /* look for newline */
1378 /* look for length */
1379 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1382 length=(png_uint_32) StringToLong(sp);
1384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1385 " length: %lu",(unsigned long) length);
1387 while (*sp != ' ' && *sp != '\n')
1390 /* allocate space */
1393 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1394 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1395 return(MagickFalse);
1398 profile=AcquireStringInfo(length);
1400 if (profile == (StringInfo *) NULL)
1402 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1403 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1404 "unable to copy profile");
1405 return(MagickFalse);
1408 /* copy profile, skipping white space and column 1 "=" signs */
1409 dp=GetStringInfoDatum(profile);
1412 for (i=0; i < (ssize_t) nibbles; i++)
1414 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1418 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1419 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1420 profile=DestroyStringInfo(profile);
1421 return(MagickFalse);
1427 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1430 (*dp++)+=unhex[(int) *sp++];
1433 We have already read "Raw profile type.
1435 (void) SetImageProfile(image,&text[ii].key[17],profile);
1436 profile=DestroyStringInfo(profile);
1438 if (image_info->verbose)
1439 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1444 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1445 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1451 /* The unknown chunk structure contains the chunk data:
1456 Note that libpng has already taken care of the CRC handling.
1460 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1461 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1462 return(0); /* Did not recognize */
1464 /* recognized vpAg */
1466 if (chunk->size != 9)
1467 return(-1); /* Error return */
1469 if (chunk->data[8] != 0)
1470 return(0); /* ImageMagick requires pixel units */
1472 image=(Image *) png_get_user_chunk_ptr(ping);
1474 image->page.width=(size_t) ((chunk->data[0] << 24) |
1475 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1477 image->page.height=(size_t) ((chunk->data[4] << 24) |
1478 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1480 /* Return one of the following: */
1481 /* return(-n); chunk had an error */
1482 /* return(0); did not recognize */
1483 /* return(n); success */
1491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1495 % R e a d O n e P N G I m a g e %
1499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1502 % (minus the 8-byte signature) and returns it. It allocates the memory
1503 % necessary for the new Image structure and returns a pointer to the new
1506 % The format of the ReadOnePNGImage method is:
1508 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1509 % ExceptionInfo *exception)
1511 % A description of each parameter follows:
1513 % o mng_info: Specifies a pointer to a MngInfo structure.
1515 % o image_info: the image info.
1517 % o exception: return any errors or warnings in this structure.
1520 static Image *ReadOnePNGImage(MngInfo *mng_info,
1521 const ImageInfo *image_info, ExceptionInfo *exception)
1523 /* Read one PNG image */
1534 ping_interlace_method,
1535 ping_compression_method,
1577 register unsigned char
1580 register IndexPacket
1587 register PixelPacket
1594 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1595 png_byte unused_chunks[]=
1597 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1598 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1599 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1600 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1601 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1602 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1606 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1607 " Enter ReadOnePNGImage()");
1609 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1610 LockSemaphoreInfo(ping_semaphore);
1613 #if (PNG_LIBPNG_VER < 10200)
1614 if (image_info->verbose)
1615 printf("Your PNG library (libpng-%s) is rather old.\n",
1616 PNG_LIBPNG_VER_STRING);
1619 #if (PNG_LIBPNG_VER >= 10400)
1620 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1621 if (image_info->verbose)
1623 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1624 PNG_LIBPNG_VER_STRING);
1625 printf("Please update it.\n");
1631 quantum_info = (QuantumInfo *) NULL;
1632 image=mng_info->image;
1634 if (logging != MagickFalse)
1635 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1636 " image->matte=%d",(int) image->matte);
1638 /* Set to an out-of-range color unless tRNS chunk is present */
1639 transparent_color.red=65537;
1640 transparent_color.green=65537;
1641 transparent_color.blue=65537;
1642 transparent_color.opacity=65537;
1645 Allocate the PNG structures
1647 #ifdef PNG_USER_MEM_SUPPORTED
1648 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1649 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1650 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1652 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1653 MagickPNGErrorHandler,MagickPNGWarningHandler);
1655 if (ping == (png_struct *) NULL)
1656 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1658 ping_info=png_create_info_struct(ping);
1660 if (ping_info == (png_info *) NULL)
1662 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1663 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1666 end_info=png_create_info_struct(ping);
1668 if (end_info == (png_info *) NULL)
1670 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1671 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1674 ping_pixels=(unsigned char *) NULL;
1676 if (setjmp(png_jmpbuf(ping)))
1679 PNG image is corrupt.
1681 png_destroy_read_struct(&ping,&ping_info,&end_info);
1682 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1683 UnlockSemaphoreInfo(ping_semaphore);
1685 if (logging != MagickFalse)
1686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1687 " exit ReadOnePNGImage() with error.");
1689 if (image != (Image *) NULL)
1691 InheritException(exception,&image->exception);
1695 return(GetFirstImageInList(image));
1698 Prepare PNG for reading.
1701 mng_info->image_found++;
1702 png_set_sig_bytes(ping,8);
1704 if (LocaleCompare(image_info->magick,"MNG") == 0)
1706 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1707 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1708 png_set_read_fn(ping,image,png_get_data);
1710 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1711 png_permit_empty_plte(ping,MagickTrue);
1712 png_set_read_fn(ping,image,png_get_data);
1714 mng_info->image=image;
1715 mng_info->bytes_in_read_buffer=0;
1716 mng_info->found_empty_plte=MagickFalse;
1717 mng_info->have_saved_bkgd_index=MagickFalse;
1718 png_set_read_fn(ping,mng_info,mng_get_data);
1724 png_set_read_fn(ping,image,png_get_data);
1726 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1727 /* Ignore unused chunks and all unknown chunks except for vpAg */
1728 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1729 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1730 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1731 (int)sizeof(unused_chunks)/5);
1732 /* Callback for other unknown chunks */
1733 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1736 #if (PNG_LIBPNG_VER < 10400)
1737 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1738 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1739 /* Disable thread-unsafe features of pnggccrd */
1740 if (png_access_version_number() >= 10200)
1742 png_uint_32 mmx_disable_mask=0;
1743 png_uint_32 asm_flags;
1745 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1746 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1747 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1748 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1749 asm_flags=png_get_asm_flags(ping);
1750 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1755 png_read_info(ping,ping_info);
1757 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1758 &ping_bit_depth,&ping_color_type,
1759 &ping_interlace_method,&ping_compression_method,
1760 &ping_filter_method);
1762 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1765 (void) png_get_bKGD(ping, ping_info, &ping_background);
1767 if (ping_bit_depth < 8)
1769 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1771 png_set_packing(ping);
1776 image->depth=ping_bit_depth;
1777 image->depth=GetImageQuantumDepth(image,MagickFalse);
1778 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1779 if (logging != MagickFalse)
1781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782 " PNG width: %.20g, height: %.20g",
1783 (double) ping_width, (double) ping_height);
1785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1786 " PNG color_type: %d, bit_depth: %d",
1787 ping_color_type, ping_bit_depth);
1789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1790 " PNG compression_method: %d",
1791 ping_compression_method);
1793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1794 " PNG interlace_method: %d, filter_method: %d",
1795 ping_interlace_method,ping_filter_method);
1798 #ifdef PNG_READ_iCCP_SUPPORTED
1799 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1804 #if (PNG_LIBPNG_VER < 10500)
1818 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1821 if (profile_length != 0)
1826 if (logging != MagickFalse)
1827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1828 " Reading PNG iCCP chunk.");
1829 profile=AcquireStringInfo(profile_length);
1830 SetStringInfoDatum(profile,(const unsigned char *) info);
1831 (void) SetImageProfile(image,"icc",profile);
1832 profile=DestroyStringInfo(profile);
1836 #if defined(PNG_READ_sRGB_SUPPORTED)
1841 if (mng_info->have_global_srgb)
1842 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1843 (mng_info->global_srgb_intent);
1845 if (png_get_sRGB(ping,ping_info,&intent))
1847 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1850 if (logging != MagickFalse)
1851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1852 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1860 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1861 if (mng_info->have_global_gama)
1862 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1864 if (png_get_gAMA(ping,ping_info,&file_gamma))
1866 image->gamma=(float) file_gamma;
1867 if (logging != MagickFalse)
1868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1869 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1872 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1874 if (mng_info->have_global_chrm != MagickFalse)
1876 (void) png_set_cHRM(ping,ping_info,
1877 mng_info->global_chrm.white_point.x,
1878 mng_info->global_chrm.white_point.y,
1879 mng_info->global_chrm.red_primary.x,
1880 mng_info->global_chrm.red_primary.y,
1881 mng_info->global_chrm.green_primary.x,
1882 mng_info->global_chrm.green_primary.y,
1883 mng_info->global_chrm.blue_primary.x,
1884 mng_info->global_chrm.blue_primary.y);
1888 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1890 (void) png_get_cHRM(ping,ping_info,
1891 &image->chromaticity.white_point.x,
1892 &image->chromaticity.white_point.y,
1893 &image->chromaticity.red_primary.x,
1894 &image->chromaticity.red_primary.y,
1895 &image->chromaticity.green_primary.x,
1896 &image->chromaticity.green_primary.y,
1897 &image->chromaticity.blue_primary.x,
1898 &image->chromaticity.blue_primary.y);
1900 if (logging != MagickFalse)
1901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1902 " Reading PNG cHRM chunk.");
1905 if (image->rendering_intent != UndefinedIntent)
1907 png_set_sRGB(ping,ping_info,
1908 Magick_RenderingIntent_to_PNG_RenderingIntent
1909 (image->rendering_intent));
1910 png_set_gAMA(ping,ping_info,0.45455f);
1911 png_set_cHRM(ping,ping_info,
1912 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1913 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1915 #if defined(PNG_oFFs_SUPPORTED)
1916 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1918 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1919 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1921 if (logging != MagickFalse)
1922 if (image->page.x || image->page.y)
1923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1924 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1925 image->page.x,(double) image->page.y);
1928 #if defined(PNG_pHYs_SUPPORTED)
1929 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1931 if (mng_info->have_global_phys)
1933 png_set_pHYs(ping,ping_info,
1934 mng_info->global_x_pixels_per_unit,
1935 mng_info->global_y_pixels_per_unit,
1936 mng_info->global_phys_unit_type);
1940 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1950 Set image resolution.
1952 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1954 image->x_resolution=(double) x_resolution;
1955 image->y_resolution=(double) y_resolution;
1957 if (unit_type == PNG_RESOLUTION_METER)
1959 image->units=PixelsPerCentimeterResolution;
1960 image->x_resolution=(double) x_resolution/100.0;
1961 image->y_resolution=(double) y_resolution/100.0;
1964 if (logging != MagickFalse)
1965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1966 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1967 (double) x_resolution,(double) y_resolution,unit_type);
1971 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1979 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1981 if ((number_colors == 0) &&
1982 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1984 if (mng_info->global_plte_length)
1986 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1987 (int) mng_info->global_plte_length);
1989 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
1990 if (mng_info->global_trns_length)
1992 if (mng_info->global_trns_length >
1993 mng_info->global_plte_length)
1994 (void) ThrowMagickException(&image->exception,
1995 GetMagickModule(),CoderError,
1996 "global tRNS has more entries than global PLTE",
1997 "`%s'",image_info->filename);
1998 png_set_tRNS(ping,ping_info,mng_info->global_trns,
1999 (int) mng_info->global_trns_length,NULL);
2001 #if defined(PNG_READ_bKGD_SUPPORTED)
2003 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2004 mng_info->have_saved_bkgd_index ||
2006 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2011 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012 if (mng_info->have_saved_bkgd_index)
2013 background.index=mng_info->saved_bkgd_index;
2015 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2016 background.index=ping_background->index;
2018 background.red=(png_uint_16)
2019 mng_info->global_plte[background.index].red;
2021 background.green=(png_uint_16)
2022 mng_info->global_plte[background.index].green;
2024 background.blue=(png_uint_16)
2025 mng_info->global_plte[background.index].blue;
2027 png_set_bKGD(ping,ping_info,&background);
2032 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2033 CoderError,"No global PLTE in file","`%s'",
2034 image_info->filename);
2038 #if defined(PNG_READ_bKGD_SUPPORTED)
2039 if (mng_info->have_global_bkgd &&
2040 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2041 image->background_color=mng_info->mng_global_bkgd;
2043 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2046 Set image background color.
2048 if (logging != MagickFalse)
2049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2050 " Reading PNG bKGD chunk.");
2052 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2054 image->background_color.red=ping_background->red;
2055 image->background_color.green=ping_background->green;
2056 image->background_color.blue=ping_background->blue;
2059 else /* Scale background components to 16-bit */
2064 if (logging != MagickFalse)
2065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2066 " raw ping_background=(%d,%d,%d).",ping_background->red,
2067 ping_background->green,ping_background->blue);
2071 if (ping_bit_depth == 1)
2074 else if (ping_bit_depth == 2)
2077 else if (ping_bit_depth == 4)
2080 if (ping_bit_depth <= 8)
2083 ping_background->red *= bkgd_scale;
2084 ping_background->green *= bkgd_scale;
2085 ping_background->blue *= bkgd_scale;
2087 if (logging != MagickFalse)
2089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2090 " bkgd_scale=%d.",bkgd_scale);
2092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2093 " ping_background=(%d,%d,%d).",ping_background->red,
2094 ping_background->green,ping_background->blue);
2097 image->background_color.red=
2098 ScaleShortToQuantum(ping_background->red);
2100 image->background_color.green=
2101 ScaleShortToQuantum(ping_background->green);
2103 image->background_color.blue=
2104 ScaleShortToQuantum(ping_background->blue);
2106 image->background_color.opacity=OpaqueOpacity;
2108 if (logging != MagickFalse)
2109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2110 " image->background_color=(%.20g,%.20g,%.20g).",
2111 (double) image->background_color.red,
2112 (double) image->background_color.green,
2113 (double) image->background_color.blue);
2118 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2121 Image has a tRNS chunk.
2129 if (logging != MagickFalse)
2130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2131 " Reading PNG tRNS chunk.");
2133 max_sample = (int) ((one << ping_bit_depth) - 1);
2135 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2136 (int)ping_trans_color->gray > max_sample) ||
2137 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2138 ((int)ping_trans_color->red > max_sample ||
2139 (int)ping_trans_color->green > max_sample ||
2140 (int)ping_trans_color->blue > max_sample)))
2142 if (logging != MagickFalse)
2143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2144 " Ignoring PNG tRNS chunk with out-of-range sample.");
2145 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2146 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2147 image->matte=MagickFalse;
2154 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2156 /* Scale transparent_color to short */
2157 transparent_color.red= scale_to_short*ping_trans_color->red;
2158 transparent_color.green= scale_to_short*ping_trans_color->green;
2159 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2160 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2162 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2164 if (logging != MagickFalse)
2166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2167 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 " scaled graylevel is %d.",transparent_color.opacity);
2172 transparent_color.red=transparent_color.opacity;
2173 transparent_color.green=transparent_color.opacity;
2174 transparent_color.blue=transparent_color.opacity;
2178 #if defined(PNG_READ_sBIT_SUPPORTED)
2179 if (mng_info->have_global_sbit)
2181 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2182 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2185 num_passes=png_set_interlace_handling(ping);
2187 png_read_update_info(ping,ping_info);
2189 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2192 Initialize image structure.
2194 mng_info->image_box.left=0;
2195 mng_info->image_box.right=(ssize_t) ping_width;
2196 mng_info->image_box.top=0;
2197 mng_info->image_box.bottom=(ssize_t) ping_height;
2198 if (mng_info->mng_type == 0)
2200 mng_info->mng_width=ping_width;
2201 mng_info->mng_height=ping_height;
2202 mng_info->frame=mng_info->image_box;
2203 mng_info->clip=mng_info->image_box;
2208 image->page.y=mng_info->y_off[mng_info->object_id];
2211 image->compression=ZipCompression;
2212 image->columns=ping_width;
2213 image->rows=ping_height;
2214 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2215 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2216 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2221 image->storage_class=PseudoClass;
2223 image->colors=one << ping_bit_depth;
2224 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2225 if (image->colors > 256)
2228 if (image->colors > 65536L)
2229 image->colors=65536L;
2231 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2239 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2240 image->colors=(size_t) number_colors;
2242 if (logging != MagickFalse)
2243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2248 if (image->storage_class == PseudoClass)
2251 Initialize image colormap.
2253 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2254 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2256 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2264 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2266 for (i=0; i < (ssize_t) image->colors; i++)
2268 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2269 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2270 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2279 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2284 for (i=0; i < (ssize_t) image->colors; i++)
2286 image->colormap[i].red=(Quantum) (i*scale);
2287 image->colormap[i].green=(Quantum) (i*scale);
2288 image->colormap[i].blue=(Quantum) (i*scale);
2293 Read image scanlines.
2295 if (image->delay != 0)
2296 mng_info->scenes_found++;
2298 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2299 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2300 (image_info->first_scene+image_info->number_scenes))))
2302 if (logging != MagickFalse)
2303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2304 " Skipping PNG image data for scene %.20g",(double)
2305 mng_info->scenes_found-1);
2306 png_destroy_read_struct(&ping,&ping_info,&end_info);
2307 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2308 UnlockSemaphoreInfo(ping_semaphore);
2310 if (logging != MagickFalse)
2311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312 " exit ReadOnePNGImage().");
2317 if (logging != MagickFalse)
2318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2319 " Reading PNG IDAT chunk(s)");
2322 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2323 ping_rowbytes*sizeof(*ping_pixels));
2326 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2327 sizeof(*ping_pixels));
2329 if (ping_pixels == (unsigned char *) NULL)
2330 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2332 if (logging != MagickFalse)
2333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2334 " Converting PNG pixels to pixel packets");
2336 Convert PNG pixels to pixel packets.
2338 if (setjmp(png_jmpbuf(ping)))
2341 PNG image is corrupt.
2343 png_destroy_read_struct(&ping,&ping_info,&end_info);
2344 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2345 UnlockSemaphoreInfo(ping_semaphore);
2347 if (quantum_info != (QuantumInfo *) NULL)
2348 quantum_info = DestroyQuantumInfo(quantum_info);
2350 if (ping_pixels != (unsigned char *) NULL)
2351 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2353 if (logging != MagickFalse)
2354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " exit ReadOnePNGImage() with error.");
2357 if (image != (Image *) NULL)
2359 InheritException(exception,&image->exception);
2363 return(GetFirstImageInList(image));
2366 quantum_info=AcquireQuantumInfo(image_info,image);
2368 if (quantum_info == (QuantumInfo *) NULL)
2369 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2374 found_transparent_pixel;
2376 found_transparent_pixel=MagickFalse;
2378 if (image->storage_class == DirectClass)
2380 for (pass=0; pass < num_passes; pass++)
2383 Convert image to DirectClass pixel packets.
2385 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2389 depth=(ssize_t) ping_bit_depth;
2391 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2392 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2393 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2394 MagickTrue : MagickFalse;
2396 for (y=0; y < (ssize_t) image->rows; y++)
2399 row_offset=ping_rowbytes*y;
2404 png_read_row(ping,ping_pixels+row_offset,NULL);
2405 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2407 if (q == (PixelPacket *) NULL)
2410 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2411 /* code deleted from version 6.6.6-8 */
2412 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2414 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2415 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2416 GrayQuantum,ping_pixels+row_offset,exception);
2418 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2419 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2420 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2422 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2423 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2424 RGBAQuantum,ping_pixels+row_offset,exception);
2426 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2427 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2428 IndexQuantum,ping_pixels+row_offset,exception);
2430 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2431 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2432 RGBQuantum,ping_pixels+row_offset,exception);
2434 if (found_transparent_pixel == MagickFalse)
2436 /* Is there a transparent pixel in the row? */
2437 if (y== 0 && logging != MagickFalse)
2438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2439 " Looking for cheap transparent pixel");
2441 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2443 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2444 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2445 (q->opacity != OpaqueOpacity))
2447 if (logging != MagickFalse)
2448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2451 found_transparent_pixel = MagickTrue;
2454 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2455 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2456 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2457 ScaleQuantumToShort(q->green) == transparent_color.green &&
2458 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2460 if (logging != MagickFalse)
2461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2463 found_transparent_pixel = MagickTrue;
2470 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2472 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2475 if (status == MagickFalse)
2478 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2482 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2484 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2485 if (status == MagickFalse)
2491 else /* image->storage_class != DirectClass */
2493 for (pass=0; pass < num_passes; pass++)
2502 Convert grayscale image to PseudoClass pixel packets.
2504 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2505 MagickTrue : MagickFalse;
2507 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2508 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2510 if (quantum_scanline == (Quantum *) NULL)
2511 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2513 for (y=0; y < (ssize_t) image->rows; y++)
2516 row_offset=ping_rowbytes*y;
2521 png_read_row(ping,ping_pixels+row_offset,NULL);
2522 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2524 if (q == (PixelPacket *) NULL)
2527 indexes=GetAuthenticIndexQueue(image);
2528 p=ping_pixels+row_offset;
2531 switch (ping_bit_depth)
2538 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2540 for (bit=7; bit >= 0; bit--)
2541 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2545 if ((image->columns % 8) != 0)
2547 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2548 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2556 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2558 *r++=(*p >> 6) & 0x03;
2559 *r++=(*p >> 4) & 0x03;
2560 *r++=(*p >> 2) & 0x03;
2564 if ((image->columns % 4) != 0)
2566 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2567 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2575 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2577 *r++=(*p >> 4) & 0x0f;
2581 if ((image->columns % 2) != 0)
2582 *r++=(*p++ >> 4) & 0x0f;
2589 if (ping_color_type == 4)
2590 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2593 /* In image.h, OpaqueOpacity is 0
2594 * TransparentOpacity is QuantumRange
2595 * In a PNG datastream, Opaque is QuantumRange
2596 * and Transparent is 0.
2598 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2599 if (q->opacity != OpaqueOpacity)
2600 found_transparent_pixel = MagickTrue;
2605 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2613 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2615 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2619 if (image->colors > 256)
2627 *r=(Quantum) quantum;
2630 if (ping_color_type == 4)
2632 quantum=((*p++) << 8);
2634 q->opacity=(Quantum) (QuantumRange-quantum);
2635 if (q->opacity != OpaqueOpacity)
2636 found_transparent_pixel = MagickTrue;
2640 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2644 if (image->colors > 256)
2655 if (ping_color_type == 4)
2657 q->opacity=(*p << 8) | *(p+1);
2659 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2660 if (q->opacity != OpaqueOpacity)
2661 found_transparent_pixel = MagickTrue;
2666 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2668 p++; /* strip low byte */
2670 if (ping_color_type == 4)
2672 q->opacity=(Quantum) (QuantumRange-(*p++));
2673 if (q->opacity != OpaqueOpacity)
2674 found_transparent_pixel = MagickTrue;
2689 Transfer image scanline.
2693 for (x=0; x < (ssize_t) image->columns; x++)
2694 indexes[x]=(IndexPacket) (*r++);
2696 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2699 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2701 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2704 if (status == MagickFalse)
2709 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2711 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2713 if (status == MagickFalse)
2717 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2720 image->matte=found_transparent_pixel;
2722 if (logging != MagickFalse)
2724 if (found_transparent_pixel != MagickFalse)
2725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2726 " Found transparent pixel");
2729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2730 " No transparent pixel was found");
2732 ping_color_type&=0x03;
2737 if (quantum_info != (QuantumInfo *) NULL)
2738 quantum_info=DestroyQuantumInfo(quantum_info);
2740 if (image->storage_class == PseudoClass)
2746 image->matte=MagickFalse;
2747 (void) SyncImage(image);
2751 png_read_end(ping,ping_info);
2753 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2754 (ssize_t) image_info->first_scene && image->delay != 0)
2756 png_destroy_read_struct(&ping,&ping_info,&end_info);
2757 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2759 (void) SetImageBackgroundColor(image);
2760 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2761 UnlockSemaphoreInfo(ping_semaphore);
2763 if (logging != MagickFalse)
2764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765 " exit ReadOnePNGImage() early.");
2769 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2775 Image has a transparent background.
2777 storage_class=image->storage_class;
2778 image->matte=MagickTrue;
2780 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2782 if (storage_class == PseudoClass)
2784 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2786 for (x=0; x < ping_num_trans; x++)
2788 image->colormap[x].opacity =
2789 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2793 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2795 for (x=0; x < (int) image->colors; x++)
2797 if (ScaleQuantumToShort(image->colormap[x].red) ==
2798 transparent_color.opacity)
2800 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2804 (void) SyncImage(image);
2807 #if 1 /* Should have already been done above, but glennrp problem P10
2812 for (y=0; y < (ssize_t) image->rows; y++)
2814 image->storage_class=storage_class;
2815 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2817 if (q == (PixelPacket *) NULL)
2820 indexes=GetAuthenticIndexQueue(image);
2822 /* Caution: on a Q8 build, this does not distinguish between
2823 * 16-bit colors that differ only in the low byte
2825 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2827 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2828 ScaleQuantumToShort(q->green) == transparent_color.green &&
2829 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2831 q->opacity=(Quantum) TransparentOpacity;
2834 #if 0 /* I have not found a case where this is needed. */
2837 q->opacity=(Quantum) OpaqueOpacity;
2844 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2850 image->storage_class=DirectClass;
2853 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2854 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2855 image->colorspace=GRAYColorspace;
2857 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2858 for (i=0; i < (ssize_t) num_text; i++)
2860 /* Check for a profile */
2862 if (logging != MagickFalse)
2863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2864 " Reading PNG text chunk");
2866 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2867 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2874 length=text[i].text_length;
2875 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2877 if (value == (char *) NULL)
2879 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2885 (void) ConcatenateMagickString(value,text[i].text,length+2);
2887 /* Don't save "density" or "units" property if we have a pHYs
2890 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2891 (LocaleCompare(text[i].key,"density") != 0 &&
2892 LocaleCompare(text[i].key,"units") != 0))
2893 (void) SetImageProperty(image,text[i].key,value);
2895 if (logging != MagickFalse)
2897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2898 " length: %lu",(unsigned long) length);
2899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2900 " Keyword: %s",text[i].key);
2903 value=DestroyString(value);
2907 #ifdef MNG_OBJECT_BUFFERS
2909 Store the object if necessary.
2911 if (object_id && !mng_info->frozen[object_id])
2913 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2916 create a new object buffer.
2918 mng_info->ob[object_id]=(MngBuffer *)
2919 AcquireMagickMemory(sizeof(MngBuffer));
2921 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2923 mng_info->ob[object_id]->image=(Image *) NULL;
2924 mng_info->ob[object_id]->reference_count=1;
2928 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2929 mng_info->ob[object_id]->frozen)
2931 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2932 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2933 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2936 if (mng_info->ob[object_id]->frozen)
2937 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2938 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2939 "`%s'",image->filename);
2945 if (mng_info->ob[object_id]->image != (Image *) NULL)
2946 mng_info->ob[object_id]->image=DestroyImage
2947 (mng_info->ob[object_id]->image);
2949 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2952 if (mng_info->ob[object_id]->image != (Image *) NULL)
2953 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2956 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2957 ResourceLimitError,"Cloning image for object buffer failed",
2958 "`%s'",image->filename);
2960 if (ping_width > 250000L || ping_height > 250000L)
2961 png_error(ping,"PNG Image dimensions are too large.");
2963 mng_info->ob[object_id]->width=ping_width;
2964 mng_info->ob[object_id]->height=ping_height;
2965 mng_info->ob[object_id]->color_type=ping_color_type;
2966 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2967 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2968 mng_info->ob[object_id]->compression_method=
2969 ping_compression_method;
2970 mng_info->ob[object_id]->filter_method=ping_filter_method;
2972 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2981 Copy the PLTE to the object buffer.
2983 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2984 mng_info->ob[object_id]->plte_length=number_colors;
2986 for (i=0; i < number_colors; i++)
2988 mng_info->ob[object_id]->plte[i]=plte[i];
2993 mng_info->ob[object_id]->plte_length=0;
2998 /* Set image->matte to MagickTrue if the input colortype supports
2999 * alpha or if a valid tRNS chunk is present, no matter whether there
3000 * is actual transparency present.
3002 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3003 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3004 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3005 MagickTrue : MagickFalse;
3008 Relinquish resources.
3010 png_destroy_read_struct(&ping,&ping_info,&end_info);
3012 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3013 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3014 UnlockSemaphoreInfo(ping_semaphore);
3017 if (logging != MagickFalse)
3018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019 " exit ReadOnePNGImage()");
3023 /* end of reading one PNG image */
3026 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3041 magic_number[MaxTextExtent];
3049 assert(image_info != (const ImageInfo *) NULL);
3050 assert(image_info->signature == MagickSignature);
3052 if (image_info->debug != MagickFalse)
3053 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3054 image_info->filename);
3056 assert(exception != (ExceptionInfo *) NULL);
3057 assert(exception->signature == MagickSignature);
3058 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3059 image=AcquireImage(image_info);
3060 mng_info=(MngInfo *) NULL;
3061 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3063 if (status == MagickFalse)
3064 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3067 Verify PNG signature.
3069 count=ReadBlob(image,8,(unsigned char *) magic_number);
3071 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3072 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3075 Allocate a MngInfo structure.
3077 have_mng_structure=MagickFalse;
3078 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3080 if (mng_info == (MngInfo *) NULL)
3081 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3084 Initialize members of the MngInfo structure.
3086 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3087 mng_info->image=image;
3088 have_mng_structure=MagickTrue;
3091 image=ReadOnePNGImage(mng_info,image_info,exception);
3092 MngInfoFreeStruct(mng_info,&have_mng_structure);
3094 if (image == (Image *) NULL)
3096 if (previous != (Image *) NULL)
3098 if (previous->signature != MagickSignature)
3099 ThrowReaderException(CorruptImageError,"CorruptImage");
3101 (void) CloseBlob(previous);
3102 (void) DestroyImageList(previous);
3105 if (logging != MagickFalse)
3106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3107 "exit ReadPNGImage() with error");
3109 return((Image *) NULL);
3112 (void) CloseBlob(image);
3114 if ((image->columns == 0) || (image->rows == 0))
3116 if (logging != MagickFalse)
3117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3118 "exit ReadPNGImage() with error.");
3120 ThrowReaderException(CorruptImageError,"CorruptImage");
3123 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3125 (void) SetImageType(image,PaletteType);
3127 if (image->matte != MagickFalse)
3129 /* To do: Reduce to binary transparency */
3133 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3135 (void) SetImageType(image,TrueColorType);
3136 image->matte=MagickFalse;
3139 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3140 (void) SetImageType(image,TrueColorMatteType);
3142 if (logging != MagickFalse)
3143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3144 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3145 (double) image->page.width,(double) image->page.height,
3146 (double) image->page.x,(double) image->page.y);
3148 if (logging != MagickFalse)
3149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3156 #if defined(JNG_SUPPORTED)
3158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3162 % R e a d O n e J N G I m a g e %
3166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3168 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3169 % (minus the 8-byte signature) and returns it. It allocates the memory
3170 % necessary for the new Image structure and returns a pointer to the new
3173 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3175 % The format of the ReadOneJNGImage method is:
3177 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3178 % ExceptionInfo *exception)
3180 % A description of each parameter follows:
3182 % o mng_info: Specifies a pointer to a MngInfo structure.
3184 % o image_info: the image info.
3186 % o exception: return any errors or warnings in this structure.
3189 static Image *ReadOneJNGImage(MngInfo *mng_info,
3190 const ImageInfo *image_info, ExceptionInfo *exception)
3217 jng_image_sample_depth,
3218 jng_image_compression_method,
3219 jng_image_interlace_method,
3220 jng_alpha_sample_depth,
3221 jng_alpha_compression_method,
3222 jng_alpha_filter_method,
3223 jng_alpha_interlace_method;
3225 register const PixelPacket
3232 register PixelPacket
3235 register unsigned char
3246 jng_alpha_compression_method=0;
3247 jng_alpha_sample_depth=8;
3251 alpha_image=(Image *) NULL;
3252 color_image=(Image *) NULL;
3253 alpha_image_info=(ImageInfo *) NULL;
3254 color_image_info=(ImageInfo *) NULL;
3256 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3257 " Enter ReadOneJNGImage()");
3259 image=mng_info->image;
3261 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3264 Allocate next image structure.
3266 if (logging != MagickFalse)
3267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3268 " AcquireNextImage()");
3270 AcquireNextImage(image_info,image);
3272 if (GetNextImageInList(image) == (Image *) NULL)
3273 return((Image *) NULL);
3275 image=SyncNextImageInList(image);
3277 mng_info->image=image;
3280 Signature bytes have already been read.
3283 read_JSEP=MagickFalse;
3284 reading_idat=MagickFalse;
3285 skip_to_iend=MagickFalse;
3289 type[MaxTextExtent];
3298 Read a new JNG chunk.
3300 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3301 2*GetBlobSize(image));
3303 if (status == MagickFalse)
3307 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3308 length=ReadBlobMSBLong(image);
3309 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3311 if (logging != MagickFalse)
3312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3313 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3314 type[0],type[1],type[2],type[3],(double) length);
3316 if (length > PNG_UINT_31_MAX || count == 0)
3317 ThrowReaderException(CorruptImageError,"CorruptImage");
3320 chunk=(unsigned char *) NULL;
3324 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3326 if (chunk == (unsigned char *) NULL)
3327 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3329 for (i=0; i < (ssize_t) length; i++)
3330 chunk[i]=(unsigned char) ReadBlobByte(image);
3335 (void) ReadBlobMSBLong(image); /* read crc word */
3340 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3345 if (memcmp(type,mng_JHDR,4) == 0)
3349 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3350 (p[2] << 8) | p[3]);
3351 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3352 (p[6] << 8) | p[7]);
3353 jng_color_type=p[8];
3354 jng_image_sample_depth=p[9];
3355 jng_image_compression_method=p[10];
3356 jng_image_interlace_method=p[11];
3358 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3361 jng_alpha_sample_depth=p[12];
3362 jng_alpha_compression_method=p[13];
3363 jng_alpha_filter_method=p[14];
3364 jng_alpha_interlace_method=p[15];
3366 if (logging != MagickFalse)
3368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3369 " jng_width: %16lu",(unsigned long) jng_width);
3371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372 " jng_width: %16lu",(unsigned long) jng_height);
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375 " jng_color_type: %16d",jng_color_type);
3377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3378 " jng_image_sample_depth: %3d",
3379 jng_image_sample_depth);
3381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3382 " jng_image_compression_method:%3d",
3383 jng_image_compression_method);
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " jng_image_interlace_method: %3d",
3387 jng_image_interlace_method);
3389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3390 " jng_alpha_sample_depth: %3d",
3391 jng_alpha_sample_depth);
3393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3394 " jng_alpha_compression_method:%3d",
3395 jng_alpha_compression_method);
3397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3398 " jng_alpha_filter_method: %3d",
3399 jng_alpha_filter_method);
3401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3402 " jng_alpha_interlace_method: %3d",
3403 jng_alpha_interlace_method);
3408 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3414 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3415 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3416 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3419 o create color_image
3420 o open color_blob, attached to color_image
3421 o if (color type has alpha)
3422 open alpha_blob, attached to alpha_image
3425 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3427 if (color_image_info == (ImageInfo *) NULL)
3428 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3430 GetImageInfo(color_image_info);
3431 color_image=AcquireImage(color_image_info);
3433 if (color_image == (Image *) NULL)
3434 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3436 if (logging != MagickFalse)
3437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3438 " Creating color_blob.");
3440 (void) AcquireUniqueFilename(color_image->filename);
3441 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3444 if (status == MagickFalse)
3445 return((Image *) NULL);
3447 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3449 alpha_image_info=(ImageInfo *)
3450 AcquireMagickMemory(sizeof(ImageInfo));
3452 if (alpha_image_info == (ImageInfo *) NULL)
3453 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3455 GetImageInfo(alpha_image_info);
3456 alpha_image=AcquireImage(alpha_image_info);
3458 if (alpha_image == (Image *) NULL)
3460 alpha_image=DestroyImage(alpha_image);
3461 ThrowReaderException(ResourceLimitError,
3462 "MemoryAllocationFailed");
3465 if (logging != MagickFalse)
3466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3467 " Creating alpha_blob.");
3469 (void) AcquireUniqueFilename(alpha_image->filename);
3470 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3473 if (status == MagickFalse)
3474 return((Image *) NULL);
3476 if (jng_alpha_compression_method == 0)
3481 if (logging != MagickFalse)
3482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483 " Writing IHDR chunk to alpha_blob.");
3485 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3486 "\211PNG\r\n\032\n");
3488 (void) WriteBlobMSBULong(alpha_image,13L);
3489 PNGType(data,mng_IHDR);
3490 LogPNGChunk(logging,mng_IHDR,13L);
3491 PNGLong(data+4,jng_width);
3492 PNGLong(data+8,jng_height);
3493 data[12]=jng_alpha_sample_depth;
3494 data[13]=0; /* color_type gray */
3495 data[14]=0; /* compression method 0 */
3496 data[15]=0; /* filter_method 0 */
3497 data[16]=0; /* interlace_method 0 */
3498 (void) WriteBlob(alpha_image,17,data);
3499 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3502 reading_idat=MagickTrue;
3505 if (memcmp(type,mng_JDAT,4) == 0)
3507 /* Copy chunk to color_image->blob */
3509 if (logging != MagickFalse)
3510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3511 " Copying JDAT chunk data to color_blob.");
3513 (void) WriteBlob(color_image,length,chunk);
3516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3521 if (memcmp(type,mng_IDAT,4) == 0)
3526 /* Copy IDAT header and chunk data to alpha_image->blob */
3528 if (image_info->ping == MagickFalse)
3530 if (logging != MagickFalse)
3531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " Copying IDAT chunk data to alpha_blob.");
3534 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3535 PNGType(data,mng_IDAT);
3536 LogPNGChunk(logging,mng_IDAT,length);
3537 (void) WriteBlob(alpha_image,4,data);
3538 (void) WriteBlob(alpha_image,length,chunk);
3539 (void) WriteBlobMSBULong(alpha_image,
3540 crc32(crc32(0,data,4),chunk,(uInt) length));
3544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3549 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3551 /* Copy chunk data to alpha_image->blob */
3553 if (image_info->ping == MagickFalse)
3555 if (logging != MagickFalse)
3556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3557 " Copying JDAA chunk data to alpha_blob.");
3559 (void) WriteBlob(alpha_image,length,chunk);
3563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3568 if (memcmp(type,mng_JSEP,4) == 0)
3570 read_JSEP=MagickTrue;
3573 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3578 if (memcmp(type,mng_bKGD,4) == 0)
3582 image->background_color.red=ScaleCharToQuantum(p[1]);
3583 image->background_color.green=image->background_color.red;
3584 image->background_color.blue=image->background_color.red;
3589 image->background_color.red=ScaleCharToQuantum(p[1]);
3590 image->background_color.green=ScaleCharToQuantum(p[3]);
3591 image->background_color.blue=ScaleCharToQuantum(p[5]);
3594 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3598 if (memcmp(type,mng_gAMA,4) == 0)
3601 image->gamma=((float) mng_get_long(p))*0.00001;
3603 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3607 if (memcmp(type,mng_cHRM,4) == 0)
3611 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3612 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3613 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3614 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3615 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3616 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3617 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3618 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3621 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3625 if (memcmp(type,mng_sRGB,4) == 0)
3629 image->rendering_intent=
3630 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3631 image->gamma=0.45455f;
3632 image->chromaticity.red_primary.x=0.6400f;
3633 image->chromaticity.red_primary.y=0.3300f;
3634 image->chromaticity.green_primary.x=0.3000f;
3635 image->chromaticity.green_primary.y=0.6000f;
3636 image->chromaticity.blue_primary.x=0.1500f;
3637 image->chromaticity.blue_primary.y=0.0600f;
3638 image->chromaticity.white_point.x=0.3127f;
3639 image->chromaticity.white_point.y=0.3290f;
3642 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3646 if (memcmp(type,mng_oFFs,4) == 0)
3650 image->page.x=(ssize_t) mng_get_long(p);
3651 image->page.y=(ssize_t) mng_get_long(&p[4]);
3653 if ((int) p[8] != 0)
3655 image->page.x/=10000;
3656 image->page.y/=10000;
3661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3666 if (memcmp(type,mng_pHYs,4) == 0)
3670 image->x_resolution=(double) mng_get_long(p);
3671 image->y_resolution=(double) mng_get_long(&p[4]);
3672 if ((int) p[8] == PNG_RESOLUTION_METER)
3674 image->units=PixelsPerCentimeterResolution;
3675 image->x_resolution=image->x_resolution/100.0f;
3676 image->y_resolution=image->y_resolution/100.0f;
3680 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3685 if (memcmp(type,mng_iCCP,4) == 0)
3689 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3696 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3698 if (memcmp(type,mng_IEND,4))
3708 Finish up reading image data:
3710 o read main image from color_blob.
3714 o if (color_type has alpha)
3715 if alpha_encoding is PNG
3716 read secondary image from alpha_blob via ReadPNG
3717 if alpha_encoding is JPEG
3718 read secondary image from alpha_blob via ReadJPEG
3722 o copy intensity of secondary image into
3723 opacity samples of main image.
3725 o destroy the secondary image.
3728 (void) CloseBlob(color_image);
3730 if (logging != MagickFalse)
3731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3732 " Reading jng_image from color_blob.");
3734 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3735 color_image->filename);
3737 color_image_info->ping=MagickFalse; /* To do: avoid this */
3738 jng_image=ReadImage(color_image_info,exception);
3740 if (jng_image == (Image *) NULL)
3741 return((Image *) NULL);
3743 (void) RelinquishUniqueFileResource(color_image->filename);
3744 color_image=DestroyImage(color_image);
3745 color_image_info=DestroyImageInfo(color_image_info);
3747 if (jng_image == (Image *) NULL)
3748 return((Image *) NULL);
3750 if (logging != MagickFalse)
3751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752 " Copying jng_image pixels to main image.");
3754 image->rows=jng_height;
3755 image->columns=jng_width;
3756 length=image->columns*sizeof(PixelPacket);
3758 for (y=0; y < (ssize_t) image->rows; y++)
3760 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3761 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3762 (void) CopyMagickMemory(q,s,length);
3764 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3768 jng_image=DestroyImage(jng_image);
3770 if (image_info->ping == MagickFalse)
3772 if (jng_color_type >= 12)
3774 if (jng_alpha_compression_method == 0)
3778 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3779 PNGType(data,mng_IEND);
3780 LogPNGChunk(logging,mng_IEND,0L);
3781 (void) WriteBlob(alpha_image,4,data);
3782 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3785 (void) CloseBlob(alpha_image);
3787 if (logging != MagickFalse)
3788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3789 " Reading opacity from alpha_blob.");
3791 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3792 "%s",alpha_image->filename);
3794 jng_image=ReadImage(alpha_image_info,exception);
3796 if (jng_image != (Image *) NULL)
3797 for (y=0; y < (ssize_t) image->rows; y++)
3799 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3801 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3803 if (image->matte != MagickFalse)
3804 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3805 q->opacity=(Quantum) QuantumRange-s->red;
3808 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3810 q->opacity=(Quantum) QuantumRange-s->red;
3811 if (q->opacity != OpaqueOpacity)
3812 image->matte=MagickTrue;
3815 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3818 (void) RelinquishUniqueFileResource(alpha_image->filename);
3819 alpha_image=DestroyImage(alpha_image);
3820 alpha_image_info=DestroyImageInfo(alpha_image_info);
3821 if (jng_image != (Image *) NULL)
3822 jng_image=DestroyImage(jng_image);
3826 /* Read the JNG image. */
3828 if (mng_info->mng_type == 0)
3830 mng_info->mng_width=jng_width;
3831 mng_info->mng_height=jng_height;
3834 if (image->page.width == 0 && image->page.height == 0)
3836 image->page.width=jng_width;
3837 image->page.height=jng_height;
3840 if (image->page.x == 0 && image->page.y == 0)
3842 image->page.x=mng_info->x_off[mng_info->object_id];
3843 image->page.y=mng_info->y_off[mng_info->object_id];
3848 image->page.y=mng_info->y_off[mng_info->object_id];
3851 mng_info->image_found++;
3852 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3853 2*GetBlobSize(image));
3855 if (logging != MagickFalse)
3856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3857 " exit ReadOneJNGImage()");
3863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3867 % R e a d J N G I m a g e %
3871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3873 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3874 % (including the 8-byte signature) and returns it. It allocates the memory
3875 % necessary for the new Image structure and returns a pointer to the new
3878 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3880 % The format of the ReadJNGImage method is:
3882 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3885 % A description of each parameter follows:
3887 % o image_info: the image info.
3889 % o exception: return any errors or warnings in this structure.
3893 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3908 magic_number[MaxTextExtent];
3916 assert(image_info != (const ImageInfo *) NULL);
3917 assert(image_info->signature == MagickSignature);
3918 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3919 assert(exception != (ExceptionInfo *) NULL);
3920 assert(exception->signature == MagickSignature);
3921 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3922 image=AcquireImage(image_info);
3923 mng_info=(MngInfo *) NULL;
3924 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3926 if (status == MagickFalse)
3927 return((Image *) NULL);
3929 if (LocaleCompare(image_info->magick,"JNG") != 0)
3930 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3932 /* Verify JNG signature. */
3934 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3936 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3937 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3939 /* Allocate a MngInfo structure. */
3941 have_mng_structure=MagickFalse;
3942 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3944 if (mng_info == (MngInfo *) NULL)
3945 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3947 /* Initialize members of the MngInfo structure. */
3949 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3950 have_mng_structure=MagickTrue;
3952 mng_info->image=image;
3954 image=ReadOneJNGImage(mng_info,image_info,exception);
3955 MngInfoFreeStruct(mng_info,&have_mng_structure);
3957 if (image == (Image *) NULL)
3959 if (IsImageObject(previous) != MagickFalse)
3961 (void) CloseBlob(previous);
3962 (void) DestroyImageList(previous);
3965 if (logging != MagickFalse)
3966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3967 "exit ReadJNGImage() with error");
3969 return((Image *) NULL);
3971 (void) CloseBlob(image);
3973 if (image->columns == 0 || image->rows == 0)
3975 if (logging != MagickFalse)
3976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 "exit ReadJNGImage() with error");
3979 ThrowReaderException(CorruptImageError,"CorruptImage");
3982 if (logging != MagickFalse)
3983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3989 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3992 page_geometry[MaxTextExtent];
4025 #if defined(MNG_INSERT_LAYERS)
4027 mng_background_color;
4030 register unsigned char
4045 #if defined(MNG_INSERT_LAYERS)
4050 volatile unsigned int
4051 #ifdef MNG_OBJECT_BUFFERS
4052 mng_background_object=0,
4054 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4057 default_frame_timeout,
4059 #if defined(MNG_INSERT_LAYERS)
4065 /* These delays are all measured in image ticks_per_second,
4066 * not in MNG ticks_per_second
4069 default_frame_delay,
4073 #if defined(MNG_INSERT_LAYERS)
4082 previous_fb.bottom=0;
4084 previous_fb.right=0;
4086 default_fb.bottom=0;
4090 /* Open image file. */
4092 assert(image_info != (const ImageInfo *) NULL);
4093 assert(image_info->signature == MagickSignature);
4094 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4095 assert(exception != (ExceptionInfo *) NULL);
4096 assert(exception->signature == MagickSignature);
4097 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4098 image=AcquireImage(image_info);
4099 mng_info=(MngInfo *) NULL;
4100 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4102 if (status == MagickFalse)
4103 return((Image *) NULL);
4105 first_mng_object=MagickFalse;
4107 have_mng_structure=MagickFalse;
4109 /* Allocate a MngInfo structure. */
4111 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4113 if (mng_info == (MngInfo *) NULL)
4114 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4116 /* Initialize members of the MngInfo structure. */
4118 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4119 mng_info->image=image;
4120 have_mng_structure=MagickTrue;
4122 if (LocaleCompare(image_info->magick,"MNG") == 0)
4125 magic_number[MaxTextExtent];
4127 /* Verify MNG signature. */
4128 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4129 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4130 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4132 /* Initialize some nonzero members of the MngInfo structure. */
4133 for (i=0; i < MNG_MAX_OBJECTS; i++)
4135 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4136 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4138 mng_info->exists[0]=MagickTrue;
4141 first_mng_object=MagickTrue;
4143 #if defined(MNG_INSERT_LAYERS)
4144 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4146 default_frame_delay=0;
4147 default_frame_timeout=0;
4150 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4152 skip_to_iend=MagickFalse;
4153 term_chunk_found=MagickFalse;
4154 mng_info->framing_mode=1;
4155 #if defined(MNG_INSERT_LAYERS)
4156 mandatory_back=MagickFalse;
4158 #if defined(MNG_INSERT_LAYERS)
4159 mng_background_color=image->background_color;
4161 default_fb=mng_info->frame;
4162 previous_fb=mng_info->frame;
4166 type[MaxTextExtent];
4168 if (LocaleCompare(image_info->magick,"MNG") == 0)
4177 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4178 length=ReadBlobMSBLong(image);
4179 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4181 if (logging != MagickFalse)
4182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4184 type[0],type[1],type[2],type[3],(double) length);
4186 if (length > PNG_UINT_31_MAX)
4190 ThrowReaderException(CorruptImageError,"CorruptImage");
4193 chunk=(unsigned char *) NULL;
4197 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4199 if (chunk == (unsigned char *) NULL)
4200 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4202 for (i=0; i < (ssize_t) length; i++)
4203 chunk[i]=(unsigned char) ReadBlobByte(image);
4208 (void) ReadBlobMSBLong(image); /* read crc word */
4210 #if !defined(JNG_SUPPORTED)
4211 if (memcmp(type,mng_JHDR,4) == 0)
4213 skip_to_iend=MagickTrue;
4215 if (mng_info->jhdr_warning == 0)
4216 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4217 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4219 mng_info->jhdr_warning++;
4222 if (memcmp(type,mng_DHDR,4) == 0)
4224 skip_to_iend=MagickTrue;
4226 if (mng_info->dhdr_warning == 0)
4227 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4228 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4230 mng_info->dhdr_warning++;
4232 if (memcmp(type,mng_MEND,4) == 0)
4237 if (memcmp(type,mng_IEND,4) == 0)
4238 skip_to_iend=MagickFalse;
4241 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4243 if (logging != MagickFalse)
4244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4250 if (memcmp(type,mng_MHDR,4) == 0)
4252 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4253 (p[2] << 8) | p[3]);
4255 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4256 (p[6] << 8) | p[7]);
4258 if (logging != MagickFalse)
4260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4261 " MNG width: %.20g",(double) mng_info->mng_width);
4262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4263 " MNG height: %.20g",(double) mng_info->mng_height);
4267 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4269 if (mng_info->ticks_per_second == 0)
4270 default_frame_delay=0;
4273 default_frame_delay=1UL*image->ticks_per_second/
4274 mng_info->ticks_per_second;
4276 frame_delay=default_frame_delay;
4282 simplicity=(size_t) mng_get_long(p);
4285 mng_type=1; /* Full MNG */
4287 if ((simplicity != 0) && ((simplicity | 11) == 11))
4288 mng_type=2; /* LC */
4290 if ((simplicity != 0) && ((simplicity | 9) == 9))
4291 mng_type=3; /* VLC */
4293 #if defined(MNG_INSERT_LAYERS)
4295 insert_layers=MagickTrue;
4297 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4299 /* Allocate next image structure. */
4300 AcquireNextImage(image_info,image);
4302 if (GetNextImageInList(image) == (Image *) NULL)
4303 return((Image *) NULL);
4305 image=SyncNextImageInList(image);
4306 mng_info->image=image;
4309 if ((mng_info->mng_width > 65535L) ||
4310 (mng_info->mng_height > 65535L))
4311 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4313 (void) FormatMagickString(page_geometry,MaxTextExtent,
4314 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4315 mng_info->mng_height);
4317 mng_info->frame.left=0;
4318 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4319 mng_info->frame.top=0;
4320 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4321 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4323 for (i=0; i < MNG_MAX_OBJECTS; i++)
4324 mng_info->object_clip[i]=mng_info->frame;
4326 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4330 if (memcmp(type,mng_TERM,4) == 0)
4341 final_delay=(png_uint_32) mng_get_long(&p[2]);
4342 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4344 if (mng_iterations == PNG_UINT_31_MAX)
4347 image->iterations=mng_iterations;
4348 term_chunk_found=MagickTrue;
4351 if (logging != MagickFalse)
4353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4354 " repeat=%d",repeat);
4356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4357 " final_delay=%.20g",(double) final_delay);
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " image->iterations=%.20g",(double) image->iterations);
4363 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4366 if (memcmp(type,mng_DEFI,4) == 0)
4369 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4370 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4373 object_id=(p[0] << 8) | p[1];
4375 if (mng_type == 2 && object_id != 0)
4376 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4377 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4380 if (object_id > MNG_MAX_OBJECTS)
4383 Instead ofsuing a warning we should allocate a larger
4384 MngInfo structure and continue.
4386 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4387 CoderError,"object id too large","`%s'",image->filename);
4388 object_id=MNG_MAX_OBJECTS;
4391 if (mng_info->exists[object_id])
4392 if (mng_info->frozen[object_id])
4394 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4395 (void) ThrowMagickException(&image->exception,
4396 GetMagickModule(),CoderError,
4397 "DEFI cannot redefine a frozen MNG object","`%s'",
4402 mng_info->exists[object_id]=MagickTrue;
4405 mng_info->invisible[object_id]=p[2];
4408 Extract object offset info.
4412 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4413 (p[5] << 16) | (p[6] << 8) | p[7]);
4415 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4416 (p[9] << 16) | (p[10] << 8) | p[11]);
4418 if (logging != MagickFalse)
4420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4421 " x_off[%d]: %.20g",object_id,(double)
4422 mng_info->x_off[object_id]);
4424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425 " y_off[%d]: %.20g",object_id,(double)
4426 mng_info->y_off[object_id]);
4431 Extract object clipping info.
4434 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4437 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4440 if (memcmp(type,mng_bKGD,4) == 0)
4442 mng_info->have_global_bkgd=MagickFalse;
4446 mng_info->mng_global_bkgd.red=
4447 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4449 mng_info->mng_global_bkgd.green=
4450 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4452 mng_info->mng_global_bkgd.blue=
4453 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4455 mng_info->have_global_bkgd=MagickTrue;
4458 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4461 if (memcmp(type,mng_BACK,4) == 0)
4463 #if defined(MNG_INSERT_LAYERS)
4465 mandatory_back=p[6];
4470 if (mandatory_back && length > 5)
4472 mng_background_color.red=
4473 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4475 mng_background_color.green=
4476 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4478 mng_background_color.blue=
4479 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4481 mng_background_color.opacity=OpaqueOpacity;
4484 #ifdef MNG_OBJECT_BUFFERS
4486 mng_background_object=(p[7] << 8) | p[8];
4489 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4493 if (memcmp(type,mng_PLTE,4) == 0)
4495 /* Read global PLTE. */
4497 if (length && (length < 769))
4499 if (mng_info->global_plte == (png_colorp) NULL)
4500 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4501 sizeof(*mng_info->global_plte));
4503 for (i=0; i < (ssize_t) (length/3); i++)
4505 mng_info->global_plte[i].red=p[3*i];
4506 mng_info->global_plte[i].green=p[3*i+1];
4507 mng_info->global_plte[i].blue=p[3*i+2];
4510 mng_info->global_plte_length=(unsigned int) (length/3);
4513 for ( ; i < 256; i++)
4515 mng_info->global_plte[i].red=i;
4516 mng_info->global_plte[i].green=i;
4517 mng_info->global_plte[i].blue=i;
4521 mng_info->global_plte_length=256;
4524 mng_info->global_plte_length=0;
4526 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4530 if (memcmp(type,mng_tRNS,4) == 0)
4532 /* read global tRNS */
4535 for (i=0; i < (ssize_t) length; i++)
4536 mng_info->global_trns[i]=p[i];
4539 for ( ; i < 256; i++)
4540 mng_info->global_trns[i]=255;
4542 mng_info->global_trns_length=(unsigned int) length;
4543 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4546 if (memcmp(type,mng_gAMA,4) == 0)
4553 igamma=mng_get_long(p);
4554 mng_info->global_gamma=((float) igamma)*0.00001;
4555 mng_info->have_global_gama=MagickTrue;
4559 mng_info->have_global_gama=MagickFalse;
4561 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4565 if (memcmp(type,mng_cHRM,4) == 0)
4567 /* Read global cHRM */
4571 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4572 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4573 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4574 mng_info->global_chrm.red_primary.y=0.00001*
4575 mng_get_long(&p[12]);
4576 mng_info->global_chrm.green_primary.x=0.00001*
4577 mng_get_long(&p[16]);
4578 mng_info->global_chrm.green_primary.y=0.00001*
4579 mng_get_long(&p[20]);
4580 mng_info->global_chrm.blue_primary.x=0.00001*
4581 mng_get_long(&p[24]);
4582 mng_info->global_chrm.blue_primary.y=0.00001*
4583 mng_get_long(&p[28]);
4584 mng_info->have_global_chrm=MagickTrue;
4587 mng_info->have_global_chrm=MagickFalse;
4589 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4593 if (memcmp(type,mng_sRGB,4) == 0)
4600 mng_info->global_srgb_intent=
4601 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4602 mng_info->have_global_srgb=MagickTrue;
4605 mng_info->have_global_srgb=MagickFalse;
4607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4611 if (memcmp(type,mng_iCCP,4) == 0)
4619 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4624 if (memcmp(type,mng_FRAM,4) == 0)
4627 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4628 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4631 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4632 image->delay=frame_delay;
4634 frame_delay=default_frame_delay;
4635 frame_timeout=default_frame_timeout;
4640 mng_info->framing_mode=p[0];
4642 if (logging != MagickFalse)
4643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4644 " Framing_mode=%d",mng_info->framing_mode);
4648 /* Note the delay and frame clipping boundaries. */
4650 p++; /* framing mode */
4652 while (*p && ((p-chunk) < (ssize_t) length))
4653 p++; /* frame name */
4655 p++; /* frame name terminator */
4657 if ((p-chunk) < (ssize_t) (length-4))
4664 change_delay=(*p++);
4665 change_timeout=(*p++);
4666 change_clipping=(*p++);
4667 p++; /* change_sync */
4671 frame_delay=1UL*image->ticks_per_second*
4674 if (mng_info->ticks_per_second != 0)
4675 frame_delay/=mng_info->ticks_per_second;
4678 frame_delay=PNG_UINT_31_MAX;
4680 if (change_delay == 2)
4681 default_frame_delay=frame_delay;
4685 if (logging != MagickFalse)
4686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4687 " Framing_delay=%.20g",(double) frame_delay);
4692 frame_timeout=1UL*image->ticks_per_second*
4695 if (mng_info->ticks_per_second != 0)
4696 frame_timeout/=mng_info->ticks_per_second;
4699 frame_timeout=PNG_UINT_31_MAX;
4701 if (change_delay == 2)
4702 default_frame_timeout=frame_timeout;
4706 if (logging != MagickFalse)
4707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4708 " Framing_timeout=%.20g",(double) frame_timeout);
4711 if (change_clipping)
4713 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4717 if (logging != MagickFalse)
4718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4719 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4720 (double) fb.left,(double) fb.right,(double) fb.top,
4721 (double) fb.bottom);
4723 if (change_clipping == 2)
4729 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4731 subframe_width=(size_t) (mng_info->clip.right
4732 -mng_info->clip.left);
4734 subframe_height=(size_t) (mng_info->clip.bottom
4735 -mng_info->clip.top);
4737 Insert a background layer behind the frame if framing_mode is 4.
4739 #if defined(MNG_INSERT_LAYERS)
4740 if (logging != MagickFalse)
4741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4742 " subframe_width=%.20g, subframe_height=%.20g",(double)
4743 subframe_width,(double) subframe_height);
4745 if (insert_layers && (mng_info->framing_mode == 4) &&
4746 (subframe_width) && (subframe_height))
4748 /* Allocate next image structure. */
4749 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4751 AcquireNextImage(image_info,image);
4753 if (GetNextImageInList(image) == (Image *) NULL)
4755 image=DestroyImageList(image);
4756 MngInfoFreeStruct(mng_info,&have_mng_structure);
4757 return((Image *) NULL);
4760 image=SyncNextImageInList(image);
4763 mng_info->image=image;
4765 if (term_chunk_found)
4767 image->start_loop=MagickTrue;
4768 image->iterations=mng_iterations;
4769 term_chunk_found=MagickFalse;
4773 image->start_loop=MagickFalse;
4775 image->columns=subframe_width;
4776 image->rows=subframe_height;
4777 image->page.width=subframe_width;
4778 image->page.height=subframe_height;
4779 image->page.x=mng_info->clip.left;
4780 image->page.y=mng_info->clip.top;
4781 image->background_color=mng_background_color;
4782 image->matte=MagickFalse;
4784 (void) SetImageBackgroundColor(image);
4786 if (logging != MagickFalse)
4787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4788 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4789 (double) mng_info->clip.left,(double) mng_info->clip.right,
4790 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4796 if (memcmp(type,mng_CLIP,4) == 0)
4805 first_object=(p[0] << 8) | p[1];
4806 last_object=(p[2] << 8) | p[3];
4808 for (i=(int) first_object; i <= (int) last_object; i++)
4810 if (mng_info->exists[i] && !mng_info->frozen[i])
4815 box=mng_info->object_clip[i];
4816 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4823 if (memcmp(type,mng_SAVE,4) == 0)
4825 for (i=1; i < MNG_MAX_OBJECTS; i++)
4826 if (mng_info->exists[i])
4828 mng_info->frozen[i]=MagickTrue;
4829 #ifdef MNG_OBJECT_BUFFERS
4830 if (mng_info->ob[i] != (MngBuffer *) NULL)
4831 mng_info->ob[i]->frozen=MagickTrue;
4836 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4841 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4843 /* Read DISC or SEEK. */
4845 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4847 for (i=1; i < MNG_MAX_OBJECTS; i++)
4848 MngInfoDiscardObject(mng_info,i);
4856 for (j=0; j < (ssize_t) length; j+=2)
4858 i=p[j] << 8 | p[j+1];
4859 MngInfoDiscardObject(mng_info,i);
4864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4869 if (memcmp(type,mng_MOVE,4) == 0)
4877 first_object=(p[0] << 8) | p[1];
4878 last_object=(p[2] << 8) | p[3];
4879 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4881 if (mng_info->exists[i] && !mng_info->frozen[i])
4889 old_pair.a=mng_info->x_off[i];
4890 old_pair.b=mng_info->y_off[i];
4891 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4892 mng_info->x_off[i]=new_pair.a;
4893 mng_info->y_off[i]=new_pair.b;
4897 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4901 if (memcmp(type,mng_LOOP,4) == 0)
4903 ssize_t loop_iters=1;
4904 loop_level=chunk[0];
4905 mng_info->loop_active[loop_level]=1; /* mark loop active */
4907 /* Record starting point. */
4908 loop_iters=mng_get_long(&chunk[1]);
4910 if (logging != MagickFalse)
4911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4912 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4913 (double) loop_iters);
4915 if (loop_iters == 0)
4916 skipping_loop=loop_level;
4920 mng_info->loop_jump[loop_level]=TellBlob(image);
4921 mng_info->loop_count[loop_level]=loop_iters;
4924 mng_info->loop_iteration[loop_level]=0;
4925 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4929 if (memcmp(type,mng_ENDL,4) == 0)
4931 loop_level=chunk[0];
4933 if (skipping_loop > 0)
4935 if (skipping_loop == loop_level)
4938 Found end of zero-iteration loop.
4941 mng_info->loop_active[loop_level]=0;
4947 if (mng_info->loop_active[loop_level] == 1)
4949 mng_info->loop_count[loop_level]--;
4950 mng_info->loop_iteration[loop_level]++;
4952 if (logging != MagickFalse)
4953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4954 " ENDL: LOOP level %.20g has %.20g remaining iters ",
4955 (double) loop_level,(double)
4956 mng_info->loop_count[loop_level]);
4958 if (mng_info->loop_count[loop_level] != 0)
4960 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4964 ThrowReaderException(CorruptImageError,
4965 "ImproperImageHeader");
4976 mng_info->loop_active[loop_level]=0;
4978 for (i=0; i < loop_level; i++)
4979 if (mng_info->loop_active[i] == 1)
4980 last_level=(short) i;
4981 loop_level=last_level;
4986 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4990 if (memcmp(type,mng_CLON,4) == 0)
4992 if (mng_info->clon_warning == 0)
4993 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4994 CoderError,"CLON is not implemented yet","`%s'",
4997 mng_info->clon_warning++;
5000 if (memcmp(type,mng_MAGN,4) == 0)
5015 magn_first=(p[0] << 8) | p[1];
5021 magn_last=(p[2] << 8) | p[3];
5024 magn_last=magn_first;
5025 #ifndef MNG_OBJECT_BUFFERS
5026 if (magn_first || magn_last)
5027 if (mng_info->magn_warning == 0)
5029 (void) ThrowMagickException(&image->exception,
5030 GetMagickModule(),CoderError,
5031 "MAGN is not implemented yet for nonzero objects",
5032 "`%s'",image->filename);
5034 mng_info->magn_warning++;
5044 magn_mx=(p[5] << 8) | p[6];
5053 magn_my=(p[7] << 8) | p[8];
5062 magn_ml=(p[9] << 8) | p[10];
5071 magn_mr=(p[11] << 8) | p[12];
5080 magn_mt=(p[13] << 8) | p[14];
5089 magn_mb=(p[15] << 8) | p[16];
5101 magn_methy=magn_methx;
5104 if (magn_methx > 5 || magn_methy > 5)
5105 if (mng_info->magn_warning == 0)
5107 (void) ThrowMagickException(&image->exception,
5108 GetMagickModule(),CoderError,
5109 "Unknown MAGN method in MNG datastream","`%s'",
5112 mng_info->magn_warning++;
5114 #ifdef MNG_OBJECT_BUFFERS
5115 /* Magnify existing objects in the range magn_first to magn_last */
5117 if (magn_first == 0 || magn_last == 0)
5119 /* Save the magnification factors for object 0 */
5120 mng_info->magn_mb=magn_mb;
5121 mng_info->magn_ml=magn_ml;
5122 mng_info->magn_mr=magn_mr;
5123 mng_info->magn_mt=magn_mt;
5124 mng_info->magn_mx=magn_mx;
5125 mng_info->magn_my=magn_my;
5126 mng_info->magn_methx=magn_methx;
5127 mng_info->magn_methy=magn_methy;
5131 if (memcmp(type,mng_PAST,4) == 0)
5133 if (mng_info->past_warning == 0)
5134 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5135 CoderError,"PAST is not implemented yet","`%s'",
5138 mng_info->past_warning++;
5141 if (memcmp(type,mng_SHOW,4) == 0)
5143 if (mng_info->show_warning == 0)
5144 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5145 CoderError,"SHOW is not implemented yet","`%s'",
5148 mng_info->show_warning++;
5151 if (memcmp(type,mng_sBIT,4) == 0)
5154 mng_info->have_global_sbit=MagickFalse;
5158 mng_info->global_sbit.gray=p[0];
5159 mng_info->global_sbit.red=p[0];
5160 mng_info->global_sbit.green=p[1];
5161 mng_info->global_sbit.blue=p[2];
5162 mng_info->global_sbit.alpha=p[3];
5163 mng_info->have_global_sbit=MagickTrue;
5166 if (memcmp(type,mng_pHYs,4) == 0)
5170 mng_info->global_x_pixels_per_unit=
5171 (size_t) mng_get_long(p);
5172 mng_info->global_y_pixels_per_unit=
5173 (size_t) mng_get_long(&p[4]);
5174 mng_info->global_phys_unit_type=p[8];
5175 mng_info->have_global_phys=MagickTrue;
5179 mng_info->have_global_phys=MagickFalse;
5181 if (memcmp(type,mng_pHYg,4) == 0)
5183 if (mng_info->phyg_warning == 0)
5184 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5185 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5187 mng_info->phyg_warning++;
5189 if (memcmp(type,mng_BASI,4) == 0)
5191 skip_to_iend=MagickTrue;
5193 if (mng_info->basi_warning == 0)
5194 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5195 CoderError,"BASI is not implemented yet","`%s'",
5198 mng_info->basi_warning++;
5199 #ifdef MNG_BASI_SUPPORTED
5200 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5201 (p[2] << 8) | p[3]);
5202 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5203 (p[6] << 8) | p[7]);
5204 basi_color_type=p[8];
5205 basi_compression_method=p[9];
5206 basi_filter_type=p[10];
5207 basi_interlace_method=p[11];
5209 basi_red=(p[12] << 8) & p[13];
5215 basi_green=(p[14] << 8) & p[15];
5221 basi_blue=(p[16] << 8) & p[17];
5227 basi_alpha=(p[18] << 8) & p[19];
5231 if (basi_sample_depth == 16)
5238 basi_viewable=p[20];
5244 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5248 if (memcmp(type,mng_IHDR,4)
5249 #if defined(JNG_SUPPORTED)
5250 && memcmp(type,mng_JHDR,4)
5254 /* Not an IHDR or JHDR chunk */
5256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5261 if (logging != MagickFalse)
5262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5263 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5265 mng_info->exists[object_id]=MagickTrue;
5266 mng_info->viewable[object_id]=MagickTrue;
5268 if (mng_info->invisible[object_id])
5270 if (logging != MagickFalse)
5271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5272 " Skipping invisible object");
5274 skip_to_iend=MagickTrue;
5275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5278 #if defined(MNG_INSERT_LAYERS)
5280 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5282 image_width=(size_t) mng_get_long(p);
5283 image_height=(size_t) mng_get_long(&p[4]);
5285 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5288 Insert a transparent background layer behind the entire animation
5289 if it is not full screen.
5291 #if defined(MNG_INSERT_LAYERS)
5292 if (insert_layers && mng_type && first_mng_object)
5294 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5295 (image_width < mng_info->mng_width) ||
5296 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5297 (image_height < mng_info->mng_height) ||
5298 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5300 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5303 Allocate next image structure.
5305 AcquireNextImage(image_info,image);
5307 if (GetNextImageInList(image) == (Image *) NULL)
5309 image=DestroyImageList(image);
5310 MngInfoFreeStruct(mng_info,&have_mng_structure);
5311 return((Image *) NULL);
5314 image=SyncNextImageInList(image);
5316 mng_info->image=image;
5318 if (term_chunk_found)
5320 image->start_loop=MagickTrue;
5321 image->iterations=mng_iterations;
5322 term_chunk_found=MagickFalse;
5326 image->start_loop=MagickFalse;
5328 /* Make a background rectangle. */
5331 image->columns=mng_info->mng_width;
5332 image->rows=mng_info->mng_height;
5333 image->page.width=mng_info->mng_width;
5334 image->page.height=mng_info->mng_height;
5337 image->background_color=mng_background_color;
5338 (void) SetImageBackgroundColor(image);
5339 if (logging != MagickFalse)
5340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5341 " Inserted transparent background layer, W=%.20g, H=%.20g",
5342 (double) mng_info->mng_width,(double) mng_info->mng_height);
5346 Insert a background layer behind the upcoming image if
5347 framing_mode is 3, and we haven't already inserted one.
5349 if (insert_layers && (mng_info->framing_mode == 3) &&
5350 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5351 (simplicity & 0x08)))
5353 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5356 Allocate next image structure.
5358 AcquireNextImage(image_info,image);
5360 if (GetNextImageInList(image) == (Image *) NULL)
5362 image=DestroyImageList(image);
5363 MngInfoFreeStruct(mng_info,&have_mng_structure);
5364 return((Image *) NULL);
5367 image=SyncNextImageInList(image);
5370 mng_info->image=image;
5372 if (term_chunk_found)
5374 image->start_loop=MagickTrue;
5375 image->iterations=mng_iterations;
5376 term_chunk_found=MagickFalse;
5380 image->start_loop=MagickFalse;
5383 image->columns=subframe_width;
5384 image->rows=subframe_height;
5385 image->page.width=subframe_width;
5386 image->page.height=subframe_height;
5387 image->page.x=mng_info->clip.left;
5388 image->page.y=mng_info->clip.top;
5389 image->background_color=mng_background_color;
5390 image->matte=MagickFalse;
5391 (void) SetImageBackgroundColor(image);
5393 if (logging != MagickFalse)
5394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5395 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5396 (double) mng_info->clip.left,(double) mng_info->clip.right,
5397 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5399 #endif /* MNG_INSERT_LAYERS */
5400 first_mng_object=MagickFalse;
5402 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5405 Allocate next image structure.
5407 AcquireNextImage(image_info,image);
5409 if (GetNextImageInList(image) == (Image *) NULL)
5411 image=DestroyImageList(image);
5412 MngInfoFreeStruct(mng_info,&have_mng_structure);
5413 return((Image *) NULL);
5416 image=SyncNextImageInList(image);
5418 mng_info->image=image;
5419 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5420 GetBlobSize(image));
5422 if (status == MagickFalse)
5425 if (term_chunk_found)
5427 image->start_loop=MagickTrue;
5428 term_chunk_found=MagickFalse;
5432 image->start_loop=MagickFalse;
5434 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5436 image->delay=frame_delay;
5437 frame_delay=default_frame_delay;
5443 image->page.width=mng_info->mng_width;
5444 image->page.height=mng_info->mng_height;
5445 image->page.x=mng_info->x_off[object_id];
5446 image->page.y=mng_info->y_off[object_id];
5447 image->iterations=mng_iterations;
5450 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5453 if (logging != MagickFalse)
5454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5455 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5458 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5461 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5465 mng_info->image=image;
5466 mng_info->mng_type=mng_type;
5467 mng_info->object_id=object_id;
5469 if (memcmp(type,mng_IHDR,4) == 0)
5470 image=ReadOnePNGImage(mng_info,image_info,exception);
5472 #if defined(JNG_SUPPORTED)
5474 image=ReadOneJNGImage(mng_info,image_info,exception);
5477 if (image == (Image *) NULL)
5479 if (IsImageObject(previous) != MagickFalse)
5481 (void) DestroyImageList(previous);
5482 (void) CloseBlob(previous);
5485 MngInfoFreeStruct(mng_info,&have_mng_structure);
5486 return((Image *) NULL);
5489 if (image->columns == 0 || image->rows == 0)
5491 (void) CloseBlob(image);
5492 image=DestroyImageList(image);
5493 MngInfoFreeStruct(mng_info,&have_mng_structure);
5494 return((Image *) NULL);
5497 mng_info->image=image;
5504 if (mng_info->magn_methx || mng_info->magn_methy)
5510 if (logging != MagickFalse)
5511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5512 " Processing MNG MAGN chunk");
5514 if (mng_info->magn_methx == 1)
5516 magnified_width=mng_info->magn_ml;
5518 if (image->columns > 1)
5519 magnified_width += mng_info->magn_mr;
5521 if (image->columns > 2)
5522 magnified_width += (png_uint_32)
5523 ((image->columns-2)*(mng_info->magn_mx));
5528 magnified_width=(png_uint_32) image->columns;
5530 if (image->columns > 1)
5531 magnified_width += mng_info->magn_ml-1;
5533 if (image->columns > 2)
5534 magnified_width += mng_info->magn_mr-1;
5536 if (image->columns > 3)
5537 magnified_width += (png_uint_32)
5538 ((image->columns-3)*(mng_info->magn_mx-1));
5541 if (mng_info->magn_methy == 1)
5543 magnified_height=mng_info->magn_mt;
5545 if (image->rows > 1)
5546 magnified_height += mng_info->magn_mb;
5548 if (image->rows > 2)
5549 magnified_height += (png_uint_32)
5550 ((image->rows-2)*(mng_info->magn_my));
5555 magnified_height=(png_uint_32) image->rows;
5557 if (image->rows > 1)
5558 magnified_height += mng_info->magn_mt-1;
5560 if (image->rows > 2)
5561 magnified_height += mng_info->magn_mb-1;
5563 if (image->rows > 3)
5564 magnified_height += (png_uint_32)
5565 ((image->rows-3)*(mng_info->magn_my-1));
5568 if (magnified_height > image->rows ||
5569 magnified_width > image->columns)
5584 register PixelPacket
5596 /* Allocate next image structure. */
5598 if (logging != MagickFalse)
5599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5600 " Allocate magnified image");
5602 AcquireNextImage(image_info,image);
5604 if (GetNextImageInList(image) == (Image *) NULL)
5606 image=DestroyImageList(image);
5607 MngInfoFreeStruct(mng_info,&have_mng_structure);
5608 return((Image *) NULL);
5611 large_image=SyncNextImageInList(image);
5613 large_image->columns=magnified_width;
5614 large_image->rows=magnified_height;
5616 magn_methx=mng_info->magn_methx;
5617 magn_methy=mng_info->magn_methy;
5619 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5620 #define QM unsigned short
5621 if (magn_methx != 1 || magn_methy != 1)
5624 Scale pixels to unsigned shorts to prevent
5625 overflow of intermediate values of interpolations
5627 for (y=0; y < (ssize_t) image->rows; y++)
5629 q=GetAuthenticPixels(image,0,y,image->columns,1,
5632 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5634 q->red=ScaleQuantumToShort(q->red);
5635 q->green=ScaleQuantumToShort(q->green);
5636 q->blue=ScaleQuantumToShort(q->blue);
5637 q->opacity=ScaleQuantumToShort(q->opacity);
5641 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5649 if (image->matte != MagickFalse)
5650 (void) SetImageBackgroundColor(large_image);
5654 large_image->background_color.opacity=OpaqueOpacity;
5655 (void) SetImageBackgroundColor(large_image);
5657 if (magn_methx == 4)
5660 if (magn_methx == 5)
5663 if (magn_methy == 4)
5666 if (magn_methy == 5)
5670 /* magnify the rows into the right side of the large image */
5672 if (logging != MagickFalse)
5673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5674 " Magnify the rows to %.20g",(double) large_image->rows);
5675 m=(ssize_t) mng_info->magn_mt;
5677 length=(size_t) image->columns;
5678 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5679 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5681 if ((prev == (PixelPacket *) NULL) ||
5682 (next == (PixelPacket *) NULL))
5684 image=DestroyImageList(image);
5685 MngInfoFreeStruct(mng_info,&have_mng_structure);
5686 ThrowReaderException(ResourceLimitError,
5687 "MemoryAllocationFailed");
5690 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5691 (void) CopyMagickMemory(next,n,length);
5693 for (y=0; y < (ssize_t) image->rows; y++)
5696 m=(ssize_t) mng_info->magn_mt;
5698 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5699 m=(ssize_t) mng_info->magn_mb;
5701 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5702 m=(ssize_t) mng_info->magn_mb;
5704 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5708 m=(ssize_t) mng_info->magn_my;
5714 if (y < (ssize_t) image->rows-1)
5716 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5718 (void) CopyMagickMemory(next,n,length);
5721 for (i=0; i < m; i++, yy++)
5723 register PixelPacket
5726 assert(yy < (ssize_t) large_image->rows);
5729 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5731 q+=(large_image->columns-image->columns);
5733 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5735 /* To do: get color as function of indexes[x] */
5737 if (image->storage_class == PseudoClass)
5742 if (magn_methy <= 1)
5744 *q=(*pixels); /* replicate previous */
5747 else if (magn_methy == 2 || magn_methy == 4)
5755 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5756 -(*pixels).red)+m))/((ssize_t) (m*2))
5758 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5759 -(*pixels).green)+m))/((ssize_t) (m*2))
5761 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5762 -(*pixels).blue)+m))/((ssize_t) (m*2))
5765 if (image->matte != MagickFalse)
5766 (*q).opacity=(QM) (((ssize_t)
5768 -(*pixels).opacity)+m))
5769 /((ssize_t) (m*2))+(*pixels).opacity);
5772 if (magn_methy == 4)
5774 /* Replicate nearest */
5775 if (i <= ((m+1) << 1))
5776 (*q).opacity=(*pixels).opacity+0;
5778 (*q).opacity=(*n).opacity+0;
5782 else /* if (magn_methy == 3 || magn_methy == 5) */
5784 /* Replicate nearest */
5785 if (i <= ((m+1) << 1))
5791 if (magn_methy == 5)
5793 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5794 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5795 +(*pixels).opacity);
5803 if (SyncAuthenticPixels(large_image,exception) == 0)
5809 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5810 next=(PixelPacket *) RelinquishMagickMemory(next);
5812 length=image->columns;
5814 if (logging != MagickFalse)
5815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5816 " Delete original image");
5818 DeleteImageFromList(&image);
5822 mng_info->image=image;
5824 /* magnify the columns */
5825 if (logging != MagickFalse)
5826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5827 " Magnify the columns to %.20g",(double) image->columns);
5829 for (y=0; y < (ssize_t) image->rows; y++)
5831 register PixelPacket
5834 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5835 pixels=q+(image->columns-length);
5838 for (x=(ssize_t) (image->columns-length);
5839 x < (ssize_t) image->columns; x++)
5841 if (x == (ssize_t) (image->columns-length))
5842 m=(ssize_t) mng_info->magn_ml;
5844 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5845 m=(ssize_t) mng_info->magn_mr;
5847 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5848 m=(ssize_t) mng_info->magn_mr;
5850 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5854 m=(ssize_t) mng_info->magn_mx;
5856 for (i=0; i < m; i++)
5858 if (magn_methx <= 1)
5860 /* replicate previous */
5864 else if (magn_methx == 2 || magn_methx == 4)
5872 (*q).red=(QM) ((2*i*((*n).red
5874 /((ssize_t) (m*2))+(*pixels).red);
5875 (*q).green=(QM) ((2*i*((*n).green
5877 +m)/((ssize_t) (m*2))+(*pixels).green);
5878 (*q).blue=(QM) ((2*i*((*n).blue
5880 /((ssize_t) (m*2))+(*pixels).blue);
5881 if (image->matte != MagickFalse)
5882 (*q).opacity=(QM) ((2*i*((*n).opacity
5883 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5884 +(*pixels).opacity);
5887 if (magn_methx == 4)
5889 /* Replicate nearest */
5890 if (i <= ((m+1) << 1))
5891 (*q).opacity=(*pixels).opacity+0;
5893 (*q).opacity=(*n).opacity+0;
5897 else /* if (magn_methx == 3 || magn_methx == 5) */
5899 /* Replicate nearest */
5900 if (i <= ((m+1) << 1))
5906 if (magn_methx == 5)
5909 (*q).opacity=(QM) ((2*i*((*n).opacity
5910 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5911 +(*pixels).opacity);
5920 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5923 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5924 if (magn_methx != 1 || magn_methy != 1)
5927 Rescale pixels to Quantum
5929 for (y=0; y < (ssize_t) image->rows; y++)
5931 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5933 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5935 q->red=ScaleShortToQuantum(q->red);
5936 q->green=ScaleShortToQuantum(q->green);
5937 q->blue=ScaleShortToQuantum(q->blue);
5938 q->opacity=ScaleShortToQuantum(q->opacity);
5942 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5947 if (logging != MagickFalse)
5948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5949 " Finished MAGN processing");
5954 Crop_box is with respect to the upper left corner of the MNG.
5956 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5957 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5958 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5959 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5960 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5961 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5962 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5963 if ((crop_box.left != (mng_info->image_box.left
5964 +mng_info->x_off[object_id])) ||
5965 (crop_box.right != (mng_info->image_box.right
5966 +mng_info->x_off[object_id])) ||
5967 (crop_box.top != (mng_info->image_box.top
5968 +mng_info->y_off[object_id])) ||
5969 (crop_box.bottom != (mng_info->image_box.bottom
5970 +mng_info->y_off[object_id])))
5972 if (logging != MagickFalse)
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5974 " Crop the PNG image");
5976 if ((crop_box.left < crop_box.right) &&
5977 (crop_box.top < crop_box.bottom))
5986 Crop_info is with respect to the upper left corner of
5989 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5990 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5991 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5992 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5993 image->page.width=image->columns;
5994 image->page.height=image->rows;
5997 im=CropImage(image,&crop_info,exception);
5999 if (im != (Image *) NULL)
6001 image->columns=im->columns;
6002 image->rows=im->rows;
6003 im=DestroyImage(im);
6004 image->page.width=image->columns;
6005 image->page.height=image->rows;
6006 image->page.x=crop_box.left;
6007 image->page.y=crop_box.top;
6014 No pixels in crop area. The MNG spec still requires
6015 a layer, though, so make a single transparent pixel in
6016 the top left corner.
6021 (void) SetImageBackgroundColor(image);
6022 image->page.width=1;
6023 image->page.height=1;
6028 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6029 image=mng_info->image;
6033 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6034 /* PNG does not handle depths greater than 16 so reduce it even
6037 if (image->depth > 16)
6041 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6042 if (LosslessReduceDepthOK(image) != MagickFalse)
6046 GetImageException(image,exception);
6048 if (image_info->number_scenes != 0)
6050 if (mng_info->scenes_found >
6051 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6055 if (logging != MagickFalse)
6056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057 " Finished reading image datastream.");
6059 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6061 (void) CloseBlob(image);
6063 if (logging != MagickFalse)
6064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6065 " Finished reading all image datastreams.");
6067 #if defined(MNG_INSERT_LAYERS)
6068 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6069 (mng_info->mng_height))
6072 Insert a background layer if nothing else was found.
6074 if (logging != MagickFalse)
6075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076 " No images found. Inserting a background layer.");
6078 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6081 Allocate next image structure.
6083 AcquireNextImage(image_info,image);
6084 if (GetNextImageInList(image) == (Image *) NULL)
6086 image=DestroyImageList(image);
6087 MngInfoFreeStruct(mng_info,&have_mng_structure);
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Allocation failed, returning NULL.");
6093 return((Image *) NULL);
6095 image=SyncNextImageInList(image);
6097 image->columns=mng_info->mng_width;
6098 image->rows=mng_info->mng_height;
6099 image->page.width=mng_info->mng_width;
6100 image->page.height=mng_info->mng_height;
6103 image->background_color=mng_background_color;
6104 image->matte=MagickFalse;
6106 if (image_info->ping == MagickFalse)
6107 (void) SetImageBackgroundColor(image);
6109 mng_info->image_found++;
6112 image->iterations=mng_iterations;
6114 if (mng_iterations == 1)
6115 image->start_loop=MagickTrue;
6117 while (GetPreviousImageInList(image) != (Image *) NULL)
6120 if (image_count > 10*mng_info->image_found)
6122 if (logging != MagickFalse)
6123 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6125 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6126 CoderError,"Linked list is corrupted, beginning of list not found",
6127 "`%s'",image_info->filename);
6129 return((Image *) NULL);
6132 image=GetPreviousImageInList(image);
6134 if (GetNextImageInList(image) == (Image *) NULL)
6136 if (logging != MagickFalse)
6137 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6139 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6140 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6141 image_info->filename);
6145 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6146 GetNextImageInList(image) ==
6149 if (logging != MagickFalse)
6150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6151 " First image null");
6153 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6154 CoderError,"image->next for first image is NULL but shouldn't be.",
6155 "`%s'",image_info->filename);
6158 if (mng_info->image_found == 0)
6160 if (logging != MagickFalse)
6161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6162 " No visible images found.");
6164 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6165 CoderError,"No visible images in file","`%s'",image_info->filename);
6167 if (image != (Image *) NULL)
6168 image=DestroyImageList(image);
6170 MngInfoFreeStruct(mng_info,&have_mng_structure);
6171 return((Image *) NULL);
6174 if (mng_info->ticks_per_second)
6175 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6176 final_delay/mng_info->ticks_per_second;
6179 image->start_loop=MagickTrue;
6181 /* Find final nonzero image delay */
6182 final_image_delay=0;
6184 while (GetNextImageInList(image) != (Image *) NULL)
6187 final_image_delay=image->delay;
6189 image=GetNextImageInList(image);
6192 if (final_delay < final_image_delay)
6193 final_delay=final_image_delay;
6195 image->delay=final_delay;
6197 if (logging != MagickFalse)
6198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6200 (double) final_delay);
6202 if (logging != MagickFalse)
6208 image=GetFirstImageInList(image);
6210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6211 " Before coalesce:");
6213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6214 " scene 0 delay=%.20g",(double) image->delay);
6216 while (GetNextImageInList(image) != (Image *) NULL)
6218 image=GetNextImageInList(image);
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6224 image=GetFirstImageInList(image);
6225 #ifdef MNG_COALESCE_LAYERS
6235 if (logging != MagickFalse)
6236 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6239 next_image=CoalesceImages(image,&image->exception);
6241 if (next_image == (Image *) NULL)
6242 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6244 image=DestroyImageList(image);
6247 for (next=image; next != (Image *) NULL; next=next_image)
6249 next->page.width=mng_info->mng_width;
6250 next->page.height=mng_info->mng_height;
6253 next->scene=scene++;
6254 next_image=GetNextImageInList(next);
6256 if (next_image == (Image *) NULL)
6259 if (next->delay == 0)
6262 next_image->previous=GetPreviousImageInList(next);
6263 if (GetPreviousImageInList(next) == (Image *) NULL)
6266 next->previous->next=next_image;
6267 next=DestroyImage(next);
6273 while (GetNextImageInList(image) != (Image *) NULL)
6274 image=GetNextImageInList(image);
6276 image->dispose=BackgroundDispose;
6278 if (logging != MagickFalse)
6284 image=GetFirstImageInList(image);
6286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6287 " After coalesce:");
6289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6290 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6291 (double) image->dispose);
6293 while (GetNextImageInList(image) != (Image *) NULL)
6295 image=GetNextImageInList(image);
6297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6298 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6299 (double) image->delay,(double) image->dispose);
6303 image=GetFirstImageInList(image);
6304 MngInfoFreeStruct(mng_info,&have_mng_structure);
6305 have_mng_structure=MagickFalse;
6307 if (logging != MagickFalse)
6308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6310 return(GetFirstImageInList(image));
6312 #else /* PNG_LIBPNG_VER > 10011 */
6313 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6315 printf("Your PNG library is too old: You have libpng-%s\n",
6316 PNG_LIBPNG_VER_STRING);
6318 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6319 "PNG library is too old","`%s'",image_info->filename);
6321 return(Image *) NULL;
6324 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6326 return(ReadPNGImage(image_info,exception));
6328 #endif /* PNG_LIBPNG_VER > 10011 */
6332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336 % R e g i s t e r P N G I m a g e %
6340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6342 % RegisterPNGImage() adds properties for the PNG image format to
6343 % the list of supported formats. The properties include the image format
6344 % tag, a method to read and/or write the format, whether the format
6345 % supports the saving of more than one frame to the same file or blob,
6346 % whether the format supports native in-memory I/O, and a brief
6347 % description of the format.
6349 % The format of the RegisterPNGImage method is:
6351 % size_t RegisterPNGImage(void)
6354 ModuleExport size_t RegisterPNGImage(void)
6357 version[MaxTextExtent];
6365 "See http://www.libpng.org/ for details about the PNG format."
6370 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6376 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6382 #if defined(PNG_LIBPNG_VER_STRING)
6383 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6384 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6386 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6388 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6389 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6394 entry=SetMagickInfo("MNG");
6395 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6397 #if defined(MAGICKCORE_PNG_DELEGATE)
6398 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6399 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6402 entry->magick=(IsImageFormatHandler *) IsMNG;
6403 entry->description=ConstantString("Multiple-image Network Graphics");
6405 if (*version != '\0')
6406 entry->version=ConstantString(version);
6408 entry->module=ConstantString("PNG");
6409 entry->note=ConstantString(MNGNote);
6410 (void) RegisterMagickInfo(entry);
6412 entry=SetMagickInfo("PNG");
6414 #if defined(MAGICKCORE_PNG_DELEGATE)
6415 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6416 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6419 entry->magick=(IsImageFormatHandler *) IsPNG;
6420 entry->adjoin=MagickFalse;
6421 entry->description=ConstantString("Portable Network Graphics");
6422 entry->module=ConstantString("PNG");
6424 if (*version != '\0')
6425 entry->version=ConstantString(version);
6427 entry->note=ConstantString(PNGNote);
6428 (void) RegisterMagickInfo(entry);
6430 entry=SetMagickInfo("PNG8");
6432 #if defined(MAGICKCORE_PNG_DELEGATE)
6433 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6434 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6437 entry->magick=(IsImageFormatHandler *) IsPNG;
6438 entry->adjoin=MagickFalse;
6439 entry->description=ConstantString(
6440 "8-bit indexed with optional binary transparency");
6441 entry->module=ConstantString("PNG");
6442 (void) RegisterMagickInfo(entry);
6444 entry=SetMagickInfo("PNG24");
6447 #if defined(ZLIB_VERSION)
6448 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6449 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6451 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6453 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6454 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6458 if (*version != '\0')
6459 entry->version=ConstantString(version);
6461 #if defined(MAGICKCORE_PNG_DELEGATE)
6462 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6463 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6466 entry->magick=(IsImageFormatHandler *) IsPNG;
6467 entry->adjoin=MagickFalse;
6468 entry->description=ConstantString("opaque 24-bit RGB");
6469 entry->module=ConstantString("PNG");
6470 (void) RegisterMagickInfo(entry);
6472 entry=SetMagickInfo("PNG32");
6474 #if defined(MAGICKCORE_PNG_DELEGATE)
6475 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6476 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6479 entry->magick=(IsImageFormatHandler *) IsPNG;
6480 entry->adjoin=MagickFalse;
6481 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6482 entry->module=ConstantString("PNG");
6483 (void) RegisterMagickInfo(entry);
6485 entry=SetMagickInfo("JNG");
6487 #if defined(JNG_SUPPORTED)
6488 #if defined(MAGICKCORE_PNG_DELEGATE)
6489 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6490 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6494 entry->magick=(IsImageFormatHandler *) IsJNG;
6495 entry->adjoin=MagickFalse;
6496 entry->description=ConstantString("JPEG Network Graphics");
6497 entry->module=ConstantString("PNG");
6498 entry->note=ConstantString(JNGNote);
6499 (void) RegisterMagickInfo(entry);
6501 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6502 ping_semaphore=AllocateSemaphoreInfo();
6505 return(MagickImageCoderSignature);
6509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6513 % U n r e g i s t e r P N G I m a g e %
6517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6519 % UnregisterPNGImage() removes format registrations made by the
6520 % PNG module from the list of supported formats.
6522 % The format of the UnregisterPNGImage method is:
6524 % UnregisterPNGImage(void)
6527 ModuleExport void UnregisterPNGImage(void)
6529 (void) UnregisterMagickInfo("MNG");
6530 (void) UnregisterMagickInfo("PNG");
6531 (void) UnregisterMagickInfo("PNG8");
6532 (void) UnregisterMagickInfo("PNG24");
6533 (void) UnregisterMagickInfo("PNG32");
6534 (void) UnregisterMagickInfo("JNG");
6536 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6537 if (ping_semaphore != (SemaphoreInfo *) NULL)
6538 DestroySemaphoreInfo(&ping_semaphore);
6542 #if defined(MAGICKCORE_PNG_DELEGATE)
6543 #if PNG_LIBPNG_VER > 10011
6545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6549 % W r i t e M N G I m a g e %
6553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6555 % WriteMNGImage() writes an image in the Portable Network Graphics
6556 % Group's "Multiple-image Network Graphics" encoded image format.
6558 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6560 % The format of the WriteMNGImage method is:
6562 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6564 % A description of each parameter follows.
6566 % o image_info: the image info.
6568 % o image: The image.
6571 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6572 % "To do" under ReadPNGImage):
6574 % Preserve all unknown and not-yet-handled known chunks found in input
6575 % PNG file and copy them into output PNG files according to the PNG
6578 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6580 % Improve selection of color type (use indexed-colour or indexed-colour
6581 % with tRNS when 256 or fewer unique RGBA values are present).
6583 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6584 % This will be complicated if we limit ourselves to generating MNG-LC
6585 % files. For now we ignore disposal method 3 and simply overlay the next
6588 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6589 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6590 % [mostly done 15 June 1999 but still need to take care of tRNS]
6592 % Check for identical sRGB and replace with a global sRGB (and remove
6593 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6594 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6595 % local gAMA/cHRM with local sRGB if appropriate).
6597 % Check for identical sBIT chunks and write global ones.
6599 % Provide option to skip writing the signature tEXt chunks.
6601 % Use signatures to detect identical objects and reuse the first
6602 % instance of such objects instead of writing duplicate objects.
6604 % Use a smaller-than-32k value of compression window size when
6607 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6608 % ancillary text chunks and save profiles.
6610 % Provide an option to force LC files (to ensure exact framing rate)
6613 % Provide an option to force VLC files instead of LC, even when offsets
6614 % are present. This will involve expanding the embedded images with a
6615 % transparent region at the top and/or left.
6619 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6620 png_info *ping_info, unsigned char *profile_type, unsigned char
6621 *profile_description, unsigned char *profile_data, png_uint_32 length)
6640 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6642 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6645 if (image_info->verbose)
6647 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6648 (char *) profile_type, (double) length);
6651 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6652 description_length=(png_uint_32) strlen((const char *) profile_description);
6653 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6654 + description_length);
6655 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6656 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6657 text[0].key[0]='\0';
6658 (void) ConcatenateMagickString(text[0].key,
6659 "Raw profile type ",MaxTextExtent);
6660 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6664 (void) CopyMagickString(dp,(const char *) profile_description,
6666 dp+=description_length;
6668 (void) FormatMagickString(dp,allocated_length-
6669 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6672 for (i=0; i < (ssize_t) length; i++)
6676 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6677 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6682 text[0].text_length=(png_size_t) (dp-text[0].text);
6683 text[0].compression=image_info->compression == NoCompression ||
6684 (image_info->compression == UndefinedCompression &&
6685 text[0].text_length < 128) ? -1 : 0;
6687 if (text[0].text_length <= allocated_length)
6688 png_set_text(ping,ping_info,text,1);
6690 png_free(ping,text[0].text);
6691 png_free(ping,text[0].key);
6692 png_free(ping,text);
6695 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6696 const char *string, MagickBooleanType logging)
6709 ResetImageProfileIterator(image);
6711 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6713 profile=GetImageProfile(image,name);
6715 if (profile != (const StringInfo *) NULL)
6720 if (LocaleNCompare(name,string,11) == 0)
6722 if (logging != MagickFalse)
6723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6724 " Found %s profile",name);
6726 ping_profile=CloneStringInfo(profile);
6727 data=GetStringInfoDatum(ping_profile),
6728 length=(png_uint_32) GetStringInfoLength(ping_profile);
6733 (void) WriteBlobMSBULong(image,length-5); /* data length */
6734 (void) WriteBlob(image,length-1,data+1);
6735 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6736 ping_profile=DestroyStringInfo(ping_profile);
6740 name=GetNextImageProfile(image);
6747 /* Write one PNG image */
6748 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6749 const ImageInfo *IMimage_info,Image *IMimage)
6773 ping_trans_alpha[256];
6801 ping_have_cheap_transparency,
6811 /* ping_exclude_EXIF, */
6814 /* ping_exclude_iTXt, */
6819 /* ping_exclude_tRNS, */
6821 ping_exclude_zCCP, /* hex-encoded iCCP */
6824 ping_need_colortype_warning,
6844 ping_interlace_method,
6845 ping_compression_method,
6862 number_semitransparent,
6864 ping_pHYs_unit_type;
6867 ping_pHYs_x_resolution,
6868 ping_pHYs_y_resolution;
6870 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " Enter WriteOnePNGImage()");
6873 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6874 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6876 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6877 LockSemaphoreInfo(ping_semaphore);
6880 /* Initialize some stuff */
6883 ping_interlace_method=0,
6884 ping_compression_method=0,
6885 ping_filter_method=0,
6888 ping_background.red = 0;
6889 ping_background.green = 0;
6890 ping_background.blue = 0;
6891 ping_background.gray = 0;
6892 ping_background.index = 0;
6894 ping_trans_color.red=0;
6895 ping_trans_color.green=0;
6896 ping_trans_color.blue=0;
6897 ping_trans_color.gray=0;
6899 ping_pHYs_unit_type = 0;
6900 ping_pHYs_x_resolution = 0;
6901 ping_pHYs_y_resolution = 0;
6903 ping_have_blob=MagickFalse;
6904 ping_have_color=MagickTrue;
6905 ping_have_non_bw=MagickTrue;
6906 ping_have_PLTE=MagickFalse;
6907 ping_have_bKGD=MagickFalse;
6908 ping_have_pHYs=MagickFalse;
6909 ping_have_tRNS=MagickFalse;
6911 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6912 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6913 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6914 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6915 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6916 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6917 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6918 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6919 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6920 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6921 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6922 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6923 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6924 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6925 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6927 ping_need_colortype_warning = MagickFalse;
6930 number_semitransparent = 0;
6931 number_transparent = 0;
6933 if (logging != MagickFalse)
6935 if (image->storage_class == UndefinedClass)
6936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6937 " storage_class=UndefinedClass");
6938 if (image->storage_class == DirectClass)
6939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6940 " storage_class=DirectClass");
6941 if (image->storage_class == PseudoClass)
6942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6943 " storage_class=PseudoClass");
6946 if (image->colorspace != RGBColorspace)
6947 (void) TransformImageColorspace(image,RGBColorspace);
6950 Sometimes we get PseudoClass images whose RGB values don't match
6951 the colors in the colormap. This code syncs the RGB values.
6953 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6954 (void) SyncImage(image);
6956 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6957 if (image->depth > 8)
6959 if (logging != MagickFalse)
6960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6961 " Reducing PNG bit depth to 8 since this is a Q8 build.");
6968 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6969 /* PNG does not handle depths greater than 16 so reduce it even
6972 if (image->depth > 16)
6976 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6977 if (image->depth == 16 && mng_info->write_png_depth != 16)
6978 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6982 /* Normally we run this just once, but in the case of writing PNG8
6983 * we reduce the transparency to binary and run again, then reduce
6984 * the colors to a simple 3-3-3 palette and run once more, and finally
6985 * to a simple 3-3-2 palette.
6988 tried_333 = MagickFalse;
6989 tried_444 = MagickFalse;
6995 * Sometimes we get DirectClass images that have 256 colors or fewer.
6996 * This code will build a colormap.
6998 * Also, sometimes we get PseudoClass images with an out-of-date
6999 * colormap. This code will replace the colormap with a new one.
7000 * Sometimes we get PseudoClass images that have more than 256 colors.
7001 * This code will delete the colormap and change the image to
7004 * If image->matte is MagickFalse, we ignore the opacity channel
7005 * even though it sometimes contains left-over non-opaque values.
7007 * Also we gather some information (number of opaque, transparent,
7008 * and semitransparent pixels, and whether the image has any non-gray
7009 * pixels or only black-and-white pixels) that we might need later.
7011 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7012 * we need to check for bogus non-opaque values, at least.
7015 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7016 # define PNGK 0 /* Shift */
7017 # define PNGM 1 /* Scale */
7018 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7020 # define PNGM 0x0101
7023 # define PNGM 0x01010101
7034 semitransparent[260],
7037 register IndexPacket
7040 register const PixelPacket
7044 register PixelPacket
7047 if (logging != MagickFalse)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049 " Enter BUILD_PALETTE:");
7051 if (logging != MagickFalse)
7053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7054 " image->columns=%.20g",(double) image->columns);
7055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056 " image->rows=%.20g",(double) image->rows);
7057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7058 " image->matte=%.20g",(double) image->matte);
7059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060 " image->depth=%.20g",(double) image->depth);
7062 if (image->storage_class == PseudoClass && image->colormap != NULL)
7064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7065 " Original colormap:");
7066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7067 " i (red,green,blue,opacity)");
7069 for (i=0; i < 256; i++)
7071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7072 " %d (%d,%d,%d,%d)",
7074 (int) image->colormap[i].red,
7075 (int) image->colormap[i].green,
7076 (int) image->colormap[i].blue,
7077 (int) image->colormap[i].opacity);
7080 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085 " %d (%d,%d,%d,%d)",
7087 (int) image->colormap[i].red,
7088 (int) image->colormap[i].green,
7089 (int) image->colormap[i].blue,
7090 (int) image->colormap[i].opacity);
7095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7096 " image->colors=%d",(int) image->colors);
7098 if (image->colors == 0)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " (zero means unknown)");
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " Regenerate the colormap");
7106 exception=(&image->exception);
7110 number_semitransparent = 0;
7111 number_transparent = 0;
7113 for (y=0; y < (ssize_t) image->rows; y++)
7115 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7117 if (q == (PixelPacket *) NULL)
7120 for (x=0; x < (ssize_t) image->columns; x++)
7122 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7124 if (number_opaque < 259)
7126 if (number_opaque == 0)
7129 opaque[0].opacity=OpaqueOpacity;
7133 for (i=0; i< (ssize_t) number_opaque; i++)
7135 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7139 if (i == (ssize_t) number_opaque &&
7140 number_opaque < 259)
7144 opaque[i].opacity = OpaqueOpacity;
7148 else if (q->opacity == TransparentOpacity)
7150 if (number_transparent < 259)
7152 if (number_transparent == 0)
7155 ping_trans_color.red=(unsigned short)(q->red);
7156 ping_trans_color.green=(unsigned short) (q->green);
7157 ping_trans_color.blue=(unsigned short) (q->blue);
7158 ping_trans_color.gray=(unsigned short) (q->blue);
7159 number_transparent = 1;
7162 for (i=0; i< (ssize_t) number_transparent; i++)
7164 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7168 if (i == (ssize_t) number_transparent &&
7169 number_transparent < 259)
7171 number_transparent++;
7172 transparent[i] = *q;
7178 if (number_semitransparent < 259)
7180 if (number_semitransparent == 0)
7182 semitransparent[0]=*q;
7183 number_semitransparent = 1;
7186 for (i=0; i< (ssize_t) number_semitransparent; i++)
7188 if (IsColorEqual(semitransparent+i,
7189 (PixelPacket *) q) &&
7190 q->opacity == semitransparent[i].opacity)
7194 if (i == (ssize_t) number_semitransparent &&
7195 number_semitransparent < 259)
7197 number_semitransparent++;
7198 semitransparent[i] = *q;
7206 if (ping_exclude_bKGD == MagickFalse)
7208 /* Add the background color to the palette, if it
7209 * isn't already there.
7211 for (i=0; i<number_opaque; i++)
7213 if (IsColorEqual(opaque+i, &image->background_color))
7217 if (number_opaque < 259 && i == number_opaque)
7219 opaque[i]=image->background_color;
7220 opaque[i].opacity = OpaqueOpacity;
7223 else if (logging != MagickFalse)
7224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225 " No room in the colormap to add background color");
7228 image_colors=number_opaque+number_transparent+number_semitransparent;
7230 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7232 /* No room for the background color; remove it. */
7237 if (logging != MagickFalse)
7239 if (image_colors > 256)
7240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7241 " image has more than 256 colors");
7244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7245 " image has %d colors",image_colors);
7248 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7250 ping_have_color=MagickFalse;
7251 ping_have_non_bw=MagickFalse;
7253 if(image_colors > 256)
7255 for (y=0; y < (ssize_t) image->rows; y++)
7257 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7259 if (q == (PixelPacket *) NULL)
7262 /* Worst case is black-and-white; we are looking at every
7266 if (ping_have_color == MagickFalse)
7269 for (x=0; x < (ssize_t) image->columns; x++)
7271 if (s->red != s->green || s->red != s->blue)
7273 ping_have_color=MagickTrue;
7274 ping_have_non_bw=MagickTrue;
7281 if (ping_have_non_bw == MagickFalse)
7284 for (x=0; x < (ssize_t) image->columns; x++)
7286 if (s->red != 0 && s->red != QuantumRange)
7288 ping_have_non_bw=MagickTrue;
7297 if (image_colors < 257)
7303 * Initialize image colormap.
7306 if (logging != MagickFalse)
7307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7308 " Sort the new colormap");
7310 /* Sort palette, transparent first */;
7314 for (i=0; i<number_transparent; i++)
7315 colormap[n++] = transparent[i];
7317 for (i=0; i<number_semitransparent; i++)
7318 colormap[n++] = semitransparent[i];
7320 for (i=0; i<number_opaque; i++)
7321 colormap[n++] = opaque[i];
7324 /* image_colors < 257; search the colormap instead of the pixels
7325 * to get ping_have_color and ping_have_non_bw
7329 if (ping_have_color == MagickFalse)
7331 if (colormap[i].red != colormap[i].green ||
7332 colormap[i].red != colormap[i].blue)
7334 ping_have_color=MagickTrue;
7335 ping_have_non_bw=MagickTrue;
7340 if (ping_have_non_bw == MagickFalse)
7342 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7343 ping_have_non_bw=MagickTrue;
7347 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7348 (number_transparent == 0 && number_semitransparent == 0)) &&
7349 (((mng_info->write_png_colortype-1) ==
7350 PNG_COLOR_TYPE_PALETTE) ||
7351 (mng_info->write_png_colortype == 0)))
7353 if (logging != MagickFalse)
7355 if (n != (ssize_t) image_colors)
7356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7357 " image_colors (%d) and n (%d) don't match",
7360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7361 " AcquireImageColormap");
7364 image->colors = image_colors;
7366 if (AcquireImageColormap(image,image_colors) ==
7368 ThrowWriterException(ResourceLimitError,
7369 "MemoryAllocationFailed");
7371 for (i=0; i< (ssize_t) image_colors; i++)
7372 image->colormap[i] = colormap[i];
7374 if (logging != MagickFalse)
7376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7377 " image->colors=%d (%d)",
7378 (int) image->colors, image_colors);
7380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7381 " Update the pixel indexes");
7384 /* Sync the pixel indices with the new colormap */
7386 for (y=0; y < (ssize_t) image->rows; y++)
7388 q=GetAuthenticPixels(image,0,y,image->columns,1,
7391 if (q == (PixelPacket *) NULL)
7394 indexes=GetAuthenticIndexQueue(image);
7396 for (x=0; x < (ssize_t) image->columns; x++)
7398 for (i=0; i< (ssize_t) image_colors; i++)
7400 if ((image->matte == MagickFalse ||
7401 image->colormap[i].opacity == q->opacity) &&
7402 (IsColorEqual(&image->colormap[i],
7403 (PixelPacket *) q)))
7405 indexes[x]=(IndexPacket) i;
7412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7418 if (logging != MagickFalse)
7420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7421 " image->colors=%d", (int) image->colors);
7423 if (image->colormap != NULL)
7425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7426 " i (red,green,blue,opacity)");
7428 for (i=0; i < (ssize_t) image->colors; i++)
7430 if (i < 300 || i >= image->colors - 10)
7432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433 " %d (%d,%d,%d,%d)",
7435 (int) image->colormap[i].red,
7436 (int) image->colormap[i].green,
7437 (int) image->colormap[i].blue,
7438 (int) image->colormap[i].opacity);
7443 if (number_transparent < 257)
7444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7445 " number_transparent = %d",
7446 number_transparent);
7449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7450 " number_transparent > 256");
7452 if (number_opaque < 257)
7453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7454 " number_opaque = %d",
7458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7459 " number_opaque > 256");
7461 if (number_semitransparent < 257)
7462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7463 " number_semitransparent = %d",
7464 number_semitransparent);
7467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468 " number_semitransparent > 256");
7470 if (ping_have_non_bw == MagickFalse)
7471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472 " All pixels and the background are black or white");
7474 else if (ping_have_color == MagickFalse)
7475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476 " All pixels and the background are gray");
7479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7480 " At least one pixel or the background is non-gray");
7482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7483 " Exit BUILD_PALETTE:");
7486 if (mng_info->write_png8 == MagickFalse)
7489 /* Make any reductions necessary for the PNG8 format */
7490 if (image_colors <= 256 &&
7491 image_colors != 0 && image->colormap != NULL &&
7492 number_semitransparent == 0 &&
7493 number_transparent <= 1)
7496 /* PNG8 can't have semitransparent colors so we threshold the
7497 * opacity to 0 or OpaqueOpacity
7499 if (number_semitransparent != 0)
7501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7502 " Thresholding the alpha channel to binary");
7504 for (y=0; y < (ssize_t) image->rows; y++)
7506 r=GetAuthenticPixels(image,0,y,image->columns,1,
7509 if (r == (PixelPacket *) NULL)
7512 for (x=0; x < (ssize_t) image->columns; x++)
7514 r->opacity = r->opacity > TransparentOpacity/2 ?
7515 TransparentOpacity : OpaqueOpacity;
7519 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7522 if (image_colors != 0 && image_colors <= 256 &&
7523 image->colormap != NULL)
7524 for (i=0; i<image_colors; i++)
7525 image->colormap[i].opacity =
7526 image->colormap[i].opacity > TransparentOpacity/2 ?
7527 TransparentOpacity : OpaqueOpacity;
7532 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7533 * background color to the 4-4-4, 3-3-3 or 3-3-2 palette. If the image is
7534 * mostly gray, the 4-4-4 palette should end up with 256 colors or less.
7536 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7538 if (logging != MagickFalse)
7539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7540 " Quantizing the background color to 4-4-4");
7542 tried_444 = MagickTrue;
7544 image->background_color.red=
7546 image->background_color.red) >> PNGK) & 0xf0) ) |
7548 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7549 image->background_color.green=
7551 image->background_color.green) >> PNGK) & 0xf0) ) |
7553 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7554 image->background_color.blue=
7556 image->background_color.blue) >> PNGK) & 0xf0) ) |
7558 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7560 if (logging != MagickFalse)
7561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7562 " Quantizing the pixel colors to 4-4-4");
7564 if (image->colormap == NULL)
7566 for (y=0; y < (ssize_t) image->rows; y++)
7568 r=GetAuthenticPixels(image,0,y,image->columns,1,
7571 if (r == (PixelPacket *) NULL)
7574 for (x=0; x < (ssize_t) image->columns; x++)
7576 if (r->opacity == TransparentOpacity)
7578 r->red = image->background_color.red;
7579 r->green = image->background_color.green;
7580 r->blue = image->background_color.blue;
7585 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7586 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7588 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7589 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7591 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7592 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7597 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7602 else /* Should not reach this; colormap already exists and
7605 if (logging != MagickFalse)
7606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607 " Quantizing the colormap to 4-4-4");
7608 for (i=0; i<image_colors; i++)
7610 image->colormap[i].red=
7612 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7614 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7615 image->colormap[i].green=
7617 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7619 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7620 image->colormap[i].blue=
7622 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7624 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7630 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7632 if (logging != MagickFalse)
7633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7634 " Quantizing the background color to 3-3-3");
7636 tried_333 = MagickTrue;
7638 image->background_color.red=
7640 image->background_color.red) >> PNGK) & 0xe0) ) |
7642 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7644 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7645 image->background_color.green=
7647 image->background_color.green) >> PNGK) & 0xe0) ) |
7649 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7651 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7652 image->background_color.blue=
7654 image->background_color.blue) >> PNGK) & 0xe0) ) |
7656 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7658 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7660 if (logging != MagickFalse)
7661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662 " Quantizing the pixel colors to 3-3-3");
7664 if (image->colormap == NULL)
7666 for (y=0; y < (ssize_t) image->rows; y++)
7668 r=GetAuthenticPixels(image,0,y,image->columns,1,
7671 if (r == (PixelPacket *) NULL)
7674 for (x=0; x < (ssize_t) image->columns; x++)
7676 if (r->opacity == TransparentOpacity)
7678 r->red = image->background_color.red;
7679 r->green = image->background_color.green;
7680 r->blue = image->background_color.blue;
7685 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7686 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7687 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7689 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7690 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7691 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7693 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7694 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7695 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7700 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7705 else /* Should not reach this; colormap already exists and
7708 if (logging != MagickFalse)
7709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710 " Quantizing the colormap to 3-3-3");
7711 for (i=0; i<image_colors; i++)
7713 image->colormap[i].red=
7715 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7717 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7719 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7720 image->colormap[i].green=
7722 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7724 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7726 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7727 image->colormap[i].blue=
7729 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7731 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7733 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7739 if (image_colors == 0 || image_colors > 256)
7741 if (logging != MagickFalse)
7742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7743 " Quantizing the background color to 3-3-2");
7745 image->background_color.red=
7747 image->background_color.red) >> PNGK) & 0xe0) ) |
7749 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7751 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7752 image->background_color.green=
7754 image->background_color.green) >> PNGK) & 0xe0) ) |
7756 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7758 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7759 image->background_color.blue=
7761 image->background_color.blue) >> PNGK) & 0xc0) ) |
7763 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7765 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7767 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7769 if (logging != MagickFalse)
7770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7771 " Quantizing the pixel colors to 3-3-2");
7773 if (image->colormap == NULL)
7775 for (y=0; y < (ssize_t) image->rows; y++)
7777 r=GetAuthenticPixels(image,0,y,image->columns,1,
7780 if (r == (PixelPacket *) NULL)
7783 for (x=0; x < (ssize_t) image->columns; x++)
7785 if (r->opacity == TransparentOpacity)
7787 r->red = image->background_color.red;
7788 r->green = image->background_color.green;
7789 r->blue = image->background_color.blue;
7794 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7795 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7796 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7798 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7799 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7800 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7802 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7803 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7804 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7805 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7810 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7815 else /* Should not reach this; colormap already exists and
7818 if (logging != MagickFalse)
7819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7820 " Quantizing the colormap to 3-3-2");
7821 for (i=0; i<image_colors; i++)
7823 image->colormap[i].red=
7825 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7827 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7829 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7830 image->colormap[i].green=
7832 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7834 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7836 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7837 image->colormap[i].blue=
7839 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
7841 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
7843 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
7845 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7852 /* END OF BUILD_PALETTE */
7854 /* If we are excluding the tRNS chunk and there is transparency,
7855 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7858 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7859 (number_transparent != 0 || number_semitransparent != 0))
7861 int colortype=mng_info->write_png_colortype;
7863 if (ping_have_color == MagickFalse)
7864 mng_info->write_png_colortype = 5;
7867 mng_info->write_png_colortype = 7;
7869 if (colortype != 0 &&
7870 mng_info->write_png_colortype != (ssize_t) colortype)
7871 ping_need_colortype_warning=MagickTrue;
7875 /* See if cheap transparency is possible. It is only possible
7876 * when there is a single transparent color, no semitransparent
7877 * color, and no opaque color that has the same RGB components
7878 * as the transparent color. We only need this information if
7879 * we are writing a PNG with colortype 0 or 2, and we have not
7880 * excluded the tRNS chunk.
7882 if (number_transparent == 1 &&
7883 mng_info->write_png_colortype < 4)
7885 ping_have_cheap_transparency = MagickTrue;
7887 if (number_semitransparent != 0)
7888 ping_have_cheap_transparency = MagickFalse;
7890 else if (image_colors == 0 || image_colors > 256 ||
7891 image->colormap == NULL)
7896 register const PixelPacket
7899 exception=(&image->exception);
7901 for (y=0; y < (ssize_t) image->rows; y++)
7903 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7905 if (q == (PixelPacket *) NULL)
7908 for (x=0; x < (ssize_t) image->columns; x++)
7910 if (q->opacity != TransparentOpacity &&
7911 (unsigned short) q->red == ping_trans_color.red &&
7912 (unsigned short) q->green == ping_trans_color.green &&
7913 (unsigned short) q->blue == ping_trans_color.blue)
7915 ping_have_cheap_transparency = MagickFalse;
7922 if (ping_have_cheap_transparency == MagickFalse)
7928 /* Assuming that image->colormap[0] is the one transparent color
7929 * and that all others are opaque.
7931 if (image_colors > 1)
7932 for (i=1; i<image_colors; i++)
7933 if (image->colormap[i].red == image->colormap[0].red &&
7934 image->colormap[i].green == image->colormap[0].green &&
7935 image->colormap[i].blue == image->colormap[0].blue)
7937 ping_have_cheap_transparency = MagickFalse;
7942 if (logging != MagickFalse)
7944 if (ping_have_cheap_transparency == MagickFalse)
7945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946 " Cheap transparency is not possible.");
7949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7950 " Cheap transparency is possible.");
7954 ping_have_cheap_transparency = MagickFalse;
7956 image_depth=image->depth;
7958 quantum_info = (QuantumInfo *) NULL;
7960 image_colors=(int) image->colors;
7961 image_matte=image->matte;
7963 mng_info->IsPalette=image->storage_class == PseudoClass &&
7964 image_colors <= 256 && image->colormap != NULL;
7966 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7967 (image->colors == 0 || image->colormap == NULL))
7969 image_info=DestroyImageInfo(image_info);
7970 image=DestroyImage(image);
7971 (void) ThrowMagickException(&IMimage->exception,
7972 GetMagickModule(),CoderError,
7973 "Cannot write PNG8 or color-type 3; colormap is NULL",
7974 "`%s'",IMimage->filename);
7975 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7976 UnlockSemaphoreInfo(ping_semaphore);
7978 return(MagickFalse);
7982 Allocate the PNG structures
7984 #ifdef PNG_USER_MEM_SUPPORTED
7985 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7986 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7987 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7990 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7991 MagickPNGErrorHandler,MagickPNGWarningHandler);
7994 if (ping == (png_struct *) NULL)
7995 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7997 ping_info=png_create_info_struct(ping);
7999 if (ping_info == (png_info *) NULL)
8001 png_destroy_write_struct(&ping,(png_info **) NULL);
8002 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8005 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8006 ping_pixels=(unsigned char *) NULL;
8008 if (setjmp(png_jmpbuf(ping)))
8014 if (image_info->verbose)
8015 (void) printf("PNG write has failed.\n");
8017 png_destroy_write_struct(&ping,&ping_info);
8018 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8019 UnlockSemaphoreInfo(ping_semaphore);
8021 if (ping_have_blob != MagickFalse)
8022 (void) CloseBlob(image);
8023 image_info=DestroyImageInfo(image_info);
8024 image=DestroyImage(image);
8025 return(MagickFalse);
8028 Prepare PNG for writing.
8030 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8031 if (mng_info->write_mng)
8032 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8035 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8036 if (mng_info->write_mng)
8037 png_permit_empty_plte(ping,MagickTrue);
8044 ping_width=(png_uint_32) image->columns;
8045 ping_height=(png_uint_32) image->rows;
8047 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8050 if (mng_info->write_png_depth != 0)
8051 image_depth=mng_info->write_png_depth;
8053 /* Adjust requested depth to next higher valid depth if necessary */
8054 if (image_depth > 8)
8057 if ((image_depth > 4) && (image_depth < 8))
8060 if (image_depth == 3)
8063 if (logging != MagickFalse)
8065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8066 " width=%.20g",(double) ping_width);
8067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8068 " height=%.20g",(double) ping_height);
8069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8070 " image_matte=%.20g",(double) image->matte);
8071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8072 " image->depth=%.20g",(double) image->depth);
8073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8074 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8077 save_image_depth=image_depth;
8078 ping_bit_depth=(png_byte) save_image_depth;
8081 #if defined(PNG_pHYs_SUPPORTED)
8082 if (ping_exclude_pHYs == MagickFalse)
8084 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8085 (!mng_info->write_mng || !mng_info->equal_physs))
8087 if (logging != MagickFalse)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " Setting up pHYs chunk");
8091 if (image->units == PixelsPerInchResolution)
8093 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8094 ping_pHYs_x_resolution=
8095 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8096 ping_pHYs_y_resolution=
8097 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8100 else if (image->units == PixelsPerCentimeterResolution)
8102 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8103 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8104 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8109 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8110 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8111 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8114 if (logging != MagickFalse)
8115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8116 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8117 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8118 (int) ping_pHYs_unit_type);
8119 ping_have_pHYs = MagickTrue;
8124 if (ping_exclude_bKGD == MagickFalse)
8126 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8132 if (ping_bit_depth == 8)
8135 if (ping_bit_depth == 4)
8138 if (ping_bit_depth == 2)
8141 if (ping_bit_depth == 1)
8144 ping_background.red=(png_uint_16)
8145 (ScaleQuantumToShort(image->background_color.red) & mask);
8147 ping_background.green=(png_uint_16)
8148 (ScaleQuantumToShort(image->background_color.green) & mask);
8150 ping_background.blue=(png_uint_16)
8151 (ScaleQuantumToShort(image->background_color.blue) & mask);
8154 if (logging != MagickFalse)
8156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8157 " Setting up bKGD chunk (1)");
8159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160 " ping_bit_depth=%d",ping_bit_depth);
8163 ping_have_bKGD = MagickTrue;
8167 Select the color type.
8172 if (mng_info->IsPalette && mng_info->write_png8)
8175 /* To do: make this a function cause it's used twice, except
8176 for reducing the sample depth from 8. */
8178 number_colors=image_colors;
8180 ping_have_tRNS=MagickFalse;
8185 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8187 if (logging != MagickFalse)
8188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8189 " Setting up PLTE chunk with %d colors (%d)",
8190 number_colors, image_colors);
8192 for (i=0; i < (ssize_t) number_colors; i++)
8194 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8195 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8196 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8197 if (logging != MagickFalse)
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 #if MAGICKCORE_QUANTUM_DEPTH == 8
8200 " %3ld (%3d,%3d,%3d)",
8202 " %5ld (%5d,%5d,%5d)",
8204 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8208 ping_have_PLTE=MagickTrue;
8209 image_depth=ping_bit_depth;
8212 if (matte != MagickFalse)
8215 Identify which colormap entry is transparent.
8217 assert(number_colors <= 256);
8218 assert(image->colormap != NULL);
8220 for (i=0; i < (ssize_t) number_transparent; i++)
8221 ping_trans_alpha[i]=0;
8224 ping_num_trans=(unsigned short) (number_transparent +
8225 number_semitransparent);
8227 if (ping_num_trans == 0)
8228 ping_have_tRNS=MagickFalse;
8231 ping_have_tRNS=MagickTrue;
8234 if (ping_exclude_bKGD == MagickFalse)
8237 * Identify which colormap entry is the background color.
8240 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8241 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8244 ping_background.index=(png_byte) i;
8246 } /* end of write_png8 */
8248 else if (mng_info->write_png24)
8250 image_matte=MagickFalse;
8251 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8254 else if (mng_info->write_png32)
8256 image_matte=MagickTrue;
8257 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8260 else /* mng_info->write_pngNN not specified */
8262 image_depth=ping_bit_depth;
8264 if (mng_info->write_png_colortype != 0)
8266 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8268 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8269 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8270 image_matte=MagickTrue;
8273 image_matte=MagickFalse;
8275 if (logging != MagickFalse)
8276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277 " PNG colortype %d was specified:",(int) ping_color_type);
8280 else /* write_png_colortype not specified */
8282 if (logging != MagickFalse)
8283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8284 " Selecting PNG colortype:");
8286 ping_color_type=(png_byte) ((matte != MagickFalse)?
8287 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8289 if (image_info->type == TrueColorType)
8291 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8292 image_matte=MagickFalse;
8295 if (image_info->type == TrueColorMatteType)
8297 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8298 image_matte=MagickTrue;
8301 if (image_info->type == PaletteType ||
8302 image_info->type == PaletteMatteType)
8303 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8305 if (mng_info->write_png_colortype == 0 &&
8306 (image_info->type == UndefinedType ||
8307 image_info->type == OptimizeType))
8309 if (ping_have_color == MagickFalse)
8311 if (image_matte == MagickFalse)
8313 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8314 image_matte=MagickFalse;
8319 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8320 image_matte=MagickTrue;
8325 if (image_matte == MagickFalse)
8327 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8328 image_matte=MagickFalse;
8333 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8334 image_matte=MagickTrue;
8341 if (logging != MagickFalse)
8342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8343 " Selected PNG colortype=%d",ping_color_type);
8345 if (ping_bit_depth < 8)
8347 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8348 ping_color_type == PNG_COLOR_TYPE_RGB ||
8349 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8353 old_bit_depth=ping_bit_depth;
8355 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8357 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8361 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8366 if (image->colors == 0)
8369 (void) ThrowMagickException(&image->exception,
8370 GetMagickModule(),CoderError,
8371 "image has 0 colors", "`%s'","");
8374 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8375 ping_bit_depth <<= 1;
8378 if (logging != MagickFalse)
8380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8381 " Number of colors: %.20g",(double) image_colors);
8383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384 " Tentative PNG bit depth: %d",ping_bit_depth);
8387 if (ping_bit_depth < (int) mng_info->write_png_depth)
8388 ping_bit_depth = mng_info->write_png_depth;
8391 image_depth=ping_bit_depth;
8393 if (logging != MagickFalse)
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Tentative PNG color type: %.20g",(double) ping_color_type);
8398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399 " image_info->type: %.20g",(double) image_info->type);
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " image_depth: %.20g",(double) image_depth);
8404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8406 " image->depth: %.20g",(double) image->depth);
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8412 if (matte != MagickFalse)
8414 if (mng_info->IsPalette)
8416 if (mng_info->write_png_colortype == 0)
8418 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8420 if (ping_have_color != MagickFalse)
8421 ping_color_type=PNG_COLOR_TYPE_RGBA;
8425 * Determine if there is any transparent color.
8427 if (number_transparent + number_semitransparent == 0)
8430 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8433 image_matte=MagickFalse;
8435 if (mng_info->write_png_colortype == 0)
8436 ping_color_type&=0x03;
8446 if (ping_bit_depth == 8)
8449 if (ping_bit_depth == 4)
8452 if (ping_bit_depth == 2)
8455 if (ping_bit_depth == 1)
8458 ping_trans_color.red=(png_uint_16)
8459 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8461 ping_trans_color.green=(png_uint_16)
8462 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8464 ping_trans_color.blue=(png_uint_16)
8465 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8467 ping_trans_color.gray=(png_uint_16)
8468 (ScaleQuantumToShort(PixelIntensityToQuantum(
8469 image->colormap)) & mask);
8471 ping_trans_color.index=(png_byte) 0;
8473 ping_have_tRNS=MagickTrue;
8476 if (ping_have_tRNS != MagickFalse)
8479 * Determine if there is one and only one transparent color
8480 * and if so if it is fully transparent.
8482 if (ping_have_cheap_transparency == MagickFalse)
8483 ping_have_tRNS=MagickFalse;
8486 if (ping_have_tRNS != MagickFalse)
8488 if (mng_info->write_png_colortype == 0)
8489 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8491 if (image_depth == 8)
8493 ping_trans_color.red&=0xff;
8494 ping_trans_color.green&=0xff;
8495 ping_trans_color.blue&=0xff;
8496 ping_trans_color.gray&=0xff;
8502 if (image_depth == 8)
8504 ping_trans_color.red&=0xff;
8505 ping_trans_color.green&=0xff;
8506 ping_trans_color.blue&=0xff;
8507 ping_trans_color.gray&=0xff;
8514 if (ping_have_tRNS != MagickFalse)
8515 image_matte=MagickFalse;
8517 if ((mng_info->IsPalette) &&
8518 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8519 ping_have_color == MagickFalse &&
8520 (image_matte == MagickFalse || image_depth >= 8))
8524 if (image_matte != MagickFalse)
8525 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8527 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8529 ping_color_type=PNG_COLOR_TYPE_GRAY;
8531 if (save_image_depth == 16 && image_depth == 8)
8533 if (logging != MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " Scaling ping_trans_color (0)");
8538 ping_trans_color.gray*=0x0101;
8542 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8543 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8545 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8546 image_colors=(int) (one << image_depth);
8548 if (image_depth > 8)
8554 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8556 if(!mng_info->write_png_depth)
8560 while ((int) (one << ping_bit_depth)
8561 < (ssize_t) image_colors)
8562 ping_bit_depth <<= 1;
8566 else if (ping_color_type ==
8567 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8568 mng_info->IsPalette)
8570 /* Check if grayscale is reducible */
8573 depth_4_ok=MagickTrue,
8574 depth_2_ok=MagickTrue,
8575 depth_1_ok=MagickTrue;
8577 for (i=0; i < (ssize_t) image_colors; i++)
8582 intensity=ScaleQuantumToChar(image->colormap[i].red);
8584 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8585 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8586 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8587 depth_2_ok=depth_1_ok=MagickFalse;
8588 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8589 depth_1_ok=MagickFalse;
8592 if (depth_1_ok && mng_info->write_png_depth <= 1)
8595 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8598 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8603 image_depth=ping_bit_depth;
8608 if (mng_info->IsPalette)
8610 number_colors=image_colors;
8612 if (image_depth <= 8)
8617 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8619 if (mng_info->have_write_global_plte && matte == MagickFalse)
8621 png_set_PLTE(ping,ping_info,NULL,0);
8623 if (logging != MagickFalse)
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " Setting up empty PLTE chunk");
8630 for (i=0; i < (ssize_t) number_colors; i++)
8632 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8633 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8634 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8637 if (logging != MagickFalse)
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " Setting up PLTE chunk with %d colors",
8642 ping_have_PLTE=MagickTrue;
8645 /* color_type is PNG_COLOR_TYPE_PALETTE */
8646 if (mng_info->write_png_depth == 0)
8654 while ((one << ping_bit_depth) < number_colors)
8655 ping_bit_depth <<= 1;
8660 if (matte != MagickFalse)
8663 * Set up trans_colors array.
8665 assert(number_colors <= 256);
8667 ping_num_trans=(unsigned short) (number_transparent +
8668 number_semitransparent);
8670 if (ping_num_trans == 0)
8671 ping_have_tRNS=MagickFalse;
8675 if (logging != MagickFalse)
8677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678 " Scaling ping_trans_color (1)");
8680 ping_have_tRNS=MagickTrue;
8682 for (i=0; i < ping_num_trans; i++)
8684 ping_trans_alpha[i]= (png_byte) (255-
8685 ScaleQuantumToChar(image->colormap[i].opacity));
8695 if (image_depth < 8)
8698 if ((save_image_depth == 16) && (image_depth == 8))
8700 if (logging != MagickFalse)
8702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703 " Scaling ping_trans_color from (%d,%d,%d)",
8704 (int) ping_trans_color.red,
8705 (int) ping_trans_color.green,
8706 (int) ping_trans_color.blue);
8709 ping_trans_color.red*=0x0101;
8710 ping_trans_color.green*=0x0101;
8711 ping_trans_color.blue*=0x0101;
8712 ping_trans_color.gray*=0x0101;
8714 if (logging != MagickFalse)
8716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8718 (int) ping_trans_color.red,
8719 (int) ping_trans_color.green,
8720 (int) ping_trans_color.blue);
8725 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8726 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8729 Adjust background and transparency samples in sub-8-bit grayscale files.
8731 if (ping_bit_depth < 8 && ping_color_type ==
8732 PNG_COLOR_TYPE_GRAY)
8740 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8742 if (ping_exclude_bKGD == MagickFalse)
8745 ping_background.gray=(png_uint_16)
8746 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8748 if (logging != MagickFalse)
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750 " Setting up bKGD chunk (2)");
8752 ping_have_bKGD = MagickTrue;
8755 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8756 ping_trans_color.gray));
8759 if (ping_exclude_bKGD == MagickFalse)
8761 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8764 Identify which colormap entry is the background color.
8767 number_colors=image_colors;
8769 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8770 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8773 ping_background.index=(png_byte) i;
8775 if (logging != MagickFalse)
8777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778 " Setting up bKGD chunk with index=%d",(int) i);
8781 if (i < (ssize_t) number_colors)
8783 ping_have_bKGD = MagickTrue;
8785 if (logging != MagickFalse)
8787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8788 " background =(%d,%d,%d)",
8789 (int) ping_background.red,
8790 (int) ping_background.green,
8791 (int) ping_background.blue);
8795 else /* Can't happen */
8797 if (logging != MagickFalse)
8798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8799 " No room in PLTE to add bKGD color");
8800 ping_have_bKGD = MagickFalse;
8805 if (logging != MagickFalse)
8806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807 " PNG color type: %d",ping_color_type);
8809 Initialize compression level and filtering.
8811 if (logging != MagickFalse)
8813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814 " Setting up deflate compression");
8816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817 " Compression buffer size: 32768");
8820 png_set_compression_buffer_size(ping,32768L);
8822 if (logging != MagickFalse)
8823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824 " Compression mem level: 9");
8826 png_set_compression_mem_level(ping, 9);
8828 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8836 level=(int) MagickMin((ssize_t) quality/10,9);
8838 if (logging != MagickFalse)
8839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8840 " Compression level: %d",level);
8842 png_set_compression_level(ping,level);
8847 if (logging != MagickFalse)
8848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849 " Compression strategy: Z_HUFFMAN_ONLY");
8851 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8854 if (logging != MagickFalse)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Setting up filtering");
8858 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8859 /* This became available in libpng-1.0.9. Output must be a MNG. */
8860 if (mng_info->write_mng && ((quality % 10) == 7))
8862 if (logging != MagickFalse)
8863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8866 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8870 if (logging != MagickFalse)
8871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8879 if ((quality % 10) > 5)
8880 base_filter=PNG_ALL_FILTERS;
8883 if ((quality % 10) != 5)
8884 base_filter=(int) quality % 10;
8887 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8888 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8890 base_filter=PNG_NO_FILTERS;
8893 base_filter=PNG_ALL_FILTERS;
8895 if (logging != MagickFalse)
8897 if (base_filter == PNG_ALL_FILTERS)
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " Base filter method: ADAPTIVE");
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " Base filter method: NONE");
8905 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8908 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8909 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8911 ResetImageProfileIterator(image);
8912 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8914 profile=GetImageProfile(image,name);
8916 if (profile != (StringInfo *) NULL)
8918 #ifdef PNG_WRITE_iCCP_SUPPORTED
8919 if ((LocaleCompare(name,"ICC") == 0) ||
8920 (LocaleCompare(name,"ICM") == 0))
8923 if (ping_exclude_iCCP == MagickFalse)
8925 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8926 #if (PNG_LIBPNG_VER < 10500)
8927 (png_charp) GetStringInfoDatum(profile),
8929 (png_const_bytep) GetStringInfoDatum(profile),
8931 (png_uint_32) GetStringInfoLength(profile));
8937 if (ping_exclude_zCCP == MagickFalse)
8939 Magick_png_write_raw_profile(image_info,ping,ping_info,
8940 (unsigned char *) name,(unsigned char *) name,
8941 GetStringInfoDatum(profile),
8942 (png_uint_32) GetStringInfoLength(profile));
8946 if (logging != MagickFalse)
8947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8948 " Setting up text chunk with %s profile",name);
8950 name=GetNextImageProfile(image);
8954 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8955 if ((mng_info->have_write_global_srgb == 0) &&
8956 ((image->rendering_intent != UndefinedIntent) ||
8957 (image->colorspace == sRGBColorspace)))
8959 if (ping_exclude_sRGB == MagickFalse)
8962 Note image rendering intent.
8964 if (logging != MagickFalse)
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966 " Setting up sRGB chunk");
8968 (void) png_set_sRGB(ping,ping_info,(
8969 Magick_RenderingIntent_to_PNG_RenderingIntent(
8970 image->rendering_intent)));
8972 if (ping_exclude_gAMA == MagickFalse)
8973 png_set_gAMA(ping,ping_info,0.45455);
8977 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8980 if (ping_exclude_gAMA == MagickFalse &&
8981 (ping_exclude_sRGB == MagickFalse ||
8982 (image->gamma < .45 || image->gamma > .46)))
8984 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8988 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8990 if (logging != MagickFalse)
8991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " Setting up gAMA chunk");
8994 png_set_gAMA(ping,ping_info,image->gamma);
8998 if (ping_exclude_cHRM == MagickFalse)
9000 if ((mng_info->have_write_global_chrm == 0) &&
9001 (image->chromaticity.red_primary.x != 0.0))
9004 Note image chromaticity.
9005 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9013 wp=image->chromaticity.white_point;
9014 rp=image->chromaticity.red_primary;
9015 gp=image->chromaticity.green_primary;
9016 bp=image->chromaticity.blue_primary;
9018 if (logging != MagickFalse)
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " Setting up cHRM chunk");
9022 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9028 ping_interlace_method=image_info->interlace != NoInterlace;
9030 if (mng_info->write_mng)
9031 png_set_sig_bytes(ping,8);
9033 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9035 if (mng_info->write_png_colortype != 0)
9037 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9038 if (ping_have_color != MagickFalse)
9040 ping_color_type = PNG_COLOR_TYPE_RGB;
9042 if (ping_bit_depth < 8)
9046 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9047 if (ping_have_color != MagickFalse)
9048 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9051 if (ping_need_colortype_warning != MagickFalse ||
9052 ((mng_info->write_png_depth &&
9053 (int) mng_info->write_png_depth != ping_bit_depth) ||
9054 (mng_info->write_png_colortype &&
9055 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9056 mng_info->write_png_colortype != 7 &&
9057 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9059 if (logging != MagickFalse)
9061 if (ping_need_colortype_warning != MagickFalse)
9063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " Image has transparency but tRNS chunk was excluded");
9067 if (mng_info->write_png_depth)
9069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " Defined PNG:bit-depth=%u, Computed depth=%u",
9071 mng_info->write_png_depth,
9075 if (mng_info->write_png_colortype)
9077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078 " Defined PNG:color-type=%u, Computed color type=%u",
9079 mng_info->write_png_colortype-1,
9085 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9088 if (image_matte != MagickFalse && image->matte == MagickFalse)
9090 /* Add an opaque matte channel */
9091 image->matte = MagickTrue;
9092 (void) SetImageOpacity(image,0);
9094 if (logging != MagickFalse)
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096 " Added an opaque matte channel");
9099 if (number_transparent != 0 || number_semitransparent != 0)
9101 if (ping_color_type < 4)
9103 ping_have_tRNS=MagickTrue;
9104 if (logging != MagickFalse)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " Setting ping_have_tRNS=MagickTrue.");
9110 if (logging != MagickFalse)
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112 " Writing PNG header chunks");
9114 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9115 ping_bit_depth,ping_color_type,
9116 ping_interlace_method,ping_compression_method,
9117 ping_filter_method);
9119 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9121 png_set_PLTE(ping,ping_info,palette,number_colors);
9123 if (logging != MagickFalse)
9125 for (i=0; i< (ssize_t) number_colors; i++)
9127 if (i < ping_num_trans)
9128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9129 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9131 (int) palette[i].red,
9132 (int) palette[i].green,
9133 (int) palette[i].blue,
9135 (int) ping_trans_alpha[i]);
9137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9138 " PLTE[%d] = (%d,%d,%d)",
9140 (int) palette[i].red,
9141 (int) palette[i].green,
9142 (int) palette[i].blue);
9147 if (ping_exclude_bKGD == MagickFalse)
9149 if (ping_have_bKGD != MagickFalse)
9150 png_set_bKGD(ping,ping_info,&ping_background);
9153 if (ping_exclude_pHYs == MagickFalse)
9155 if (ping_have_pHYs != MagickFalse)
9157 png_set_pHYs(ping,ping_info,
9158 ping_pHYs_x_resolution,
9159 ping_pHYs_y_resolution,
9160 ping_pHYs_unit_type);
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " Setting up pHYs chunk");
9166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167 " x_resolution=%lu",
9168 (unsigned long) ping_pHYs_x_resolution);
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " y_resolution=%lu",
9171 (unsigned long) ping_pHYs_y_resolution);
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9174 (unsigned long) ping_pHYs_unit_type);
9179 #if defined(PNG_oFFs_SUPPORTED)
9180 if (ping_exclude_oFFs == MagickFalse)
9182 if (image->page.x || image->page.y)
9184 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9185 (png_int_32) image->page.y, 0);
9187 if (logging != MagickFalse)
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9189 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9190 (int) image->page.x, (int) image->page.y);
9195 if (mng_info->need_blob != MagickFalse)
9197 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9199 png_error(ping,"WriteBlob Failed");
9201 ping_have_blob=MagickTrue;
9204 png_write_info_before_PLTE(ping, ping_info);
9206 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9208 if (logging != MagickFalse)
9210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9211 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9214 if (ping_color_type == 3)
9215 (void) png_set_tRNS(ping, ping_info,
9222 (void) png_set_tRNS(ping, ping_info,
9227 if (logging != MagickFalse)
9229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230 " tRNS color =(%d,%d,%d)",
9231 (int) ping_trans_color.red,
9232 (int) ping_trans_color.green,
9233 (int) ping_trans_color.blue);
9238 /* write any png-chunk-b profiles */
9239 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9241 png_write_info(ping,ping_info);
9243 /* write any PNG-chunk-m profiles */
9244 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9246 if (ping_exclude_vpAg == MagickFalse)
9248 if ((image->page.width != 0 && image->page.width != image->columns) ||
9249 (image->page.height != 0 && image->page.height != image->rows))
9254 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9255 PNGType(chunk,mng_vpAg);
9256 LogPNGChunk(logging,mng_vpAg,9L);
9257 PNGLong(chunk+4,(png_uint_32) image->page.width);
9258 PNGLong(chunk+8,(png_uint_32) image->page.height);
9259 chunk[12]=0; /* unit = pixels */
9260 (void) WriteBlob(image,13,chunk);
9261 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9265 #if (PNG_LIBPNG_VER == 10206)
9266 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9267 #define PNG_HAVE_IDAT 0x04
9268 ping->mode |= PNG_HAVE_IDAT;
9269 #undef PNG_HAVE_IDAT
9272 png_set_packing(ping);
9276 rowbytes=image->columns;
9277 if (image_depth > 8)
9279 switch (ping_color_type)
9281 case PNG_COLOR_TYPE_RGB:
9285 case PNG_COLOR_TYPE_GRAY_ALPHA:
9289 case PNG_COLOR_TYPE_RGBA:
9297 if (logging != MagickFalse)
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Writing PNG image data");
9302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9305 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9306 sizeof(*ping_pixels));
9308 if (ping_pixels == (unsigned char *) NULL)
9309 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9312 Initialize image scanlines.
9314 if (setjmp(png_jmpbuf(ping)))
9320 if (image_info->verbose)
9321 (void) printf("PNG write has failed.\n");
9323 png_destroy_write_struct(&ping,&ping_info);
9324 if (quantum_info != (QuantumInfo *) NULL)
9325 quantum_info=DestroyQuantumInfo(quantum_info);
9326 if (ping_pixels != (unsigned char *) NULL)
9327 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9328 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9329 UnlockSemaphoreInfo(ping_semaphore);
9331 if (ping_have_blob != MagickFalse)
9332 (void) CloseBlob(image);
9333 image_info=DestroyImageInfo(image_info);
9334 image=DestroyImage(image);
9335 return(MagickFalse);
9337 quantum_info=AcquireQuantumInfo(image_info,image);
9338 if (quantum_info == (QuantumInfo *) NULL)
9339 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9340 quantum_info->format=UndefinedQuantumFormat;
9341 quantum_info->depth=image_depth;
9342 num_passes=png_set_interlace_handling(ping);
9344 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9345 !mng_info->write_png32) &&
9346 (mng_info->IsPalette ||
9347 (image_info->type == BilevelType)) &&
9348 image_matte == MagickFalse &&
9349 ping_have_non_bw == MagickFalse)
9351 /* Palette, Bilevel, or Opaque Monochrome */
9352 register const PixelPacket
9355 quantum_info->depth=8;
9356 for (pass=0; pass < num_passes; pass++)
9359 Convert PseudoClass image to a PNG monochrome image.
9361 for (y=0; y < (ssize_t) image->rows; y++)
9363 if (logging != MagickFalse && y == 0)
9364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365 " Writing row of pixels (0)");
9367 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9369 if (p == (const PixelPacket *) NULL)
9372 if (mng_info->IsPalette)
9374 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9375 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9376 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9377 mng_info->write_png_depth &&
9378 mng_info->write_png_depth != old_bit_depth)
9380 /* Undo pixel scaling */
9381 for (i=0; i < (ssize_t) image->columns; i++)
9382 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9383 >> (8-old_bit_depth));
9389 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9390 quantum_info,RedQuantum,ping_pixels,&image->exception);
9393 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9394 for (i=0; i < (ssize_t) image->columns; i++)
9395 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9398 if (logging != MagickFalse && y == 0)
9399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400 " Writing row of pixels (1)");
9402 png_write_row(ping,ping_pixels);
9404 if (image->previous == (Image *) NULL)
9406 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9407 if (status == MagickFalse)
9413 else /* Not Palette, Bilevel, or Opaque Monochrome */
9415 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9416 !mng_info->write_png32) &&
9417 (image_matte != MagickFalse ||
9418 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9419 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9421 register const PixelPacket
9424 for (pass=0; pass < num_passes; pass++)
9427 for (y=0; y < (ssize_t) image->rows; y++)
9429 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9431 if (p == (const PixelPacket *) NULL)
9434 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9436 if (mng_info->IsPalette)
9437 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9438 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9441 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9442 quantum_info,RedQuantum,ping_pixels,&image->exception);
9444 if (logging != MagickFalse && y == 0)
9445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9446 " Writing GRAY PNG pixels (2)");
9449 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9451 if (logging != MagickFalse && y == 0)
9452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453 " Writing GRAY_ALPHA PNG pixels (2)");
9455 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9456 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9459 if (logging != MagickFalse && y == 0)
9460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9461 " Writing row of pixels (2)");
9463 png_write_row(ping,ping_pixels);
9466 if (image->previous == (Image *) NULL)
9468 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9469 if (status == MagickFalse)
9477 register const PixelPacket
9480 for (pass=0; pass < num_passes; pass++)
9482 if ((image_depth > 8) || (mng_info->write_png24 ||
9483 mng_info->write_png32 ||
9484 (!mng_info->write_png8 && !mng_info->IsPalette)))
9486 for (y=0; y < (ssize_t) image->rows; y++)
9488 p=GetVirtualPixels(image,0,y,image->columns,1,
9491 if (p == (const PixelPacket *) NULL)
9494 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9496 if (image->storage_class == DirectClass)
9497 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9498 quantum_info,RedQuantum,ping_pixels,&image->exception);
9501 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9502 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9505 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9507 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9508 quantum_info,GrayAlphaQuantum,ping_pixels,
9511 if (logging != MagickFalse && y == 0)
9512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513 " Writing GRAY_ALPHA PNG pixels (3)");
9516 else if (image_matte != MagickFalse)
9517 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9518 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9521 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9522 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9524 if (logging != MagickFalse && y == 0)
9525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9526 " Writing row of pixels (3)");
9528 png_write_row(ping,ping_pixels);
9533 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9534 mng_info->write_png32 ||
9535 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9537 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9538 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9540 if (logging != MagickFalse)
9541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9542 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9544 quantum_info->depth=8;
9548 for (y=0; y < (ssize_t) image->rows; y++)
9550 if (logging != MagickFalse && y == 0)
9551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9552 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9554 p=GetVirtualPixels(image,0,y,image->columns,1,
9557 if (p == (const PixelPacket *) NULL)
9560 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9562 quantum_info->depth=image->depth;
9564 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9565 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9568 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9570 if (logging != MagickFalse && y == 0)
9571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572 " Writing GRAY_ALPHA PNG pixels (4)");
9574 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9575 quantum_info,GrayAlphaQuantum,ping_pixels,
9581 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9582 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9584 if (logging != MagickFalse && y <= 2)
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Writing row of non-gray pixels (4)");
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9591 (int)ping_pixels[0],(int)ping_pixels[1]);
9594 png_write_row(ping,ping_pixels);
9598 if (image->previous == (Image *) NULL)
9600 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9601 if (status == MagickFalse)
9608 if (quantum_info != (QuantumInfo *) NULL)
9609 quantum_info=DestroyQuantumInfo(quantum_info);
9611 if (logging != MagickFalse)
9613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614 " Wrote PNG image data");
9616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9617 " Width: %.20g",(double) ping_width);
9619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620 " Height: %.20g",(double) ping_height);
9622 if (mng_info->write_png_depth)
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629 " PNG bit-depth written: %d",ping_bit_depth);
9631 if (mng_info->write_png_colortype)
9633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9634 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9638 " PNG color-type written: %d",ping_color_type);
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641 " PNG Interlace method: %d",ping_interlace_method);
9644 Generate text chunks.
9646 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9648 ResetImagePropertyIterator(image);
9649 property=GetNextImageProperty(image);
9650 while (property != (const char *) NULL)
9655 value=GetImageProperty(image,property);
9656 if (ping_exclude_pHYs != MagickFalse ||
9657 LocaleCompare(property,"density") != 0 ||
9658 LocaleCompare(property,"units") != 0)
9660 if (value != (const char *) NULL)
9662 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9663 text[0].key=(char *) property;
9664 text[0].text=(char *) value;
9665 text[0].text_length=strlen(value);
9667 if (ping_exclude_tEXt != MagickFalse)
9668 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9670 else if (ping_exclude_zTXt != MagickFalse)
9671 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9675 text[0].compression=image_info->compression == NoCompression ||
9676 (image_info->compression == UndefinedCompression &&
9677 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9678 PNG_TEXT_COMPRESSION_zTXt ;
9681 if (logging != MagickFalse)
9683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9684 " Setting up text chunk");
9686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9687 " keyword: %s",text[0].key);
9690 png_set_text(ping,ping_info,text,1);
9691 png_free(ping,text);
9694 property=GetNextImageProperty(image);
9698 /* write any PNG-chunk-e profiles */
9699 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9701 if (logging != MagickFalse)
9702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9703 " Writing PNG end info");
9705 png_write_end(ping,ping_info);
9707 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9709 if (mng_info->page.x || mng_info->page.y ||
9710 (ping_width != mng_info->page.width) ||
9711 (ping_height != mng_info->page.height))
9717 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9719 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9720 PNGType(chunk,mng_FRAM);
9721 LogPNGChunk(logging,mng_FRAM,27L);
9723 chunk[5]=0; /* frame name separator (no name) */
9724 chunk[6]=1; /* flag for changing delay, for next frame only */
9725 chunk[7]=0; /* flag for changing frame timeout */
9726 chunk[8]=1; /* flag for changing frame clipping for next frame */
9727 chunk[9]=0; /* flag for changing frame sync_id */
9728 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9729 chunk[14]=0; /* clipping boundaries delta type */
9730 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9732 (png_uint_32) (mng_info->page.x + ping_width));
9733 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9735 (png_uint_32) (mng_info->page.y + ping_height));
9736 (void) WriteBlob(image,31,chunk);
9737 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9738 mng_info->old_framing_mode=4;
9739 mng_info->framing_mode=1;
9743 mng_info->framing_mode=3;
9745 if (mng_info->write_mng && !mng_info->need_fram &&
9746 ((int) image->dispose == 3))
9747 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9748 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9749 "`%s'",image->filename);
9755 png_destroy_write_struct(&ping,&ping_info);
9757 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9759 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9760 UnlockSemaphoreInfo(ping_semaphore);
9763 if (ping_have_blob != MagickFalse)
9764 (void) CloseBlob(image);
9766 image_info=DestroyImageInfo(image_info);
9767 image=DestroyImage(image);
9769 /* Store bit depth actually written */
9770 s[0]=(char) ping_bit_depth;
9773 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9775 if (logging != MagickFalse)
9776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9777 " exit WriteOnePNGImage()");
9780 /* End write one PNG image */
9784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9788 % W r i t e P N G I m a g e %
9792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9794 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9795 % Multiple-image Network Graphics (MNG) image file.
9797 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9799 % The format of the WritePNGImage method is:
9801 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9803 % A description of each parameter follows:
9805 % o image_info: the image info.
9807 % o image: The image.
9809 % Returns MagickTrue on success, MagickFalse on failure.
9811 % Communicating with the PNG encoder:
9813 % While the datastream written is always in PNG format and normally would
9814 % be given the "png" file extension, this method also writes the following
9815 % pseudo-formats which are subsets of PNG:
9817 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9818 % a depth greater than 8, the depth is reduced. If transparency
9819 % is present, the tRNS chunk must only have values 0 and 255
9820 % (i.e., transparency is binary: fully opaque or fully
9821 % transparent). If other values are present they will be
9822 % 50%-thresholded to binary transparency. If more than 256
9823 % colors are present, they will be quantized to the 3-3-2
9824 % palette. If you want better quantization or dithering of
9825 % the colors or alpha, you need to do it before calling the
9826 % PNG encoder. The pixels contain 8-bit indices even if
9827 % they could be represented with 1, 2, or 4 bits. Grayscale
9828 % images will be written as indexed PNG files even though the
9829 % PNG grayscale type might be slightly more efficient. Please
9830 % note that writing to the PNG8 format may result in loss
9831 % of color and alpha data.
9833 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9834 % chunk can be present to convey binary transparency by naming
9835 % one of the colors as transparent. The only loss incurred
9836 % is reduction of sample depth to 8. If the image has more
9837 % than one transparent color, has semitransparent pixels, or
9838 % has an opaque pixel with the same RGB components as the
9839 % transparent color, an image is not written.
9841 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9842 % transparency is permitted, i.e., the alpha sample for
9843 % each pixel can have any value from 0 to 255. The alpha
9844 % channel is present even if the image is fully opaque.
9845 % The only loss in data is the reduction of the sample depth
9848 % o -define: For more precise control of the PNG output, you can use the
9849 % Image options "png:bit-depth" and "png:color-type". These
9850 % can be set from the commandline with "-define" and also
9851 % from the application programming interfaces. The options
9852 % are case-independent and are converted to lowercase before
9853 % being passed to this encoder.
9855 % png:color-type can be 0, 2, 3, 4, or 6.
9857 % When png:color-type is 0 (Grayscale), png:bit-depth can
9858 % be 1, 2, 4, 8, or 16.
9860 % When png:color-type is 2 (RGB), png:bit-depth can
9863 % When png:color-type is 3 (Indexed), png:bit-depth can
9864 % be 1, 2, 4, or 8. This refers to the number of bits
9865 % used to store the index. The color samples always have
9866 % bit-depth 8 in indexed PNG files.
9868 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9869 % png:bit-depth can be 8 or 16.
9871 % If the image cannot be written without loss with the requested bit-depth
9872 % and color-type, a PNG file will not be written, and the encoder will
9873 % return MagickFalse.
9875 % Since image encoders should not be responsible for the "heavy lifting",
9876 % the user should make sure that ImageMagick has already reduced the
9877 % image depth and number of colors and limit transparency to binary
9878 % transparency prior to attempting to write the image with depth, color,
9879 % or transparency limitations.
9881 % TODO: Enforce the previous paragraph.
9883 % Note that another definition, "png:bit-depth-written" exists, but it
9884 % is not intended for external use. It is only used internally by the
9885 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9887 % It is possible to request that the PNG encoder write previously-formatted
9888 % ancillary chunks in the output PNG file, using the "-profile" commandline
9889 % option as shown below or by setting the profile via a programming
9892 % -profile PNG-chunk-x:<file>
9894 % where x is a location flag and <file> is a file containing the chunk
9895 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9896 % This encoder will compute the chunk length and CRC, so those must not
9897 % be included in the file.
9899 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9900 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9901 % of the same type, then add a short unique string after the "x" to prevent
9902 % subsequent profiles from overwriting the preceding ones, e.g.,
9904 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9906 % As of version 6.6.6 the following optimizations are always done:
9908 % o 32-bit depth is reduced to 16.
9909 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9910 % high byte and low byte are identical.
9911 % o Palette is sorted to remove unused entries and to put a
9912 % transparent color first, if BUILD_PNG_PALETTE is defined.
9913 % o Opaque matte channel is removed when writing an indexed PNG.
9914 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9915 % this can be done without loss and a larger bit depth N was not
9916 % requested via the "-define PNG:bit-depth=N" option.
9917 % o If matte channel is present but only one transparent color is
9918 % present, RGB+tRNS is written instead of RGBA
9919 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9920 % was requested when converting an opaque image).
9922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9924 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9946 assert(image_info != (const ImageInfo *) NULL);
9947 assert(image_info->signature == MagickSignature);
9948 assert(image != (Image *) NULL);
9949 assert(image->signature == MagickSignature);
9950 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9951 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9953 Allocate a MngInfo structure.
9955 have_mng_structure=MagickFalse;
9956 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9958 if (mng_info == (MngInfo *) NULL)
9959 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9962 Initialize members of the MngInfo structure.
9964 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9965 mng_info->image=image;
9966 mng_info->equal_backgrounds=MagickTrue;
9967 have_mng_structure=MagickTrue;
9969 /* See if user has requested a specific PNG subformat */
9971 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9972 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9973 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9975 if (mng_info->write_png8)
9977 mng_info->write_png_colortype = /* 3 */ 4;
9978 mng_info->write_png_depth = 8;
9982 if (mng_info->write_png24)
9984 mng_info->write_png_colortype = /* 2 */ 3;
9985 mng_info->write_png_depth = 8;
9988 if (image->matte == MagickTrue)
9989 (void) SetImageType(image,TrueColorMatteType);
9992 (void) SetImageType(image,TrueColorType);
9994 (void) SyncImage(image);
9997 if (mng_info->write_png32)
9999 mng_info->write_png_colortype = /* 6 */ 7;
10000 mng_info->write_png_depth = 8;
10003 if (image->matte == MagickTrue)
10004 (void) SetImageType(image,TrueColorMatteType);
10007 (void) SetImageType(image,TrueColorType);
10009 (void) SyncImage(image);
10012 value=GetImageOption(image_info,"png:bit-depth");
10014 if (value != (char *) NULL)
10016 if (LocaleCompare(value,"1") == 0)
10017 mng_info->write_png_depth = 1;
10019 else if (LocaleCompare(value,"2") == 0)
10020 mng_info->write_png_depth = 2;
10022 else if (LocaleCompare(value,"4") == 0)
10023 mng_info->write_png_depth = 4;
10025 else if (LocaleCompare(value,"8") == 0)
10026 mng_info->write_png_depth = 8;
10028 else if (LocaleCompare(value,"16") == 0)
10029 mng_info->write_png_depth = 16;
10032 (void) ThrowMagickException(&image->exception,
10033 GetMagickModule(),CoderWarning,
10034 "ignoring invalid defined png:bit-depth",
10037 if (logging != MagickFalse)
10038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10042 value=GetImageOption(image_info,"png:color-type");
10044 if (value != (char *) NULL)
10046 /* We must store colortype+1 because 0 is a valid colortype */
10047 if (LocaleCompare(value,"0") == 0)
10048 mng_info->write_png_colortype = 1;
10050 else if (LocaleCompare(value,"2") == 0)
10051 mng_info->write_png_colortype = 3;
10053 else if (LocaleCompare(value,"3") == 0)
10054 mng_info->write_png_colortype = 4;
10056 else if (LocaleCompare(value,"4") == 0)
10057 mng_info->write_png_colortype = 5;
10059 else if (LocaleCompare(value,"6") == 0)
10060 mng_info->write_png_colortype = 7;
10063 (void) ThrowMagickException(&image->exception,
10064 GetMagickModule(),CoderWarning,
10065 "ignoring invalid defined png:color-type",
10068 if (logging != MagickFalse)
10069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10070 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10073 /* Check for chunks to be excluded:
10075 * The default is to not exclude any known chunks except for any
10076 * listed in the "unused_chunks" array, above.
10078 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10079 * define (in the image properties or in the image artifacts)
10080 * or via a mng_info member. For convenience, in addition
10081 * to or instead of a comma-separated list of chunks, the
10082 * "exclude-chunk" string can be simply "all" or "none".
10084 * The exclude-chunk define takes priority over the mng_info.
10086 * A "PNG:include-chunk" define takes priority over both the
10087 * mng_info and the "PNG:exclude-chunk" define. Like the
10088 * "exclude-chunk" string, it can define "all" or "none" as
10089 * well as a comma-separated list. Chunks that are unknown to
10090 * ImageMagick are always excluded, regardless of their "copy-safe"
10091 * status according to the PNG specification, and even if they
10092 * appear in the "include-chunk" list.
10094 * Finally, all chunks listed in the "unused_chunks" array are
10095 * automatically excluded, regardless of the other instructions
10098 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10099 * will not be written and the gAMA chunk will only be written if it
10100 * is not between .45 and .46, or approximately (1.0/2.2).
10102 * If you exclude tRNS and the image has transparency, the colortype
10103 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10105 * The -strip option causes StripImage() to set the png:include-chunk
10106 * artifact to "none,gama".
10109 mng_info->ping_exclude_bKGD=MagickFalse;
10110 mng_info->ping_exclude_cHRM=MagickFalse;
10111 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10112 mng_info->ping_exclude_gAMA=MagickFalse;
10113 mng_info->ping_exclude_cHRM=MagickFalse;
10114 mng_info->ping_exclude_iCCP=MagickFalse;
10115 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10116 mng_info->ping_exclude_oFFs=MagickFalse;
10117 mng_info->ping_exclude_pHYs=MagickFalse;
10118 mng_info->ping_exclude_sRGB=MagickFalse;
10119 mng_info->ping_exclude_tEXt=MagickFalse;
10120 mng_info->ping_exclude_tRNS=MagickFalse;
10121 mng_info->ping_exclude_vpAg=MagickFalse;
10122 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10123 mng_info->ping_exclude_zTXt=MagickFalse;
10125 excluding=MagickFalse;
10127 for (source=0; source<1; source++)
10131 value=GetImageArtifact(image,"png:exclude-chunk");
10134 value=GetImageArtifact(image,"png:exclude-chunks");
10138 value=GetImageOption(image_info,"png:exclude-chunk");
10141 value=GetImageOption(image_info,"png:exclude-chunks");
10150 excluding=MagickTrue;
10152 if (logging != MagickFalse)
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " png:exclude-chunk=%s found in image artifacts.\n", value);
10158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159 " png:exclude-chunk=%s found in image properties.\n", value);
10162 last=strlen(value);
10164 for (i=0; i<(int) last; i+=5)
10167 if (LocaleNCompare(value+i,"all",3) == 0)
10169 mng_info->ping_exclude_bKGD=MagickTrue;
10170 mng_info->ping_exclude_cHRM=MagickTrue;
10171 mng_info->ping_exclude_EXIF=MagickTrue;
10172 mng_info->ping_exclude_gAMA=MagickTrue;
10173 mng_info->ping_exclude_iCCP=MagickTrue;
10174 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10175 mng_info->ping_exclude_oFFs=MagickTrue;
10176 mng_info->ping_exclude_pHYs=MagickTrue;
10177 mng_info->ping_exclude_sRGB=MagickTrue;
10178 mng_info->ping_exclude_tEXt=MagickTrue;
10179 mng_info->ping_exclude_tRNS=MagickTrue;
10180 mng_info->ping_exclude_vpAg=MagickTrue;
10181 mng_info->ping_exclude_zCCP=MagickTrue;
10182 mng_info->ping_exclude_zTXt=MagickTrue;
10186 if (LocaleNCompare(value+i,"none",4) == 0)
10188 mng_info->ping_exclude_bKGD=MagickFalse;
10189 mng_info->ping_exclude_cHRM=MagickFalse;
10190 mng_info->ping_exclude_EXIF=MagickFalse;
10191 mng_info->ping_exclude_gAMA=MagickFalse;
10192 mng_info->ping_exclude_iCCP=MagickFalse;
10193 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10194 mng_info->ping_exclude_oFFs=MagickFalse;
10195 mng_info->ping_exclude_pHYs=MagickFalse;
10196 mng_info->ping_exclude_sRGB=MagickFalse;
10197 mng_info->ping_exclude_tEXt=MagickFalse;
10198 mng_info->ping_exclude_tRNS=MagickFalse;
10199 mng_info->ping_exclude_vpAg=MagickFalse;
10200 mng_info->ping_exclude_zCCP=MagickFalse;
10201 mng_info->ping_exclude_zTXt=MagickFalse;
10204 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10205 mng_info->ping_exclude_bKGD=MagickTrue;
10207 if (LocaleNCompare(value+i,"chrm",4) == 0)
10208 mng_info->ping_exclude_cHRM=MagickTrue;
10210 if (LocaleNCompare(value+i,"exif",4) == 0)
10211 mng_info->ping_exclude_EXIF=MagickTrue;
10213 if (LocaleNCompare(value+i,"gama",4) == 0)
10214 mng_info->ping_exclude_gAMA=MagickTrue;
10216 if (LocaleNCompare(value+i,"iccp",4) == 0)
10217 mng_info->ping_exclude_iCCP=MagickTrue;
10220 if (LocaleNCompare(value+i,"itxt",4) == 0)
10221 mng_info->ping_exclude_iTXt=MagickTrue;
10224 if (LocaleNCompare(value+i,"gama",4) == 0)
10225 mng_info->ping_exclude_gAMA=MagickTrue;
10227 if (LocaleNCompare(value+i,"offs",4) == 0)
10228 mng_info->ping_exclude_oFFs=MagickTrue;
10230 if (LocaleNCompare(value+i,"phys",4) == 0)
10231 mng_info->ping_exclude_pHYs=MagickTrue;
10233 if (LocaleNCompare(value+i,"srgb",4) == 0)
10234 mng_info->ping_exclude_sRGB=MagickTrue;
10236 if (LocaleNCompare(value+i,"text",4) == 0)
10237 mng_info->ping_exclude_tEXt=MagickTrue;
10239 if (LocaleNCompare(value+i,"trns",4) == 0)
10240 mng_info->ping_exclude_tRNS=MagickTrue;
10242 if (LocaleNCompare(value+i,"vpag",4) == 0)
10243 mng_info->ping_exclude_vpAg=MagickTrue;
10245 if (LocaleNCompare(value+i,"zccp",4) == 0)
10246 mng_info->ping_exclude_zCCP=MagickTrue;
10248 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10249 mng_info->ping_exclude_zTXt=MagickTrue;
10255 for (source=0; source<1; source++)
10259 value=GetImageArtifact(image,"png:include-chunk");
10262 value=GetImageArtifact(image,"png:include-chunks");
10266 value=GetImageOption(image_info,"png:include-chunk");
10269 value=GetImageOption(image_info,"png:include-chunks");
10277 excluding=MagickTrue;
10279 if (logging != MagickFalse)
10282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283 " png:include-chunk=%s found in image artifacts.\n", value);
10285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286 " png:include-chunk=%s found in image properties.\n", value);
10289 last=strlen(value);
10291 for (i=0; i<(int) last; i+=5)
10293 if (LocaleNCompare(value+i,"all",3) == 0)
10295 mng_info->ping_exclude_bKGD=MagickFalse;
10296 mng_info->ping_exclude_cHRM=MagickFalse;
10297 mng_info->ping_exclude_EXIF=MagickFalse;
10298 mng_info->ping_exclude_gAMA=MagickFalse;
10299 mng_info->ping_exclude_iCCP=MagickFalse;
10300 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10301 mng_info->ping_exclude_oFFs=MagickFalse;
10302 mng_info->ping_exclude_pHYs=MagickFalse;
10303 mng_info->ping_exclude_sRGB=MagickFalse;
10304 mng_info->ping_exclude_tEXt=MagickFalse;
10305 mng_info->ping_exclude_tRNS=MagickFalse;
10306 mng_info->ping_exclude_vpAg=MagickFalse;
10307 mng_info->ping_exclude_zCCP=MagickFalse;
10308 mng_info->ping_exclude_zTXt=MagickFalse;
10312 if (LocaleNCompare(value+i,"none",4) == 0)
10314 mng_info->ping_exclude_bKGD=MagickTrue;
10315 mng_info->ping_exclude_cHRM=MagickTrue;
10316 mng_info->ping_exclude_EXIF=MagickTrue;
10317 mng_info->ping_exclude_gAMA=MagickTrue;
10318 mng_info->ping_exclude_iCCP=MagickTrue;
10319 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10320 mng_info->ping_exclude_oFFs=MagickTrue;
10321 mng_info->ping_exclude_pHYs=MagickTrue;
10322 mng_info->ping_exclude_sRGB=MagickTrue;
10323 mng_info->ping_exclude_tEXt=MagickTrue;
10324 mng_info->ping_exclude_tRNS=MagickTrue;
10325 mng_info->ping_exclude_vpAg=MagickTrue;
10326 mng_info->ping_exclude_zCCP=MagickTrue;
10327 mng_info->ping_exclude_zTXt=MagickTrue;
10330 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10331 mng_info->ping_exclude_bKGD=MagickFalse;
10333 if (LocaleNCompare(value+i,"chrm",4) == 0)
10334 mng_info->ping_exclude_cHRM=MagickFalse;
10336 if (LocaleNCompare(value+i,"exif",4) == 0)
10337 mng_info->ping_exclude_EXIF=MagickFalse;
10339 if (LocaleNCompare(value+i,"gama",4) == 0)
10340 mng_info->ping_exclude_gAMA=MagickFalse;
10342 if (LocaleNCompare(value+i,"iccp",4) == 0)
10343 mng_info->ping_exclude_iCCP=MagickFalse;
10346 if (LocaleNCompare(value+i,"itxt",4) == 0)
10347 mng_info->ping_exclude_iTXt=MagickFalse;
10350 if (LocaleNCompare(value+i,"gama",4) == 0)
10351 mng_info->ping_exclude_gAMA=MagickFalse;
10353 if (LocaleNCompare(value+i,"offs",4) == 0)
10354 mng_info->ping_exclude_oFFs=MagickFalse;
10356 if (LocaleNCompare(value+i,"phys",4) == 0)
10357 mng_info->ping_exclude_pHYs=MagickFalse;
10359 if (LocaleNCompare(value+i,"srgb",4) == 0)
10360 mng_info->ping_exclude_sRGB=MagickFalse;
10362 if (LocaleNCompare(value+i,"text",4) == 0)
10363 mng_info->ping_exclude_tEXt=MagickFalse;
10365 if (LocaleNCompare(value+i,"trns",4) == 0)
10366 mng_info->ping_exclude_tRNS=MagickFalse;
10368 if (LocaleNCompare(value+i,"vpag",4) == 0)
10369 mng_info->ping_exclude_vpAg=MagickFalse;
10371 if (LocaleNCompare(value+i,"zccp",4) == 0)
10372 mng_info->ping_exclude_zCCP=MagickFalse;
10374 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10375 mng_info->ping_exclude_zTXt=MagickFalse;
10381 if (excluding != MagickFalse && logging != MagickFalse)
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " Chunks to be excluded from the output PNG:");
10385 if (mng_info->ping_exclude_bKGD != MagickFalse)
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 if (mng_info->ping_exclude_cHRM != MagickFalse)
10389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391 if (mng_info->ping_exclude_EXIF != MagickFalse)
10392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10394 if (mng_info->ping_exclude_gAMA != MagickFalse)
10395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10397 if (mng_info->ping_exclude_iCCP != MagickFalse)
10398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10401 if (mng_info->ping_exclude_iTXt != MagickFalse)
10402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10405 if (mng_info->ping_exclude_oFFs != MagickFalse)
10406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10408 if (mng_info->ping_exclude_pHYs != MagickFalse)
10409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10411 if (mng_info->ping_exclude_sRGB != MagickFalse)
10412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10414 if (mng_info->ping_exclude_tEXt != MagickFalse)
10415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10417 if (mng_info->ping_exclude_tRNS != MagickFalse)
10418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10420 if (mng_info->ping_exclude_vpAg != MagickFalse)
10421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10423 if (mng_info->ping_exclude_zCCP != MagickFalse)
10424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426 if (mng_info->ping_exclude_zTXt != MagickFalse)
10427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 mng_info->need_blob = MagickTrue;
10433 status=WriteOnePNGImage(mng_info,image_info,image);
10435 MngInfoFreeStruct(mng_info,&have_mng_structure);
10437 if (logging != MagickFalse)
10438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10443 #if defined(JNG_SUPPORTED)
10445 /* Write one JNG image */
10446 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10447 const ImageInfo *image_info,Image *image)
10468 jng_alpha_compression_method,
10469 jng_alpha_sample_depth,
10476 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10477 " Enter WriteOneJNGImage()");
10479 blob=(unsigned char *) NULL;
10480 jpeg_image=(Image *) NULL;
10481 jpeg_image_info=(ImageInfo *) NULL;
10484 transparent=image_info->type==GrayscaleMatteType ||
10485 image_info->type==TrueColorMatteType;
10487 jng_alpha_sample_depth=0;
10488 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10489 jng_alpha_compression_method=0;
10491 if (image->matte != MagickFalse)
10493 /* if any pixels are transparent */
10494 transparent=MagickTrue;
10495 if (image_info->compression==JPEGCompression)
10496 jng_alpha_compression_method=8;
10503 /* Create JPEG blob, image, and image_info */
10504 if (logging != MagickFalse)
10505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10506 " Creating jpeg_image_info for opacity.");
10508 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10510 if (jpeg_image_info == (ImageInfo *) NULL)
10511 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10513 if (logging != MagickFalse)
10514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10515 " Creating jpeg_image.");
10517 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10519 if (jpeg_image == (Image *) NULL)
10520 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10522 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10523 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10524 status=NegateImage(jpeg_image,MagickFalse);
10525 jpeg_image->matte=MagickFalse;
10527 if (jng_quality >= 1000)
10528 jpeg_image_info->quality=jng_quality/1000;
10531 jpeg_image_info->quality=jng_quality;
10533 jpeg_image_info->type=GrayscaleType;
10534 (void) SetImageType(jpeg_image,GrayscaleType);
10535 (void) AcquireUniqueFilename(jpeg_image->filename);
10536 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10537 "%s",jpeg_image->filename);
10540 /* To do: check bit depth of PNG alpha channel */
10542 /* Check if image is grayscale. */
10543 if (image_info->type != TrueColorMatteType && image_info->type !=
10544 TrueColorType && ImageIsGray(image))
10549 if (jng_alpha_compression_method==0)
10554 /* Encode opacity as a grayscale PNG blob */
10555 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10556 &image->exception);
10557 if (logging != MagickFalse)
10558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559 " Creating PNG blob.");
10562 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10563 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10564 jpeg_image_info->interlace=NoInterlace;
10566 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10567 &image->exception);
10569 /* Retrieve sample depth used */
10570 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10571 if (value != (char *) NULL)
10572 jng_alpha_sample_depth= (unsigned int) value[0];
10576 /* Encode opacity as a grayscale JPEG blob */
10578 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10579 &image->exception);
10581 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10582 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10583 jpeg_image_info->interlace=NoInterlace;
10584 if (logging != MagickFalse)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Creating blob.");
10587 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10588 &image->exception);
10589 jng_alpha_sample_depth=8;
10591 if (logging != MagickFalse)
10592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593 " Successfully read jpeg_image into a blob, length=%.20g.",
10597 /* Destroy JPEG image and image_info */
10598 jpeg_image=DestroyImage(jpeg_image);
10599 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10600 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10603 /* Write JHDR chunk */
10604 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10605 PNGType(chunk,mng_JHDR);
10606 LogPNGChunk(logging,mng_JHDR,16L);
10607 PNGLong(chunk+4,(png_uint_32) image->columns);
10608 PNGLong(chunk+8,(png_uint_32) image->rows);
10609 chunk[12]=jng_color_type;
10610 chunk[13]=8; /* sample depth */
10611 chunk[14]=8; /*jng_image_compression_method */
10612 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10613 chunk[16]=jng_alpha_sample_depth;
10614 chunk[17]=jng_alpha_compression_method;
10615 chunk[18]=0; /*jng_alpha_filter_method */
10616 chunk[19]=0; /*jng_alpha_interlace_method */
10617 (void) WriteBlob(image,20,chunk);
10618 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10619 if (logging != MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " JNG width:%15lu",(unsigned long) image->columns);
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 " JNG height:%14lu",(unsigned long) image->rows);
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 " JNG color type:%10d",jng_color_type);
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " JNG sample depth:%8d",8);
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " JNG compression:%9d",8);
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " JNG interlace:%11d",0);
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " JNG alpha compression:%3d",jng_alpha_compression_method);
10645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 " JNG alpha filter:%8d",0);
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " JNG alpha interlace:%5d",0);
10652 /* Write any JNG-chunk-b profiles */
10653 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10656 Write leading ancillary chunks
10662 Write JNG bKGD chunk
10673 if (jng_color_type == 8 || jng_color_type == 12)
10677 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10678 PNGType(chunk,mng_bKGD);
10679 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10680 red=ScaleQuantumToChar(image->background_color.red);
10681 green=ScaleQuantumToChar(image->background_color.green);
10682 blue=ScaleQuantumToChar(image->background_color.blue);
10689 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10690 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10693 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10696 Write JNG sRGB chunk
10698 (void) WriteBlobMSBULong(image,1L);
10699 PNGType(chunk,mng_sRGB);
10700 LogPNGChunk(logging,mng_sRGB,1L);
10702 if (image->rendering_intent != UndefinedIntent)
10703 chunk[4]=(unsigned char)
10704 Magick_RenderingIntent_to_PNG_RenderingIntent(
10705 (image->rendering_intent));
10708 chunk[4]=(unsigned char)
10709 Magick_RenderingIntent_to_PNG_RenderingIntent(
10710 (PerceptualIntent));
10712 (void) WriteBlob(image,5,chunk);
10713 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10717 if (image->gamma != 0.0)
10720 Write JNG gAMA chunk
10722 (void) WriteBlobMSBULong(image,4L);
10723 PNGType(chunk,mng_gAMA);
10724 LogPNGChunk(logging,mng_gAMA,4L);
10725 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10726 (void) WriteBlob(image,8,chunk);
10727 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10730 if ((mng_info->equal_chrms == MagickFalse) &&
10731 (image->chromaticity.red_primary.x != 0.0))
10737 Write JNG cHRM chunk
10739 (void) WriteBlobMSBULong(image,32L);
10740 PNGType(chunk,mng_cHRM);
10741 LogPNGChunk(logging,mng_cHRM,32L);
10742 primary=image->chromaticity.white_point;
10743 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10744 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10745 primary=image->chromaticity.red_primary;
10746 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10747 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10748 primary=image->chromaticity.green_primary;
10749 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10750 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10751 primary=image->chromaticity.blue_primary;
10752 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10753 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10754 (void) WriteBlob(image,36,chunk);
10755 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10759 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10762 Write JNG pHYs chunk
10764 (void) WriteBlobMSBULong(image,9L);
10765 PNGType(chunk,mng_pHYs);
10766 LogPNGChunk(logging,mng_pHYs,9L);
10767 if (image->units == PixelsPerInchResolution)
10769 PNGLong(chunk+4,(png_uint_32)
10770 (image->x_resolution*100.0/2.54+0.5));
10772 PNGLong(chunk+8,(png_uint_32)
10773 (image->y_resolution*100.0/2.54+0.5));
10780 if (image->units == PixelsPerCentimeterResolution)
10782 PNGLong(chunk+4,(png_uint_32)
10783 (image->x_resolution*100.0+0.5));
10785 PNGLong(chunk+8,(png_uint_32)
10786 (image->y_resolution*100.0+0.5));
10793 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10794 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10798 (void) WriteBlob(image,13,chunk);
10799 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10802 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10805 Write JNG oFFs chunk
10807 (void) WriteBlobMSBULong(image,9L);
10808 PNGType(chunk,mng_oFFs);
10809 LogPNGChunk(logging,mng_oFFs,9L);
10810 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10811 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10813 (void) WriteBlob(image,13,chunk);
10814 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10816 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10818 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10819 PNGType(chunk,mng_vpAg);
10820 LogPNGChunk(logging,mng_vpAg,9L);
10821 PNGLong(chunk+4,(png_uint_32) image->page.width);
10822 PNGLong(chunk+8,(png_uint_32) image->page.height);
10823 chunk[12]=0; /* unit = pixels */
10824 (void) WriteBlob(image,13,chunk);
10825 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10831 if (jng_alpha_compression_method==0)
10839 /* Write IDAT chunk header */
10840 if (logging != MagickFalse)
10841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10842 " Write IDAT chunks from blob, length=%.20g.",(double)
10845 /* Copy IDAT chunks */
10848 for (i=8; i<(ssize_t) length; i+=len+12)
10850 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10853 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10855 /* Found an IDAT chunk. */
10856 (void) WriteBlobMSBULong(image,(size_t) len);
10857 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10858 (void) WriteBlob(image,(size_t) len+4,p);
10859 (void) WriteBlobMSBULong(image,
10860 crc32(0,p,(uInt) len+4));
10865 if (logging != MagickFalse)
10866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867 " Skipping %c%c%c%c chunk, length=%.20g.",
10868 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10875 /* Write JDAA chunk header */
10876 if (logging != MagickFalse)
10877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878 " Write JDAA chunk, length=%.20g.",(double) length);
10879 (void) WriteBlobMSBULong(image,(size_t) length);
10880 PNGType(chunk,mng_JDAA);
10881 LogPNGChunk(logging,mng_JDAA,length);
10882 /* Write JDAT chunk(s) data */
10883 (void) WriteBlob(image,4,chunk);
10884 (void) WriteBlob(image,length,blob);
10885 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10888 blob=(unsigned char *) RelinquishMagickMemory(blob);
10891 /* Encode image as a JPEG blob */
10892 if (logging != MagickFalse)
10893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10894 " Creating jpeg_image_info.");
10895 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10896 if (jpeg_image_info == (ImageInfo *) NULL)
10897 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10899 if (logging != MagickFalse)
10900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10901 " Creating jpeg_image.");
10903 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10904 if (jpeg_image == (Image *) NULL)
10905 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10906 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10908 (void) AcquireUniqueFilename(jpeg_image->filename);
10909 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10910 jpeg_image->filename);
10912 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10913 &image->exception);
10915 if (logging != MagickFalse)
10916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10917 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10918 (double) jpeg_image->rows);
10920 if (jng_color_type == 8 || jng_color_type == 12)
10921 jpeg_image_info->type=GrayscaleType;
10923 jpeg_image_info->quality=jng_quality % 1000;
10924 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10925 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10927 if (logging != MagickFalse)
10928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10929 " Creating blob.");
10931 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10933 if (logging != MagickFalse)
10935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10936 " Successfully read jpeg_image into a blob, length=%.20g.",
10939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10940 " Write JDAT chunk, length=%.20g.",(double) length);
10943 /* Write JDAT chunk(s) */
10944 (void) WriteBlobMSBULong(image,(size_t) length);
10945 PNGType(chunk,mng_JDAT);
10946 LogPNGChunk(logging,mng_JDAT,length);
10947 (void) WriteBlob(image,4,chunk);
10948 (void) WriteBlob(image,length,blob);
10949 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10951 jpeg_image=DestroyImage(jpeg_image);
10952 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10953 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10954 blob=(unsigned char *) RelinquishMagickMemory(blob);
10956 /* Write any JNG-chunk-e profiles */
10957 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10959 /* Write IEND chunk */
10960 (void) WriteBlobMSBULong(image,0L);
10961 PNGType(chunk,mng_IEND);
10962 LogPNGChunk(logging,mng_IEND,0);
10963 (void) WriteBlob(image,4,chunk);
10964 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10966 if (logging != MagickFalse)
10967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10968 " exit WriteOneJNGImage()");
10975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10979 % W r i t e J N G I m a g e %
10983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10985 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10987 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10989 % The format of the WriteJNGImage method is:
10991 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10993 % A description of each parameter follows:
10995 % o image_info: the image info.
10997 % o image: The image.
10999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11004 have_mng_structure,
11014 assert(image_info != (const ImageInfo *) NULL);
11015 assert(image_info->signature == MagickSignature);
11016 assert(image != (Image *) NULL);
11017 assert(image->signature == MagickSignature);
11018 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11019 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11020 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11021 if (status == MagickFalse)
11025 Allocate a MngInfo structure.
11027 have_mng_structure=MagickFalse;
11028 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11029 if (mng_info == (MngInfo *) NULL)
11030 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11032 Initialize members of the MngInfo structure.
11034 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11035 mng_info->image=image;
11036 have_mng_structure=MagickTrue;
11038 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11040 status=WriteOneJNGImage(mng_info,image_info,image);
11041 (void) CloseBlob(image);
11043 (void) CatchImageException(image);
11044 MngInfoFreeStruct(mng_info,&have_mng_structure);
11045 if (logging != MagickFalse)
11046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11053 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11062 have_mng_structure,
11065 volatile MagickBooleanType
11077 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11078 defined(PNG_MNG_FEATURES_SUPPORTED)
11081 all_images_are_gray,
11091 volatile unsigned int
11102 #if (PNG_LIBPNG_VER < 10200)
11103 if (image_info->verbose)
11104 printf("Your PNG library (libpng-%s) is rather old.\n",
11105 PNG_LIBPNG_VER_STRING);
11111 assert(image_info != (const ImageInfo *) NULL);
11112 assert(image_info->signature == MagickSignature);
11113 assert(image != (Image *) NULL);
11114 assert(image->signature == MagickSignature);
11115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11116 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11117 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11118 if (status == MagickFalse)
11122 Allocate a MngInfo structure.
11124 have_mng_structure=MagickFalse;
11125 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11126 if (mng_info == (MngInfo *) NULL)
11127 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11129 Initialize members of the MngInfo structure.
11131 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11132 mng_info->image=image;
11133 have_mng_structure=MagickTrue;
11134 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11137 * See if user has requested a specific PNG subformat to be used
11138 * for all of the PNGs in the MNG being written, e.g.,
11140 * convert *.png png8:animation.mng
11142 * To do: check -define png:bit_depth and png:color_type as well,
11143 * or perhaps use mng:bit_depth and mng:color_type instead for
11147 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11148 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11149 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11151 write_jng=MagickFalse;
11152 if (image_info->compression == JPEGCompression)
11153 write_jng=MagickTrue;
11155 mng_info->adjoin=image_info->adjoin &&
11156 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11158 if (logging != MagickFalse)
11160 /* Log some info about the input */
11164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11165 " Checking input image(s)");
11167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168 " Image_info depth: %.20g",(double) image_info->depth);
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11171 " Type: %d",image_info->type);
11174 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177 " Scene: %.20g",(double) scene++);
11179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11180 " Image depth: %.20g",(double) p->depth);
11183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190 if (p->storage_class == PseudoClass)
11191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192 " Storage class: PseudoClass");
11195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11196 " Storage class: DirectClass");
11199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200 " Number of colors: %.20g",(double) p->colors);
11203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204 " Number of colors: unspecified");
11206 if (mng_info->adjoin == MagickFalse)
11211 use_global_plte=MagickFalse;
11212 all_images_are_gray=MagickFalse;
11213 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11214 need_local_plte=MagickTrue;
11216 need_defi=MagickFalse;
11217 need_matte=MagickFalse;
11218 mng_info->framing_mode=1;
11219 mng_info->old_framing_mode=1;
11222 if (image_info->page != (char *) NULL)
11225 Determine image bounding box.
11227 SetGeometry(image,&mng_info->page);
11228 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11229 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11241 mng_info->page=image->page;
11242 need_geom=MagickTrue;
11243 if (mng_info->page.width || mng_info->page.height)
11244 need_geom=MagickFalse;
11246 Check all the scenes.
11248 initial_delay=image->delay;
11249 need_iterations=MagickFalse;
11250 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11251 mng_info->equal_physs=MagickTrue,
11252 mng_info->equal_gammas=MagickTrue;
11253 mng_info->equal_srgbs=MagickTrue;
11254 mng_info->equal_backgrounds=MagickTrue;
11256 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11257 defined(PNG_MNG_FEATURES_SUPPORTED)
11258 all_images_are_gray=MagickTrue;
11259 mng_info->equal_palettes=MagickFalse;
11260 need_local_plte=MagickFalse;
11262 for (next_image=image; next_image != (Image *) NULL; )
11266 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11267 mng_info->page.width=next_image->columns+next_image->page.x;
11269 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11270 mng_info->page.height=next_image->rows+next_image->page.y;
11273 if (next_image->page.x || next_image->page.y)
11274 need_defi=MagickTrue;
11276 if (next_image->matte)
11277 need_matte=MagickTrue;
11279 if ((int) next_image->dispose >= BackgroundDispose)
11280 if (next_image->matte || next_image->page.x || next_image->page.y ||
11281 ((next_image->columns < mng_info->page.width) &&
11282 (next_image->rows < mng_info->page.height)))
11283 mng_info->need_fram=MagickTrue;
11285 if (next_image->iterations)
11286 need_iterations=MagickTrue;
11288 final_delay=next_image->delay;
11290 if (final_delay != initial_delay || final_delay > 1UL*
11291 next_image->ticks_per_second)
11292 mng_info->need_fram=1;
11294 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11295 defined(PNG_MNG_FEATURES_SUPPORTED)
11297 check for global palette possibility.
11299 if (image->matte != MagickFalse)
11300 need_local_plte=MagickTrue;
11302 if (need_local_plte == 0)
11304 if (ImageIsGray(image) == MagickFalse)
11305 all_images_are_gray=MagickFalse;
11306 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11307 if (use_global_plte == 0)
11308 use_global_plte=mng_info->equal_palettes;
11309 need_local_plte=!mng_info->equal_palettes;
11312 if (GetNextImageInList(next_image) != (Image *) NULL)
11314 if (next_image->background_color.red !=
11315 next_image->next->background_color.red ||
11316 next_image->background_color.green !=
11317 next_image->next->background_color.green ||
11318 next_image->background_color.blue !=
11319 next_image->next->background_color.blue)
11320 mng_info->equal_backgrounds=MagickFalse;
11322 if (next_image->gamma != next_image->next->gamma)
11323 mng_info->equal_gammas=MagickFalse;
11325 if (next_image->rendering_intent !=
11326 next_image->next->rendering_intent)
11327 mng_info->equal_srgbs=MagickFalse;
11329 if ((next_image->units != next_image->next->units) ||
11330 (next_image->x_resolution != next_image->next->x_resolution) ||
11331 (next_image->y_resolution != next_image->next->y_resolution))
11332 mng_info->equal_physs=MagickFalse;
11334 if (mng_info->equal_chrms)
11336 if (next_image->chromaticity.red_primary.x !=
11337 next_image->next->chromaticity.red_primary.x ||
11338 next_image->chromaticity.red_primary.y !=
11339 next_image->next->chromaticity.red_primary.y ||
11340 next_image->chromaticity.green_primary.x !=
11341 next_image->next->chromaticity.green_primary.x ||
11342 next_image->chromaticity.green_primary.y !=
11343 next_image->next->chromaticity.green_primary.y ||
11344 next_image->chromaticity.blue_primary.x !=
11345 next_image->next->chromaticity.blue_primary.x ||
11346 next_image->chromaticity.blue_primary.y !=
11347 next_image->next->chromaticity.blue_primary.y ||
11348 next_image->chromaticity.white_point.x !=
11349 next_image->next->chromaticity.white_point.x ||
11350 next_image->chromaticity.white_point.y !=
11351 next_image->next->chromaticity.white_point.y)
11352 mng_info->equal_chrms=MagickFalse;
11356 next_image=GetNextImageInList(next_image);
11358 if (image_count < 2)
11360 mng_info->equal_backgrounds=MagickFalse;
11361 mng_info->equal_chrms=MagickFalse;
11362 mng_info->equal_gammas=MagickFalse;
11363 mng_info->equal_srgbs=MagickFalse;
11364 mng_info->equal_physs=MagickFalse;
11365 use_global_plte=MagickFalse;
11366 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11367 need_local_plte=MagickTrue;
11369 need_iterations=MagickFalse;
11372 if (mng_info->need_fram == MagickFalse)
11375 Only certain framing rates 100/n are exactly representable without
11376 the FRAM chunk but we'll allow some slop in VLC files
11378 if (final_delay == 0)
11380 if (need_iterations != MagickFalse)
11383 It's probably a GIF with loop; don't run it *too* fast.
11385 if (mng_info->adjoin)
11388 (void) ThrowMagickException(&image->exception,
11389 GetMagickModule(),CoderWarning,
11390 "input has zero delay between all frames; assuming",
11395 mng_info->ticks_per_second=0;
11397 if (final_delay != 0)
11398 mng_info->ticks_per_second=(png_uint_32)
11399 (image->ticks_per_second/final_delay);
11400 if (final_delay > 50)
11401 mng_info->ticks_per_second=2;
11403 if (final_delay > 75)
11404 mng_info->ticks_per_second=1;
11406 if (final_delay > 125)
11407 mng_info->need_fram=MagickTrue;
11409 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11410 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11411 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11412 1UL*image->ticks_per_second))
11413 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11416 if (mng_info->need_fram != MagickFalse)
11417 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11419 If pseudocolor, we should also check to see if all the
11420 palettes are identical and write a global PLTE if they are.
11424 Write the MNG version 1.0 signature and MHDR chunk.
11426 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11427 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11428 PNGType(chunk,mng_MHDR);
11429 LogPNGChunk(logging,mng_MHDR,28L);
11430 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11431 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11432 PNGLong(chunk+12,mng_info->ticks_per_second);
11433 PNGLong(chunk+16,0L); /* layer count=unknown */
11434 PNGLong(chunk+20,0L); /* frame count=unknown */
11435 PNGLong(chunk+24,0L); /* play time=unknown */
11440 if (need_defi || mng_info->need_fram || use_global_plte)
11441 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11444 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11449 if (need_defi || mng_info->need_fram || use_global_plte)
11450 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11453 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11461 if (need_defi || mng_info->need_fram || use_global_plte)
11462 PNGLong(chunk+28,11L); /* simplicity=LC */
11465 PNGLong(chunk+28,9L); /* simplicity=VLC */
11470 if (need_defi || mng_info->need_fram || use_global_plte)
11471 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11474 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11477 (void) WriteBlob(image,32,chunk);
11478 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11479 option=GetImageOption(image_info,"mng:need-cacheoff");
11480 if (option != (const char *) NULL)
11486 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11488 PNGType(chunk,mng_nEED);
11489 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11490 (void) WriteBlobMSBULong(image,(size_t) length);
11491 LogPNGChunk(logging,mng_nEED,(size_t) length);
11493 (void) WriteBlob(image,length,chunk);
11494 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11496 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11497 (GetNextImageInList(image) != (Image *) NULL) &&
11498 (image->iterations != 1))
11501 Write MNG TERM chunk
11503 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11504 PNGType(chunk,mng_TERM);
11505 LogPNGChunk(logging,mng_TERM,10L);
11506 chunk[4]=3; /* repeat animation */
11507 chunk[5]=0; /* show last frame when done */
11508 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11509 final_delay/MagickMax(image->ticks_per_second,1)));
11511 if (image->iterations == 0)
11512 PNGLong(chunk+10,PNG_UINT_31_MAX);
11515 PNGLong(chunk+10,(png_uint_32) image->iterations);
11517 if (logging != MagickFalse)
11519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11520 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11521 final_delay/MagickMax(image->ticks_per_second,1)));
11523 if (image->iterations == 0)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11529 " Image iterations: %.20g",(double) image->iterations);
11531 (void) WriteBlob(image,14,chunk);
11532 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11535 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11537 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11538 mng_info->equal_srgbs)
11541 Write MNG sRGB chunk
11543 (void) WriteBlobMSBULong(image,1L);
11544 PNGType(chunk,mng_sRGB);
11545 LogPNGChunk(logging,mng_sRGB,1L);
11547 if (image->rendering_intent != UndefinedIntent)
11548 chunk[4]=(unsigned char)
11549 Magick_RenderingIntent_to_PNG_RenderingIntent(
11550 (image->rendering_intent));
11553 chunk[4]=(unsigned char)
11554 Magick_RenderingIntent_to_PNG_RenderingIntent(
11555 (PerceptualIntent));
11557 (void) WriteBlob(image,5,chunk);
11558 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11559 mng_info->have_write_global_srgb=MagickTrue;
11564 if (image->gamma && mng_info->equal_gammas)
11567 Write MNG gAMA chunk
11569 (void) WriteBlobMSBULong(image,4L);
11570 PNGType(chunk,mng_gAMA);
11571 LogPNGChunk(logging,mng_gAMA,4L);
11572 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11573 (void) WriteBlob(image,8,chunk);
11574 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11575 mng_info->have_write_global_gama=MagickTrue;
11577 if (mng_info->equal_chrms)
11583 Write MNG cHRM chunk
11585 (void) WriteBlobMSBULong(image,32L);
11586 PNGType(chunk,mng_cHRM);
11587 LogPNGChunk(logging,mng_cHRM,32L);
11588 primary=image->chromaticity.white_point;
11589 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11590 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11591 primary=image->chromaticity.red_primary;
11592 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11593 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11594 primary=image->chromaticity.green_primary;
11595 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11596 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11597 primary=image->chromaticity.blue_primary;
11598 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11599 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11600 (void) WriteBlob(image,36,chunk);
11601 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11602 mng_info->have_write_global_chrm=MagickTrue;
11605 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11608 Write MNG pHYs chunk
11610 (void) WriteBlobMSBULong(image,9L);
11611 PNGType(chunk,mng_pHYs);
11612 LogPNGChunk(logging,mng_pHYs,9L);
11614 if (image->units == PixelsPerInchResolution)
11616 PNGLong(chunk+4,(png_uint_32)
11617 (image->x_resolution*100.0/2.54+0.5));
11619 PNGLong(chunk+8,(png_uint_32)
11620 (image->y_resolution*100.0/2.54+0.5));
11627 if (image->units == PixelsPerCentimeterResolution)
11629 PNGLong(chunk+4,(png_uint_32)
11630 (image->x_resolution*100.0+0.5));
11632 PNGLong(chunk+8,(png_uint_32)
11633 (image->y_resolution*100.0+0.5));
11640 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11641 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11645 (void) WriteBlob(image,13,chunk);
11646 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11649 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11650 or does not cover the entire frame.
11652 if (write_mng && (image->matte || image->page.x > 0 ||
11653 image->page.y > 0 || (image->page.width &&
11654 (image->page.width+image->page.x < mng_info->page.width))
11655 || (image->page.height && (image->page.height+image->page.y
11656 < mng_info->page.height))))
11658 (void) WriteBlobMSBULong(image,6L);
11659 PNGType(chunk,mng_BACK);
11660 LogPNGChunk(logging,mng_BACK,6L);
11661 red=ScaleQuantumToShort(image->background_color.red);
11662 green=ScaleQuantumToShort(image->background_color.green);
11663 blue=ScaleQuantumToShort(image->background_color.blue);
11664 PNGShort(chunk+4,red);
11665 PNGShort(chunk+6,green);
11666 PNGShort(chunk+8,blue);
11667 (void) WriteBlob(image,10,chunk);
11668 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11669 if (mng_info->equal_backgrounds)
11671 (void) WriteBlobMSBULong(image,6L);
11672 PNGType(chunk,mng_bKGD);
11673 LogPNGChunk(logging,mng_bKGD,6L);
11674 (void) WriteBlob(image,10,chunk);
11675 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11679 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11680 if ((need_local_plte == MagickFalse) &&
11681 (image->storage_class == PseudoClass) &&
11682 (all_images_are_gray == MagickFalse))
11688 Write MNG PLTE chunk
11690 data_length=3*image->colors;
11691 (void) WriteBlobMSBULong(image,data_length);
11692 PNGType(chunk,mng_PLTE);
11693 LogPNGChunk(logging,mng_PLTE,data_length);
11695 for (i=0; i < (ssize_t) image->colors; i++)
11697 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11698 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11699 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11702 (void) WriteBlob(image,data_length+4,chunk);
11703 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11704 mng_info->have_write_global_plte=MagickTrue;
11710 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11711 defined(PNG_MNG_FEATURES_SUPPORTED)
11712 mng_info->equal_palettes=MagickFalse;
11716 if (mng_info->adjoin)
11718 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11719 defined(PNG_MNG_FEATURES_SUPPORTED)
11721 If we aren't using a global palette for the entire MNG, check to
11722 see if we can use one for two or more consecutive images.
11724 if (need_local_plte && use_global_plte && !all_images_are_gray)
11726 if (mng_info->IsPalette)
11729 When equal_palettes is true, this image has the same palette
11730 as the previous PseudoClass image
11732 mng_info->have_write_global_plte=mng_info->equal_palettes;
11733 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11734 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11737 Write MNG PLTE chunk
11742 data_length=3*image->colors;
11743 (void) WriteBlobMSBULong(image,data_length);
11744 PNGType(chunk,mng_PLTE);
11745 LogPNGChunk(logging,mng_PLTE,data_length);
11747 for (i=0; i < (ssize_t) image->colors; i++)
11749 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11750 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11751 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11754 (void) WriteBlob(image,data_length+4,chunk);
11755 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11756 (uInt) (data_length+4)));
11757 mng_info->have_write_global_plte=MagickTrue;
11761 mng_info->have_write_global_plte=MagickFalse;
11772 previous_x=mng_info->page.x;
11773 previous_y=mng_info->page.y;
11780 mng_info->page=image->page;
11781 if ((mng_info->page.x != previous_x) ||
11782 (mng_info->page.y != previous_y))
11784 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11785 PNGType(chunk,mng_DEFI);
11786 LogPNGChunk(logging,mng_DEFI,12L);
11787 chunk[4]=0; /* object 0 MSB */
11788 chunk[5]=0; /* object 0 LSB */
11789 chunk[6]=0; /* visible */
11790 chunk[7]=0; /* abstract */
11791 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11792 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11793 (void) WriteBlob(image,16,chunk);
11794 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11799 mng_info->write_mng=write_mng;
11801 if ((int) image->dispose >= 3)
11802 mng_info->framing_mode=3;
11804 if (mng_info->need_fram && mng_info->adjoin &&
11805 ((image->delay != mng_info->delay) ||
11806 (mng_info->framing_mode != mng_info->old_framing_mode)))
11808 if (image->delay == mng_info->delay)
11811 Write a MNG FRAM chunk with the new framing mode.
11813 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11814 PNGType(chunk,mng_FRAM);
11815 LogPNGChunk(logging,mng_FRAM,1L);
11816 chunk[4]=(unsigned char) mng_info->framing_mode;
11817 (void) WriteBlob(image,5,chunk);
11818 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11823 Write a MNG FRAM chunk with the delay.
11825 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11826 PNGType(chunk,mng_FRAM);
11827 LogPNGChunk(logging,mng_FRAM,10L);
11828 chunk[4]=(unsigned char) mng_info->framing_mode;
11829 chunk[5]=0; /* frame name separator (no name) */
11830 chunk[6]=2; /* flag for changing default delay */
11831 chunk[7]=0; /* flag for changing frame timeout */
11832 chunk[8]=0; /* flag for changing frame clipping */
11833 chunk[9]=0; /* flag for changing frame sync_id */
11834 PNGLong(chunk+10,(png_uint_32)
11835 ((mng_info->ticks_per_second*
11836 image->delay)/MagickMax(image->ticks_per_second,1)));
11837 (void) WriteBlob(image,14,chunk);
11838 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11839 mng_info->delay=(png_uint_32) image->delay;
11841 mng_info->old_framing_mode=mng_info->framing_mode;
11844 #if defined(JNG_SUPPORTED)
11845 if (image_info->compression == JPEGCompression)
11850 if (logging != MagickFalse)
11851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11852 " Writing JNG object.");
11853 /* To do: specify the desired alpha compression method. */
11854 write_info=CloneImageInfo(image_info);
11855 write_info->compression=UndefinedCompression;
11856 status=WriteOneJNGImage(mng_info,write_info,image);
11857 write_info=DestroyImageInfo(write_info);
11862 if (logging != MagickFalse)
11863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11864 " Writing PNG object.");
11866 mng_info->need_blob = MagickFalse;
11868 /* We don't want any ancillary chunks written */
11869 mng_info->ping_exclude_bKGD=MagickTrue;
11870 mng_info->ping_exclude_cHRM=MagickTrue;
11871 mng_info->ping_exclude_EXIF=MagickTrue;
11872 mng_info->ping_exclude_gAMA=MagickTrue;
11873 mng_info->ping_exclude_cHRM=MagickTrue;
11874 mng_info->ping_exclude_iCCP=MagickTrue;
11875 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11876 mng_info->ping_exclude_oFFs=MagickTrue;
11877 mng_info->ping_exclude_pHYs=MagickTrue;
11878 mng_info->ping_exclude_sRGB=MagickTrue;
11879 mng_info->ping_exclude_tEXt=MagickTrue;
11880 mng_info->ping_exclude_tRNS=MagickTrue;
11881 mng_info->ping_exclude_vpAg=MagickTrue;
11882 mng_info->ping_exclude_zCCP=MagickTrue;
11883 mng_info->ping_exclude_zTXt=MagickTrue;
11885 status=WriteOnePNGImage(mng_info,image_info,image);
11888 if (status == MagickFalse)
11890 MngInfoFreeStruct(mng_info,&have_mng_structure);
11891 (void) CloseBlob(image);
11892 return(MagickFalse);
11894 (void) CatchImageException(image);
11895 if (GetNextImageInList(image) == (Image *) NULL)
11897 image=SyncNextImageInList(image);
11898 status=SetImageProgress(image,SaveImagesTag,scene++,
11899 GetImageListLength(image));
11901 if (status == MagickFalse)
11904 } while (mng_info->adjoin);
11908 while (GetPreviousImageInList(image) != (Image *) NULL)
11909 image=GetPreviousImageInList(image);
11911 Write the MEND chunk.
11913 (void) WriteBlobMSBULong(image,0x00000000L);
11914 PNGType(chunk,mng_MEND);
11915 LogPNGChunk(logging,mng_MEND,0L);
11916 (void) WriteBlob(image,4,chunk);
11917 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11920 Relinquish resources.
11922 (void) CloseBlob(image);
11923 MngInfoFreeStruct(mng_info,&have_mng_structure);
11925 if (logging != MagickFalse)
11926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11928 return(MagickTrue);
11930 #else /* PNG_LIBPNG_VER > 10011 */
11932 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11935 printf("Your PNG library is too old: You have libpng-%s\n",
11936 PNG_LIBPNG_VER_STRING);
11938 ThrowBinaryException(CoderError,"PNG library is too old",
11939 image_info->filename);
11942 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11944 return(WritePNGImage(image_info,image));
11946 #endif /* PNG_LIBPNG_VER > 10011 */