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 Relinquish resources.
3000 png_destroy_read_struct(&ping,&ping_info,&end_info);
3002 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3003 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3004 UnlockSemaphoreInfo(ping_semaphore);
3007 if (logging != MagickFalse)
3008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3009 " exit ReadOnePNGImage()");
3013 /* end of reading one PNG image */
3016 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3031 magic_number[MaxTextExtent];
3039 assert(image_info != (const ImageInfo *) NULL);
3040 assert(image_info->signature == MagickSignature);
3042 if (image_info->debug != MagickFalse)
3043 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3044 image_info->filename);
3046 assert(exception != (ExceptionInfo *) NULL);
3047 assert(exception->signature == MagickSignature);
3048 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3049 image=AcquireImage(image_info);
3050 mng_info=(MngInfo *) NULL;
3051 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3053 if (status == MagickFalse)
3054 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3057 Verify PNG signature.
3059 count=ReadBlob(image,8,(unsigned char *) magic_number);
3061 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3062 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3065 Allocate a MngInfo structure.
3067 have_mng_structure=MagickFalse;
3068 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3070 if (mng_info == (MngInfo *) NULL)
3071 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3074 Initialize members of the MngInfo structure.
3076 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3077 mng_info->image=image;
3078 have_mng_structure=MagickTrue;
3081 image=ReadOnePNGImage(mng_info,image_info,exception);
3082 MngInfoFreeStruct(mng_info,&have_mng_structure);
3084 if (image == (Image *) NULL)
3086 if (previous != (Image *) NULL)
3088 if (previous->signature != MagickSignature)
3089 ThrowReaderException(CorruptImageError,"CorruptImage");
3091 (void) CloseBlob(previous);
3092 (void) DestroyImageList(previous);
3095 if (logging != MagickFalse)
3096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3097 "exit ReadPNGImage() with error");
3099 return((Image *) NULL);
3102 (void) CloseBlob(image);
3104 if ((image->columns == 0) || (image->rows == 0))
3106 if (logging != MagickFalse)
3107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3108 "exit ReadPNGImage() with error.");
3110 ThrowReaderException(CorruptImageError,"CorruptImage");
3113 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3115 (void) SetImageType(image,PaletteType);
3117 if (image->matte != MagickFalse)
3119 /* To do: Reduce to binary transparency */
3123 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3125 (void) SetImageType(image,TrueColorType);
3126 image->matte=MagickFalse;
3129 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3130 (void) SetImageType(image,TrueColorMatteType);
3132 if (logging != MagickFalse)
3133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3135 (double) image->page.width,(double) image->page.height,
3136 (double) image->page.x,(double) image->page.y);
3138 if (logging != MagickFalse)
3139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3146 #if defined(JNG_SUPPORTED)
3148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3152 % R e a d O n e J N G I m a g e %
3156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3158 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3159 % (minus the 8-byte signature) and returns it. It allocates the memory
3160 % necessary for the new Image structure and returns a pointer to the new
3163 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3165 % The format of the ReadOneJNGImage method is:
3167 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3168 % ExceptionInfo *exception)
3170 % A description of each parameter follows:
3172 % o mng_info: Specifies a pointer to a MngInfo structure.
3174 % o image_info: the image info.
3176 % o exception: return any errors or warnings in this structure.
3179 static Image *ReadOneJNGImage(MngInfo *mng_info,
3180 const ImageInfo *image_info, ExceptionInfo *exception)
3207 jng_image_sample_depth,
3208 jng_image_compression_method,
3209 jng_image_interlace_method,
3210 jng_alpha_sample_depth,
3211 jng_alpha_compression_method,
3212 jng_alpha_filter_method,
3213 jng_alpha_interlace_method;
3215 register const PixelPacket
3222 register PixelPacket
3225 register unsigned char
3236 jng_alpha_compression_method=0;
3237 jng_alpha_sample_depth=8;
3241 alpha_image=(Image *) NULL;
3242 color_image=(Image *) NULL;
3243 alpha_image_info=(ImageInfo *) NULL;
3244 color_image_info=(ImageInfo *) NULL;
3246 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3247 " Enter ReadOneJNGImage()");
3249 image=mng_info->image;
3251 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3254 Allocate next image structure.
3256 if (logging != MagickFalse)
3257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258 " AcquireNextImage()");
3260 AcquireNextImage(image_info,image);
3262 if (GetNextImageInList(image) == (Image *) NULL)
3263 return((Image *) NULL);
3265 image=SyncNextImageInList(image);
3267 mng_info->image=image;
3270 Signature bytes have already been read.
3273 read_JSEP=MagickFalse;
3274 reading_idat=MagickFalse;
3275 skip_to_iend=MagickFalse;
3279 type[MaxTextExtent];
3288 Read a new JNG chunk.
3290 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3291 2*GetBlobSize(image));
3293 if (status == MagickFalse)
3297 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3298 length=ReadBlobMSBLong(image);
3299 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3301 if (logging != MagickFalse)
3302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3303 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3304 type[0],type[1],type[2],type[3],(double) length);
3306 if (length > PNG_UINT_31_MAX || count == 0)
3307 ThrowReaderException(CorruptImageError,"CorruptImage");
3310 chunk=(unsigned char *) NULL;
3314 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3316 if (chunk == (unsigned char *) NULL)
3317 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3319 for (i=0; i < (ssize_t) length; i++)
3320 chunk[i]=(unsigned char) ReadBlobByte(image);
3325 (void) ReadBlobMSBLong(image); /* read crc word */
3330 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3335 if (memcmp(type,mng_JHDR,4) == 0)
3339 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3340 (p[2] << 8) | p[3]);
3341 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3342 (p[6] << 8) | p[7]);
3343 jng_color_type=p[8];
3344 jng_image_sample_depth=p[9];
3345 jng_image_compression_method=p[10];
3346 jng_image_interlace_method=p[11];
3348 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3351 jng_alpha_sample_depth=p[12];
3352 jng_alpha_compression_method=p[13];
3353 jng_alpha_filter_method=p[14];
3354 jng_alpha_interlace_method=p[15];
3356 if (logging != MagickFalse)
3358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3359 " jng_width: %16lu",(unsigned long) jng_width);
3361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3362 " jng_width: %16lu",(unsigned long) jng_height);
3364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3365 " jng_color_type: %16d",jng_color_type);
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 " jng_image_sample_depth: %3d",
3369 jng_image_sample_depth);
3371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372 " jng_image_compression_method:%3d",
3373 jng_image_compression_method);
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " jng_image_interlace_method: %3d",
3377 jng_image_interlace_method);
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380 " jng_alpha_sample_depth: %3d",
3381 jng_alpha_sample_depth);
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " jng_alpha_compression_method:%3d",
3385 jng_alpha_compression_method);
3387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3388 " jng_alpha_filter_method: %3d",
3389 jng_alpha_filter_method);
3391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3392 " jng_alpha_interlace_method: %3d",
3393 jng_alpha_interlace_method);
3398 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3404 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3405 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3406 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3409 o create color_image
3410 o open color_blob, attached to color_image
3411 o if (color type has alpha)
3412 open alpha_blob, attached to alpha_image
3415 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3417 if (color_image_info == (ImageInfo *) NULL)
3418 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3420 GetImageInfo(color_image_info);
3421 color_image=AcquireImage(color_image_info);
3423 if (color_image == (Image *) NULL)
3424 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3426 if (logging != MagickFalse)
3427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3428 " Creating color_blob.");
3430 (void) AcquireUniqueFilename(color_image->filename);
3431 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3434 if (status == MagickFalse)
3435 return((Image *) NULL);
3437 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3439 alpha_image_info=(ImageInfo *)
3440 AcquireMagickMemory(sizeof(ImageInfo));
3442 if (alpha_image_info == (ImageInfo *) NULL)
3443 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3445 GetImageInfo(alpha_image_info);
3446 alpha_image=AcquireImage(alpha_image_info);
3448 if (alpha_image == (Image *) NULL)
3450 alpha_image=DestroyImage(alpha_image);
3451 ThrowReaderException(ResourceLimitError,
3452 "MemoryAllocationFailed");
3455 if (logging != MagickFalse)
3456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3457 " Creating alpha_blob.");
3459 (void) AcquireUniqueFilename(alpha_image->filename);
3460 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3463 if (status == MagickFalse)
3464 return((Image *) NULL);
3466 if (jng_alpha_compression_method == 0)
3471 if (logging != MagickFalse)
3472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3473 " Writing IHDR chunk to alpha_blob.");
3475 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3476 "\211PNG\r\n\032\n");
3478 (void) WriteBlobMSBULong(alpha_image,13L);
3479 PNGType(data,mng_IHDR);
3480 LogPNGChunk(logging,mng_IHDR,13L);
3481 PNGLong(data+4,jng_width);
3482 PNGLong(data+8,jng_height);
3483 data[12]=jng_alpha_sample_depth;
3484 data[13]=0; /* color_type gray */
3485 data[14]=0; /* compression method 0 */
3486 data[15]=0; /* filter_method 0 */
3487 data[16]=0; /* interlace_method 0 */
3488 (void) WriteBlob(alpha_image,17,data);
3489 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3492 reading_idat=MagickTrue;
3495 if (memcmp(type,mng_JDAT,4) == 0)
3497 /* Copy chunk to color_image->blob */
3499 if (logging != MagickFalse)
3500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3501 " Copying JDAT chunk data to color_blob.");
3503 (void) WriteBlob(color_image,length,chunk);
3506 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3511 if (memcmp(type,mng_IDAT,4) == 0)
3516 /* Copy IDAT header and chunk data to alpha_image->blob */
3518 if (image_info->ping == MagickFalse)
3520 if (logging != MagickFalse)
3521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522 " Copying IDAT chunk data to alpha_blob.");
3524 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3525 PNGType(data,mng_IDAT);
3526 LogPNGChunk(logging,mng_IDAT,length);
3527 (void) WriteBlob(alpha_image,4,data);
3528 (void) WriteBlob(alpha_image,length,chunk);
3529 (void) WriteBlobMSBULong(alpha_image,
3530 crc32(crc32(0,data,4),chunk,(uInt) length));
3534 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3539 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3541 /* Copy chunk data to alpha_image->blob */
3543 if (image_info->ping == MagickFalse)
3545 if (logging != MagickFalse)
3546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547 " Copying JDAA chunk data to alpha_blob.");
3549 (void) WriteBlob(alpha_image,length,chunk);
3553 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3558 if (memcmp(type,mng_JSEP,4) == 0)
3560 read_JSEP=MagickTrue;
3563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3568 if (memcmp(type,mng_bKGD,4) == 0)
3572 image->background_color.red=ScaleCharToQuantum(p[1]);
3573 image->background_color.green=image->background_color.red;
3574 image->background_color.blue=image->background_color.red;
3579 image->background_color.red=ScaleCharToQuantum(p[1]);
3580 image->background_color.green=ScaleCharToQuantum(p[3]);
3581 image->background_color.blue=ScaleCharToQuantum(p[5]);
3584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3588 if (memcmp(type,mng_gAMA,4) == 0)
3591 image->gamma=((float) mng_get_long(p))*0.00001;
3593 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3597 if (memcmp(type,mng_cHRM,4) == 0)
3601 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3602 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3603 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3604 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3605 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3606 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3607 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3608 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3611 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3615 if (memcmp(type,mng_sRGB,4) == 0)
3619 image->rendering_intent=
3620 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3621 image->gamma=0.45455f;
3622 image->chromaticity.red_primary.x=0.6400f;
3623 image->chromaticity.red_primary.y=0.3300f;
3624 image->chromaticity.green_primary.x=0.3000f;
3625 image->chromaticity.green_primary.y=0.6000f;
3626 image->chromaticity.blue_primary.x=0.1500f;
3627 image->chromaticity.blue_primary.y=0.0600f;
3628 image->chromaticity.white_point.x=0.3127f;
3629 image->chromaticity.white_point.y=0.3290f;
3632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3636 if (memcmp(type,mng_oFFs,4) == 0)
3640 image->page.x=(ssize_t) mng_get_long(p);
3641 image->page.y=(ssize_t) mng_get_long(&p[4]);
3643 if ((int) p[8] != 0)
3645 image->page.x/=10000;
3646 image->page.y/=10000;
3651 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3656 if (memcmp(type,mng_pHYs,4) == 0)
3660 image->x_resolution=(double) mng_get_long(p);
3661 image->y_resolution=(double) mng_get_long(&p[4]);
3662 if ((int) p[8] == PNG_RESOLUTION_METER)
3664 image->units=PixelsPerCentimeterResolution;
3665 image->x_resolution=image->x_resolution/100.0f;
3666 image->y_resolution=image->y_resolution/100.0f;
3670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3675 if (memcmp(type,mng_iCCP,4) == 0)
3679 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3688 if (memcmp(type,mng_IEND,4))
3698 Finish up reading image data:
3700 o read main image from color_blob.
3704 o if (color_type has alpha)
3705 if alpha_encoding is PNG
3706 read secondary image from alpha_blob via ReadPNG
3707 if alpha_encoding is JPEG
3708 read secondary image from alpha_blob via ReadJPEG
3712 o copy intensity of secondary image into
3713 opacity samples of main image.
3715 o destroy the secondary image.
3718 (void) CloseBlob(color_image);
3720 if (logging != MagickFalse)
3721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3722 " Reading jng_image from color_blob.");
3724 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3725 color_image->filename);
3727 color_image_info->ping=MagickFalse; /* To do: avoid this */
3728 jng_image=ReadImage(color_image_info,exception);
3730 if (jng_image == (Image *) NULL)
3731 return((Image *) NULL);
3733 (void) RelinquishUniqueFileResource(color_image->filename);
3734 color_image=DestroyImage(color_image);
3735 color_image_info=DestroyImageInfo(color_image_info);
3737 if (jng_image == (Image *) NULL)
3738 return((Image *) NULL);
3740 if (logging != MagickFalse)
3741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742 " Copying jng_image pixels to main image.");
3744 image->rows=jng_height;
3745 image->columns=jng_width;
3746 length=image->columns*sizeof(PixelPacket);
3748 for (y=0; y < (ssize_t) image->rows; y++)
3750 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3751 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3752 (void) CopyMagickMemory(q,s,length);
3754 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3758 jng_image=DestroyImage(jng_image);
3760 if (image_info->ping == MagickFalse)
3762 if (jng_color_type >= 12)
3764 if (jng_alpha_compression_method == 0)
3768 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3769 PNGType(data,mng_IEND);
3770 LogPNGChunk(logging,mng_IEND,0L);
3771 (void) WriteBlob(alpha_image,4,data);
3772 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3775 (void) CloseBlob(alpha_image);
3777 if (logging != MagickFalse)
3778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3779 " Reading opacity from alpha_blob.");
3781 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3782 "%s",alpha_image->filename);
3784 jng_image=ReadImage(alpha_image_info,exception);
3786 if (jng_image != (Image *) NULL)
3787 for (y=0; y < (ssize_t) image->rows; y++)
3789 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3791 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3793 if (image->matte != MagickFalse)
3794 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3795 q->opacity=(Quantum) QuantumRange-s->red;
3798 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3800 q->opacity=(Quantum) QuantumRange-s->red;
3801 if (q->opacity != OpaqueOpacity)
3802 image->matte=MagickTrue;
3805 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3808 (void) RelinquishUniqueFileResource(alpha_image->filename);
3809 alpha_image=DestroyImage(alpha_image);
3810 alpha_image_info=DestroyImageInfo(alpha_image_info);
3811 if (jng_image != (Image *) NULL)
3812 jng_image=DestroyImage(jng_image);
3816 /* Read the JNG image. */
3818 if (mng_info->mng_type == 0)
3820 mng_info->mng_width=jng_width;
3821 mng_info->mng_height=jng_height;
3824 if (image->page.width == 0 && image->page.height == 0)
3826 image->page.width=jng_width;
3827 image->page.height=jng_height;
3830 if (image->page.x == 0 && image->page.y == 0)
3832 image->page.x=mng_info->x_off[mng_info->object_id];
3833 image->page.y=mng_info->y_off[mng_info->object_id];
3838 image->page.y=mng_info->y_off[mng_info->object_id];
3841 mng_info->image_found++;
3842 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3843 2*GetBlobSize(image));
3845 if (logging != MagickFalse)
3846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3847 " exit ReadOneJNGImage()");
3853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3857 % R e a d J N G I m a g e %
3861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3863 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3864 % (including the 8-byte signature) and returns it. It allocates the memory
3865 % necessary for the new Image structure and returns a pointer to the new
3868 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3870 % The format of the ReadJNGImage method is:
3872 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3875 % A description of each parameter follows:
3877 % o image_info: the image info.
3879 % o exception: return any errors or warnings in this structure.
3883 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3898 magic_number[MaxTextExtent];
3906 assert(image_info != (const ImageInfo *) NULL);
3907 assert(image_info->signature == MagickSignature);
3908 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3909 assert(exception != (ExceptionInfo *) NULL);
3910 assert(exception->signature == MagickSignature);
3911 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3912 image=AcquireImage(image_info);
3913 mng_info=(MngInfo *) NULL;
3914 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3916 if (status == MagickFalse)
3917 return((Image *) NULL);
3919 if (LocaleCompare(image_info->magick,"JNG") != 0)
3920 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3922 /* Verify JNG signature. */
3924 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3926 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3927 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3929 /* Allocate a MngInfo structure. */
3931 have_mng_structure=MagickFalse;
3932 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3934 if (mng_info == (MngInfo *) NULL)
3935 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3937 /* Initialize members of the MngInfo structure. */
3939 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3940 have_mng_structure=MagickTrue;
3942 mng_info->image=image;
3944 image=ReadOneJNGImage(mng_info,image_info,exception);
3945 MngInfoFreeStruct(mng_info,&have_mng_structure);
3947 if (image == (Image *) NULL)
3949 if (IsImageObject(previous) != MagickFalse)
3951 (void) CloseBlob(previous);
3952 (void) DestroyImageList(previous);
3955 if (logging != MagickFalse)
3956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 "exit ReadJNGImage() with error");
3959 return((Image *) NULL);
3961 (void) CloseBlob(image);
3963 if (image->columns == 0 || image->rows == 0)
3965 if (logging != MagickFalse)
3966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3967 "exit ReadJNGImage() with error");
3969 ThrowReaderException(CorruptImageError,"CorruptImage");
3972 if (logging != MagickFalse)
3973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3979 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3982 page_geometry[MaxTextExtent];
4015 #if defined(MNG_INSERT_LAYERS)
4017 mng_background_color;
4020 register unsigned char
4035 #if defined(MNG_INSERT_LAYERS)
4040 volatile unsigned int
4041 #ifdef MNG_OBJECT_BUFFERS
4042 mng_background_object=0,
4044 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4047 default_frame_timeout,
4049 #if defined(MNG_INSERT_LAYERS)
4055 /* These delays are all measured in image ticks_per_second,
4056 * not in MNG ticks_per_second
4059 default_frame_delay,
4063 #if defined(MNG_INSERT_LAYERS)
4072 previous_fb.bottom=0;
4074 previous_fb.right=0;
4076 default_fb.bottom=0;
4080 /* Open image file. */
4082 assert(image_info != (const ImageInfo *) NULL);
4083 assert(image_info->signature == MagickSignature);
4084 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4085 assert(exception != (ExceptionInfo *) NULL);
4086 assert(exception->signature == MagickSignature);
4087 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4088 image=AcquireImage(image_info);
4089 mng_info=(MngInfo *) NULL;
4090 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4092 if (status == MagickFalse)
4093 return((Image *) NULL);
4095 first_mng_object=MagickFalse;
4097 have_mng_structure=MagickFalse;
4099 /* Allocate a MngInfo structure. */
4101 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4103 if (mng_info == (MngInfo *) NULL)
4104 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4106 /* Initialize members of the MngInfo structure. */
4108 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4109 mng_info->image=image;
4110 have_mng_structure=MagickTrue;
4112 if (LocaleCompare(image_info->magick,"MNG") == 0)
4115 magic_number[MaxTextExtent];
4117 /* Verify MNG signature. */
4118 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4119 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4120 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4122 /* Initialize some nonzero members of the MngInfo structure. */
4123 for (i=0; i < MNG_MAX_OBJECTS; i++)
4125 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4126 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4128 mng_info->exists[0]=MagickTrue;
4131 first_mng_object=MagickTrue;
4133 #if defined(MNG_INSERT_LAYERS)
4134 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4136 default_frame_delay=0;
4137 default_frame_timeout=0;
4140 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4142 skip_to_iend=MagickFalse;
4143 term_chunk_found=MagickFalse;
4144 mng_info->framing_mode=1;
4145 #if defined(MNG_INSERT_LAYERS)
4146 mandatory_back=MagickFalse;
4148 #if defined(MNG_INSERT_LAYERS)
4149 mng_background_color=image->background_color;
4151 default_fb=mng_info->frame;
4152 previous_fb=mng_info->frame;
4156 type[MaxTextExtent];
4158 if (LocaleCompare(image_info->magick,"MNG") == 0)
4167 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4168 length=ReadBlobMSBLong(image);
4169 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4171 if (logging != MagickFalse)
4172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4174 type[0],type[1],type[2],type[3],(double) length);
4176 if (length > PNG_UINT_31_MAX)
4180 ThrowReaderException(CorruptImageError,"CorruptImage");
4183 chunk=(unsigned char *) NULL;
4187 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4189 if (chunk == (unsigned char *) NULL)
4190 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4192 for (i=0; i < (ssize_t) length; i++)
4193 chunk[i]=(unsigned char) ReadBlobByte(image);
4198 (void) ReadBlobMSBLong(image); /* read crc word */
4200 #if !defined(JNG_SUPPORTED)
4201 if (memcmp(type,mng_JHDR,4) == 0)
4203 skip_to_iend=MagickTrue;
4205 if (mng_info->jhdr_warning == 0)
4206 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4207 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4209 mng_info->jhdr_warning++;
4212 if (memcmp(type,mng_DHDR,4) == 0)
4214 skip_to_iend=MagickTrue;
4216 if (mng_info->dhdr_warning == 0)
4217 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4218 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4220 mng_info->dhdr_warning++;
4222 if (memcmp(type,mng_MEND,4) == 0)
4227 if (memcmp(type,mng_IEND,4) == 0)
4228 skip_to_iend=MagickFalse;
4231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4233 if (logging != MagickFalse)
4234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4240 if (memcmp(type,mng_MHDR,4) == 0)
4242 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4243 (p[2] << 8) | p[3]);
4245 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4246 (p[6] << 8) | p[7]);
4248 if (logging != MagickFalse)
4250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4251 " MNG width: %.20g",(double) mng_info->mng_width);
4252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4253 " MNG height: %.20g",(double) mng_info->mng_height);
4257 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4259 if (mng_info->ticks_per_second == 0)
4260 default_frame_delay=0;
4263 default_frame_delay=1UL*image->ticks_per_second/
4264 mng_info->ticks_per_second;
4266 frame_delay=default_frame_delay;
4272 simplicity=(size_t) mng_get_long(p);
4275 mng_type=1; /* Full MNG */
4277 if ((simplicity != 0) && ((simplicity | 11) == 11))
4278 mng_type=2; /* LC */
4280 if ((simplicity != 0) && ((simplicity | 9) == 9))
4281 mng_type=3; /* VLC */
4283 #if defined(MNG_INSERT_LAYERS)
4285 insert_layers=MagickTrue;
4287 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4289 /* Allocate next image structure. */
4290 AcquireNextImage(image_info,image);
4292 if (GetNextImageInList(image) == (Image *) NULL)
4293 return((Image *) NULL);
4295 image=SyncNextImageInList(image);
4296 mng_info->image=image;
4299 if ((mng_info->mng_width > 65535L) ||
4300 (mng_info->mng_height > 65535L))
4301 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4303 (void) FormatMagickString(page_geometry,MaxTextExtent,
4304 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4305 mng_info->mng_height);
4307 mng_info->frame.left=0;
4308 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4309 mng_info->frame.top=0;
4310 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4311 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4313 for (i=0; i < MNG_MAX_OBJECTS; i++)
4314 mng_info->object_clip[i]=mng_info->frame;
4316 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4320 if (memcmp(type,mng_TERM,4) == 0)
4331 final_delay=(png_uint_32) mng_get_long(&p[2]);
4332 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4334 if (mng_iterations == PNG_UINT_31_MAX)
4337 image->iterations=mng_iterations;
4338 term_chunk_found=MagickTrue;
4341 if (logging != MagickFalse)
4343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4344 " repeat=%d",repeat);
4346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4347 " final_delay=%.20g",(double) final_delay);
4349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4350 " image->iterations=%.20g",(double) image->iterations);
4353 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4356 if (memcmp(type,mng_DEFI,4) == 0)
4359 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4360 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4363 object_id=(p[0] << 8) | p[1];
4365 if (mng_type == 2 && object_id != 0)
4366 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4367 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4370 if (object_id > MNG_MAX_OBJECTS)
4373 Instead ofsuing a warning we should allocate a larger
4374 MngInfo structure and continue.
4376 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4377 CoderError,"object id too large","`%s'",image->filename);
4378 object_id=MNG_MAX_OBJECTS;
4381 if (mng_info->exists[object_id])
4382 if (mng_info->frozen[object_id])
4384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4385 (void) ThrowMagickException(&image->exception,
4386 GetMagickModule(),CoderError,
4387 "DEFI cannot redefine a frozen MNG object","`%s'",
4392 mng_info->exists[object_id]=MagickTrue;
4395 mng_info->invisible[object_id]=p[2];
4398 Extract object offset info.
4402 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4403 (p[5] << 16) | (p[6] << 8) | p[7]);
4405 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4406 (p[9] << 16) | (p[10] << 8) | p[11]);
4408 if (logging != MagickFalse)
4410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4411 " x_off[%d]: %.20g",object_id,(double)
4412 mng_info->x_off[object_id]);
4414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4415 " y_off[%d]: %.20g",object_id,(double)
4416 mng_info->y_off[object_id]);
4421 Extract object clipping info.
4424 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4430 if (memcmp(type,mng_bKGD,4) == 0)
4432 mng_info->have_global_bkgd=MagickFalse;
4436 mng_info->mng_global_bkgd.red=
4437 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4439 mng_info->mng_global_bkgd.green=
4440 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4442 mng_info->mng_global_bkgd.blue=
4443 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4445 mng_info->have_global_bkgd=MagickTrue;
4448 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4451 if (memcmp(type,mng_BACK,4) == 0)
4453 #if defined(MNG_INSERT_LAYERS)
4455 mandatory_back=p[6];
4460 if (mandatory_back && length > 5)
4462 mng_background_color.red=
4463 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4465 mng_background_color.green=
4466 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4468 mng_background_color.blue=
4469 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4471 mng_background_color.opacity=OpaqueOpacity;
4474 #ifdef MNG_OBJECT_BUFFERS
4476 mng_background_object=(p[7] << 8) | p[8];
4479 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4483 if (memcmp(type,mng_PLTE,4) == 0)
4485 /* Read global PLTE. */
4487 if (length && (length < 769))
4489 if (mng_info->global_plte == (png_colorp) NULL)
4490 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4491 sizeof(*mng_info->global_plte));
4493 for (i=0; i < (ssize_t) (length/3); i++)
4495 mng_info->global_plte[i].red=p[3*i];
4496 mng_info->global_plte[i].green=p[3*i+1];
4497 mng_info->global_plte[i].blue=p[3*i+2];
4500 mng_info->global_plte_length=(unsigned int) (length/3);
4503 for ( ; i < 256; i++)
4505 mng_info->global_plte[i].red=i;
4506 mng_info->global_plte[i].green=i;
4507 mng_info->global_plte[i].blue=i;
4511 mng_info->global_plte_length=256;
4514 mng_info->global_plte_length=0;
4516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4520 if (memcmp(type,mng_tRNS,4) == 0)
4522 /* read global tRNS */
4525 for (i=0; i < (ssize_t) length; i++)
4526 mng_info->global_trns[i]=p[i];
4529 for ( ; i < 256; i++)
4530 mng_info->global_trns[i]=255;
4532 mng_info->global_trns_length=(unsigned int) length;
4533 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4536 if (memcmp(type,mng_gAMA,4) == 0)
4543 igamma=mng_get_long(p);
4544 mng_info->global_gamma=((float) igamma)*0.00001;
4545 mng_info->have_global_gama=MagickTrue;
4549 mng_info->have_global_gama=MagickFalse;
4551 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4555 if (memcmp(type,mng_cHRM,4) == 0)
4557 /* Read global cHRM */
4561 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4562 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4563 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4564 mng_info->global_chrm.red_primary.y=0.00001*
4565 mng_get_long(&p[12]);
4566 mng_info->global_chrm.green_primary.x=0.00001*
4567 mng_get_long(&p[16]);
4568 mng_info->global_chrm.green_primary.y=0.00001*
4569 mng_get_long(&p[20]);
4570 mng_info->global_chrm.blue_primary.x=0.00001*
4571 mng_get_long(&p[24]);
4572 mng_info->global_chrm.blue_primary.y=0.00001*
4573 mng_get_long(&p[28]);
4574 mng_info->have_global_chrm=MagickTrue;
4577 mng_info->have_global_chrm=MagickFalse;
4579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4583 if (memcmp(type,mng_sRGB,4) == 0)
4590 mng_info->global_srgb_intent=
4591 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4592 mng_info->have_global_srgb=MagickTrue;
4595 mng_info->have_global_srgb=MagickFalse;
4597 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4601 if (memcmp(type,mng_iCCP,4) == 0)
4609 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4614 if (memcmp(type,mng_FRAM,4) == 0)
4617 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4618 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4621 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4622 image->delay=frame_delay;
4624 frame_delay=default_frame_delay;
4625 frame_timeout=default_frame_timeout;
4630 mng_info->framing_mode=p[0];
4632 if (logging != MagickFalse)
4633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4634 " Framing_mode=%d",mng_info->framing_mode);
4638 /* Note the delay and frame clipping boundaries. */
4640 p++; /* framing mode */
4642 while (*p && ((p-chunk) < (ssize_t) length))
4643 p++; /* frame name */
4645 p++; /* frame name terminator */
4647 if ((p-chunk) < (ssize_t) (length-4))
4654 change_delay=(*p++);
4655 change_timeout=(*p++);
4656 change_clipping=(*p++);
4657 p++; /* change_sync */
4661 frame_delay=1UL*image->ticks_per_second*
4664 if (mng_info->ticks_per_second != 0)
4665 frame_delay/=mng_info->ticks_per_second;
4668 frame_delay=PNG_UINT_31_MAX;
4670 if (change_delay == 2)
4671 default_frame_delay=frame_delay;
4675 if (logging != MagickFalse)
4676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4677 " Framing_delay=%.20g",(double) frame_delay);
4682 frame_timeout=1UL*image->ticks_per_second*
4685 if (mng_info->ticks_per_second != 0)
4686 frame_timeout/=mng_info->ticks_per_second;
4689 frame_timeout=PNG_UINT_31_MAX;
4691 if (change_delay == 2)
4692 default_frame_timeout=frame_timeout;
4696 if (logging != MagickFalse)
4697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4698 " Framing_timeout=%.20g",(double) frame_timeout);
4701 if (change_clipping)
4703 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4707 if (logging != MagickFalse)
4708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4709 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4710 (double) fb.left,(double) fb.right,(double) fb.top,
4711 (double) fb.bottom);
4713 if (change_clipping == 2)
4719 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4721 subframe_width=(size_t) (mng_info->clip.right
4722 -mng_info->clip.left);
4724 subframe_height=(size_t) (mng_info->clip.bottom
4725 -mng_info->clip.top);
4727 Insert a background layer behind the frame if framing_mode is 4.
4729 #if defined(MNG_INSERT_LAYERS)
4730 if (logging != MagickFalse)
4731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4732 " subframe_width=%.20g, subframe_height=%.20g",(double)
4733 subframe_width,(double) subframe_height);
4735 if (insert_layers && (mng_info->framing_mode == 4) &&
4736 (subframe_width) && (subframe_height))
4738 /* Allocate next image structure. */
4739 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4741 AcquireNextImage(image_info,image);
4743 if (GetNextImageInList(image) == (Image *) NULL)
4745 image=DestroyImageList(image);
4746 MngInfoFreeStruct(mng_info,&have_mng_structure);
4747 return((Image *) NULL);
4750 image=SyncNextImageInList(image);
4753 mng_info->image=image;
4755 if (term_chunk_found)
4757 image->start_loop=MagickTrue;
4758 image->iterations=mng_iterations;
4759 term_chunk_found=MagickFalse;
4763 image->start_loop=MagickFalse;
4765 image->columns=subframe_width;
4766 image->rows=subframe_height;
4767 image->page.width=subframe_width;
4768 image->page.height=subframe_height;
4769 image->page.x=mng_info->clip.left;
4770 image->page.y=mng_info->clip.top;
4771 image->background_color=mng_background_color;
4772 image->matte=MagickFalse;
4774 (void) SetImageBackgroundColor(image);
4776 if (logging != MagickFalse)
4777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4778 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4779 (double) mng_info->clip.left,(double) mng_info->clip.right,
4780 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4783 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4786 if (memcmp(type,mng_CLIP,4) == 0)
4795 first_object=(p[0] << 8) | p[1];
4796 last_object=(p[2] << 8) | p[3];
4798 for (i=(int) first_object; i <= (int) last_object; i++)
4800 if (mng_info->exists[i] && !mng_info->frozen[i])
4805 box=mng_info->object_clip[i];
4806 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4810 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4813 if (memcmp(type,mng_SAVE,4) == 0)
4815 for (i=1; i < MNG_MAX_OBJECTS; i++)
4816 if (mng_info->exists[i])
4818 mng_info->frozen[i]=MagickTrue;
4819 #ifdef MNG_OBJECT_BUFFERS
4820 if (mng_info->ob[i] != (MngBuffer *) NULL)
4821 mng_info->ob[i]->frozen=MagickTrue;
4826 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4831 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4833 /* Read DISC or SEEK. */
4835 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4837 for (i=1; i < MNG_MAX_OBJECTS; i++)
4838 MngInfoDiscardObject(mng_info,i);
4846 for (j=0; j < (ssize_t) length; j+=2)
4848 i=p[j] << 8 | p[j+1];
4849 MngInfoDiscardObject(mng_info,i);
4854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4859 if (memcmp(type,mng_MOVE,4) == 0)
4867 first_object=(p[0] << 8) | p[1];
4868 last_object=(p[2] << 8) | p[3];
4869 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4871 if (mng_info->exists[i] && !mng_info->frozen[i])
4879 old_pair.a=mng_info->x_off[i];
4880 old_pair.b=mng_info->y_off[i];
4881 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4882 mng_info->x_off[i]=new_pair.a;
4883 mng_info->y_off[i]=new_pair.b;
4887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4891 if (memcmp(type,mng_LOOP,4) == 0)
4893 ssize_t loop_iters=1;
4894 loop_level=chunk[0];
4895 mng_info->loop_active[loop_level]=1; /* mark loop active */
4897 /* Record starting point. */
4898 loop_iters=mng_get_long(&chunk[1]);
4900 if (logging != MagickFalse)
4901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4902 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4903 (double) loop_iters);
4905 if (loop_iters == 0)
4906 skipping_loop=loop_level;
4910 mng_info->loop_jump[loop_level]=TellBlob(image);
4911 mng_info->loop_count[loop_level]=loop_iters;
4914 mng_info->loop_iteration[loop_level]=0;
4915 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4919 if (memcmp(type,mng_ENDL,4) == 0)
4921 loop_level=chunk[0];
4923 if (skipping_loop > 0)
4925 if (skipping_loop == loop_level)
4928 Found end of zero-iteration loop.
4931 mng_info->loop_active[loop_level]=0;
4937 if (mng_info->loop_active[loop_level] == 1)
4939 mng_info->loop_count[loop_level]--;
4940 mng_info->loop_iteration[loop_level]++;
4942 if (logging != MagickFalse)
4943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4944 " ENDL: LOOP level %.20g has %.20g remaining iters ",
4945 (double) loop_level,(double)
4946 mng_info->loop_count[loop_level]);
4948 if (mng_info->loop_count[loop_level] != 0)
4950 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4954 ThrowReaderException(CorruptImageError,
4955 "ImproperImageHeader");
4966 mng_info->loop_active[loop_level]=0;
4968 for (i=0; i < loop_level; i++)
4969 if (mng_info->loop_active[i] == 1)
4970 last_level=(short) i;
4971 loop_level=last_level;
4976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4980 if (memcmp(type,mng_CLON,4) == 0)
4982 if (mng_info->clon_warning == 0)
4983 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4984 CoderError,"CLON is not implemented yet","`%s'",
4987 mng_info->clon_warning++;
4990 if (memcmp(type,mng_MAGN,4) == 0)
5005 magn_first=(p[0] << 8) | p[1];
5011 magn_last=(p[2] << 8) | p[3];
5014 magn_last=magn_first;
5015 #ifndef MNG_OBJECT_BUFFERS
5016 if (magn_first || magn_last)
5017 if (mng_info->magn_warning == 0)
5019 (void) ThrowMagickException(&image->exception,
5020 GetMagickModule(),CoderError,
5021 "MAGN is not implemented yet for nonzero objects",
5022 "`%s'",image->filename);
5024 mng_info->magn_warning++;
5034 magn_mx=(p[5] << 8) | p[6];
5043 magn_my=(p[7] << 8) | p[8];
5052 magn_ml=(p[9] << 8) | p[10];
5061 magn_mr=(p[11] << 8) | p[12];
5070 magn_mt=(p[13] << 8) | p[14];
5079 magn_mb=(p[15] << 8) | p[16];
5091 magn_methy=magn_methx;
5094 if (magn_methx > 5 || magn_methy > 5)
5095 if (mng_info->magn_warning == 0)
5097 (void) ThrowMagickException(&image->exception,
5098 GetMagickModule(),CoderError,
5099 "Unknown MAGN method in MNG datastream","`%s'",
5102 mng_info->magn_warning++;
5104 #ifdef MNG_OBJECT_BUFFERS
5105 /* Magnify existing objects in the range magn_first to magn_last */
5107 if (magn_first == 0 || magn_last == 0)
5109 /* Save the magnification factors for object 0 */
5110 mng_info->magn_mb=magn_mb;
5111 mng_info->magn_ml=magn_ml;
5112 mng_info->magn_mr=magn_mr;
5113 mng_info->magn_mt=magn_mt;
5114 mng_info->magn_mx=magn_mx;
5115 mng_info->magn_my=magn_my;
5116 mng_info->magn_methx=magn_methx;
5117 mng_info->magn_methy=magn_methy;
5121 if (memcmp(type,mng_PAST,4) == 0)
5123 if (mng_info->past_warning == 0)
5124 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5125 CoderError,"PAST is not implemented yet","`%s'",
5128 mng_info->past_warning++;
5131 if (memcmp(type,mng_SHOW,4) == 0)
5133 if (mng_info->show_warning == 0)
5134 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5135 CoderError,"SHOW is not implemented yet","`%s'",
5138 mng_info->show_warning++;
5141 if (memcmp(type,mng_sBIT,4) == 0)
5144 mng_info->have_global_sbit=MagickFalse;
5148 mng_info->global_sbit.gray=p[0];
5149 mng_info->global_sbit.red=p[0];
5150 mng_info->global_sbit.green=p[1];
5151 mng_info->global_sbit.blue=p[2];
5152 mng_info->global_sbit.alpha=p[3];
5153 mng_info->have_global_sbit=MagickTrue;
5156 if (memcmp(type,mng_pHYs,4) == 0)
5160 mng_info->global_x_pixels_per_unit=
5161 (size_t) mng_get_long(p);
5162 mng_info->global_y_pixels_per_unit=
5163 (size_t) mng_get_long(&p[4]);
5164 mng_info->global_phys_unit_type=p[8];
5165 mng_info->have_global_phys=MagickTrue;
5169 mng_info->have_global_phys=MagickFalse;
5171 if (memcmp(type,mng_pHYg,4) == 0)
5173 if (mng_info->phyg_warning == 0)
5174 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5175 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5177 mng_info->phyg_warning++;
5179 if (memcmp(type,mng_BASI,4) == 0)
5181 skip_to_iend=MagickTrue;
5183 if (mng_info->basi_warning == 0)
5184 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5185 CoderError,"BASI is not implemented yet","`%s'",
5188 mng_info->basi_warning++;
5189 #ifdef MNG_BASI_SUPPORTED
5190 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5191 (p[2] << 8) | p[3]);
5192 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5193 (p[6] << 8) | p[7]);
5194 basi_color_type=p[8];
5195 basi_compression_method=p[9];
5196 basi_filter_type=p[10];
5197 basi_interlace_method=p[11];
5199 basi_red=(p[12] << 8) & p[13];
5205 basi_green=(p[14] << 8) & p[15];
5211 basi_blue=(p[16] << 8) & p[17];
5217 basi_alpha=(p[18] << 8) & p[19];
5221 if (basi_sample_depth == 16)
5228 basi_viewable=p[20];
5234 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5238 if (memcmp(type,mng_IHDR,4)
5239 #if defined(JNG_SUPPORTED)
5240 && memcmp(type,mng_JHDR,4)
5244 /* Not an IHDR or JHDR chunk */
5246 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5251 if (logging != MagickFalse)
5252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5253 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5255 mng_info->exists[object_id]=MagickTrue;
5256 mng_info->viewable[object_id]=MagickTrue;
5258 if (mng_info->invisible[object_id])
5260 if (logging != MagickFalse)
5261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5262 " Skipping invisible object");
5264 skip_to_iend=MagickTrue;
5265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5268 #if defined(MNG_INSERT_LAYERS)
5270 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5272 image_width=(size_t) mng_get_long(p);
5273 image_height=(size_t) mng_get_long(&p[4]);
5275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5278 Insert a transparent background layer behind the entire animation
5279 if it is not full screen.
5281 #if defined(MNG_INSERT_LAYERS)
5282 if (insert_layers && mng_type && first_mng_object)
5284 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5285 (image_width < mng_info->mng_width) ||
5286 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5287 (image_height < mng_info->mng_height) ||
5288 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5290 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5293 Allocate next image structure.
5295 AcquireNextImage(image_info,image);
5297 if (GetNextImageInList(image) == (Image *) NULL)
5299 image=DestroyImageList(image);
5300 MngInfoFreeStruct(mng_info,&have_mng_structure);
5301 return((Image *) NULL);
5304 image=SyncNextImageInList(image);
5306 mng_info->image=image;
5308 if (term_chunk_found)
5310 image->start_loop=MagickTrue;
5311 image->iterations=mng_iterations;
5312 term_chunk_found=MagickFalse;
5316 image->start_loop=MagickFalse;
5318 /* Make a background rectangle. */
5321 image->columns=mng_info->mng_width;
5322 image->rows=mng_info->mng_height;
5323 image->page.width=mng_info->mng_width;
5324 image->page.height=mng_info->mng_height;
5327 image->background_color=mng_background_color;
5328 (void) SetImageBackgroundColor(image);
5329 if (logging != MagickFalse)
5330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5331 " Inserted transparent background layer, W=%.20g, H=%.20g",
5332 (double) mng_info->mng_width,(double) mng_info->mng_height);
5336 Insert a background layer behind the upcoming image if
5337 framing_mode is 3, and we haven't already inserted one.
5339 if (insert_layers && (mng_info->framing_mode == 3) &&
5340 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5341 (simplicity & 0x08)))
5343 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5346 Allocate next image structure.
5348 AcquireNextImage(image_info,image);
5350 if (GetNextImageInList(image) == (Image *) NULL)
5352 image=DestroyImageList(image);
5353 MngInfoFreeStruct(mng_info,&have_mng_structure);
5354 return((Image *) NULL);
5357 image=SyncNextImageInList(image);
5360 mng_info->image=image;
5362 if (term_chunk_found)
5364 image->start_loop=MagickTrue;
5365 image->iterations=mng_iterations;
5366 term_chunk_found=MagickFalse;
5370 image->start_loop=MagickFalse;
5373 image->columns=subframe_width;
5374 image->rows=subframe_height;
5375 image->page.width=subframe_width;
5376 image->page.height=subframe_height;
5377 image->page.x=mng_info->clip.left;
5378 image->page.y=mng_info->clip.top;
5379 image->background_color=mng_background_color;
5380 image->matte=MagickFalse;
5381 (void) SetImageBackgroundColor(image);
5383 if (logging != MagickFalse)
5384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5385 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5386 (double) mng_info->clip.left,(double) mng_info->clip.right,
5387 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5389 #endif /* MNG_INSERT_LAYERS */
5390 first_mng_object=MagickFalse;
5392 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5395 Allocate next image structure.
5397 AcquireNextImage(image_info,image);
5399 if (GetNextImageInList(image) == (Image *) NULL)
5401 image=DestroyImageList(image);
5402 MngInfoFreeStruct(mng_info,&have_mng_structure);
5403 return((Image *) NULL);
5406 image=SyncNextImageInList(image);
5408 mng_info->image=image;
5409 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5410 GetBlobSize(image));
5412 if (status == MagickFalse)
5415 if (term_chunk_found)
5417 image->start_loop=MagickTrue;
5418 term_chunk_found=MagickFalse;
5422 image->start_loop=MagickFalse;
5424 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5426 image->delay=frame_delay;
5427 frame_delay=default_frame_delay;
5433 image->page.width=mng_info->mng_width;
5434 image->page.height=mng_info->mng_height;
5435 image->page.x=mng_info->x_off[object_id];
5436 image->page.y=mng_info->y_off[object_id];
5437 image->iterations=mng_iterations;
5440 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5443 if (logging != MagickFalse)
5444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5445 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5448 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5451 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5455 mng_info->image=image;
5456 mng_info->mng_type=mng_type;
5457 mng_info->object_id=object_id;
5459 if (memcmp(type,mng_IHDR,4) == 0)
5460 image=ReadOnePNGImage(mng_info,image_info,exception);
5462 #if defined(JNG_SUPPORTED)
5464 image=ReadOneJNGImage(mng_info,image_info,exception);
5467 if (image == (Image *) NULL)
5469 if (IsImageObject(previous) != MagickFalse)
5471 (void) DestroyImageList(previous);
5472 (void) CloseBlob(previous);
5475 MngInfoFreeStruct(mng_info,&have_mng_structure);
5476 return((Image *) NULL);
5479 if (image->columns == 0 || image->rows == 0)
5481 (void) CloseBlob(image);
5482 image=DestroyImageList(image);
5483 MngInfoFreeStruct(mng_info,&have_mng_structure);
5484 return((Image *) NULL);
5487 mng_info->image=image;
5494 if (mng_info->magn_methx || mng_info->magn_methy)
5500 if (logging != MagickFalse)
5501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5502 " Processing MNG MAGN chunk");
5504 if (mng_info->magn_methx == 1)
5506 magnified_width=mng_info->magn_ml;
5508 if (image->columns > 1)
5509 magnified_width += mng_info->magn_mr;
5511 if (image->columns > 2)
5512 magnified_width += (png_uint_32)
5513 ((image->columns-2)*(mng_info->magn_mx));
5518 magnified_width=(png_uint_32) image->columns;
5520 if (image->columns > 1)
5521 magnified_width += mng_info->magn_ml-1;
5523 if (image->columns > 2)
5524 magnified_width += mng_info->magn_mr-1;
5526 if (image->columns > 3)
5527 magnified_width += (png_uint_32)
5528 ((image->columns-3)*(mng_info->magn_mx-1));
5531 if (mng_info->magn_methy == 1)
5533 magnified_height=mng_info->magn_mt;
5535 if (image->rows > 1)
5536 magnified_height += mng_info->magn_mb;
5538 if (image->rows > 2)
5539 magnified_height += (png_uint_32)
5540 ((image->rows-2)*(mng_info->magn_my));
5545 magnified_height=(png_uint_32) image->rows;
5547 if (image->rows > 1)
5548 magnified_height += mng_info->magn_mt-1;
5550 if (image->rows > 2)
5551 magnified_height += mng_info->magn_mb-1;
5553 if (image->rows > 3)
5554 magnified_height += (png_uint_32)
5555 ((image->rows-3)*(mng_info->magn_my-1));
5558 if (magnified_height > image->rows ||
5559 magnified_width > image->columns)
5574 register PixelPacket
5586 /* Allocate next image structure. */
5588 if (logging != MagickFalse)
5589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5590 " Allocate magnified image");
5592 AcquireNextImage(image_info,image);
5594 if (GetNextImageInList(image) == (Image *) NULL)
5596 image=DestroyImageList(image);
5597 MngInfoFreeStruct(mng_info,&have_mng_structure);
5598 return((Image *) NULL);
5601 large_image=SyncNextImageInList(image);
5603 large_image->columns=magnified_width;
5604 large_image->rows=magnified_height;
5606 magn_methx=mng_info->magn_methx;
5607 magn_methy=mng_info->magn_methy;
5609 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5610 #define QM unsigned short
5611 if (magn_methx != 1 || magn_methy != 1)
5614 Scale pixels to unsigned shorts to prevent
5615 overflow of intermediate values of interpolations
5617 for (y=0; y < (ssize_t) image->rows; y++)
5619 q=GetAuthenticPixels(image,0,y,image->columns,1,
5622 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5624 q->red=ScaleQuantumToShort(q->red);
5625 q->green=ScaleQuantumToShort(q->green);
5626 q->blue=ScaleQuantumToShort(q->blue);
5627 q->opacity=ScaleQuantumToShort(q->opacity);
5631 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5639 if (image->matte != MagickFalse)
5640 (void) SetImageBackgroundColor(large_image);
5644 large_image->background_color.opacity=OpaqueOpacity;
5645 (void) SetImageBackgroundColor(large_image);
5647 if (magn_methx == 4)
5650 if (magn_methx == 5)
5653 if (magn_methy == 4)
5656 if (magn_methy == 5)
5660 /* magnify the rows into the right side of the large image */
5662 if (logging != MagickFalse)
5663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5664 " Magnify the rows to %.20g",(double) large_image->rows);
5665 m=(ssize_t) mng_info->magn_mt;
5667 length=(size_t) image->columns;
5668 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5669 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5671 if ((prev == (PixelPacket *) NULL) ||
5672 (next == (PixelPacket *) NULL))
5674 image=DestroyImageList(image);
5675 MngInfoFreeStruct(mng_info,&have_mng_structure);
5676 ThrowReaderException(ResourceLimitError,
5677 "MemoryAllocationFailed");
5680 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5681 (void) CopyMagickMemory(next,n,length);
5683 for (y=0; y < (ssize_t) image->rows; y++)
5686 m=(ssize_t) mng_info->magn_mt;
5688 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5689 m=(ssize_t) mng_info->magn_mb;
5691 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5692 m=(ssize_t) mng_info->magn_mb;
5694 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5698 m=(ssize_t) mng_info->magn_my;
5704 if (y < (ssize_t) image->rows-1)
5706 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5708 (void) CopyMagickMemory(next,n,length);
5711 for (i=0; i < m; i++, yy++)
5713 register PixelPacket
5716 assert(yy < (ssize_t) large_image->rows);
5719 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5721 q+=(large_image->columns-image->columns);
5723 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5725 /* To do: get color as function of indexes[x] */
5727 if (image->storage_class == PseudoClass)
5732 if (magn_methy <= 1)
5734 *q=(*pixels); /* replicate previous */
5737 else if (magn_methy == 2 || magn_methy == 4)
5745 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5746 -(*pixels).red)+m))/((ssize_t) (m*2))
5748 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5749 -(*pixels).green)+m))/((ssize_t) (m*2))
5751 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5752 -(*pixels).blue)+m))/((ssize_t) (m*2))
5755 if (image->matte != MagickFalse)
5756 (*q).opacity=(QM) (((ssize_t)
5758 -(*pixels).opacity)+m))
5759 /((ssize_t) (m*2))+(*pixels).opacity);
5762 if (magn_methy == 4)
5764 /* Replicate nearest */
5765 if (i <= ((m+1) << 1))
5766 (*q).opacity=(*pixels).opacity+0;
5768 (*q).opacity=(*n).opacity+0;
5772 else /* if (magn_methy == 3 || magn_methy == 5) */
5774 /* Replicate nearest */
5775 if (i <= ((m+1) << 1))
5781 if (magn_methy == 5)
5783 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5784 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5785 +(*pixels).opacity);
5793 if (SyncAuthenticPixels(large_image,exception) == 0)
5799 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5800 next=(PixelPacket *) RelinquishMagickMemory(next);
5802 length=image->columns;
5804 if (logging != MagickFalse)
5805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5806 " Delete original image");
5808 DeleteImageFromList(&image);
5812 mng_info->image=image;
5814 /* magnify the columns */
5815 if (logging != MagickFalse)
5816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5817 " Magnify the columns to %.20g",(double) image->columns);
5819 for (y=0; y < (ssize_t) image->rows; y++)
5821 register PixelPacket
5824 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5825 pixels=q+(image->columns-length);
5828 for (x=(ssize_t) (image->columns-length);
5829 x < (ssize_t) image->columns; x++)
5831 if (x == (ssize_t) (image->columns-length))
5832 m=(ssize_t) mng_info->magn_ml;
5834 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5835 m=(ssize_t) mng_info->magn_mr;
5837 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5838 m=(ssize_t) mng_info->magn_mr;
5840 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5844 m=(ssize_t) mng_info->magn_mx;
5846 for (i=0; i < m; i++)
5848 if (magn_methx <= 1)
5850 /* replicate previous */
5854 else if (magn_methx == 2 || magn_methx == 4)
5862 (*q).red=(QM) ((2*i*((*n).red
5864 /((ssize_t) (m*2))+(*pixels).red);
5865 (*q).green=(QM) ((2*i*((*n).green
5867 +m)/((ssize_t) (m*2))+(*pixels).green);
5868 (*q).blue=(QM) ((2*i*((*n).blue
5870 /((ssize_t) (m*2))+(*pixels).blue);
5871 if (image->matte != MagickFalse)
5872 (*q).opacity=(QM) ((2*i*((*n).opacity
5873 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5874 +(*pixels).opacity);
5877 if (magn_methx == 4)
5879 /* Replicate nearest */
5880 if (i <= ((m+1) << 1))
5881 (*q).opacity=(*pixels).opacity+0;
5883 (*q).opacity=(*n).opacity+0;
5887 else /* if (magn_methx == 3 || magn_methx == 5) */
5889 /* Replicate nearest */
5890 if (i <= ((m+1) << 1))
5896 if (magn_methx == 5)
5899 (*q).opacity=(QM) ((2*i*((*n).opacity
5900 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5901 +(*pixels).opacity);
5910 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5913 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5914 if (magn_methx != 1 || magn_methy != 1)
5917 Rescale pixels to Quantum
5919 for (y=0; y < (ssize_t) image->rows; y++)
5921 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5923 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5925 q->red=ScaleShortToQuantum(q->red);
5926 q->green=ScaleShortToQuantum(q->green);
5927 q->blue=ScaleShortToQuantum(q->blue);
5928 q->opacity=ScaleShortToQuantum(q->opacity);
5932 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5937 if (logging != MagickFalse)
5938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5939 " Finished MAGN processing");
5944 Crop_box is with respect to the upper left corner of the MNG.
5946 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5947 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5948 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5949 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5950 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5951 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5952 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5953 if ((crop_box.left != (mng_info->image_box.left
5954 +mng_info->x_off[object_id])) ||
5955 (crop_box.right != (mng_info->image_box.right
5956 +mng_info->x_off[object_id])) ||
5957 (crop_box.top != (mng_info->image_box.top
5958 +mng_info->y_off[object_id])) ||
5959 (crop_box.bottom != (mng_info->image_box.bottom
5960 +mng_info->y_off[object_id])))
5962 if (logging != MagickFalse)
5963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5964 " Crop the PNG image");
5966 if ((crop_box.left < crop_box.right) &&
5967 (crop_box.top < crop_box.bottom))
5976 Crop_info is with respect to the upper left corner of
5979 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5980 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5981 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5982 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5983 image->page.width=image->columns;
5984 image->page.height=image->rows;
5987 im=CropImage(image,&crop_info,exception);
5989 if (im != (Image *) NULL)
5991 image->columns=im->columns;
5992 image->rows=im->rows;
5993 im=DestroyImage(im);
5994 image->page.width=image->columns;
5995 image->page.height=image->rows;
5996 image->page.x=crop_box.left;
5997 image->page.y=crop_box.top;
6004 No pixels in crop area. The MNG spec still requires
6005 a layer, though, so make a single transparent pixel in
6006 the top left corner.
6011 (void) SetImageBackgroundColor(image);
6012 image->page.width=1;
6013 image->page.height=1;
6018 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6019 image=mng_info->image;
6023 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6024 /* PNG does not handle depths greater than 16 so reduce it even
6027 if (image->depth > 16)
6031 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6032 if (LosslessReduceDepthOK(image) != MagickFalse)
6036 GetImageException(image,exception);
6038 if (image_info->number_scenes != 0)
6040 if (mng_info->scenes_found >
6041 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6045 if (logging != MagickFalse)
6046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6047 " Finished reading image datastream.");
6049 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6051 (void) CloseBlob(image);
6053 if (logging != MagickFalse)
6054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6055 " Finished reading all image datastreams.");
6057 #if defined(MNG_INSERT_LAYERS)
6058 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6059 (mng_info->mng_height))
6062 Insert a background layer if nothing else was found.
6064 if (logging != MagickFalse)
6065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6066 " No images found. Inserting a background layer.");
6068 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6071 Allocate next image structure.
6073 AcquireNextImage(image_info,image);
6074 if (GetNextImageInList(image) == (Image *) NULL)
6076 image=DestroyImageList(image);
6077 MngInfoFreeStruct(mng_info,&have_mng_structure);
6079 if (logging != MagickFalse)
6080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6081 " Allocation failed, returning NULL.");
6083 return((Image *) NULL);
6085 image=SyncNextImageInList(image);
6087 image->columns=mng_info->mng_width;
6088 image->rows=mng_info->mng_height;
6089 image->page.width=mng_info->mng_width;
6090 image->page.height=mng_info->mng_height;
6093 image->background_color=mng_background_color;
6094 image->matte=MagickFalse;
6096 if (image_info->ping == MagickFalse)
6097 (void) SetImageBackgroundColor(image);
6099 mng_info->image_found++;
6102 image->iterations=mng_iterations;
6104 if (mng_iterations == 1)
6105 image->start_loop=MagickTrue;
6107 while (GetPreviousImageInList(image) != (Image *) NULL)
6110 if (image_count > 10*mng_info->image_found)
6112 if (logging != MagickFalse)
6113 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6115 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6116 CoderError,"Linked list is corrupted, beginning of list not found",
6117 "`%s'",image_info->filename);
6119 return((Image *) NULL);
6122 image=GetPreviousImageInList(image);
6124 if (GetNextImageInList(image) == (Image *) NULL)
6126 if (logging != MagickFalse)
6127 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6129 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6130 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6131 image_info->filename);
6135 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6136 GetNextImageInList(image) ==
6139 if (logging != MagickFalse)
6140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6141 " First image null");
6143 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6144 CoderError,"image->next for first image is NULL but shouldn't be.",
6145 "`%s'",image_info->filename);
6148 if (mng_info->image_found == 0)
6150 if (logging != MagickFalse)
6151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6152 " No visible images found.");
6154 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6155 CoderError,"No visible images in file","`%s'",image_info->filename);
6157 if (image != (Image *) NULL)
6158 image=DestroyImageList(image);
6160 MngInfoFreeStruct(mng_info,&have_mng_structure);
6161 return((Image *) NULL);
6164 if (mng_info->ticks_per_second)
6165 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6166 final_delay/mng_info->ticks_per_second;
6169 image->start_loop=MagickTrue;
6171 /* Find final nonzero image delay */
6172 final_image_delay=0;
6174 while (GetNextImageInList(image) != (Image *) NULL)
6177 final_image_delay=image->delay;
6179 image=GetNextImageInList(image);
6182 if (final_delay < final_image_delay)
6183 final_delay=final_image_delay;
6185 image->delay=final_delay;
6187 if (logging != MagickFalse)
6188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6189 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6190 (double) final_delay);
6192 if (logging != MagickFalse)
6198 image=GetFirstImageInList(image);
6200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6201 " Before coalesce:");
6203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6204 " scene 0 delay=%.20g",(double) image->delay);
6206 while (GetNextImageInList(image) != (Image *) NULL)
6208 image=GetNextImageInList(image);
6209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6210 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6214 image=GetFirstImageInList(image);
6215 #ifdef MNG_COALESCE_LAYERS
6225 if (logging != MagickFalse)
6226 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6229 next_image=CoalesceImages(image,&image->exception);
6231 if (next_image == (Image *) NULL)
6232 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6234 image=DestroyImageList(image);
6237 for (next=image; next != (Image *) NULL; next=next_image)
6239 next->page.width=mng_info->mng_width;
6240 next->page.height=mng_info->mng_height;
6243 next->scene=scene++;
6244 next_image=GetNextImageInList(next);
6246 if (next_image == (Image *) NULL)
6249 if (next->delay == 0)
6252 next_image->previous=GetPreviousImageInList(next);
6253 if (GetPreviousImageInList(next) == (Image *) NULL)
6256 next->previous->next=next_image;
6257 next=DestroyImage(next);
6263 while (GetNextImageInList(image) != (Image *) NULL)
6264 image=GetNextImageInList(image);
6266 image->dispose=BackgroundDispose;
6268 if (logging != MagickFalse)
6274 image=GetFirstImageInList(image);
6276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6277 " After coalesce:");
6279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6280 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6281 (double) image->dispose);
6283 while (GetNextImageInList(image) != (Image *) NULL)
6285 image=GetNextImageInList(image);
6287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6288 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6289 (double) image->delay,(double) image->dispose);
6293 image=GetFirstImageInList(image);
6294 MngInfoFreeStruct(mng_info,&have_mng_structure);
6295 have_mng_structure=MagickFalse;
6297 if (logging != MagickFalse)
6298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6300 return(GetFirstImageInList(image));
6302 #else /* PNG_LIBPNG_VER > 10011 */
6303 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6305 printf("Your PNG library is too old: You have libpng-%s\n",
6306 PNG_LIBPNG_VER_STRING);
6308 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6309 "PNG library is too old","`%s'",image_info->filename);
6311 return(Image *) NULL;
6314 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6316 return(ReadPNGImage(image_info,exception));
6318 #endif /* PNG_LIBPNG_VER > 10011 */
6322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6326 % R e g i s t e r P N G I m a g e %
6330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6332 % RegisterPNGImage() adds properties for the PNG image format to
6333 % the list of supported formats. The properties include the image format
6334 % tag, a method to read and/or write the format, whether the format
6335 % supports the saving of more than one frame to the same file or blob,
6336 % whether the format supports native in-memory I/O, and a brief
6337 % description of the format.
6339 % The format of the RegisterPNGImage method is:
6341 % size_t RegisterPNGImage(void)
6344 ModuleExport size_t RegisterPNGImage(void)
6347 version[MaxTextExtent];
6355 "See http://www.libpng.org/ for details about the PNG format."
6360 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6366 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6372 #if defined(PNG_LIBPNG_VER_STRING)
6373 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6374 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6376 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6378 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6379 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6384 entry=SetMagickInfo("MNG");
6385 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6387 #if defined(MAGICKCORE_PNG_DELEGATE)
6388 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6389 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6392 entry->magick=(IsImageFormatHandler *) IsMNG;
6393 entry->description=ConstantString("Multiple-image Network Graphics");
6395 if (*version != '\0')
6396 entry->version=ConstantString(version);
6398 entry->module=ConstantString("PNG");
6399 entry->note=ConstantString(MNGNote);
6400 (void) RegisterMagickInfo(entry);
6402 entry=SetMagickInfo("PNG");
6404 #if defined(MAGICKCORE_PNG_DELEGATE)
6405 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6406 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6409 entry->magick=(IsImageFormatHandler *) IsPNG;
6410 entry->adjoin=MagickFalse;
6411 entry->description=ConstantString("Portable Network Graphics");
6412 entry->module=ConstantString("PNG");
6414 if (*version != '\0')
6415 entry->version=ConstantString(version);
6417 entry->note=ConstantString(PNGNote);
6418 (void) RegisterMagickInfo(entry);
6420 entry=SetMagickInfo("PNG8");
6422 #if defined(MAGICKCORE_PNG_DELEGATE)
6423 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6424 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6427 entry->magick=(IsImageFormatHandler *) IsPNG;
6428 entry->adjoin=MagickFalse;
6429 entry->description=ConstantString(
6430 "8-bit indexed with optional binary transparency");
6431 entry->module=ConstantString("PNG");
6432 (void) RegisterMagickInfo(entry);
6434 entry=SetMagickInfo("PNG24");
6437 #if defined(ZLIB_VERSION)
6438 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6439 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6441 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6443 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6444 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6448 if (*version != '\0')
6449 entry->version=ConstantString(version);
6451 #if defined(MAGICKCORE_PNG_DELEGATE)
6452 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6453 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6456 entry->magick=(IsImageFormatHandler *) IsPNG;
6457 entry->adjoin=MagickFalse;
6458 entry->description=ConstantString("opaque 24-bit RGB");
6459 entry->module=ConstantString("PNG");
6460 (void) RegisterMagickInfo(entry);
6462 entry=SetMagickInfo("PNG32");
6464 #if defined(MAGICKCORE_PNG_DELEGATE)
6465 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6466 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6469 entry->magick=(IsImageFormatHandler *) IsPNG;
6470 entry->adjoin=MagickFalse;
6471 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6472 entry->module=ConstantString("PNG");
6473 (void) RegisterMagickInfo(entry);
6475 entry=SetMagickInfo("JNG");
6477 #if defined(JNG_SUPPORTED)
6478 #if defined(MAGICKCORE_PNG_DELEGATE)
6479 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6480 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6484 entry->magick=(IsImageFormatHandler *) IsJNG;
6485 entry->adjoin=MagickFalse;
6486 entry->description=ConstantString("JPEG Network Graphics");
6487 entry->module=ConstantString("PNG");
6488 entry->note=ConstantString(JNGNote);
6489 (void) RegisterMagickInfo(entry);
6491 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6492 ping_semaphore=AllocateSemaphoreInfo();
6495 return(MagickImageCoderSignature);
6499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6503 % U n r e g i s t e r P N G I m a g e %
6507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6509 % UnregisterPNGImage() removes format registrations made by the
6510 % PNG module from the list of supported formats.
6512 % The format of the UnregisterPNGImage method is:
6514 % UnregisterPNGImage(void)
6517 ModuleExport void UnregisterPNGImage(void)
6519 (void) UnregisterMagickInfo("MNG");
6520 (void) UnregisterMagickInfo("PNG");
6521 (void) UnregisterMagickInfo("PNG8");
6522 (void) UnregisterMagickInfo("PNG24");
6523 (void) UnregisterMagickInfo("PNG32");
6524 (void) UnregisterMagickInfo("JNG");
6526 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6527 if (ping_semaphore != (SemaphoreInfo *) NULL)
6528 DestroySemaphoreInfo(&ping_semaphore);
6532 #if defined(MAGICKCORE_PNG_DELEGATE)
6533 #if PNG_LIBPNG_VER > 10011
6535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6539 % W r i t e M N G I m a g e %
6543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6545 % WriteMNGImage() writes an image in the Portable Network Graphics
6546 % Group's "Multiple-image Network Graphics" encoded image format.
6548 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6550 % The format of the WriteMNGImage method is:
6552 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6554 % A description of each parameter follows.
6556 % o image_info: the image info.
6558 % o image: The image.
6561 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6562 % "To do" under ReadPNGImage):
6564 % Preserve all unknown and not-yet-handled known chunks found in input
6565 % PNG file and copy them into output PNG files according to the PNG
6568 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6570 % Improve selection of color type (use indexed-colour or indexed-colour
6571 % with tRNS when 256 or fewer unique RGBA values are present).
6573 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6574 % This will be complicated if we limit ourselves to generating MNG-LC
6575 % files. For now we ignore disposal method 3 and simply overlay the next
6578 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6579 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6580 % [mostly done 15 June 1999 but still need to take care of tRNS]
6582 % Check for identical sRGB and replace with a global sRGB (and remove
6583 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6584 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6585 % local gAMA/cHRM with local sRGB if appropriate).
6587 % Check for identical sBIT chunks and write global ones.
6589 % Provide option to skip writing the signature tEXt chunks.
6591 % Use signatures to detect identical objects and reuse the first
6592 % instance of such objects instead of writing duplicate objects.
6594 % Use a smaller-than-32k value of compression window size when
6597 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6598 % ancillary text chunks and save profiles.
6600 % Provide an option to force LC files (to ensure exact framing rate)
6603 % Provide an option to force VLC files instead of LC, even when offsets
6604 % are present. This will involve expanding the embedded images with a
6605 % transparent region at the top and/or left.
6609 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6610 png_info *ping_info, unsigned char *profile_type, unsigned char
6611 *profile_description, unsigned char *profile_data, png_uint_32 length)
6630 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6632 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6635 if (image_info->verbose)
6637 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6638 (char *) profile_type, (double) length);
6641 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6642 description_length=(png_uint_32) strlen((const char *) profile_description);
6643 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6644 + description_length);
6645 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6646 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6647 text[0].key[0]='\0';
6648 (void) ConcatenateMagickString(text[0].key,
6649 "Raw profile type ",MaxTextExtent);
6650 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6654 (void) CopyMagickString(dp,(const char *) profile_description,
6656 dp+=description_length;
6658 (void) FormatMagickString(dp,allocated_length-
6659 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6662 for (i=0; i < (ssize_t) length; i++)
6666 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6667 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6672 text[0].text_length=(png_size_t) (dp-text[0].text);
6673 text[0].compression=image_info->compression == NoCompression ||
6674 (image_info->compression == UndefinedCompression &&
6675 text[0].text_length < 128) ? -1 : 0;
6677 if (text[0].text_length <= allocated_length)
6678 png_set_text(ping,ping_info,text,1);
6680 png_free(ping,text[0].text);
6681 png_free(ping,text[0].key);
6682 png_free(ping,text);
6685 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6686 const char *string, MagickBooleanType logging)
6699 ResetImageProfileIterator(image);
6701 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6703 profile=GetImageProfile(image,name);
6705 if (profile != (const StringInfo *) NULL)
6710 if (LocaleNCompare(name,string,11) == 0)
6712 if (logging != MagickFalse)
6713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6714 " Found %s profile",name);
6716 ping_profile=CloneStringInfo(profile);
6717 data=GetStringInfoDatum(ping_profile),
6718 length=(png_uint_32) GetStringInfoLength(ping_profile);
6723 (void) WriteBlobMSBULong(image,length-5); /* data length */
6724 (void) WriteBlob(image,length-1,data+1);
6725 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6726 ping_profile=DestroyStringInfo(ping_profile);
6730 name=GetNextImageProfile(image);
6737 /* Write one PNG image */
6738 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6739 const ImageInfo *IMimage_info,Image *IMimage)
6763 ping_trans_alpha[256];
6791 ping_have_cheap_transparency,
6801 /* ping_exclude_EXIF, */
6804 /* ping_exclude_iTXt, */
6809 /* ping_exclude_tRNS, */
6811 ping_exclude_zCCP, /* hex-encoded iCCP */
6814 ping_need_colortype_warning,
6834 ping_interlace_method,
6835 ping_compression_method,
6852 number_semitransparent,
6854 ping_pHYs_unit_type;
6857 ping_pHYs_x_resolution,
6858 ping_pHYs_y_resolution;
6860 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6861 " Enter WriteOnePNGImage()");
6863 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6864 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6866 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6867 LockSemaphoreInfo(ping_semaphore);
6870 /* Initialize some stuff */
6873 ping_interlace_method=0,
6874 ping_compression_method=0,
6875 ping_filter_method=0,
6878 ping_background.red = 0;
6879 ping_background.green = 0;
6880 ping_background.blue = 0;
6881 ping_background.gray = 0;
6882 ping_background.index = 0;
6884 ping_trans_color.red=0;
6885 ping_trans_color.green=0;
6886 ping_trans_color.blue=0;
6887 ping_trans_color.gray=0;
6889 ping_pHYs_unit_type = 0;
6890 ping_pHYs_x_resolution = 0;
6891 ping_pHYs_y_resolution = 0;
6893 ping_have_blob=MagickFalse;
6894 ping_have_color=MagickTrue;
6895 ping_have_non_bw=MagickTrue;
6896 ping_have_PLTE=MagickFalse;
6897 ping_have_bKGD=MagickFalse;
6898 ping_have_pHYs=MagickFalse;
6899 ping_have_tRNS=MagickFalse;
6901 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6902 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6903 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6904 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6905 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6906 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6907 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6908 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6909 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6910 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6911 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6912 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6913 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6914 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6915 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6917 ping_need_colortype_warning = MagickFalse;
6920 number_semitransparent = 0;
6921 number_transparent = 0;
6923 if (logging != MagickFalse)
6925 if (image->storage_class == UndefinedClass)
6926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927 " storage_class=UndefinedClass");
6928 if (image->storage_class == DirectClass)
6929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6930 " storage_class=DirectClass");
6931 if (image->storage_class == PseudoClass)
6932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6933 " storage_class=PseudoClass");
6936 if (image->colorspace != RGBColorspace)
6937 (void) TransformImageColorspace(image,RGBColorspace);
6940 Sometimes we get PseudoClass images whose RGB values don't match
6941 the colors in the colormap. This code syncs the RGB values.
6943 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6944 (void) SyncImage(image);
6946 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6947 if (image->depth > 8)
6949 if (logging != MagickFalse)
6950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6951 " Reducing PNG bit depth to 8 since this is a Q8 build.");
6958 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6959 /* PNG does not handle depths greater than 16 so reduce it even
6962 if (image->depth > 16)
6966 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6967 if (image->depth == 16 && mng_info->write_png_depth != 16)
6968 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6972 /* Normally we run this just once, but in the case of writing PNG8
6973 * we reduce the transparency to binary and run again, then reduce
6974 * the colors to a simple 3-3-3 palette and run once more, and finally
6975 * to a simple 3-3-2 palette.
6978 tried_333 = MagickFalse;
6979 tried_444 = MagickFalse;
6985 * Sometimes we get DirectClass images that have 256 colors or fewer.
6986 * This code will build a colormap.
6988 * Also, sometimes we get PseudoClass images with an out-of-date
6989 * colormap. This code will replace the colormap with a new one.
6990 * Sometimes we get PseudoClass images that have more than 256 colors.
6991 * This code will delete the colormap and change the image to
6994 * If image->matte is MagickFalse, we ignore the opacity channel
6995 * even though it sometimes contains left-over non-opaque values.
6997 * Also we gather some information (number of opaque, transparent,
6998 * and semitransparent pixels, and whether the image has any non-gray
6999 * pixels or only black-and-white pixels) that we might need later.
7001 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7002 * we need to check for bogus non-opaque values, at least.
7005 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7006 # define PNGK 0 /* Shift */
7007 # define PNGM 1 /* Scale */
7008 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7010 # define PNGM 0x0101
7013 # define PNGM 0x01010101
7024 semitransparent[260],
7027 register IndexPacket
7030 register const PixelPacket
7034 register PixelPacket
7037 if (logging != MagickFalse)
7038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7039 " Enter BUILD_PALETTE:");
7041 if (logging != MagickFalse)
7043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7044 " image->columns=%.20g",(double) image->columns);
7045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7046 " image->rows=%.20g",(double) image->rows);
7047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048 " image->matte=%.20g",(double) image->matte);
7049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7050 " image->depth=%.20g",(double) image->depth);
7052 if (image->storage_class == PseudoClass && image->colormap != NULL)
7054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7055 " Original colormap:");
7056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7057 " i (red,green,blue,opacity)");
7059 for (i=0; i < 256; i++)
7061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062 " %d (%d,%d,%d,%d)",
7064 (int) image->colormap[i].red,
7065 (int) image->colormap[i].green,
7066 (int) image->colormap[i].blue,
7067 (int) image->colormap[i].opacity);
7070 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7075 " %d (%d,%d,%d,%d)",
7077 (int) image->colormap[i].red,
7078 (int) image->colormap[i].green,
7079 (int) image->colormap[i].blue,
7080 (int) image->colormap[i].opacity);
7085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7086 " image->colors=%d",(int) image->colors);
7088 if (image->colors == 0)
7089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7090 " (zero means unknown)");
7092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7093 " Regenerate the colormap");
7096 exception=(&image->exception);
7100 number_semitransparent = 0;
7101 number_transparent = 0;
7103 for (y=0; y < (ssize_t) image->rows; y++)
7105 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7107 if (q == (PixelPacket *) NULL)
7110 for (x=0; x < (ssize_t) image->columns; x++)
7112 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7114 if (number_opaque < 259)
7116 if (number_opaque == 0)
7119 opaque[0].opacity=OpaqueOpacity;
7123 for (i=0; i< (ssize_t) number_opaque; i++)
7125 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7129 if (i == (ssize_t) number_opaque &&
7130 number_opaque < 259)
7134 opaque[i].opacity = OpaqueOpacity;
7138 else if (q->opacity == TransparentOpacity)
7140 if (number_transparent < 259)
7142 if (number_transparent == 0)
7145 ping_trans_color.red=(unsigned short)(q->red);
7146 ping_trans_color.green=(unsigned short) (q->green);
7147 ping_trans_color.blue=(unsigned short) (q->blue);
7148 ping_trans_color.gray=(unsigned short) (q->blue);
7149 number_transparent = 1;
7152 for (i=0; i< (ssize_t) number_transparent; i++)
7154 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7158 if (i == (ssize_t) number_transparent &&
7159 number_transparent < 259)
7161 number_transparent++;
7162 transparent[i] = *q;
7168 if (number_semitransparent < 259)
7170 if (number_semitransparent == 0)
7172 semitransparent[0]=*q;
7173 number_semitransparent = 1;
7176 for (i=0; i< (ssize_t) number_semitransparent; i++)
7178 if (IsColorEqual(semitransparent+i,
7179 (PixelPacket *) q) &&
7180 q->opacity == semitransparent[i].opacity)
7184 if (i == (ssize_t) number_semitransparent &&
7185 number_semitransparent < 259)
7187 number_semitransparent++;
7188 semitransparent[i] = *q;
7196 if (ping_exclude_bKGD == MagickFalse)
7198 /* Add the background color to the palette, if it
7199 * isn't already there.
7201 for (i=0; i<number_opaque; i++)
7203 if (IsColorEqual(opaque+i, &image->background_color))
7207 if (number_opaque < 259 && i == number_opaque)
7209 opaque[i]=image->background_color;
7210 opaque[i].opacity = OpaqueOpacity;
7213 else if (logging != MagickFalse)
7214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7215 " No room in the colormap to add background color");
7218 image_colors=number_opaque+number_transparent+number_semitransparent;
7220 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7222 /* No room for the background color; remove it. */
7227 if (logging != MagickFalse)
7229 if (image_colors > 256)
7230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231 " image has more than 256 colors");
7234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7235 " image has %d colors",image_colors);
7238 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7240 ping_have_color=MagickFalse;
7241 ping_have_non_bw=MagickFalse;
7243 if(image_colors > 256)
7245 for (y=0; y < (ssize_t) image->rows; y++)
7247 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7249 if (q == (PixelPacket *) NULL)
7252 /* Worst case is black-and-white; we are looking at every
7256 if (ping_have_color == MagickFalse)
7259 for (x=0; x < (ssize_t) image->columns; x++)
7261 if (s->red != s->green || s->red != s->blue)
7263 ping_have_color=MagickTrue;
7264 ping_have_non_bw=MagickTrue;
7271 if (ping_have_non_bw == MagickFalse)
7274 for (x=0; x < (ssize_t) image->columns; x++)
7276 if (s->red != 0 && s->red != QuantumRange)
7278 ping_have_non_bw=MagickTrue;
7287 if (image_colors < 257)
7293 * Initialize image colormap.
7296 if (logging != MagickFalse)
7297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7298 " Sort the new colormap");
7300 /* Sort palette, transparent first */;
7304 for (i=0; i<number_transparent; i++)
7305 colormap[n++] = transparent[i];
7307 for (i=0; i<number_semitransparent; i++)
7308 colormap[n++] = semitransparent[i];
7310 for (i=0; i<number_opaque; i++)
7311 colormap[n++] = opaque[i];
7314 /* image_colors < 257; search the colormap instead of the pixels
7315 * to get ping_have_color and ping_have_non_bw
7319 if (ping_have_color == MagickFalse)
7321 if (colormap[i].red != colormap[i].green ||
7322 colormap[i].red != colormap[i].blue)
7324 ping_have_color=MagickTrue;
7325 ping_have_non_bw=MagickTrue;
7330 if (ping_have_non_bw == MagickFalse)
7332 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7333 ping_have_non_bw=MagickTrue;
7337 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7338 (number_transparent == 0 && number_semitransparent == 0)) &&
7339 (((mng_info->write_png_colortype-1) ==
7340 PNG_COLOR_TYPE_PALETTE) ||
7341 (mng_info->write_png_colortype == 0)))
7343 if (logging != MagickFalse)
7345 if (n != (ssize_t) image_colors)
7346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7347 " image_colors (%d) and n (%d) don't match",
7350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7351 " AcquireImageColormap");
7354 image->colors = image_colors;
7356 if (AcquireImageColormap(image,image_colors) ==
7358 ThrowWriterException(ResourceLimitError,
7359 "MemoryAllocationFailed");
7361 for (i=0; i< (ssize_t) image_colors; i++)
7362 image->colormap[i] = colormap[i];
7364 if (logging != MagickFalse)
7366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7367 " image->colors=%d (%d)",
7368 (int) image->colors, image_colors);
7370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7371 " Update the pixel indexes");
7374 /* Sync the pixel indices with the new colormap */
7376 for (y=0; y < (ssize_t) image->rows; y++)
7378 q=GetAuthenticPixels(image,0,y,image->columns,1,
7381 if (q == (PixelPacket *) NULL)
7384 indexes=GetAuthenticIndexQueue(image);
7386 for (x=0; x < (ssize_t) image->columns; x++)
7388 for (i=0; i< (ssize_t) image_colors; i++)
7390 if ((image->matte == MagickFalse ||
7391 image->colormap[i].opacity == q->opacity) &&
7392 (IsColorEqual(&image->colormap[i],
7393 (PixelPacket *) q)))
7395 indexes[x]=(IndexPacket) i;
7402 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7408 if (logging != MagickFalse)
7410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7411 " image->colors=%d", (int) image->colors);
7413 if (image->colormap != NULL)
7415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7416 " i (red,green,blue,opacity)");
7418 for (i=0; i < (ssize_t) image->colors; i++)
7420 if (i < 300 || i >= image->colors - 10)
7422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7423 " %d (%d,%d,%d,%d)",
7425 (int) image->colormap[i].red,
7426 (int) image->colormap[i].green,
7427 (int) image->colormap[i].blue,
7428 (int) image->colormap[i].opacity);
7433 if (number_transparent < 257)
7434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435 " number_transparent = %d",
7436 number_transparent);
7439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7440 " number_transparent > 256");
7442 if (number_opaque < 257)
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444 " number_opaque = %d",
7448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449 " number_opaque > 256");
7451 if (number_semitransparent < 257)
7452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453 " number_semitransparent = %d",
7454 number_semitransparent);
7457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7458 " number_semitransparent > 256");
7460 if (ping_have_non_bw == MagickFalse)
7461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7462 " All pixels and the background are black or white");
7464 else if (ping_have_color == MagickFalse)
7465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7466 " All pixels and the background are gray");
7469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7470 " At least one pixel or the background is non-gray");
7472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473 " Exit BUILD_PALETTE:");
7476 if (mng_info->write_png8 == MagickFalse)
7479 /* Make any reductions necessary for the PNG8 format */
7480 if (image_colors <= 256 &&
7481 image_colors != 0 && image->colormap != NULL &&
7482 number_semitransparent == 0 &&
7483 number_transparent <= 1)
7486 /* PNG8 can't have semitransparent colors so we threshold the
7487 * opacity to 0 or OpaqueOpacity
7489 if (number_semitransparent != 0)
7491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7492 " Thresholding the alpha channel to binary");
7494 for (y=0; y < (ssize_t) image->rows; y++)
7496 r=GetAuthenticPixels(image,0,y,image->columns,1,
7499 if (r == (PixelPacket *) NULL)
7502 for (x=0; x < (ssize_t) image->columns; x++)
7504 r->opacity = r->opacity > TransparentOpacity/2 ?
7505 TransparentOpacity : OpaqueOpacity;
7509 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7512 if (image_colors != 0 && image_colors <= 256 &&
7513 image->colormap != NULL)
7514 for (i=0; i<image_colors; i++)
7515 image->colormap[i].opacity =
7516 image->colormap[i].opacity > TransparentOpacity/2 ?
7517 TransparentOpacity : OpaqueOpacity;
7522 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7523 * background color to the 4-4-4, 3-3-3 or 3-3-2 palette. If the image is
7524 * mostly gray, the 4-4-4 palette should end up with 256 colors or less.
7526 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7528 if (logging != MagickFalse)
7529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7530 " Quantizing the background color to 4-4-4");
7532 tried_444 = MagickTrue;
7534 image->background_color.red=
7536 image->background_color.red) >> PNGK) & 0xf0) ) |
7538 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7539 image->background_color.green=
7541 image->background_color.green) >> PNGK) & 0xf0) ) |
7543 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7544 image->background_color.blue=
7546 image->background_color.blue) >> PNGK) & 0xf0) ) |
7548 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7550 if (logging != MagickFalse)
7551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552 " Quantizing the pixel colors to 4-4-4");
7554 if (image->colormap == NULL)
7556 for (y=0; y < (ssize_t) image->rows; y++)
7558 r=GetAuthenticPixels(image,0,y,image->columns,1,
7561 if (r == (PixelPacket *) NULL)
7564 for (x=0; x < (ssize_t) image->columns; x++)
7566 if (r->opacity == TransparentOpacity)
7568 r->red = image->background_color.red;
7569 r->green = image->background_color.green;
7570 r->blue = image->background_color.blue;
7575 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7576 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7578 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7579 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7581 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7582 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7587 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7592 else /* Should not reach this; colormap already exists and
7595 if (logging != MagickFalse)
7596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7597 " Quantizing the colormap to 4-4-4");
7598 for (i=0; i<image_colors; i++)
7600 image->colormap[i].red=
7602 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7604 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7605 image->colormap[i].green=
7607 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7609 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7610 image->colormap[i].blue=
7612 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7614 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7620 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7622 if (logging != MagickFalse)
7623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7624 " Quantizing the background color to 3-3-3");
7626 tried_333 = MagickTrue;
7628 image->background_color.red=
7630 image->background_color.red) >> PNGK) & 0xe0) ) |
7632 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7634 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7635 image->background_color.green=
7637 image->background_color.green) >> PNGK) & 0xe0) ) |
7639 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7641 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7642 image->background_color.blue=
7644 image->background_color.blue) >> PNGK) & 0xe0) ) |
7646 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7648 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7650 if (logging != MagickFalse)
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " Quantizing the pixel colors to 3-3-3");
7654 if (image->colormap == NULL)
7656 for (y=0; y < (ssize_t) image->rows; y++)
7658 r=GetAuthenticPixels(image,0,y,image->columns,1,
7661 if (r == (PixelPacket *) NULL)
7664 for (x=0; x < (ssize_t) image->columns; x++)
7666 if (r->opacity == TransparentOpacity)
7668 r->red = image->background_color.red;
7669 r->green = image->background_color.green;
7670 r->blue = image->background_color.blue;
7675 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7676 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7677 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7679 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7680 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7681 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7683 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7684 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7685 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7690 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7695 else /* Should not reach this; colormap already exists and
7698 if (logging != MagickFalse)
7699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7700 " Quantizing the colormap to 3-3-3");
7701 for (i=0; i<image_colors; i++)
7703 image->colormap[i].red=
7705 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7707 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7709 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7710 image->colormap[i].green=
7712 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7714 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7716 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7717 image->colormap[i].blue=
7719 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7721 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7723 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7729 if (image_colors == 0 || image_colors > 256)
7731 if (logging != MagickFalse)
7732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7733 " Quantizing the background color to 3-3-2");
7735 image->background_color.red=
7737 image->background_color.red) >> PNGK) & 0xe0) ) |
7739 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7741 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7742 image->background_color.green=
7744 image->background_color.green) >> PNGK) & 0xe0) ) |
7746 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7748 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7749 image->background_color.blue=
7751 image->background_color.blue) >> PNGK) & 0xc0) ) |
7753 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7755 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7757 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7759 if (logging != MagickFalse)
7760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7761 " Quantizing the pixel colors to 3-3-2");
7763 if (image->colormap == NULL)
7765 for (y=0; y < (ssize_t) image->rows; y++)
7767 r=GetAuthenticPixels(image,0,y,image->columns,1,
7770 if (r == (PixelPacket *) NULL)
7773 for (x=0; x < (ssize_t) image->columns; x++)
7775 if (r->opacity == TransparentOpacity)
7777 r->red = image->background_color.red;
7778 r->green = image->background_color.green;
7779 r->blue = image->background_color.blue;
7784 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7785 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7786 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7788 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7789 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7790 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7792 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7793 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7794 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7795 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7800 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7805 else /* Should not reach this; colormap already exists and
7808 if (logging != MagickFalse)
7809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7810 " Quantizing the colormap to 3-3-2");
7811 for (i=0; i<image_colors; i++)
7813 image->colormap[i].red=
7815 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7817 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7819 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7820 image->colormap[i].green=
7822 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7824 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7826 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7827 image->colormap[i].blue=
7829 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
7831 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
7833 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
7835 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7842 /* END OF BUILD_PALETTE */
7844 /* If we are excluding the tRNS chunk and there is transparency,
7845 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7848 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7849 (number_transparent != 0 || number_semitransparent != 0))
7851 int colortype=mng_info->write_png_colortype;
7853 if (ping_have_color == MagickFalse)
7854 mng_info->write_png_colortype = 5;
7857 mng_info->write_png_colortype = 7;
7859 if (colortype != 0 &&
7860 mng_info->write_png_colortype != (ssize_t) colortype)
7861 ping_need_colortype_warning=MagickTrue;
7865 /* See if cheap transparency is possible. It is only possible
7866 * when there is a single transparent color, no semitransparent
7867 * color, and no opaque color that has the same RGB components
7868 * as the transparent color. We only need this information if
7869 * we are writing a PNG with colortype 0 or 2, and we have not
7870 * excluded the tRNS chunk.
7872 if (number_transparent == 1 &&
7873 mng_info->write_png_colortype < 4)
7875 ping_have_cheap_transparency = MagickTrue;
7877 if (number_semitransparent != 0)
7878 ping_have_cheap_transparency = MagickFalse;
7880 else if (image_colors == 0 || image_colors > 256 ||
7881 image->colormap == NULL)
7886 register const PixelPacket
7889 exception=(&image->exception);
7891 for (y=0; y < (ssize_t) image->rows; y++)
7893 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7895 if (q == (PixelPacket *) NULL)
7898 for (x=0; x < (ssize_t) image->columns; x++)
7900 if (q->opacity != TransparentOpacity &&
7901 (unsigned short) q->red == ping_trans_color.red &&
7902 (unsigned short) q->green == ping_trans_color.green &&
7903 (unsigned short) q->blue == ping_trans_color.blue)
7905 ping_have_cheap_transparency = MagickFalse;
7912 if (ping_have_cheap_transparency == MagickFalse)
7918 /* Assuming that image->colormap[0] is the one transparent color
7919 * and that all others are opaque.
7921 if (image_colors > 1)
7922 for (i=1; i<image_colors; i++)
7923 if (image->colormap[i].red == image->colormap[0].red &&
7924 image->colormap[i].green == image->colormap[0].green &&
7925 image->colormap[i].blue == image->colormap[0].blue)
7927 ping_have_cheap_transparency = MagickFalse;
7932 if (logging != MagickFalse)
7934 if (ping_have_cheap_transparency == MagickFalse)
7935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936 " Cheap transparency is not possible.");
7939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7940 " Cheap transparency is possible.");
7944 ping_have_cheap_transparency = MagickFalse;
7946 image_depth=image->depth;
7948 quantum_info = (QuantumInfo *) NULL;
7950 image_colors=(int) image->colors;
7951 image_matte=image->matte;
7953 mng_info->IsPalette=image->storage_class == PseudoClass &&
7954 image_colors <= 256 && image->colormap != NULL;
7956 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7957 (image->colors == 0 || image->colormap == NULL))
7959 image_info=DestroyImageInfo(image_info);
7960 image=DestroyImage(image);
7961 (void) ThrowMagickException(&IMimage->exception,
7962 GetMagickModule(),CoderError,
7963 "Cannot write PNG8 or color-type 3; colormap is NULL",
7964 "`%s'",IMimage->filename);
7965 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7966 UnlockSemaphoreInfo(ping_semaphore);
7968 return(MagickFalse);
7972 Allocate the PNG structures
7974 #ifdef PNG_USER_MEM_SUPPORTED
7975 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7976 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7977 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7980 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7981 MagickPNGErrorHandler,MagickPNGWarningHandler);
7984 if (ping == (png_struct *) NULL)
7985 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7987 ping_info=png_create_info_struct(ping);
7989 if (ping_info == (png_info *) NULL)
7991 png_destroy_write_struct(&ping,(png_info **) NULL);
7992 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7995 png_set_write_fn(ping,image,png_put_data,png_flush_data);
7996 ping_pixels=(unsigned char *) NULL;
7998 if (setjmp(png_jmpbuf(ping)))
8004 if (image_info->verbose)
8005 (void) printf("PNG write has failed.\n");
8007 png_destroy_write_struct(&ping,&ping_info);
8008 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8009 UnlockSemaphoreInfo(ping_semaphore);
8011 if (ping_have_blob != MagickFalse)
8012 (void) CloseBlob(image);
8013 image_info=DestroyImageInfo(image_info);
8014 image=DestroyImage(image);
8015 return(MagickFalse);
8018 Prepare PNG for writing.
8020 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8021 if (mng_info->write_mng)
8022 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8025 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8026 if (mng_info->write_mng)
8027 png_permit_empty_plte(ping,MagickTrue);
8034 ping_width=(png_uint_32) image->columns;
8035 ping_height=(png_uint_32) image->rows;
8037 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8040 if (mng_info->write_png_depth != 0)
8041 image_depth=mng_info->write_png_depth;
8043 /* Adjust requested depth to next higher valid depth if necessary */
8044 if (image_depth > 8)
8047 if ((image_depth > 4) && (image_depth < 8))
8050 if (image_depth == 3)
8053 if (logging != MagickFalse)
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " width=%.20g",(double) ping_width);
8057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8058 " height=%.20g",(double) ping_height);
8059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8060 " image_matte=%.20g",(double) image->matte);
8061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8062 " image->depth=%.20g",(double) image->depth);
8063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8064 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8067 save_image_depth=image_depth;
8068 ping_bit_depth=(png_byte) save_image_depth;
8071 #if defined(PNG_pHYs_SUPPORTED)
8072 if (ping_exclude_pHYs == MagickFalse)
8074 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8075 (!mng_info->write_mng || !mng_info->equal_physs))
8077 if (logging != MagickFalse)
8078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8079 " Setting up pHYs chunk");
8081 if (image->units == PixelsPerInchResolution)
8083 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8084 ping_pHYs_x_resolution=
8085 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8086 ping_pHYs_y_resolution=
8087 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8090 else if (image->units == PixelsPerCentimeterResolution)
8092 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8093 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8094 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8099 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8100 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8101 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8104 if (logging != MagickFalse)
8105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8106 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8107 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8108 (int) ping_pHYs_unit_type);
8109 ping_have_pHYs = MagickTrue;
8114 if (ping_exclude_bKGD == MagickFalse)
8116 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8122 if (ping_bit_depth == 8)
8125 if (ping_bit_depth == 4)
8128 if (ping_bit_depth == 2)
8131 if (ping_bit_depth == 1)
8134 ping_background.red=(png_uint_16)
8135 (ScaleQuantumToShort(image->background_color.red) & mask);
8137 ping_background.green=(png_uint_16)
8138 (ScaleQuantumToShort(image->background_color.green) & mask);
8140 ping_background.blue=(png_uint_16)
8141 (ScaleQuantumToShort(image->background_color.blue) & mask);
8144 if (logging != MagickFalse)
8146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8147 " Setting up bKGD chunk (1)");
8149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8150 " ping_bit_depth=%d",ping_bit_depth);
8153 ping_have_bKGD = MagickTrue;
8157 Select the color type.
8162 if (mng_info->IsPalette && mng_info->write_png8)
8165 /* To do: make this a function cause it's used twice, except
8166 for reducing the sample depth from 8. */
8168 number_colors=image_colors;
8170 ping_have_tRNS=MagickFalse;
8175 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8177 if (logging != MagickFalse)
8178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8179 " Setting up PLTE chunk with %d colors (%d)",
8180 number_colors, image_colors);
8182 for (i=0; i < (ssize_t) number_colors; i++)
8184 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8185 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8186 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8187 if (logging != MagickFalse)
8188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8189 #if MAGICKCORE_QUANTUM_DEPTH == 8
8190 " %3ld (%3d,%3d,%3d)",
8192 " %5ld (%5d,%5d,%5d)",
8194 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8198 ping_have_PLTE=MagickTrue;
8199 image_depth=ping_bit_depth;
8202 if (matte != MagickFalse)
8205 Identify which colormap entry is transparent.
8207 assert(number_colors <= 256);
8208 assert(image->colormap != NULL);
8210 for (i=0; i < (ssize_t) number_transparent; i++)
8211 ping_trans_alpha[i]=0;
8214 ping_num_trans=(unsigned short) (number_transparent +
8215 number_semitransparent);
8217 if (ping_num_trans == 0)
8218 ping_have_tRNS=MagickFalse;
8221 ping_have_tRNS=MagickTrue;
8224 if (ping_exclude_bKGD == MagickFalse)
8227 * Identify which colormap entry is the background color.
8230 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8231 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8234 ping_background.index=(png_byte) i;
8236 } /* end of write_png8 */
8238 else if (mng_info->write_png24)
8240 image_matte=MagickFalse;
8241 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8244 else if (mng_info->write_png32)
8246 image_matte=MagickTrue;
8247 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8250 else /* mng_info->write_pngNN not specified */
8252 image_depth=ping_bit_depth;
8254 if (mng_info->write_png_colortype != 0)
8256 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8258 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8259 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8260 image_matte=MagickTrue;
8263 image_matte=MagickFalse;
8265 if (logging != MagickFalse)
8266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " PNG colortype %d was specified:",(int) ping_color_type);
8270 else /* write_png_colortype not specified */
8272 if (logging != MagickFalse)
8273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274 " Selecting PNG colortype:");
8276 ping_color_type=(png_byte) ((matte != MagickFalse)?
8277 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8279 if (image_info->type == TrueColorType)
8281 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8282 image_matte=MagickFalse;
8285 if (image_info->type == TrueColorMatteType)
8287 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8288 image_matte=MagickTrue;
8291 if (image_info->type == PaletteType ||
8292 image_info->type == PaletteMatteType)
8293 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8295 if (mng_info->write_png_colortype == 0 &&
8296 (image_info->type == UndefinedType ||
8297 image_info->type == OptimizeType))
8299 if (ping_have_color == MagickFalse)
8301 if (image_matte == MagickFalse)
8303 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8304 image_matte=MagickFalse;
8309 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8310 image_matte=MagickTrue;
8315 if (image_matte == MagickFalse)
8317 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8318 image_matte=MagickFalse;
8323 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8324 image_matte=MagickTrue;
8331 if (logging != MagickFalse)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " Selected PNG colortype=%d",ping_color_type);
8335 if (ping_bit_depth < 8)
8337 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8338 ping_color_type == PNG_COLOR_TYPE_RGB ||
8339 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8343 old_bit_depth=ping_bit_depth;
8345 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8347 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8351 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8356 if (image->colors == 0)
8359 (void) ThrowMagickException(&image->exception,
8360 GetMagickModule(),CoderError,
8361 "image has 0 colors", "`%s'","");
8364 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8365 ping_bit_depth <<= 1;
8368 if (logging != MagickFalse)
8370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371 " Number of colors: %.20g",(double) image_colors);
8373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8374 " Tentative PNG bit depth: %d",ping_bit_depth);
8377 if (ping_bit_depth < (int) mng_info->write_png_depth)
8378 ping_bit_depth = mng_info->write_png_depth;
8381 image_depth=ping_bit_depth;
8383 if (logging != MagickFalse)
8385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8386 " Tentative PNG color type: %.20g",(double) ping_color_type);
8388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8389 " image_info->type: %.20g",(double) image_info->type);
8391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8392 " image_depth: %.20g",(double) image_depth);
8394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " image->depth: %.20g",(double) image->depth);
8398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8402 if (matte != MagickFalse)
8404 if (mng_info->IsPalette)
8406 if (mng_info->write_png_colortype == 0)
8408 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8410 if (ping_have_color != MagickFalse)
8411 ping_color_type=PNG_COLOR_TYPE_RGBA;
8415 * Determine if there is any transparent color.
8417 if (number_transparent + number_semitransparent == 0)
8420 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8423 image_matte=MagickFalse;
8425 if (mng_info->write_png_colortype == 0)
8426 ping_color_type&=0x03;
8436 if (ping_bit_depth == 8)
8439 if (ping_bit_depth == 4)
8442 if (ping_bit_depth == 2)
8445 if (ping_bit_depth == 1)
8448 ping_trans_color.red=(png_uint_16)
8449 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8451 ping_trans_color.green=(png_uint_16)
8452 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8454 ping_trans_color.blue=(png_uint_16)
8455 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8457 ping_trans_color.gray=(png_uint_16)
8458 (ScaleQuantumToShort(PixelIntensityToQuantum(
8459 image->colormap)) & mask);
8461 ping_trans_color.index=(png_byte) 0;
8463 ping_have_tRNS=MagickTrue;
8466 if (ping_have_tRNS != MagickFalse)
8469 * Determine if there is one and only one transparent color
8470 * and if so if it is fully transparent.
8472 if (ping_have_cheap_transparency == MagickFalse)
8473 ping_have_tRNS=MagickFalse;
8476 if (ping_have_tRNS != MagickFalse)
8478 if (mng_info->write_png_colortype == 0)
8479 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8481 if (image_depth == 8)
8483 ping_trans_color.red&=0xff;
8484 ping_trans_color.green&=0xff;
8485 ping_trans_color.blue&=0xff;
8486 ping_trans_color.gray&=0xff;
8492 if (image_depth == 8)
8494 ping_trans_color.red&=0xff;
8495 ping_trans_color.green&=0xff;
8496 ping_trans_color.blue&=0xff;
8497 ping_trans_color.gray&=0xff;
8504 if (ping_have_tRNS != MagickFalse)
8505 image_matte=MagickFalse;
8507 if ((mng_info->IsPalette) &&
8508 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8509 ping_have_color == MagickFalse &&
8510 (image_matte == MagickFalse || image_depth >= 8))
8514 if (image_matte != MagickFalse)
8515 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8517 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8519 ping_color_type=PNG_COLOR_TYPE_GRAY;
8521 if (save_image_depth == 16 && image_depth == 8)
8523 if (logging != MagickFalse)
8525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526 " Scaling ping_trans_color (0)");
8528 ping_trans_color.gray*=0x0101;
8532 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8533 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8535 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8536 image_colors=(int) (one << image_depth);
8538 if (image_depth > 8)
8544 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8546 if(!mng_info->write_png_depth)
8550 while ((int) (one << ping_bit_depth)
8551 < (ssize_t) image_colors)
8552 ping_bit_depth <<= 1;
8556 else if (ping_color_type ==
8557 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8558 mng_info->IsPalette)
8560 /* Check if grayscale is reducible */
8563 depth_4_ok=MagickTrue,
8564 depth_2_ok=MagickTrue,
8565 depth_1_ok=MagickTrue;
8567 for (i=0; i < (ssize_t) image_colors; i++)
8572 intensity=ScaleQuantumToChar(image->colormap[i].red);
8574 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8575 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8576 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8577 depth_2_ok=depth_1_ok=MagickFalse;
8578 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8579 depth_1_ok=MagickFalse;
8582 if (depth_1_ok && mng_info->write_png_depth <= 1)
8585 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8588 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8593 image_depth=ping_bit_depth;
8598 if (mng_info->IsPalette)
8600 number_colors=image_colors;
8602 if (image_depth <= 8)
8607 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8609 if (mng_info->have_write_global_plte && matte == MagickFalse)
8611 png_set_PLTE(ping,ping_info,NULL,0);
8613 if (logging != MagickFalse)
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615 " Setting up empty PLTE chunk");
8620 for (i=0; i < (ssize_t) number_colors; i++)
8622 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8623 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8624 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8627 if (logging != MagickFalse)
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629 " Setting up PLTE chunk with %d colors",
8632 ping_have_PLTE=MagickTrue;
8635 /* color_type is PNG_COLOR_TYPE_PALETTE */
8636 if (mng_info->write_png_depth == 0)
8644 while ((one << ping_bit_depth) < number_colors)
8645 ping_bit_depth <<= 1;
8650 if (matte != MagickFalse)
8653 * Set up trans_colors array.
8655 assert(number_colors <= 256);
8657 ping_num_trans=(unsigned short) (number_transparent +
8658 number_semitransparent);
8660 if (ping_num_trans == 0)
8661 ping_have_tRNS=MagickFalse;
8665 if (logging != MagickFalse)
8667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8668 " Scaling ping_trans_color (1)");
8670 ping_have_tRNS=MagickTrue;
8672 for (i=0; i < ping_num_trans; i++)
8674 ping_trans_alpha[i]= (png_byte) (255-
8675 ScaleQuantumToChar(image->colormap[i].opacity));
8685 if (image_depth < 8)
8688 if ((save_image_depth == 16) && (image_depth == 8))
8690 if (logging != MagickFalse)
8692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8693 " Scaling ping_trans_color from (%d,%d,%d)",
8694 (int) ping_trans_color.red,
8695 (int) ping_trans_color.green,
8696 (int) ping_trans_color.blue);
8699 ping_trans_color.red*=0x0101;
8700 ping_trans_color.green*=0x0101;
8701 ping_trans_color.blue*=0x0101;
8702 ping_trans_color.gray*=0x0101;
8704 if (logging != MagickFalse)
8706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8708 (int) ping_trans_color.red,
8709 (int) ping_trans_color.green,
8710 (int) ping_trans_color.blue);
8715 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8716 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8719 Adjust background and transparency samples in sub-8-bit grayscale files.
8721 if (ping_bit_depth < 8 && ping_color_type ==
8722 PNG_COLOR_TYPE_GRAY)
8730 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8732 if (ping_exclude_bKGD == MagickFalse)
8735 ping_background.gray=(png_uint_16)
8736 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8738 if (logging != MagickFalse)
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740 " Setting up bKGD chunk (2)");
8742 ping_have_bKGD = MagickTrue;
8745 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8746 ping_trans_color.gray));
8749 if (ping_exclude_bKGD == MagickFalse)
8751 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8754 Identify which colormap entry is the background color.
8757 number_colors=image_colors;
8759 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8760 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8763 ping_background.index=(png_byte) i;
8765 if (logging != MagickFalse)
8767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8768 " Setting up bKGD chunk with index=%d",(int) i);
8771 if (i < (ssize_t) number_colors)
8773 ping_have_bKGD = MagickTrue;
8775 if (logging != MagickFalse)
8777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778 " background =(%d,%d,%d)",
8779 (int) ping_background.red,
8780 (int) ping_background.green,
8781 (int) ping_background.blue);
8785 else /* Can't happen */
8787 if (logging != MagickFalse)
8788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8789 " No room in PLTE to add bKGD color");
8790 ping_have_bKGD = MagickFalse;
8795 if (logging != MagickFalse)
8796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8797 " PNG color type: %d",ping_color_type);
8799 Initialize compression level and filtering.
8801 if (logging != MagickFalse)
8803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8804 " Setting up deflate compression");
8806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807 " Compression buffer size: 32768");
8810 png_set_compression_buffer_size(ping,32768L);
8812 if (logging != MagickFalse)
8813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814 " Compression mem level: 9");
8816 png_set_compression_mem_level(ping, 9);
8818 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8826 level=(int) MagickMin((ssize_t) quality/10,9);
8828 if (logging != MagickFalse)
8829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830 " Compression level: %d",level);
8832 png_set_compression_level(ping,level);
8837 if (logging != MagickFalse)
8838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8839 " Compression strategy: Z_HUFFMAN_ONLY");
8841 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8844 if (logging != MagickFalse)
8845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8846 " Setting up filtering");
8848 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8849 /* This became available in libpng-1.0.9. Output must be a MNG. */
8850 if (mng_info->write_mng && ((quality % 10) == 7))
8852 if (logging != MagickFalse)
8853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8856 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8860 if (logging != MagickFalse)
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8869 if ((quality % 10) > 5)
8870 base_filter=PNG_ALL_FILTERS;
8873 if ((quality % 10) != 5)
8874 base_filter=(int) quality % 10;
8877 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8878 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8880 base_filter=PNG_NO_FILTERS;
8883 base_filter=PNG_ALL_FILTERS;
8885 if (logging != MagickFalse)
8887 if (base_filter == PNG_ALL_FILTERS)
8888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8889 " Base filter method: ADAPTIVE");
8891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8892 " Base filter method: NONE");
8895 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8898 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8899 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8901 ResetImageProfileIterator(image);
8902 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8904 profile=GetImageProfile(image,name);
8906 if (profile != (StringInfo *) NULL)
8908 #ifdef PNG_WRITE_iCCP_SUPPORTED
8909 if ((LocaleCompare(name,"ICC") == 0) ||
8910 (LocaleCompare(name,"ICM") == 0))
8913 if (ping_exclude_iCCP == MagickFalse)
8915 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8916 #if (PNG_LIBPNG_VER < 10500)
8917 (png_charp) GetStringInfoDatum(profile),
8919 (png_const_bytep) GetStringInfoDatum(profile),
8921 (png_uint_32) GetStringInfoLength(profile));
8927 if (ping_exclude_zCCP == MagickFalse)
8929 Magick_png_write_raw_profile(image_info,ping,ping_info,
8930 (unsigned char *) name,(unsigned char *) name,
8931 GetStringInfoDatum(profile),
8932 (png_uint_32) GetStringInfoLength(profile));
8936 if (logging != MagickFalse)
8937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " Setting up text chunk with %s profile",name);
8940 name=GetNextImageProfile(image);
8944 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8945 if ((mng_info->have_write_global_srgb == 0) &&
8946 ((image->rendering_intent != UndefinedIntent) ||
8947 (image->colorspace == sRGBColorspace)))
8949 if (ping_exclude_sRGB == MagickFalse)
8952 Note image rendering intent.
8954 if (logging != MagickFalse)
8955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956 " Setting up sRGB chunk");
8958 (void) png_set_sRGB(ping,ping_info,(
8959 Magick_RenderingIntent_to_PNG_RenderingIntent(
8960 image->rendering_intent)));
8962 if (ping_exclude_gAMA == MagickFalse)
8963 png_set_gAMA(ping,ping_info,0.45455);
8967 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8970 if (ping_exclude_gAMA == MagickFalse &&
8971 (ping_exclude_sRGB == MagickFalse ||
8972 (image->gamma < .45 || image->gamma > .46)))
8974 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8978 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8980 if (logging != MagickFalse)
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " Setting up gAMA chunk");
8984 png_set_gAMA(ping,ping_info,image->gamma);
8988 if (ping_exclude_cHRM == MagickFalse)
8990 if ((mng_info->have_write_global_chrm == 0) &&
8991 (image->chromaticity.red_primary.x != 0.0))
8994 Note image chromaticity.
8995 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9003 wp=image->chromaticity.white_point;
9004 rp=image->chromaticity.red_primary;
9005 gp=image->chromaticity.green_primary;
9006 bp=image->chromaticity.blue_primary;
9008 if (logging != MagickFalse)
9009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9010 " Setting up cHRM chunk");
9012 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9018 ping_interlace_method=image_info->interlace != NoInterlace;
9020 if (mng_info->write_mng)
9021 png_set_sig_bytes(ping,8);
9023 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9025 if (mng_info->write_png_colortype != 0)
9027 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9028 if (ping_have_color != MagickFalse)
9030 ping_color_type = PNG_COLOR_TYPE_RGB;
9032 if (ping_bit_depth < 8)
9036 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9037 if (ping_have_color != MagickFalse)
9038 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9041 if (ping_need_colortype_warning != MagickFalse ||
9042 ((mng_info->write_png_depth &&
9043 (int) mng_info->write_png_depth != ping_bit_depth) ||
9044 (mng_info->write_png_colortype &&
9045 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9046 mng_info->write_png_colortype != 7 &&
9047 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9049 if (logging != MagickFalse)
9051 if (ping_need_colortype_warning != MagickFalse)
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " Image has transparency but tRNS chunk was excluded");
9057 if (mng_info->write_png_depth)
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " Defined PNG:bit-depth=%u, Computed depth=%u",
9061 mng_info->write_png_depth,
9065 if (mng_info->write_png_colortype)
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " Defined PNG:color-type=%u, Computed color type=%u",
9069 mng_info->write_png_colortype-1,
9075 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9078 if (image_matte != MagickFalse && image->matte == MagickFalse)
9080 /* Add an opaque matte channel */
9081 image->matte = MagickTrue;
9082 (void) SetImageOpacity(image,0);
9084 if (logging != MagickFalse)
9085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9086 " Added an opaque matte channel");
9089 if (number_transparent != 0 || number_semitransparent != 0)
9091 if (ping_color_type < 4)
9093 ping_have_tRNS=MagickTrue;
9094 if (logging != MagickFalse)
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096 " Setting ping_have_tRNS=MagickTrue.");
9100 if (logging != MagickFalse)
9101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9102 " Writing PNG header chunks");
9104 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9105 ping_bit_depth,ping_color_type,
9106 ping_interlace_method,ping_compression_method,
9107 ping_filter_method);
9109 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9111 png_set_PLTE(ping,ping_info,palette,number_colors);
9113 if (logging != MagickFalse)
9115 for (i=0; i< (ssize_t) number_colors; i++)
9117 if (i < ping_num_trans)
9118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9119 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9121 (int) palette[i].red,
9122 (int) palette[i].green,
9123 (int) palette[i].blue,
9125 (int) ping_trans_alpha[i]);
9127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9128 " PLTE[%d] = (%d,%d,%d)",
9130 (int) palette[i].red,
9131 (int) palette[i].green,
9132 (int) palette[i].blue);
9137 if (ping_exclude_bKGD == MagickFalse)
9139 if (ping_have_bKGD != MagickFalse)
9140 png_set_bKGD(ping,ping_info,&ping_background);
9143 if (ping_exclude_pHYs == MagickFalse)
9145 if (ping_have_pHYs != MagickFalse)
9147 png_set_pHYs(ping,ping_info,
9148 ping_pHYs_x_resolution,
9149 ping_pHYs_y_resolution,
9150 ping_pHYs_unit_type);
9154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155 " Setting up pHYs chunk");
9156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9157 " x_resolution=%lu",
9158 (unsigned long) ping_pHYs_x_resolution);
9159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9160 " y_resolution=%lu",
9161 (unsigned long) ping_pHYs_y_resolution);
9162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164 (unsigned long) ping_pHYs_unit_type);
9169 #if defined(PNG_oFFs_SUPPORTED)
9170 if (ping_exclude_oFFs == MagickFalse)
9172 if (image->page.x || image->page.y)
9174 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9175 (png_int_32) image->page.y, 0);
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9180 (int) image->page.x, (int) image->page.y);
9185 if (mng_info->need_blob != MagickFalse)
9187 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9189 png_error(ping,"WriteBlob Failed");
9191 ping_have_blob=MagickTrue;
9194 png_write_info_before_PLTE(ping, ping_info);
9196 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9198 if (logging != MagickFalse)
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9204 if (ping_color_type == 3)
9205 (void) png_set_tRNS(ping, ping_info,
9212 (void) png_set_tRNS(ping, ping_info,
9217 if (logging != MagickFalse)
9219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9220 " tRNS color =(%d,%d,%d)",
9221 (int) ping_trans_color.red,
9222 (int) ping_trans_color.green,
9223 (int) ping_trans_color.blue);
9228 /* write any png-chunk-b profiles */
9229 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9231 png_write_info(ping,ping_info);
9233 /* write any PNG-chunk-m profiles */
9234 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9236 if (ping_exclude_vpAg == MagickFalse)
9238 if ((image->page.width != 0 && image->page.width != image->columns) ||
9239 (image->page.height != 0 && image->page.height != image->rows))
9244 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9245 PNGType(chunk,mng_vpAg);
9246 LogPNGChunk(logging,mng_vpAg,9L);
9247 PNGLong(chunk+4,(png_uint_32) image->page.width);
9248 PNGLong(chunk+8,(png_uint_32) image->page.height);
9249 chunk[12]=0; /* unit = pixels */
9250 (void) WriteBlob(image,13,chunk);
9251 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9255 #if (PNG_LIBPNG_VER == 10206)
9256 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9257 #define PNG_HAVE_IDAT 0x04
9258 ping->mode |= PNG_HAVE_IDAT;
9259 #undef PNG_HAVE_IDAT
9262 png_set_packing(ping);
9266 rowbytes=image->columns;
9267 if (image_depth > 8)
9269 switch (ping_color_type)
9271 case PNG_COLOR_TYPE_RGB:
9275 case PNG_COLOR_TYPE_GRAY_ALPHA:
9279 case PNG_COLOR_TYPE_RGBA:
9287 if (logging != MagickFalse)
9289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9290 " Writing PNG image data");
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9295 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9296 sizeof(*ping_pixels));
9298 if (ping_pixels == (unsigned char *) NULL)
9299 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9302 Initialize image scanlines.
9304 if (setjmp(png_jmpbuf(ping)))
9310 if (image_info->verbose)
9311 (void) printf("PNG write has failed.\n");
9313 png_destroy_write_struct(&ping,&ping_info);
9314 if (quantum_info != (QuantumInfo *) NULL)
9315 quantum_info=DestroyQuantumInfo(quantum_info);
9316 if (ping_pixels != (unsigned char *) NULL)
9317 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9318 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9319 UnlockSemaphoreInfo(ping_semaphore);
9321 if (ping_have_blob != MagickFalse)
9322 (void) CloseBlob(image);
9323 image_info=DestroyImageInfo(image_info);
9324 image=DestroyImage(image);
9325 return(MagickFalse);
9327 quantum_info=AcquireQuantumInfo(image_info,image);
9328 if (quantum_info == (QuantumInfo *) NULL)
9329 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9330 quantum_info->format=UndefinedQuantumFormat;
9331 quantum_info->depth=image_depth;
9332 num_passes=png_set_interlace_handling(ping);
9334 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9335 !mng_info->write_png32) &&
9336 (mng_info->IsPalette ||
9337 (image_info->type == BilevelType)) &&
9338 image_matte == MagickFalse &&
9339 ping_have_non_bw == MagickFalse)
9341 /* Palette, Bilevel, or Opaque Monochrome */
9342 register const PixelPacket
9345 quantum_info->depth=8;
9346 for (pass=0; pass < num_passes; pass++)
9349 Convert PseudoClass image to a PNG monochrome image.
9351 for (y=0; y < (ssize_t) image->rows; y++)
9353 if (logging != MagickFalse && y == 0)
9354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355 " Writing row of pixels (0)");
9357 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9359 if (p == (const PixelPacket *) NULL)
9362 if (mng_info->IsPalette)
9364 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9365 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9366 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9367 mng_info->write_png_depth &&
9368 mng_info->write_png_depth != old_bit_depth)
9370 /* Undo pixel scaling */
9371 for (i=0; i < (ssize_t) image->columns; i++)
9372 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9373 >> (8-old_bit_depth));
9379 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9380 quantum_info,RedQuantum,ping_pixels,&image->exception);
9383 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9384 for (i=0; i < (ssize_t) image->columns; i++)
9385 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9388 if (logging != MagickFalse && y == 0)
9389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390 " Writing row of pixels (1)");
9392 png_write_row(ping,ping_pixels);
9394 if (image->previous == (Image *) NULL)
9396 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9397 if (status == MagickFalse)
9403 else /* Not Palette, Bilevel, or Opaque Monochrome */
9405 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9406 !mng_info->write_png32) &&
9407 (image_matte != MagickFalse ||
9408 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9409 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9411 register const PixelPacket
9414 for (pass=0; pass < num_passes; pass++)
9417 for (y=0; y < (ssize_t) image->rows; y++)
9419 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9421 if (p == (const PixelPacket *) NULL)
9424 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9426 if (mng_info->IsPalette)
9427 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9428 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9431 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9432 quantum_info,RedQuantum,ping_pixels,&image->exception);
9434 if (logging != MagickFalse && y == 0)
9435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436 " Writing GRAY PNG pixels (2)");
9439 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9441 if (logging != MagickFalse && y == 0)
9442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443 " Writing GRAY_ALPHA PNG pixels (2)");
9445 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9446 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9449 if (logging != MagickFalse && y == 0)
9450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9451 " Writing row of pixels (2)");
9453 png_write_row(ping,ping_pixels);
9456 if (image->previous == (Image *) NULL)
9458 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9459 if (status == MagickFalse)
9467 register const PixelPacket
9470 for (pass=0; pass < num_passes; pass++)
9472 if ((image_depth > 8) || (mng_info->write_png24 ||
9473 mng_info->write_png32 ||
9474 (!mng_info->write_png8 && !mng_info->IsPalette)))
9476 for (y=0; y < (ssize_t) image->rows; y++)
9478 p=GetVirtualPixels(image,0,y,image->columns,1,
9481 if (p == (const PixelPacket *) NULL)
9484 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9486 if (image->storage_class == DirectClass)
9487 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9488 quantum_info,RedQuantum,ping_pixels,&image->exception);
9491 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9492 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9495 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9497 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9498 quantum_info,GrayAlphaQuantum,ping_pixels,
9501 if (logging != MagickFalse && y == 0)
9502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503 " Writing GRAY_ALPHA PNG pixels (3)");
9506 else if (image_matte != MagickFalse)
9507 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9508 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9511 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9512 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9514 if (logging != MagickFalse && y == 0)
9515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516 " Writing row of pixels (3)");
9518 png_write_row(ping,ping_pixels);
9523 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9524 mng_info->write_png32 ||
9525 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9527 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9528 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9530 if (logging != MagickFalse)
9531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9534 quantum_info->depth=8;
9538 for (y=0; y < (ssize_t) image->rows; y++)
9540 if (logging != MagickFalse && y == 0)
9541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9542 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9544 p=GetVirtualPixels(image,0,y,image->columns,1,
9547 if (p == (const PixelPacket *) NULL)
9550 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9552 quantum_info->depth=image->depth;
9554 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9555 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9558 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9560 if (logging != MagickFalse && y == 0)
9561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9562 " Writing GRAY_ALPHA PNG pixels (4)");
9564 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9565 quantum_info,GrayAlphaQuantum,ping_pixels,
9571 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9572 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9574 if (logging != MagickFalse && y <= 2)
9576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577 " Writing row of non-gray pixels (4)");
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9581 (int)ping_pixels[0],(int)ping_pixels[1]);
9584 png_write_row(ping,ping_pixels);
9588 if (image->previous == (Image *) NULL)
9590 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9591 if (status == MagickFalse)
9598 if (quantum_info != (QuantumInfo *) NULL)
9599 quantum_info=DestroyQuantumInfo(quantum_info);
9601 if (logging != MagickFalse)
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Wrote PNG image data");
9606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9607 " Width: %.20g",(double) ping_width);
9609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9610 " Height: %.20g",(double) ping_height);
9612 if (mng_info->write_png_depth)
9614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9615 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " PNG bit-depth written: %d",ping_bit_depth);
9621 if (mng_info->write_png_colortype)
9623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628 " PNG color-type written: %d",ping_color_type);
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " PNG Interlace method: %d",ping_interlace_method);
9634 Generate text chunks.
9636 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9638 ResetImagePropertyIterator(image);
9639 property=GetNextImageProperty(image);
9640 while (property != (const char *) NULL)
9645 value=GetImageProperty(image,property);
9646 if (ping_exclude_pHYs != MagickFalse ||
9647 LocaleCompare(property,"density") != 0 ||
9648 LocaleCompare(property,"units") != 0)
9650 if (value != (const char *) NULL)
9652 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9653 text[0].key=(char *) property;
9654 text[0].text=(char *) value;
9655 text[0].text_length=strlen(value);
9657 if (ping_exclude_tEXt != MagickFalse)
9658 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9660 else if (ping_exclude_zTXt != MagickFalse)
9661 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9665 text[0].compression=image_info->compression == NoCompression ||
9666 (image_info->compression == UndefinedCompression &&
9667 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9668 PNG_TEXT_COMPRESSION_zTXt ;
9671 if (logging != MagickFalse)
9673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9674 " Setting up text chunk");
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " keyword: %s",text[0].key);
9680 png_set_text(ping,ping_info,text,1);
9681 png_free(ping,text);
9684 property=GetNextImageProperty(image);
9688 /* write any PNG-chunk-e profiles */
9689 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9691 if (logging != MagickFalse)
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " Writing PNG end info");
9695 png_write_end(ping,ping_info);
9697 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9699 if (mng_info->page.x || mng_info->page.y ||
9700 (ping_width != mng_info->page.width) ||
9701 (ping_height != mng_info->page.height))
9707 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9709 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9710 PNGType(chunk,mng_FRAM);
9711 LogPNGChunk(logging,mng_FRAM,27L);
9713 chunk[5]=0; /* frame name separator (no name) */
9714 chunk[6]=1; /* flag for changing delay, for next frame only */
9715 chunk[7]=0; /* flag for changing frame timeout */
9716 chunk[8]=1; /* flag for changing frame clipping for next frame */
9717 chunk[9]=0; /* flag for changing frame sync_id */
9718 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9719 chunk[14]=0; /* clipping boundaries delta type */
9720 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9722 (png_uint_32) (mng_info->page.x + ping_width));
9723 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9725 (png_uint_32) (mng_info->page.y + ping_height));
9726 (void) WriteBlob(image,31,chunk);
9727 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9728 mng_info->old_framing_mode=4;
9729 mng_info->framing_mode=1;
9733 mng_info->framing_mode=3;
9735 if (mng_info->write_mng && !mng_info->need_fram &&
9736 ((int) image->dispose == 3))
9737 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9738 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9739 "`%s'",image->filename);
9745 png_destroy_write_struct(&ping,&ping_info);
9747 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9749 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9750 UnlockSemaphoreInfo(ping_semaphore);
9753 if (ping_have_blob != MagickFalse)
9754 (void) CloseBlob(image);
9756 image_info=DestroyImageInfo(image_info);
9757 image=DestroyImage(image);
9759 /* Store bit depth actually written */
9760 s[0]=(char) ping_bit_depth;
9763 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9765 if (logging != MagickFalse)
9766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9767 " exit WriteOnePNGImage()");
9770 /* End write one PNG image */
9774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9778 % W r i t e P N G I m a g e %
9782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9784 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9785 % Multiple-image Network Graphics (MNG) image file.
9787 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9789 % The format of the WritePNGImage method is:
9791 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9793 % A description of each parameter follows:
9795 % o image_info: the image info.
9797 % o image: The image.
9799 % Returns MagickTrue on success, MagickFalse on failure.
9801 % Communicating with the PNG encoder:
9803 % While the datastream written is always in PNG format and normally would
9804 % be given the "png" file extension, this method also writes the following
9805 % pseudo-formats which are subsets of PNG:
9807 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9808 % a depth greater than 8, the depth is reduced. If transparency
9809 % is present, the tRNS chunk must only have values 0 and 255
9810 % (i.e., transparency is binary: fully opaque or fully
9811 % transparent). If other values are present they will be
9812 % 50%-thresholded to binary transparency. If more than 256
9813 % colors are present, they will be quantized to the 3-3-2
9814 % palette. If you want better quantization or dithering of
9815 % the colors or alpha, you need to do it before calling the
9816 % PNG encoder. The pixels contain 8-bit indices even if
9817 % they could be represented with 1, 2, or 4 bits. Grayscale
9818 % images will be written as indexed PNG files even though the
9819 % PNG grayscale type might be slightly more efficient. Please
9820 % note that writing to the PNG8 format may result in loss
9821 % of color and alpha data.
9823 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9824 % chunk can be present to convey binary transparency by naming
9825 % one of the colors as transparent. The only loss incurred
9826 % is reduction of sample depth to 8. If the image has more
9827 % than one transparent color, has semitransparent pixels, or
9828 % has an opaque pixel with the same RGB components as the
9829 % transparent color, an image is not written.
9831 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9832 % transparency is permitted, i.e., the alpha sample for
9833 % each pixel can have any value from 0 to 255. The alpha
9834 % channel is present even if the image is fully opaque.
9835 % The only loss in data is the reduction of the sample depth
9838 % o -define: For more precise control of the PNG output, you can use the
9839 % Image options "png:bit-depth" and "png:color-type". These
9840 % can be set from the commandline with "-define" and also
9841 % from the application programming interfaces. The options
9842 % are case-independent and are converted to lowercase before
9843 % being passed to this encoder.
9845 % png:color-type can be 0, 2, 3, 4, or 6.
9847 % When png:color-type is 0 (Grayscale), png:bit-depth can
9848 % be 1, 2, 4, 8, or 16.
9850 % When png:color-type is 2 (RGB), png:bit-depth can
9853 % When png:color-type is 3 (Indexed), png:bit-depth can
9854 % be 1, 2, 4, or 8. This refers to the number of bits
9855 % used to store the index. The color samples always have
9856 % bit-depth 8 in indexed PNG files.
9858 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9859 % png:bit-depth can be 8 or 16.
9861 % If the image cannot be written without loss with the requested bit-depth
9862 % and color-type, a PNG file will not be written, and the encoder will
9863 % return MagickFalse.
9865 % Since image encoders should not be responsible for the "heavy lifting",
9866 % the user should make sure that ImageMagick has already reduced the
9867 % image depth and number of colors and limit transparency to binary
9868 % transparency prior to attempting to write the image with depth, color,
9869 % or transparency limitations.
9871 % TODO: Enforce the previous paragraph.
9873 % Note that another definition, "png:bit-depth-written" exists, but it
9874 % is not intended for external use. It is only used internally by the
9875 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9877 % It is possible to request that the PNG encoder write previously-formatted
9878 % ancillary chunks in the output PNG file, using the "-profile" commandline
9879 % option as shown below or by setting the profile via a programming
9882 % -profile PNG-chunk-x:<file>
9884 % where x is a location flag and <file> is a file containing the chunk
9885 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9886 % This encoder will compute the chunk length and CRC, so those must not
9887 % be included in the file.
9889 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9890 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9891 % of the same type, then add a short unique string after the "x" to prevent
9892 % subsequent profiles from overwriting the preceding ones, e.g.,
9894 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9896 % As of version 6.6.6 the following optimizations are always done:
9898 % o 32-bit depth is reduced to 16.
9899 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9900 % high byte and low byte are identical.
9901 % o Palette is sorted to remove unused entries and to put a
9902 % transparent color first, if BUILD_PNG_PALETTE is defined.
9903 % o Opaque matte channel is removed when writing an indexed PNG.
9904 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9905 % this can be done without loss and a larger bit depth N was not
9906 % requested via the "-define PNG:bit-depth=N" option.
9907 % o If matte channel is present but only one transparent color is
9908 % present, RGB+tRNS is written instead of RGBA
9909 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9910 % was requested when converting an opaque image).
9912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9914 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9936 assert(image_info != (const ImageInfo *) NULL);
9937 assert(image_info->signature == MagickSignature);
9938 assert(image != (Image *) NULL);
9939 assert(image->signature == MagickSignature);
9940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9941 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9943 Allocate a MngInfo structure.
9945 have_mng_structure=MagickFalse;
9946 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9948 if (mng_info == (MngInfo *) NULL)
9949 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9952 Initialize members of the MngInfo structure.
9954 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9955 mng_info->image=image;
9956 mng_info->equal_backgrounds=MagickTrue;
9957 have_mng_structure=MagickTrue;
9959 /* See if user has requested a specific PNG subformat */
9961 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9962 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9963 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9965 if (mng_info->write_png8)
9967 mng_info->write_png_colortype = /* 3 */ 4;
9968 mng_info->write_png_depth = 8;
9972 if (mng_info->write_png24)
9974 mng_info->write_png_colortype = /* 2 */ 3;
9975 mng_info->write_png_depth = 8;
9978 if (image->matte == MagickTrue)
9979 (void) SetImageType(image,TrueColorMatteType);
9982 (void) SetImageType(image,TrueColorType);
9984 (void) SyncImage(image);
9987 if (mng_info->write_png32)
9989 mng_info->write_png_colortype = /* 6 */ 7;
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 value=GetImageOption(image_info,"png:bit-depth");
10004 if (value != (char *) NULL)
10006 if (LocaleCompare(value,"1") == 0)
10007 mng_info->write_png_depth = 1;
10009 else if (LocaleCompare(value,"2") == 0)
10010 mng_info->write_png_depth = 2;
10012 else if (LocaleCompare(value,"4") == 0)
10013 mng_info->write_png_depth = 4;
10015 else if (LocaleCompare(value,"8") == 0)
10016 mng_info->write_png_depth = 8;
10018 else if (LocaleCompare(value,"16") == 0)
10019 mng_info->write_png_depth = 16;
10022 (void) ThrowMagickException(&image->exception,
10023 GetMagickModule(),CoderWarning,
10024 "ignoring invalid defined png:bit-depth",
10027 if (logging != MagickFalse)
10028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10032 value=GetImageOption(image_info,"png:color-type");
10034 if (value != (char *) NULL)
10036 /* We must store colortype+1 because 0 is a valid colortype */
10037 if (LocaleCompare(value,"0") == 0)
10038 mng_info->write_png_colortype = 1;
10040 else if (LocaleCompare(value,"2") == 0)
10041 mng_info->write_png_colortype = 3;
10043 else if (LocaleCompare(value,"3") == 0)
10044 mng_info->write_png_colortype = 4;
10046 else if (LocaleCompare(value,"4") == 0)
10047 mng_info->write_png_colortype = 5;
10049 else if (LocaleCompare(value,"6") == 0)
10050 mng_info->write_png_colortype = 7;
10053 (void) ThrowMagickException(&image->exception,
10054 GetMagickModule(),CoderWarning,
10055 "ignoring invalid defined png:color-type",
10058 if (logging != MagickFalse)
10059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10060 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10063 /* Check for chunks to be excluded:
10065 * The default is to not exclude any known chunks except for any
10066 * listed in the "unused_chunks" array, above.
10068 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10069 * define (in the image properties or in the image artifacts)
10070 * or via a mng_info member. For convenience, in addition
10071 * to or instead of a comma-separated list of chunks, the
10072 * "exclude-chunk" string can be simply "all" or "none".
10074 * The exclude-chunk define takes priority over the mng_info.
10076 * A "PNG:include-chunk" define takes priority over both the
10077 * mng_info and the "PNG:exclude-chunk" define. Like the
10078 * "exclude-chunk" string, it can define "all" or "none" as
10079 * well as a comma-separated list. Chunks that are unknown to
10080 * ImageMagick are always excluded, regardless of their "copy-safe"
10081 * status according to the PNG specification, and even if they
10082 * appear in the "include-chunk" list.
10084 * Finally, all chunks listed in the "unused_chunks" array are
10085 * automatically excluded, regardless of the other instructions
10088 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10089 * will not be written and the gAMA chunk will only be written if it
10090 * is not between .45 and .46, or approximately (1.0/2.2).
10092 * If you exclude tRNS and the image has transparency, the colortype
10093 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10095 * The -strip option causes StripImage() to set the png:include-chunk
10096 * artifact to "none,gama".
10099 mng_info->ping_exclude_bKGD=MagickFalse;
10100 mng_info->ping_exclude_cHRM=MagickFalse;
10101 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10102 mng_info->ping_exclude_gAMA=MagickFalse;
10103 mng_info->ping_exclude_cHRM=MagickFalse;
10104 mng_info->ping_exclude_iCCP=MagickFalse;
10105 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10106 mng_info->ping_exclude_oFFs=MagickFalse;
10107 mng_info->ping_exclude_pHYs=MagickFalse;
10108 mng_info->ping_exclude_sRGB=MagickFalse;
10109 mng_info->ping_exclude_tEXt=MagickFalse;
10110 mng_info->ping_exclude_tRNS=MagickFalse;
10111 mng_info->ping_exclude_vpAg=MagickFalse;
10112 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10113 mng_info->ping_exclude_zTXt=MagickFalse;
10115 excluding=MagickFalse;
10117 for (source=0; source<1; source++)
10121 value=GetImageArtifact(image,"png:exclude-chunk");
10124 value=GetImageArtifact(image,"png:exclude-chunks");
10128 value=GetImageOption(image_info,"png:exclude-chunk");
10131 value=GetImageOption(image_info,"png:exclude-chunks");
10140 excluding=MagickTrue;
10142 if (logging != MagickFalse)
10145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10146 " png:exclude-chunk=%s found in image artifacts.\n", value);
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " png:exclude-chunk=%s found in image properties.\n", value);
10152 last=strlen(value);
10154 for (i=0; i<(int) last; i+=5)
10157 if (LocaleNCompare(value+i,"all",3) == 0)
10159 mng_info->ping_exclude_bKGD=MagickTrue;
10160 mng_info->ping_exclude_cHRM=MagickTrue;
10161 mng_info->ping_exclude_EXIF=MagickTrue;
10162 mng_info->ping_exclude_gAMA=MagickTrue;
10163 mng_info->ping_exclude_iCCP=MagickTrue;
10164 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10165 mng_info->ping_exclude_oFFs=MagickTrue;
10166 mng_info->ping_exclude_pHYs=MagickTrue;
10167 mng_info->ping_exclude_sRGB=MagickTrue;
10168 mng_info->ping_exclude_tEXt=MagickTrue;
10169 mng_info->ping_exclude_tRNS=MagickTrue;
10170 mng_info->ping_exclude_vpAg=MagickTrue;
10171 mng_info->ping_exclude_zCCP=MagickTrue;
10172 mng_info->ping_exclude_zTXt=MagickTrue;
10176 if (LocaleNCompare(value+i,"none",4) == 0)
10178 mng_info->ping_exclude_bKGD=MagickFalse;
10179 mng_info->ping_exclude_cHRM=MagickFalse;
10180 mng_info->ping_exclude_EXIF=MagickFalse;
10181 mng_info->ping_exclude_gAMA=MagickFalse;
10182 mng_info->ping_exclude_iCCP=MagickFalse;
10183 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10184 mng_info->ping_exclude_oFFs=MagickFalse;
10185 mng_info->ping_exclude_pHYs=MagickFalse;
10186 mng_info->ping_exclude_sRGB=MagickFalse;
10187 mng_info->ping_exclude_tEXt=MagickFalse;
10188 mng_info->ping_exclude_tRNS=MagickFalse;
10189 mng_info->ping_exclude_vpAg=MagickFalse;
10190 mng_info->ping_exclude_zCCP=MagickFalse;
10191 mng_info->ping_exclude_zTXt=MagickFalse;
10194 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10195 mng_info->ping_exclude_bKGD=MagickTrue;
10197 if (LocaleNCompare(value+i,"chrm",4) == 0)
10198 mng_info->ping_exclude_cHRM=MagickTrue;
10200 if (LocaleNCompare(value+i,"exif",4) == 0)
10201 mng_info->ping_exclude_EXIF=MagickTrue;
10203 if (LocaleNCompare(value+i,"gama",4) == 0)
10204 mng_info->ping_exclude_gAMA=MagickTrue;
10206 if (LocaleNCompare(value+i,"iccp",4) == 0)
10207 mng_info->ping_exclude_iCCP=MagickTrue;
10210 if (LocaleNCompare(value+i,"itxt",4) == 0)
10211 mng_info->ping_exclude_iTXt=MagickTrue;
10214 if (LocaleNCompare(value+i,"gama",4) == 0)
10215 mng_info->ping_exclude_gAMA=MagickTrue;
10217 if (LocaleNCompare(value+i,"offs",4) == 0)
10218 mng_info->ping_exclude_oFFs=MagickTrue;
10220 if (LocaleNCompare(value+i,"phys",4) == 0)
10221 mng_info->ping_exclude_pHYs=MagickTrue;
10223 if (LocaleNCompare(value+i,"srgb",4) == 0)
10224 mng_info->ping_exclude_sRGB=MagickTrue;
10226 if (LocaleNCompare(value+i,"text",4) == 0)
10227 mng_info->ping_exclude_tEXt=MagickTrue;
10229 if (LocaleNCompare(value+i,"trns",4) == 0)
10230 mng_info->ping_exclude_tRNS=MagickTrue;
10232 if (LocaleNCompare(value+i,"vpag",4) == 0)
10233 mng_info->ping_exclude_vpAg=MagickTrue;
10235 if (LocaleNCompare(value+i,"zccp",4) == 0)
10236 mng_info->ping_exclude_zCCP=MagickTrue;
10238 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10239 mng_info->ping_exclude_zTXt=MagickTrue;
10245 for (source=0; source<1; source++)
10249 value=GetImageArtifact(image,"png:include-chunk");
10252 value=GetImageArtifact(image,"png:include-chunks");
10256 value=GetImageOption(image_info,"png:include-chunk");
10259 value=GetImageOption(image_info,"png:include-chunks");
10267 excluding=MagickTrue;
10269 if (logging != MagickFalse)
10272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10273 " png:include-chunk=%s found in image artifacts.\n", value);
10275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276 " png:include-chunk=%s found in image properties.\n", value);
10279 last=strlen(value);
10281 for (i=0; i<(int) last; i+=5)
10283 if (LocaleNCompare(value+i,"all",3) == 0)
10285 mng_info->ping_exclude_bKGD=MagickFalse;
10286 mng_info->ping_exclude_cHRM=MagickFalse;
10287 mng_info->ping_exclude_EXIF=MagickFalse;
10288 mng_info->ping_exclude_gAMA=MagickFalse;
10289 mng_info->ping_exclude_iCCP=MagickFalse;
10290 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10291 mng_info->ping_exclude_oFFs=MagickFalse;
10292 mng_info->ping_exclude_pHYs=MagickFalse;
10293 mng_info->ping_exclude_sRGB=MagickFalse;
10294 mng_info->ping_exclude_tEXt=MagickFalse;
10295 mng_info->ping_exclude_tRNS=MagickFalse;
10296 mng_info->ping_exclude_vpAg=MagickFalse;
10297 mng_info->ping_exclude_zCCP=MagickFalse;
10298 mng_info->ping_exclude_zTXt=MagickFalse;
10302 if (LocaleNCompare(value+i,"none",4) == 0)
10304 mng_info->ping_exclude_bKGD=MagickTrue;
10305 mng_info->ping_exclude_cHRM=MagickTrue;
10306 mng_info->ping_exclude_EXIF=MagickTrue;
10307 mng_info->ping_exclude_gAMA=MagickTrue;
10308 mng_info->ping_exclude_iCCP=MagickTrue;
10309 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10310 mng_info->ping_exclude_oFFs=MagickTrue;
10311 mng_info->ping_exclude_pHYs=MagickTrue;
10312 mng_info->ping_exclude_sRGB=MagickTrue;
10313 mng_info->ping_exclude_tEXt=MagickTrue;
10314 mng_info->ping_exclude_tRNS=MagickTrue;
10315 mng_info->ping_exclude_vpAg=MagickTrue;
10316 mng_info->ping_exclude_zCCP=MagickTrue;
10317 mng_info->ping_exclude_zTXt=MagickTrue;
10320 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10321 mng_info->ping_exclude_bKGD=MagickFalse;
10323 if (LocaleNCompare(value+i,"chrm",4) == 0)
10324 mng_info->ping_exclude_cHRM=MagickFalse;
10326 if (LocaleNCompare(value+i,"exif",4) == 0)
10327 mng_info->ping_exclude_EXIF=MagickFalse;
10329 if (LocaleNCompare(value+i,"gama",4) == 0)
10330 mng_info->ping_exclude_gAMA=MagickFalse;
10332 if (LocaleNCompare(value+i,"iccp",4) == 0)
10333 mng_info->ping_exclude_iCCP=MagickFalse;
10336 if (LocaleNCompare(value+i,"itxt",4) == 0)
10337 mng_info->ping_exclude_iTXt=MagickFalse;
10340 if (LocaleNCompare(value+i,"gama",4) == 0)
10341 mng_info->ping_exclude_gAMA=MagickFalse;
10343 if (LocaleNCompare(value+i,"offs",4) == 0)
10344 mng_info->ping_exclude_oFFs=MagickFalse;
10346 if (LocaleNCompare(value+i,"phys",4) == 0)
10347 mng_info->ping_exclude_pHYs=MagickFalse;
10349 if (LocaleNCompare(value+i,"srgb",4) == 0)
10350 mng_info->ping_exclude_sRGB=MagickFalse;
10352 if (LocaleNCompare(value+i,"text",4) == 0)
10353 mng_info->ping_exclude_tEXt=MagickFalse;
10355 if (LocaleNCompare(value+i,"trns",4) == 0)
10356 mng_info->ping_exclude_tRNS=MagickFalse;
10358 if (LocaleNCompare(value+i,"vpag",4) == 0)
10359 mng_info->ping_exclude_vpAg=MagickFalse;
10361 if (LocaleNCompare(value+i,"zccp",4) == 0)
10362 mng_info->ping_exclude_zCCP=MagickFalse;
10364 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10365 mng_info->ping_exclude_zTXt=MagickFalse;
10371 if (excluding != MagickFalse && logging != MagickFalse)
10373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " Chunks to be excluded from the output PNG:");
10375 if (mng_info->ping_exclude_bKGD != MagickFalse)
10376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378 if (mng_info->ping_exclude_cHRM != MagickFalse)
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 if (mng_info->ping_exclude_EXIF != MagickFalse)
10382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 if (mng_info->ping_exclude_gAMA != MagickFalse)
10385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 if (mng_info->ping_exclude_iCCP != MagickFalse)
10388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391 if (mng_info->ping_exclude_iTXt != MagickFalse)
10392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10395 if (mng_info->ping_exclude_oFFs != MagickFalse)
10396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10398 if (mng_info->ping_exclude_pHYs != MagickFalse)
10399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10401 if (mng_info->ping_exclude_sRGB != MagickFalse)
10402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404 if (mng_info->ping_exclude_tEXt != MagickFalse)
10405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10407 if (mng_info->ping_exclude_tRNS != MagickFalse)
10408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 if (mng_info->ping_exclude_vpAg != MagickFalse)
10411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413 if (mng_info->ping_exclude_zCCP != MagickFalse)
10414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 if (mng_info->ping_exclude_zTXt != MagickFalse)
10417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421 mng_info->need_blob = MagickTrue;
10423 status=WriteOnePNGImage(mng_info,image_info,image);
10425 MngInfoFreeStruct(mng_info,&have_mng_structure);
10427 if (logging != MagickFalse)
10428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10433 #if defined(JNG_SUPPORTED)
10435 /* Write one JNG image */
10436 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10437 const ImageInfo *image_info,Image *image)
10458 jng_alpha_compression_method,
10459 jng_alpha_sample_depth,
10466 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10467 " Enter WriteOneJNGImage()");
10469 blob=(unsigned char *) NULL;
10470 jpeg_image=(Image *) NULL;
10471 jpeg_image_info=(ImageInfo *) NULL;
10474 transparent=image_info->type==GrayscaleMatteType ||
10475 image_info->type==TrueColorMatteType;
10477 jng_alpha_sample_depth=0;
10478 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10479 jng_alpha_compression_method=0;
10481 if (image->matte != MagickFalse)
10483 /* if any pixels are transparent */
10484 transparent=MagickTrue;
10485 if (image_info->compression==JPEGCompression)
10486 jng_alpha_compression_method=8;
10493 /* Create JPEG blob, image, and image_info */
10494 if (logging != MagickFalse)
10495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10496 " Creating jpeg_image_info for opacity.");
10498 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10500 if (jpeg_image_info == (ImageInfo *) NULL)
10501 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10503 if (logging != MagickFalse)
10504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505 " Creating jpeg_image.");
10507 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10509 if (jpeg_image == (Image *) NULL)
10510 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10512 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10513 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10514 status=NegateImage(jpeg_image,MagickFalse);
10515 jpeg_image->matte=MagickFalse;
10517 if (jng_quality >= 1000)
10518 jpeg_image_info->quality=jng_quality/1000;
10521 jpeg_image_info->quality=jng_quality;
10523 jpeg_image_info->type=GrayscaleType;
10524 (void) SetImageType(jpeg_image,GrayscaleType);
10525 (void) AcquireUniqueFilename(jpeg_image->filename);
10526 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10527 "%s",jpeg_image->filename);
10530 /* To do: check bit depth of PNG alpha channel */
10532 /* Check if image is grayscale. */
10533 if (image_info->type != TrueColorMatteType && image_info->type !=
10534 TrueColorType && ImageIsGray(image))
10539 if (jng_alpha_compression_method==0)
10544 /* Encode opacity as a grayscale PNG blob */
10545 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10546 &image->exception);
10547 if (logging != MagickFalse)
10548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549 " Creating PNG blob.");
10552 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10553 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10554 jpeg_image_info->interlace=NoInterlace;
10556 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10557 &image->exception);
10559 /* Retrieve sample depth used */
10560 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10561 if (value != (char *) NULL)
10562 jng_alpha_sample_depth= (unsigned int) value[0];
10566 /* Encode opacity as a grayscale JPEG blob */
10568 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10569 &image->exception);
10571 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10572 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10573 jpeg_image_info->interlace=NoInterlace;
10574 if (logging != MagickFalse)
10575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576 " Creating blob.");
10577 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10578 &image->exception);
10579 jng_alpha_sample_depth=8;
10581 if (logging != MagickFalse)
10582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583 " Successfully read jpeg_image into a blob, length=%.20g.",
10587 /* Destroy JPEG image and image_info */
10588 jpeg_image=DestroyImage(jpeg_image);
10589 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10590 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10593 /* Write JHDR chunk */
10594 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10595 PNGType(chunk,mng_JHDR);
10596 LogPNGChunk(logging,mng_JHDR,16L);
10597 PNGLong(chunk+4,(png_uint_32) image->columns);
10598 PNGLong(chunk+8,(png_uint_32) image->rows);
10599 chunk[12]=jng_color_type;
10600 chunk[13]=8; /* sample depth */
10601 chunk[14]=8; /*jng_image_compression_method */
10602 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10603 chunk[16]=jng_alpha_sample_depth;
10604 chunk[17]=jng_alpha_compression_method;
10605 chunk[18]=0; /*jng_alpha_filter_method */
10606 chunk[19]=0; /*jng_alpha_interlace_method */
10607 (void) WriteBlob(image,20,chunk);
10608 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10609 if (logging != MagickFalse)
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612 " JNG width:%15lu",(unsigned long) image->columns);
10614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615 " JNG height:%14lu",(unsigned long) image->rows);
10617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618 " JNG color type:%10d",jng_color_type);
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621 " JNG sample depth:%8d",8);
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " JNG compression:%9d",8);
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 " JNG interlace:%11d",0);
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " JNG alpha compression:%3d",jng_alpha_compression_method);
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " JNG alpha filter:%8d",0);
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " JNG alpha interlace:%5d",0);
10642 /* Write any JNG-chunk-b profiles */
10643 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10646 Write leading ancillary chunks
10652 Write JNG bKGD chunk
10663 if (jng_color_type == 8 || jng_color_type == 12)
10667 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10668 PNGType(chunk,mng_bKGD);
10669 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10670 red=ScaleQuantumToChar(image->background_color.red);
10671 green=ScaleQuantumToChar(image->background_color.green);
10672 blue=ScaleQuantumToChar(image->background_color.blue);
10679 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10680 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10683 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10686 Write JNG sRGB chunk
10688 (void) WriteBlobMSBULong(image,1L);
10689 PNGType(chunk,mng_sRGB);
10690 LogPNGChunk(logging,mng_sRGB,1L);
10692 if (image->rendering_intent != UndefinedIntent)
10693 chunk[4]=(unsigned char)
10694 Magick_RenderingIntent_to_PNG_RenderingIntent(
10695 (image->rendering_intent));
10698 chunk[4]=(unsigned char)
10699 Magick_RenderingIntent_to_PNG_RenderingIntent(
10700 (PerceptualIntent));
10702 (void) WriteBlob(image,5,chunk);
10703 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10707 if (image->gamma != 0.0)
10710 Write JNG gAMA chunk
10712 (void) WriteBlobMSBULong(image,4L);
10713 PNGType(chunk,mng_gAMA);
10714 LogPNGChunk(logging,mng_gAMA,4L);
10715 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10716 (void) WriteBlob(image,8,chunk);
10717 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10720 if ((mng_info->equal_chrms == MagickFalse) &&
10721 (image->chromaticity.red_primary.x != 0.0))
10727 Write JNG cHRM chunk
10729 (void) WriteBlobMSBULong(image,32L);
10730 PNGType(chunk,mng_cHRM);
10731 LogPNGChunk(logging,mng_cHRM,32L);
10732 primary=image->chromaticity.white_point;
10733 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10734 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10735 primary=image->chromaticity.red_primary;
10736 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10737 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10738 primary=image->chromaticity.green_primary;
10739 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10740 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10741 primary=image->chromaticity.blue_primary;
10742 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10743 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10744 (void) WriteBlob(image,36,chunk);
10745 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10749 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10752 Write JNG pHYs chunk
10754 (void) WriteBlobMSBULong(image,9L);
10755 PNGType(chunk,mng_pHYs);
10756 LogPNGChunk(logging,mng_pHYs,9L);
10757 if (image->units == PixelsPerInchResolution)
10759 PNGLong(chunk+4,(png_uint_32)
10760 (image->x_resolution*100.0/2.54+0.5));
10762 PNGLong(chunk+8,(png_uint_32)
10763 (image->y_resolution*100.0/2.54+0.5));
10770 if (image->units == PixelsPerCentimeterResolution)
10772 PNGLong(chunk+4,(png_uint_32)
10773 (image->x_resolution*100.0+0.5));
10775 PNGLong(chunk+8,(png_uint_32)
10776 (image->y_resolution*100.0+0.5));
10783 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10784 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10788 (void) WriteBlob(image,13,chunk);
10789 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10792 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10795 Write JNG oFFs chunk
10797 (void) WriteBlobMSBULong(image,9L);
10798 PNGType(chunk,mng_oFFs);
10799 LogPNGChunk(logging,mng_oFFs,9L);
10800 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10801 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10803 (void) WriteBlob(image,13,chunk);
10804 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10806 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10808 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10809 PNGType(chunk,mng_vpAg);
10810 LogPNGChunk(logging,mng_vpAg,9L);
10811 PNGLong(chunk+4,(png_uint_32) image->page.width);
10812 PNGLong(chunk+8,(png_uint_32) image->page.height);
10813 chunk[12]=0; /* unit = pixels */
10814 (void) WriteBlob(image,13,chunk);
10815 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10821 if (jng_alpha_compression_method==0)
10829 /* Write IDAT chunk header */
10830 if (logging != MagickFalse)
10831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10832 " Write IDAT chunks from blob, length=%.20g.",(double)
10835 /* Copy IDAT chunks */
10838 for (i=8; i<(ssize_t) length; i+=len+12)
10840 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10843 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10845 /* Found an IDAT chunk. */
10846 (void) WriteBlobMSBULong(image,(size_t) len);
10847 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10848 (void) WriteBlob(image,(size_t) len+4,p);
10849 (void) WriteBlobMSBULong(image,
10850 crc32(0,p,(uInt) len+4));
10855 if (logging != MagickFalse)
10856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10857 " Skipping %c%c%c%c chunk, length=%.20g.",
10858 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10865 /* Write JDAA chunk header */
10866 if (logging != MagickFalse)
10867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10868 " Write JDAA chunk, length=%.20g.",(double) length);
10869 (void) WriteBlobMSBULong(image,(size_t) length);
10870 PNGType(chunk,mng_JDAA);
10871 LogPNGChunk(logging,mng_JDAA,length);
10872 /* Write JDAT chunk(s) data */
10873 (void) WriteBlob(image,4,chunk);
10874 (void) WriteBlob(image,length,blob);
10875 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10878 blob=(unsigned char *) RelinquishMagickMemory(blob);
10881 /* Encode image as a JPEG blob */
10882 if (logging != MagickFalse)
10883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884 " Creating jpeg_image_info.");
10885 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10886 if (jpeg_image_info == (ImageInfo *) NULL)
10887 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10889 if (logging != MagickFalse)
10890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10891 " Creating jpeg_image.");
10893 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10894 if (jpeg_image == (Image *) NULL)
10895 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10896 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10898 (void) AcquireUniqueFilename(jpeg_image->filename);
10899 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10900 jpeg_image->filename);
10902 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10903 &image->exception);
10905 if (logging != MagickFalse)
10906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10907 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10908 (double) jpeg_image->rows);
10910 if (jng_color_type == 8 || jng_color_type == 12)
10911 jpeg_image_info->type=GrayscaleType;
10913 jpeg_image_info->quality=jng_quality % 1000;
10914 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10915 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10917 if (logging != MagickFalse)
10918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10919 " Creating blob.");
10921 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10923 if (logging != MagickFalse)
10925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10926 " Successfully read jpeg_image into a blob, length=%.20g.",
10929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930 " Write JDAT chunk, length=%.20g.",(double) length);
10933 /* Write JDAT chunk(s) */
10934 (void) WriteBlobMSBULong(image,(size_t) length);
10935 PNGType(chunk,mng_JDAT);
10936 LogPNGChunk(logging,mng_JDAT,length);
10937 (void) WriteBlob(image,4,chunk);
10938 (void) WriteBlob(image,length,blob);
10939 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10941 jpeg_image=DestroyImage(jpeg_image);
10942 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10943 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10944 blob=(unsigned char *) RelinquishMagickMemory(blob);
10946 /* Write any JNG-chunk-e profiles */
10947 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10949 /* Write IEND chunk */
10950 (void) WriteBlobMSBULong(image,0L);
10951 PNGType(chunk,mng_IEND);
10952 LogPNGChunk(logging,mng_IEND,0);
10953 (void) WriteBlob(image,4,chunk);
10954 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10956 if (logging != MagickFalse)
10957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10958 " exit WriteOneJNGImage()");
10965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10969 % W r i t e J N G I m a g e %
10973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10975 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10977 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10979 % The format of the WriteJNGImage method is:
10981 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10983 % A description of each parameter follows:
10985 % o image_info: the image info.
10987 % o image: The image.
10989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10991 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10994 have_mng_structure,
11004 assert(image_info != (const ImageInfo *) NULL);
11005 assert(image_info->signature == MagickSignature);
11006 assert(image != (Image *) NULL);
11007 assert(image->signature == MagickSignature);
11008 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11009 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11010 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11011 if (status == MagickFalse)
11015 Allocate a MngInfo structure.
11017 have_mng_structure=MagickFalse;
11018 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11019 if (mng_info == (MngInfo *) NULL)
11020 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11022 Initialize members of the MngInfo structure.
11024 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11025 mng_info->image=image;
11026 have_mng_structure=MagickTrue;
11028 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11030 status=WriteOneJNGImage(mng_info,image_info,image);
11031 (void) CloseBlob(image);
11033 (void) CatchImageException(image);
11034 MngInfoFreeStruct(mng_info,&have_mng_structure);
11035 if (logging != MagickFalse)
11036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11043 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11052 have_mng_structure,
11055 volatile MagickBooleanType
11067 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11068 defined(PNG_MNG_FEATURES_SUPPORTED)
11071 all_images_are_gray,
11081 volatile unsigned int
11092 #if (PNG_LIBPNG_VER < 10200)
11093 if (image_info->verbose)
11094 printf("Your PNG library (libpng-%s) is rather old.\n",
11095 PNG_LIBPNG_VER_STRING);
11101 assert(image_info != (const ImageInfo *) NULL);
11102 assert(image_info->signature == MagickSignature);
11103 assert(image != (Image *) NULL);
11104 assert(image->signature == MagickSignature);
11105 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11106 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11107 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11108 if (status == MagickFalse)
11112 Allocate a MngInfo structure.
11114 have_mng_structure=MagickFalse;
11115 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11116 if (mng_info == (MngInfo *) NULL)
11117 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11119 Initialize members of the MngInfo structure.
11121 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11122 mng_info->image=image;
11123 have_mng_structure=MagickTrue;
11124 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11127 * See if user has requested a specific PNG subformat to be used
11128 * for all of the PNGs in the MNG being written, e.g.,
11130 * convert *.png png8:animation.mng
11132 * To do: check -define png:bit_depth and png:color_type as well,
11133 * or perhaps use mng:bit_depth and mng:color_type instead for
11137 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11138 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11139 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11141 write_jng=MagickFalse;
11142 if (image_info->compression == JPEGCompression)
11143 write_jng=MagickTrue;
11145 mng_info->adjoin=image_info->adjoin &&
11146 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11148 if (logging != MagickFalse)
11150 /* Log some info about the input */
11154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155 " Checking input image(s)");
11157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11158 " Image_info depth: %.20g",(double) image_info->depth);
11160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11161 " Type: %d",image_info->type);
11164 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11167 " Scene: %.20g",(double) scene++);
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170 " Image depth: %.20g",(double) p->depth);
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11180 if (p->storage_class == PseudoClass)
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182 " Storage class: PseudoClass");
11185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186 " Storage class: DirectClass");
11189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190 " Number of colors: %.20g",(double) p->colors);
11193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194 " Number of colors: unspecified");
11196 if (mng_info->adjoin == MagickFalse)
11201 use_global_plte=MagickFalse;
11202 all_images_are_gray=MagickFalse;
11203 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11204 need_local_plte=MagickTrue;
11206 need_defi=MagickFalse;
11207 need_matte=MagickFalse;
11208 mng_info->framing_mode=1;
11209 mng_info->old_framing_mode=1;
11212 if (image_info->page != (char *) NULL)
11215 Determine image bounding box.
11217 SetGeometry(image,&mng_info->page);
11218 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11219 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11231 mng_info->page=image->page;
11232 need_geom=MagickTrue;
11233 if (mng_info->page.width || mng_info->page.height)
11234 need_geom=MagickFalse;
11236 Check all the scenes.
11238 initial_delay=image->delay;
11239 need_iterations=MagickFalse;
11240 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11241 mng_info->equal_physs=MagickTrue,
11242 mng_info->equal_gammas=MagickTrue;
11243 mng_info->equal_srgbs=MagickTrue;
11244 mng_info->equal_backgrounds=MagickTrue;
11246 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11247 defined(PNG_MNG_FEATURES_SUPPORTED)
11248 all_images_are_gray=MagickTrue;
11249 mng_info->equal_palettes=MagickFalse;
11250 need_local_plte=MagickFalse;
11252 for (next_image=image; next_image != (Image *) NULL; )
11256 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11257 mng_info->page.width=next_image->columns+next_image->page.x;
11259 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11260 mng_info->page.height=next_image->rows+next_image->page.y;
11263 if (next_image->page.x || next_image->page.y)
11264 need_defi=MagickTrue;
11266 if (next_image->matte)
11267 need_matte=MagickTrue;
11269 if ((int) next_image->dispose >= BackgroundDispose)
11270 if (next_image->matte || next_image->page.x || next_image->page.y ||
11271 ((next_image->columns < mng_info->page.width) &&
11272 (next_image->rows < mng_info->page.height)))
11273 mng_info->need_fram=MagickTrue;
11275 if (next_image->iterations)
11276 need_iterations=MagickTrue;
11278 final_delay=next_image->delay;
11280 if (final_delay != initial_delay || final_delay > 1UL*
11281 next_image->ticks_per_second)
11282 mng_info->need_fram=1;
11284 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11285 defined(PNG_MNG_FEATURES_SUPPORTED)
11287 check for global palette possibility.
11289 if (image->matte != MagickFalse)
11290 need_local_plte=MagickTrue;
11292 if (need_local_plte == 0)
11294 if (ImageIsGray(image) == MagickFalse)
11295 all_images_are_gray=MagickFalse;
11296 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11297 if (use_global_plte == 0)
11298 use_global_plte=mng_info->equal_palettes;
11299 need_local_plte=!mng_info->equal_palettes;
11302 if (GetNextImageInList(next_image) != (Image *) NULL)
11304 if (next_image->background_color.red !=
11305 next_image->next->background_color.red ||
11306 next_image->background_color.green !=
11307 next_image->next->background_color.green ||
11308 next_image->background_color.blue !=
11309 next_image->next->background_color.blue)
11310 mng_info->equal_backgrounds=MagickFalse;
11312 if (next_image->gamma != next_image->next->gamma)
11313 mng_info->equal_gammas=MagickFalse;
11315 if (next_image->rendering_intent !=
11316 next_image->next->rendering_intent)
11317 mng_info->equal_srgbs=MagickFalse;
11319 if ((next_image->units != next_image->next->units) ||
11320 (next_image->x_resolution != next_image->next->x_resolution) ||
11321 (next_image->y_resolution != next_image->next->y_resolution))
11322 mng_info->equal_physs=MagickFalse;
11324 if (mng_info->equal_chrms)
11326 if (next_image->chromaticity.red_primary.x !=
11327 next_image->next->chromaticity.red_primary.x ||
11328 next_image->chromaticity.red_primary.y !=
11329 next_image->next->chromaticity.red_primary.y ||
11330 next_image->chromaticity.green_primary.x !=
11331 next_image->next->chromaticity.green_primary.x ||
11332 next_image->chromaticity.green_primary.y !=
11333 next_image->next->chromaticity.green_primary.y ||
11334 next_image->chromaticity.blue_primary.x !=
11335 next_image->next->chromaticity.blue_primary.x ||
11336 next_image->chromaticity.blue_primary.y !=
11337 next_image->next->chromaticity.blue_primary.y ||
11338 next_image->chromaticity.white_point.x !=
11339 next_image->next->chromaticity.white_point.x ||
11340 next_image->chromaticity.white_point.y !=
11341 next_image->next->chromaticity.white_point.y)
11342 mng_info->equal_chrms=MagickFalse;
11346 next_image=GetNextImageInList(next_image);
11348 if (image_count < 2)
11350 mng_info->equal_backgrounds=MagickFalse;
11351 mng_info->equal_chrms=MagickFalse;
11352 mng_info->equal_gammas=MagickFalse;
11353 mng_info->equal_srgbs=MagickFalse;
11354 mng_info->equal_physs=MagickFalse;
11355 use_global_plte=MagickFalse;
11356 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11357 need_local_plte=MagickTrue;
11359 need_iterations=MagickFalse;
11362 if (mng_info->need_fram == MagickFalse)
11365 Only certain framing rates 100/n are exactly representable without
11366 the FRAM chunk but we'll allow some slop in VLC files
11368 if (final_delay == 0)
11370 if (need_iterations != MagickFalse)
11373 It's probably a GIF with loop; don't run it *too* fast.
11375 if (mng_info->adjoin)
11378 (void) ThrowMagickException(&image->exception,
11379 GetMagickModule(),CoderWarning,
11380 "input has zero delay between all frames; assuming",
11385 mng_info->ticks_per_second=0;
11387 if (final_delay != 0)
11388 mng_info->ticks_per_second=(png_uint_32)
11389 (image->ticks_per_second/final_delay);
11390 if (final_delay > 50)
11391 mng_info->ticks_per_second=2;
11393 if (final_delay > 75)
11394 mng_info->ticks_per_second=1;
11396 if (final_delay > 125)
11397 mng_info->need_fram=MagickTrue;
11399 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11400 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11401 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11402 1UL*image->ticks_per_second))
11403 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11406 if (mng_info->need_fram != MagickFalse)
11407 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11409 If pseudocolor, we should also check to see if all the
11410 palettes are identical and write a global PLTE if they are.
11414 Write the MNG version 1.0 signature and MHDR chunk.
11416 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11417 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11418 PNGType(chunk,mng_MHDR);
11419 LogPNGChunk(logging,mng_MHDR,28L);
11420 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11421 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11422 PNGLong(chunk+12,mng_info->ticks_per_second);
11423 PNGLong(chunk+16,0L); /* layer count=unknown */
11424 PNGLong(chunk+20,0L); /* frame count=unknown */
11425 PNGLong(chunk+24,0L); /* play time=unknown */
11430 if (need_defi || mng_info->need_fram || use_global_plte)
11431 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11434 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11439 if (need_defi || mng_info->need_fram || use_global_plte)
11440 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11443 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11451 if (need_defi || mng_info->need_fram || use_global_plte)
11452 PNGLong(chunk+28,11L); /* simplicity=LC */
11455 PNGLong(chunk+28,9L); /* simplicity=VLC */
11460 if (need_defi || mng_info->need_fram || use_global_plte)
11461 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11464 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11467 (void) WriteBlob(image,32,chunk);
11468 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11469 option=GetImageOption(image_info,"mng:need-cacheoff");
11470 if (option != (const char *) NULL)
11476 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11478 PNGType(chunk,mng_nEED);
11479 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11480 (void) WriteBlobMSBULong(image,(size_t) length);
11481 LogPNGChunk(logging,mng_nEED,(size_t) length);
11483 (void) WriteBlob(image,length,chunk);
11484 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11486 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11487 (GetNextImageInList(image) != (Image *) NULL) &&
11488 (image->iterations != 1))
11491 Write MNG TERM chunk
11493 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11494 PNGType(chunk,mng_TERM);
11495 LogPNGChunk(logging,mng_TERM,10L);
11496 chunk[4]=3; /* repeat animation */
11497 chunk[5]=0; /* show last frame when done */
11498 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11499 final_delay/MagickMax(image->ticks_per_second,1)));
11501 if (image->iterations == 0)
11502 PNGLong(chunk+10,PNG_UINT_31_MAX);
11505 PNGLong(chunk+10,(png_uint_32) image->iterations);
11507 if (logging != MagickFalse)
11509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11510 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11511 final_delay/MagickMax(image->ticks_per_second,1)));
11513 if (image->iterations == 0)
11514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11515 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519 " Image iterations: %.20g",(double) image->iterations);
11521 (void) WriteBlob(image,14,chunk);
11522 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11525 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11527 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11528 mng_info->equal_srgbs)
11531 Write MNG sRGB chunk
11533 (void) WriteBlobMSBULong(image,1L);
11534 PNGType(chunk,mng_sRGB);
11535 LogPNGChunk(logging,mng_sRGB,1L);
11537 if (image->rendering_intent != UndefinedIntent)
11538 chunk[4]=(unsigned char)
11539 Magick_RenderingIntent_to_PNG_RenderingIntent(
11540 (image->rendering_intent));
11543 chunk[4]=(unsigned char)
11544 Magick_RenderingIntent_to_PNG_RenderingIntent(
11545 (PerceptualIntent));
11547 (void) WriteBlob(image,5,chunk);
11548 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11549 mng_info->have_write_global_srgb=MagickTrue;
11554 if (image->gamma && mng_info->equal_gammas)
11557 Write MNG gAMA chunk
11559 (void) WriteBlobMSBULong(image,4L);
11560 PNGType(chunk,mng_gAMA);
11561 LogPNGChunk(logging,mng_gAMA,4L);
11562 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11563 (void) WriteBlob(image,8,chunk);
11564 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11565 mng_info->have_write_global_gama=MagickTrue;
11567 if (mng_info->equal_chrms)
11573 Write MNG cHRM chunk
11575 (void) WriteBlobMSBULong(image,32L);
11576 PNGType(chunk,mng_cHRM);
11577 LogPNGChunk(logging,mng_cHRM,32L);
11578 primary=image->chromaticity.white_point;
11579 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11580 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11581 primary=image->chromaticity.red_primary;
11582 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11583 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11584 primary=image->chromaticity.green_primary;
11585 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11586 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11587 primary=image->chromaticity.blue_primary;
11588 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11589 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11590 (void) WriteBlob(image,36,chunk);
11591 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11592 mng_info->have_write_global_chrm=MagickTrue;
11595 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11598 Write MNG pHYs chunk
11600 (void) WriteBlobMSBULong(image,9L);
11601 PNGType(chunk,mng_pHYs);
11602 LogPNGChunk(logging,mng_pHYs,9L);
11604 if (image->units == PixelsPerInchResolution)
11606 PNGLong(chunk+4,(png_uint_32)
11607 (image->x_resolution*100.0/2.54+0.5));
11609 PNGLong(chunk+8,(png_uint_32)
11610 (image->y_resolution*100.0/2.54+0.5));
11617 if (image->units == PixelsPerCentimeterResolution)
11619 PNGLong(chunk+4,(png_uint_32)
11620 (image->x_resolution*100.0+0.5));
11622 PNGLong(chunk+8,(png_uint_32)
11623 (image->y_resolution*100.0+0.5));
11630 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11631 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11635 (void) WriteBlob(image,13,chunk);
11636 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11639 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11640 or does not cover the entire frame.
11642 if (write_mng && (image->matte || image->page.x > 0 ||
11643 image->page.y > 0 || (image->page.width &&
11644 (image->page.width+image->page.x < mng_info->page.width))
11645 || (image->page.height && (image->page.height+image->page.y
11646 < mng_info->page.height))))
11648 (void) WriteBlobMSBULong(image,6L);
11649 PNGType(chunk,mng_BACK);
11650 LogPNGChunk(logging,mng_BACK,6L);
11651 red=ScaleQuantumToShort(image->background_color.red);
11652 green=ScaleQuantumToShort(image->background_color.green);
11653 blue=ScaleQuantumToShort(image->background_color.blue);
11654 PNGShort(chunk+4,red);
11655 PNGShort(chunk+6,green);
11656 PNGShort(chunk+8,blue);
11657 (void) WriteBlob(image,10,chunk);
11658 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11659 if (mng_info->equal_backgrounds)
11661 (void) WriteBlobMSBULong(image,6L);
11662 PNGType(chunk,mng_bKGD);
11663 LogPNGChunk(logging,mng_bKGD,6L);
11664 (void) WriteBlob(image,10,chunk);
11665 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11669 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11670 if ((need_local_plte == MagickFalse) &&
11671 (image->storage_class == PseudoClass) &&
11672 (all_images_are_gray == MagickFalse))
11678 Write MNG PLTE chunk
11680 data_length=3*image->colors;
11681 (void) WriteBlobMSBULong(image,data_length);
11682 PNGType(chunk,mng_PLTE);
11683 LogPNGChunk(logging,mng_PLTE,data_length);
11685 for (i=0; i < (ssize_t) image->colors; i++)
11687 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11688 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11689 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11692 (void) WriteBlob(image,data_length+4,chunk);
11693 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11694 mng_info->have_write_global_plte=MagickTrue;
11700 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11701 defined(PNG_MNG_FEATURES_SUPPORTED)
11702 mng_info->equal_palettes=MagickFalse;
11706 if (mng_info->adjoin)
11708 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11709 defined(PNG_MNG_FEATURES_SUPPORTED)
11711 If we aren't using a global palette for the entire MNG, check to
11712 see if we can use one for two or more consecutive images.
11714 if (need_local_plte && use_global_plte && !all_images_are_gray)
11716 if (mng_info->IsPalette)
11719 When equal_palettes is true, this image has the same palette
11720 as the previous PseudoClass image
11722 mng_info->have_write_global_plte=mng_info->equal_palettes;
11723 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11724 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11727 Write MNG PLTE chunk
11732 data_length=3*image->colors;
11733 (void) WriteBlobMSBULong(image,data_length);
11734 PNGType(chunk,mng_PLTE);
11735 LogPNGChunk(logging,mng_PLTE,data_length);
11737 for (i=0; i < (ssize_t) image->colors; i++)
11739 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11740 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11741 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11744 (void) WriteBlob(image,data_length+4,chunk);
11745 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11746 (uInt) (data_length+4)));
11747 mng_info->have_write_global_plte=MagickTrue;
11751 mng_info->have_write_global_plte=MagickFalse;
11762 previous_x=mng_info->page.x;
11763 previous_y=mng_info->page.y;
11770 mng_info->page=image->page;
11771 if ((mng_info->page.x != previous_x) ||
11772 (mng_info->page.y != previous_y))
11774 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11775 PNGType(chunk,mng_DEFI);
11776 LogPNGChunk(logging,mng_DEFI,12L);
11777 chunk[4]=0; /* object 0 MSB */
11778 chunk[5]=0; /* object 0 LSB */
11779 chunk[6]=0; /* visible */
11780 chunk[7]=0; /* abstract */
11781 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11782 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11783 (void) WriteBlob(image,16,chunk);
11784 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11789 mng_info->write_mng=write_mng;
11791 if ((int) image->dispose >= 3)
11792 mng_info->framing_mode=3;
11794 if (mng_info->need_fram && mng_info->adjoin &&
11795 ((image->delay != mng_info->delay) ||
11796 (mng_info->framing_mode != mng_info->old_framing_mode)))
11798 if (image->delay == mng_info->delay)
11801 Write a MNG FRAM chunk with the new framing mode.
11803 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11804 PNGType(chunk,mng_FRAM);
11805 LogPNGChunk(logging,mng_FRAM,1L);
11806 chunk[4]=(unsigned char) mng_info->framing_mode;
11807 (void) WriteBlob(image,5,chunk);
11808 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11813 Write a MNG FRAM chunk with the delay.
11815 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11816 PNGType(chunk,mng_FRAM);
11817 LogPNGChunk(logging,mng_FRAM,10L);
11818 chunk[4]=(unsigned char) mng_info->framing_mode;
11819 chunk[5]=0; /* frame name separator (no name) */
11820 chunk[6]=2; /* flag for changing default delay */
11821 chunk[7]=0; /* flag for changing frame timeout */
11822 chunk[8]=0; /* flag for changing frame clipping */
11823 chunk[9]=0; /* flag for changing frame sync_id */
11824 PNGLong(chunk+10,(png_uint_32)
11825 ((mng_info->ticks_per_second*
11826 image->delay)/MagickMax(image->ticks_per_second,1)));
11827 (void) WriteBlob(image,14,chunk);
11828 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11829 mng_info->delay=(png_uint_32) image->delay;
11831 mng_info->old_framing_mode=mng_info->framing_mode;
11834 #if defined(JNG_SUPPORTED)
11835 if (image_info->compression == JPEGCompression)
11840 if (logging != MagickFalse)
11841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842 " Writing JNG object.");
11843 /* To do: specify the desired alpha compression method. */
11844 write_info=CloneImageInfo(image_info);
11845 write_info->compression=UndefinedCompression;
11846 status=WriteOneJNGImage(mng_info,write_info,image);
11847 write_info=DestroyImageInfo(write_info);
11852 if (logging != MagickFalse)
11853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11854 " Writing PNG object.");
11856 mng_info->need_blob = MagickFalse;
11858 /* We don't want any ancillary chunks written */
11859 mng_info->ping_exclude_bKGD=MagickTrue;
11860 mng_info->ping_exclude_cHRM=MagickTrue;
11861 mng_info->ping_exclude_EXIF=MagickTrue;
11862 mng_info->ping_exclude_gAMA=MagickTrue;
11863 mng_info->ping_exclude_cHRM=MagickTrue;
11864 mng_info->ping_exclude_iCCP=MagickTrue;
11865 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11866 mng_info->ping_exclude_oFFs=MagickTrue;
11867 mng_info->ping_exclude_pHYs=MagickTrue;
11868 mng_info->ping_exclude_sRGB=MagickTrue;
11869 mng_info->ping_exclude_tEXt=MagickTrue;
11870 mng_info->ping_exclude_tRNS=MagickTrue;
11871 mng_info->ping_exclude_vpAg=MagickTrue;
11872 mng_info->ping_exclude_zCCP=MagickTrue;
11873 mng_info->ping_exclude_zTXt=MagickTrue;
11875 status=WriteOnePNGImage(mng_info,image_info,image);
11878 if (status == MagickFalse)
11880 MngInfoFreeStruct(mng_info,&have_mng_structure);
11881 (void) CloseBlob(image);
11882 return(MagickFalse);
11884 (void) CatchImageException(image);
11885 if (GetNextImageInList(image) == (Image *) NULL)
11887 image=SyncNextImageInList(image);
11888 status=SetImageProgress(image,SaveImagesTag,scene++,
11889 GetImageListLength(image));
11891 if (status == MagickFalse)
11894 } while (mng_info->adjoin);
11898 while (GetPreviousImageInList(image) != (Image *) NULL)
11899 image=GetPreviousImageInList(image);
11901 Write the MEND chunk.
11903 (void) WriteBlobMSBULong(image,0x00000000L);
11904 PNGType(chunk,mng_MEND);
11905 LogPNGChunk(logging,mng_MEND,0L);
11906 (void) WriteBlob(image,4,chunk);
11907 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11910 Relinquish resources.
11912 (void) CloseBlob(image);
11913 MngInfoFreeStruct(mng_info,&have_mng_structure);
11915 if (logging != MagickFalse)
11916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11918 return(MagickTrue);
11920 #else /* PNG_LIBPNG_VER > 10011 */
11922 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11925 printf("Your PNG library is too old: You have libpng-%s\n",
11926 PNG_LIBPNG_VER_STRING);
11928 ThrowBinaryException(CoderError,"PNG library is too old",
11929 image_info->filename);
11932 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11934 return(WritePNGImage(image_info,image));
11936 #endif /* PNG_LIBPNG_VER > 10011 */