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 */
96 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
97 #define PNG_PTR_NORETURN
102 /* ImageMagick differences */
103 #define first_scene scene
105 #if PNG_LIBPNG_VER > 10011
107 Optional declarations. Define or undefine them as you like.
109 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
112 Features under construction. Define these to work on them.
114 #undef MNG_OBJECT_BUFFERS
115 #undef MNG_BASI_SUPPORTED
116 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
118 #if defined(MAGICKCORE_JPEG_DELEGATE)
119 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
121 #if !defined(RGBColorMatchExact)
122 #define IsPNGColorEqual(color,target) \
123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
129 Establish thread safety.
130 setjmp/longjmp is claimed to be safe on these platforms:
131 setjmp/longjmp is alleged to be unsafe on these platforms:
133 #ifndef SETJMP_IS_THREAD_SAFE
134 #define PNG_SETJMP_NOT_THREAD_SAFE
137 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
139 *ping_semaphore = (SemaphoreInfo *) NULL;
143 This temporary until I set up malloc'ed object attributes array.
144 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
147 #define MNG_MAX_OBJECTS 256
150 If this not defined, spec is interpreted strictly. If it is
151 defined, an attempt will be made to recover from some errors,
153 o global PLTE too short
158 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
159 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
160 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
161 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
162 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
163 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
164 will be enabled by default in libpng-1.2.0.
166 #ifdef PNG_MNG_FEATURES_SUPPORTED
167 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
168 # define PNG_READ_EMPTY_PLTE_SUPPORTED
170 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
171 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
176 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
177 This macro is only defined in libpng-1.0.3 and later.
178 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
180 #ifndef PNG_UINT_31_MAX
181 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
185 Constant strings for known chunk types. If you need to add a chunk,
186 add a string holding the name here. To make the code more
187 portable, we use ASCII numbers like this, not characters.
190 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
191 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
192 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
193 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
194 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
195 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
196 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
197 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
198 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
199 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
200 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
201 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
202 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
203 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
204 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
205 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
206 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
207 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
208 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
209 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
210 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
211 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
212 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
213 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
214 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
215 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
216 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
217 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
218 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
219 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
220 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
221 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
222 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
223 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
225 #if defined(JNG_SUPPORTED)
226 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
227 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
228 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
229 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
230 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
231 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
235 Other known chunks that are not yet supported by ImageMagick:
236 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
237 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
238 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
239 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
240 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
241 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
242 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
243 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
246 typedef struct _MngBox
255 typedef struct _MngPair
262 #ifdef MNG_OBJECT_BUFFERS
263 typedef struct _MngBuffer
295 typedef struct _MngInfo
298 #ifdef MNG_OBJECT_BUFFERS
300 *ob[MNG_MAX_OBJECTS];
311 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
312 bytes_in_read_buffer,
318 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
319 defined(PNG_MNG_FEATURES_SUPPORTED)
331 have_saved_bkgd_index,
332 have_write_global_chrm,
333 have_write_global_gama,
334 have_write_global_plte,
335 have_write_global_srgb,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
356 object_clip[MNG_MAX_OBJECTS];
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
376 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
405 global_phys_unit_type,
424 #ifdef MNG_BASI_SUPPORTED
432 basi_compression_method,
434 basi_interlace_method,
457 /* Added at version 6.6.6-7 */
465 /* ping_exclude_iTXt, */
472 ping_exclude_zCCP, /* hex-encoded iCCP */
474 ping_preserve_colormap;
480 Forward declarations.
482 static MagickBooleanType
483 WritePNGImage(const ImageInfo *,Image *);
485 static MagickBooleanType
486 WriteMNGImage(const ImageInfo *,Image *);
488 #if defined(JNG_SUPPORTED)
489 static MagickBooleanType
490 WriteJNGImage(const ImageInfo *,Image *);
493 #if PNG_LIBPNG_VER > 10011
496 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
497 static MagickBooleanType
498 LosslessReduceDepthOK(Image *image)
500 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
502 * This is true if the high byte and the next highest byte of
503 * each sample of the image, the colormap, and the background color
504 * are equal to each other. We check this by seeing if the samples
505 * are unchanged when we scale them down to 8 and back up to Quantum.
507 * We don't use the method GetImageDepth() because it doesn't check
508 * background and doesn't handle PseudoClass specially.
511 #define ScaleQuantumToCharToQuantum(quantum) \
512 (ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum)))
515 ok_to_reduce=MagickFalse;
517 if (image->depth >= 16)
524 (image->background_color.red ==
525 ScaleQuantumToCharToQuantum(image->background_color.red) &&
526 image->background_color.green ==
527 ScaleQuantumToCharToQuantum(image->background_color.green) &&
528 image->background_color.blue ==
529 ScaleQuantumToCharToQuantum(image->background_color.blue)) ?
530 MagickTrue : MagickFalse;
532 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
536 for (indx=0; indx < (ssize_t) image->colors; indx++)
539 image->colormap[indx].red ==
540 ScaleQuantumToCharToQuantum(image->colormap[indx].red) &&
541 image->colormap[indx].green ==
542 ScaleQuantumToCharToQuantum(image->colormap[indx].green) &&
543 image->colormap[indx].blue ==
544 ScaleQuantumToCharToQuantum(image->colormap[indx].blue) &&
545 (image->matte == MagickFalse ||
546 image->colormap[indx].opacity ==
547 ScaleQuantumToCharToQuantum(image->colormap[indx].opacity)))
548 ? MagickTrue : MagickFalse;
550 if (ok_to_reduce == MagickFalse)
555 if ((ok_to_reduce != MagickFalse) &&
556 (image->storage_class != PseudoClass))
564 for (y=0; y < (ssize_t) image->rows; y++)
566 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
568 if (p == (const PixelPacket *) NULL)
570 ok_to_reduce = MagickFalse;
574 for (x=(ssize_t) image->columns-1; x >= 0; x--)
577 (p->red == ScaleQuantumToCharToQuantum(p->red) &&
578 p->green == ScaleQuantumToCharToQuantum(p->green) &&
579 p->blue == ScaleQuantumToCharToQuantum(p->blue) &&
580 (image->matte == MagickFalse ||
581 p->opacity == ScaleQuantumToCharToQuantum(p->opacity)))
582 ? MagickTrue : MagickFalse;
584 if (ok_to_reduce == MagickFalse)
594 if (ok_to_reduce != MagickFalse)
596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
597 " OK to reduce PNG bit depth to 8 without loss of info");
601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
602 " Not OK to reduce PNG bit depth to 8 without loss of info");
608 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
611 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
615 case PerceptualIntent:
621 case SaturationIntent:
632 static RenderingIntent
633 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
638 return PerceptualIntent;
641 return RelativeIntent;
644 return SaturationIntent;
647 return AbsoluteIntent;
650 return UndefinedIntent;
654 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
662 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 % I m a g e I s G r a y %
680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682 % Like IsGrayImage except does not change DirectClass to PseudoClass %
684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
686 static MagickBooleanType ImageIsGray(Image *image)
688 register const PixelPacket
696 assert(image != (Image *) NULL);
697 assert(image->signature == MagickSignature);
698 if (image->debug != MagickFalse)
699 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
701 if (image->storage_class == PseudoClass)
703 for (i=0; i < (ssize_t) image->colors; i++)
704 if (IsGray(image->colormap+i) == MagickFalse)
708 for (y=0; y < (ssize_t) image->rows; y++)
710 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
711 if (p == (const PixelPacket *) NULL)
713 for (x=(ssize_t) image->columns-1; x >= 0; x--)
715 if (IsGray(p) == MagickFalse)
722 #endif /* PNG_LIBPNG_VER > 10011 */
723 #endif /* MAGICKCORE_PNG_DELEGATE */
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736 % IsMNG() returns MagickTrue if the image format type, identified by the
737 % magick string, is MNG.
739 % The format of the IsMNG method is:
741 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
743 % A description of each parameter follows:
745 % o magick: compare image format pattern against these bytes.
747 % o length: Specifies the length of the magick string.
751 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
756 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773 % IsJNG() returns MagickTrue if the image format type, identified by the
774 % magick string, is JNG.
776 % The format of the IsJNG method is:
778 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
780 % A description of each parameter follows:
782 % o magick: compare image format pattern against these bytes.
784 % o length: Specifies the length of the magick string.
788 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
793 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810 % IsPNG() returns MagickTrue if the image format type, identified by the
811 % magick string, is PNG.
813 % The format of the IsPNG method is:
815 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
817 % A description of each parameter follows:
819 % o magick: compare image format pattern against these bytes.
821 % o length: Specifies the length of the magick string.
824 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
829 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
835 #if defined(MAGICKCORE_PNG_DELEGATE)
836 #if defined(__cplusplus) || defined(c_plusplus)
840 #if (PNG_LIBPNG_VER > 10011)
841 static size_t WriteBlobMSBULong(Image *image,const size_t value)
846 assert(image != (Image *) NULL);
847 assert(image->signature == MagickSignature);
848 buffer[0]=(unsigned char) (value >> 24);
849 buffer[1]=(unsigned char) (value >> 16);
850 buffer[2]=(unsigned char) (value >> 8);
851 buffer[3]=(unsigned char) value;
852 return((size_t) WriteBlob(image,4,buffer));
855 static void PNGLong(png_bytep p,png_uint_32 value)
857 *p++=(png_byte) ((value >> 24) & 0xff);
858 *p++=(png_byte) ((value >> 16) & 0xff);
859 *p++=(png_byte) ((value >> 8) & 0xff);
860 *p++=(png_byte) (value & 0xff);
863 #if defined(JNG_SUPPORTED)
864 static void PNGsLong(png_bytep p,png_int_32 value)
866 *p++=(png_byte) ((value >> 24) & 0xff);
867 *p++=(png_byte) ((value >> 16) & 0xff);
868 *p++=(png_byte) ((value >> 8) & 0xff);
869 *p++=(png_byte) (value & 0xff);
873 static void PNGShort(png_bytep p,png_uint_16 value)
875 *p++=(png_byte) ((value >> 8) & 0xff);
876 *p++=(png_byte) (value & 0xff);
879 static void PNGType(png_bytep p,png_bytep type)
881 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
884 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
887 if (logging != MagickFalse)
888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
889 " Writing %c%c%c%c chunk, length: %.20g",
890 type[0],type[1],type[2],type[3],(double) length);
892 #endif /* PNG_LIBPNG_VER > 10011 */
894 #if defined(__cplusplus) || defined(c_plusplus)
898 #if PNG_LIBPNG_VER > 10011
900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
904 % R e a d P N G I m a g e %
908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
911 % Multiple-image Network Graphics (MNG) image file and returns it. It
912 % allocates the memory necessary for the new Image structure and returns a
913 % pointer to the new image or set of images.
915 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
917 % The format of the ReadPNGImage method is:
919 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
921 % A description of each parameter follows:
923 % o image_info: the image info.
925 % o exception: return any errors or warnings in this structure.
927 % To do, more or less in chronological order (as of version 5.5.2,
928 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
930 % Get 16-bit cheap transparency working.
932 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
934 % Preserve all unknown and not-yet-handled known chunks found in input
935 % PNG file and copy them into output PNG files according to the PNG
938 % (At this point, PNG encoding should be in full MNG compliance)
940 % Provide options for choice of background to use when the MNG BACK
941 % chunk is not present or is not mandatory (i.e., leave transparent,
942 % user specified, MNG BACK, PNG bKGD)
944 % Implement LOOP/ENDL [done, but could do discretionary loops more
945 % efficiently by linking in the duplicate frames.].
947 % Decode and act on the MHDR simplicity profile (offer option to reject
948 % files or attempt to process them anyway when the profile isn't LC or VLC).
950 % Upgrade to full MNG without Delta-PNG.
952 % o BACK [done a while ago except for background image ID]
953 % o MOVE [done 15 May 1999]
954 % o CLIP [done 15 May 1999]
955 % o DISC [done 19 May 1999]
956 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
957 % o SEEK [partially done 19 May 1999 (discard function only)]
961 % o MNG-level tEXt/iTXt/zTXt
966 % o iTXt (wait for libpng implementation).
968 % Use the scene signature to discover when an identical scene is
969 % being reused, and just point to the original image->exception instead
970 % of storing another set of pixels. This not specific to MNG
971 % but could be applied generally.
973 % Upgrade to full MNG with Delta-PNG.
977 % We will not attempt to read files containing the CgBI chunk.
978 % They are really Xcode files meant for display on the iPhone.
979 % These are not valid PNG files and it is impossible to recover
980 % the orginal PNG from files that have been converted to Xcode-PNG,
981 % since irretrievable loss of color data has occurred due to the
982 % use of premultiplied alpha.
985 #if defined(__cplusplus) || defined(c_plusplus)
990 This the function that does the actual reading of data. It is
991 the same as the one supplied in libpng, except that it receives the
992 datastream from the ReadBlob() function instead of standard input.
994 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
999 image=(Image *) png_get_io_ptr(png_ptr);
1005 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1006 if (check != length)
1011 (void) FormatMagickString(msg,MaxTextExtent,
1012 "Expected %.20g bytes; found %.20g bytes",(double) length,
1014 png_warning(png_ptr,msg);
1015 png_error(png_ptr,"Read Exception");
1020 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1021 !defined(PNG_MNG_FEATURES_SUPPORTED)
1022 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1023 * older than libpng-1.0.3a, which was the first to allow the empty
1024 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1025 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1026 * encountered after an empty PLTE, so we have to look ahead for bKGD
1027 * chunks and remove them from the datastream that is passed to libpng,
1028 * and store their contents for later use.
1030 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1045 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1046 image=(Image *) mng_info->image;
1047 while (mng_info->bytes_in_read_buffer && length)
1049 data[i]=mng_info->read_buffer[i];
1050 mng_info->bytes_in_read_buffer--;
1056 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1058 if (check != length)
1059 png_error(png_ptr,"Read Exception");
1063 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1066 check=(png_size_t) ReadBlob(image,(size_t) length,
1067 (char *) mng_info->read_buffer);
1068 mng_info->read_buffer[4]=0;
1069 mng_info->bytes_in_read_buffer=4;
1070 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1071 mng_info->found_empty_plte=MagickTrue;
1072 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1074 mng_info->found_empty_plte=MagickFalse;
1075 mng_info->have_saved_bkgd_index=MagickFalse;
1079 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1082 check=(png_size_t) ReadBlob(image,(size_t) length,
1083 (char *) mng_info->read_buffer);
1084 mng_info->read_buffer[4]=0;
1085 mng_info->bytes_in_read_buffer=4;
1086 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1087 if (mng_info->found_empty_plte)
1090 Skip the bKGD data byte and CRC.
1093 ReadBlob(image,5,(char *) mng_info->read_buffer);
1094 check=(png_size_t) ReadBlob(image,(size_t) length,
1095 (char *) mng_info->read_buffer);
1096 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1097 mng_info->have_saved_bkgd_index=MagickTrue;
1098 mng_info->bytes_in_read_buffer=0;
1106 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1111 image=(Image *) png_get_io_ptr(png_ptr);
1117 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1119 if (check != length)
1120 png_error(png_ptr,"WriteBlob Failed");
1124 static void png_flush_data(png_structp png_ptr)
1129 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1130 static int PalettesAreEqual(Image *a,Image *b)
1135 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1136 return((int) MagickFalse);
1138 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1139 return((int) MagickFalse);
1141 if (a->colors != b->colors)
1142 return((int) MagickFalse);
1144 for (i=0; i < (ssize_t) a->colors; i++)
1146 if ((a->colormap[i].red != b->colormap[i].red) ||
1147 (a->colormap[i].green != b->colormap[i].green) ||
1148 (a->colormap[i].blue != b->colormap[i].blue))
1149 return((int) MagickFalse);
1152 return((int) MagickTrue);
1156 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1158 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1159 mng_info->exists[i] && !mng_info->frozen[i])
1161 #ifdef MNG_OBJECT_BUFFERS
1162 if (mng_info->ob[i] != (MngBuffer *) NULL)
1164 if (mng_info->ob[i]->reference_count > 0)
1165 mng_info->ob[i]->reference_count--;
1167 if (mng_info->ob[i]->reference_count == 0)
1169 if (mng_info->ob[i]->image != (Image *) NULL)
1170 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1172 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1175 mng_info->ob[i]=(MngBuffer *) NULL;
1177 mng_info->exists[i]=MagickFalse;
1178 mng_info->invisible[i]=MagickFalse;
1179 mng_info->viewable[i]=MagickFalse;
1180 mng_info->frozen[i]=MagickFalse;
1181 mng_info->x_off[i]=0;
1182 mng_info->y_off[i]=0;
1183 mng_info->object_clip[i].left=0;
1184 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1185 mng_info->object_clip[i].top=0;
1186 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1190 static void MngInfoFreeStruct(MngInfo *mng_info,
1191 MagickBooleanType *have_mng_structure)
1193 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1198 for (i=1; i < MNG_MAX_OBJECTS; i++)
1199 MngInfoDiscardObject(mng_info,i);
1201 if (mng_info->global_plte != (png_colorp) NULL)
1202 mng_info->global_plte=(png_colorp)
1203 RelinquishMagickMemory(mng_info->global_plte);
1205 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1206 *have_mng_structure=MagickFalse;
1210 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1216 if (box.left < box2.left)
1219 if (box.top < box2.top)
1222 if (box.right > box2.right)
1223 box.right=box2.right;
1225 if (box.bottom > box2.bottom)
1226 box.bottom=box2.bottom;
1231 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1237 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1239 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1240 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1241 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1242 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1243 if (delta_type != 0)
1245 box.left+=previous_box.left;
1246 box.right+=previous_box.right;
1247 box.top+=previous_box.top;
1248 box.bottom+=previous_box.bottom;
1254 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1260 Read two ssize_ts from CLON, MOVE or PAST chunk
1262 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1263 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1265 if (delta_type != 0)
1267 pair.a+=previous_pair.a;
1268 pair.b+=previous_pair.b;
1274 static long mng_get_long(unsigned char *p)
1276 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1279 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1284 image=(Image *) png_get_error_ptr(ping);
1286 if (image->debug != MagickFalse)
1287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1288 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1290 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1291 message,"`%s'",image->filename);
1293 #if (PNG_LIBPNG_VER < 10500)
1294 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1295 * are building with libpng-1.4.x and can be ignored.
1297 longjmp(ping->jmpbuf,1);
1299 png_longjmp(ping,1);
1303 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1308 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1309 png_error(ping, message);
1311 image=(Image *) png_get_error_ptr(ping);
1312 if (image->debug != MagickFalse)
1313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1314 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1316 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1317 message,"`%s'",image->filename);
1320 #ifdef PNG_USER_MEM_SUPPORTED
1321 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1323 #if (PNG_LIBPNG_VER < 10011)
1328 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1331 png_error("Insufficient memory.");
1336 return((png_voidp) AcquireMagickMemory((size_t) size));
1341 Free a pointer. It is removed from the list at the same time.
1343 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1346 ptr=RelinquishMagickMemory(ptr);
1347 return((png_free_ptr) NULL);
1351 #if defined(__cplusplus) || defined(c_plusplus)
1356 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1357 png_textp text,int ii)
1362 register unsigned char
1376 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1377 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1378 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1379 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1380 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1384 /* look for newline */
1388 /* look for length */
1389 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1392 length=(png_uint_32) StringToLong(sp);
1394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1395 " length: %lu",(unsigned long) length);
1397 while (*sp != ' ' && *sp != '\n')
1400 /* allocate space */
1403 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1404 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1405 return(MagickFalse);
1408 profile=AcquireStringInfo(length);
1410 if (profile == (StringInfo *) NULL)
1412 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1413 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1414 "unable to copy profile");
1415 return(MagickFalse);
1418 /* copy profile, skipping white space and column 1 "=" signs */
1419 dp=GetStringInfoDatum(profile);
1422 for (i=0; i < (ssize_t) nibbles; i++)
1424 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1428 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1429 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1430 profile=DestroyStringInfo(profile);
1431 return(MagickFalse);
1437 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1440 (*dp++)+=unhex[(int) *sp++];
1443 We have already read "Raw profile type.
1445 (void) SetImageProfile(image,&text[ii].key[17],profile);
1446 profile=DestroyStringInfo(profile);
1448 if (image_info->verbose)
1449 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1454 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1455 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1461 /* The unknown chunk structure contains the chunk data:
1466 Note that libpng has already taken care of the CRC handling.
1470 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1471 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1472 return(0); /* Did not recognize */
1474 /* recognized vpAg */
1476 if (chunk->size != 9)
1477 return(-1); /* Error return */
1479 if (chunk->data[8] != 0)
1480 return(0); /* ImageMagick requires pixel units */
1482 image=(Image *) png_get_user_chunk_ptr(ping);
1484 image->page.width=(size_t) ((chunk->data[0] << 24) |
1485 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1487 image->page.height=(size_t) ((chunk->data[4] << 24) |
1488 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1490 /* Return one of the following: */
1491 /* return(-n); chunk had an error */
1492 /* return(0); did not recognize */
1493 /* return(n); success */
1501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505 % R e a d O n e P N G I m a g e %
1509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1511 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1512 % (minus the 8-byte signature) and returns it. It allocates the memory
1513 % necessary for the new Image structure and returns a pointer to the new
1516 % The format of the ReadOnePNGImage method is:
1518 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1519 % ExceptionInfo *exception)
1521 % A description of each parameter follows:
1523 % o mng_info: Specifies a pointer to a MngInfo structure.
1525 % o image_info: the image info.
1527 % o exception: return any errors or warnings in this structure.
1530 static Image *ReadOnePNGImage(MngInfo *mng_info,
1531 const ImageInfo *image_info, ExceptionInfo *exception)
1533 /* Read one PNG image */
1535 /* To do: Read the tIME chunk into the date:modify property */
1536 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1550 ping_interlace_method,
1551 ping_compression_method,
1599 register unsigned char
1602 register IndexPacket
1609 register PixelPacket
1619 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1620 png_byte unused_chunks[]=
1622 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1623 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1624 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1625 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1626 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1627 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1631 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1632 " Enter ReadOnePNGImage()");
1634 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1635 LockSemaphoreInfo(ping_semaphore);
1638 #if (PNG_LIBPNG_VER < 10200)
1639 if (image_info->verbose)
1640 printf("Your PNG library (libpng-%s) is rather old.\n",
1641 PNG_LIBPNG_VER_STRING);
1644 #if (PNG_LIBPNG_VER >= 10400)
1645 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1646 if (image_info->verbose)
1648 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1649 PNG_LIBPNG_VER_STRING);
1650 printf("Please update it.\n");
1656 quantum_info = (QuantumInfo *) NULL;
1657 image=mng_info->image;
1659 if (logging != MagickFalse)
1660 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1661 " image->matte=%d",(int) image->matte);
1663 /* Set to an out-of-range color unless tRNS chunk is present */
1664 transparent_color.red=65537;
1665 transparent_color.green=65537;
1666 transparent_color.blue=65537;
1667 transparent_color.opacity=65537;
1671 num_raw_profiles = 0;
1674 Allocate the PNG structures
1676 #ifdef PNG_USER_MEM_SUPPORTED
1677 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1678 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1679 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1681 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1682 MagickPNGErrorHandler,MagickPNGWarningHandler);
1684 if (ping == (png_struct *) NULL)
1685 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1687 ping_info=png_create_info_struct(ping);
1689 if (ping_info == (png_info *) NULL)
1691 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1695 end_info=png_create_info_struct(ping);
1697 if (end_info == (png_info *) NULL)
1699 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1700 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1703 ping_pixels=(unsigned char *) NULL;
1705 if (setjmp(png_jmpbuf(ping)))
1708 PNG image is corrupt.
1710 png_destroy_read_struct(&ping,&ping_info,&end_info);
1711 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1712 UnlockSemaphoreInfo(ping_semaphore);
1714 if (logging != MagickFalse)
1715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1716 " exit ReadOnePNGImage() with error.");
1718 if (image != (Image *) NULL)
1720 InheritException(exception,&image->exception);
1724 return(GetFirstImageInList(image));
1727 Prepare PNG for reading.
1730 mng_info->image_found++;
1731 png_set_sig_bytes(ping,8);
1733 if (LocaleCompare(image_info->magick,"MNG") == 0)
1735 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1736 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1737 png_set_read_fn(ping,image,png_get_data);
1739 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1740 png_permit_empty_plte(ping,MagickTrue);
1741 png_set_read_fn(ping,image,png_get_data);
1743 mng_info->image=image;
1744 mng_info->bytes_in_read_buffer=0;
1745 mng_info->found_empty_plte=MagickFalse;
1746 mng_info->have_saved_bkgd_index=MagickFalse;
1747 png_set_read_fn(ping,mng_info,mng_get_data);
1753 png_set_read_fn(ping,image,png_get_data);
1755 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1756 /* Ignore unused chunks and all unknown chunks except for vpAg */
1757 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1758 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1759 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1760 (int)sizeof(unused_chunks)/5);
1761 /* Callback for other unknown chunks */
1762 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1765 #if (PNG_LIBPNG_VER < 10400)
1766 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1767 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1768 /* Disable thread-unsafe features of pnggccrd */
1769 if (png_access_version_number() >= 10200)
1771 png_uint_32 mmx_disable_mask=0;
1772 png_uint_32 asm_flags;
1774 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1775 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1776 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1777 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1778 asm_flags=png_get_asm_flags(ping);
1779 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1784 png_read_info(ping,ping_info);
1786 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1787 &ping_bit_depth,&ping_color_type,
1788 &ping_interlace_method,&ping_compression_method,
1789 &ping_filter_method);
1791 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1794 (void) png_get_bKGD(ping, ping_info, &ping_background);
1796 if (ping_bit_depth < 8)
1798 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1800 png_set_packing(ping);
1805 image->depth=ping_bit_depth;
1806 image->depth=GetImageQuantumDepth(image,MagickFalse);
1807 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1808 if (logging != MagickFalse)
1810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1811 " PNG width: %.20g, height: %.20g",
1812 (double) ping_width, (double) ping_height);
1814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " PNG color_type: %d, bit_depth: %d",
1816 ping_color_type, ping_bit_depth);
1818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1819 " PNG compression_method: %d",
1820 ping_compression_method);
1822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1823 " PNG interlace_method: %d, filter_method: %d",
1824 ping_interlace_method,ping_filter_method);
1827 #ifdef PNG_READ_iCCP_SUPPORTED
1828 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1833 #if (PNG_LIBPNG_VER < 10500)
1847 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1850 if (profile_length != 0)
1855 if (logging != MagickFalse)
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857 " Reading PNG iCCP chunk.");
1858 profile=AcquireStringInfo(profile_length);
1859 SetStringInfoDatum(profile,(const unsigned char *) info);
1860 (void) SetImageProfile(image,"icc",profile);
1861 profile=DestroyStringInfo(profile);
1865 #if defined(PNG_READ_sRGB_SUPPORTED)
1867 if (mng_info->have_global_srgb)
1868 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1869 (mng_info->global_srgb_intent);
1871 if (png_get_sRGB(ping,ping_info,&intent))
1873 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1876 if (logging != MagickFalse)
1877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1878 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1883 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1884 if (mng_info->have_global_gama)
1885 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1887 if (png_get_gAMA(ping,ping_info,&file_gamma))
1889 image->gamma=(float) file_gamma;
1890 if (logging != MagickFalse)
1891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1892 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1895 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1897 if (mng_info->have_global_chrm != MagickFalse)
1899 (void) png_set_cHRM(ping,ping_info,
1900 mng_info->global_chrm.white_point.x,
1901 mng_info->global_chrm.white_point.y,
1902 mng_info->global_chrm.red_primary.x,
1903 mng_info->global_chrm.red_primary.y,
1904 mng_info->global_chrm.green_primary.x,
1905 mng_info->global_chrm.green_primary.y,
1906 mng_info->global_chrm.blue_primary.x,
1907 mng_info->global_chrm.blue_primary.y);
1911 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1913 (void) png_get_cHRM(ping,ping_info,
1914 &image->chromaticity.white_point.x,
1915 &image->chromaticity.white_point.y,
1916 &image->chromaticity.red_primary.x,
1917 &image->chromaticity.red_primary.y,
1918 &image->chromaticity.green_primary.x,
1919 &image->chromaticity.green_primary.y,
1920 &image->chromaticity.blue_primary.x,
1921 &image->chromaticity.blue_primary.y);
1923 if (logging != MagickFalse)
1924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1925 " Reading PNG cHRM chunk.");
1928 if (image->rendering_intent != UndefinedIntent)
1930 png_set_sRGB(ping,ping_info,
1931 Magick_RenderingIntent_to_PNG_RenderingIntent
1932 (image->rendering_intent));
1933 png_set_gAMA(ping,ping_info,0.45455f);
1934 png_set_cHRM(ping,ping_info,
1935 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1936 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1938 #if defined(PNG_oFFs_SUPPORTED)
1939 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1941 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1942 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1944 if (logging != MagickFalse)
1945 if (image->page.x || image->page.y)
1946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1947 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1948 image->page.x,(double) image->page.y);
1951 #if defined(PNG_pHYs_SUPPORTED)
1952 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1954 if (mng_info->have_global_phys)
1956 png_set_pHYs(ping,ping_info,
1957 mng_info->global_x_pixels_per_unit,
1958 mng_info->global_y_pixels_per_unit,
1959 mng_info->global_phys_unit_type);
1963 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1966 Set image resolution.
1968 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1970 image->x_resolution=(double) x_resolution;
1971 image->y_resolution=(double) y_resolution;
1973 if (unit_type == PNG_RESOLUTION_METER)
1975 image->units=PixelsPerCentimeterResolution;
1976 image->x_resolution=(double) x_resolution/100.0;
1977 image->y_resolution=(double) y_resolution/100.0;
1980 if (logging != MagickFalse)
1981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1982 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1983 (double) x_resolution,(double) y_resolution,unit_type);
1987 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1995 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1997 if ((number_colors == 0) &&
1998 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2000 if (mng_info->global_plte_length)
2002 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2003 (int) mng_info->global_plte_length);
2005 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2006 if (mng_info->global_trns_length)
2008 if (mng_info->global_trns_length >
2009 mng_info->global_plte_length)
2010 (void) ThrowMagickException(&image->exception,
2011 GetMagickModule(),CoderError,
2012 "global tRNS has more entries than global PLTE",
2013 "`%s'",image_info->filename);
2014 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2015 (int) mng_info->global_trns_length,NULL);
2017 #ifdef PNG_READ_bKGD_SUPPORTED
2019 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2020 mng_info->have_saved_bkgd_index ||
2022 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2027 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2028 if (mng_info->have_saved_bkgd_index)
2029 background.index=mng_info->saved_bkgd_index;
2031 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2032 background.index=ping_background->index;
2034 background.red=(png_uint_16)
2035 mng_info->global_plte[background.index].red;
2037 background.green=(png_uint_16)
2038 mng_info->global_plte[background.index].green;
2040 background.blue=(png_uint_16)
2041 mng_info->global_plte[background.index].blue;
2043 png_set_bKGD(ping,ping_info,&background);
2048 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2049 CoderError,"No global PLTE in file","`%s'",
2050 image_info->filename);
2054 #ifdef PNG_READ_bKGD_SUPPORTED
2055 if (mng_info->have_global_bkgd &&
2056 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2057 image->background_color=mng_info->mng_global_bkgd;
2059 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2065 Set image background color.
2067 if (logging != MagickFalse)
2068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2069 " Reading PNG bKGD chunk.");
2071 /* Scale background components to 16-bit, then scale
2074 if (logging != MagickFalse)
2075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2076 " raw ping_background=(%d,%d,%d).",ping_background->red,
2077 ping_background->green,ping_background->blue);
2081 if (ping_bit_depth == 1)
2084 else if (ping_bit_depth == 2)
2087 else if (ping_bit_depth == 4)
2090 if (ping_bit_depth <= 8)
2093 ping_background->red *= bkgd_scale;
2094 ping_background->green *= bkgd_scale;
2095 ping_background->blue *= bkgd_scale;
2097 if (logging != MagickFalse)
2099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2100 " bkgd_scale=%d.",bkgd_scale);
2102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2103 " ping_background=(%d,%d,%d).",ping_background->red,
2104 ping_background->green,ping_background->blue);
2107 image->background_color.red=
2108 ScaleShortToQuantum(ping_background->red);
2110 image->background_color.green=
2111 ScaleShortToQuantum(ping_background->green);
2113 image->background_color.blue=
2114 ScaleShortToQuantum(ping_background->blue);
2116 image->background_color.opacity=OpaqueOpacity;
2118 if (logging != MagickFalse)
2119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2120 " image->background_color=(%.20g,%.20g,%.20g).",
2121 (double) image->background_color.red,
2122 (double) image->background_color.green,
2123 (double) image->background_color.blue);
2125 #endif /* PNG_READ_bKGD_SUPPORTED */
2127 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2130 Image has a tRNS chunk.
2138 if (logging != MagickFalse)
2139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2140 " Reading PNG tRNS chunk.");
2142 max_sample = (int) ((one << ping_bit_depth) - 1);
2144 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2145 (int)ping_trans_color->gray > max_sample) ||
2146 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2147 ((int)ping_trans_color->red > max_sample ||
2148 (int)ping_trans_color->green > max_sample ||
2149 (int)ping_trans_color->blue > max_sample)))
2151 if (logging != MagickFalse)
2152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2153 " Ignoring PNG tRNS chunk with out-of-range sample.");
2154 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2155 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2156 image->matte=MagickFalse;
2163 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2165 /* Scale transparent_color to short */
2166 transparent_color.red= scale_to_short*ping_trans_color->red;
2167 transparent_color.green= scale_to_short*ping_trans_color->green;
2168 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2169 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2171 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2173 if (logging != MagickFalse)
2175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2176 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2179 " scaled graylevel is %d.",transparent_color.opacity);
2181 transparent_color.red=transparent_color.opacity;
2182 transparent_color.green=transparent_color.opacity;
2183 transparent_color.blue=transparent_color.opacity;
2187 #if defined(PNG_READ_sBIT_SUPPORTED)
2188 if (mng_info->have_global_sbit)
2190 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2191 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2194 num_passes=png_set_interlace_handling(ping);
2196 png_read_update_info(ping,ping_info);
2198 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2201 Initialize image structure.
2203 mng_info->image_box.left=0;
2204 mng_info->image_box.right=(ssize_t) ping_width;
2205 mng_info->image_box.top=0;
2206 mng_info->image_box.bottom=(ssize_t) ping_height;
2207 if (mng_info->mng_type == 0)
2209 mng_info->mng_width=ping_width;
2210 mng_info->mng_height=ping_height;
2211 mng_info->frame=mng_info->image_box;
2212 mng_info->clip=mng_info->image_box;
2217 image->page.y=mng_info->y_off[mng_info->object_id];
2220 image->compression=ZipCompression;
2221 image->columns=ping_width;
2222 image->rows=ping_height;
2223 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2224 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2229 image->storage_class=PseudoClass;
2231 image->colors=one << ping_bit_depth;
2232 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2233 if (image->colors > 256)
2236 if (image->colors > 65536L)
2237 image->colors=65536L;
2239 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2247 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2248 image->colors=(size_t) number_colors;
2250 if (logging != MagickFalse)
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2252 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2256 if (image->storage_class == PseudoClass)
2259 Initialize image colormap.
2261 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2262 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2264 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2272 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2274 for (i=0; i < (ssize_t) number_colors; i++)
2276 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2277 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2278 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2281 for ( ; i < (ssize_t) image->colors; i++)
2283 image->colormap[i].red=0;
2284 image->colormap[i].green=0;
2285 image->colormap[i].blue=0;
2294 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2299 for (i=0; i < (ssize_t) image->colors; i++)
2301 image->colormap[i].red=(Quantum) (i*scale);
2302 image->colormap[i].green=(Quantum) (i*scale);
2303 image->colormap[i].blue=(Quantum) (i*scale);
2308 /* Set some properties for reporting by "identify" */
2313 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2314 ping_interlace_method in value */
2316 (void) FormatMagickString(msg,MaxTextExtent,
2317 "%d, %d",(int) ping_width, (int) ping_height);
2318 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2320 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2321 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2323 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2324 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2326 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2327 (int) ping_interlace_method);
2328 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2332 Read image scanlines.
2334 if (image->delay != 0)
2335 mng_info->scenes_found++;
2337 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2338 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2339 (image_info->first_scene+image_info->number_scenes))))
2341 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " Skipping PNG image data for scene %.20g",(double)
2344 mng_info->scenes_found-1);
2345 png_destroy_read_struct(&ping,&ping_info,&end_info);
2346 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2347 UnlockSemaphoreInfo(ping_semaphore);
2349 if (logging != MagickFalse)
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " exit ReadOnePNGImage().");
2356 if (logging != MagickFalse)
2357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2358 " Reading PNG IDAT chunk(s)");
2361 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2362 ping_rowbytes*sizeof(*ping_pixels));
2365 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2366 sizeof(*ping_pixels));
2368 if (ping_pixels == (unsigned char *) NULL)
2369 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Converting PNG pixels to pixel packets");
2375 Convert PNG pixels to pixel packets.
2377 if (setjmp(png_jmpbuf(ping)))
2380 PNG image is corrupt.
2382 png_destroy_read_struct(&ping,&ping_info,&end_info);
2383 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2384 UnlockSemaphoreInfo(ping_semaphore);
2386 if (quantum_info != (QuantumInfo *) NULL)
2387 quantum_info = DestroyQuantumInfo(quantum_info);
2389 if (ping_pixels != (unsigned char *) NULL)
2390 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2392 if (logging != MagickFalse)
2393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2394 " exit ReadOnePNGImage() with error.");
2396 if (image != (Image *) NULL)
2398 InheritException(exception,&image->exception);
2402 return(GetFirstImageInList(image));
2405 quantum_info=AcquireQuantumInfo(image_info,image);
2407 if (quantum_info == (QuantumInfo *) NULL)
2408 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2413 found_transparent_pixel;
2415 found_transparent_pixel=MagickFalse;
2417 if (image->storage_class == DirectClass)
2419 for (pass=0; pass < num_passes; pass++)
2422 Convert image to DirectClass pixel packets.
2424 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2428 depth=(ssize_t) ping_bit_depth;
2430 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2431 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2432 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2433 MagickTrue : MagickFalse;
2435 for (y=0; y < (ssize_t) image->rows; y++)
2438 row_offset=ping_rowbytes*y;
2443 png_read_row(ping,ping_pixels+row_offset,NULL);
2444 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2446 if (q == (PixelPacket *) NULL)
2449 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2450 /* code deleted from version 6.6.6-8 */
2451 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2453 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2454 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2455 GrayQuantum,ping_pixels+row_offset,exception);
2457 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2458 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2459 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2461 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2462 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2463 RGBAQuantum,ping_pixels+row_offset,exception);
2465 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2466 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2467 IndexQuantum,ping_pixels+row_offset,exception);
2469 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2470 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2471 RGBQuantum,ping_pixels+row_offset,exception);
2473 if (found_transparent_pixel == MagickFalse)
2475 /* Is there a transparent pixel in the row? */
2476 if (y== 0 && logging != MagickFalse)
2477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2478 " Looking for cheap transparent pixel");
2480 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2482 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2483 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2484 (q->opacity != OpaqueOpacity))
2486 if (logging != MagickFalse)
2487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2490 found_transparent_pixel = MagickTrue;
2493 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2494 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2495 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2496 ScaleQuantumToShort(q->green) == transparent_color.green &&
2497 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2499 if (logging != MagickFalse)
2500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2502 found_transparent_pixel = MagickTrue;
2509 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2511 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2514 if (status == MagickFalse)
2517 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2521 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2523 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2524 if (status == MagickFalse)
2530 else /* image->storage_class != DirectClass */
2532 for (pass=0; pass < num_passes; pass++)
2541 Convert grayscale image to PseudoClass pixel packets.
2543 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2544 MagickTrue : MagickFalse;
2546 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2547 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2549 if (quantum_scanline == (Quantum *) NULL)
2550 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2552 for (y=0; y < (ssize_t) image->rows; y++)
2555 row_offset=ping_rowbytes*y;
2560 png_read_row(ping,ping_pixels+row_offset,NULL);
2561 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2563 if (q == (PixelPacket *) NULL)
2566 indexes=GetAuthenticIndexQueue(image);
2567 p=ping_pixels+row_offset;
2570 switch (ping_bit_depth)
2577 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2579 for (bit=7; bit >= 0; bit--)
2580 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2584 if ((image->columns % 8) != 0)
2586 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2587 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2595 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2597 *r++=(*p >> 6) & 0x03;
2598 *r++=(*p >> 4) & 0x03;
2599 *r++=(*p >> 2) & 0x03;
2603 if ((image->columns % 4) != 0)
2605 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2606 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2614 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2616 *r++=(*p >> 4) & 0x0f;
2620 if ((image->columns % 2) != 0)
2621 *r++=(*p++ >> 4) & 0x0f;
2628 if (ping_color_type == 4)
2629 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2632 /* In image.h, OpaqueOpacity is 0
2633 * TransparentOpacity is QuantumRange
2634 * In a PNG datastream, Opaque is QuantumRange
2635 * and Transparent is 0.
2637 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2638 if (q->opacity != OpaqueOpacity)
2639 found_transparent_pixel = MagickTrue;
2644 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2652 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2654 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2658 if (image->colors > 256)
2666 *r=(Quantum) quantum;
2669 if (ping_color_type == 4)
2671 quantum=((*p++) << 8);
2673 q->opacity=(Quantum) (QuantumRange-quantum);
2674 if (q->opacity != OpaqueOpacity)
2675 found_transparent_pixel = MagickTrue;
2679 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2683 if (image->colors > 256)
2694 if (ping_color_type == 4)
2696 q->opacity=(*p << 8) | *(p+1);
2698 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2699 if (q->opacity != OpaqueOpacity)
2700 found_transparent_pixel = MagickTrue;
2705 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2707 p++; /* strip low byte */
2709 if (ping_color_type == 4)
2711 q->opacity=(Quantum) (QuantumRange-(*p++));
2712 if (q->opacity != OpaqueOpacity)
2713 found_transparent_pixel = MagickTrue;
2728 Transfer image scanline.
2732 for (x=0; x < (ssize_t) image->columns; x++)
2733 indexes[x]=(IndexPacket) (*r++);
2735 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2738 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2740 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2743 if (status == MagickFalse)
2748 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2750 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2752 if (status == MagickFalse)
2756 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2759 image->matte=found_transparent_pixel;
2761 if (logging != MagickFalse)
2763 if (found_transparent_pixel != MagickFalse)
2764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765 " Found transparent pixel");
2768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2769 " No transparent pixel was found");
2771 ping_color_type&=0x03;
2776 if (quantum_info != (QuantumInfo *) NULL)
2777 quantum_info=DestroyQuantumInfo(quantum_info);
2779 if (image->storage_class == PseudoClass)
2785 image->matte=MagickFalse;
2786 (void) SyncImage(image);
2790 png_read_end(ping,end_info);
2792 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2793 (ssize_t) image_info->first_scene && image->delay != 0)
2795 png_destroy_read_struct(&ping,&ping_info,&end_info);
2796 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2798 (void) SetImageBackgroundColor(image);
2799 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2800 UnlockSemaphoreInfo(ping_semaphore);
2802 if (logging != MagickFalse)
2803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804 " exit ReadOnePNGImage() early.");
2808 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2814 Image has a transparent background.
2816 storage_class=image->storage_class;
2817 image->matte=MagickTrue;
2819 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2821 if (storage_class == PseudoClass)
2823 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2825 for (x=0; x < ping_num_trans; x++)
2827 image->colormap[x].opacity =
2828 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2832 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2834 for (x=0; x < (int) image->colors; x++)
2836 if (ScaleQuantumToShort(image->colormap[x].red) ==
2837 transparent_color.opacity)
2839 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2843 (void) SyncImage(image);
2846 #if 1 /* Should have already been done above, but glennrp problem P10
2851 for (y=0; y < (ssize_t) image->rows; y++)
2853 image->storage_class=storage_class;
2854 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2856 if (q == (PixelPacket *) NULL)
2859 indexes=GetAuthenticIndexQueue(image);
2861 /* Caution: on a Q8 build, this does not distinguish between
2862 * 16-bit colors that differ only in the low byte
2864 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2866 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2867 ScaleQuantumToShort(q->green) == transparent_color.green &&
2868 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2870 q->opacity=(Quantum) TransparentOpacity;
2873 #if 0 /* I have not found a case where this is needed. */
2876 q->opacity=(Quantum) OpaqueOpacity;
2883 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2889 image->storage_class=DirectClass;
2892 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2893 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2894 image->colorspace=GRAYColorspace;
2896 for (j = 0; j < 2; j++)
2899 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
2902 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2903 MagickTrue : MagickFalse;
2905 if (status != MagickFalse)
2906 for (i=0; i < (ssize_t) num_text; i++)
2908 /* Check for a profile */
2910 if (logging != MagickFalse)
2911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2912 " Reading PNG text chunk");
2914 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2916 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2925 length=text[i].text_length;
2926 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2928 if (value == (char *) NULL)
2930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2931 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2936 (void) ConcatenateMagickString(value,text[i].text,length+2);
2938 /* Don't save "density" or "units" property if we have a pHYs
2941 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2942 (LocaleCompare(text[i].key,"density") != 0 &&
2943 LocaleCompare(text[i].key,"units") != 0))
2944 (void) SetImageProperty(image,text[i].key,value);
2946 if (logging != MagickFalse)
2948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2949 " length: %lu",(unsigned long) length);
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " Keyword: %s",text[i].key);
2954 value=DestroyString(value);
2957 num_text_total += num_text;
2960 #ifdef MNG_OBJECT_BUFFERS
2962 Store the object if necessary.
2964 if (object_id && !mng_info->frozen[object_id])
2966 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2969 create a new object buffer.
2971 mng_info->ob[object_id]=(MngBuffer *)
2972 AcquireMagickMemory(sizeof(MngBuffer));
2974 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2976 mng_info->ob[object_id]->image=(Image *) NULL;
2977 mng_info->ob[object_id]->reference_count=1;
2981 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2982 mng_info->ob[object_id]->frozen)
2984 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2985 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2986 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2989 if (mng_info->ob[object_id]->frozen)
2990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2992 "`%s'",image->filename);
2998 if (mng_info->ob[object_id]->image != (Image *) NULL)
2999 mng_info->ob[object_id]->image=DestroyImage
3000 (mng_info->ob[object_id]->image);
3002 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3005 if (mng_info->ob[object_id]->image != (Image *) NULL)
3006 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3009 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3010 ResourceLimitError,"Cloning image for object buffer failed",
3011 "`%s'",image->filename);
3013 if (ping_width > 250000L || ping_height > 250000L)
3014 png_error(ping,"PNG Image dimensions are too large.");
3016 mng_info->ob[object_id]->width=ping_width;
3017 mng_info->ob[object_id]->height=ping_height;
3018 mng_info->ob[object_id]->color_type=ping_color_type;
3019 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3020 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3021 mng_info->ob[object_id]->compression_method=
3022 ping_compression_method;
3023 mng_info->ob[object_id]->filter_method=ping_filter_method;
3025 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3034 Copy the PLTE to the object buffer.
3036 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3037 mng_info->ob[object_id]->plte_length=number_colors;
3039 for (i=0; i < number_colors; i++)
3041 mng_info->ob[object_id]->plte[i]=plte[i];
3046 mng_info->ob[object_id]->plte_length=0;
3051 /* Set image->matte to MagickTrue if the input colortype supports
3052 * alpha or if a valid tRNS chunk is present, no matter whether there
3053 * is actual transparency present.
3055 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3056 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3057 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3058 MagickTrue : MagickFalse;
3060 /* Set more properties for identify to retrieve */
3065 if (num_text_total != 0)
3067 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3068 (void) FormatMagickString(msg,MaxTextExtent,
3069 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3070 (void) SetImageProperty(image,"PNG:text ",msg);
3073 if (num_raw_profiles != 0)
3075 (void) FormatMagickString(msg,MaxTextExtent,
3076 "%d were found", num_raw_profiles);
3077 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3080 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3082 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3083 "chunk was found (see Chromaticity, above)");
3084 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3087 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3089 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3090 "chunk was found (see Background color, above)");
3091 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3094 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3097 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3098 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3100 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3101 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3103 #if defined(PNG_sRGB_SUPPORTED)
3104 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3106 (void) FormatMagickString(msg,MaxTextExtent,
3107 "intent=%d (See Rendering intent)",
3109 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3113 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3115 (void) FormatMagickString(msg,MaxTextExtent,
3116 "gamma=%.8g (See Gamma, above)",
3118 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3121 #if defined(PNG_pHYs_SUPPORTED)
3122 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3124 (void) FormatMagickString(msg,MaxTextExtent,
3125 "x_res=%.10g, y_res=%.10g, units=%d",
3126 (double) x_resolution,(double) y_resolution, unit_type);
3127 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3131 #if defined(PNG_oFFs_SUPPORTED)
3132 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3134 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3135 (double) image->page.x,(double) image->page.y);
3136 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3140 if ((image->page.width != 0 && image->page.width != image->columns) ||
3141 (image->page.height != 0 && image->page.height != image->rows))
3143 (void) FormatMagickString(msg,MaxTextExtent,
3144 "width=%.20g, height=%.20g",
3145 (double) image->page.width,(double) image->page.height);
3146 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3151 Relinquish resources.
3153 png_destroy_read_struct(&ping,&ping_info,&end_info);
3155 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3156 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3157 UnlockSemaphoreInfo(ping_semaphore);
3160 if (logging != MagickFalse)
3161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3162 " exit ReadOnePNGImage()");
3166 /* end of reading one PNG image */
3169 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3184 magic_number[MaxTextExtent];
3192 assert(image_info != (const ImageInfo *) NULL);
3193 assert(image_info->signature == MagickSignature);
3195 if (image_info->debug != MagickFalse)
3196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3197 image_info->filename);
3199 assert(exception != (ExceptionInfo *) NULL);
3200 assert(exception->signature == MagickSignature);
3201 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3202 image=AcquireImage(image_info);
3203 mng_info=(MngInfo *) NULL;
3204 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3206 if (status == MagickFalse)
3207 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3210 Verify PNG signature.
3212 count=ReadBlob(image,8,(unsigned char *) magic_number);
3214 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3215 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3218 Allocate a MngInfo structure.
3220 have_mng_structure=MagickFalse;
3221 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3223 if (mng_info == (MngInfo *) NULL)
3224 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3227 Initialize members of the MngInfo structure.
3229 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3230 mng_info->image=image;
3231 have_mng_structure=MagickTrue;
3234 image=ReadOnePNGImage(mng_info,image_info,exception);
3235 MngInfoFreeStruct(mng_info,&have_mng_structure);
3237 if (image == (Image *) NULL)
3239 if (previous != (Image *) NULL)
3241 if (previous->signature != MagickSignature)
3242 ThrowReaderException(CorruptImageError,"CorruptImage");
3244 (void) CloseBlob(previous);
3245 (void) DestroyImageList(previous);
3248 if (logging != MagickFalse)
3249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3250 "exit ReadPNGImage() with error");
3252 return((Image *) NULL);
3255 (void) CloseBlob(image);
3257 if ((image->columns == 0) || (image->rows == 0))
3259 if (logging != MagickFalse)
3260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3261 "exit ReadPNGImage() with error.");
3263 ThrowReaderException(CorruptImageError,"CorruptImage");
3266 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3268 (void) SetImageType(image,PaletteType);
3270 if (image->matte != MagickFalse)
3272 /* To do: Reduce to binary transparency */
3276 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3278 (void) SetImageType(image,TrueColorType);
3279 image->matte=MagickFalse;
3282 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3283 (void) SetImageType(image,TrueColorMatteType);
3285 if (logging != MagickFalse)
3286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3287 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3288 (double) image->page.width,(double) image->page.height,
3289 (double) image->page.x,(double) image->page.y);
3291 if (logging != MagickFalse)
3292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3299 #if defined(JNG_SUPPORTED)
3301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3305 % R e a d O n e J N G I m a g e %
3309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3311 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3312 % (minus the 8-byte signature) and returns it. It allocates the memory
3313 % necessary for the new Image structure and returns a pointer to the new
3316 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3318 % The format of the ReadOneJNGImage method is:
3320 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3321 % ExceptionInfo *exception)
3323 % A description of each parameter follows:
3325 % o mng_info: Specifies a pointer to a MngInfo structure.
3327 % o image_info: the image info.
3329 % o exception: return any errors or warnings in this structure.
3332 static Image *ReadOneJNGImage(MngInfo *mng_info,
3333 const ImageInfo *image_info, ExceptionInfo *exception)
3360 jng_image_sample_depth,
3361 jng_image_compression_method,
3362 jng_image_interlace_method,
3363 jng_alpha_sample_depth,
3364 jng_alpha_compression_method,
3365 jng_alpha_filter_method,
3366 jng_alpha_interlace_method;
3368 register const PixelPacket
3375 register PixelPacket
3378 register unsigned char
3389 jng_alpha_compression_method=0;
3390 jng_alpha_sample_depth=8;
3394 alpha_image=(Image *) NULL;
3395 color_image=(Image *) NULL;
3396 alpha_image_info=(ImageInfo *) NULL;
3397 color_image_info=(ImageInfo *) NULL;
3399 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3400 " Enter ReadOneJNGImage()");
3402 image=mng_info->image;
3404 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3407 Allocate next image structure.
3409 if (logging != MagickFalse)
3410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3411 " AcquireNextImage()");
3413 AcquireNextImage(image_info,image);
3415 if (GetNextImageInList(image) == (Image *) NULL)
3416 return((Image *) NULL);
3418 image=SyncNextImageInList(image);
3420 mng_info->image=image;
3423 Signature bytes have already been read.
3426 read_JSEP=MagickFalse;
3427 reading_idat=MagickFalse;
3428 skip_to_iend=MagickFalse;
3432 type[MaxTextExtent];
3441 Read a new JNG chunk.
3443 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3444 2*GetBlobSize(image));
3446 if (status == MagickFalse)
3450 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3451 length=ReadBlobMSBLong(image);
3452 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3454 if (logging != MagickFalse)
3455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3456 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3457 type[0],type[1],type[2],type[3],(double) length);
3459 if (length > PNG_UINT_31_MAX || count == 0)
3460 ThrowReaderException(CorruptImageError,"CorruptImage");
3463 chunk=(unsigned char *) NULL;
3467 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3469 if (chunk == (unsigned char *) NULL)
3470 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3472 for (i=0; i < (ssize_t) length; i++)
3473 chunk[i]=(unsigned char) ReadBlobByte(image);
3478 (void) ReadBlobMSBLong(image); /* read crc word */
3483 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3488 if (memcmp(type,mng_JHDR,4) == 0)
3492 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3493 (p[2] << 8) | p[3]);
3494 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3495 (p[6] << 8) | p[7]);
3496 jng_color_type=p[8];
3497 jng_image_sample_depth=p[9];
3498 jng_image_compression_method=p[10];
3499 jng_image_interlace_method=p[11];
3501 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3504 jng_alpha_sample_depth=p[12];
3505 jng_alpha_compression_method=p[13];
3506 jng_alpha_filter_method=p[14];
3507 jng_alpha_interlace_method=p[15];
3509 if (logging != MagickFalse)
3511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3512 " jng_width: %16lu",(unsigned long) jng_width);
3514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3515 " jng_width: %16lu",(unsigned long) jng_height);
3517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3518 " jng_color_type: %16d",jng_color_type);
3520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3521 " jng_image_sample_depth: %3d",
3522 jng_image_sample_depth);
3524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3525 " jng_image_compression_method:%3d",
3526 jng_image_compression_method);
3528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3529 " jng_image_interlace_method: %3d",
3530 jng_image_interlace_method);
3532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3533 " jng_alpha_sample_depth: %3d",
3534 jng_alpha_sample_depth);
3536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3537 " jng_alpha_compression_method:%3d",
3538 jng_alpha_compression_method);
3540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3541 " jng_alpha_filter_method: %3d",
3542 jng_alpha_filter_method);
3544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3545 " jng_alpha_interlace_method: %3d",
3546 jng_alpha_interlace_method);
3551 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3557 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3558 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3559 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3562 o create color_image
3563 o open color_blob, attached to color_image
3564 o if (color type has alpha)
3565 open alpha_blob, attached to alpha_image
3568 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3570 if (color_image_info == (ImageInfo *) NULL)
3571 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3573 GetImageInfo(color_image_info);
3574 color_image=AcquireImage(color_image_info);
3576 if (color_image == (Image *) NULL)
3577 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3579 if (logging != MagickFalse)
3580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3581 " Creating color_blob.");
3583 (void) AcquireUniqueFilename(color_image->filename);
3584 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3587 if (status == MagickFalse)
3588 return((Image *) NULL);
3590 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3592 alpha_image_info=(ImageInfo *)
3593 AcquireMagickMemory(sizeof(ImageInfo));
3595 if (alpha_image_info == (ImageInfo *) NULL)
3596 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3598 GetImageInfo(alpha_image_info);
3599 alpha_image=AcquireImage(alpha_image_info);
3601 if (alpha_image == (Image *) NULL)
3603 alpha_image=DestroyImage(alpha_image);
3604 ThrowReaderException(ResourceLimitError,
3605 "MemoryAllocationFailed");
3608 if (logging != MagickFalse)
3609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3610 " Creating alpha_blob.");
3612 (void) AcquireUniqueFilename(alpha_image->filename);
3613 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3616 if (status == MagickFalse)
3617 return((Image *) NULL);
3619 if (jng_alpha_compression_method == 0)
3624 if (logging != MagickFalse)
3625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3626 " Writing IHDR chunk to alpha_blob.");
3628 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3629 "\211PNG\r\n\032\n");
3631 (void) WriteBlobMSBULong(alpha_image,13L);
3632 PNGType(data,mng_IHDR);
3633 LogPNGChunk(logging,mng_IHDR,13L);
3634 PNGLong(data+4,jng_width);
3635 PNGLong(data+8,jng_height);
3636 data[12]=jng_alpha_sample_depth;
3637 data[13]=0; /* color_type gray */
3638 data[14]=0; /* compression method 0 */
3639 data[15]=0; /* filter_method 0 */
3640 data[16]=0; /* interlace_method 0 */
3641 (void) WriteBlob(alpha_image,17,data);
3642 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3645 reading_idat=MagickTrue;
3648 if (memcmp(type,mng_JDAT,4) == 0)
3650 /* Copy chunk to color_image->blob */
3652 if (logging != MagickFalse)
3653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3654 " Copying JDAT chunk data to color_blob.");
3656 (void) WriteBlob(color_image,length,chunk);
3659 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3664 if (memcmp(type,mng_IDAT,4) == 0)
3669 /* Copy IDAT header and chunk data to alpha_image->blob */
3671 if (image_info->ping == MagickFalse)
3673 if (logging != MagickFalse)
3674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3675 " Copying IDAT chunk data to alpha_blob.");
3677 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3678 PNGType(data,mng_IDAT);
3679 LogPNGChunk(logging,mng_IDAT,length);
3680 (void) WriteBlob(alpha_image,4,data);
3681 (void) WriteBlob(alpha_image,length,chunk);
3682 (void) WriteBlobMSBULong(alpha_image,
3683 crc32(crc32(0,data,4),chunk,(uInt) length));
3687 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3692 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3694 /* Copy chunk data to alpha_image->blob */
3696 if (image_info->ping == MagickFalse)
3698 if (logging != MagickFalse)
3699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3700 " Copying JDAA chunk data to alpha_blob.");
3702 (void) WriteBlob(alpha_image,length,chunk);
3706 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3711 if (memcmp(type,mng_JSEP,4) == 0)
3713 read_JSEP=MagickTrue;
3716 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3721 if (memcmp(type,mng_bKGD,4) == 0)
3725 image->background_color.red=ScaleCharToQuantum(p[1]);
3726 image->background_color.green=image->background_color.red;
3727 image->background_color.blue=image->background_color.red;
3732 image->background_color.red=ScaleCharToQuantum(p[1]);
3733 image->background_color.green=ScaleCharToQuantum(p[3]);
3734 image->background_color.blue=ScaleCharToQuantum(p[5]);
3737 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3741 if (memcmp(type,mng_gAMA,4) == 0)
3744 image->gamma=((float) mng_get_long(p))*0.00001;
3746 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3750 if (memcmp(type,mng_cHRM,4) == 0)
3754 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3755 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3756 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3757 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3758 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3759 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3760 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3761 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3764 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3768 if (memcmp(type,mng_sRGB,4) == 0)
3772 image->rendering_intent=
3773 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3774 image->gamma=0.45455f;
3775 image->chromaticity.red_primary.x=0.6400f;
3776 image->chromaticity.red_primary.y=0.3300f;
3777 image->chromaticity.green_primary.x=0.3000f;
3778 image->chromaticity.green_primary.y=0.6000f;
3779 image->chromaticity.blue_primary.x=0.1500f;
3780 image->chromaticity.blue_primary.y=0.0600f;
3781 image->chromaticity.white_point.x=0.3127f;
3782 image->chromaticity.white_point.y=0.3290f;
3785 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3789 if (memcmp(type,mng_oFFs,4) == 0)
3793 image->page.x=(ssize_t) mng_get_long(p);
3794 image->page.y=(ssize_t) mng_get_long(&p[4]);
3796 if ((int) p[8] != 0)
3798 image->page.x/=10000;
3799 image->page.y/=10000;
3804 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3809 if (memcmp(type,mng_pHYs,4) == 0)
3813 image->x_resolution=(double) mng_get_long(p);
3814 image->y_resolution=(double) mng_get_long(&p[4]);
3815 if ((int) p[8] == PNG_RESOLUTION_METER)
3817 image->units=PixelsPerCentimeterResolution;
3818 image->x_resolution=image->x_resolution/100.0f;
3819 image->y_resolution=image->y_resolution/100.0f;
3823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3828 if (memcmp(type,mng_iCCP,4) == 0)
3832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3839 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3841 if (memcmp(type,mng_IEND,4))
3851 Finish up reading image data:
3853 o read main image from color_blob.
3857 o if (color_type has alpha)
3858 if alpha_encoding is PNG
3859 read secondary image from alpha_blob via ReadPNG
3860 if alpha_encoding is JPEG
3861 read secondary image from alpha_blob via ReadJPEG
3865 o copy intensity of secondary image into
3866 opacity samples of main image.
3868 o destroy the secondary image.
3871 (void) CloseBlob(color_image);
3873 if (logging != MagickFalse)
3874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3875 " Reading jng_image from color_blob.");
3877 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3878 color_image->filename);
3880 color_image_info->ping=MagickFalse; /* To do: avoid this */
3881 jng_image=ReadImage(color_image_info,exception);
3883 if (jng_image == (Image *) NULL)
3884 return((Image *) NULL);
3886 (void) RelinquishUniqueFileResource(color_image->filename);
3887 color_image=DestroyImage(color_image);
3888 color_image_info=DestroyImageInfo(color_image_info);
3890 if (jng_image == (Image *) NULL)
3891 return((Image *) NULL);
3893 if (logging != MagickFalse)
3894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3895 " Copying jng_image pixels to main image.");
3897 image->rows=jng_height;
3898 image->columns=jng_width;
3899 length=image->columns*sizeof(PixelPacket);
3901 for (y=0; y < (ssize_t) image->rows; y++)
3903 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3904 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3905 (void) CopyMagickMemory(q,s,length);
3907 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3911 jng_image=DestroyImage(jng_image);
3913 if (image_info->ping == MagickFalse)
3915 if (jng_color_type >= 12)
3917 if (jng_alpha_compression_method == 0)
3921 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3922 PNGType(data,mng_IEND);
3923 LogPNGChunk(logging,mng_IEND,0L);
3924 (void) WriteBlob(alpha_image,4,data);
3925 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3928 (void) CloseBlob(alpha_image);
3930 if (logging != MagickFalse)
3931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3932 " Reading opacity from alpha_blob.");
3934 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3935 "%s",alpha_image->filename);
3937 jng_image=ReadImage(alpha_image_info,exception);
3939 if (jng_image != (Image *) NULL)
3940 for (y=0; y < (ssize_t) image->rows; y++)
3942 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3944 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3946 if (image->matte != MagickFalse)
3947 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3948 q->opacity=(Quantum) QuantumRange-s->red;
3951 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3953 q->opacity=(Quantum) QuantumRange-s->red;
3954 if (q->opacity != OpaqueOpacity)
3955 image->matte=MagickTrue;
3958 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3961 (void) RelinquishUniqueFileResource(alpha_image->filename);
3962 alpha_image=DestroyImage(alpha_image);
3963 alpha_image_info=DestroyImageInfo(alpha_image_info);
3964 if (jng_image != (Image *) NULL)
3965 jng_image=DestroyImage(jng_image);
3969 /* Read the JNG image. */
3971 if (mng_info->mng_type == 0)
3973 mng_info->mng_width=jng_width;
3974 mng_info->mng_height=jng_height;
3977 if (image->page.width == 0 && image->page.height == 0)
3979 image->page.width=jng_width;
3980 image->page.height=jng_height;
3983 if (image->page.x == 0 && image->page.y == 0)
3985 image->page.x=mng_info->x_off[mng_info->object_id];
3986 image->page.y=mng_info->y_off[mng_info->object_id];
3991 image->page.y=mng_info->y_off[mng_info->object_id];
3994 mng_info->image_found++;
3995 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3996 2*GetBlobSize(image));
3998 if (logging != MagickFalse)
3999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4000 " exit ReadOneJNGImage()");
4006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4010 % R e a d J N G I m a g e %
4014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4016 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4017 % (including the 8-byte signature) and returns it. It allocates the memory
4018 % necessary for the new Image structure and returns a pointer to the new
4021 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4023 % The format of the ReadJNGImage method is:
4025 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4028 % A description of each parameter follows:
4030 % o image_info: the image info.
4032 % o exception: return any errors or warnings in this structure.
4036 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4051 magic_number[MaxTextExtent];
4059 assert(image_info != (const ImageInfo *) NULL);
4060 assert(image_info->signature == MagickSignature);
4061 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4062 assert(exception != (ExceptionInfo *) NULL);
4063 assert(exception->signature == MagickSignature);
4064 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4065 image=AcquireImage(image_info);
4066 mng_info=(MngInfo *) NULL;
4067 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4069 if (status == MagickFalse)
4070 return((Image *) NULL);
4072 if (LocaleCompare(image_info->magick,"JNG") != 0)
4073 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4075 /* Verify JNG signature. */
4077 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4079 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4080 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4082 /* Allocate a MngInfo structure. */
4084 have_mng_structure=MagickFalse;
4085 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4087 if (mng_info == (MngInfo *) NULL)
4088 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4090 /* Initialize members of the MngInfo structure. */
4092 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4093 have_mng_structure=MagickTrue;
4095 mng_info->image=image;
4097 image=ReadOneJNGImage(mng_info,image_info,exception);
4098 MngInfoFreeStruct(mng_info,&have_mng_structure);
4100 if (image == (Image *) NULL)
4102 if (IsImageObject(previous) != MagickFalse)
4104 (void) CloseBlob(previous);
4105 (void) DestroyImageList(previous);
4108 if (logging != MagickFalse)
4109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4110 "exit ReadJNGImage() with error");
4112 return((Image *) NULL);
4114 (void) CloseBlob(image);
4116 if (image->columns == 0 || image->rows == 0)
4118 if (logging != MagickFalse)
4119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4120 "exit ReadJNGImage() with error");
4122 ThrowReaderException(CorruptImageError,"CorruptImage");
4125 if (logging != MagickFalse)
4126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4132 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4135 page_geometry[MaxTextExtent];
4168 #if defined(MNG_INSERT_LAYERS)
4170 mng_background_color;
4173 register unsigned char
4188 #if defined(MNG_INSERT_LAYERS)
4193 volatile unsigned int
4194 #ifdef MNG_OBJECT_BUFFERS
4195 mng_background_object=0,
4197 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4200 default_frame_timeout,
4202 #if defined(MNG_INSERT_LAYERS)
4208 /* These delays are all measured in image ticks_per_second,
4209 * not in MNG ticks_per_second
4212 default_frame_delay,
4216 #if defined(MNG_INSERT_LAYERS)
4225 previous_fb.bottom=0;
4227 previous_fb.right=0;
4229 default_fb.bottom=0;
4233 /* Open image file. */
4235 assert(image_info != (const ImageInfo *) NULL);
4236 assert(image_info->signature == MagickSignature);
4237 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4238 assert(exception != (ExceptionInfo *) NULL);
4239 assert(exception->signature == MagickSignature);
4240 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4241 image=AcquireImage(image_info);
4242 mng_info=(MngInfo *) NULL;
4243 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4245 if (status == MagickFalse)
4246 return((Image *) NULL);
4248 first_mng_object=MagickFalse;
4250 have_mng_structure=MagickFalse;
4252 /* Allocate a MngInfo structure. */
4254 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4256 if (mng_info == (MngInfo *) NULL)
4257 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4259 /* Initialize members of the MngInfo structure. */
4261 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4262 mng_info->image=image;
4263 have_mng_structure=MagickTrue;
4265 if (LocaleCompare(image_info->magick,"MNG") == 0)
4268 magic_number[MaxTextExtent];
4270 /* Verify MNG signature. */
4271 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4272 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4273 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4275 /* Initialize some nonzero members of the MngInfo structure. */
4276 for (i=0; i < MNG_MAX_OBJECTS; i++)
4278 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4279 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4281 mng_info->exists[0]=MagickTrue;
4284 first_mng_object=MagickTrue;
4286 #if defined(MNG_INSERT_LAYERS)
4287 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4289 default_frame_delay=0;
4290 default_frame_timeout=0;
4293 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4295 skip_to_iend=MagickFalse;
4296 term_chunk_found=MagickFalse;
4297 mng_info->framing_mode=1;
4298 #if defined(MNG_INSERT_LAYERS)
4299 mandatory_back=MagickFalse;
4301 #if defined(MNG_INSERT_LAYERS)
4302 mng_background_color=image->background_color;
4304 default_fb=mng_info->frame;
4305 previous_fb=mng_info->frame;
4309 type[MaxTextExtent];
4311 if (LocaleCompare(image_info->magick,"MNG") == 0)
4320 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4321 length=ReadBlobMSBLong(image);
4322 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4324 if (logging != MagickFalse)
4325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4326 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4327 type[0],type[1],type[2],type[3],(double) length);
4329 if (length > PNG_UINT_31_MAX)
4333 ThrowReaderException(CorruptImageError,"CorruptImage");
4336 chunk=(unsigned char *) NULL;
4340 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4342 if (chunk == (unsigned char *) NULL)
4343 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4345 for (i=0; i < (ssize_t) length; i++)
4346 chunk[i]=(unsigned char) ReadBlobByte(image);
4351 (void) ReadBlobMSBLong(image); /* read crc word */
4353 #if !defined(JNG_SUPPORTED)
4354 if (memcmp(type,mng_JHDR,4) == 0)
4356 skip_to_iend=MagickTrue;
4358 if (mng_info->jhdr_warning == 0)
4359 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4360 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4362 mng_info->jhdr_warning++;
4365 if (memcmp(type,mng_DHDR,4) == 0)
4367 skip_to_iend=MagickTrue;
4369 if (mng_info->dhdr_warning == 0)
4370 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4371 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4373 mng_info->dhdr_warning++;
4375 if (memcmp(type,mng_MEND,4) == 0)
4380 if (memcmp(type,mng_IEND,4) == 0)
4381 skip_to_iend=MagickFalse;
4384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4386 if (logging != MagickFalse)
4387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4393 if (memcmp(type,mng_MHDR,4) == 0)
4395 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4396 (p[2] << 8) | p[3]);
4398 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4399 (p[6] << 8) | p[7]);
4401 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " MNG width: %.20g",(double) mng_info->mng_width);
4405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4406 " MNG height: %.20g",(double) mng_info->mng_height);
4410 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4412 if (mng_info->ticks_per_second == 0)
4413 default_frame_delay=0;
4416 default_frame_delay=1UL*image->ticks_per_second/
4417 mng_info->ticks_per_second;
4419 frame_delay=default_frame_delay;
4425 simplicity=(size_t) mng_get_long(p);
4428 mng_type=1; /* Full MNG */
4430 if ((simplicity != 0) && ((simplicity | 11) == 11))
4431 mng_type=2; /* LC */
4433 if ((simplicity != 0) && ((simplicity | 9) == 9))
4434 mng_type=3; /* VLC */
4436 #if defined(MNG_INSERT_LAYERS)
4438 insert_layers=MagickTrue;
4440 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4442 /* Allocate next image structure. */
4443 AcquireNextImage(image_info,image);
4445 if (GetNextImageInList(image) == (Image *) NULL)
4446 return((Image *) NULL);
4448 image=SyncNextImageInList(image);
4449 mng_info->image=image;
4452 if ((mng_info->mng_width > 65535L) ||
4453 (mng_info->mng_height > 65535L))
4454 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4456 (void) FormatMagickString(page_geometry,MaxTextExtent,
4457 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4458 mng_info->mng_height);
4460 mng_info->frame.left=0;
4461 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4462 mng_info->frame.top=0;
4463 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4464 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4466 for (i=0; i < MNG_MAX_OBJECTS; i++)
4467 mng_info->object_clip[i]=mng_info->frame;
4469 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4473 if (memcmp(type,mng_TERM,4) == 0)
4484 final_delay=(png_uint_32) mng_get_long(&p[2]);
4485 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4487 if (mng_iterations == PNG_UINT_31_MAX)
4490 image->iterations=mng_iterations;
4491 term_chunk_found=MagickTrue;
4494 if (logging != MagickFalse)
4496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4497 " repeat=%d",repeat);
4499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4500 " final_delay=%.20g",(double) final_delay);
4502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4503 " image->iterations=%.20g",(double) image->iterations);
4506 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4509 if (memcmp(type,mng_DEFI,4) == 0)
4512 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4513 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4516 object_id=(p[0] << 8) | p[1];
4518 if (mng_type == 2 && object_id != 0)
4519 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4520 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4523 if (object_id > MNG_MAX_OBJECTS)
4526 Instead ofsuing a warning we should allocate a larger
4527 MngInfo structure and continue.
4529 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4530 CoderError,"object id too large","`%s'",image->filename);
4531 object_id=MNG_MAX_OBJECTS;
4534 if (mng_info->exists[object_id])
4535 if (mng_info->frozen[object_id])
4537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4538 (void) ThrowMagickException(&image->exception,
4539 GetMagickModule(),CoderError,
4540 "DEFI cannot redefine a frozen MNG object","`%s'",
4545 mng_info->exists[object_id]=MagickTrue;
4548 mng_info->invisible[object_id]=p[2];
4551 Extract object offset info.
4555 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4556 (p[5] << 16) | (p[6] << 8) | p[7]);
4558 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4559 (p[9] << 16) | (p[10] << 8) | p[11]);
4561 if (logging != MagickFalse)
4563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4564 " x_off[%d]: %.20g",object_id,(double)
4565 mng_info->x_off[object_id]);
4567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4568 " y_off[%d]: %.20g",object_id,(double)
4569 mng_info->y_off[object_id]);
4574 Extract object clipping info.
4577 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4583 if (memcmp(type,mng_bKGD,4) == 0)
4585 mng_info->have_global_bkgd=MagickFalse;
4589 mng_info->mng_global_bkgd.red=
4590 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4592 mng_info->mng_global_bkgd.green=
4593 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4595 mng_info->mng_global_bkgd.blue=
4596 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4598 mng_info->have_global_bkgd=MagickTrue;
4601 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4604 if (memcmp(type,mng_BACK,4) == 0)
4606 #if defined(MNG_INSERT_LAYERS)
4608 mandatory_back=p[6];
4613 if (mandatory_back && length > 5)
4615 mng_background_color.red=
4616 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4618 mng_background_color.green=
4619 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4621 mng_background_color.blue=
4622 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4624 mng_background_color.opacity=OpaqueOpacity;
4627 #ifdef MNG_OBJECT_BUFFERS
4629 mng_background_object=(p[7] << 8) | p[8];
4632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4636 if (memcmp(type,mng_PLTE,4) == 0)
4638 /* Read global PLTE. */
4640 if (length && (length < 769))
4642 if (mng_info->global_plte == (png_colorp) NULL)
4643 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4644 sizeof(*mng_info->global_plte));
4646 for (i=0; i < (ssize_t) (length/3); i++)
4648 mng_info->global_plte[i].red=p[3*i];
4649 mng_info->global_plte[i].green=p[3*i+1];
4650 mng_info->global_plte[i].blue=p[3*i+2];
4653 mng_info->global_plte_length=(unsigned int) (length/3);
4656 for ( ; i < 256; i++)
4658 mng_info->global_plte[i].red=i;
4659 mng_info->global_plte[i].green=i;
4660 mng_info->global_plte[i].blue=i;
4664 mng_info->global_plte_length=256;
4667 mng_info->global_plte_length=0;
4669 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4673 if (memcmp(type,mng_tRNS,4) == 0)
4675 /* read global tRNS */
4678 for (i=0; i < (ssize_t) length; i++)
4679 mng_info->global_trns[i]=p[i];
4682 for ( ; i < 256; i++)
4683 mng_info->global_trns[i]=255;
4685 mng_info->global_trns_length=(unsigned int) length;
4686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4689 if (memcmp(type,mng_gAMA,4) == 0)
4696 igamma=mng_get_long(p);
4697 mng_info->global_gamma=((float) igamma)*0.00001;
4698 mng_info->have_global_gama=MagickTrue;
4702 mng_info->have_global_gama=MagickFalse;
4704 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4708 if (memcmp(type,mng_cHRM,4) == 0)
4710 /* Read global cHRM */
4714 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4715 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4716 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4717 mng_info->global_chrm.red_primary.y=0.00001*
4718 mng_get_long(&p[12]);
4719 mng_info->global_chrm.green_primary.x=0.00001*
4720 mng_get_long(&p[16]);
4721 mng_info->global_chrm.green_primary.y=0.00001*
4722 mng_get_long(&p[20]);
4723 mng_info->global_chrm.blue_primary.x=0.00001*
4724 mng_get_long(&p[24]);
4725 mng_info->global_chrm.blue_primary.y=0.00001*
4726 mng_get_long(&p[28]);
4727 mng_info->have_global_chrm=MagickTrue;
4730 mng_info->have_global_chrm=MagickFalse;
4732 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4736 if (memcmp(type,mng_sRGB,4) == 0)
4743 mng_info->global_srgb_intent=
4744 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4745 mng_info->have_global_srgb=MagickTrue;
4748 mng_info->have_global_srgb=MagickFalse;
4750 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4754 if (memcmp(type,mng_iCCP,4) == 0)
4762 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4767 if (memcmp(type,mng_FRAM,4) == 0)
4770 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4771 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4774 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4775 image->delay=frame_delay;
4777 frame_delay=default_frame_delay;
4778 frame_timeout=default_frame_timeout;
4783 mng_info->framing_mode=p[0];
4785 if (logging != MagickFalse)
4786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4787 " Framing_mode=%d",mng_info->framing_mode);
4791 /* Note the delay and frame clipping boundaries. */
4793 p++; /* framing mode */
4795 while (*p && ((p-chunk) < (ssize_t) length))
4796 p++; /* frame name */
4798 p++; /* frame name terminator */
4800 if ((p-chunk) < (ssize_t) (length-4))
4807 change_delay=(*p++);
4808 change_timeout=(*p++);
4809 change_clipping=(*p++);
4810 p++; /* change_sync */
4814 frame_delay=1UL*image->ticks_per_second*
4817 if (mng_info->ticks_per_second != 0)
4818 frame_delay/=mng_info->ticks_per_second;
4821 frame_delay=PNG_UINT_31_MAX;
4823 if (change_delay == 2)
4824 default_frame_delay=frame_delay;
4828 if (logging != MagickFalse)
4829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4830 " Framing_delay=%.20g",(double) frame_delay);
4835 frame_timeout=1UL*image->ticks_per_second*
4838 if (mng_info->ticks_per_second != 0)
4839 frame_timeout/=mng_info->ticks_per_second;
4842 frame_timeout=PNG_UINT_31_MAX;
4844 if (change_delay == 2)
4845 default_frame_timeout=frame_timeout;
4849 if (logging != MagickFalse)
4850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4851 " Framing_timeout=%.20g",(double) frame_timeout);
4854 if (change_clipping)
4856 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4860 if (logging != MagickFalse)
4861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4862 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4863 (double) fb.left,(double) fb.right,(double) fb.top,
4864 (double) fb.bottom);
4866 if (change_clipping == 2)
4872 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4874 subframe_width=(size_t) (mng_info->clip.right
4875 -mng_info->clip.left);
4877 subframe_height=(size_t) (mng_info->clip.bottom
4878 -mng_info->clip.top);
4880 Insert a background layer behind the frame if framing_mode is 4.
4882 #if defined(MNG_INSERT_LAYERS)
4883 if (logging != MagickFalse)
4884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4885 " subframe_width=%.20g, subframe_height=%.20g",(double)
4886 subframe_width,(double) subframe_height);
4888 if (insert_layers && (mng_info->framing_mode == 4) &&
4889 (subframe_width) && (subframe_height))
4891 /* Allocate next image structure. */
4892 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4894 AcquireNextImage(image_info,image);
4896 if (GetNextImageInList(image) == (Image *) NULL)
4898 image=DestroyImageList(image);
4899 MngInfoFreeStruct(mng_info,&have_mng_structure);
4900 return((Image *) NULL);
4903 image=SyncNextImageInList(image);
4906 mng_info->image=image;
4908 if (term_chunk_found)
4910 image->start_loop=MagickTrue;
4911 image->iterations=mng_iterations;
4912 term_chunk_found=MagickFalse;
4916 image->start_loop=MagickFalse;
4918 image->columns=subframe_width;
4919 image->rows=subframe_height;
4920 image->page.width=subframe_width;
4921 image->page.height=subframe_height;
4922 image->page.x=mng_info->clip.left;
4923 image->page.y=mng_info->clip.top;
4924 image->background_color=mng_background_color;
4925 image->matte=MagickFalse;
4927 (void) SetImageBackgroundColor(image);
4929 if (logging != MagickFalse)
4930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4931 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4932 (double) mng_info->clip.left,(double) mng_info->clip.right,
4933 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4936 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4939 if (memcmp(type,mng_CLIP,4) == 0)
4948 first_object=(p[0] << 8) | p[1];
4949 last_object=(p[2] << 8) | p[3];
4951 for (i=(int) first_object; i <= (int) last_object; i++)
4953 if (mng_info->exists[i] && !mng_info->frozen[i])
4958 box=mng_info->object_clip[i];
4959 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4963 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4966 if (memcmp(type,mng_SAVE,4) == 0)
4968 for (i=1; i < MNG_MAX_OBJECTS; i++)
4969 if (mng_info->exists[i])
4971 mng_info->frozen[i]=MagickTrue;
4972 #ifdef MNG_OBJECT_BUFFERS
4973 if (mng_info->ob[i] != (MngBuffer *) NULL)
4974 mng_info->ob[i]->frozen=MagickTrue;
4979 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4984 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4986 /* Read DISC or SEEK. */
4988 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4990 for (i=1; i < MNG_MAX_OBJECTS; i++)
4991 MngInfoDiscardObject(mng_info,i);
4999 for (j=0; j < (ssize_t) length; j+=2)
5001 i=p[j] << 8 | p[j+1];
5002 MngInfoDiscardObject(mng_info,i);
5007 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5012 if (memcmp(type,mng_MOVE,4) == 0)
5020 first_object=(p[0] << 8) | p[1];
5021 last_object=(p[2] << 8) | p[3];
5022 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5024 if (mng_info->exists[i] && !mng_info->frozen[i])
5032 old_pair.a=mng_info->x_off[i];
5033 old_pair.b=mng_info->y_off[i];
5034 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5035 mng_info->x_off[i]=new_pair.a;
5036 mng_info->y_off[i]=new_pair.b;
5040 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5044 if (memcmp(type,mng_LOOP,4) == 0)
5046 ssize_t loop_iters=1;
5047 loop_level=chunk[0];
5048 mng_info->loop_active[loop_level]=1; /* mark loop active */
5050 /* Record starting point. */
5051 loop_iters=mng_get_long(&chunk[1]);
5053 if (logging != MagickFalse)
5054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5055 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5056 (double) loop_iters);
5058 if (loop_iters == 0)
5059 skipping_loop=loop_level;
5063 mng_info->loop_jump[loop_level]=TellBlob(image);
5064 mng_info->loop_count[loop_level]=loop_iters;
5067 mng_info->loop_iteration[loop_level]=0;
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5072 if (memcmp(type,mng_ENDL,4) == 0)
5074 loop_level=chunk[0];
5076 if (skipping_loop > 0)
5078 if (skipping_loop == loop_level)
5081 Found end of zero-iteration loop.
5084 mng_info->loop_active[loop_level]=0;
5090 if (mng_info->loop_active[loop_level] == 1)
5092 mng_info->loop_count[loop_level]--;
5093 mng_info->loop_iteration[loop_level]++;
5095 if (logging != MagickFalse)
5096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5098 (double) loop_level,(double)
5099 mng_info->loop_count[loop_level]);
5101 if (mng_info->loop_count[loop_level] != 0)
5103 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5107 ThrowReaderException(CorruptImageError,
5108 "ImproperImageHeader");
5119 mng_info->loop_active[loop_level]=0;
5121 for (i=0; i < loop_level; i++)
5122 if (mng_info->loop_active[i] == 1)
5123 last_level=(short) i;
5124 loop_level=last_level;
5129 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5133 if (memcmp(type,mng_CLON,4) == 0)
5135 if (mng_info->clon_warning == 0)
5136 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5137 CoderError,"CLON is not implemented yet","`%s'",
5140 mng_info->clon_warning++;
5143 if (memcmp(type,mng_MAGN,4) == 0)
5158 magn_first=(p[0] << 8) | p[1];
5164 magn_last=(p[2] << 8) | p[3];
5167 magn_last=magn_first;
5168 #ifndef MNG_OBJECT_BUFFERS
5169 if (magn_first || magn_last)
5170 if (mng_info->magn_warning == 0)
5172 (void) ThrowMagickException(&image->exception,
5173 GetMagickModule(),CoderError,
5174 "MAGN is not implemented yet for nonzero objects",
5175 "`%s'",image->filename);
5177 mng_info->magn_warning++;
5187 magn_mx=(p[5] << 8) | p[6];
5196 magn_my=(p[7] << 8) | p[8];
5205 magn_ml=(p[9] << 8) | p[10];
5214 magn_mr=(p[11] << 8) | p[12];
5223 magn_mt=(p[13] << 8) | p[14];
5232 magn_mb=(p[15] << 8) | p[16];
5244 magn_methy=magn_methx;
5247 if (magn_methx > 5 || magn_methy > 5)
5248 if (mng_info->magn_warning == 0)
5250 (void) ThrowMagickException(&image->exception,
5251 GetMagickModule(),CoderError,
5252 "Unknown MAGN method in MNG datastream","`%s'",
5255 mng_info->magn_warning++;
5257 #ifdef MNG_OBJECT_BUFFERS
5258 /* Magnify existing objects in the range magn_first to magn_last */
5260 if (magn_first == 0 || magn_last == 0)
5262 /* Save the magnification factors for object 0 */
5263 mng_info->magn_mb=magn_mb;
5264 mng_info->magn_ml=magn_ml;
5265 mng_info->magn_mr=magn_mr;
5266 mng_info->magn_mt=magn_mt;
5267 mng_info->magn_mx=magn_mx;
5268 mng_info->magn_my=magn_my;
5269 mng_info->magn_methx=magn_methx;
5270 mng_info->magn_methy=magn_methy;
5274 if (memcmp(type,mng_PAST,4) == 0)
5276 if (mng_info->past_warning == 0)
5277 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5278 CoderError,"PAST is not implemented yet","`%s'",
5281 mng_info->past_warning++;
5284 if (memcmp(type,mng_SHOW,4) == 0)
5286 if (mng_info->show_warning == 0)
5287 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5288 CoderError,"SHOW is not implemented yet","`%s'",
5291 mng_info->show_warning++;
5294 if (memcmp(type,mng_sBIT,4) == 0)
5297 mng_info->have_global_sbit=MagickFalse;
5301 mng_info->global_sbit.gray=p[0];
5302 mng_info->global_sbit.red=p[0];
5303 mng_info->global_sbit.green=p[1];
5304 mng_info->global_sbit.blue=p[2];
5305 mng_info->global_sbit.alpha=p[3];
5306 mng_info->have_global_sbit=MagickTrue;
5309 if (memcmp(type,mng_pHYs,4) == 0)
5313 mng_info->global_x_pixels_per_unit=
5314 (size_t) mng_get_long(p);
5315 mng_info->global_y_pixels_per_unit=
5316 (size_t) mng_get_long(&p[4]);
5317 mng_info->global_phys_unit_type=p[8];
5318 mng_info->have_global_phys=MagickTrue;
5322 mng_info->have_global_phys=MagickFalse;
5324 if (memcmp(type,mng_pHYg,4) == 0)
5326 if (mng_info->phyg_warning == 0)
5327 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5328 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5330 mng_info->phyg_warning++;
5332 if (memcmp(type,mng_BASI,4) == 0)
5334 skip_to_iend=MagickTrue;
5336 if (mng_info->basi_warning == 0)
5337 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5338 CoderError,"BASI is not implemented yet","`%s'",
5341 mng_info->basi_warning++;
5342 #ifdef MNG_BASI_SUPPORTED
5343 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5344 (p[2] << 8) | p[3]);
5345 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5346 (p[6] << 8) | p[7]);
5347 basi_color_type=p[8];
5348 basi_compression_method=p[9];
5349 basi_filter_type=p[10];
5350 basi_interlace_method=p[11];
5352 basi_red=(p[12] << 8) & p[13];
5358 basi_green=(p[14] << 8) & p[15];
5364 basi_blue=(p[16] << 8) & p[17];
5370 basi_alpha=(p[18] << 8) & p[19];
5374 if (basi_sample_depth == 16)
5381 basi_viewable=p[20];
5387 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5391 if (memcmp(type,mng_IHDR,4)
5392 #if defined(JNG_SUPPORTED)
5393 && memcmp(type,mng_JHDR,4)
5397 /* Not an IHDR or JHDR chunk */
5399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5404 if (logging != MagickFalse)
5405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5406 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5408 mng_info->exists[object_id]=MagickTrue;
5409 mng_info->viewable[object_id]=MagickTrue;
5411 if (mng_info->invisible[object_id])
5413 if (logging != MagickFalse)
5414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5415 " Skipping invisible object");
5417 skip_to_iend=MagickTrue;
5418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5421 #if defined(MNG_INSERT_LAYERS)
5423 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5425 image_width=(size_t) mng_get_long(p);
5426 image_height=(size_t) mng_get_long(&p[4]);
5428 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5431 Insert a transparent background layer behind the entire animation
5432 if it is not full screen.
5434 #if defined(MNG_INSERT_LAYERS)
5435 if (insert_layers && mng_type && first_mng_object)
5437 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5438 (image_width < mng_info->mng_width) ||
5439 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5440 (image_height < mng_info->mng_height) ||
5441 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5443 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5446 Allocate next image structure.
5448 AcquireNextImage(image_info,image);
5450 if (GetNextImageInList(image) == (Image *) NULL)
5452 image=DestroyImageList(image);
5453 MngInfoFreeStruct(mng_info,&have_mng_structure);
5454 return((Image *) NULL);
5457 image=SyncNextImageInList(image);
5459 mng_info->image=image;
5461 if (term_chunk_found)
5463 image->start_loop=MagickTrue;
5464 image->iterations=mng_iterations;
5465 term_chunk_found=MagickFalse;
5469 image->start_loop=MagickFalse;
5471 /* Make a background rectangle. */
5474 image->columns=mng_info->mng_width;
5475 image->rows=mng_info->mng_height;
5476 image->page.width=mng_info->mng_width;
5477 image->page.height=mng_info->mng_height;
5480 image->background_color=mng_background_color;
5481 (void) SetImageBackgroundColor(image);
5482 if (logging != MagickFalse)
5483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5484 " Inserted transparent background layer, W=%.20g, H=%.20g",
5485 (double) mng_info->mng_width,(double) mng_info->mng_height);
5489 Insert a background layer behind the upcoming image if
5490 framing_mode is 3, and we haven't already inserted one.
5492 if (insert_layers && (mng_info->framing_mode == 3) &&
5493 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5494 (simplicity & 0x08)))
5496 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5499 Allocate next image structure.
5501 AcquireNextImage(image_info,image);
5503 if (GetNextImageInList(image) == (Image *) NULL)
5505 image=DestroyImageList(image);
5506 MngInfoFreeStruct(mng_info,&have_mng_structure);
5507 return((Image *) NULL);
5510 image=SyncNextImageInList(image);
5513 mng_info->image=image;
5515 if (term_chunk_found)
5517 image->start_loop=MagickTrue;
5518 image->iterations=mng_iterations;
5519 term_chunk_found=MagickFalse;
5523 image->start_loop=MagickFalse;
5526 image->columns=subframe_width;
5527 image->rows=subframe_height;
5528 image->page.width=subframe_width;
5529 image->page.height=subframe_height;
5530 image->page.x=mng_info->clip.left;
5531 image->page.y=mng_info->clip.top;
5532 image->background_color=mng_background_color;
5533 image->matte=MagickFalse;
5534 (void) SetImageBackgroundColor(image);
5536 if (logging != MagickFalse)
5537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5538 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5539 (double) mng_info->clip.left,(double) mng_info->clip.right,
5540 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5542 #endif /* MNG_INSERT_LAYERS */
5543 first_mng_object=MagickFalse;
5545 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5548 Allocate next image structure.
5550 AcquireNextImage(image_info,image);
5552 if (GetNextImageInList(image) == (Image *) NULL)
5554 image=DestroyImageList(image);
5555 MngInfoFreeStruct(mng_info,&have_mng_structure);
5556 return((Image *) NULL);
5559 image=SyncNextImageInList(image);
5561 mng_info->image=image;
5562 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5563 GetBlobSize(image));
5565 if (status == MagickFalse)
5568 if (term_chunk_found)
5570 image->start_loop=MagickTrue;
5571 term_chunk_found=MagickFalse;
5575 image->start_loop=MagickFalse;
5577 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5579 image->delay=frame_delay;
5580 frame_delay=default_frame_delay;
5586 image->page.width=mng_info->mng_width;
5587 image->page.height=mng_info->mng_height;
5588 image->page.x=mng_info->x_off[object_id];
5589 image->page.y=mng_info->y_off[object_id];
5590 image->iterations=mng_iterations;
5593 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5596 if (logging != MagickFalse)
5597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5598 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5601 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5604 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5608 mng_info->image=image;
5609 mng_info->mng_type=mng_type;
5610 mng_info->object_id=object_id;
5612 if (memcmp(type,mng_IHDR,4) == 0)
5613 image=ReadOnePNGImage(mng_info,image_info,exception);
5615 #if defined(JNG_SUPPORTED)
5617 image=ReadOneJNGImage(mng_info,image_info,exception);
5620 if (image == (Image *) NULL)
5622 if (IsImageObject(previous) != MagickFalse)
5624 (void) DestroyImageList(previous);
5625 (void) CloseBlob(previous);
5628 MngInfoFreeStruct(mng_info,&have_mng_structure);
5629 return((Image *) NULL);
5632 if (image->columns == 0 || image->rows == 0)
5634 (void) CloseBlob(image);
5635 image=DestroyImageList(image);
5636 MngInfoFreeStruct(mng_info,&have_mng_structure);
5637 return((Image *) NULL);
5640 mng_info->image=image;
5647 if (mng_info->magn_methx || mng_info->magn_methy)
5653 if (logging != MagickFalse)
5654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5655 " Processing MNG MAGN chunk");
5657 if (mng_info->magn_methx == 1)
5659 magnified_width=mng_info->magn_ml;
5661 if (image->columns > 1)
5662 magnified_width += mng_info->magn_mr;
5664 if (image->columns > 2)
5665 magnified_width += (png_uint_32)
5666 ((image->columns-2)*(mng_info->magn_mx));
5671 magnified_width=(png_uint_32) image->columns;
5673 if (image->columns > 1)
5674 magnified_width += mng_info->magn_ml-1;
5676 if (image->columns > 2)
5677 magnified_width += mng_info->magn_mr-1;
5679 if (image->columns > 3)
5680 magnified_width += (png_uint_32)
5681 ((image->columns-3)*(mng_info->magn_mx-1));
5684 if (mng_info->magn_methy == 1)
5686 magnified_height=mng_info->magn_mt;
5688 if (image->rows > 1)
5689 magnified_height += mng_info->magn_mb;
5691 if (image->rows > 2)
5692 magnified_height += (png_uint_32)
5693 ((image->rows-2)*(mng_info->magn_my));
5698 magnified_height=(png_uint_32) image->rows;
5700 if (image->rows > 1)
5701 magnified_height += mng_info->magn_mt-1;
5703 if (image->rows > 2)
5704 magnified_height += mng_info->magn_mb-1;
5706 if (image->rows > 3)
5707 magnified_height += (png_uint_32)
5708 ((image->rows-3)*(mng_info->magn_my-1));
5711 if (magnified_height > image->rows ||
5712 magnified_width > image->columns)
5727 register PixelPacket
5739 /* Allocate next image structure. */
5741 if (logging != MagickFalse)
5742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5743 " Allocate magnified image");
5745 AcquireNextImage(image_info,image);
5747 if (GetNextImageInList(image) == (Image *) NULL)
5749 image=DestroyImageList(image);
5750 MngInfoFreeStruct(mng_info,&have_mng_structure);
5751 return((Image *) NULL);
5754 large_image=SyncNextImageInList(image);
5756 large_image->columns=magnified_width;
5757 large_image->rows=magnified_height;
5759 magn_methx=mng_info->magn_methx;
5760 magn_methy=mng_info->magn_methy;
5762 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5763 #define QM unsigned short
5764 if (magn_methx != 1 || magn_methy != 1)
5767 Scale pixels to unsigned shorts to prevent
5768 overflow of intermediate values of interpolations
5770 for (y=0; y < (ssize_t) image->rows; y++)
5772 q=GetAuthenticPixels(image,0,y,image->columns,1,
5775 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5777 q->red=ScaleQuantumToShort(q->red);
5778 q->green=ScaleQuantumToShort(q->green);
5779 q->blue=ScaleQuantumToShort(q->blue);
5780 q->opacity=ScaleQuantumToShort(q->opacity);
5784 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5792 if (image->matte != MagickFalse)
5793 (void) SetImageBackgroundColor(large_image);
5797 large_image->background_color.opacity=OpaqueOpacity;
5798 (void) SetImageBackgroundColor(large_image);
5800 if (magn_methx == 4)
5803 if (magn_methx == 5)
5806 if (magn_methy == 4)
5809 if (magn_methy == 5)
5813 /* magnify the rows into the right side of the large image */
5815 if (logging != MagickFalse)
5816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5817 " Magnify the rows to %.20g",(double) large_image->rows);
5818 m=(ssize_t) mng_info->magn_mt;
5820 length=(size_t) image->columns;
5821 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5822 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5824 if ((prev == (PixelPacket *) NULL) ||
5825 (next == (PixelPacket *) NULL))
5827 image=DestroyImageList(image);
5828 MngInfoFreeStruct(mng_info,&have_mng_structure);
5829 ThrowReaderException(ResourceLimitError,
5830 "MemoryAllocationFailed");
5833 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5834 (void) CopyMagickMemory(next,n,length);
5836 for (y=0; y < (ssize_t) image->rows; y++)
5839 m=(ssize_t) mng_info->magn_mt;
5841 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5842 m=(ssize_t) mng_info->magn_mb;
5844 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5845 m=(ssize_t) mng_info->magn_mb;
5847 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5851 m=(ssize_t) mng_info->magn_my;
5857 if (y < (ssize_t) image->rows-1)
5859 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5861 (void) CopyMagickMemory(next,n,length);
5864 for (i=0; i < m; i++, yy++)
5866 register PixelPacket
5869 assert(yy < (ssize_t) large_image->rows);
5872 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5874 q+=(large_image->columns-image->columns);
5876 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5878 /* To do: get color as function of indexes[x] */
5880 if (image->storage_class == PseudoClass)
5885 if (magn_methy <= 1)
5887 *q=(*pixels); /* replicate previous */
5890 else if (magn_methy == 2 || magn_methy == 4)
5898 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5899 -(*pixels).red)+m))/((ssize_t) (m*2))
5901 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5902 -(*pixels).green)+m))/((ssize_t) (m*2))
5904 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5905 -(*pixels).blue)+m))/((ssize_t) (m*2))
5908 if (image->matte != MagickFalse)
5909 (*q).opacity=(QM) (((ssize_t)
5911 -(*pixels).opacity)+m))
5912 /((ssize_t) (m*2))+(*pixels).opacity);
5915 if (magn_methy == 4)
5917 /* Replicate nearest */
5918 if (i <= ((m+1) << 1))
5919 (*q).opacity=(*pixels).opacity+0;
5921 (*q).opacity=(*n).opacity+0;
5925 else /* if (magn_methy == 3 || magn_methy == 5) */
5927 /* Replicate nearest */
5928 if (i <= ((m+1) << 1))
5934 if (magn_methy == 5)
5936 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5937 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5938 +(*pixels).opacity);
5946 if (SyncAuthenticPixels(large_image,exception) == 0)
5952 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5953 next=(PixelPacket *) RelinquishMagickMemory(next);
5955 length=image->columns;
5957 if (logging != MagickFalse)
5958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5959 " Delete original image");
5961 DeleteImageFromList(&image);
5965 mng_info->image=image;
5967 /* magnify the columns */
5968 if (logging != MagickFalse)
5969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5970 " Magnify the columns to %.20g",(double) image->columns);
5972 for (y=0; y < (ssize_t) image->rows; y++)
5974 register PixelPacket
5977 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5978 pixels=q+(image->columns-length);
5981 for (x=(ssize_t) (image->columns-length);
5982 x < (ssize_t) image->columns; x++)
5984 if (x == (ssize_t) (image->columns-length))
5985 m=(ssize_t) mng_info->magn_ml;
5987 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5988 m=(ssize_t) mng_info->magn_mr;
5990 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5991 m=(ssize_t) mng_info->magn_mr;
5993 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5997 m=(ssize_t) mng_info->magn_mx;
5999 for (i=0; i < m; i++)
6001 if (magn_methx <= 1)
6003 /* replicate previous */
6007 else if (magn_methx == 2 || magn_methx == 4)
6015 (*q).red=(QM) ((2*i*((*n).red
6017 /((ssize_t) (m*2))+(*pixels).red);
6018 (*q).green=(QM) ((2*i*((*n).green
6020 +m)/((ssize_t) (m*2))+(*pixels).green);
6021 (*q).blue=(QM) ((2*i*((*n).blue
6023 /((ssize_t) (m*2))+(*pixels).blue);
6024 if (image->matte != MagickFalse)
6025 (*q).opacity=(QM) ((2*i*((*n).opacity
6026 -(*pixels).opacity)+m)/((ssize_t) (m*2))
6027 +(*pixels).opacity);
6030 if (magn_methx == 4)
6032 /* Replicate nearest */
6033 if (i <= ((m+1) << 1))
6034 (*q).opacity=(*pixels).opacity+0;
6036 (*q).opacity=(*n).opacity+0;
6040 else /* if (magn_methx == 3 || magn_methx == 5) */
6042 /* Replicate nearest */
6043 if (i <= ((m+1) << 1))
6049 if (magn_methx == 5)
6052 (*q).opacity=(QM) ((2*i*((*n).opacity
6053 -(*pixels).opacity)+m) /((ssize_t) (m*2))
6054 +(*pixels).opacity);
6063 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6066 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
6067 if (magn_methx != 1 || magn_methy != 1)
6070 Rescale pixels to Quantum
6072 for (y=0; y < (ssize_t) image->rows; y++)
6074 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6076 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6078 q->red=ScaleShortToQuantum(q->red);
6079 q->green=ScaleShortToQuantum(q->green);
6080 q->blue=ScaleShortToQuantum(q->blue);
6081 q->opacity=ScaleShortToQuantum(q->opacity);
6085 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6090 if (logging != MagickFalse)
6091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6092 " Finished MAGN processing");
6097 Crop_box is with respect to the upper left corner of the MNG.
6099 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6100 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6101 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6102 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6103 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6104 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6105 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6106 if ((crop_box.left != (mng_info->image_box.left
6107 +mng_info->x_off[object_id])) ||
6108 (crop_box.right != (mng_info->image_box.right
6109 +mng_info->x_off[object_id])) ||
6110 (crop_box.top != (mng_info->image_box.top
6111 +mng_info->y_off[object_id])) ||
6112 (crop_box.bottom != (mng_info->image_box.bottom
6113 +mng_info->y_off[object_id])))
6115 if (logging != MagickFalse)
6116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6117 " Crop the PNG image");
6119 if ((crop_box.left < crop_box.right) &&
6120 (crop_box.top < crop_box.bottom))
6129 Crop_info is with respect to the upper left corner of
6132 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6133 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6134 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6135 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6136 image->page.width=image->columns;
6137 image->page.height=image->rows;
6140 im=CropImage(image,&crop_info,exception);
6142 if (im != (Image *) NULL)
6144 image->columns=im->columns;
6145 image->rows=im->rows;
6146 im=DestroyImage(im);
6147 image->page.width=image->columns;
6148 image->page.height=image->rows;
6149 image->page.x=crop_box.left;
6150 image->page.y=crop_box.top;
6157 No pixels in crop area. The MNG spec still requires
6158 a layer, though, so make a single transparent pixel in
6159 the top left corner.
6164 (void) SetImageBackgroundColor(image);
6165 image->page.width=1;
6166 image->page.height=1;
6171 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6172 image=mng_info->image;
6176 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6177 /* PNG does not handle depths greater than 16 so reduce it even
6180 if (image->depth > 16)
6184 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6185 if (LosslessReduceDepthOK(image) != MagickFalse)
6189 GetImageException(image,exception);
6191 if (image_info->number_scenes != 0)
6193 if (mng_info->scenes_found >
6194 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6198 if (logging != MagickFalse)
6199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6200 " Finished reading image datastream.");
6202 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6204 (void) CloseBlob(image);
6206 if (logging != MagickFalse)
6207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6208 " Finished reading all image datastreams.");
6210 #if defined(MNG_INSERT_LAYERS)
6211 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6212 (mng_info->mng_height))
6215 Insert a background layer if nothing else was found.
6217 if (logging != MagickFalse)
6218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6219 " No images found. Inserting a background layer.");
6221 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6224 Allocate next image structure.
6226 AcquireNextImage(image_info,image);
6227 if (GetNextImageInList(image) == (Image *) NULL)
6229 image=DestroyImageList(image);
6230 MngInfoFreeStruct(mng_info,&have_mng_structure);
6232 if (logging != MagickFalse)
6233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6234 " Allocation failed, returning NULL.");
6236 return((Image *) NULL);
6238 image=SyncNextImageInList(image);
6240 image->columns=mng_info->mng_width;
6241 image->rows=mng_info->mng_height;
6242 image->page.width=mng_info->mng_width;
6243 image->page.height=mng_info->mng_height;
6246 image->background_color=mng_background_color;
6247 image->matte=MagickFalse;
6249 if (image_info->ping == MagickFalse)
6250 (void) SetImageBackgroundColor(image);
6252 mng_info->image_found++;
6255 image->iterations=mng_iterations;
6257 if (mng_iterations == 1)
6258 image->start_loop=MagickTrue;
6260 while (GetPreviousImageInList(image) != (Image *) NULL)
6263 if (image_count > 10*mng_info->image_found)
6265 if (logging != MagickFalse)
6266 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6268 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6269 CoderError,"Linked list is corrupted, beginning of list not found",
6270 "`%s'",image_info->filename);
6272 return((Image *) NULL);
6275 image=GetPreviousImageInList(image);
6277 if (GetNextImageInList(image) == (Image *) NULL)
6279 if (logging != MagickFalse)
6280 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6282 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6283 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6284 image_info->filename);
6288 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6289 GetNextImageInList(image) ==
6292 if (logging != MagickFalse)
6293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6294 " First image null");
6296 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6297 CoderError,"image->next for first image is NULL but shouldn't be.",
6298 "`%s'",image_info->filename);
6301 if (mng_info->image_found == 0)
6303 if (logging != MagickFalse)
6304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6305 " No visible images found.");
6307 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6308 CoderError,"No visible images in file","`%s'",image_info->filename);
6310 if (image != (Image *) NULL)
6311 image=DestroyImageList(image);
6313 MngInfoFreeStruct(mng_info,&have_mng_structure);
6314 return((Image *) NULL);
6317 if (mng_info->ticks_per_second)
6318 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6319 final_delay/mng_info->ticks_per_second;
6322 image->start_loop=MagickTrue;
6324 /* Find final nonzero image delay */
6325 final_image_delay=0;
6327 while (GetNextImageInList(image) != (Image *) NULL)
6330 final_image_delay=image->delay;
6332 image=GetNextImageInList(image);
6335 if (final_delay < final_image_delay)
6336 final_delay=final_image_delay;
6338 image->delay=final_delay;
6340 if (logging != MagickFalse)
6341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6342 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6343 (double) final_delay);
6345 if (logging != MagickFalse)
6351 image=GetFirstImageInList(image);
6353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6354 " Before coalesce:");
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6357 " scene 0 delay=%.20g",(double) image->delay);
6359 while (GetNextImageInList(image) != (Image *) NULL)
6361 image=GetNextImageInList(image);
6362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6363 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6367 image=GetFirstImageInList(image);
6368 #ifdef MNG_COALESCE_LAYERS
6378 if (logging != MagickFalse)
6379 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6382 next_image=CoalesceImages(image,&image->exception);
6384 if (next_image == (Image *) NULL)
6385 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6387 image=DestroyImageList(image);
6390 for (next=image; next != (Image *) NULL; next=next_image)
6392 next->page.width=mng_info->mng_width;
6393 next->page.height=mng_info->mng_height;
6396 next->scene=scene++;
6397 next_image=GetNextImageInList(next);
6399 if (next_image == (Image *) NULL)
6402 if (next->delay == 0)
6405 next_image->previous=GetPreviousImageInList(next);
6406 if (GetPreviousImageInList(next) == (Image *) NULL)
6409 next->previous->next=next_image;
6410 next=DestroyImage(next);
6416 while (GetNextImageInList(image) != (Image *) NULL)
6417 image=GetNextImageInList(image);
6419 image->dispose=BackgroundDispose;
6421 if (logging != MagickFalse)
6427 image=GetFirstImageInList(image);
6429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6430 " After coalesce:");
6432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6433 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6434 (double) image->dispose);
6436 while (GetNextImageInList(image) != (Image *) NULL)
6438 image=GetNextImageInList(image);
6440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6441 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6442 (double) image->delay,(double) image->dispose);
6446 image=GetFirstImageInList(image);
6447 MngInfoFreeStruct(mng_info,&have_mng_structure);
6448 have_mng_structure=MagickFalse;
6450 if (logging != MagickFalse)
6451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6453 return(GetFirstImageInList(image));
6455 #else /* PNG_LIBPNG_VER > 10011 */
6456 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6458 printf("Your PNG library is too old: You have libpng-%s\n",
6459 PNG_LIBPNG_VER_STRING);
6461 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6462 "PNG library is too old","`%s'",image_info->filename);
6464 return(Image *) NULL;
6467 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6469 return(ReadPNGImage(image_info,exception));
6471 #endif /* PNG_LIBPNG_VER > 10011 */
6475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6479 % R e g i s t e r P N G I m a g e %
6483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6485 % RegisterPNGImage() adds properties for the PNG image format to
6486 % the list of supported formats. The properties include the image format
6487 % tag, a method to read and/or write the format, whether the format
6488 % supports the saving of more than one frame to the same file or blob,
6489 % whether the format supports native in-memory I/O, and a brief
6490 % description of the format.
6492 % The format of the RegisterPNGImage method is:
6494 % size_t RegisterPNGImage(void)
6497 ModuleExport size_t RegisterPNGImage(void)
6500 version[MaxTextExtent];
6508 "See http://www.libpng.org/ for details about the PNG format."
6513 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6519 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6525 #if defined(PNG_LIBPNG_VER_STRING)
6526 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6527 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6529 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6531 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6532 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6537 entry=SetMagickInfo("MNG");
6538 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6540 #if defined(MAGICKCORE_PNG_DELEGATE)
6541 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6542 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6545 entry->magick=(IsImageFormatHandler *) IsMNG;
6546 entry->description=ConstantString("Multiple-image Network Graphics");
6548 if (*version != '\0')
6549 entry->version=ConstantString(version);
6551 entry->module=ConstantString("PNG");
6552 entry->note=ConstantString(MNGNote);
6553 (void) RegisterMagickInfo(entry);
6555 entry=SetMagickInfo("PNG");
6557 #if defined(MAGICKCORE_PNG_DELEGATE)
6558 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6559 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6562 entry->magick=(IsImageFormatHandler *) IsPNG;
6563 entry->adjoin=MagickFalse;
6564 entry->description=ConstantString("Portable Network Graphics");
6565 entry->module=ConstantString("PNG");
6567 if (*version != '\0')
6568 entry->version=ConstantString(version);
6570 entry->note=ConstantString(PNGNote);
6571 (void) RegisterMagickInfo(entry);
6573 entry=SetMagickInfo("PNG8");
6575 #if defined(MAGICKCORE_PNG_DELEGATE)
6576 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6577 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6580 entry->magick=(IsImageFormatHandler *) IsPNG;
6581 entry->adjoin=MagickFalse;
6582 entry->description=ConstantString(
6583 "8-bit indexed with optional binary transparency");
6584 entry->module=ConstantString("PNG");
6585 (void) RegisterMagickInfo(entry);
6587 entry=SetMagickInfo("PNG24");
6590 #if defined(ZLIB_VERSION)
6591 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6592 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6594 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6596 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6597 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6601 if (*version != '\0')
6602 entry->version=ConstantString(version);
6604 #if defined(MAGICKCORE_PNG_DELEGATE)
6605 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6606 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6609 entry->magick=(IsImageFormatHandler *) IsPNG;
6610 entry->adjoin=MagickFalse;
6611 entry->description=ConstantString("opaque 24-bit RGB");
6612 entry->module=ConstantString("PNG");
6613 (void) RegisterMagickInfo(entry);
6615 entry=SetMagickInfo("PNG32");
6617 #if defined(MAGICKCORE_PNG_DELEGATE)
6618 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6619 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6622 entry->magick=(IsImageFormatHandler *) IsPNG;
6623 entry->adjoin=MagickFalse;
6624 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6625 entry->module=ConstantString("PNG");
6626 (void) RegisterMagickInfo(entry);
6628 entry=SetMagickInfo("JNG");
6630 #if defined(JNG_SUPPORTED)
6631 #if defined(MAGICKCORE_PNG_DELEGATE)
6632 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6633 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6637 entry->magick=(IsImageFormatHandler *) IsJNG;
6638 entry->adjoin=MagickFalse;
6639 entry->description=ConstantString("JPEG Network Graphics");
6640 entry->module=ConstantString("PNG");
6641 entry->note=ConstantString(JNGNote);
6642 (void) RegisterMagickInfo(entry);
6644 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6645 ping_semaphore=AllocateSemaphoreInfo();
6648 return(MagickImageCoderSignature);
6652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6656 % U n r e g i s t e r P N G I m a g e %
6660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6662 % UnregisterPNGImage() removes format registrations made by the
6663 % PNG module from the list of supported formats.
6665 % The format of the UnregisterPNGImage method is:
6667 % UnregisterPNGImage(void)
6670 ModuleExport void UnregisterPNGImage(void)
6672 (void) UnregisterMagickInfo("MNG");
6673 (void) UnregisterMagickInfo("PNG");
6674 (void) UnregisterMagickInfo("PNG8");
6675 (void) UnregisterMagickInfo("PNG24");
6676 (void) UnregisterMagickInfo("PNG32");
6677 (void) UnregisterMagickInfo("JNG");
6679 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6680 if (ping_semaphore != (SemaphoreInfo *) NULL)
6681 DestroySemaphoreInfo(&ping_semaphore);
6685 #if defined(MAGICKCORE_PNG_DELEGATE)
6686 #if PNG_LIBPNG_VER > 10011
6688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6692 % W r i t e M N G I m a g e %
6696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6698 % WriteMNGImage() writes an image in the Portable Network Graphics
6699 % Group's "Multiple-image Network Graphics" encoded image format.
6701 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6703 % The format of the WriteMNGImage method is:
6705 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6707 % A description of each parameter follows.
6709 % o image_info: the image info.
6711 % o image: The image.
6714 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6715 % "To do" under ReadPNGImage):
6717 % Preserve all unknown and not-yet-handled known chunks found in input
6718 % PNG file and copy them into output PNG files according to the PNG
6721 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6723 % Improve selection of color type (use indexed-colour or indexed-colour
6724 % with tRNS when 256 or fewer unique RGBA values are present).
6726 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6727 % This will be complicated if we limit ourselves to generating MNG-LC
6728 % files. For now we ignore disposal method 3 and simply overlay the next
6731 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6732 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6733 % [mostly done 15 June 1999 but still need to take care of tRNS]
6735 % Check for identical sRGB and replace with a global sRGB (and remove
6736 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6737 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6738 % local gAMA/cHRM with local sRGB if appropriate).
6740 % Check for identical sBIT chunks and write global ones.
6742 % Provide option to skip writing the signature tEXt chunks.
6744 % Use signatures to detect identical objects and reuse the first
6745 % instance of such objects instead of writing duplicate objects.
6747 % Use a smaller-than-32k value of compression window size when
6750 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6751 % ancillary text chunks and save profiles.
6753 % Provide an option to force LC files (to ensure exact framing rate)
6756 % Provide an option to force VLC files instead of LC, even when offsets
6757 % are present. This will involve expanding the embedded images with a
6758 % transparent region at the top and/or left.
6762 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6763 png_info *ping_info, unsigned char *profile_type, unsigned char
6764 *profile_description, unsigned char *profile_data, png_uint_32 length)
6783 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6785 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6788 if (image_info->verbose)
6790 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6791 (char *) profile_type, (double) length);
6794 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6795 description_length=(png_uint_32) strlen((const char *) profile_description);
6796 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6797 + description_length);
6798 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6799 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6800 text[0].key[0]='\0';
6801 (void) ConcatenateMagickString(text[0].key,
6802 "Raw profile type ",MaxTextExtent);
6803 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6807 (void) CopyMagickString(dp,(const char *) profile_description,
6809 dp+=description_length;
6811 (void) FormatMagickString(dp,allocated_length-
6812 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6815 for (i=0; i < (ssize_t) length; i++)
6819 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6820 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6825 text[0].text_length=(png_size_t) (dp-text[0].text);
6826 text[0].compression=image_info->compression == NoCompression ||
6827 (image_info->compression == UndefinedCompression &&
6828 text[0].text_length < 128) ? -1 : 0;
6830 if (text[0].text_length <= allocated_length)
6831 png_set_text(ping,ping_info,text,1);
6833 png_free(ping,text[0].text);
6834 png_free(ping,text[0].key);
6835 png_free(ping,text);
6838 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6839 const char *string, MagickBooleanType logging)
6852 ResetImageProfileIterator(image);
6854 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6856 profile=GetImageProfile(image,name);
6858 if (profile != (const StringInfo *) NULL)
6863 if (LocaleNCompare(name,string,11) == 0)
6865 if (logging != MagickFalse)
6866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6867 " Found %s profile",name);
6869 ping_profile=CloneStringInfo(profile);
6870 data=GetStringInfoDatum(ping_profile),
6871 length=(png_uint_32) GetStringInfoLength(ping_profile);
6876 (void) WriteBlobMSBULong(image,length-5); /* data length */
6877 (void) WriteBlob(image,length-1,data+1);
6878 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6879 ping_profile=DestroyStringInfo(ping_profile);
6883 name=GetNextImageProfile(image);
6890 /* Write one PNG image */
6891 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6892 const ImageInfo *IMimage_info,Image *IMimage)
6916 ping_trans_alpha[256];
6944 ping_have_cheap_transparency,
6955 /* ping_exclude_EXIF, */
6958 /* ping_exclude_iTXt, */
6963 /* ping_exclude_tRNS, */
6965 ping_exclude_zCCP, /* hex-encoded iCCP */
6968 ping_preserve_colormap,
6969 ping_need_colortype_warning,
6989 ping_interlace_method,
6990 ping_compression_method,
7007 number_semitransparent,
7009 ping_pHYs_unit_type;
7012 ping_pHYs_x_resolution,
7013 ping_pHYs_y_resolution;
7015 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7016 " Enter WriteOnePNGImage()");
7018 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7019 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7020 if (image_info == (ImageInfo *) NULL)
7021 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7023 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7024 LockSemaphoreInfo(ping_semaphore);
7027 /* Initialize some stuff */
7030 ping_interlace_method=0,
7031 ping_compression_method=0,
7032 ping_filter_method=0,
7035 ping_background.red = 0;
7036 ping_background.green = 0;
7037 ping_background.blue = 0;
7038 ping_background.gray = 0;
7039 ping_background.index = 0;
7041 ping_trans_color.red=0;
7042 ping_trans_color.green=0;
7043 ping_trans_color.blue=0;
7044 ping_trans_color.gray=0;
7046 ping_pHYs_unit_type = 0;
7047 ping_pHYs_x_resolution = 0;
7048 ping_pHYs_y_resolution = 0;
7050 ping_have_blob=MagickFalse;
7051 ping_have_color=MagickTrue;
7052 ping_have_non_bw=MagickTrue;
7053 ping_have_PLTE=MagickFalse;
7054 ping_have_bKGD=MagickFalse;
7055 ping_have_pHYs=MagickFalse;
7056 ping_have_tRNS=MagickFalse;
7058 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7059 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7060 ping_exclude_date=mng_info->ping_exclude_date;
7061 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7062 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7063 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7064 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7065 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7066 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7067 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7068 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7069 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7070 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7071 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7072 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7074 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7075 ping_need_colortype_warning = MagickFalse;
7078 number_semitransparent = 0;
7079 number_transparent = 0;
7081 if (logging != MagickFalse)
7083 if (image->storage_class == UndefinedClass)
7084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085 " storage_class=UndefinedClass");
7086 if (image->storage_class == DirectClass)
7087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7088 " storage_class=DirectClass");
7089 if (image->storage_class == PseudoClass)
7090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7091 " storage_class=PseudoClass");
7094 if (image->storage_class != PseudoClass && image->colormap != NULL)
7096 /* Free the bogus colormap; it can cause trouble later */
7097 if (logging != MagickFalse)
7098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7099 " Freeing bogus colormap");
7100 (void *) RelinquishMagickMemory(image->colormap);
7101 image->colormap=NULL;
7104 if (image->colorspace != RGBColorspace)
7105 (void) TransformImageColorspace(image,RGBColorspace);
7108 Sometimes we get PseudoClass images whose RGB values don't match
7109 the colors in the colormap. This code syncs the RGB values.
7111 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7112 (void) SyncImage(image);
7114 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7115 if (image->depth > 8)
7117 if (logging != MagickFalse)
7118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7119 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7125 #if 0 /* To do: Option to use the original colormap */
7126 if (ping_preserve_colormap != MagickFalse)
7131 #if 0 /* To do: honor -depth */
7132 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7137 /* To do: set to next higher multiple of 8 */
7138 if (image->depth < 8)
7141 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7142 /* PNG does not handle depths greater than 16 so reduce it even
7145 if (image->depth > 16)
7149 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7150 if (image->depth == 16 && mng_info->write_png_depth != 16)
7151 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7155 /* Normally we run this just once, but in the case of writing PNG8
7156 * we reduce the transparency to binary and run again, then if there
7157 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7158 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7159 * palette. The final reduction can only fail if there are still 256
7160 * colors present and one of them has both transparent and opaque instances.
7163 tried_333 = MagickFalse;
7164 tried_444 = MagickFalse;
7170 * Sometimes we get DirectClass images that have 256 colors or fewer.
7171 * This code will build a colormap.
7173 * Also, sometimes we get PseudoClass images with an out-of-date
7174 * colormap. This code will replace the colormap with a new one.
7175 * Sometimes we get PseudoClass images that have more than 256 colors.
7176 * This code will delete the colormap and change the image to
7179 * If image->matte is MagickFalse, we ignore the opacity channel
7180 * even though it sometimes contains left-over non-opaque values.
7182 * Also we gather some information (number of opaque, transparent,
7183 * and semitransparent pixels, and whether the image has any non-gray
7184 * pixels or only black-and-white pixels) that we might need later.
7186 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7187 * we need to check for bogus non-opaque values, at least.
7190 # define PNGK (MAGICKCORE_QUANTUM_DEPTH-8) /* Shift */
7191 # define PNGM (ScaleCharToQuantum((unsigned char) 0x01)) /* Scale */
7201 semitransparent[260],
7204 register IndexPacket
7207 register const PixelPacket
7211 register PixelPacket
7214 if (logging != MagickFalse)
7215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7216 " Enter BUILD_PALETTE:");
7218 if (logging != MagickFalse)
7220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221 " image->columns=%.20g",(double) image->columns);
7222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7223 " image->rows=%.20g",(double) image->rows);
7224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225 " image->matte=%.20g",(double) image->matte);
7226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7227 " image->depth=%.20g",(double) image->depth);
7229 if (image->storage_class == PseudoClass && image->colormap != NULL)
7231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232 " Original colormap:");
7233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7234 " i (red,green,blue,opacity)");
7236 for (i=0; i < 256; i++)
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7239 " %d (%d,%d,%d,%d)",
7241 (int) image->colormap[i].red,
7242 (int) image->colormap[i].green,
7243 (int) image->colormap[i].blue,
7244 (int) image->colormap[i].opacity);
7247 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7252 " %d (%d,%d,%d,%d)",
7254 (int) image->colormap[i].red,
7255 (int) image->colormap[i].green,
7256 (int) image->colormap[i].blue,
7257 (int) image->colormap[i].opacity);
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " image->colors=%d",(int) image->colors);
7265 if (image->colors == 0)
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " (zero means unknown)");
7269 if (ping_preserve_colormap == MagickFalse)
7270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7271 " Regenerate the colormap");
7274 exception=(&image->exception);
7278 number_semitransparent = 0;
7279 number_transparent = 0;
7281 for (y=0; y < (ssize_t) image->rows; y++)
7283 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7285 if (q == (PixelPacket *) NULL)
7288 for (x=0; x < (ssize_t) image->columns; x++)
7290 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7292 if (number_opaque < 259)
7294 if (number_opaque == 0)
7297 opaque[0].opacity=OpaqueOpacity;
7301 for (i=0; i< (ssize_t) number_opaque; i++)
7303 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7307 if (i == (ssize_t) number_opaque &&
7308 number_opaque < 259)
7312 opaque[i].opacity = OpaqueOpacity;
7316 else if (q->opacity == TransparentOpacity)
7318 if (number_transparent < 259)
7320 if (number_transparent == 0)
7323 ping_trans_color.red=(unsigned short)(q->red);
7324 ping_trans_color.green=(unsigned short) (q->green);
7325 ping_trans_color.blue=(unsigned short) (q->blue);
7326 ping_trans_color.gray=(unsigned short) (q->blue);
7327 number_transparent = 1;
7330 for (i=0; i< (ssize_t) number_transparent; i++)
7332 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7336 if (i == (ssize_t) number_transparent &&
7337 number_transparent < 259)
7339 number_transparent++;
7340 transparent[i] = *q;
7346 if (number_semitransparent < 259)
7348 if (number_semitransparent == 0)
7350 semitransparent[0]=*q;
7351 number_semitransparent = 1;
7354 for (i=0; i< (ssize_t) number_semitransparent; i++)
7356 if (IsColorEqual(semitransparent+i,
7357 (PixelPacket *) q) &&
7358 q->opacity == semitransparent[i].opacity)
7362 if (i == (ssize_t) number_semitransparent &&
7363 number_semitransparent < 259)
7365 number_semitransparent++;
7366 semitransparent[i] = *q;
7374 if (ping_exclude_bKGD == MagickFalse)
7376 /* Add the background color to the palette, if it
7377 * isn't already there.
7379 for (i=0; i<number_opaque; i++)
7381 if (IsColorEqual(opaque+i, &image->background_color))
7385 if (number_opaque < 259 && i == number_opaque)
7387 opaque[i]=image->background_color;
7388 opaque[i].opacity = OpaqueOpacity;
7391 else if (logging != MagickFalse)
7392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7393 " No room in the colormap to add background color");
7396 image_colors=number_opaque+number_transparent+number_semitransparent;
7398 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7400 /* No room for the background color; remove it. */
7405 if (logging != MagickFalse)
7407 if (image_colors > 256)
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " image has more than 256 colors");
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413 " image has %d colors",image_colors);
7417 if (ping_preserve_colormap != MagickFalse)
7421 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7423 ping_have_color=MagickFalse;
7424 ping_have_non_bw=MagickFalse;
7426 if(image_colors > 256)
7428 for (y=0; y < (ssize_t) image->rows; y++)
7430 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7432 if (q == (PixelPacket *) NULL)
7435 /* Worst case is black-and-white; we are looking at every
7439 if (ping_have_color == MagickFalse)
7442 for (x=0; x < (ssize_t) image->columns; x++)
7444 if (s->red != s->green || s->red != s->blue)
7446 ping_have_color=MagickTrue;
7447 ping_have_non_bw=MagickTrue;
7454 if (ping_have_non_bw == MagickFalse)
7457 for (x=0; x < (ssize_t) image->columns; x++)
7459 if (s->red != 0 && s->red != QuantumRange)
7461 ping_have_non_bw=MagickTrue;
7470 if (image_colors < 257)
7476 * Initialize image colormap.
7479 if (logging != MagickFalse)
7480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481 " Sort the new colormap");
7483 /* Sort palette, transparent first */;
7487 for (i=0; i<number_transparent; i++)
7488 colormap[n++] = transparent[i];
7490 for (i=0; i<number_semitransparent; i++)
7491 colormap[n++] = semitransparent[i];
7493 for (i=0; i<number_opaque; i++)
7494 colormap[n++] = opaque[i];
7497 /* image_colors < 257; search the colormap instead of the pixels
7498 * to get ping_have_color and ping_have_non_bw
7502 if (ping_have_color == MagickFalse)
7504 if (colormap[i].red != colormap[i].green ||
7505 colormap[i].red != colormap[i].blue)
7507 ping_have_color=MagickTrue;
7508 ping_have_non_bw=MagickTrue;
7513 if (ping_have_non_bw == MagickFalse)
7515 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7516 ping_have_non_bw=MagickTrue;
7520 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7521 (number_transparent == 0 && number_semitransparent == 0)) &&
7522 (((mng_info->write_png_colortype-1) ==
7523 PNG_COLOR_TYPE_PALETTE) ||
7524 (mng_info->write_png_colortype == 0)))
7526 if (logging != MagickFalse)
7528 if (n != (ssize_t) image_colors)
7529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7530 " image_colors (%d) and n (%d) don't match",
7533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7534 " AcquireImageColormap");
7537 image->colors = image_colors;
7539 if (AcquireImageColormap(image,image_colors) ==
7541 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7543 for (i=0; i< (ssize_t) image_colors; i++)
7544 image->colormap[i] = colormap[i];
7546 if (logging != MagickFalse)
7548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7549 " image->colors=%d (%d)",
7550 (int) image->colors, image_colors);
7552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7553 " Update the pixel indexes");
7556 /* Sync the pixel indices with the new colormap */
7558 for (y=0; y < (ssize_t) image->rows; y++)
7560 q=GetAuthenticPixels(image,0,y,image->columns,1,
7563 if (q == (PixelPacket *) NULL)
7566 indexes=GetAuthenticIndexQueue(image);
7568 for (x=0; x < (ssize_t) image->columns; x++)
7570 for (i=0; i< (ssize_t) image_colors; i++)
7572 if ((image->matte == MagickFalse ||
7573 image->colormap[i].opacity == q->opacity) &&
7574 (IsColorEqual(&image->colormap[i],
7575 (PixelPacket *) q)))
7577 indexes[x]=(IndexPacket) i;
7584 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7590 if (logging != MagickFalse)
7592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7593 " image->colors=%d", (int) image->colors);
7595 if (image->colormap != NULL)
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " i (red,green,blue,opacity)");
7600 for (i=0; i < (ssize_t) image->colors; i++)
7602 if (i < 300 || i >= (ssize_t) image->colors - 10)
7604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7605 " %d (%d,%d,%d,%d)",
7607 (int) image->colormap[i].red,
7608 (int) image->colormap[i].green,
7609 (int) image->colormap[i].blue,
7610 (int) image->colormap[i].opacity);
7615 if (number_transparent < 257)
7616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7617 " number_transparent = %d",
7618 number_transparent);
7621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7622 " number_transparent > 256");
7624 if (number_opaque < 257)
7625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7626 " number_opaque = %d",
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " number_opaque > 256");
7633 if (number_semitransparent < 257)
7634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7635 " number_semitransparent = %d",
7636 number_semitransparent);
7639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7640 " number_semitransparent > 256");
7642 if (ping_have_non_bw == MagickFalse)
7643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7644 " All pixels and the background are black or white");
7646 else if (ping_have_color == MagickFalse)
7647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7648 " All pixels and the background are gray");
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " At least one pixel or the background is non-gray");
7654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7655 " Exit BUILD_PALETTE:");
7658 if (mng_info->write_png8 == MagickFalse)
7661 /* Make any reductions necessary for the PNG8 format */
7662 if (image_colors <= 256 &&
7663 image_colors != 0 && image->colormap != NULL &&
7664 number_semitransparent == 0 &&
7665 number_transparent <= 1)
7668 /* PNG8 can't have semitransparent colors so we threshold the
7669 * opacity to 0 or OpaqueOpacity
7671 if (number_semitransparent != 0)
7673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7674 " Thresholding the alpha channel to binary");
7676 for (y=0; y < (ssize_t) image->rows; y++)
7678 r=GetAuthenticPixels(image,0,y,image->columns,1,
7681 if (r == (PixelPacket *) NULL)
7684 for (x=0; x < (ssize_t) image->columns; x++)
7686 r->opacity = r->opacity > TransparentOpacity/2 ?
7687 TransparentOpacity : OpaqueOpacity;
7691 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7694 if (image_colors != 0 && image_colors <= 256 &&
7695 image->colormap != NULL)
7696 for (i=0; i<image_colors; i++)
7697 image->colormap[i].opacity =
7698 image->colormap[i].opacity > TransparentOpacity/2 ?
7699 TransparentOpacity : OpaqueOpacity;
7704 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7705 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7706 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7709 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7711 if (logging != MagickFalse)
7712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7713 " Quantizing the background color to 4-4-4");
7715 tried_444 = MagickTrue;
7717 image->background_color.red=
7719 image->background_color.red) >> PNGK) & 0xf0) ) |
7721 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7722 image->background_color.green=
7724 image->background_color.green) >> PNGK) & 0xf0) ) |
7726 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7727 image->background_color.blue=
7729 image->background_color.blue) >> PNGK) & 0xf0) ) |
7731 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7733 if (logging != MagickFalse)
7734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7735 " Quantizing the pixel colors to 4-4-4");
7737 if (image->colormap == NULL)
7739 for (y=0; y < (ssize_t) image->rows; y++)
7741 r=GetAuthenticPixels(image,0,y,image->columns,1,
7744 if (r == (PixelPacket *) NULL)
7747 for (x=0; x < (ssize_t) image->columns; x++)
7749 if (r->opacity == TransparentOpacity)
7751 r->red = image->background_color.red;
7752 r->green = image->background_color.green;
7753 r->blue = image->background_color.blue;
7758 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7759 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7761 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7762 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7764 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7765 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7770 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7775 else /* Should not reach this; colormap already exists and
7778 if (logging != MagickFalse)
7779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7780 " Quantizing the colormap to 4-4-4");
7781 for (i=0; i<image_colors; i++)
7783 image->colormap[i].red=
7785 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7787 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7788 image->colormap[i].green=
7790 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7792 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7793 image->colormap[i].blue=
7795 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7797 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7803 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7805 if (logging != MagickFalse)
7806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7807 " Quantizing the background color to 3-3-3");
7809 tried_333 = MagickTrue;
7811 image->background_color.red=
7813 image->background_color.red) >> PNGK) & 0xe0) ) |
7815 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7817 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7818 image->background_color.green=
7820 image->background_color.green) >> PNGK) & 0xe0) ) |
7822 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7824 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7825 image->background_color.blue=
7827 image->background_color.blue) >> PNGK) & 0xe0) ) |
7829 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7831 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7833 if (logging != MagickFalse)
7834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7835 " Quantizing the pixel colors to 3-3-3-1");
7837 if (image->colormap == NULL)
7839 for (y=0; y < (ssize_t) image->rows; y++)
7841 r=GetAuthenticPixels(image,0,y,image->columns,1,
7844 if (r == (PixelPacket *) NULL)
7847 for (x=0; x < (ssize_t) image->columns; x++)
7849 if (r->opacity == TransparentOpacity)
7851 r->red = image->background_color.red;
7852 r->green = image->background_color.green;
7853 r->blue = image->background_color.blue;
7858 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7859 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7860 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7862 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7863 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7864 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7866 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7867 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7868 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7873 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7878 else /* Should not reach this; colormap already exists and
7881 if (logging != MagickFalse)
7882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7883 " Quantizing the colormap to 3-3-3-1");
7884 for (i=0; i<image_colors; i++)
7886 image->colormap[i].red=
7888 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7890 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7892 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7893 image->colormap[i].green=
7895 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7897 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7899 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7900 image->colormap[i].blue=
7902 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7904 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7906 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7912 if (image_colors == 0 || image_colors > 256)
7914 if (logging != MagickFalse)
7915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7916 " Quantizing the background color to 3-3-2");
7918 image->background_color.red=
7920 image->background_color.red) >> PNGK) & 0xe0) ) |
7922 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7924 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7925 image->background_color.green=
7927 image->background_color.green) >> PNGK) & 0xe0) ) |
7929 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7931 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7932 image->background_color.blue=
7934 image->background_color.blue) >> PNGK) & 0xc0) ) |
7936 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7938 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7940 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7942 if (logging != MagickFalse)
7943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7944 " Quantizing the pixel colors to 3-3-2-1");
7946 if (image->colormap == NULL)
7948 for (y=0; y < (ssize_t) image->rows; y++)
7950 r=GetAuthenticPixels(image,0,y,image->columns,1,
7953 if (r == (PixelPacket *) NULL)
7956 for (x=0; x < (ssize_t) image->columns; x++)
7958 if (r->opacity == TransparentOpacity)
7960 r->red = image->background_color.red;
7961 r->green = image->background_color.green;
7962 r->blue = image->background_color.blue;
7967 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7968 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7969 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7971 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7972 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7973 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7975 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7976 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7977 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7978 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7983 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7988 else /* Should not reach this; colormap already exists and
7991 if (logging != MagickFalse)
7992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7993 " Quantizing the colormap to 3-3-2-1");
7994 for (i=0; i<image_colors; i++)
7996 image->colormap[i].red=
7998 image->colormap[i].red) >> PNGK) & 0xe0) ) |
8000 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
8002 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
8003 image->colormap[i].green=
8005 image->colormap[i].green) >> PNGK) & 0xe0) ) |
8007 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
8009 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
8010 image->colormap[i].blue=
8012 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
8014 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
8016 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
8018 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
8025 /* END OF BUILD_PALETTE */
8027 /* If we are excluding the tRNS chunk and there is transparency,
8028 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8031 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8032 (number_transparent != 0 || number_semitransparent != 0))
8034 int colortype=mng_info->write_png_colortype;
8036 if (ping_have_color == MagickFalse)
8037 mng_info->write_png_colortype = 5;
8040 mng_info->write_png_colortype = 7;
8042 if (colortype != 0 &&
8043 mng_info->write_png_colortype != (ssize_t) colortype)
8044 ping_need_colortype_warning=MagickTrue;
8048 /* See if cheap transparency is possible. It is only possible
8049 * when there is a single transparent color, no semitransparent
8050 * color, and no opaque color that has the same RGB components
8051 * as the transparent color. We only need this information if
8052 * we are writing a PNG with colortype 0 or 2, and we have not
8053 * excluded the tRNS chunk.
8055 if (number_transparent == 1 &&
8056 mng_info->write_png_colortype < 4)
8058 ping_have_cheap_transparency = MagickTrue;
8060 if (number_semitransparent != 0)
8061 ping_have_cheap_transparency = MagickFalse;
8063 else if (image_colors == 0 || image_colors > 256 ||
8064 image->colormap == NULL)
8069 register const PixelPacket
8072 exception=(&image->exception);
8074 for (y=0; y < (ssize_t) image->rows; y++)
8076 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8078 if (q == (PixelPacket *) NULL)
8081 for (x=0; x < (ssize_t) image->columns; x++)
8083 if (q->opacity != TransparentOpacity &&
8084 (unsigned short) q->red == ping_trans_color.red &&
8085 (unsigned short) q->green == ping_trans_color.green &&
8086 (unsigned short) q->blue == ping_trans_color.blue)
8088 ping_have_cheap_transparency = MagickFalse;
8095 if (ping_have_cheap_transparency == MagickFalse)
8101 /* Assuming that image->colormap[0] is the one transparent color
8102 * and that all others are opaque.
8104 if (image_colors > 1)
8105 for (i=1; i<image_colors; i++)
8106 if (image->colormap[i].red == image->colormap[0].red &&
8107 image->colormap[i].green == image->colormap[0].green &&
8108 image->colormap[i].blue == image->colormap[0].blue)
8110 ping_have_cheap_transparency = MagickFalse;
8115 if (logging != MagickFalse)
8117 if (ping_have_cheap_transparency == MagickFalse)
8118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8119 " Cheap transparency is not possible.");
8122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8123 " Cheap transparency is possible.");
8127 ping_have_cheap_transparency = MagickFalse;
8129 image_depth=image->depth;
8131 quantum_info = (QuantumInfo *) NULL;
8133 image_colors=(int) image->colors;
8134 image_matte=image->matte;
8136 mng_info->IsPalette=image->storage_class == PseudoClass &&
8137 image_colors <= 256 && image->colormap != NULL;
8139 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8140 (image->colors == 0 || image->colormap == NULL))
8142 image_info=DestroyImageInfo(image_info);
8143 image=DestroyImage(image);
8144 (void) ThrowMagickException(&IMimage->exception,
8145 GetMagickModule(),CoderError,
8146 "Cannot write PNG8 or color-type 3; colormap is NULL",
8147 "`%s'",IMimage->filename);
8148 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8149 UnlockSemaphoreInfo(ping_semaphore);
8151 return(MagickFalse);
8155 Allocate the PNG structures
8157 #ifdef PNG_USER_MEM_SUPPORTED
8158 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8159 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8160 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8163 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8164 MagickPNGErrorHandler,MagickPNGWarningHandler);
8167 if (ping == (png_struct *) NULL)
8168 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8170 ping_info=png_create_info_struct(ping);
8172 if (ping_info == (png_info *) NULL)
8174 png_destroy_write_struct(&ping,(png_info **) NULL);
8175 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8178 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8179 ping_pixels=(unsigned char *) NULL;
8181 if (setjmp(png_jmpbuf(ping)))
8187 if (image_info->verbose)
8188 (void) printf("PNG write has failed.\n");
8190 png_destroy_write_struct(&ping,&ping_info);
8191 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8192 UnlockSemaphoreInfo(ping_semaphore);
8194 if (ping_have_blob != MagickFalse)
8195 (void) CloseBlob(image);
8196 image_info=DestroyImageInfo(image_info);
8197 image=DestroyImage(image);
8198 return(MagickFalse);
8201 Prepare PNG for writing.
8203 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8204 if (mng_info->write_mng)
8205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8208 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8209 if (mng_info->write_mng)
8210 png_permit_empty_plte(ping,MagickTrue);
8217 ping_width=(png_uint_32) image->columns;
8218 ping_height=(png_uint_32) image->rows;
8220 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8223 if (mng_info->write_png_depth != 0)
8224 image_depth=mng_info->write_png_depth;
8226 /* Adjust requested depth to next higher valid depth if necessary */
8227 if (image_depth > 8)
8230 if ((image_depth > 4) && (image_depth < 8))
8233 if (image_depth == 3)
8236 if (logging != MagickFalse)
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " width=%.20g",(double) ping_width);
8240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8241 " height=%.20g",(double) ping_height);
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243 " image_matte=%.20g",(double) image->matte);
8244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8245 " image->depth=%.20g",(double) image->depth);
8246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8247 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8250 save_image_depth=image_depth;
8251 ping_bit_depth=(png_byte) save_image_depth;
8254 #if defined(PNG_pHYs_SUPPORTED)
8255 if (ping_exclude_pHYs == MagickFalse)
8257 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8258 (!mng_info->write_mng || !mng_info->equal_physs))
8260 if (logging != MagickFalse)
8261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262 " Setting up pHYs chunk");
8264 if (image->units == PixelsPerInchResolution)
8266 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8267 ping_pHYs_x_resolution=
8268 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8269 ping_pHYs_y_resolution=
8270 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8273 else if (image->units == PixelsPerCentimeterResolution)
8275 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8276 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8277 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8282 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8283 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8284 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8287 if (logging != MagickFalse)
8288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8289 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8290 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8291 (int) ping_pHYs_unit_type);
8292 ping_have_pHYs = MagickTrue;
8297 if (ping_exclude_bKGD == MagickFalse)
8299 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8305 if (ping_bit_depth == 8)
8308 if (ping_bit_depth == 4)
8311 if (ping_bit_depth == 2)
8314 if (ping_bit_depth == 1)
8317 ping_background.red=(png_uint_16)
8318 (ScaleQuantumToShort(image->background_color.red) & mask);
8320 ping_background.green=(png_uint_16)
8321 (ScaleQuantumToShort(image->background_color.green) & mask);
8323 ping_background.blue=(png_uint_16)
8324 (ScaleQuantumToShort(image->background_color.blue) & mask);
8327 if (logging != MagickFalse)
8329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8330 " Setting up bKGD chunk (1)");
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " ping_bit_depth=%d",ping_bit_depth);
8336 ping_have_bKGD = MagickTrue;
8340 Select the color type.
8345 if (mng_info->IsPalette && mng_info->write_png8)
8348 /* To do: make this a function cause it's used twice, except
8349 for reducing the sample depth from 8. */
8351 number_colors=image_colors;
8353 ping_have_tRNS=MagickFalse;
8358 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8360 if (logging != MagickFalse)
8361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Setting up PLTE chunk with %d colors (%d)",
8363 number_colors, image_colors);
8365 for (i=0; i < (ssize_t) number_colors; i++)
8367 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8368 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8369 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8370 if (logging != MagickFalse)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 #if MAGICKCORE_QUANTUM_DEPTH == 8
8373 " %3ld (%3d,%3d,%3d)",
8375 " %5ld (%5d,%5d,%5d)",
8377 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8381 ping_have_PLTE=MagickTrue;
8382 image_depth=ping_bit_depth;
8385 if (matte != MagickFalse)
8388 Identify which colormap entry is transparent.
8390 assert(number_colors <= 256);
8391 assert(image->colormap != NULL);
8393 for (i=0; i < (ssize_t) number_transparent; i++)
8394 ping_trans_alpha[i]=0;
8397 ping_num_trans=(unsigned short) (number_transparent +
8398 number_semitransparent);
8400 if (ping_num_trans == 0)
8401 ping_have_tRNS=MagickFalse;
8404 ping_have_tRNS=MagickTrue;
8407 if (ping_exclude_bKGD == MagickFalse)
8410 * Identify which colormap entry is the background color.
8413 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8414 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8417 ping_background.index=(png_byte) i;
8419 } /* end of write_png8 */
8421 else if (mng_info->write_png24)
8423 image_matte=MagickFalse;
8424 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8427 else if (mng_info->write_png32)
8429 image_matte=MagickTrue;
8430 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8433 else /* mng_info->write_pngNN not specified */
8435 image_depth=ping_bit_depth;
8437 if (mng_info->write_png_colortype != 0)
8439 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8441 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8442 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8443 image_matte=MagickTrue;
8446 image_matte=MagickFalse;
8448 if (logging != MagickFalse)
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " PNG colortype %d was specified:",(int) ping_color_type);
8453 else /* write_png_colortype not specified */
8455 if (logging != MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " Selecting PNG colortype:");
8459 ping_color_type=(png_byte) ((matte != MagickFalse)?
8460 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8462 if (image_info->type == TrueColorType)
8464 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8465 image_matte=MagickFalse;
8468 if (image_info->type == TrueColorMatteType)
8470 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8471 image_matte=MagickTrue;
8474 if (image_info->type == PaletteType ||
8475 image_info->type == PaletteMatteType)
8476 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8478 if (mng_info->write_png_colortype == 0 &&
8479 (image_info->type == UndefinedType ||
8480 image_info->type == OptimizeType))
8482 if (ping_have_color == MagickFalse)
8484 if (image_matte == MagickFalse)
8486 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8487 image_matte=MagickFalse;
8492 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8493 image_matte=MagickTrue;
8498 if (image_matte == MagickFalse)
8500 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8501 image_matte=MagickFalse;
8506 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8507 image_matte=MagickTrue;
8514 if (logging != MagickFalse)
8515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8516 " Selected PNG colortype=%d",ping_color_type);
8518 if (ping_bit_depth < 8)
8520 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8521 ping_color_type == PNG_COLOR_TYPE_RGB ||
8522 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8526 old_bit_depth=ping_bit_depth;
8528 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8530 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8534 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8539 if (image->colors == 0)
8542 (void) ThrowMagickException(&image->exception,
8543 GetMagickModule(),CoderError,
8544 "image has 0 colors", "`%s'","");
8547 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8548 ping_bit_depth <<= 1;
8551 if (logging != MagickFalse)
8553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8554 " Number of colors: %.20g",(double) image_colors);
8556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557 " Tentative PNG bit depth: %d",ping_bit_depth);
8560 if (ping_bit_depth < (int) mng_info->write_png_depth)
8561 ping_bit_depth = mng_info->write_png_depth;
8564 image_depth=ping_bit_depth;
8566 if (logging != MagickFalse)
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " Tentative PNG color type: %.20g",(double) ping_color_type);
8571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572 " image_info->type: %.20g",(double) image_info->type);
8574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8575 " image_depth: %.20g",(double) image_depth);
8577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579 " image->depth: %.20g",(double) image->depth);
8581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8582 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8585 if (matte != MagickFalse)
8587 if (mng_info->IsPalette)
8589 if (mng_info->write_png_colortype == 0)
8591 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8593 if (ping_have_color != MagickFalse)
8594 ping_color_type=PNG_COLOR_TYPE_RGBA;
8598 * Determine if there is any transparent color.
8600 if (number_transparent + number_semitransparent == 0)
8603 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8606 image_matte=MagickFalse;
8608 if (mng_info->write_png_colortype == 0)
8609 ping_color_type&=0x03;
8619 if (ping_bit_depth == 8)
8622 if (ping_bit_depth == 4)
8625 if (ping_bit_depth == 2)
8628 if (ping_bit_depth == 1)
8631 ping_trans_color.red=(png_uint_16)
8632 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8634 ping_trans_color.green=(png_uint_16)
8635 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8637 ping_trans_color.blue=(png_uint_16)
8638 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8640 ping_trans_color.gray=(png_uint_16)
8641 (ScaleQuantumToShort(PixelIntensityToQuantum(
8642 image->colormap)) & mask);
8644 ping_trans_color.index=(png_byte) 0;
8646 ping_have_tRNS=MagickTrue;
8649 if (ping_have_tRNS != MagickFalse)
8652 * Determine if there is one and only one transparent color
8653 * and if so if it is fully transparent.
8655 if (ping_have_cheap_transparency == MagickFalse)
8656 ping_have_tRNS=MagickFalse;
8659 if (ping_have_tRNS != MagickFalse)
8661 if (mng_info->write_png_colortype == 0)
8662 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8664 if (image_depth == 8)
8666 ping_trans_color.red&=0xff;
8667 ping_trans_color.green&=0xff;
8668 ping_trans_color.blue&=0xff;
8669 ping_trans_color.gray&=0xff;
8675 if (image_depth == 8)
8677 ping_trans_color.red&=0xff;
8678 ping_trans_color.green&=0xff;
8679 ping_trans_color.blue&=0xff;
8680 ping_trans_color.gray&=0xff;
8687 if (ping_have_tRNS != MagickFalse)
8688 image_matte=MagickFalse;
8690 if ((mng_info->IsPalette) &&
8691 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8692 ping_have_color == MagickFalse &&
8693 (image_matte == MagickFalse || image_depth >= 8))
8697 if (image_matte != MagickFalse)
8698 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8700 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8702 ping_color_type=PNG_COLOR_TYPE_GRAY;
8704 if (save_image_depth == 16 && image_depth == 8)
8706 if (logging != MagickFalse)
8708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8709 " Scaling ping_trans_color (0)");
8711 ping_trans_color.gray*=0x0101;
8715 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8716 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8718 if ((image_colors == 0) || ((ssize_t) image_colors-1 > MaxColormapSize))
8719 image_colors=(int) (one << image_depth);
8721 if (image_depth > 8)
8727 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8729 if(!mng_info->write_png_depth)
8733 while ((int) (one << ping_bit_depth)
8734 < (ssize_t) image_colors)
8735 ping_bit_depth <<= 1;
8739 else if (ping_color_type ==
8740 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8741 mng_info->IsPalette)
8743 /* Check if grayscale is reducible */
8746 depth_4_ok=MagickTrue,
8747 depth_2_ok=MagickTrue,
8748 depth_1_ok=MagickTrue;
8750 for (i=0; i < (ssize_t) image_colors; i++)
8755 intensity=ScaleQuantumToChar(image->colormap[i].red);
8757 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8758 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8759 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8760 depth_2_ok=depth_1_ok=MagickFalse;
8761 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8762 depth_1_ok=MagickFalse;
8765 if (depth_1_ok && mng_info->write_png_depth <= 1)
8768 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8771 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8776 image_depth=ping_bit_depth;
8781 if (mng_info->IsPalette)
8783 number_colors=image_colors;
8785 if (image_depth <= 8)
8790 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8792 if (mng_info->have_write_global_plte && matte == MagickFalse)
8794 png_set_PLTE(ping,ping_info,NULL,0);
8796 if (logging != MagickFalse)
8797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798 " Setting up empty PLTE chunk");
8803 for (i=0; i < (ssize_t) number_colors; i++)
8805 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8806 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8807 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8810 if (logging != MagickFalse)
8811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812 " Setting up PLTE chunk with %d colors",
8815 ping_have_PLTE=MagickTrue;
8818 /* color_type is PNG_COLOR_TYPE_PALETTE */
8819 if (mng_info->write_png_depth == 0)
8827 while ((one << ping_bit_depth) < number_colors)
8828 ping_bit_depth <<= 1;
8833 if (matte != MagickFalse)
8836 * Set up trans_colors array.
8838 assert(number_colors <= 256);
8840 ping_num_trans=(unsigned short) (number_transparent +
8841 number_semitransparent);
8843 if (ping_num_trans == 0)
8844 ping_have_tRNS=MagickFalse;
8848 if (logging != MagickFalse)
8850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8851 " Scaling ping_trans_color (1)");
8853 ping_have_tRNS=MagickTrue;
8855 for (i=0; i < ping_num_trans; i++)
8857 ping_trans_alpha[i]= (png_byte) (255-
8858 ScaleQuantumToChar(image->colormap[i].opacity));
8868 if (image_depth < 8)
8871 if ((save_image_depth == 16) && (image_depth == 8))
8873 if (logging != MagickFalse)
8875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8876 " Scaling ping_trans_color from (%d,%d,%d)",
8877 (int) ping_trans_color.red,
8878 (int) ping_trans_color.green,
8879 (int) ping_trans_color.blue);
8882 ping_trans_color.red*=0x0101;
8883 ping_trans_color.green*=0x0101;
8884 ping_trans_color.blue*=0x0101;
8885 ping_trans_color.gray*=0x0101;
8887 if (logging != MagickFalse)
8889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891 (int) ping_trans_color.red,
8892 (int) ping_trans_color.green,
8893 (int) ping_trans_color.blue);
8898 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8899 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8902 Adjust background and transparency samples in sub-8-bit grayscale files.
8904 if (ping_bit_depth < 8 && ping_color_type ==
8905 PNG_COLOR_TYPE_GRAY)
8913 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8915 if (ping_exclude_bKGD == MagickFalse)
8918 ping_background.gray=(png_uint_16)
8919 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8921 if (logging != MagickFalse)
8922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8923 " Setting up bKGD chunk (2)");
8925 ping_have_bKGD = MagickTrue;
8928 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8929 ping_trans_color.gray));
8932 if (ping_exclude_bKGD == MagickFalse)
8934 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8937 Identify which colormap entry is the background color.
8940 number_colors=image_colors;
8942 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8943 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8946 ping_background.index=(png_byte) i;
8948 if (logging != MagickFalse)
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " Setting up bKGD chunk with index=%d",(int) i);
8954 if (i < (ssize_t) number_colors)
8956 ping_have_bKGD = MagickTrue;
8958 if (logging != MagickFalse)
8960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8961 " background =(%d,%d,%d)",
8962 (int) ping_background.red,
8963 (int) ping_background.green,
8964 (int) ping_background.blue);
8968 else /* Can't happen */
8970 if (logging != MagickFalse)
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " No room in PLTE to add bKGD color");
8973 ping_have_bKGD = MagickFalse;
8978 if (logging != MagickFalse)
8979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8980 " PNG color type: %d",ping_color_type);
8982 Initialize compression level and filtering.
8984 if (logging != MagickFalse)
8986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 " Setting up deflate compression");
8989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8990 " Compression buffer size: 32768");
8993 png_set_compression_buffer_size(ping,32768L);
8995 if (logging != MagickFalse)
8996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8997 " Compression mem level: 9");
8999 png_set_compression_mem_level(ping, 9);
9001 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9009 level=(int) MagickMin((ssize_t) quality/10,9);
9011 if (logging != MagickFalse)
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " Compression level: %d",level);
9015 png_set_compression_level(ping,level);
9020 if (logging != MagickFalse)
9021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9022 " Compression strategy: Z_HUFFMAN_ONLY");
9024 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9027 if (logging != MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " Setting up filtering");
9031 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9032 /* This became available in libpng-1.0.9. Output must be a MNG. */
9033 if (mng_info->write_mng && ((quality % 10) == 7))
9035 if (logging != MagickFalse)
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
9039 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9043 if (logging != MagickFalse)
9044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 if ((quality % 10) > 5)
9053 base_filter=PNG_ALL_FILTERS;
9056 if ((quality % 10) != 5)
9057 base_filter=(int) quality % 10;
9060 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9061 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9063 base_filter=PNG_NO_FILTERS;
9066 base_filter=PNG_ALL_FILTERS;
9068 if (logging != MagickFalse)
9070 if (base_filter == PNG_ALL_FILTERS)
9071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9072 " Base filter method: ADAPTIVE");
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Base filter method: NONE");
9078 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9081 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9082 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9084 ResetImageProfileIterator(image);
9085 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9087 profile=GetImageProfile(image,name);
9089 if (profile != (StringInfo *) NULL)
9091 #ifdef PNG_WRITE_iCCP_SUPPORTED
9092 if ((LocaleCompare(name,"ICC") == 0) ||
9093 (LocaleCompare(name,"ICM") == 0))
9096 if (ping_exclude_iCCP == MagickFalse)
9098 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9099 #if (PNG_LIBPNG_VER < 10500)
9100 (png_charp) GetStringInfoDatum(profile),
9102 (png_const_bytep) GetStringInfoDatum(profile),
9104 (png_uint_32) GetStringInfoLength(profile));
9110 if (ping_exclude_zCCP == MagickFalse)
9112 Magick_png_write_raw_profile(image_info,ping,ping_info,
9113 (unsigned char *) name,(unsigned char *) name,
9114 GetStringInfoDatum(profile),
9115 (png_uint_32) GetStringInfoLength(profile));
9119 if (logging != MagickFalse)
9120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9121 " Setting up text chunk with %s profile",name);
9123 name=GetNextImageProfile(image);
9127 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9128 if ((mng_info->have_write_global_srgb == 0) &&
9129 ((image->rendering_intent != UndefinedIntent) ||
9130 (image->colorspace == sRGBColorspace)))
9132 if (ping_exclude_sRGB == MagickFalse)
9135 Note image rendering intent.
9137 if (logging != MagickFalse)
9138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9139 " Setting up sRGB chunk");
9141 (void) png_set_sRGB(ping,ping_info,(
9142 Magick_RenderingIntent_to_PNG_RenderingIntent(
9143 image->rendering_intent)));
9145 if (ping_exclude_gAMA == MagickFalse)
9146 png_set_gAMA(ping,ping_info,0.45455);
9150 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9153 if (ping_exclude_gAMA == MagickFalse &&
9154 (ping_exclude_sRGB == MagickFalse ||
9155 (image->gamma < .45 || image->gamma > .46)))
9157 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9161 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9163 if (logging != MagickFalse)
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " Setting up gAMA chunk");
9167 png_set_gAMA(ping,ping_info,image->gamma);
9171 if (ping_exclude_cHRM == MagickFalse)
9173 if ((mng_info->have_write_global_chrm == 0) &&
9174 (image->chromaticity.red_primary.x != 0.0))
9177 Note image chromaticity.
9178 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9186 wp=image->chromaticity.white_point;
9187 rp=image->chromaticity.red_primary;
9188 gp=image->chromaticity.green_primary;
9189 bp=image->chromaticity.blue_primary;
9191 if (logging != MagickFalse)
9192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9193 " Setting up cHRM chunk");
9195 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9201 ping_interlace_method=image_info->interlace != NoInterlace;
9203 if (mng_info->write_mng)
9204 png_set_sig_bytes(ping,8);
9206 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9208 if (mng_info->write_png_colortype != 0)
9210 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9211 if (ping_have_color != MagickFalse)
9213 ping_color_type = PNG_COLOR_TYPE_RGB;
9215 if (ping_bit_depth < 8)
9219 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9220 if (ping_have_color != MagickFalse)
9221 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9224 if (ping_need_colortype_warning != MagickFalse ||
9225 ((mng_info->write_png_depth &&
9226 (int) mng_info->write_png_depth != ping_bit_depth) ||
9227 (mng_info->write_png_colortype &&
9228 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9229 mng_info->write_png_colortype != 7 &&
9230 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9232 if (logging != MagickFalse)
9234 if (ping_need_colortype_warning != MagickFalse)
9236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9237 " Image has transparency but tRNS chunk was excluded");
9240 if (mng_info->write_png_depth)
9242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9243 " Defined PNG:bit-depth=%u, Computed depth=%u",
9244 mng_info->write_png_depth,
9248 if (mng_info->write_png_colortype)
9250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9251 " Defined PNG:color-type=%u, Computed color type=%u",
9252 mng_info->write_png_colortype-1,
9258 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9261 if (image_matte != MagickFalse && image->matte == MagickFalse)
9263 /* Add an opaque matte channel */
9264 image->matte = MagickTrue;
9265 (void) SetImageOpacity(image,0);
9267 if (logging != MagickFalse)
9268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9269 " Added an opaque matte channel");
9272 if (number_transparent != 0 || number_semitransparent != 0)
9274 if (ping_color_type < 4)
9276 ping_have_tRNS=MagickTrue;
9277 if (logging != MagickFalse)
9278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9279 " Setting ping_have_tRNS=MagickTrue.");
9283 if (logging != MagickFalse)
9284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285 " Writing PNG header chunks");
9287 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9288 ping_bit_depth,ping_color_type,
9289 ping_interlace_method,ping_compression_method,
9290 ping_filter_method);
9292 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9294 png_set_PLTE(ping,ping_info,palette,number_colors);
9296 if (logging != MagickFalse)
9298 for (i=0; i< (ssize_t) number_colors; i++)
9300 if (i < ping_num_trans)
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9304 (int) palette[i].red,
9305 (int) palette[i].green,
9306 (int) palette[i].blue,
9308 (int) ping_trans_alpha[i]);
9310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9311 " PLTE[%d] = (%d,%d,%d)",
9313 (int) palette[i].red,
9314 (int) palette[i].green,
9315 (int) palette[i].blue);
9320 if (ping_exclude_bKGD == MagickFalse)
9322 if (ping_have_bKGD != MagickFalse)
9323 png_set_bKGD(ping,ping_info,&ping_background);
9326 if (ping_exclude_pHYs == MagickFalse)
9328 if (ping_have_pHYs != MagickFalse)
9330 png_set_pHYs(ping,ping_info,
9331 ping_pHYs_x_resolution,
9332 ping_pHYs_y_resolution,
9333 ping_pHYs_unit_type);
9337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338 " Setting up pHYs chunk");
9339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9340 " x_resolution=%lu",
9341 (unsigned long) ping_pHYs_x_resolution);
9342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9343 " y_resolution=%lu",
9344 (unsigned long) ping_pHYs_y_resolution);
9345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9347 (unsigned long) ping_pHYs_unit_type);
9352 #if defined(PNG_oFFs_SUPPORTED)
9353 if (ping_exclude_oFFs == MagickFalse)
9355 if (image->page.x || image->page.y)
9357 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9358 (png_int_32) image->page.y, 0);
9360 if (logging != MagickFalse)
9361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9363 (int) image->page.x, (int) image->page.y);
9368 if (mng_info->need_blob != MagickFalse)
9370 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9372 png_error(ping,"WriteBlob Failed");
9374 ping_have_blob=MagickTrue;
9377 png_write_info_before_PLTE(ping, ping_info);
9379 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9381 if (logging != MagickFalse)
9383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9384 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9387 if (ping_color_type == 3)
9388 (void) png_set_tRNS(ping, ping_info,
9395 (void) png_set_tRNS(ping, ping_info,
9400 if (logging != MagickFalse)
9402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " tRNS color =(%d,%d,%d)",
9404 (int) ping_trans_color.red,
9405 (int) ping_trans_color.green,
9406 (int) ping_trans_color.blue);
9411 /* write any png-chunk-b profiles */
9412 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9414 png_write_info(ping,ping_info);
9416 /* write any PNG-chunk-m profiles */
9417 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9419 if (ping_exclude_vpAg == MagickFalse)
9421 if ((image->page.width != 0 && image->page.width != image->columns) ||
9422 (image->page.height != 0 && image->page.height != image->rows))
9427 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9428 PNGType(chunk,mng_vpAg);
9429 LogPNGChunk(logging,mng_vpAg,9L);
9430 PNGLong(chunk+4,(png_uint_32) image->page.width);
9431 PNGLong(chunk+8,(png_uint_32) image->page.height);
9432 chunk[12]=0; /* unit = pixels */
9433 (void) WriteBlob(image,13,chunk);
9434 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9438 #if (PNG_LIBPNG_VER == 10206)
9439 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9440 #define PNG_HAVE_IDAT 0x04
9441 ping->mode |= PNG_HAVE_IDAT;
9442 #undef PNG_HAVE_IDAT
9445 png_set_packing(ping);
9449 rowbytes=image->columns;
9450 if (image_depth > 8)
9452 switch (ping_color_type)
9454 case PNG_COLOR_TYPE_RGB:
9458 case PNG_COLOR_TYPE_GRAY_ALPHA:
9462 case PNG_COLOR_TYPE_RGBA:
9470 if (logging != MagickFalse)
9472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9473 " Writing PNG image data");
9475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9476 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9478 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9479 sizeof(*ping_pixels));
9481 if (ping_pixels == (unsigned char *) NULL)
9482 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9485 Initialize image scanlines.
9487 if (setjmp(png_jmpbuf(ping)))
9493 if (image_info->verbose)
9494 (void) printf("PNG write has failed.\n");
9496 png_destroy_write_struct(&ping,&ping_info);
9497 if (quantum_info != (QuantumInfo *) NULL)
9498 quantum_info=DestroyQuantumInfo(quantum_info);
9499 if (ping_pixels != (unsigned char *) NULL)
9500 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9501 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9502 UnlockSemaphoreInfo(ping_semaphore);
9504 if (ping_have_blob != MagickFalse)
9505 (void) CloseBlob(image);
9506 image_info=DestroyImageInfo(image_info);
9507 image=DestroyImage(image);
9508 return(MagickFalse);
9510 quantum_info=AcquireQuantumInfo(image_info,image);
9511 if (quantum_info == (QuantumInfo *) NULL)
9512 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9513 quantum_info->format=UndefinedQuantumFormat;
9514 quantum_info->depth=image_depth;
9515 num_passes=png_set_interlace_handling(ping);
9517 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9518 !mng_info->write_png32) &&
9519 (mng_info->IsPalette ||
9520 (image_info->type == BilevelType)) &&
9521 image_matte == MagickFalse &&
9522 ping_have_non_bw == MagickFalse)
9524 /* Palette, Bilevel, or Opaque Monochrome */
9525 register const PixelPacket
9528 quantum_info->depth=8;
9529 for (pass=0; pass < num_passes; pass++)
9532 Convert PseudoClass image to a PNG monochrome image.
9534 for (y=0; y < (ssize_t) image->rows; y++)
9536 if (logging != MagickFalse && y == 0)
9537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9538 " Writing row of pixels (0)");
9540 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9542 if (p == (const PixelPacket *) NULL)
9545 if (mng_info->IsPalette)
9547 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9548 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9549 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9550 mng_info->write_png_depth &&
9551 mng_info->write_png_depth != old_bit_depth)
9553 /* Undo pixel scaling */
9554 for (i=0; i < (ssize_t) image->columns; i++)
9555 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9556 >> (8-old_bit_depth));
9562 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9563 quantum_info,RedQuantum,ping_pixels,&image->exception);
9566 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9567 for (i=0; i < (ssize_t) image->columns; i++)
9568 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9571 if (logging != MagickFalse && y == 0)
9572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573 " Writing row of pixels (1)");
9575 png_write_row(ping,ping_pixels);
9577 if (image->previous == (Image *) NULL)
9579 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9580 if (status == MagickFalse)
9586 else /* Not Palette, Bilevel, or Opaque Monochrome */
9588 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9589 !mng_info->write_png32) &&
9590 (image_matte != MagickFalse ||
9591 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9592 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9594 register const PixelPacket
9597 for (pass=0; pass < num_passes; pass++)
9600 for (y=0; y < (ssize_t) image->rows; y++)
9602 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9604 if (p == (const PixelPacket *) NULL)
9607 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9609 if (mng_info->IsPalette)
9610 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9611 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9614 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9615 quantum_info,RedQuantum,ping_pixels,&image->exception);
9617 if (logging != MagickFalse && y == 0)
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " Writing GRAY PNG pixels (2)");
9622 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9624 if (logging != MagickFalse && y == 0)
9625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9626 " Writing GRAY_ALPHA PNG pixels (2)");
9628 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9629 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9632 if (logging != MagickFalse && y == 0)
9633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9634 " Writing row of pixels (2)");
9636 png_write_row(ping,ping_pixels);
9639 if (image->previous == (Image *) NULL)
9641 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9642 if (status == MagickFalse)
9650 register const PixelPacket
9653 for (pass=0; pass < num_passes; pass++)
9655 if ((image_depth > 8) || (mng_info->write_png24 ||
9656 mng_info->write_png32 ||
9657 (!mng_info->write_png8 && !mng_info->IsPalette)))
9659 for (y=0; y < (ssize_t) image->rows; y++)
9661 p=GetVirtualPixels(image,0,y,image->columns,1,
9664 if (p == (const PixelPacket *) NULL)
9667 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9669 if (image->storage_class == DirectClass)
9670 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9671 quantum_info,RedQuantum,ping_pixels,&image->exception);
9674 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9675 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9678 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9680 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9681 quantum_info,GrayAlphaQuantum,ping_pixels,
9684 if (logging != MagickFalse && y == 0)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " Writing GRAY_ALPHA PNG pixels (3)");
9689 else if (image_matte != MagickFalse)
9690 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9691 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9694 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9695 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9697 if (logging != MagickFalse && y == 0)
9698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9699 " Writing row of pixels (3)");
9701 png_write_row(ping,ping_pixels);
9706 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9707 mng_info->write_png32 ||
9708 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9710 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9711 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9713 if (logging != MagickFalse)
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9715 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9717 quantum_info->depth=8;
9721 for (y=0; y < (ssize_t) image->rows; y++)
9723 if (logging != MagickFalse && y == 0)
9724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9725 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9727 p=GetVirtualPixels(image,0,y,image->columns,1,
9730 if (p == (const PixelPacket *) NULL)
9733 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9735 quantum_info->depth=image->depth;
9737 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9738 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9741 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9743 if (logging != MagickFalse && y == 0)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " Writing GRAY_ALPHA PNG pixels (4)");
9747 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9748 quantum_info,GrayAlphaQuantum,ping_pixels,
9754 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9755 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9757 if (logging != MagickFalse && y <= 2)
9759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9760 " Writing row of non-gray pixels (4)");
9762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9763 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9764 (int)ping_pixels[0],(int)ping_pixels[1]);
9767 png_write_row(ping,ping_pixels);
9771 if (image->previous == (Image *) NULL)
9773 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9774 if (status == MagickFalse)
9781 if (quantum_info != (QuantumInfo *) NULL)
9782 quantum_info=DestroyQuantumInfo(quantum_info);
9784 if (logging != MagickFalse)
9786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9787 " Wrote PNG image data");
9789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9790 " Width: %.20g",(double) ping_width);
9792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9793 " Height: %.20g",(double) ping_height);
9795 if (mng_info->write_png_depth)
9797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9802 " PNG bit-depth written: %d",ping_bit_depth);
9804 if (mng_info->write_png_colortype)
9806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9807 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9811 " PNG color-type written: %d",ping_color_type);
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " PNG Interlace method: %d",ping_interlace_method);
9817 Generate text chunks after IDAT.
9819 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9821 ResetImagePropertyIterator(image);
9822 property=GetNextImageProperty(image);
9823 while (property != (const char *) NULL)
9828 value=GetImageProperty(image,property);
9830 /* Don't write any "png:" properties; those are just for "identify" */
9831 if (LocaleNCompare(property,"png:",4) != 0 &&
9833 /* Suppress density and units if we wrote a pHYs chunk */
9834 (ping_exclude_pHYs != MagickFalse ||
9835 LocaleCompare(property,"density") != 0 ||
9836 LocaleCompare(property,"units") != 0) &&
9838 /* Suppress the IM-generated Date:create and Date:modify */
9839 (ping_exclude_date == MagickFalse ||
9840 LocaleNCompare(property, "Date:",5) != 0))
9842 if (value != (const char *) NULL)
9844 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9845 text[0].key=(char *) property;
9846 text[0].text=(char *) value;
9847 text[0].text_length=strlen(value);
9849 if (ping_exclude_tEXt != MagickFalse)
9850 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9852 else if (ping_exclude_zTXt != MagickFalse)
9853 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9857 text[0].compression=image_info->compression == NoCompression ||
9858 (image_info->compression == UndefinedCompression &&
9859 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9860 PNG_TEXT_COMPRESSION_zTXt ;
9863 if (logging != MagickFalse)
9865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9866 " Setting up text chunk");
9868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9869 " keyword: %s",text[0].key);
9872 png_set_text(ping,ping_info,text,1);
9873 png_free(ping,text);
9876 property=GetNextImageProperty(image);
9880 /* write any PNG-chunk-e profiles */
9881 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9883 if (logging != MagickFalse)
9884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9885 " Writing PNG end info");
9887 png_write_end(ping,ping_info);
9889 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9891 if (mng_info->page.x || mng_info->page.y ||
9892 (ping_width != mng_info->page.width) ||
9893 (ping_height != mng_info->page.height))
9899 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9901 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9902 PNGType(chunk,mng_FRAM);
9903 LogPNGChunk(logging,mng_FRAM,27L);
9905 chunk[5]=0; /* frame name separator (no name) */
9906 chunk[6]=1; /* flag for changing delay, for next frame only */
9907 chunk[7]=0; /* flag for changing frame timeout */
9908 chunk[8]=1; /* flag for changing frame clipping for next frame */
9909 chunk[9]=0; /* flag for changing frame sync_id */
9910 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9911 chunk[14]=0; /* clipping boundaries delta type */
9912 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9914 (png_uint_32) (mng_info->page.x + ping_width));
9915 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9917 (png_uint_32) (mng_info->page.y + ping_height));
9918 (void) WriteBlob(image,31,chunk);
9919 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9920 mng_info->old_framing_mode=4;
9921 mng_info->framing_mode=1;
9925 mng_info->framing_mode=3;
9927 if (mng_info->write_mng && !mng_info->need_fram &&
9928 ((int) image->dispose == 3))
9929 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9930 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9931 "`%s'",image->filename);
9937 png_destroy_write_struct(&ping,&ping_info);
9939 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9941 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9942 UnlockSemaphoreInfo(ping_semaphore);
9945 if (ping_have_blob != MagickFalse)
9946 (void) CloseBlob(image);
9948 image_info=DestroyImageInfo(image_info);
9949 image=DestroyImage(image);
9951 /* Store bit depth actually written */
9952 s[0]=(char) ping_bit_depth;
9955 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9957 if (logging != MagickFalse)
9958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9959 " exit WriteOnePNGImage()");
9962 /* End write one PNG image */
9966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9970 % W r i t e P N G I m a g e %
9974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9976 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9977 % Multiple-image Network Graphics (MNG) image file.
9979 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9981 % The format of the WritePNGImage method is:
9983 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9985 % A description of each parameter follows:
9987 % o image_info: the image info.
9989 % o image: The image.
9991 % Returns MagickTrue on success, MagickFalse on failure.
9993 % Communicating with the PNG encoder:
9995 % While the datastream written is always in PNG format and normally would
9996 % be given the "png" file extension, this method also writes the following
9997 % pseudo-formats which are subsets of PNG:
9999 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10000 % a depth greater than 8, the depth is reduced. If transparency
10001 % is present, the tRNS chunk must only have values 0 and 255
10002 % (i.e., transparency is binary: fully opaque or fully
10003 % transparent). If other values are present they will be
10004 % 50%-thresholded to binary transparency. If more than 256
10005 % colors are present, they will be quantized to the 4-4-4-1,
10006 % 3-3-3-1, or 3-3-2-1 palette.
10008 % If you want better quantization or dithering of the colors
10009 % or alpha than that, you need to do it before calling the
10010 % PNG encoder. The pixels contain 8-bit indices even if
10011 % they could be represented with 1, 2, or 4 bits. Grayscale
10012 % images will be written as indexed PNG files even though the
10013 % PNG grayscale type might be slightly more efficient. Please
10014 % note that writing to the PNG8 format may result in loss
10015 % of color and alpha data.
10017 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10018 % chunk can be present to convey binary transparency by naming
10019 % one of the colors as transparent. The only loss incurred
10020 % is reduction of sample depth to 8. If the image has more
10021 % than one transparent color, has semitransparent pixels, or
10022 % has an opaque pixel with the same RGB components as the
10023 % transparent color, an image is not written.
10025 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10026 % transparency is permitted, i.e., the alpha sample for
10027 % each pixel can have any value from 0 to 255. The alpha
10028 % channel is present even if the image is fully opaque.
10029 % The only loss in data is the reduction of the sample depth
10032 % o -define: For more precise control of the PNG output, you can use the
10033 % Image options "png:bit-depth" and "png:color-type". These
10034 % can be set from the commandline with "-define" and also
10035 % from the application programming interfaces. The options
10036 % are case-independent and are converted to lowercase before
10037 % being passed to this encoder.
10039 % png:color-type can be 0, 2, 3, 4, or 6.
10041 % When png:color-type is 0 (Grayscale), png:bit-depth can
10042 % be 1, 2, 4, 8, or 16.
10044 % When png:color-type is 2 (RGB), png:bit-depth can
10047 % When png:color-type is 3 (Indexed), png:bit-depth can
10048 % be 1, 2, 4, or 8. This refers to the number of bits
10049 % used to store the index. The color samples always have
10050 % bit-depth 8 in indexed PNG files.
10052 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10053 % png:bit-depth can be 8 or 16.
10055 % If the image cannot be written without loss with the requested bit-depth
10056 % and color-type, a PNG file will not be written, and the encoder will
10057 % return MagickFalse.
10059 % Since image encoders should not be responsible for the "heavy lifting",
10060 % the user should make sure that ImageMagick has already reduced the
10061 % image depth and number of colors and limit transparency to binary
10062 % transparency prior to attempting to write the image with depth, color,
10063 % or transparency limitations.
10065 % TODO: Enforce the previous paragraph.
10067 % Note that another definition, "png:bit-depth-written" exists, but it
10068 % is not intended for external use. It is only used internally by the
10069 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10071 % It is possible to request that the PNG encoder write previously-formatted
10072 % ancillary chunks in the output PNG file, using the "-profile" commandline
10073 % option as shown below or by setting the profile via a programming
10076 % -profile PNG-chunk-x:<file>
10078 % where x is a location flag and <file> is a file containing the chunk
10079 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10080 % This encoder will compute the chunk length and CRC, so those must not
10081 % be included in the file.
10083 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10084 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10085 % of the same type, then add a short unique string after the "x" to prevent
10086 % subsequent profiles from overwriting the preceding ones, e.g.,
10088 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10090 % As of version 6.6.6 the following optimizations are always done:
10092 % o 32-bit depth is reduced to 16.
10093 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10094 % high byte and low byte are identical.
10095 % o Palette is sorted to remove unused entries and to put a
10096 % transparent color first, if BUILD_PNG_PALETTE is defined.
10097 % o Opaque matte channel is removed when writing an indexed PNG.
10098 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10099 % this can be done without loss and a larger bit depth N was not
10100 % requested via the "-define PNG:bit-depth=N" option.
10101 % o If matte channel is present but only one transparent color is
10102 % present, RGB+tRNS is written instead of RGBA
10103 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10104 % was requested when converting an opaque image).
10106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10108 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10114 have_mng_structure,
10130 assert(image_info != (const ImageInfo *) NULL);
10131 assert(image_info->signature == MagickSignature);
10132 assert(image != (Image *) NULL);
10133 assert(image->signature == MagickSignature);
10134 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10135 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10137 Allocate a MngInfo structure.
10139 have_mng_structure=MagickFalse;
10140 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10142 if (mng_info == (MngInfo *) NULL)
10143 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10146 Initialize members of the MngInfo structure.
10148 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10149 mng_info->image=image;
10150 mng_info->equal_backgrounds=MagickTrue;
10151 have_mng_structure=MagickTrue;
10153 /* See if user has requested a specific PNG subformat */
10155 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10156 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10157 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10159 if (mng_info->write_png8)
10161 mng_info->write_png_colortype = /* 3 */ 4;
10162 mng_info->write_png_depth = 8;
10166 if (mng_info->write_png24)
10168 mng_info->write_png_colortype = /* 2 */ 3;
10169 mng_info->write_png_depth = 8;
10172 if (image->matte == MagickTrue)
10173 (void) SetImageType(image,TrueColorMatteType);
10176 (void) SetImageType(image,TrueColorType);
10178 (void) SyncImage(image);
10181 if (mng_info->write_png32)
10183 mng_info->write_png_colortype = /* 6 */ 7;
10184 mng_info->write_png_depth = 8;
10187 if (image->matte == MagickTrue)
10188 (void) SetImageType(image,TrueColorMatteType);
10191 (void) SetImageType(image,TrueColorType);
10193 (void) SyncImage(image);
10196 value=GetImageOption(image_info,"png:bit-depth");
10198 if (value != (char *) NULL)
10200 if (LocaleCompare(value,"1") == 0)
10201 mng_info->write_png_depth = 1;
10203 else if (LocaleCompare(value,"2") == 0)
10204 mng_info->write_png_depth = 2;
10206 else if (LocaleCompare(value,"4") == 0)
10207 mng_info->write_png_depth = 4;
10209 else if (LocaleCompare(value,"8") == 0)
10210 mng_info->write_png_depth = 8;
10212 else if (LocaleCompare(value,"16") == 0)
10213 mng_info->write_png_depth = 16;
10216 (void) ThrowMagickException(&image->exception,
10217 GetMagickModule(),CoderWarning,
10218 "ignoring invalid defined png:bit-depth",
10221 if (logging != MagickFalse)
10222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10223 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10226 value=GetImageOption(image_info,"png:color-type");
10228 if (value != (char *) NULL)
10230 /* We must store colortype+1 because 0 is a valid colortype */
10231 if (LocaleCompare(value,"0") == 0)
10232 mng_info->write_png_colortype = 1;
10234 else if (LocaleCompare(value,"2") == 0)
10235 mng_info->write_png_colortype = 3;
10237 else if (LocaleCompare(value,"3") == 0)
10238 mng_info->write_png_colortype = 4;
10240 else if (LocaleCompare(value,"4") == 0)
10241 mng_info->write_png_colortype = 5;
10243 else if (LocaleCompare(value,"6") == 0)
10244 mng_info->write_png_colortype = 7;
10247 (void) ThrowMagickException(&image->exception,
10248 GetMagickModule(),CoderWarning,
10249 "ignoring invalid defined png:color-type",
10252 if (logging != MagickFalse)
10253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10254 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10257 /* Check for chunks to be excluded:
10259 * The default is to not exclude any known chunks except for any
10260 * listed in the "unused_chunks" array, above.
10262 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10263 * define (in the image properties or in the image artifacts)
10264 * or via a mng_info member. For convenience, in addition
10265 * to or instead of a comma-separated list of chunks, the
10266 * "exclude-chunk" string can be simply "all" or "none".
10268 * The exclude-chunk define takes priority over the mng_info.
10270 * A "PNG:include-chunk" define takes priority over both the
10271 * mng_info and the "PNG:exclude-chunk" define. Like the
10272 * "exclude-chunk" string, it can define "all" or "none" as
10273 * well as a comma-separated list. Chunks that are unknown to
10274 * ImageMagick are always excluded, regardless of their "copy-safe"
10275 * status according to the PNG specification, and even if they
10276 * appear in the "include-chunk" list.
10278 * Finally, all chunks listed in the "unused_chunks" array are
10279 * automatically excluded, regardless of the other instructions
10282 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10283 * will not be written and the gAMA chunk will only be written if it
10284 * is not between .45 and .46, or approximately (1.0/2.2).
10286 * If you exclude tRNS and the image has transparency, the colortype
10287 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10289 * The -strip option causes StripImage() to set the png:include-chunk
10290 * artifact to "none,gama".
10293 mng_info->ping_exclude_bKGD=MagickFalse;
10294 mng_info->ping_exclude_cHRM=MagickFalse;
10295 mng_info->ping_exclude_date=MagickFalse;
10296 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10297 mng_info->ping_exclude_gAMA=MagickFalse;
10298 mng_info->ping_exclude_iCCP=MagickFalse;
10299 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10300 mng_info->ping_exclude_oFFs=MagickFalse;
10301 mng_info->ping_exclude_pHYs=MagickFalse;
10302 mng_info->ping_exclude_sRGB=MagickFalse;
10303 mng_info->ping_exclude_tEXt=MagickFalse;
10304 mng_info->ping_exclude_tRNS=MagickFalse;
10305 mng_info->ping_exclude_vpAg=MagickFalse;
10306 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10307 mng_info->ping_exclude_zTXt=MagickFalse;
10309 mng_info->ping_preserve_colormap=MagickFalse;
10311 value=GetImageArtifact(image,"png:preserve-colormap");
10313 value=GetImageOption(image_info,"png:preserve-colormap");
10315 mng_info->ping_preserve_colormap=MagickTrue;
10317 excluding=MagickFalse;
10319 for (source=0; source<1; source++)
10323 value=GetImageArtifact(image,"png:exclude-chunk");
10326 value=GetImageArtifact(image,"png:exclude-chunks");
10330 value=GetImageOption(image_info,"png:exclude-chunk");
10333 value=GetImageOption(image_info,"png:exclude-chunks");
10342 excluding=MagickTrue;
10344 if (logging != MagickFalse)
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " png:exclude-chunk=%s found in image artifacts.\n", value);
10350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10351 " png:exclude-chunk=%s found in image properties.\n", value);
10354 last=strlen(value);
10356 for (i=0; i<(int) last; i+=5)
10359 if (LocaleNCompare(value+i,"all",3) == 0)
10361 mng_info->ping_exclude_bKGD=MagickTrue;
10362 mng_info->ping_exclude_cHRM=MagickTrue;
10363 mng_info->ping_exclude_date=MagickTrue;
10364 mng_info->ping_exclude_EXIF=MagickTrue;
10365 mng_info->ping_exclude_gAMA=MagickTrue;
10366 mng_info->ping_exclude_iCCP=MagickTrue;
10367 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10368 mng_info->ping_exclude_oFFs=MagickTrue;
10369 mng_info->ping_exclude_pHYs=MagickTrue;
10370 mng_info->ping_exclude_sRGB=MagickTrue;
10371 mng_info->ping_exclude_tEXt=MagickTrue;
10372 mng_info->ping_exclude_tRNS=MagickTrue;
10373 mng_info->ping_exclude_vpAg=MagickTrue;
10374 mng_info->ping_exclude_zCCP=MagickTrue;
10375 mng_info->ping_exclude_zTXt=MagickTrue;
10379 if (LocaleNCompare(value+i,"none",4) == 0)
10381 mng_info->ping_exclude_bKGD=MagickFalse;
10382 mng_info->ping_exclude_cHRM=MagickFalse;
10383 mng_info->ping_exclude_date=MagickFalse;
10384 mng_info->ping_exclude_EXIF=MagickFalse;
10385 mng_info->ping_exclude_gAMA=MagickFalse;
10386 mng_info->ping_exclude_iCCP=MagickFalse;
10387 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10388 mng_info->ping_exclude_oFFs=MagickFalse;
10389 mng_info->ping_exclude_pHYs=MagickFalse;
10390 mng_info->ping_exclude_sRGB=MagickFalse;
10391 mng_info->ping_exclude_tEXt=MagickFalse;
10392 mng_info->ping_exclude_tRNS=MagickFalse;
10393 mng_info->ping_exclude_vpAg=MagickFalse;
10394 mng_info->ping_exclude_zCCP=MagickFalse;
10395 mng_info->ping_exclude_zTXt=MagickFalse;
10398 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10399 mng_info->ping_exclude_bKGD=MagickTrue;
10401 if (LocaleNCompare(value+i,"chrm",4) == 0)
10402 mng_info->ping_exclude_cHRM=MagickTrue;
10404 if (LocaleNCompare(value+i,"date",4) == 0)
10405 mng_info->ping_exclude_date=MagickTrue;
10407 if (LocaleNCompare(value+i,"exif",4) == 0)
10408 mng_info->ping_exclude_EXIF=MagickTrue;
10410 if (LocaleNCompare(value+i,"gama",4) == 0)
10411 mng_info->ping_exclude_gAMA=MagickTrue;
10413 if (LocaleNCompare(value+i,"iccp",4) == 0)
10414 mng_info->ping_exclude_iCCP=MagickTrue;
10417 if (LocaleNCompare(value+i,"itxt",4) == 0)
10418 mng_info->ping_exclude_iTXt=MagickTrue;
10421 if (LocaleNCompare(value+i,"gama",4) == 0)
10422 mng_info->ping_exclude_gAMA=MagickTrue;
10424 if (LocaleNCompare(value+i,"offs",4) == 0)
10425 mng_info->ping_exclude_oFFs=MagickTrue;
10427 if (LocaleNCompare(value+i,"phys",4) == 0)
10428 mng_info->ping_exclude_pHYs=MagickTrue;
10430 if (LocaleNCompare(value+i,"srgb",4) == 0)
10431 mng_info->ping_exclude_sRGB=MagickTrue;
10433 if (LocaleNCompare(value+i,"text",4) == 0)
10434 mng_info->ping_exclude_tEXt=MagickTrue;
10436 if (LocaleNCompare(value+i,"trns",4) == 0)
10437 mng_info->ping_exclude_tRNS=MagickTrue;
10439 if (LocaleNCompare(value+i,"vpag",4) == 0)
10440 mng_info->ping_exclude_vpAg=MagickTrue;
10442 if (LocaleNCompare(value+i,"zccp",4) == 0)
10443 mng_info->ping_exclude_zCCP=MagickTrue;
10445 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10446 mng_info->ping_exclude_zTXt=MagickTrue;
10452 for (source=0; source<1; source++)
10456 value=GetImageArtifact(image,"png:include-chunk");
10459 value=GetImageArtifact(image,"png:include-chunks");
10463 value=GetImageOption(image_info,"png:include-chunk");
10466 value=GetImageOption(image_info,"png:include-chunks");
10474 excluding=MagickTrue;
10476 if (logging != MagickFalse)
10479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10480 " png:include-chunk=%s found in image artifacts.\n", value);
10482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10483 " png:include-chunk=%s found in image properties.\n", value);
10486 last=strlen(value);
10488 for (i=0; i<(int) last; i+=5)
10490 if (LocaleNCompare(value+i,"all",3) == 0)
10492 mng_info->ping_exclude_bKGD=MagickFalse;
10493 mng_info->ping_exclude_cHRM=MagickFalse;
10494 mng_info->ping_exclude_date=MagickFalse;
10495 mng_info->ping_exclude_EXIF=MagickFalse;
10496 mng_info->ping_exclude_gAMA=MagickFalse;
10497 mng_info->ping_exclude_iCCP=MagickFalse;
10498 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10499 mng_info->ping_exclude_oFFs=MagickFalse;
10500 mng_info->ping_exclude_pHYs=MagickFalse;
10501 mng_info->ping_exclude_sRGB=MagickFalse;
10502 mng_info->ping_exclude_tEXt=MagickFalse;
10503 mng_info->ping_exclude_tRNS=MagickFalse;
10504 mng_info->ping_exclude_vpAg=MagickFalse;
10505 mng_info->ping_exclude_zCCP=MagickFalse;
10506 mng_info->ping_exclude_zTXt=MagickFalse;
10510 if (LocaleNCompare(value+i,"none",4) == 0)
10512 mng_info->ping_exclude_bKGD=MagickTrue;
10513 mng_info->ping_exclude_cHRM=MagickTrue;
10514 mng_info->ping_exclude_date=MagickTrue;
10515 mng_info->ping_exclude_EXIF=MagickTrue;
10516 mng_info->ping_exclude_gAMA=MagickTrue;
10517 mng_info->ping_exclude_iCCP=MagickTrue;
10518 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10519 mng_info->ping_exclude_oFFs=MagickTrue;
10520 mng_info->ping_exclude_pHYs=MagickTrue;
10521 mng_info->ping_exclude_sRGB=MagickTrue;
10522 mng_info->ping_exclude_tEXt=MagickTrue;
10523 mng_info->ping_exclude_tRNS=MagickTrue;
10524 mng_info->ping_exclude_vpAg=MagickTrue;
10525 mng_info->ping_exclude_zCCP=MagickTrue;
10526 mng_info->ping_exclude_zTXt=MagickTrue;
10529 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10530 mng_info->ping_exclude_bKGD=MagickFalse;
10532 if (LocaleNCompare(value+i,"chrm",4) == 0)
10533 mng_info->ping_exclude_cHRM=MagickFalse;
10535 if (LocaleNCompare(value+i,"date",4) == 0)
10536 mng_info->ping_exclude_date=MagickFalse;
10538 if (LocaleNCompare(value+i,"exif",4) == 0)
10539 mng_info->ping_exclude_EXIF=MagickFalse;
10541 if (LocaleNCompare(value+i,"gama",4) == 0)
10542 mng_info->ping_exclude_gAMA=MagickFalse;
10544 if (LocaleNCompare(value+i,"iccp",4) == 0)
10545 mng_info->ping_exclude_iCCP=MagickFalse;
10548 if (LocaleNCompare(value+i,"itxt",4) == 0)
10549 mng_info->ping_exclude_iTXt=MagickFalse;
10552 if (LocaleNCompare(value+i,"gama",4) == 0)
10553 mng_info->ping_exclude_gAMA=MagickFalse;
10555 if (LocaleNCompare(value+i,"offs",4) == 0)
10556 mng_info->ping_exclude_oFFs=MagickFalse;
10558 if (LocaleNCompare(value+i,"phys",4) == 0)
10559 mng_info->ping_exclude_pHYs=MagickFalse;
10561 if (LocaleNCompare(value+i,"srgb",4) == 0)
10562 mng_info->ping_exclude_sRGB=MagickFalse;
10564 if (LocaleNCompare(value+i,"text",4) == 0)
10565 mng_info->ping_exclude_tEXt=MagickFalse;
10567 if (LocaleNCompare(value+i,"trns",4) == 0)
10568 mng_info->ping_exclude_tRNS=MagickFalse;
10570 if (LocaleNCompare(value+i,"vpag",4) == 0)
10571 mng_info->ping_exclude_vpAg=MagickFalse;
10573 if (LocaleNCompare(value+i,"zccp",4) == 0)
10574 mng_info->ping_exclude_zCCP=MagickFalse;
10576 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10577 mng_info->ping_exclude_zTXt=MagickFalse;
10583 if (excluding != MagickFalse && logging != MagickFalse)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Chunks to be excluded from the output PNG:");
10587 if (mng_info->ping_exclude_bKGD != MagickFalse)
10588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 if (mng_info->ping_exclude_cHRM != MagickFalse)
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593 if (mng_info->ping_exclude_date != MagickFalse)
10594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10596 if (mng_info->ping_exclude_EXIF != MagickFalse)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 if (mng_info->ping_exclude_gAMA != MagickFalse)
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602 if (mng_info->ping_exclude_iCCP != MagickFalse)
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606 if (mng_info->ping_exclude_iTXt != MagickFalse)
10607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 if (mng_info->ping_exclude_oFFs != MagickFalse)
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10613 if (mng_info->ping_exclude_pHYs != MagickFalse)
10614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10616 if (mng_info->ping_exclude_sRGB != MagickFalse)
10617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619 if (mng_info->ping_exclude_tEXt != MagickFalse)
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 if (mng_info->ping_exclude_tRNS != MagickFalse)
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 if (mng_info->ping_exclude_vpAg != MagickFalse)
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 if (mng_info->ping_exclude_zCCP != MagickFalse)
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 if (mng_info->ping_exclude_zTXt != MagickFalse)
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 mng_info->need_blob = MagickTrue;
10638 status=WriteOnePNGImage(mng_info,image_info,image);
10640 MngInfoFreeStruct(mng_info,&have_mng_structure);
10642 if (logging != MagickFalse)
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10648 #if defined(JNG_SUPPORTED)
10650 /* Write one JNG image */
10651 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10652 const ImageInfo *image_info,Image *image)
10673 jng_alpha_compression_method,
10674 jng_alpha_sample_depth,
10681 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10682 " Enter WriteOneJNGImage()");
10684 blob=(unsigned char *) NULL;
10685 jpeg_image=(Image *) NULL;
10686 jpeg_image_info=(ImageInfo *) NULL;
10689 transparent=image_info->type==GrayscaleMatteType ||
10690 image_info->type==TrueColorMatteType;
10692 jng_alpha_sample_depth=0;
10693 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10694 jng_alpha_compression_method=0;
10696 if (image->matte != MagickFalse)
10698 /* if any pixels are transparent */
10699 transparent=MagickTrue;
10700 if (image_info->compression==JPEGCompression)
10701 jng_alpha_compression_method=8;
10708 /* Create JPEG blob, image, and image_info */
10709 if (logging != MagickFalse)
10710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10711 " Creating jpeg_image_info for opacity.");
10713 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10715 if (jpeg_image_info == (ImageInfo *) NULL)
10716 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10718 if (logging != MagickFalse)
10719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10720 " Creating jpeg_image.");
10722 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10724 if (jpeg_image == (Image *) NULL)
10725 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10727 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10728 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10729 status=NegateImage(jpeg_image,MagickFalse);
10730 jpeg_image->matte=MagickFalse;
10732 if (jng_quality >= 1000)
10733 jpeg_image_info->quality=jng_quality/1000;
10736 jpeg_image_info->quality=jng_quality;
10738 jpeg_image_info->type=GrayscaleType;
10739 (void) SetImageType(jpeg_image,GrayscaleType);
10740 (void) AcquireUniqueFilename(jpeg_image->filename);
10741 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10742 "%s",jpeg_image->filename);
10745 /* To do: check bit depth of PNG alpha channel */
10747 /* Check if image is grayscale. */
10748 if (image_info->type != TrueColorMatteType && image_info->type !=
10749 TrueColorType && ImageIsGray(image))
10754 if (jng_alpha_compression_method==0)
10759 /* Encode opacity as a grayscale PNG blob */
10760 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10761 &image->exception);
10762 if (logging != MagickFalse)
10763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10764 " Creating PNG blob.");
10767 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10768 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10769 jpeg_image_info->interlace=NoInterlace;
10771 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10772 &image->exception);
10774 /* Retrieve sample depth used */
10775 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10776 if (value != (char *) NULL)
10777 jng_alpha_sample_depth= (unsigned int) value[0];
10781 /* Encode opacity as a grayscale JPEG blob */
10783 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10784 &image->exception);
10786 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10787 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10788 jpeg_image_info->interlace=NoInterlace;
10789 if (logging != MagickFalse)
10790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10791 " Creating blob.");
10792 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10793 &image->exception);
10794 jng_alpha_sample_depth=8;
10796 if (logging != MagickFalse)
10797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10798 " Successfully read jpeg_image into a blob, length=%.20g.",
10802 /* Destroy JPEG image and image_info */
10803 jpeg_image=DestroyImage(jpeg_image);
10804 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10805 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10808 /* Write JHDR chunk */
10809 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10810 PNGType(chunk,mng_JHDR);
10811 LogPNGChunk(logging,mng_JHDR,16L);
10812 PNGLong(chunk+4,(png_uint_32) image->columns);
10813 PNGLong(chunk+8,(png_uint_32) image->rows);
10814 chunk[12]=jng_color_type;
10815 chunk[13]=8; /* sample depth */
10816 chunk[14]=8; /*jng_image_compression_method */
10817 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10818 chunk[16]=jng_alpha_sample_depth;
10819 chunk[17]=jng_alpha_compression_method;
10820 chunk[18]=0; /*jng_alpha_filter_method */
10821 chunk[19]=0; /*jng_alpha_interlace_method */
10822 (void) WriteBlob(image,20,chunk);
10823 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10824 if (logging != MagickFalse)
10826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827 " JNG width:%15lu",(unsigned long) image->columns);
10829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10830 " JNG height:%14lu",(unsigned long) image->rows);
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10833 " JNG color type:%10d",jng_color_type);
10835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10836 " JNG sample depth:%8d",8);
10838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10839 " JNG compression:%9d",8);
10841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10842 " JNG interlace:%11d",0);
10844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10848 " JNG alpha compression:%3d",jng_alpha_compression_method);
10850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10851 " JNG alpha filter:%8d",0);
10853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10854 " JNG alpha interlace:%5d",0);
10857 /* Write any JNG-chunk-b profiles */
10858 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10861 Write leading ancillary chunks
10867 Write JNG bKGD chunk
10878 if (jng_color_type == 8 || jng_color_type == 12)
10882 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10883 PNGType(chunk,mng_bKGD);
10884 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10885 red=ScaleQuantumToChar(image->background_color.red);
10886 green=ScaleQuantumToChar(image->background_color.green);
10887 blue=ScaleQuantumToChar(image->background_color.blue);
10894 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10895 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10898 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10901 Write JNG sRGB chunk
10903 (void) WriteBlobMSBULong(image,1L);
10904 PNGType(chunk,mng_sRGB);
10905 LogPNGChunk(logging,mng_sRGB,1L);
10907 if (image->rendering_intent != UndefinedIntent)
10908 chunk[4]=(unsigned char)
10909 Magick_RenderingIntent_to_PNG_RenderingIntent(
10910 (image->rendering_intent));
10913 chunk[4]=(unsigned char)
10914 Magick_RenderingIntent_to_PNG_RenderingIntent(
10915 (PerceptualIntent));
10917 (void) WriteBlob(image,5,chunk);
10918 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10922 if (image->gamma != 0.0)
10925 Write JNG gAMA chunk
10927 (void) WriteBlobMSBULong(image,4L);
10928 PNGType(chunk,mng_gAMA);
10929 LogPNGChunk(logging,mng_gAMA,4L);
10930 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10931 (void) WriteBlob(image,8,chunk);
10932 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10935 if ((mng_info->equal_chrms == MagickFalse) &&
10936 (image->chromaticity.red_primary.x != 0.0))
10942 Write JNG cHRM chunk
10944 (void) WriteBlobMSBULong(image,32L);
10945 PNGType(chunk,mng_cHRM);
10946 LogPNGChunk(logging,mng_cHRM,32L);
10947 primary=image->chromaticity.white_point;
10948 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10949 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10950 primary=image->chromaticity.red_primary;
10951 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10952 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10953 primary=image->chromaticity.green_primary;
10954 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10955 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10956 primary=image->chromaticity.blue_primary;
10957 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10958 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10959 (void) WriteBlob(image,36,chunk);
10960 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10964 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10967 Write JNG pHYs chunk
10969 (void) WriteBlobMSBULong(image,9L);
10970 PNGType(chunk,mng_pHYs);
10971 LogPNGChunk(logging,mng_pHYs,9L);
10972 if (image->units == PixelsPerInchResolution)
10974 PNGLong(chunk+4,(png_uint_32)
10975 (image->x_resolution*100.0/2.54+0.5));
10977 PNGLong(chunk+8,(png_uint_32)
10978 (image->y_resolution*100.0/2.54+0.5));
10985 if (image->units == PixelsPerCentimeterResolution)
10987 PNGLong(chunk+4,(png_uint_32)
10988 (image->x_resolution*100.0+0.5));
10990 PNGLong(chunk+8,(png_uint_32)
10991 (image->y_resolution*100.0+0.5));
10998 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10999 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11003 (void) WriteBlob(image,13,chunk);
11004 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11007 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11010 Write JNG oFFs chunk
11012 (void) WriteBlobMSBULong(image,9L);
11013 PNGType(chunk,mng_oFFs);
11014 LogPNGChunk(logging,mng_oFFs,9L);
11015 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11016 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11018 (void) WriteBlob(image,13,chunk);
11019 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11021 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11023 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11024 PNGType(chunk,mng_vpAg);
11025 LogPNGChunk(logging,mng_vpAg,9L);
11026 PNGLong(chunk+4,(png_uint_32) image->page.width);
11027 PNGLong(chunk+8,(png_uint_32) image->page.height);
11028 chunk[12]=0; /* unit = pixels */
11029 (void) WriteBlob(image,13,chunk);
11030 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11036 if (jng_alpha_compression_method==0)
11044 /* Write IDAT chunk header */
11045 if (logging != MagickFalse)
11046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11047 " Write IDAT chunks from blob, length=%.20g.",(double)
11050 /* Copy IDAT chunks */
11053 for (i=8; i<(ssize_t) length; i+=len+12)
11055 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11058 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11060 /* Found an IDAT chunk. */
11061 (void) WriteBlobMSBULong(image,(size_t) len);
11062 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11063 (void) WriteBlob(image,(size_t) len+4,p);
11064 (void) WriteBlobMSBULong(image,
11065 crc32(0,p,(uInt) len+4));
11070 if (logging != MagickFalse)
11071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11072 " Skipping %c%c%c%c chunk, length=%.20g.",
11073 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11080 /* Write JDAA chunk header */
11081 if (logging != MagickFalse)
11082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11083 " Write JDAA chunk, length=%.20g.",(double) length);
11084 (void) WriteBlobMSBULong(image,(size_t) length);
11085 PNGType(chunk,mng_JDAA);
11086 LogPNGChunk(logging,mng_JDAA,length);
11087 /* Write JDAT chunk(s) data */
11088 (void) WriteBlob(image,4,chunk);
11089 (void) WriteBlob(image,length,blob);
11090 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11093 blob=(unsigned char *) RelinquishMagickMemory(blob);
11096 /* Encode image as a JPEG blob */
11097 if (logging != MagickFalse)
11098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11099 " Creating jpeg_image_info.");
11100 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11101 if (jpeg_image_info == (ImageInfo *) NULL)
11102 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11104 if (logging != MagickFalse)
11105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11106 " Creating jpeg_image.");
11108 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11109 if (jpeg_image == (Image *) NULL)
11110 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11111 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11113 (void) AcquireUniqueFilename(jpeg_image->filename);
11114 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11115 jpeg_image->filename);
11117 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11118 &image->exception);
11120 if (logging != MagickFalse)
11121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11122 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11123 (double) jpeg_image->rows);
11125 if (jng_color_type == 8 || jng_color_type == 12)
11126 jpeg_image_info->type=GrayscaleType;
11128 jpeg_image_info->quality=jng_quality % 1000;
11129 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11130 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11132 if (logging != MagickFalse)
11133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11134 " Creating blob.");
11136 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11138 if (logging != MagickFalse)
11140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11141 " Successfully read jpeg_image into a blob, length=%.20g.",
11144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11145 " Write JDAT chunk, length=%.20g.",(double) length);
11148 /* Write JDAT chunk(s) */
11149 (void) WriteBlobMSBULong(image,(size_t) length);
11150 PNGType(chunk,mng_JDAT);
11151 LogPNGChunk(logging,mng_JDAT,length);
11152 (void) WriteBlob(image,4,chunk);
11153 (void) WriteBlob(image,length,blob);
11154 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11156 jpeg_image=DestroyImage(jpeg_image);
11157 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11158 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11159 blob=(unsigned char *) RelinquishMagickMemory(blob);
11161 /* Write any JNG-chunk-e profiles */
11162 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
11164 /* Write IEND chunk */
11165 (void) WriteBlobMSBULong(image,0L);
11166 PNGType(chunk,mng_IEND);
11167 LogPNGChunk(logging,mng_IEND,0);
11168 (void) WriteBlob(image,4,chunk);
11169 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11171 if (logging != MagickFalse)
11172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11173 " exit WriteOneJNGImage()");
11180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11184 % W r i t e J N G I m a g e %
11188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11190 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11192 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
11194 % The format of the WriteJNGImage method is:
11196 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11198 % A description of each parameter follows:
11200 % o image_info: the image info.
11202 % o image: The image.
11204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11206 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11209 have_mng_structure,
11219 assert(image_info != (const ImageInfo *) NULL);
11220 assert(image_info->signature == MagickSignature);
11221 assert(image != (Image *) NULL);
11222 assert(image->signature == MagickSignature);
11223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11224 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11225 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11226 if (status == MagickFalse)
11230 Allocate a MngInfo structure.
11232 have_mng_structure=MagickFalse;
11233 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11234 if (mng_info == (MngInfo *) NULL)
11235 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11237 Initialize members of the MngInfo structure.
11239 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11240 mng_info->image=image;
11241 have_mng_structure=MagickTrue;
11243 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11245 status=WriteOneJNGImage(mng_info,image_info,image);
11246 (void) CloseBlob(image);
11248 (void) CatchImageException(image);
11249 MngInfoFreeStruct(mng_info,&have_mng_structure);
11250 if (logging != MagickFalse)
11251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11258 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11267 have_mng_structure,
11270 volatile MagickBooleanType
11282 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11283 defined(PNG_MNG_FEATURES_SUPPORTED)
11286 all_images_are_gray,
11296 volatile unsigned int
11307 #if (PNG_LIBPNG_VER < 10200)
11308 if (image_info->verbose)
11309 printf("Your PNG library (libpng-%s) is rather old.\n",
11310 PNG_LIBPNG_VER_STRING);
11316 assert(image_info != (const ImageInfo *) NULL);
11317 assert(image_info->signature == MagickSignature);
11318 assert(image != (Image *) NULL);
11319 assert(image->signature == MagickSignature);
11320 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11321 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11322 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11323 if (status == MagickFalse)
11327 Allocate a MngInfo structure.
11329 have_mng_structure=MagickFalse;
11330 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11331 if (mng_info == (MngInfo *) NULL)
11332 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11334 Initialize members of the MngInfo structure.
11336 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11337 mng_info->image=image;
11338 have_mng_structure=MagickTrue;
11339 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11342 * See if user has requested a specific PNG subformat to be used
11343 * for all of the PNGs in the MNG being written, e.g.,
11345 * convert *.png png8:animation.mng
11347 * To do: check -define png:bit_depth and png:color_type as well,
11348 * or perhaps use mng:bit_depth and mng:color_type instead for
11352 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11353 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11354 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11356 write_jng=MagickFalse;
11357 if (image_info->compression == JPEGCompression)
11358 write_jng=MagickTrue;
11360 mng_info->adjoin=image_info->adjoin &&
11361 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11363 if (logging != MagickFalse)
11365 /* Log some info about the input */
11369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370 " Checking input image(s)");
11372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11373 " Image_info depth: %.20g",(double) image_info->depth);
11375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11376 " Type: %d",image_info->type);
11379 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11382 " Scene: %.20g",(double) scene++);
11384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11385 " Image depth: %.20g",(double) p->depth);
11388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11395 if (p->storage_class == PseudoClass)
11396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 " Storage class: PseudoClass");
11400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11401 " Storage class: DirectClass");
11404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11405 " Number of colors: %.20g",(double) p->colors);
11408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11409 " Number of colors: unspecified");
11411 if (mng_info->adjoin == MagickFalse)
11416 use_global_plte=MagickFalse;
11417 all_images_are_gray=MagickFalse;
11418 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11419 need_local_plte=MagickTrue;
11421 need_defi=MagickFalse;
11422 need_matte=MagickFalse;
11423 mng_info->framing_mode=1;
11424 mng_info->old_framing_mode=1;
11427 if (image_info->page != (char *) NULL)
11430 Determine image bounding box.
11432 SetGeometry(image,&mng_info->page);
11433 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11434 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11446 mng_info->page=image->page;
11447 need_geom=MagickTrue;
11448 if (mng_info->page.width || mng_info->page.height)
11449 need_geom=MagickFalse;
11451 Check all the scenes.
11453 initial_delay=image->delay;
11454 need_iterations=MagickFalse;
11455 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11456 mng_info->equal_physs=MagickTrue,
11457 mng_info->equal_gammas=MagickTrue;
11458 mng_info->equal_srgbs=MagickTrue;
11459 mng_info->equal_backgrounds=MagickTrue;
11461 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11462 defined(PNG_MNG_FEATURES_SUPPORTED)
11463 all_images_are_gray=MagickTrue;
11464 mng_info->equal_palettes=MagickFalse;
11465 need_local_plte=MagickFalse;
11467 for (next_image=image; next_image != (Image *) NULL; )
11471 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11472 mng_info->page.width=next_image->columns+next_image->page.x;
11474 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11475 mng_info->page.height=next_image->rows+next_image->page.y;
11478 if (next_image->page.x || next_image->page.y)
11479 need_defi=MagickTrue;
11481 if (next_image->matte)
11482 need_matte=MagickTrue;
11484 if ((int) next_image->dispose >= BackgroundDispose)
11485 if (next_image->matte || next_image->page.x || next_image->page.y ||
11486 ((next_image->columns < mng_info->page.width) &&
11487 (next_image->rows < mng_info->page.height)))
11488 mng_info->need_fram=MagickTrue;
11490 if (next_image->iterations)
11491 need_iterations=MagickTrue;
11493 final_delay=next_image->delay;
11495 if (final_delay != initial_delay || final_delay > 1UL*
11496 next_image->ticks_per_second)
11497 mng_info->need_fram=1;
11499 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11500 defined(PNG_MNG_FEATURES_SUPPORTED)
11502 check for global palette possibility.
11504 if (image->matte != MagickFalse)
11505 need_local_plte=MagickTrue;
11507 if (need_local_plte == 0)
11509 if (ImageIsGray(image) == MagickFalse)
11510 all_images_are_gray=MagickFalse;
11511 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11512 if (use_global_plte == 0)
11513 use_global_plte=mng_info->equal_palettes;
11514 need_local_plte=!mng_info->equal_palettes;
11517 if (GetNextImageInList(next_image) != (Image *) NULL)
11519 if (next_image->background_color.red !=
11520 next_image->next->background_color.red ||
11521 next_image->background_color.green !=
11522 next_image->next->background_color.green ||
11523 next_image->background_color.blue !=
11524 next_image->next->background_color.blue)
11525 mng_info->equal_backgrounds=MagickFalse;
11527 if (next_image->gamma != next_image->next->gamma)
11528 mng_info->equal_gammas=MagickFalse;
11530 if (next_image->rendering_intent !=
11531 next_image->next->rendering_intent)
11532 mng_info->equal_srgbs=MagickFalse;
11534 if ((next_image->units != next_image->next->units) ||
11535 (next_image->x_resolution != next_image->next->x_resolution) ||
11536 (next_image->y_resolution != next_image->next->y_resolution))
11537 mng_info->equal_physs=MagickFalse;
11539 if (mng_info->equal_chrms)
11541 if (next_image->chromaticity.red_primary.x !=
11542 next_image->next->chromaticity.red_primary.x ||
11543 next_image->chromaticity.red_primary.y !=
11544 next_image->next->chromaticity.red_primary.y ||
11545 next_image->chromaticity.green_primary.x !=
11546 next_image->next->chromaticity.green_primary.x ||
11547 next_image->chromaticity.green_primary.y !=
11548 next_image->next->chromaticity.green_primary.y ||
11549 next_image->chromaticity.blue_primary.x !=
11550 next_image->next->chromaticity.blue_primary.x ||
11551 next_image->chromaticity.blue_primary.y !=
11552 next_image->next->chromaticity.blue_primary.y ||
11553 next_image->chromaticity.white_point.x !=
11554 next_image->next->chromaticity.white_point.x ||
11555 next_image->chromaticity.white_point.y !=
11556 next_image->next->chromaticity.white_point.y)
11557 mng_info->equal_chrms=MagickFalse;
11561 next_image=GetNextImageInList(next_image);
11563 if (image_count < 2)
11565 mng_info->equal_backgrounds=MagickFalse;
11566 mng_info->equal_chrms=MagickFalse;
11567 mng_info->equal_gammas=MagickFalse;
11568 mng_info->equal_srgbs=MagickFalse;
11569 mng_info->equal_physs=MagickFalse;
11570 use_global_plte=MagickFalse;
11571 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11572 need_local_plte=MagickTrue;
11574 need_iterations=MagickFalse;
11577 if (mng_info->need_fram == MagickFalse)
11580 Only certain framing rates 100/n are exactly representable without
11581 the FRAM chunk but we'll allow some slop in VLC files
11583 if (final_delay == 0)
11585 if (need_iterations != MagickFalse)
11588 It's probably a GIF with loop; don't run it *too* fast.
11590 if (mng_info->adjoin)
11593 (void) ThrowMagickException(&image->exception,
11594 GetMagickModule(),CoderWarning,
11595 "input has zero delay between all frames; assuming",
11600 mng_info->ticks_per_second=0;
11602 if (final_delay != 0)
11603 mng_info->ticks_per_second=(png_uint_32)
11604 (image->ticks_per_second/final_delay);
11605 if (final_delay > 50)
11606 mng_info->ticks_per_second=2;
11608 if (final_delay > 75)
11609 mng_info->ticks_per_second=1;
11611 if (final_delay > 125)
11612 mng_info->need_fram=MagickTrue;
11614 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11615 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11616 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11617 1UL*image->ticks_per_second))
11618 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11621 if (mng_info->need_fram != MagickFalse)
11622 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11624 If pseudocolor, we should also check to see if all the
11625 palettes are identical and write a global PLTE if they are.
11629 Write the MNG version 1.0 signature and MHDR chunk.
11631 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11632 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11633 PNGType(chunk,mng_MHDR);
11634 LogPNGChunk(logging,mng_MHDR,28L);
11635 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11636 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11637 PNGLong(chunk+12,mng_info->ticks_per_second);
11638 PNGLong(chunk+16,0L); /* layer count=unknown */
11639 PNGLong(chunk+20,0L); /* frame count=unknown */
11640 PNGLong(chunk+24,0L); /* play time=unknown */
11645 if (need_defi || mng_info->need_fram || use_global_plte)
11646 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11649 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11654 if (need_defi || mng_info->need_fram || use_global_plte)
11655 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11658 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11666 if (need_defi || mng_info->need_fram || use_global_plte)
11667 PNGLong(chunk+28,11L); /* simplicity=LC */
11670 PNGLong(chunk+28,9L); /* simplicity=VLC */
11675 if (need_defi || mng_info->need_fram || use_global_plte)
11676 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11679 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11682 (void) WriteBlob(image,32,chunk);
11683 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11684 option=GetImageOption(image_info,"mng:need-cacheoff");
11685 if (option != (const char *) NULL)
11691 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11693 PNGType(chunk,mng_nEED);
11694 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11695 (void) WriteBlobMSBULong(image,(size_t) length);
11696 LogPNGChunk(logging,mng_nEED,(size_t) length);
11698 (void) WriteBlob(image,length,chunk);
11699 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11701 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11702 (GetNextImageInList(image) != (Image *) NULL) &&
11703 (image->iterations != 1))
11706 Write MNG TERM chunk
11708 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11709 PNGType(chunk,mng_TERM);
11710 LogPNGChunk(logging,mng_TERM,10L);
11711 chunk[4]=3; /* repeat animation */
11712 chunk[5]=0; /* show last frame when done */
11713 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11714 final_delay/MagickMax(image->ticks_per_second,1)));
11716 if (image->iterations == 0)
11717 PNGLong(chunk+10,PNG_UINT_31_MAX);
11720 PNGLong(chunk+10,(png_uint_32) image->iterations);
11722 if (logging != MagickFalse)
11724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11725 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11726 final_delay/MagickMax(image->ticks_per_second,1)));
11728 if (image->iterations == 0)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11734 " Image iterations: %.20g",(double) image->iterations);
11736 (void) WriteBlob(image,14,chunk);
11737 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11740 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11742 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11743 mng_info->equal_srgbs)
11746 Write MNG sRGB chunk
11748 (void) WriteBlobMSBULong(image,1L);
11749 PNGType(chunk,mng_sRGB);
11750 LogPNGChunk(logging,mng_sRGB,1L);
11752 if (image->rendering_intent != UndefinedIntent)
11753 chunk[4]=(unsigned char)
11754 Magick_RenderingIntent_to_PNG_RenderingIntent(
11755 (image->rendering_intent));
11758 chunk[4]=(unsigned char)
11759 Magick_RenderingIntent_to_PNG_RenderingIntent(
11760 (PerceptualIntent));
11762 (void) WriteBlob(image,5,chunk);
11763 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11764 mng_info->have_write_global_srgb=MagickTrue;
11769 if (image->gamma && mng_info->equal_gammas)
11772 Write MNG gAMA chunk
11774 (void) WriteBlobMSBULong(image,4L);
11775 PNGType(chunk,mng_gAMA);
11776 LogPNGChunk(logging,mng_gAMA,4L);
11777 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11778 (void) WriteBlob(image,8,chunk);
11779 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11780 mng_info->have_write_global_gama=MagickTrue;
11782 if (mng_info->equal_chrms)
11788 Write MNG cHRM chunk
11790 (void) WriteBlobMSBULong(image,32L);
11791 PNGType(chunk,mng_cHRM);
11792 LogPNGChunk(logging,mng_cHRM,32L);
11793 primary=image->chromaticity.white_point;
11794 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11795 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11796 primary=image->chromaticity.red_primary;
11797 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11798 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11799 primary=image->chromaticity.green_primary;
11800 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11801 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11802 primary=image->chromaticity.blue_primary;
11803 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11804 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11805 (void) WriteBlob(image,36,chunk);
11806 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11807 mng_info->have_write_global_chrm=MagickTrue;
11810 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11813 Write MNG pHYs chunk
11815 (void) WriteBlobMSBULong(image,9L);
11816 PNGType(chunk,mng_pHYs);
11817 LogPNGChunk(logging,mng_pHYs,9L);
11819 if (image->units == PixelsPerInchResolution)
11821 PNGLong(chunk+4,(png_uint_32)
11822 (image->x_resolution*100.0/2.54+0.5));
11824 PNGLong(chunk+8,(png_uint_32)
11825 (image->y_resolution*100.0/2.54+0.5));
11832 if (image->units == PixelsPerCentimeterResolution)
11834 PNGLong(chunk+4,(png_uint_32)
11835 (image->x_resolution*100.0+0.5));
11837 PNGLong(chunk+8,(png_uint_32)
11838 (image->y_resolution*100.0+0.5));
11845 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11846 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11850 (void) WriteBlob(image,13,chunk);
11851 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11854 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11855 or does not cover the entire frame.
11857 if (write_mng && (image->matte || image->page.x > 0 ||
11858 image->page.y > 0 || (image->page.width &&
11859 (image->page.width+image->page.x < mng_info->page.width))
11860 || (image->page.height && (image->page.height+image->page.y
11861 < mng_info->page.height))))
11863 (void) WriteBlobMSBULong(image,6L);
11864 PNGType(chunk,mng_BACK);
11865 LogPNGChunk(logging,mng_BACK,6L);
11866 red=ScaleQuantumToShort(image->background_color.red);
11867 green=ScaleQuantumToShort(image->background_color.green);
11868 blue=ScaleQuantumToShort(image->background_color.blue);
11869 PNGShort(chunk+4,red);
11870 PNGShort(chunk+6,green);
11871 PNGShort(chunk+8,blue);
11872 (void) WriteBlob(image,10,chunk);
11873 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11874 if (mng_info->equal_backgrounds)
11876 (void) WriteBlobMSBULong(image,6L);
11877 PNGType(chunk,mng_bKGD);
11878 LogPNGChunk(logging,mng_bKGD,6L);
11879 (void) WriteBlob(image,10,chunk);
11880 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11884 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11885 if ((need_local_plte == MagickFalse) &&
11886 (image->storage_class == PseudoClass) &&
11887 (all_images_are_gray == MagickFalse))
11893 Write MNG PLTE chunk
11895 data_length=3*image->colors;
11896 (void) WriteBlobMSBULong(image,data_length);
11897 PNGType(chunk,mng_PLTE);
11898 LogPNGChunk(logging,mng_PLTE,data_length);
11900 for (i=0; i < (ssize_t) image->colors; i++)
11902 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11903 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11904 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11907 (void) WriteBlob(image,data_length+4,chunk);
11908 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11909 mng_info->have_write_global_plte=MagickTrue;
11915 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11916 defined(PNG_MNG_FEATURES_SUPPORTED)
11917 mng_info->equal_palettes=MagickFalse;
11921 if (mng_info->adjoin)
11923 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11924 defined(PNG_MNG_FEATURES_SUPPORTED)
11926 If we aren't using a global palette for the entire MNG, check to
11927 see if we can use one for two or more consecutive images.
11929 if (need_local_plte && use_global_plte && !all_images_are_gray)
11931 if (mng_info->IsPalette)
11934 When equal_palettes is true, this image has the same palette
11935 as the previous PseudoClass image
11937 mng_info->have_write_global_plte=mng_info->equal_palettes;
11938 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11939 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11942 Write MNG PLTE chunk
11947 data_length=3*image->colors;
11948 (void) WriteBlobMSBULong(image,data_length);
11949 PNGType(chunk,mng_PLTE);
11950 LogPNGChunk(logging,mng_PLTE,data_length);
11952 for (i=0; i < (ssize_t) image->colors; i++)
11954 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11955 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11956 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11959 (void) WriteBlob(image,data_length+4,chunk);
11960 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11961 (uInt) (data_length+4)));
11962 mng_info->have_write_global_plte=MagickTrue;
11966 mng_info->have_write_global_plte=MagickFalse;
11977 previous_x=mng_info->page.x;
11978 previous_y=mng_info->page.y;
11985 mng_info->page=image->page;
11986 if ((mng_info->page.x != previous_x) ||
11987 (mng_info->page.y != previous_y))
11989 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11990 PNGType(chunk,mng_DEFI);
11991 LogPNGChunk(logging,mng_DEFI,12L);
11992 chunk[4]=0; /* object 0 MSB */
11993 chunk[5]=0; /* object 0 LSB */
11994 chunk[6]=0; /* visible */
11995 chunk[7]=0; /* abstract */
11996 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11997 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11998 (void) WriteBlob(image,16,chunk);
11999 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12004 mng_info->write_mng=write_mng;
12006 if ((int) image->dispose >= 3)
12007 mng_info->framing_mode=3;
12009 if (mng_info->need_fram && mng_info->adjoin &&
12010 ((image->delay != mng_info->delay) ||
12011 (mng_info->framing_mode != mng_info->old_framing_mode)))
12013 if (image->delay == mng_info->delay)
12016 Write a MNG FRAM chunk with the new framing mode.
12018 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12019 PNGType(chunk,mng_FRAM);
12020 LogPNGChunk(logging,mng_FRAM,1L);
12021 chunk[4]=(unsigned char) mng_info->framing_mode;
12022 (void) WriteBlob(image,5,chunk);
12023 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12028 Write a MNG FRAM chunk with the delay.
12030 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12031 PNGType(chunk,mng_FRAM);
12032 LogPNGChunk(logging,mng_FRAM,10L);
12033 chunk[4]=(unsigned char) mng_info->framing_mode;
12034 chunk[5]=0; /* frame name separator (no name) */
12035 chunk[6]=2; /* flag for changing default delay */
12036 chunk[7]=0; /* flag for changing frame timeout */
12037 chunk[8]=0; /* flag for changing frame clipping */
12038 chunk[9]=0; /* flag for changing frame sync_id */
12039 PNGLong(chunk+10,(png_uint_32)
12040 ((mng_info->ticks_per_second*
12041 image->delay)/MagickMax(image->ticks_per_second,1)));
12042 (void) WriteBlob(image,14,chunk);
12043 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12044 mng_info->delay=(png_uint_32) image->delay;
12046 mng_info->old_framing_mode=mng_info->framing_mode;
12049 #if defined(JNG_SUPPORTED)
12050 if (image_info->compression == JPEGCompression)
12055 if (logging != MagickFalse)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12057 " Writing JNG object.");
12058 /* To do: specify the desired alpha compression method. */
12059 write_info=CloneImageInfo(image_info);
12060 write_info->compression=UndefinedCompression;
12061 status=WriteOneJNGImage(mng_info,write_info,image);
12062 write_info=DestroyImageInfo(write_info);
12067 if (logging != MagickFalse)
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069 " Writing PNG object.");
12071 mng_info->need_blob = MagickFalse;
12072 mng_info->ping_preserve_colormap = MagickFalse;
12074 /* We don't want any ancillary chunks written */
12075 mng_info->ping_exclude_bKGD=MagickTrue;
12076 mng_info->ping_exclude_cHRM=MagickTrue;
12077 mng_info->ping_exclude_date=MagickTrue;
12078 mng_info->ping_exclude_EXIF=MagickTrue;
12079 mng_info->ping_exclude_gAMA=MagickTrue;
12080 mng_info->ping_exclude_iCCP=MagickTrue;
12081 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12082 mng_info->ping_exclude_oFFs=MagickTrue;
12083 mng_info->ping_exclude_pHYs=MagickTrue;
12084 mng_info->ping_exclude_sRGB=MagickTrue;
12085 mng_info->ping_exclude_tEXt=MagickTrue;
12086 mng_info->ping_exclude_tRNS=MagickTrue;
12087 mng_info->ping_exclude_vpAg=MagickTrue;
12088 mng_info->ping_exclude_zCCP=MagickTrue;
12089 mng_info->ping_exclude_zTXt=MagickTrue;
12091 status=WriteOnePNGImage(mng_info,image_info,image);
12094 if (status == MagickFalse)
12096 MngInfoFreeStruct(mng_info,&have_mng_structure);
12097 (void) CloseBlob(image);
12098 return(MagickFalse);
12100 (void) CatchImageException(image);
12101 if (GetNextImageInList(image) == (Image *) NULL)
12103 image=SyncNextImageInList(image);
12104 status=SetImageProgress(image,SaveImagesTag,scene++,
12105 GetImageListLength(image));
12107 if (status == MagickFalse)
12110 } while (mng_info->adjoin);
12114 while (GetPreviousImageInList(image) != (Image *) NULL)
12115 image=GetPreviousImageInList(image);
12117 Write the MEND chunk.
12119 (void) WriteBlobMSBULong(image,0x00000000L);
12120 PNGType(chunk,mng_MEND);
12121 LogPNGChunk(logging,mng_MEND,0L);
12122 (void) WriteBlob(image,4,chunk);
12123 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12126 Relinquish resources.
12128 (void) CloseBlob(image);
12129 MngInfoFreeStruct(mng_info,&have_mng_structure);
12131 if (logging != MagickFalse)
12132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12134 return(MagickTrue);
12136 #else /* PNG_LIBPNG_VER > 10011 */
12138 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12141 printf("Your PNG library is too old: You have libpng-%s\n",
12142 PNG_LIBPNG_VER_STRING);
12144 ThrowBinaryException(CoderError,"PNG library is too old",
12145 image_info->filename);
12148 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12150 return(WritePNGImage(image_info,image));
12152 #endif /* PNG_LIBPNG_VER > 10011 */