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 if there
6984 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
6985 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
6986 * palette. The final reduction can only fail if there are still 256
6987 * colors present and one of them has both transparent and opaque instances.
6990 tried_333 = MagickFalse;
6991 tried_444 = MagickFalse;
6997 * Sometimes we get DirectClass images that have 256 colors or fewer.
6998 * This code will build a colormap.
7000 * Also, sometimes we get PseudoClass images with an out-of-date
7001 * colormap. This code will replace the colormap with a new one.
7002 * Sometimes we get PseudoClass images that have more than 256 colors.
7003 * This code will delete the colormap and change the image to
7006 * If image->matte is MagickFalse, we ignore the opacity channel
7007 * even though it sometimes contains left-over non-opaque values.
7009 * Also we gather some information (number of opaque, transparent,
7010 * and semitransparent pixels, and whether the image has any non-gray
7011 * pixels or only black-and-white pixels) that we might need later.
7013 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7014 * we need to check for bogus non-opaque values, at least.
7017 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7018 # define PNGK 0 /* Shift */
7019 # define PNGM 1 /* Scale */
7020 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7022 # define PNGM 0x0101
7025 # define PNGM 0x01010101
7036 semitransparent[260],
7039 register IndexPacket
7042 register const PixelPacket
7046 register PixelPacket
7049 if (logging != MagickFalse)
7050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7051 " Enter BUILD_PALETTE:");
7053 if (logging != MagickFalse)
7055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056 " image->columns=%.20g",(double) image->columns);
7057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7058 " image->rows=%.20g",(double) image->rows);
7059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060 " image->matte=%.20g",(double) image->matte);
7061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062 " image->depth=%.20g",(double) image->depth);
7064 if (image->storage_class == PseudoClass && image->colormap != NULL)
7066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7067 " Original colormap:");
7068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7069 " i (red,green,blue,opacity)");
7071 for (i=0; i < 256; i++)
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " %d (%d,%d,%d,%d)",
7076 (int) image->colormap[i].red,
7077 (int) image->colormap[i].green,
7078 (int) image->colormap[i].blue,
7079 (int) image->colormap[i].opacity);
7082 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087 " %d (%d,%d,%d,%d)",
7089 (int) image->colormap[i].red,
7090 (int) image->colormap[i].green,
7091 (int) image->colormap[i].blue,
7092 (int) image->colormap[i].opacity);
7097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7098 " image->colors=%d",(int) image->colors);
7100 if (image->colors == 0)
7101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7102 " (zero means unknown)");
7104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7105 " Regenerate the colormap");
7108 exception=(&image->exception);
7112 number_semitransparent = 0;
7113 number_transparent = 0;
7115 for (y=0; y < (ssize_t) image->rows; y++)
7117 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7119 if (q == (PixelPacket *) NULL)
7122 for (x=0; x < (ssize_t) image->columns; x++)
7124 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7126 if (number_opaque < 259)
7128 if (number_opaque == 0)
7131 opaque[0].opacity=OpaqueOpacity;
7135 for (i=0; i< (ssize_t) number_opaque; i++)
7137 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7141 if (i == (ssize_t) number_opaque &&
7142 number_opaque < 259)
7146 opaque[i].opacity = OpaqueOpacity;
7150 else if (q->opacity == TransparentOpacity)
7152 if (number_transparent < 259)
7154 if (number_transparent == 0)
7157 ping_trans_color.red=(unsigned short)(q->red);
7158 ping_trans_color.green=(unsigned short) (q->green);
7159 ping_trans_color.blue=(unsigned short) (q->blue);
7160 ping_trans_color.gray=(unsigned short) (q->blue);
7161 number_transparent = 1;
7164 for (i=0; i< (ssize_t) number_transparent; i++)
7166 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7170 if (i == (ssize_t) number_transparent &&
7171 number_transparent < 259)
7173 number_transparent++;
7174 transparent[i] = *q;
7180 if (number_semitransparent < 259)
7182 if (number_semitransparent == 0)
7184 semitransparent[0]=*q;
7185 number_semitransparent = 1;
7188 for (i=0; i< (ssize_t) number_semitransparent; i++)
7190 if (IsColorEqual(semitransparent+i,
7191 (PixelPacket *) q) &&
7192 q->opacity == semitransparent[i].opacity)
7196 if (i == (ssize_t) number_semitransparent &&
7197 number_semitransparent < 259)
7199 number_semitransparent++;
7200 semitransparent[i] = *q;
7208 if (ping_exclude_bKGD == MagickFalse)
7210 /* Add the background color to the palette, if it
7211 * isn't already there.
7213 for (i=0; i<number_opaque; i++)
7215 if (IsColorEqual(opaque+i, &image->background_color))
7219 if (number_opaque < 259 && i == number_opaque)
7221 opaque[i]=image->background_color;
7222 opaque[i].opacity = OpaqueOpacity;
7225 else if (logging != MagickFalse)
7226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7227 " No room in the colormap to add background color");
7230 image_colors=number_opaque+number_transparent+number_semitransparent;
7232 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7234 /* No room for the background color; remove it. */
7239 if (logging != MagickFalse)
7241 if (image_colors > 256)
7242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7243 " image has more than 256 colors");
7246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7247 " image has %d colors",image_colors);
7250 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7252 ping_have_color=MagickFalse;
7253 ping_have_non_bw=MagickFalse;
7255 if(image_colors > 256)
7257 for (y=0; y < (ssize_t) image->rows; y++)
7259 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7261 if (q == (PixelPacket *) NULL)
7264 /* Worst case is black-and-white; we are looking at every
7268 if (ping_have_color == MagickFalse)
7271 for (x=0; x < (ssize_t) image->columns; x++)
7273 if (s->red != s->green || s->red != s->blue)
7275 ping_have_color=MagickTrue;
7276 ping_have_non_bw=MagickTrue;
7283 if (ping_have_non_bw == MagickFalse)
7286 for (x=0; x < (ssize_t) image->columns; x++)
7288 if (s->red != 0 && s->red != QuantumRange)
7290 ping_have_non_bw=MagickTrue;
7299 if (image_colors < 257)
7305 * Initialize image colormap.
7308 if (logging != MagickFalse)
7309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7310 " Sort the new colormap");
7312 /* Sort palette, transparent first */;
7316 for (i=0; i<number_transparent; i++)
7317 colormap[n++] = transparent[i];
7319 for (i=0; i<number_semitransparent; i++)
7320 colormap[n++] = semitransparent[i];
7322 for (i=0; i<number_opaque; i++)
7323 colormap[n++] = opaque[i];
7326 /* image_colors < 257; search the colormap instead of the pixels
7327 * to get ping_have_color and ping_have_non_bw
7331 if (ping_have_color == MagickFalse)
7333 if (colormap[i].red != colormap[i].green ||
7334 colormap[i].red != colormap[i].blue)
7336 ping_have_color=MagickTrue;
7337 ping_have_non_bw=MagickTrue;
7342 if (ping_have_non_bw == MagickFalse)
7344 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7345 ping_have_non_bw=MagickTrue;
7349 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7350 (number_transparent == 0 && number_semitransparent == 0)) &&
7351 (((mng_info->write_png_colortype-1) ==
7352 PNG_COLOR_TYPE_PALETTE) ||
7353 (mng_info->write_png_colortype == 0)))
7355 if (logging != MagickFalse)
7357 if (n != (ssize_t) image_colors)
7358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7359 " image_colors (%d) and n (%d) don't match",
7362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363 " AcquireImageColormap");
7366 image->colors = image_colors;
7368 if (AcquireImageColormap(image,image_colors) ==
7370 ThrowWriterException(ResourceLimitError,
7371 "MemoryAllocationFailed");
7373 for (i=0; i< (ssize_t) image_colors; i++)
7374 image->colormap[i] = colormap[i];
7376 if (logging != MagickFalse)
7378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7379 " image->colors=%d (%d)",
7380 (int) image->colors, image_colors);
7382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7383 " Update the pixel indexes");
7386 /* Sync the pixel indices with the new colormap */
7388 for (y=0; y < (ssize_t) image->rows; y++)
7390 q=GetAuthenticPixels(image,0,y,image->columns,1,
7393 if (q == (PixelPacket *) NULL)
7396 indexes=GetAuthenticIndexQueue(image);
7398 for (x=0; x < (ssize_t) image->columns; x++)
7400 for (i=0; i< (ssize_t) image_colors; i++)
7402 if ((image->matte == MagickFalse ||
7403 image->colormap[i].opacity == q->opacity) &&
7404 (IsColorEqual(&image->colormap[i],
7405 (PixelPacket *) q)))
7407 indexes[x]=(IndexPacket) i;
7414 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7420 if (logging != MagickFalse)
7422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7423 " image->colors=%d", (int) image->colors);
7425 if (image->colormap != NULL)
7427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428 " i (red,green,blue,opacity)");
7430 for (i=0; i < (ssize_t) image->colors; i++)
7432 if (i < 300 || i >= image->colors - 10)
7434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435 " %d (%d,%d,%d,%d)",
7437 (int) image->colormap[i].red,
7438 (int) image->colormap[i].green,
7439 (int) image->colormap[i].blue,
7440 (int) image->colormap[i].opacity);
7445 if (number_transparent < 257)
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447 " number_transparent = %d",
7448 number_transparent);
7451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7452 " number_transparent > 256");
7454 if (number_opaque < 257)
7455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7456 " number_opaque = %d",
7460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7461 " number_opaque > 256");
7463 if (number_semitransparent < 257)
7464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465 " number_semitransparent = %d",
7466 number_semitransparent);
7469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7470 " number_semitransparent > 256");
7472 if (ping_have_non_bw == MagickFalse)
7473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7474 " All pixels and the background are black or white");
7476 else if (ping_have_color == MagickFalse)
7477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7478 " All pixels and the background are gray");
7481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7482 " At least one pixel or the background is non-gray");
7484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7485 " Exit BUILD_PALETTE:");
7488 if (mng_info->write_png8 == MagickFalse)
7491 /* Make any reductions necessary for the PNG8 format */
7492 if (image_colors <= 256 &&
7493 image_colors != 0 && image->colormap != NULL &&
7494 number_semitransparent == 0 &&
7495 number_transparent <= 1)
7498 /* PNG8 can't have semitransparent colors so we threshold the
7499 * opacity to 0 or OpaqueOpacity
7501 if (number_semitransparent != 0)
7503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504 " Thresholding the alpha channel to binary");
7506 for (y=0; y < (ssize_t) image->rows; y++)
7508 r=GetAuthenticPixels(image,0,y,image->columns,1,
7511 if (r == (PixelPacket *) NULL)
7514 for (x=0; x < (ssize_t) image->columns; x++)
7516 r->opacity = r->opacity > TransparentOpacity/2 ?
7517 TransparentOpacity : OpaqueOpacity;
7521 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7524 if (image_colors != 0 && image_colors <= 256 &&
7525 image->colormap != NULL)
7526 for (i=0; i<image_colors; i++)
7527 image->colormap[i].opacity =
7528 image->colormap[i].opacity > TransparentOpacity/2 ?
7529 TransparentOpacity : OpaqueOpacity;
7534 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7535 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7536 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7539 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7541 if (logging != MagickFalse)
7542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7543 " Quantizing the background color to 4-4-4");
7545 tried_444 = MagickTrue;
7547 image->background_color.red=
7549 image->background_color.red) >> PNGK) & 0xf0) ) |
7551 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7552 image->background_color.green=
7554 image->background_color.green) >> PNGK) & 0xf0) ) |
7556 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7557 image->background_color.blue=
7559 image->background_color.blue) >> PNGK) & 0xf0) ) |
7561 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7563 if (logging != MagickFalse)
7564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7565 " Quantizing the pixel colors to 4-4-4");
7567 if (image->colormap == NULL)
7569 for (y=0; y < (ssize_t) image->rows; y++)
7571 r=GetAuthenticPixels(image,0,y,image->columns,1,
7574 if (r == (PixelPacket *) NULL)
7577 for (x=0; x < (ssize_t) image->columns; x++)
7579 if (r->opacity == TransparentOpacity)
7581 r->red = image->background_color.red;
7582 r->green = image->background_color.green;
7583 r->blue = image->background_color.blue;
7588 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7589 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7591 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7592 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7594 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7595 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7600 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7605 else /* Should not reach this; colormap already exists and
7608 if (logging != MagickFalse)
7609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610 " Quantizing the colormap to 4-4-4");
7611 for (i=0; i<image_colors; i++)
7613 image->colormap[i].red=
7615 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7617 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7618 image->colormap[i].green=
7620 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7622 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7623 image->colormap[i].blue=
7625 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7627 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7633 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7635 if (logging != MagickFalse)
7636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7637 " Quantizing the background color to 3-3-3");
7639 tried_333 = MagickTrue;
7641 image->background_color.red=
7643 image->background_color.red) >> PNGK) & 0xe0) ) |
7645 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7647 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7648 image->background_color.green=
7650 image->background_color.green) >> PNGK) & 0xe0) ) |
7652 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7654 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7655 image->background_color.blue=
7657 image->background_color.blue) >> PNGK) & 0xe0) ) |
7659 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7661 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7663 if (logging != MagickFalse)
7664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7665 " Quantizing the pixel colors to 3-3-3-1");
7667 if (image->colormap == NULL)
7669 for (y=0; y < (ssize_t) image->rows; y++)
7671 r=GetAuthenticPixels(image,0,y,image->columns,1,
7674 if (r == (PixelPacket *) NULL)
7677 for (x=0; x < (ssize_t) image->columns; x++)
7679 if (r->opacity == TransparentOpacity)
7681 r->red = image->background_color.red;
7682 r->green = image->background_color.green;
7683 r->blue = image->background_color.blue;
7688 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7689 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7690 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7692 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7693 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7694 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7696 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7697 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7698 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7703 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7708 else /* Should not reach this; colormap already exists and
7711 if (logging != MagickFalse)
7712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7713 " Quantizing the colormap to 3-3-3-1");
7714 for (i=0; i<image_colors; i++)
7716 image->colormap[i].red=
7718 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7720 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7722 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7723 image->colormap[i].green=
7725 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7727 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7729 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7730 image->colormap[i].blue=
7732 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7734 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7736 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7742 if (image_colors == 0 || image_colors > 256)
7744 if (logging != MagickFalse)
7745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7746 " Quantizing the background color to 3-3-2");
7748 image->background_color.red=
7750 image->background_color.red) >> PNGK) & 0xe0) ) |
7752 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7754 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7755 image->background_color.green=
7757 image->background_color.green) >> PNGK) & 0xe0) ) |
7759 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7761 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7762 image->background_color.blue=
7764 image->background_color.blue) >> PNGK) & 0xc0) ) |
7766 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7768 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7770 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7772 if (logging != MagickFalse)
7773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7774 " Quantizing the pixel colors to 3-3-2-1");
7776 if (image->colormap == NULL)
7778 for (y=0; y < (ssize_t) image->rows; y++)
7780 r=GetAuthenticPixels(image,0,y,image->columns,1,
7783 if (r == (PixelPacket *) NULL)
7786 for (x=0; x < (ssize_t) image->columns; x++)
7788 if (r->opacity == TransparentOpacity)
7790 r->red = image->background_color.red;
7791 r->green = image->background_color.green;
7792 r->blue = image->background_color.blue;
7797 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7798 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7799 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7801 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7802 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7803 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7805 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7806 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7807 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7808 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7813 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7818 else /* Should not reach this; colormap already exists and
7821 if (logging != MagickFalse)
7822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7823 " Quantizing the colormap to 3-3-2-1");
7824 for (i=0; i<image_colors; i++)
7826 image->colormap[i].red=
7828 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7830 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7832 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7833 image->colormap[i].green=
7835 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7837 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7839 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7840 image->colormap[i].blue=
7842 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
7844 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
7846 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
7848 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7855 /* END OF BUILD_PALETTE */
7857 /* If we are excluding the tRNS chunk and there is transparency,
7858 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7861 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7862 (number_transparent != 0 || number_semitransparent != 0))
7864 int colortype=mng_info->write_png_colortype;
7866 if (ping_have_color == MagickFalse)
7867 mng_info->write_png_colortype = 5;
7870 mng_info->write_png_colortype = 7;
7872 if (colortype != 0 &&
7873 mng_info->write_png_colortype != (ssize_t) colortype)
7874 ping_need_colortype_warning=MagickTrue;
7878 /* See if cheap transparency is possible. It is only possible
7879 * when there is a single transparent color, no semitransparent
7880 * color, and no opaque color that has the same RGB components
7881 * as the transparent color. We only need this information if
7882 * we are writing a PNG with colortype 0 or 2, and we have not
7883 * excluded the tRNS chunk.
7885 if (number_transparent == 1 &&
7886 mng_info->write_png_colortype < 4)
7888 ping_have_cheap_transparency = MagickTrue;
7890 if (number_semitransparent != 0)
7891 ping_have_cheap_transparency = MagickFalse;
7893 else if (image_colors == 0 || image_colors > 256 ||
7894 image->colormap == NULL)
7899 register const PixelPacket
7902 exception=(&image->exception);
7904 for (y=0; y < (ssize_t) image->rows; y++)
7906 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7908 if (q == (PixelPacket *) NULL)
7911 for (x=0; x < (ssize_t) image->columns; x++)
7913 if (q->opacity != TransparentOpacity &&
7914 (unsigned short) q->red == ping_trans_color.red &&
7915 (unsigned short) q->green == ping_trans_color.green &&
7916 (unsigned short) q->blue == ping_trans_color.blue)
7918 ping_have_cheap_transparency = MagickFalse;
7925 if (ping_have_cheap_transparency == MagickFalse)
7931 /* Assuming that image->colormap[0] is the one transparent color
7932 * and that all others are opaque.
7934 if (image_colors > 1)
7935 for (i=1; i<image_colors; i++)
7936 if (image->colormap[i].red == image->colormap[0].red &&
7937 image->colormap[i].green == image->colormap[0].green &&
7938 image->colormap[i].blue == image->colormap[0].blue)
7940 ping_have_cheap_transparency = MagickFalse;
7945 if (logging != MagickFalse)
7947 if (ping_have_cheap_transparency == MagickFalse)
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " Cheap transparency is not possible.");
7952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953 " Cheap transparency is possible.");
7957 ping_have_cheap_transparency = MagickFalse;
7959 image_depth=image->depth;
7961 quantum_info = (QuantumInfo *) NULL;
7963 image_colors=(int) image->colors;
7964 image_matte=image->matte;
7966 mng_info->IsPalette=image->storage_class == PseudoClass &&
7967 image_colors <= 256 && image->colormap != NULL;
7969 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7970 (image->colors == 0 || image->colormap == NULL))
7972 image_info=DestroyImageInfo(image_info);
7973 image=DestroyImage(image);
7974 (void) ThrowMagickException(&IMimage->exception,
7975 GetMagickModule(),CoderError,
7976 "Cannot write PNG8 or color-type 3; colormap is NULL",
7977 "`%s'",IMimage->filename);
7978 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7979 UnlockSemaphoreInfo(ping_semaphore);
7981 return(MagickFalse);
7985 Allocate the PNG structures
7987 #ifdef PNG_USER_MEM_SUPPORTED
7988 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7989 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7990 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7993 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7994 MagickPNGErrorHandler,MagickPNGWarningHandler);
7997 if (ping == (png_struct *) NULL)
7998 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8000 ping_info=png_create_info_struct(ping);
8002 if (ping_info == (png_info *) NULL)
8004 png_destroy_write_struct(&ping,(png_info **) NULL);
8005 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8008 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8009 ping_pixels=(unsigned char *) NULL;
8011 if (setjmp(png_jmpbuf(ping)))
8017 if (image_info->verbose)
8018 (void) printf("PNG write has failed.\n");
8020 png_destroy_write_struct(&ping,&ping_info);
8021 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8022 UnlockSemaphoreInfo(ping_semaphore);
8024 if (ping_have_blob != MagickFalse)
8025 (void) CloseBlob(image);
8026 image_info=DestroyImageInfo(image_info);
8027 image=DestroyImage(image);
8028 return(MagickFalse);
8031 Prepare PNG for writing.
8033 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8034 if (mng_info->write_mng)
8035 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8038 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8039 if (mng_info->write_mng)
8040 png_permit_empty_plte(ping,MagickTrue);
8047 ping_width=(png_uint_32) image->columns;
8048 ping_height=(png_uint_32) image->rows;
8050 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8053 if (mng_info->write_png_depth != 0)
8054 image_depth=mng_info->write_png_depth;
8056 /* Adjust requested depth to next higher valid depth if necessary */
8057 if (image_depth > 8)
8060 if ((image_depth > 4) && (image_depth < 8))
8063 if (image_depth == 3)
8066 if (logging != MagickFalse)
8068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8069 " width=%.20g",(double) ping_width);
8070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8071 " height=%.20g",(double) ping_height);
8072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8073 " image_matte=%.20g",(double) image->matte);
8074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075 " image->depth=%.20g",(double) image->depth);
8076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8080 save_image_depth=image_depth;
8081 ping_bit_depth=(png_byte) save_image_depth;
8084 #if defined(PNG_pHYs_SUPPORTED)
8085 if (ping_exclude_pHYs == MagickFalse)
8087 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8088 (!mng_info->write_mng || !mng_info->equal_physs))
8090 if (logging != MagickFalse)
8091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8092 " Setting up pHYs chunk");
8094 if (image->units == PixelsPerInchResolution)
8096 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8097 ping_pHYs_x_resolution=
8098 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8099 ping_pHYs_y_resolution=
8100 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8103 else if (image->units == PixelsPerCentimeterResolution)
8105 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8106 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8107 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8112 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8113 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8114 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8117 if (logging != MagickFalse)
8118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8119 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8120 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8121 (int) ping_pHYs_unit_type);
8122 ping_have_pHYs = MagickTrue;
8127 if (ping_exclude_bKGD == MagickFalse)
8129 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8135 if (ping_bit_depth == 8)
8138 if (ping_bit_depth == 4)
8141 if (ping_bit_depth == 2)
8144 if (ping_bit_depth == 1)
8147 ping_background.red=(png_uint_16)
8148 (ScaleQuantumToShort(image->background_color.red) & mask);
8150 ping_background.green=(png_uint_16)
8151 (ScaleQuantumToShort(image->background_color.green) & mask);
8153 ping_background.blue=(png_uint_16)
8154 (ScaleQuantumToShort(image->background_color.blue) & mask);
8157 if (logging != MagickFalse)
8159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160 " Setting up bKGD chunk (1)");
8162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8163 " ping_bit_depth=%d",ping_bit_depth);
8166 ping_have_bKGD = MagickTrue;
8170 Select the color type.
8175 if (mng_info->IsPalette && mng_info->write_png8)
8178 /* To do: make this a function cause it's used twice, except
8179 for reducing the sample depth from 8. */
8181 number_colors=image_colors;
8183 ping_have_tRNS=MagickFalse;
8188 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8190 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " Setting up PLTE chunk with %d colors (%d)",
8193 number_colors, image_colors);
8195 for (i=0; i < (ssize_t) number_colors; i++)
8197 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8198 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8199 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8200 if (logging != MagickFalse)
8201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 #if MAGICKCORE_QUANTUM_DEPTH == 8
8203 " %3ld (%3d,%3d,%3d)",
8205 " %5ld (%5d,%5d,%5d)",
8207 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8211 ping_have_PLTE=MagickTrue;
8212 image_depth=ping_bit_depth;
8215 if (matte != MagickFalse)
8218 Identify which colormap entry is transparent.
8220 assert(number_colors <= 256);
8221 assert(image->colormap != NULL);
8223 for (i=0; i < (ssize_t) number_transparent; i++)
8224 ping_trans_alpha[i]=0;
8227 ping_num_trans=(unsigned short) (number_transparent +
8228 number_semitransparent);
8230 if (ping_num_trans == 0)
8231 ping_have_tRNS=MagickFalse;
8234 ping_have_tRNS=MagickTrue;
8237 if (ping_exclude_bKGD == MagickFalse)
8240 * Identify which colormap entry is the background color.
8243 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8244 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8247 ping_background.index=(png_byte) i;
8249 } /* end of write_png8 */
8251 else if (mng_info->write_png24)
8253 image_matte=MagickFalse;
8254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8257 else if (mng_info->write_png32)
8259 image_matte=MagickTrue;
8260 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8263 else /* mng_info->write_pngNN not specified */
8265 image_depth=ping_bit_depth;
8267 if (mng_info->write_png_colortype != 0)
8269 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8271 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8272 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8273 image_matte=MagickTrue;
8276 image_matte=MagickFalse;
8278 if (logging != MagickFalse)
8279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280 " PNG colortype %d was specified:",(int) ping_color_type);
8283 else /* write_png_colortype not specified */
8285 if (logging != MagickFalse)
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " Selecting PNG colortype:");
8289 ping_color_type=(png_byte) ((matte != MagickFalse)?
8290 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8292 if (image_info->type == TrueColorType)
8294 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8295 image_matte=MagickFalse;
8298 if (image_info->type == TrueColorMatteType)
8300 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8301 image_matte=MagickTrue;
8304 if (image_info->type == PaletteType ||
8305 image_info->type == PaletteMatteType)
8306 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8308 if (mng_info->write_png_colortype == 0 &&
8309 (image_info->type == UndefinedType ||
8310 image_info->type == OptimizeType))
8312 if (ping_have_color == MagickFalse)
8314 if (image_matte == MagickFalse)
8316 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8317 image_matte=MagickFalse;
8322 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8323 image_matte=MagickTrue;
8328 if (image_matte == MagickFalse)
8330 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8331 image_matte=MagickFalse;
8336 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8337 image_matte=MagickTrue;
8344 if (logging != MagickFalse)
8345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346 " Selected PNG colortype=%d",ping_color_type);
8348 if (ping_bit_depth < 8)
8350 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8351 ping_color_type == PNG_COLOR_TYPE_RGB ||
8352 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8356 old_bit_depth=ping_bit_depth;
8358 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8360 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8364 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8369 if (image->colors == 0)
8372 (void) ThrowMagickException(&image->exception,
8373 GetMagickModule(),CoderError,
8374 "image has 0 colors", "`%s'","");
8377 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8378 ping_bit_depth <<= 1;
8381 if (logging != MagickFalse)
8383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384 " Number of colors: %.20g",(double) image_colors);
8386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8387 " Tentative PNG bit depth: %d",ping_bit_depth);
8390 if (ping_bit_depth < (int) mng_info->write_png_depth)
8391 ping_bit_depth = mng_info->write_png_depth;
8394 image_depth=ping_bit_depth;
8396 if (logging != MagickFalse)
8398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399 " Tentative PNG color type: %.20g",(double) ping_color_type);
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " image_info->type: %.20g",(double) image_info->type);
8404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8405 " image_depth: %.20g",(double) image_depth);
8407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " image->depth: %.20g",(double) image->depth);
8411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8412 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8415 if (matte != MagickFalse)
8417 if (mng_info->IsPalette)
8419 if (mng_info->write_png_colortype == 0)
8421 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8423 if (ping_have_color != MagickFalse)
8424 ping_color_type=PNG_COLOR_TYPE_RGBA;
8428 * Determine if there is any transparent color.
8430 if (number_transparent + number_semitransparent == 0)
8433 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8436 image_matte=MagickFalse;
8438 if (mng_info->write_png_colortype == 0)
8439 ping_color_type&=0x03;
8449 if (ping_bit_depth == 8)
8452 if (ping_bit_depth == 4)
8455 if (ping_bit_depth == 2)
8458 if (ping_bit_depth == 1)
8461 ping_trans_color.red=(png_uint_16)
8462 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8464 ping_trans_color.green=(png_uint_16)
8465 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8467 ping_trans_color.blue=(png_uint_16)
8468 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8470 ping_trans_color.gray=(png_uint_16)
8471 (ScaleQuantumToShort(PixelIntensityToQuantum(
8472 image->colormap)) & mask);
8474 ping_trans_color.index=(png_byte) 0;
8476 ping_have_tRNS=MagickTrue;
8479 if (ping_have_tRNS != MagickFalse)
8482 * Determine if there is one and only one transparent color
8483 * and if so if it is fully transparent.
8485 if (ping_have_cheap_transparency == MagickFalse)
8486 ping_have_tRNS=MagickFalse;
8489 if (ping_have_tRNS != MagickFalse)
8491 if (mng_info->write_png_colortype == 0)
8492 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8494 if (image_depth == 8)
8496 ping_trans_color.red&=0xff;
8497 ping_trans_color.green&=0xff;
8498 ping_trans_color.blue&=0xff;
8499 ping_trans_color.gray&=0xff;
8505 if (image_depth == 8)
8507 ping_trans_color.red&=0xff;
8508 ping_trans_color.green&=0xff;
8509 ping_trans_color.blue&=0xff;
8510 ping_trans_color.gray&=0xff;
8517 if (ping_have_tRNS != MagickFalse)
8518 image_matte=MagickFalse;
8520 if ((mng_info->IsPalette) &&
8521 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8522 ping_have_color == MagickFalse &&
8523 (image_matte == MagickFalse || image_depth >= 8))
8527 if (image_matte != MagickFalse)
8528 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8530 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8532 ping_color_type=PNG_COLOR_TYPE_GRAY;
8534 if (save_image_depth == 16 && image_depth == 8)
8536 if (logging != MagickFalse)
8538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539 " Scaling ping_trans_color (0)");
8541 ping_trans_color.gray*=0x0101;
8545 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8546 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8548 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8549 image_colors=(int) (one << image_depth);
8551 if (image_depth > 8)
8557 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8559 if(!mng_info->write_png_depth)
8563 while ((int) (one << ping_bit_depth)
8564 < (ssize_t) image_colors)
8565 ping_bit_depth <<= 1;
8569 else if (ping_color_type ==
8570 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8571 mng_info->IsPalette)
8573 /* Check if grayscale is reducible */
8576 depth_4_ok=MagickTrue,
8577 depth_2_ok=MagickTrue,
8578 depth_1_ok=MagickTrue;
8580 for (i=0; i < (ssize_t) image_colors; i++)
8585 intensity=ScaleQuantumToChar(image->colormap[i].red);
8587 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8588 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8589 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8590 depth_2_ok=depth_1_ok=MagickFalse;
8591 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8592 depth_1_ok=MagickFalse;
8595 if (depth_1_ok && mng_info->write_png_depth <= 1)
8598 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8601 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8606 image_depth=ping_bit_depth;
8611 if (mng_info->IsPalette)
8613 number_colors=image_colors;
8615 if (image_depth <= 8)
8620 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8622 if (mng_info->have_write_global_plte && matte == MagickFalse)
8624 png_set_PLTE(ping,ping_info,NULL,0);
8626 if (logging != MagickFalse)
8627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8628 " Setting up empty PLTE chunk");
8633 for (i=0; i < (ssize_t) number_colors; i++)
8635 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8636 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8637 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8640 if (logging != MagickFalse)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " Setting up PLTE chunk with %d colors",
8645 ping_have_PLTE=MagickTrue;
8648 /* color_type is PNG_COLOR_TYPE_PALETTE */
8649 if (mng_info->write_png_depth == 0)
8657 while ((one << ping_bit_depth) < number_colors)
8658 ping_bit_depth <<= 1;
8663 if (matte != MagickFalse)
8666 * Set up trans_colors array.
8668 assert(number_colors <= 256);
8670 ping_num_trans=(unsigned short) (number_transparent +
8671 number_semitransparent);
8673 if (ping_num_trans == 0)
8674 ping_have_tRNS=MagickFalse;
8678 if (logging != MagickFalse)
8680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681 " Scaling ping_trans_color (1)");
8683 ping_have_tRNS=MagickTrue;
8685 for (i=0; i < ping_num_trans; i++)
8687 ping_trans_alpha[i]= (png_byte) (255-
8688 ScaleQuantumToChar(image->colormap[i].opacity));
8698 if (image_depth < 8)
8701 if ((save_image_depth == 16) && (image_depth == 8))
8703 if (logging != MagickFalse)
8705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8706 " Scaling ping_trans_color from (%d,%d,%d)",
8707 (int) ping_trans_color.red,
8708 (int) ping_trans_color.green,
8709 (int) ping_trans_color.blue);
8712 ping_trans_color.red*=0x0101;
8713 ping_trans_color.green*=0x0101;
8714 ping_trans_color.blue*=0x0101;
8715 ping_trans_color.gray*=0x0101;
8717 if (logging != MagickFalse)
8719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8721 (int) ping_trans_color.red,
8722 (int) ping_trans_color.green,
8723 (int) ping_trans_color.blue);
8728 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8729 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8732 Adjust background and transparency samples in sub-8-bit grayscale files.
8734 if (ping_bit_depth < 8 && ping_color_type ==
8735 PNG_COLOR_TYPE_GRAY)
8743 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8745 if (ping_exclude_bKGD == MagickFalse)
8748 ping_background.gray=(png_uint_16)
8749 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8751 if (logging != MagickFalse)
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " Setting up bKGD chunk (2)");
8755 ping_have_bKGD = MagickTrue;
8758 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8759 ping_trans_color.gray));
8762 if (ping_exclude_bKGD == MagickFalse)
8764 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8767 Identify which colormap entry is the background color.
8770 number_colors=image_colors;
8772 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8773 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8776 ping_background.index=(png_byte) i;
8778 if (logging != MagickFalse)
8780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8781 " Setting up bKGD chunk with index=%d",(int) i);
8784 if (i < (ssize_t) number_colors)
8786 ping_have_bKGD = MagickTrue;
8788 if (logging != MagickFalse)
8790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8791 " background =(%d,%d,%d)",
8792 (int) ping_background.red,
8793 (int) ping_background.green,
8794 (int) ping_background.blue);
8798 else /* Can't happen */
8800 if (logging != MagickFalse)
8801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8802 " No room in PLTE to add bKGD color");
8803 ping_have_bKGD = MagickFalse;
8808 if (logging != MagickFalse)
8809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " PNG color type: %d",ping_color_type);
8812 Initialize compression level and filtering.
8814 if (logging != MagickFalse)
8816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817 " Setting up deflate compression");
8819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8820 " Compression buffer size: 32768");
8823 png_set_compression_buffer_size(ping,32768L);
8825 if (logging != MagickFalse)
8826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8827 " Compression mem level: 9");
8829 png_set_compression_mem_level(ping, 9);
8831 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8839 level=(int) MagickMin((ssize_t) quality/10,9);
8841 if (logging != MagickFalse)
8842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8843 " Compression level: %d",level);
8845 png_set_compression_level(ping,level);
8850 if (logging != MagickFalse)
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " Compression strategy: Z_HUFFMAN_ONLY");
8854 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8857 if (logging != MagickFalse)
8858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8859 " Setting up filtering");
8861 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8862 /* This became available in libpng-1.0.9. Output must be a MNG. */
8863 if (mng_info->write_mng && ((quality % 10) == 7))
8865 if (logging != MagickFalse)
8866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8867 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8869 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8873 if (logging != MagickFalse)
8874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8882 if ((quality % 10) > 5)
8883 base_filter=PNG_ALL_FILTERS;
8886 if ((quality % 10) != 5)
8887 base_filter=(int) quality % 10;
8890 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8891 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8893 base_filter=PNG_NO_FILTERS;
8896 base_filter=PNG_ALL_FILTERS;
8898 if (logging != MagickFalse)
8900 if (base_filter == PNG_ALL_FILTERS)
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " Base filter method: ADAPTIVE");
8904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905 " Base filter method: NONE");
8908 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8911 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8912 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8914 ResetImageProfileIterator(image);
8915 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8917 profile=GetImageProfile(image,name);
8919 if (profile != (StringInfo *) NULL)
8921 #ifdef PNG_WRITE_iCCP_SUPPORTED
8922 if ((LocaleCompare(name,"ICC") == 0) ||
8923 (LocaleCompare(name,"ICM") == 0))
8926 if (ping_exclude_iCCP == MagickFalse)
8928 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8929 #if (PNG_LIBPNG_VER < 10500)
8930 (png_charp) GetStringInfoDatum(profile),
8932 (png_const_bytep) GetStringInfoDatum(profile),
8934 (png_uint_32) GetStringInfoLength(profile));
8940 if (ping_exclude_zCCP == MagickFalse)
8942 Magick_png_write_raw_profile(image_info,ping,ping_info,
8943 (unsigned char *) name,(unsigned char *) name,
8944 GetStringInfoDatum(profile),
8945 (png_uint_32) GetStringInfoLength(profile));
8949 if (logging != MagickFalse)
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " Setting up text chunk with %s profile",name);
8953 name=GetNextImageProfile(image);
8957 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8958 if ((mng_info->have_write_global_srgb == 0) &&
8959 ((image->rendering_intent != UndefinedIntent) ||
8960 (image->colorspace == sRGBColorspace)))
8962 if (ping_exclude_sRGB == MagickFalse)
8965 Note image rendering intent.
8967 if (logging != MagickFalse)
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8969 " Setting up sRGB chunk");
8971 (void) png_set_sRGB(ping,ping_info,(
8972 Magick_RenderingIntent_to_PNG_RenderingIntent(
8973 image->rendering_intent)));
8975 if (ping_exclude_gAMA == MagickFalse)
8976 png_set_gAMA(ping,ping_info,0.45455);
8980 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8983 if (ping_exclude_gAMA == MagickFalse &&
8984 (ping_exclude_sRGB == MagickFalse ||
8985 (image->gamma < .45 || image->gamma > .46)))
8987 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8991 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8993 if (logging != MagickFalse)
8994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8995 " Setting up gAMA chunk");
8997 png_set_gAMA(ping,ping_info,image->gamma);
9001 if (ping_exclude_cHRM == MagickFalse)
9003 if ((mng_info->have_write_global_chrm == 0) &&
9004 (image->chromaticity.red_primary.x != 0.0))
9007 Note image chromaticity.
9008 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9016 wp=image->chromaticity.white_point;
9017 rp=image->chromaticity.red_primary;
9018 gp=image->chromaticity.green_primary;
9019 bp=image->chromaticity.blue_primary;
9021 if (logging != MagickFalse)
9022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9023 " Setting up cHRM chunk");
9025 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9031 ping_interlace_method=image_info->interlace != NoInterlace;
9033 if (mng_info->write_mng)
9034 png_set_sig_bytes(ping,8);
9036 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9038 if (mng_info->write_png_colortype != 0)
9040 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9041 if (ping_have_color != MagickFalse)
9043 ping_color_type = PNG_COLOR_TYPE_RGB;
9045 if (ping_bit_depth < 8)
9049 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9050 if (ping_have_color != MagickFalse)
9051 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9054 if (ping_need_colortype_warning != MagickFalse ||
9055 ((mng_info->write_png_depth &&
9056 (int) mng_info->write_png_depth != ping_bit_depth) ||
9057 (mng_info->write_png_colortype &&
9058 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9059 mng_info->write_png_colortype != 7 &&
9060 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9062 if (logging != MagickFalse)
9064 if (ping_need_colortype_warning != MagickFalse)
9066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9067 " Image has transparency but tRNS chunk was excluded");
9070 if (mng_info->write_png_depth)
9072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " Defined PNG:bit-depth=%u, Computed depth=%u",
9074 mng_info->write_png_depth,
9078 if (mng_info->write_png_colortype)
9080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9081 " Defined PNG:color-type=%u, Computed color type=%u",
9082 mng_info->write_png_colortype-1,
9088 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9091 if (image_matte != MagickFalse && image->matte == MagickFalse)
9093 /* Add an opaque matte channel */
9094 image->matte = MagickTrue;
9095 (void) SetImageOpacity(image,0);
9097 if (logging != MagickFalse)
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099 " Added an opaque matte channel");
9102 if (number_transparent != 0 || number_semitransparent != 0)
9104 if (ping_color_type < 4)
9106 ping_have_tRNS=MagickTrue;
9107 if (logging != MagickFalse)
9108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9109 " Setting ping_have_tRNS=MagickTrue.");
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 " Writing PNG header chunks");
9117 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9118 ping_bit_depth,ping_color_type,
9119 ping_interlace_method,ping_compression_method,
9120 ping_filter_method);
9122 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9124 png_set_PLTE(ping,ping_info,palette,number_colors);
9126 if (logging != MagickFalse)
9128 for (i=0; i< (ssize_t) number_colors; i++)
9130 if (i < ping_num_trans)
9131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9132 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9134 (int) palette[i].red,
9135 (int) palette[i].green,
9136 (int) palette[i].blue,
9138 (int) ping_trans_alpha[i]);
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " PLTE[%d] = (%d,%d,%d)",
9143 (int) palette[i].red,
9144 (int) palette[i].green,
9145 (int) palette[i].blue);
9150 if (ping_exclude_bKGD == MagickFalse)
9152 if (ping_have_bKGD != MagickFalse)
9153 png_set_bKGD(ping,ping_info,&ping_background);
9156 if (ping_exclude_pHYs == MagickFalse)
9158 if (ping_have_pHYs != MagickFalse)
9160 png_set_pHYs(ping,ping_info,
9161 ping_pHYs_x_resolution,
9162 ping_pHYs_y_resolution,
9163 ping_pHYs_unit_type);
9167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9168 " Setting up pHYs chunk");
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " x_resolution=%lu",
9171 (unsigned long) ping_pHYs_x_resolution);
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173 " y_resolution=%lu",
9174 (unsigned long) ping_pHYs_y_resolution);
9175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 (unsigned long) ping_pHYs_unit_type);
9182 #if defined(PNG_oFFs_SUPPORTED)
9183 if (ping_exclude_oFFs == MagickFalse)
9185 if (image->page.x || image->page.y)
9187 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9188 (png_int_32) image->page.y, 0);
9190 if (logging != MagickFalse)
9191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9192 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9193 (int) image->page.x, (int) image->page.y);
9198 if (mng_info->need_blob != MagickFalse)
9200 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9202 png_error(ping,"WriteBlob Failed");
9204 ping_have_blob=MagickTrue;
9207 png_write_info_before_PLTE(ping, ping_info);
9209 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9211 if (logging != MagickFalse)
9213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9217 if (ping_color_type == 3)
9218 (void) png_set_tRNS(ping, ping_info,
9225 (void) png_set_tRNS(ping, ping_info,
9230 if (logging != MagickFalse)
9232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9233 " tRNS color =(%d,%d,%d)",
9234 (int) ping_trans_color.red,
9235 (int) ping_trans_color.green,
9236 (int) ping_trans_color.blue);
9241 /* write any png-chunk-b profiles */
9242 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9244 png_write_info(ping,ping_info);
9246 /* write any PNG-chunk-m profiles */
9247 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9249 if (ping_exclude_vpAg == MagickFalse)
9251 if ((image->page.width != 0 && image->page.width != image->columns) ||
9252 (image->page.height != 0 && image->page.height != image->rows))
9257 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9258 PNGType(chunk,mng_vpAg);
9259 LogPNGChunk(logging,mng_vpAg,9L);
9260 PNGLong(chunk+4,(png_uint_32) image->page.width);
9261 PNGLong(chunk+8,(png_uint_32) image->page.height);
9262 chunk[12]=0; /* unit = pixels */
9263 (void) WriteBlob(image,13,chunk);
9264 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9268 #if (PNG_LIBPNG_VER == 10206)
9269 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9270 #define PNG_HAVE_IDAT 0x04
9271 ping->mode |= PNG_HAVE_IDAT;
9272 #undef PNG_HAVE_IDAT
9275 png_set_packing(ping);
9279 rowbytes=image->columns;
9280 if (image_depth > 8)
9282 switch (ping_color_type)
9284 case PNG_COLOR_TYPE_RGB:
9288 case PNG_COLOR_TYPE_GRAY_ALPHA:
9292 case PNG_COLOR_TYPE_RGBA:
9300 if (logging != MagickFalse)
9302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303 " Writing PNG image data");
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9308 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9309 sizeof(*ping_pixels));
9311 if (ping_pixels == (unsigned char *) NULL)
9312 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9315 Initialize image scanlines.
9317 if (setjmp(png_jmpbuf(ping)))
9323 if (image_info->verbose)
9324 (void) printf("PNG write has failed.\n");
9326 png_destroy_write_struct(&ping,&ping_info);
9327 if (quantum_info != (QuantumInfo *) NULL)
9328 quantum_info=DestroyQuantumInfo(quantum_info);
9329 if (ping_pixels != (unsigned char *) NULL)
9330 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9331 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9332 UnlockSemaphoreInfo(ping_semaphore);
9334 if (ping_have_blob != MagickFalse)
9335 (void) CloseBlob(image);
9336 image_info=DestroyImageInfo(image_info);
9337 image=DestroyImage(image);
9338 return(MagickFalse);
9340 quantum_info=AcquireQuantumInfo(image_info,image);
9341 if (quantum_info == (QuantumInfo *) NULL)
9342 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9343 quantum_info->format=UndefinedQuantumFormat;
9344 quantum_info->depth=image_depth;
9345 num_passes=png_set_interlace_handling(ping);
9347 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9348 !mng_info->write_png32) &&
9349 (mng_info->IsPalette ||
9350 (image_info->type == BilevelType)) &&
9351 image_matte == MagickFalse &&
9352 ping_have_non_bw == MagickFalse)
9354 /* Palette, Bilevel, or Opaque Monochrome */
9355 register const PixelPacket
9358 quantum_info->depth=8;
9359 for (pass=0; pass < num_passes; pass++)
9362 Convert PseudoClass image to a PNG monochrome image.
9364 for (y=0; y < (ssize_t) image->rows; y++)
9366 if (logging != MagickFalse && y == 0)
9367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368 " Writing row of pixels (0)");
9370 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9372 if (p == (const PixelPacket *) NULL)
9375 if (mng_info->IsPalette)
9377 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9378 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9379 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9380 mng_info->write_png_depth &&
9381 mng_info->write_png_depth != old_bit_depth)
9383 /* Undo pixel scaling */
9384 for (i=0; i < (ssize_t) image->columns; i++)
9385 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9386 >> (8-old_bit_depth));
9392 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9393 quantum_info,RedQuantum,ping_pixels,&image->exception);
9396 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9397 for (i=0; i < (ssize_t) image->columns; i++)
9398 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9401 if (logging != MagickFalse && y == 0)
9402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " Writing row of pixels (1)");
9405 png_write_row(ping,ping_pixels);
9407 if (image->previous == (Image *) NULL)
9409 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9410 if (status == MagickFalse)
9416 else /* Not Palette, Bilevel, or Opaque Monochrome */
9418 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9419 !mng_info->write_png32) &&
9420 (image_matte != MagickFalse ||
9421 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9422 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9424 register const PixelPacket
9427 for (pass=0; pass < num_passes; pass++)
9430 for (y=0; y < (ssize_t) image->rows; y++)
9432 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9434 if (p == (const PixelPacket *) NULL)
9437 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9439 if (mng_info->IsPalette)
9440 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9441 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9444 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9445 quantum_info,RedQuantum,ping_pixels,&image->exception);
9447 if (logging != MagickFalse && y == 0)
9448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449 " Writing GRAY PNG pixels (2)");
9452 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9454 if (logging != MagickFalse && y == 0)
9455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456 " Writing GRAY_ALPHA PNG pixels (2)");
9458 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9459 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9462 if (logging != MagickFalse && y == 0)
9463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9464 " Writing row of pixels (2)");
9466 png_write_row(ping,ping_pixels);
9469 if (image->previous == (Image *) NULL)
9471 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9472 if (status == MagickFalse)
9480 register const PixelPacket
9483 for (pass=0; pass < num_passes; pass++)
9485 if ((image_depth > 8) || (mng_info->write_png24 ||
9486 mng_info->write_png32 ||
9487 (!mng_info->write_png8 && !mng_info->IsPalette)))
9489 for (y=0; y < (ssize_t) image->rows; y++)
9491 p=GetVirtualPixels(image,0,y,image->columns,1,
9494 if (p == (const PixelPacket *) NULL)
9497 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9499 if (image->storage_class == DirectClass)
9500 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9501 quantum_info,RedQuantum,ping_pixels,&image->exception);
9504 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9505 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9508 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9510 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9511 quantum_info,GrayAlphaQuantum,ping_pixels,
9514 if (logging != MagickFalse && y == 0)
9515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516 " Writing GRAY_ALPHA PNG pixels (3)");
9519 else if (image_matte != MagickFalse)
9520 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9521 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9524 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9525 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9527 if (logging != MagickFalse && y == 0)
9528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529 " Writing row of pixels (3)");
9531 png_write_row(ping,ping_pixels);
9536 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9537 mng_info->write_png32 ||
9538 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9540 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9541 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9543 if (logging != MagickFalse)
9544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9545 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9547 quantum_info->depth=8;
9551 for (y=0; y < (ssize_t) image->rows; y++)
9553 if (logging != MagickFalse && y == 0)
9554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9555 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9557 p=GetVirtualPixels(image,0,y,image->columns,1,
9560 if (p == (const PixelPacket *) NULL)
9563 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9565 quantum_info->depth=image->depth;
9567 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9568 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9571 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9573 if (logging != MagickFalse && y == 0)
9574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575 " Writing GRAY_ALPHA PNG pixels (4)");
9577 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9578 quantum_info,GrayAlphaQuantum,ping_pixels,
9584 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9585 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9587 if (logging != MagickFalse && y <= 2)
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " Writing row of non-gray pixels (4)");
9592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9593 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9594 (int)ping_pixels[0],(int)ping_pixels[1]);
9597 png_write_row(ping,ping_pixels);
9601 if (image->previous == (Image *) NULL)
9603 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9604 if (status == MagickFalse)
9611 if (quantum_info != (QuantumInfo *) NULL)
9612 quantum_info=DestroyQuantumInfo(quantum_info);
9614 if (logging != MagickFalse)
9616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9617 " Wrote PNG image data");
9619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620 " Width: %.20g",(double) ping_width);
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Height: %.20g",(double) ping_height);
9625 if (mng_info->write_png_depth)
9627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632 " PNG bit-depth written: %d",ping_bit_depth);
9634 if (mng_info->write_png_colortype)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641 " PNG color-type written: %d",ping_color_type);
9643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9644 " PNG Interlace method: %d",ping_interlace_method);
9647 Generate text chunks.
9649 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9651 ResetImagePropertyIterator(image);
9652 property=GetNextImageProperty(image);
9653 while (property != (const char *) NULL)
9658 value=GetImageProperty(image,property);
9659 if (ping_exclude_pHYs != MagickFalse ||
9660 LocaleCompare(property,"density") != 0 ||
9661 LocaleCompare(property,"units") != 0)
9663 if (value != (const char *) NULL)
9665 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9666 text[0].key=(char *) property;
9667 text[0].text=(char *) value;
9668 text[0].text_length=strlen(value);
9670 if (ping_exclude_tEXt != MagickFalse)
9671 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9673 else if (ping_exclude_zTXt != MagickFalse)
9674 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9678 text[0].compression=image_info->compression == NoCompression ||
9679 (image_info->compression == UndefinedCompression &&
9680 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9681 PNG_TEXT_COMPRESSION_zTXt ;
9684 if (logging != MagickFalse)
9686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9687 " Setting up text chunk");
9689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9690 " keyword: %s",text[0].key);
9693 png_set_text(ping,ping_info,text,1);
9694 png_free(ping,text);
9697 property=GetNextImageProperty(image);
9701 /* write any PNG-chunk-e profiles */
9702 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9704 if (logging != MagickFalse)
9705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9706 " Writing PNG end info");
9708 png_write_end(ping,ping_info);
9710 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9712 if (mng_info->page.x || mng_info->page.y ||
9713 (ping_width != mng_info->page.width) ||
9714 (ping_height != mng_info->page.height))
9720 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9722 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9723 PNGType(chunk,mng_FRAM);
9724 LogPNGChunk(logging,mng_FRAM,27L);
9726 chunk[5]=0; /* frame name separator (no name) */
9727 chunk[6]=1; /* flag for changing delay, for next frame only */
9728 chunk[7]=0; /* flag for changing frame timeout */
9729 chunk[8]=1; /* flag for changing frame clipping for next frame */
9730 chunk[9]=0; /* flag for changing frame sync_id */
9731 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9732 chunk[14]=0; /* clipping boundaries delta type */
9733 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9735 (png_uint_32) (mng_info->page.x + ping_width));
9736 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9738 (png_uint_32) (mng_info->page.y + ping_height));
9739 (void) WriteBlob(image,31,chunk);
9740 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9741 mng_info->old_framing_mode=4;
9742 mng_info->framing_mode=1;
9746 mng_info->framing_mode=3;
9748 if (mng_info->write_mng && !mng_info->need_fram &&
9749 ((int) image->dispose == 3))
9750 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9751 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9752 "`%s'",image->filename);
9758 png_destroy_write_struct(&ping,&ping_info);
9760 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9762 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9763 UnlockSemaphoreInfo(ping_semaphore);
9766 if (ping_have_blob != MagickFalse)
9767 (void) CloseBlob(image);
9769 image_info=DestroyImageInfo(image_info);
9770 image=DestroyImage(image);
9772 /* Store bit depth actually written */
9773 s[0]=(char) ping_bit_depth;
9776 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9778 if (logging != MagickFalse)
9779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9780 " exit WriteOnePNGImage()");
9783 /* End write one PNG image */
9787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9791 % W r i t e P N G I m a g e %
9795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9797 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9798 % Multiple-image Network Graphics (MNG) image file.
9800 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9802 % The format of the WritePNGImage method is:
9804 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9806 % A description of each parameter follows:
9808 % o image_info: the image info.
9810 % o image: The image.
9812 % Returns MagickTrue on success, MagickFalse on failure.
9814 % Communicating with the PNG encoder:
9816 % While the datastream written is always in PNG format and normally would
9817 % be given the "png" file extension, this method also writes the following
9818 % pseudo-formats which are subsets of PNG:
9820 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9821 % a depth greater than 8, the depth is reduced. If transparency
9822 % is present, the tRNS chunk must only have values 0 and 255
9823 % (i.e., transparency is binary: fully opaque or fully
9824 % transparent). If other values are present they will be
9825 % 50%-thresholded to binary transparency. If more than 256
9826 % colors are present, they will be quantized to the 4-4-4-1,
9827 % 3-3-3-1, or 3-3-2-1 palette.
9829 % If you want better quantization or dithering of the colors
9830 % or alpha than that, you need to do it before calling the
9831 % PNG encoder. The pixels contain 8-bit indices even if
9832 % they could be represented with 1, 2, or 4 bits. Grayscale
9833 % images will be written as indexed PNG files even though the
9834 % PNG grayscale type might be slightly more efficient. Please
9835 % note that writing to the PNG8 format may result in loss
9836 % of color and alpha data.
9838 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9839 % chunk can be present to convey binary transparency by naming
9840 % one of the colors as transparent. The only loss incurred
9841 % is reduction of sample depth to 8. If the image has more
9842 % than one transparent color, has semitransparent pixels, or
9843 % has an opaque pixel with the same RGB components as the
9844 % transparent color, an image is not written.
9846 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9847 % transparency is permitted, i.e., the alpha sample for
9848 % each pixel can have any value from 0 to 255. The alpha
9849 % channel is present even if the image is fully opaque.
9850 % The only loss in data is the reduction of the sample depth
9853 % o -define: For more precise control of the PNG output, you can use the
9854 % Image options "png:bit-depth" and "png:color-type". These
9855 % can be set from the commandline with "-define" and also
9856 % from the application programming interfaces. The options
9857 % are case-independent and are converted to lowercase before
9858 % being passed to this encoder.
9860 % png:color-type can be 0, 2, 3, 4, or 6.
9862 % When png:color-type is 0 (Grayscale), png:bit-depth can
9863 % be 1, 2, 4, 8, or 16.
9865 % When png:color-type is 2 (RGB), png:bit-depth can
9868 % When png:color-type is 3 (Indexed), png:bit-depth can
9869 % be 1, 2, 4, or 8. This refers to the number of bits
9870 % used to store the index. The color samples always have
9871 % bit-depth 8 in indexed PNG files.
9873 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9874 % png:bit-depth can be 8 or 16.
9876 % If the image cannot be written without loss with the requested bit-depth
9877 % and color-type, a PNG file will not be written, and the encoder will
9878 % return MagickFalse.
9880 % Since image encoders should not be responsible for the "heavy lifting",
9881 % the user should make sure that ImageMagick has already reduced the
9882 % image depth and number of colors and limit transparency to binary
9883 % transparency prior to attempting to write the image with depth, color,
9884 % or transparency limitations.
9886 % TODO: Enforce the previous paragraph.
9888 % Note that another definition, "png:bit-depth-written" exists, but it
9889 % is not intended for external use. It is only used internally by the
9890 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9892 % It is possible to request that the PNG encoder write previously-formatted
9893 % ancillary chunks in the output PNG file, using the "-profile" commandline
9894 % option as shown below or by setting the profile via a programming
9897 % -profile PNG-chunk-x:<file>
9899 % where x is a location flag and <file> is a file containing the chunk
9900 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9901 % This encoder will compute the chunk length and CRC, so those must not
9902 % be included in the file.
9904 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9905 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9906 % of the same type, then add a short unique string after the "x" to prevent
9907 % subsequent profiles from overwriting the preceding ones, e.g.,
9909 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9911 % As of version 6.6.6 the following optimizations are always done:
9913 % o 32-bit depth is reduced to 16.
9914 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9915 % high byte and low byte are identical.
9916 % o Palette is sorted to remove unused entries and to put a
9917 % transparent color first, if BUILD_PNG_PALETTE is defined.
9918 % o Opaque matte channel is removed when writing an indexed PNG.
9919 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9920 % this can be done without loss and a larger bit depth N was not
9921 % requested via the "-define PNG:bit-depth=N" option.
9922 % o If matte channel is present but only one transparent color is
9923 % present, RGB+tRNS is written instead of RGBA
9924 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9925 % was requested when converting an opaque image).
9927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9929 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9951 assert(image_info != (const ImageInfo *) NULL);
9952 assert(image_info->signature == MagickSignature);
9953 assert(image != (Image *) NULL);
9954 assert(image->signature == MagickSignature);
9955 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9956 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9958 Allocate a MngInfo structure.
9960 have_mng_structure=MagickFalse;
9961 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9963 if (mng_info == (MngInfo *) NULL)
9964 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9967 Initialize members of the MngInfo structure.
9969 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9970 mng_info->image=image;
9971 mng_info->equal_backgrounds=MagickTrue;
9972 have_mng_structure=MagickTrue;
9974 /* See if user has requested a specific PNG subformat */
9976 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9977 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9978 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9980 if (mng_info->write_png8)
9982 mng_info->write_png_colortype = /* 3 */ 4;
9983 mng_info->write_png_depth = 8;
9987 if (mng_info->write_png24)
9989 mng_info->write_png_colortype = /* 2 */ 3;
9990 mng_info->write_png_depth = 8;
9993 if (image->matte == MagickTrue)
9994 (void) SetImageType(image,TrueColorMatteType);
9997 (void) SetImageType(image,TrueColorType);
9999 (void) SyncImage(image);
10002 if (mng_info->write_png32)
10004 mng_info->write_png_colortype = /* 6 */ 7;
10005 mng_info->write_png_depth = 8;
10008 if (image->matte == MagickTrue)
10009 (void) SetImageType(image,TrueColorMatteType);
10012 (void) SetImageType(image,TrueColorType);
10014 (void) SyncImage(image);
10017 value=GetImageOption(image_info,"png:bit-depth");
10019 if (value != (char *) NULL)
10021 if (LocaleCompare(value,"1") == 0)
10022 mng_info->write_png_depth = 1;
10024 else if (LocaleCompare(value,"2") == 0)
10025 mng_info->write_png_depth = 2;
10027 else if (LocaleCompare(value,"4") == 0)
10028 mng_info->write_png_depth = 4;
10030 else if (LocaleCompare(value,"8") == 0)
10031 mng_info->write_png_depth = 8;
10033 else if (LocaleCompare(value,"16") == 0)
10034 mng_info->write_png_depth = 16;
10037 (void) ThrowMagickException(&image->exception,
10038 GetMagickModule(),CoderWarning,
10039 "ignoring invalid defined png:bit-depth",
10042 if (logging != MagickFalse)
10043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10047 value=GetImageOption(image_info,"png:color-type");
10049 if (value != (char *) NULL)
10051 /* We must store colortype+1 because 0 is a valid colortype */
10052 if (LocaleCompare(value,"0") == 0)
10053 mng_info->write_png_colortype = 1;
10055 else if (LocaleCompare(value,"2") == 0)
10056 mng_info->write_png_colortype = 3;
10058 else if (LocaleCompare(value,"3") == 0)
10059 mng_info->write_png_colortype = 4;
10061 else if (LocaleCompare(value,"4") == 0)
10062 mng_info->write_png_colortype = 5;
10064 else if (LocaleCompare(value,"6") == 0)
10065 mng_info->write_png_colortype = 7;
10068 (void) ThrowMagickException(&image->exception,
10069 GetMagickModule(),CoderWarning,
10070 "ignoring invalid defined png:color-type",
10073 if (logging != MagickFalse)
10074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10075 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10078 /* Check for chunks to be excluded:
10080 * The default is to not exclude any known chunks except for any
10081 * listed in the "unused_chunks" array, above.
10083 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10084 * define (in the image properties or in the image artifacts)
10085 * or via a mng_info member. For convenience, in addition
10086 * to or instead of a comma-separated list of chunks, the
10087 * "exclude-chunk" string can be simply "all" or "none".
10089 * The exclude-chunk define takes priority over the mng_info.
10091 * A "PNG:include-chunk" define takes priority over both the
10092 * mng_info and the "PNG:exclude-chunk" define. Like the
10093 * "exclude-chunk" string, it can define "all" or "none" as
10094 * well as a comma-separated list. Chunks that are unknown to
10095 * ImageMagick are always excluded, regardless of their "copy-safe"
10096 * status according to the PNG specification, and even if they
10097 * appear in the "include-chunk" list.
10099 * Finally, all chunks listed in the "unused_chunks" array are
10100 * automatically excluded, regardless of the other instructions
10103 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10104 * will not be written and the gAMA chunk will only be written if it
10105 * is not between .45 and .46, or approximately (1.0/2.2).
10107 * If you exclude tRNS and the image has transparency, the colortype
10108 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10110 * The -strip option causes StripImage() to set the png:include-chunk
10111 * artifact to "none,gama".
10114 mng_info->ping_exclude_bKGD=MagickFalse;
10115 mng_info->ping_exclude_cHRM=MagickFalse;
10116 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10117 mng_info->ping_exclude_gAMA=MagickFalse;
10118 mng_info->ping_exclude_cHRM=MagickFalse;
10119 mng_info->ping_exclude_iCCP=MagickFalse;
10120 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10121 mng_info->ping_exclude_oFFs=MagickFalse;
10122 mng_info->ping_exclude_pHYs=MagickFalse;
10123 mng_info->ping_exclude_sRGB=MagickFalse;
10124 mng_info->ping_exclude_tEXt=MagickFalse;
10125 mng_info->ping_exclude_tRNS=MagickFalse;
10126 mng_info->ping_exclude_vpAg=MagickFalse;
10127 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10128 mng_info->ping_exclude_zTXt=MagickFalse;
10130 excluding=MagickFalse;
10132 for (source=0; source<1; source++)
10136 value=GetImageArtifact(image,"png:exclude-chunk");
10139 value=GetImageArtifact(image,"png:exclude-chunks");
10143 value=GetImageOption(image_info,"png:exclude-chunk");
10146 value=GetImageOption(image_info,"png:exclude-chunks");
10155 excluding=MagickTrue;
10157 if (logging != MagickFalse)
10160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10161 " png:exclude-chunk=%s found in image artifacts.\n", value);
10163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164 " png:exclude-chunk=%s found in image properties.\n", value);
10167 last=strlen(value);
10169 for (i=0; i<(int) last; i+=5)
10172 if (LocaleNCompare(value+i,"all",3) == 0)
10174 mng_info->ping_exclude_bKGD=MagickTrue;
10175 mng_info->ping_exclude_cHRM=MagickTrue;
10176 mng_info->ping_exclude_EXIF=MagickTrue;
10177 mng_info->ping_exclude_gAMA=MagickTrue;
10178 mng_info->ping_exclude_iCCP=MagickTrue;
10179 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10180 mng_info->ping_exclude_oFFs=MagickTrue;
10181 mng_info->ping_exclude_pHYs=MagickTrue;
10182 mng_info->ping_exclude_sRGB=MagickTrue;
10183 mng_info->ping_exclude_tEXt=MagickTrue;
10184 mng_info->ping_exclude_tRNS=MagickTrue;
10185 mng_info->ping_exclude_vpAg=MagickTrue;
10186 mng_info->ping_exclude_zCCP=MagickTrue;
10187 mng_info->ping_exclude_zTXt=MagickTrue;
10191 if (LocaleNCompare(value+i,"none",4) == 0)
10193 mng_info->ping_exclude_bKGD=MagickFalse;
10194 mng_info->ping_exclude_cHRM=MagickFalse;
10195 mng_info->ping_exclude_EXIF=MagickFalse;
10196 mng_info->ping_exclude_gAMA=MagickFalse;
10197 mng_info->ping_exclude_iCCP=MagickFalse;
10198 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10199 mng_info->ping_exclude_oFFs=MagickFalse;
10200 mng_info->ping_exclude_pHYs=MagickFalse;
10201 mng_info->ping_exclude_sRGB=MagickFalse;
10202 mng_info->ping_exclude_tEXt=MagickFalse;
10203 mng_info->ping_exclude_tRNS=MagickFalse;
10204 mng_info->ping_exclude_vpAg=MagickFalse;
10205 mng_info->ping_exclude_zCCP=MagickFalse;
10206 mng_info->ping_exclude_zTXt=MagickFalse;
10209 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10210 mng_info->ping_exclude_bKGD=MagickTrue;
10212 if (LocaleNCompare(value+i,"chrm",4) == 0)
10213 mng_info->ping_exclude_cHRM=MagickTrue;
10215 if (LocaleNCompare(value+i,"exif",4) == 0)
10216 mng_info->ping_exclude_EXIF=MagickTrue;
10218 if (LocaleNCompare(value+i,"gama",4) == 0)
10219 mng_info->ping_exclude_gAMA=MagickTrue;
10221 if (LocaleNCompare(value+i,"iccp",4) == 0)
10222 mng_info->ping_exclude_iCCP=MagickTrue;
10225 if (LocaleNCompare(value+i,"itxt",4) == 0)
10226 mng_info->ping_exclude_iTXt=MagickTrue;
10229 if (LocaleNCompare(value+i,"gama",4) == 0)
10230 mng_info->ping_exclude_gAMA=MagickTrue;
10232 if (LocaleNCompare(value+i,"offs",4) == 0)
10233 mng_info->ping_exclude_oFFs=MagickTrue;
10235 if (LocaleNCompare(value+i,"phys",4) == 0)
10236 mng_info->ping_exclude_pHYs=MagickTrue;
10238 if (LocaleNCompare(value+i,"srgb",4) == 0)
10239 mng_info->ping_exclude_sRGB=MagickTrue;
10241 if (LocaleNCompare(value+i,"text",4) == 0)
10242 mng_info->ping_exclude_tEXt=MagickTrue;
10244 if (LocaleNCompare(value+i,"trns",4) == 0)
10245 mng_info->ping_exclude_tRNS=MagickTrue;
10247 if (LocaleNCompare(value+i,"vpag",4) == 0)
10248 mng_info->ping_exclude_vpAg=MagickTrue;
10250 if (LocaleNCompare(value+i,"zccp",4) == 0)
10251 mng_info->ping_exclude_zCCP=MagickTrue;
10253 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10254 mng_info->ping_exclude_zTXt=MagickTrue;
10260 for (source=0; source<1; source++)
10264 value=GetImageArtifact(image,"png:include-chunk");
10267 value=GetImageArtifact(image,"png:include-chunks");
10271 value=GetImageOption(image_info,"png:include-chunk");
10274 value=GetImageOption(image_info,"png:include-chunks");
10282 excluding=MagickTrue;
10284 if (logging != MagickFalse)
10287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288 " png:include-chunk=%s found in image artifacts.\n", value);
10290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291 " png:include-chunk=%s found in image properties.\n", value);
10294 last=strlen(value);
10296 for (i=0; i<(int) last; i+=5)
10298 if (LocaleNCompare(value+i,"all",3) == 0)
10300 mng_info->ping_exclude_bKGD=MagickFalse;
10301 mng_info->ping_exclude_cHRM=MagickFalse;
10302 mng_info->ping_exclude_EXIF=MagickFalse;
10303 mng_info->ping_exclude_gAMA=MagickFalse;
10304 mng_info->ping_exclude_iCCP=MagickFalse;
10305 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10306 mng_info->ping_exclude_oFFs=MagickFalse;
10307 mng_info->ping_exclude_pHYs=MagickFalse;
10308 mng_info->ping_exclude_sRGB=MagickFalse;
10309 mng_info->ping_exclude_tEXt=MagickFalse;
10310 mng_info->ping_exclude_tRNS=MagickFalse;
10311 mng_info->ping_exclude_vpAg=MagickFalse;
10312 mng_info->ping_exclude_zCCP=MagickFalse;
10313 mng_info->ping_exclude_zTXt=MagickFalse;
10317 if (LocaleNCompare(value+i,"none",4) == 0)
10319 mng_info->ping_exclude_bKGD=MagickTrue;
10320 mng_info->ping_exclude_cHRM=MagickTrue;
10321 mng_info->ping_exclude_EXIF=MagickTrue;
10322 mng_info->ping_exclude_gAMA=MagickTrue;
10323 mng_info->ping_exclude_iCCP=MagickTrue;
10324 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10325 mng_info->ping_exclude_oFFs=MagickTrue;
10326 mng_info->ping_exclude_pHYs=MagickTrue;
10327 mng_info->ping_exclude_sRGB=MagickTrue;
10328 mng_info->ping_exclude_tEXt=MagickTrue;
10329 mng_info->ping_exclude_tRNS=MagickTrue;
10330 mng_info->ping_exclude_vpAg=MagickTrue;
10331 mng_info->ping_exclude_zCCP=MagickTrue;
10332 mng_info->ping_exclude_zTXt=MagickTrue;
10335 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10336 mng_info->ping_exclude_bKGD=MagickFalse;
10338 if (LocaleNCompare(value+i,"chrm",4) == 0)
10339 mng_info->ping_exclude_cHRM=MagickFalse;
10341 if (LocaleNCompare(value+i,"exif",4) == 0)
10342 mng_info->ping_exclude_EXIF=MagickFalse;
10344 if (LocaleNCompare(value+i,"gama",4) == 0)
10345 mng_info->ping_exclude_gAMA=MagickFalse;
10347 if (LocaleNCompare(value+i,"iccp",4) == 0)
10348 mng_info->ping_exclude_iCCP=MagickFalse;
10351 if (LocaleNCompare(value+i,"itxt",4) == 0)
10352 mng_info->ping_exclude_iTXt=MagickFalse;
10355 if (LocaleNCompare(value+i,"gama",4) == 0)
10356 mng_info->ping_exclude_gAMA=MagickFalse;
10358 if (LocaleNCompare(value+i,"offs",4) == 0)
10359 mng_info->ping_exclude_oFFs=MagickFalse;
10361 if (LocaleNCompare(value+i,"phys",4) == 0)
10362 mng_info->ping_exclude_pHYs=MagickFalse;
10364 if (LocaleNCompare(value+i,"srgb",4) == 0)
10365 mng_info->ping_exclude_sRGB=MagickFalse;
10367 if (LocaleNCompare(value+i,"text",4) == 0)
10368 mng_info->ping_exclude_tEXt=MagickFalse;
10370 if (LocaleNCompare(value+i,"trns",4) == 0)
10371 mng_info->ping_exclude_tRNS=MagickFalse;
10373 if (LocaleNCompare(value+i,"vpag",4) == 0)
10374 mng_info->ping_exclude_vpAg=MagickFalse;
10376 if (LocaleNCompare(value+i,"zccp",4) == 0)
10377 mng_info->ping_exclude_zCCP=MagickFalse;
10379 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10380 mng_info->ping_exclude_zTXt=MagickFalse;
10386 if (excluding != MagickFalse && logging != MagickFalse)
10388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10389 " Chunks to be excluded from the output PNG:");
10390 if (mng_info->ping_exclude_bKGD != MagickFalse)
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393 if (mng_info->ping_exclude_cHRM != MagickFalse)
10394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 if (mng_info->ping_exclude_EXIF != MagickFalse)
10397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10399 if (mng_info->ping_exclude_gAMA != MagickFalse)
10400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10402 if (mng_info->ping_exclude_iCCP != MagickFalse)
10403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10406 if (mng_info->ping_exclude_iTXt != MagickFalse)
10407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 if (mng_info->ping_exclude_oFFs != MagickFalse)
10411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413 if (mng_info->ping_exclude_pHYs != MagickFalse)
10414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 if (mng_info->ping_exclude_sRGB != MagickFalse)
10417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10419 if (mng_info->ping_exclude_tEXt != MagickFalse)
10420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422 if (mng_info->ping_exclude_tRNS != MagickFalse)
10423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10425 if (mng_info->ping_exclude_vpAg != MagickFalse)
10426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10428 if (mng_info->ping_exclude_zCCP != MagickFalse)
10429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 if (mng_info->ping_exclude_zTXt != MagickFalse)
10432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 mng_info->need_blob = MagickTrue;
10438 status=WriteOnePNGImage(mng_info,image_info,image);
10440 MngInfoFreeStruct(mng_info,&have_mng_structure);
10442 if (logging != MagickFalse)
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10448 #if defined(JNG_SUPPORTED)
10450 /* Write one JNG image */
10451 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10452 const ImageInfo *image_info,Image *image)
10473 jng_alpha_compression_method,
10474 jng_alpha_sample_depth,
10481 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10482 " Enter WriteOneJNGImage()");
10484 blob=(unsigned char *) NULL;
10485 jpeg_image=(Image *) NULL;
10486 jpeg_image_info=(ImageInfo *) NULL;
10489 transparent=image_info->type==GrayscaleMatteType ||
10490 image_info->type==TrueColorMatteType;
10492 jng_alpha_sample_depth=0;
10493 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10494 jng_alpha_compression_method=0;
10496 if (image->matte != MagickFalse)
10498 /* if any pixels are transparent */
10499 transparent=MagickTrue;
10500 if (image_info->compression==JPEGCompression)
10501 jng_alpha_compression_method=8;
10508 /* Create JPEG blob, image, and image_info */
10509 if (logging != MagickFalse)
10510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511 " Creating jpeg_image_info for opacity.");
10513 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10515 if (jpeg_image_info == (ImageInfo *) NULL)
10516 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10518 if (logging != MagickFalse)
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " Creating jpeg_image.");
10522 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10524 if (jpeg_image == (Image *) NULL)
10525 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10527 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10528 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10529 status=NegateImage(jpeg_image,MagickFalse);
10530 jpeg_image->matte=MagickFalse;
10532 if (jng_quality >= 1000)
10533 jpeg_image_info->quality=jng_quality/1000;
10536 jpeg_image_info->quality=jng_quality;
10538 jpeg_image_info->type=GrayscaleType;
10539 (void) SetImageType(jpeg_image,GrayscaleType);
10540 (void) AcquireUniqueFilename(jpeg_image->filename);
10541 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10542 "%s",jpeg_image->filename);
10545 /* To do: check bit depth of PNG alpha channel */
10547 /* Check if image is grayscale. */
10548 if (image_info->type != TrueColorMatteType && image_info->type !=
10549 TrueColorType && ImageIsGray(image))
10554 if (jng_alpha_compression_method==0)
10559 /* Encode opacity as a grayscale PNG blob */
10560 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10561 &image->exception);
10562 if (logging != MagickFalse)
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " Creating PNG blob.");
10567 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10568 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10569 jpeg_image_info->interlace=NoInterlace;
10571 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10572 &image->exception);
10574 /* Retrieve sample depth used */
10575 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10576 if (value != (char *) NULL)
10577 jng_alpha_sample_depth= (unsigned int) value[0];
10581 /* Encode opacity as a grayscale JPEG blob */
10583 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10584 &image->exception);
10586 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10587 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10588 jpeg_image_info->interlace=NoInterlace;
10589 if (logging != MagickFalse)
10590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591 " Creating blob.");
10592 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10593 &image->exception);
10594 jng_alpha_sample_depth=8;
10596 if (logging != MagickFalse)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 " Successfully read jpeg_image into a blob, length=%.20g.",
10602 /* Destroy JPEG image and image_info */
10603 jpeg_image=DestroyImage(jpeg_image);
10604 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10605 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10608 /* Write JHDR chunk */
10609 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10610 PNGType(chunk,mng_JHDR);
10611 LogPNGChunk(logging,mng_JHDR,16L);
10612 PNGLong(chunk+4,(png_uint_32) image->columns);
10613 PNGLong(chunk+8,(png_uint_32) image->rows);
10614 chunk[12]=jng_color_type;
10615 chunk[13]=8; /* sample depth */
10616 chunk[14]=8; /*jng_image_compression_method */
10617 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10618 chunk[16]=jng_alpha_sample_depth;
10619 chunk[17]=jng_alpha_compression_method;
10620 chunk[18]=0; /*jng_alpha_filter_method */
10621 chunk[19]=0; /*jng_alpha_interlace_method */
10622 (void) WriteBlob(image,20,chunk);
10623 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10624 if (logging != MagickFalse)
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 " JNG width:%15lu",(unsigned long) image->columns);
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " JNG height:%14lu",(unsigned long) image->rows);
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " JNG color type:%10d",jng_color_type);
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " JNG sample depth:%8d",8);
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " JNG compression:%9d",8);
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642 " JNG interlace:%11d",0);
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " JNG alpha compression:%3d",jng_alpha_compression_method);
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " JNG alpha filter:%8d",0);
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " JNG alpha interlace:%5d",0);
10657 /* Write any JNG-chunk-b profiles */
10658 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10661 Write leading ancillary chunks
10667 Write JNG bKGD chunk
10678 if (jng_color_type == 8 || jng_color_type == 12)
10682 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10683 PNGType(chunk,mng_bKGD);
10684 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10685 red=ScaleQuantumToChar(image->background_color.red);
10686 green=ScaleQuantumToChar(image->background_color.green);
10687 blue=ScaleQuantumToChar(image->background_color.blue);
10694 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10695 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10698 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10701 Write JNG sRGB chunk
10703 (void) WriteBlobMSBULong(image,1L);
10704 PNGType(chunk,mng_sRGB);
10705 LogPNGChunk(logging,mng_sRGB,1L);
10707 if (image->rendering_intent != UndefinedIntent)
10708 chunk[4]=(unsigned char)
10709 Magick_RenderingIntent_to_PNG_RenderingIntent(
10710 (image->rendering_intent));
10713 chunk[4]=(unsigned char)
10714 Magick_RenderingIntent_to_PNG_RenderingIntent(
10715 (PerceptualIntent));
10717 (void) WriteBlob(image,5,chunk);
10718 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10722 if (image->gamma != 0.0)
10725 Write JNG gAMA chunk
10727 (void) WriteBlobMSBULong(image,4L);
10728 PNGType(chunk,mng_gAMA);
10729 LogPNGChunk(logging,mng_gAMA,4L);
10730 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10731 (void) WriteBlob(image,8,chunk);
10732 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10735 if ((mng_info->equal_chrms == MagickFalse) &&
10736 (image->chromaticity.red_primary.x != 0.0))
10742 Write JNG cHRM chunk
10744 (void) WriteBlobMSBULong(image,32L);
10745 PNGType(chunk,mng_cHRM);
10746 LogPNGChunk(logging,mng_cHRM,32L);
10747 primary=image->chromaticity.white_point;
10748 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10749 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10750 primary=image->chromaticity.red_primary;
10751 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10752 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10753 primary=image->chromaticity.green_primary;
10754 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10755 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10756 primary=image->chromaticity.blue_primary;
10757 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10758 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10759 (void) WriteBlob(image,36,chunk);
10760 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10764 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10767 Write JNG pHYs chunk
10769 (void) WriteBlobMSBULong(image,9L);
10770 PNGType(chunk,mng_pHYs);
10771 LogPNGChunk(logging,mng_pHYs,9L);
10772 if (image->units == PixelsPerInchResolution)
10774 PNGLong(chunk+4,(png_uint_32)
10775 (image->x_resolution*100.0/2.54+0.5));
10777 PNGLong(chunk+8,(png_uint_32)
10778 (image->y_resolution*100.0/2.54+0.5));
10785 if (image->units == PixelsPerCentimeterResolution)
10787 PNGLong(chunk+4,(png_uint_32)
10788 (image->x_resolution*100.0+0.5));
10790 PNGLong(chunk+8,(png_uint_32)
10791 (image->y_resolution*100.0+0.5));
10798 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10799 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10803 (void) WriteBlob(image,13,chunk);
10804 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10807 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10810 Write JNG oFFs chunk
10812 (void) WriteBlobMSBULong(image,9L);
10813 PNGType(chunk,mng_oFFs);
10814 LogPNGChunk(logging,mng_oFFs,9L);
10815 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10816 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10818 (void) WriteBlob(image,13,chunk);
10819 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10821 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10823 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10824 PNGType(chunk,mng_vpAg);
10825 LogPNGChunk(logging,mng_vpAg,9L);
10826 PNGLong(chunk+4,(png_uint_32) image->page.width);
10827 PNGLong(chunk+8,(png_uint_32) image->page.height);
10828 chunk[12]=0; /* unit = pixels */
10829 (void) WriteBlob(image,13,chunk);
10830 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10836 if (jng_alpha_compression_method==0)
10844 /* Write IDAT chunk header */
10845 if (logging != MagickFalse)
10846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10847 " Write IDAT chunks from blob, length=%.20g.",(double)
10850 /* Copy IDAT chunks */
10853 for (i=8; i<(ssize_t) length; i+=len+12)
10855 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10858 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10860 /* Found an IDAT chunk. */
10861 (void) WriteBlobMSBULong(image,(size_t) len);
10862 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10863 (void) WriteBlob(image,(size_t) len+4,p);
10864 (void) WriteBlobMSBULong(image,
10865 crc32(0,p,(uInt) len+4));
10870 if (logging != MagickFalse)
10871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " Skipping %c%c%c%c chunk, length=%.20g.",
10873 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10880 /* Write JDAA chunk header */
10881 if (logging != MagickFalse)
10882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10883 " Write JDAA chunk, length=%.20g.",(double) length);
10884 (void) WriteBlobMSBULong(image,(size_t) length);
10885 PNGType(chunk,mng_JDAA);
10886 LogPNGChunk(logging,mng_JDAA,length);
10887 /* Write JDAT chunk(s) data */
10888 (void) WriteBlob(image,4,chunk);
10889 (void) WriteBlob(image,length,blob);
10890 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10893 blob=(unsigned char *) RelinquishMagickMemory(blob);
10896 /* Encode image as a JPEG blob */
10897 if (logging != MagickFalse)
10898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10899 " Creating jpeg_image_info.");
10900 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10901 if (jpeg_image_info == (ImageInfo *) NULL)
10902 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10904 if (logging != MagickFalse)
10905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906 " Creating jpeg_image.");
10908 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10909 if (jpeg_image == (Image *) NULL)
10910 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10911 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10913 (void) AcquireUniqueFilename(jpeg_image->filename);
10914 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10915 jpeg_image->filename);
10917 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10918 &image->exception);
10920 if (logging != MagickFalse)
10921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10923 (double) jpeg_image->rows);
10925 if (jng_color_type == 8 || jng_color_type == 12)
10926 jpeg_image_info->type=GrayscaleType;
10928 jpeg_image_info->quality=jng_quality % 1000;
10929 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10930 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10932 if (logging != MagickFalse)
10933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934 " Creating blob.");
10936 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10938 if (logging != MagickFalse)
10940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941 " Successfully read jpeg_image into a blob, length=%.20g.",
10944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10945 " Write JDAT chunk, length=%.20g.",(double) length);
10948 /* Write JDAT chunk(s) */
10949 (void) WriteBlobMSBULong(image,(size_t) length);
10950 PNGType(chunk,mng_JDAT);
10951 LogPNGChunk(logging,mng_JDAT,length);
10952 (void) WriteBlob(image,4,chunk);
10953 (void) WriteBlob(image,length,blob);
10954 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10956 jpeg_image=DestroyImage(jpeg_image);
10957 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10958 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10959 blob=(unsigned char *) RelinquishMagickMemory(blob);
10961 /* Write any JNG-chunk-e profiles */
10962 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10964 /* Write IEND chunk */
10965 (void) WriteBlobMSBULong(image,0L);
10966 PNGType(chunk,mng_IEND);
10967 LogPNGChunk(logging,mng_IEND,0);
10968 (void) WriteBlob(image,4,chunk);
10969 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10971 if (logging != MagickFalse)
10972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10973 " exit WriteOneJNGImage()");
10980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10984 % W r i t e J N G I m a g e %
10988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10990 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10992 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10994 % The format of the WriteJNGImage method is:
10996 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10998 % A description of each parameter follows:
11000 % o image_info: the image info.
11002 % o image: The image.
11004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11006 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11009 have_mng_structure,
11019 assert(image_info != (const ImageInfo *) NULL);
11020 assert(image_info->signature == MagickSignature);
11021 assert(image != (Image *) NULL);
11022 assert(image->signature == MagickSignature);
11023 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11024 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11025 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11026 if (status == MagickFalse)
11030 Allocate a MngInfo structure.
11032 have_mng_structure=MagickFalse;
11033 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11034 if (mng_info == (MngInfo *) NULL)
11035 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11037 Initialize members of the MngInfo structure.
11039 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11040 mng_info->image=image;
11041 have_mng_structure=MagickTrue;
11043 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11045 status=WriteOneJNGImage(mng_info,image_info,image);
11046 (void) CloseBlob(image);
11048 (void) CatchImageException(image);
11049 MngInfoFreeStruct(mng_info,&have_mng_structure);
11050 if (logging != MagickFalse)
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11058 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11067 have_mng_structure,
11070 volatile MagickBooleanType
11082 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11083 defined(PNG_MNG_FEATURES_SUPPORTED)
11086 all_images_are_gray,
11096 volatile unsigned int
11107 #if (PNG_LIBPNG_VER < 10200)
11108 if (image_info->verbose)
11109 printf("Your PNG library (libpng-%s) is rather old.\n",
11110 PNG_LIBPNG_VER_STRING);
11116 assert(image_info != (const ImageInfo *) NULL);
11117 assert(image_info->signature == MagickSignature);
11118 assert(image != (Image *) NULL);
11119 assert(image->signature == MagickSignature);
11120 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11121 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11122 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11123 if (status == MagickFalse)
11127 Allocate a MngInfo structure.
11129 have_mng_structure=MagickFalse;
11130 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11131 if (mng_info == (MngInfo *) NULL)
11132 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11134 Initialize members of the MngInfo structure.
11136 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11137 mng_info->image=image;
11138 have_mng_structure=MagickTrue;
11139 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11142 * See if user has requested a specific PNG subformat to be used
11143 * for all of the PNGs in the MNG being written, e.g.,
11145 * convert *.png png8:animation.mng
11147 * To do: check -define png:bit_depth and png:color_type as well,
11148 * or perhaps use mng:bit_depth and mng:color_type instead for
11152 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11153 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11154 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11156 write_jng=MagickFalse;
11157 if (image_info->compression == JPEGCompression)
11158 write_jng=MagickTrue;
11160 mng_info->adjoin=image_info->adjoin &&
11161 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11163 if (logging != MagickFalse)
11165 /* Log some info about the input */
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170 " Checking input image(s)");
11172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11173 " Image_info depth: %.20g",(double) image_info->depth);
11175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176 " Type: %d",image_info->type);
11179 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182 " Scene: %.20g",(double) scene++);
11184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185 " Image depth: %.20g",(double) p->depth);
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11195 if (p->storage_class == PseudoClass)
11196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11197 " Storage class: PseudoClass");
11200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201 " Storage class: DirectClass");
11204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205 " Number of colors: %.20g",(double) p->colors);
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209 " Number of colors: unspecified");
11211 if (mng_info->adjoin == MagickFalse)
11216 use_global_plte=MagickFalse;
11217 all_images_are_gray=MagickFalse;
11218 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11219 need_local_plte=MagickTrue;
11221 need_defi=MagickFalse;
11222 need_matte=MagickFalse;
11223 mng_info->framing_mode=1;
11224 mng_info->old_framing_mode=1;
11227 if (image_info->page != (char *) NULL)
11230 Determine image bounding box.
11232 SetGeometry(image,&mng_info->page);
11233 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11234 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11246 mng_info->page=image->page;
11247 need_geom=MagickTrue;
11248 if (mng_info->page.width || mng_info->page.height)
11249 need_geom=MagickFalse;
11251 Check all the scenes.
11253 initial_delay=image->delay;
11254 need_iterations=MagickFalse;
11255 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11256 mng_info->equal_physs=MagickTrue,
11257 mng_info->equal_gammas=MagickTrue;
11258 mng_info->equal_srgbs=MagickTrue;
11259 mng_info->equal_backgrounds=MagickTrue;
11261 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11262 defined(PNG_MNG_FEATURES_SUPPORTED)
11263 all_images_are_gray=MagickTrue;
11264 mng_info->equal_palettes=MagickFalse;
11265 need_local_plte=MagickFalse;
11267 for (next_image=image; next_image != (Image *) NULL; )
11271 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11272 mng_info->page.width=next_image->columns+next_image->page.x;
11274 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11275 mng_info->page.height=next_image->rows+next_image->page.y;
11278 if (next_image->page.x || next_image->page.y)
11279 need_defi=MagickTrue;
11281 if (next_image->matte)
11282 need_matte=MagickTrue;
11284 if ((int) next_image->dispose >= BackgroundDispose)
11285 if (next_image->matte || next_image->page.x || next_image->page.y ||
11286 ((next_image->columns < mng_info->page.width) &&
11287 (next_image->rows < mng_info->page.height)))
11288 mng_info->need_fram=MagickTrue;
11290 if (next_image->iterations)
11291 need_iterations=MagickTrue;
11293 final_delay=next_image->delay;
11295 if (final_delay != initial_delay || final_delay > 1UL*
11296 next_image->ticks_per_second)
11297 mng_info->need_fram=1;
11299 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11300 defined(PNG_MNG_FEATURES_SUPPORTED)
11302 check for global palette possibility.
11304 if (image->matte != MagickFalse)
11305 need_local_plte=MagickTrue;
11307 if (need_local_plte == 0)
11309 if (ImageIsGray(image) == MagickFalse)
11310 all_images_are_gray=MagickFalse;
11311 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11312 if (use_global_plte == 0)
11313 use_global_plte=mng_info->equal_palettes;
11314 need_local_plte=!mng_info->equal_palettes;
11317 if (GetNextImageInList(next_image) != (Image *) NULL)
11319 if (next_image->background_color.red !=
11320 next_image->next->background_color.red ||
11321 next_image->background_color.green !=
11322 next_image->next->background_color.green ||
11323 next_image->background_color.blue !=
11324 next_image->next->background_color.blue)
11325 mng_info->equal_backgrounds=MagickFalse;
11327 if (next_image->gamma != next_image->next->gamma)
11328 mng_info->equal_gammas=MagickFalse;
11330 if (next_image->rendering_intent !=
11331 next_image->next->rendering_intent)
11332 mng_info->equal_srgbs=MagickFalse;
11334 if ((next_image->units != next_image->next->units) ||
11335 (next_image->x_resolution != next_image->next->x_resolution) ||
11336 (next_image->y_resolution != next_image->next->y_resolution))
11337 mng_info->equal_physs=MagickFalse;
11339 if (mng_info->equal_chrms)
11341 if (next_image->chromaticity.red_primary.x !=
11342 next_image->next->chromaticity.red_primary.x ||
11343 next_image->chromaticity.red_primary.y !=
11344 next_image->next->chromaticity.red_primary.y ||
11345 next_image->chromaticity.green_primary.x !=
11346 next_image->next->chromaticity.green_primary.x ||
11347 next_image->chromaticity.green_primary.y !=
11348 next_image->next->chromaticity.green_primary.y ||
11349 next_image->chromaticity.blue_primary.x !=
11350 next_image->next->chromaticity.blue_primary.x ||
11351 next_image->chromaticity.blue_primary.y !=
11352 next_image->next->chromaticity.blue_primary.y ||
11353 next_image->chromaticity.white_point.x !=
11354 next_image->next->chromaticity.white_point.x ||
11355 next_image->chromaticity.white_point.y !=
11356 next_image->next->chromaticity.white_point.y)
11357 mng_info->equal_chrms=MagickFalse;
11361 next_image=GetNextImageInList(next_image);
11363 if (image_count < 2)
11365 mng_info->equal_backgrounds=MagickFalse;
11366 mng_info->equal_chrms=MagickFalse;
11367 mng_info->equal_gammas=MagickFalse;
11368 mng_info->equal_srgbs=MagickFalse;
11369 mng_info->equal_physs=MagickFalse;
11370 use_global_plte=MagickFalse;
11371 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11372 need_local_plte=MagickTrue;
11374 need_iterations=MagickFalse;
11377 if (mng_info->need_fram == MagickFalse)
11380 Only certain framing rates 100/n are exactly representable without
11381 the FRAM chunk but we'll allow some slop in VLC files
11383 if (final_delay == 0)
11385 if (need_iterations != MagickFalse)
11388 It's probably a GIF with loop; don't run it *too* fast.
11390 if (mng_info->adjoin)
11393 (void) ThrowMagickException(&image->exception,
11394 GetMagickModule(),CoderWarning,
11395 "input has zero delay between all frames; assuming",
11400 mng_info->ticks_per_second=0;
11402 if (final_delay != 0)
11403 mng_info->ticks_per_second=(png_uint_32)
11404 (image->ticks_per_second/final_delay);
11405 if (final_delay > 50)
11406 mng_info->ticks_per_second=2;
11408 if (final_delay > 75)
11409 mng_info->ticks_per_second=1;
11411 if (final_delay > 125)
11412 mng_info->need_fram=MagickTrue;
11414 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11415 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11416 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11417 1UL*image->ticks_per_second))
11418 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11421 if (mng_info->need_fram != MagickFalse)
11422 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11424 If pseudocolor, we should also check to see if all the
11425 palettes are identical and write a global PLTE if they are.
11429 Write the MNG version 1.0 signature and MHDR chunk.
11431 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11432 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11433 PNGType(chunk,mng_MHDR);
11434 LogPNGChunk(logging,mng_MHDR,28L);
11435 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11436 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11437 PNGLong(chunk+12,mng_info->ticks_per_second);
11438 PNGLong(chunk+16,0L); /* layer count=unknown */
11439 PNGLong(chunk+20,0L); /* frame count=unknown */
11440 PNGLong(chunk+24,0L); /* play time=unknown */
11445 if (need_defi || mng_info->need_fram || use_global_plte)
11446 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11449 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11454 if (need_defi || mng_info->need_fram || use_global_plte)
11455 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11458 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11466 if (need_defi || mng_info->need_fram || use_global_plte)
11467 PNGLong(chunk+28,11L); /* simplicity=LC */
11470 PNGLong(chunk+28,9L); /* simplicity=VLC */
11475 if (need_defi || mng_info->need_fram || use_global_plte)
11476 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11479 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11482 (void) WriteBlob(image,32,chunk);
11483 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11484 option=GetImageOption(image_info,"mng:need-cacheoff");
11485 if (option != (const char *) NULL)
11491 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11493 PNGType(chunk,mng_nEED);
11494 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11495 (void) WriteBlobMSBULong(image,(size_t) length);
11496 LogPNGChunk(logging,mng_nEED,(size_t) length);
11498 (void) WriteBlob(image,length,chunk);
11499 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11501 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11502 (GetNextImageInList(image) != (Image *) NULL) &&
11503 (image->iterations != 1))
11506 Write MNG TERM chunk
11508 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11509 PNGType(chunk,mng_TERM);
11510 LogPNGChunk(logging,mng_TERM,10L);
11511 chunk[4]=3; /* repeat animation */
11512 chunk[5]=0; /* show last frame when done */
11513 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11514 final_delay/MagickMax(image->ticks_per_second,1)));
11516 if (image->iterations == 0)
11517 PNGLong(chunk+10,PNG_UINT_31_MAX);
11520 PNGLong(chunk+10,(png_uint_32) image->iterations);
11522 if (logging != MagickFalse)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11526 final_delay/MagickMax(image->ticks_per_second,1)));
11528 if (image->iterations == 0)
11529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11530 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11534 " Image iterations: %.20g",(double) image->iterations);
11536 (void) WriteBlob(image,14,chunk);
11537 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11540 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11542 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11543 mng_info->equal_srgbs)
11546 Write MNG sRGB chunk
11548 (void) WriteBlobMSBULong(image,1L);
11549 PNGType(chunk,mng_sRGB);
11550 LogPNGChunk(logging,mng_sRGB,1L);
11552 if (image->rendering_intent != UndefinedIntent)
11553 chunk[4]=(unsigned char)
11554 Magick_RenderingIntent_to_PNG_RenderingIntent(
11555 (image->rendering_intent));
11558 chunk[4]=(unsigned char)
11559 Magick_RenderingIntent_to_PNG_RenderingIntent(
11560 (PerceptualIntent));
11562 (void) WriteBlob(image,5,chunk);
11563 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11564 mng_info->have_write_global_srgb=MagickTrue;
11569 if (image->gamma && mng_info->equal_gammas)
11572 Write MNG gAMA chunk
11574 (void) WriteBlobMSBULong(image,4L);
11575 PNGType(chunk,mng_gAMA);
11576 LogPNGChunk(logging,mng_gAMA,4L);
11577 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11578 (void) WriteBlob(image,8,chunk);
11579 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11580 mng_info->have_write_global_gama=MagickTrue;
11582 if (mng_info->equal_chrms)
11588 Write MNG cHRM chunk
11590 (void) WriteBlobMSBULong(image,32L);
11591 PNGType(chunk,mng_cHRM);
11592 LogPNGChunk(logging,mng_cHRM,32L);
11593 primary=image->chromaticity.white_point;
11594 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11595 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11596 primary=image->chromaticity.red_primary;
11597 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11598 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11599 primary=image->chromaticity.green_primary;
11600 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11601 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11602 primary=image->chromaticity.blue_primary;
11603 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11604 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11605 (void) WriteBlob(image,36,chunk);
11606 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11607 mng_info->have_write_global_chrm=MagickTrue;
11610 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11613 Write MNG pHYs chunk
11615 (void) WriteBlobMSBULong(image,9L);
11616 PNGType(chunk,mng_pHYs);
11617 LogPNGChunk(logging,mng_pHYs,9L);
11619 if (image->units == PixelsPerInchResolution)
11621 PNGLong(chunk+4,(png_uint_32)
11622 (image->x_resolution*100.0/2.54+0.5));
11624 PNGLong(chunk+8,(png_uint_32)
11625 (image->y_resolution*100.0/2.54+0.5));
11632 if (image->units == PixelsPerCentimeterResolution)
11634 PNGLong(chunk+4,(png_uint_32)
11635 (image->x_resolution*100.0+0.5));
11637 PNGLong(chunk+8,(png_uint_32)
11638 (image->y_resolution*100.0+0.5));
11645 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11646 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11650 (void) WriteBlob(image,13,chunk);
11651 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11654 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11655 or does not cover the entire frame.
11657 if (write_mng && (image->matte || image->page.x > 0 ||
11658 image->page.y > 0 || (image->page.width &&
11659 (image->page.width+image->page.x < mng_info->page.width))
11660 || (image->page.height && (image->page.height+image->page.y
11661 < mng_info->page.height))))
11663 (void) WriteBlobMSBULong(image,6L);
11664 PNGType(chunk,mng_BACK);
11665 LogPNGChunk(logging,mng_BACK,6L);
11666 red=ScaleQuantumToShort(image->background_color.red);
11667 green=ScaleQuantumToShort(image->background_color.green);
11668 blue=ScaleQuantumToShort(image->background_color.blue);
11669 PNGShort(chunk+4,red);
11670 PNGShort(chunk+6,green);
11671 PNGShort(chunk+8,blue);
11672 (void) WriteBlob(image,10,chunk);
11673 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11674 if (mng_info->equal_backgrounds)
11676 (void) WriteBlobMSBULong(image,6L);
11677 PNGType(chunk,mng_bKGD);
11678 LogPNGChunk(logging,mng_bKGD,6L);
11679 (void) WriteBlob(image,10,chunk);
11680 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11684 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11685 if ((need_local_plte == MagickFalse) &&
11686 (image->storage_class == PseudoClass) &&
11687 (all_images_are_gray == MagickFalse))
11693 Write MNG PLTE chunk
11695 data_length=3*image->colors;
11696 (void) WriteBlobMSBULong(image,data_length);
11697 PNGType(chunk,mng_PLTE);
11698 LogPNGChunk(logging,mng_PLTE,data_length);
11700 for (i=0; i < (ssize_t) image->colors; i++)
11702 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11703 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11704 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11707 (void) WriteBlob(image,data_length+4,chunk);
11708 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11709 mng_info->have_write_global_plte=MagickTrue;
11715 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11716 defined(PNG_MNG_FEATURES_SUPPORTED)
11717 mng_info->equal_palettes=MagickFalse;
11721 if (mng_info->adjoin)
11723 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11724 defined(PNG_MNG_FEATURES_SUPPORTED)
11726 If we aren't using a global palette for the entire MNG, check to
11727 see if we can use one for two or more consecutive images.
11729 if (need_local_plte && use_global_plte && !all_images_are_gray)
11731 if (mng_info->IsPalette)
11734 When equal_palettes is true, this image has the same palette
11735 as the previous PseudoClass image
11737 mng_info->have_write_global_plte=mng_info->equal_palettes;
11738 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11739 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11742 Write MNG PLTE chunk
11747 data_length=3*image->colors;
11748 (void) WriteBlobMSBULong(image,data_length);
11749 PNGType(chunk,mng_PLTE);
11750 LogPNGChunk(logging,mng_PLTE,data_length);
11752 for (i=0; i < (ssize_t) image->colors; i++)
11754 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11755 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11756 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11759 (void) WriteBlob(image,data_length+4,chunk);
11760 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11761 (uInt) (data_length+4)));
11762 mng_info->have_write_global_plte=MagickTrue;
11766 mng_info->have_write_global_plte=MagickFalse;
11777 previous_x=mng_info->page.x;
11778 previous_y=mng_info->page.y;
11785 mng_info->page=image->page;
11786 if ((mng_info->page.x != previous_x) ||
11787 (mng_info->page.y != previous_y))
11789 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11790 PNGType(chunk,mng_DEFI);
11791 LogPNGChunk(logging,mng_DEFI,12L);
11792 chunk[4]=0; /* object 0 MSB */
11793 chunk[5]=0; /* object 0 LSB */
11794 chunk[6]=0; /* visible */
11795 chunk[7]=0; /* abstract */
11796 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11797 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11798 (void) WriteBlob(image,16,chunk);
11799 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11804 mng_info->write_mng=write_mng;
11806 if ((int) image->dispose >= 3)
11807 mng_info->framing_mode=3;
11809 if (mng_info->need_fram && mng_info->adjoin &&
11810 ((image->delay != mng_info->delay) ||
11811 (mng_info->framing_mode != mng_info->old_framing_mode)))
11813 if (image->delay == mng_info->delay)
11816 Write a MNG FRAM chunk with the new framing mode.
11818 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11819 PNGType(chunk,mng_FRAM);
11820 LogPNGChunk(logging,mng_FRAM,1L);
11821 chunk[4]=(unsigned char) mng_info->framing_mode;
11822 (void) WriteBlob(image,5,chunk);
11823 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11828 Write a MNG FRAM chunk with the delay.
11830 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11831 PNGType(chunk,mng_FRAM);
11832 LogPNGChunk(logging,mng_FRAM,10L);
11833 chunk[4]=(unsigned char) mng_info->framing_mode;
11834 chunk[5]=0; /* frame name separator (no name) */
11835 chunk[6]=2; /* flag for changing default delay */
11836 chunk[7]=0; /* flag for changing frame timeout */
11837 chunk[8]=0; /* flag for changing frame clipping */
11838 chunk[9]=0; /* flag for changing frame sync_id */
11839 PNGLong(chunk+10,(png_uint_32)
11840 ((mng_info->ticks_per_second*
11841 image->delay)/MagickMax(image->ticks_per_second,1)));
11842 (void) WriteBlob(image,14,chunk);
11843 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11844 mng_info->delay=(png_uint_32) image->delay;
11846 mng_info->old_framing_mode=mng_info->framing_mode;
11849 #if defined(JNG_SUPPORTED)
11850 if (image_info->compression == JPEGCompression)
11855 if (logging != MagickFalse)
11856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11857 " Writing JNG object.");
11858 /* To do: specify the desired alpha compression method. */
11859 write_info=CloneImageInfo(image_info);
11860 write_info->compression=UndefinedCompression;
11861 status=WriteOneJNGImage(mng_info,write_info,image);
11862 write_info=DestroyImageInfo(write_info);
11867 if (logging != MagickFalse)
11868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11869 " Writing PNG object.");
11871 mng_info->need_blob = MagickFalse;
11873 /* We don't want any ancillary chunks written */
11874 mng_info->ping_exclude_bKGD=MagickTrue;
11875 mng_info->ping_exclude_cHRM=MagickTrue;
11876 mng_info->ping_exclude_EXIF=MagickTrue;
11877 mng_info->ping_exclude_gAMA=MagickTrue;
11878 mng_info->ping_exclude_cHRM=MagickTrue;
11879 mng_info->ping_exclude_iCCP=MagickTrue;
11880 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11881 mng_info->ping_exclude_oFFs=MagickTrue;
11882 mng_info->ping_exclude_pHYs=MagickTrue;
11883 mng_info->ping_exclude_sRGB=MagickTrue;
11884 mng_info->ping_exclude_tEXt=MagickTrue;
11885 mng_info->ping_exclude_tRNS=MagickTrue;
11886 mng_info->ping_exclude_vpAg=MagickTrue;
11887 mng_info->ping_exclude_zCCP=MagickTrue;
11888 mng_info->ping_exclude_zTXt=MagickTrue;
11890 status=WriteOnePNGImage(mng_info,image_info,image);
11893 if (status == MagickFalse)
11895 MngInfoFreeStruct(mng_info,&have_mng_structure);
11896 (void) CloseBlob(image);
11897 return(MagickFalse);
11899 (void) CatchImageException(image);
11900 if (GetNextImageInList(image) == (Image *) NULL)
11902 image=SyncNextImageInList(image);
11903 status=SetImageProgress(image,SaveImagesTag,scene++,
11904 GetImageListLength(image));
11906 if (status == MagickFalse)
11909 } while (mng_info->adjoin);
11913 while (GetPreviousImageInList(image) != (Image *) NULL)
11914 image=GetPreviousImageInList(image);
11916 Write the MEND chunk.
11918 (void) WriteBlobMSBULong(image,0x00000000L);
11919 PNGType(chunk,mng_MEND);
11920 LogPNGChunk(logging,mng_MEND,0L);
11921 (void) WriteBlob(image,4,chunk);
11922 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11925 Relinquish resources.
11927 (void) CloseBlob(image);
11928 MngInfoFreeStruct(mng_info,&have_mng_structure);
11930 if (logging != MagickFalse)
11931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11933 return(MagickTrue);
11935 #else /* PNG_LIBPNG_VER > 10011 */
11937 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11940 printf("Your PNG library is too old: You have libpng-%s\n",
11941 PNG_LIBPNG_VER_STRING);
11943 ThrowBinaryException(CoderError,"PNG library is too old",
11944 image_info->filename);
11947 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11949 return(WritePNGImage(image_info,image));
11951 #endif /* PNG_LIBPNG_VER > 10011 */