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.
506 * We don't use the method GetImageDepth() because it doesn't check
507 * background * and doesn't handle PseudoClass specially. Also
508 * GetImageDepth() uses multiplication and division by 257 instead of
509 * shifting, so it might be slower.
513 ok_to_reduce=MagickFalse;
515 if (image->depth >= 16)
522 pnghi= MAGICKCORE_QUANTUM_DEPTH - 8,
523 pnglo= MAGICKCORE_QUANTUM_DEPTH - 16;
526 (((((size_t) image->background_color.red >> pnghi) & 0xff)
527 == (((size_t) image->background_color.red >> pnglo) & 0xff)) &&
528 ((((size_t) image->background_color.green >> pnghi) & 0xff)
529 == (((size_t) image->background_color.green >> pnglo) & 0xff)) &&
530 ((((size_t) image->background_color.blue >> pnghi) & 0xff)
531 == (((size_t) image->background_color.blue >> pnglo) & 0xff))) ?
532 MagickTrue : MagickFalse;
534 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
538 for (indx=0; indx < (ssize_t) image->colors; indx++)
541 (((((size_t) image->colormap[indx].red >> pnghi) & 0xff)
542 == (((size_t) image->colormap[indx].red >> pnglo) & 0xff)) &&
543 ((((size_t) image->colormap[indx].green >> pnghi) & 0xff)
544 == (((size_t) image->colormap[indx].green >> pnglo) & 0xff))
545 && ((((size_t) image->colormap[indx].blue >> pnghi) & 0xff)
546 == (((size_t) image->colormap[indx].blue >> pnglo) & 0xff)) &&
547 (image->matte == MagickFalse ||
548 (((size_t) image->colormap[indx].opacity >> pnghi) & 0xff)
549 == (((size_t) image->colormap[indx].opacity >> pnglo)
550 & 0xff))) ? MagickTrue : MagickFalse;
551 if (ok_to_reduce == MagickFalse)
556 if ((ok_to_reduce != MagickFalse) &&
557 (image->storage_class != PseudoClass))
565 for (y=0; y < (ssize_t) image->rows; y++)
567 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
569 if (p == (const PixelPacket *) NULL)
571 ok_to_reduce = MagickFalse;
575 for (x=(ssize_t) image->columns-1; x >= 0; x--)
578 ((((size_t) p->red >> pnghi) & 0xff) ==
579 (((size_t) p->red >> pnglo) & 0xff)) &&
580 ((((size_t) p->green >> pnghi) & 0xff) ==
581 (((size_t) p->green >> pnglo) & 0xff)) &&
582 ((((size_t) p->blue >> pnghi) & 0xff) ==
583 (((size_t) p->blue >> pnglo) & 0xff)) &&
584 (((image->matte == MagickFalse ||
585 (((size_t) p->opacity >> pnghi) & 0xff) ==
586 (((size_t) p->opacity >> pnglo) & 0xff))))) ?
587 MagickTrue : MagickFalse;
589 if (ok_to_reduce == MagickFalse)
599 if (ok_to_reduce != MagickFalse)
601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
602 " OK to reduce PNG bit depth to 8 without loss of info");
606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
607 " Not OK to reduce PNG bit depth to 8 without loss of info");
613 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
616 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
620 case PerceptualIntent:
626 case SaturationIntent:
637 static RenderingIntent
638 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
643 return PerceptualIntent;
646 return RelativeIntent;
649 return SaturationIntent;
652 return AbsoluteIntent;
655 return UndefinedIntent;
659 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
667 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681 % I m a g e I s G r a y %
685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687 % Like IsGrayImage except does not change DirectClass to PseudoClass %
689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
691 static MagickBooleanType ImageIsGray(Image *image)
693 register const PixelPacket
701 assert(image != (Image *) NULL);
702 assert(image->signature == MagickSignature);
703 if (image->debug != MagickFalse)
704 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
706 if (image->storage_class == PseudoClass)
708 for (i=0; i < (ssize_t) image->colors; i++)
709 if (IsGray(image->colormap+i) == MagickFalse)
713 for (y=0; y < (ssize_t) image->rows; y++)
715 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
716 if (p == (const PixelPacket *) NULL)
718 for (x=(ssize_t) image->columns-1; x >= 0; x--)
720 if (IsGray(p) == MagickFalse)
727 #endif /* PNG_LIBPNG_VER > 10011 */
728 #endif /* MAGICKCORE_PNG_DELEGATE */
731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
741 % IsMNG() returns MagickTrue if the image format type, identified by the
742 % magick string, is MNG.
744 % The format of the IsMNG method is:
746 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
748 % A description of each parameter follows:
750 % o magick: compare image format pattern against these bytes.
752 % o length: Specifies the length of the magick string.
756 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
761 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778 % IsJNG() returns MagickTrue if the image format type, identified by the
779 % magick string, is JNG.
781 % The format of the IsJNG method is:
783 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
785 % A description of each parameter follows:
787 % o magick: compare image format pattern against these bytes.
789 % o length: Specifies the length of the magick string.
793 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
798 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815 % IsPNG() returns MagickTrue if the image format type, identified by the
816 % magick string, is PNG.
818 % The format of the IsPNG method is:
820 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
822 % A description of each parameter follows:
824 % o magick: compare image format pattern against these bytes.
826 % o length: Specifies the length of the magick string.
829 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
834 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
840 #if defined(MAGICKCORE_PNG_DELEGATE)
841 #if defined(__cplusplus) || defined(c_plusplus)
845 #if (PNG_LIBPNG_VER > 10011)
846 static size_t WriteBlobMSBULong(Image *image,const size_t value)
851 assert(image != (Image *) NULL);
852 assert(image->signature == MagickSignature);
853 buffer[0]=(unsigned char) (value >> 24);
854 buffer[1]=(unsigned char) (value >> 16);
855 buffer[2]=(unsigned char) (value >> 8);
856 buffer[3]=(unsigned char) value;
857 return((size_t) WriteBlob(image,4,buffer));
860 static void PNGLong(png_bytep p,png_uint_32 value)
862 *p++=(png_byte) ((value >> 24) & 0xff);
863 *p++=(png_byte) ((value >> 16) & 0xff);
864 *p++=(png_byte) ((value >> 8) & 0xff);
865 *p++=(png_byte) (value & 0xff);
868 #if defined(JNG_SUPPORTED)
869 static void PNGsLong(png_bytep p,png_int_32 value)
871 *p++=(png_byte) ((value >> 24) & 0xff);
872 *p++=(png_byte) ((value >> 16) & 0xff);
873 *p++=(png_byte) ((value >> 8) & 0xff);
874 *p++=(png_byte) (value & 0xff);
878 static void PNGShort(png_bytep p,png_uint_16 value)
880 *p++=(png_byte) ((value >> 8) & 0xff);
881 *p++=(png_byte) (value & 0xff);
884 static void PNGType(png_bytep p,png_bytep type)
886 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
889 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
892 if (logging != MagickFalse)
893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
894 " Writing %c%c%c%c chunk, length: %.20g",
895 type[0],type[1],type[2],type[3],(double) length);
897 #endif /* PNG_LIBPNG_VER > 10011 */
899 #if defined(__cplusplus) || defined(c_plusplus)
903 #if PNG_LIBPNG_VER > 10011
905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
909 % R e a d P N G I m a g e %
913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
916 % Multiple-image Network Graphics (MNG) image file and returns it. It
917 % allocates the memory necessary for the new Image structure and returns a
918 % pointer to the new image or set of images.
920 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
922 % The format of the ReadPNGImage method is:
924 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
926 % A description of each parameter follows:
928 % o image_info: the image info.
930 % o exception: return any errors or warnings in this structure.
932 % To do, more or less in chronological order (as of version 5.5.2,
933 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
935 % Get 16-bit cheap transparency working.
937 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
939 % Preserve all unknown and not-yet-handled known chunks found in input
940 % PNG file and copy them into output PNG files according to the PNG
943 % (At this point, PNG encoding should be in full MNG compliance)
945 % Provide options for choice of background to use when the MNG BACK
946 % chunk is not present or is not mandatory (i.e., leave transparent,
947 % user specified, MNG BACK, PNG bKGD)
949 % Implement LOOP/ENDL [done, but could do discretionary loops more
950 % efficiently by linking in the duplicate frames.].
952 % Decode and act on the MHDR simplicity profile (offer option to reject
953 % files or attempt to process them anyway when the profile isn't LC or VLC).
955 % Upgrade to full MNG without Delta-PNG.
957 % o BACK [done a while ago except for background image ID]
958 % o MOVE [done 15 May 1999]
959 % o CLIP [done 15 May 1999]
960 % o DISC [done 19 May 1999]
961 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
962 % o SEEK [partially done 19 May 1999 (discard function only)]
966 % o MNG-level tEXt/iTXt/zTXt
971 % o iTXt (wait for libpng implementation).
973 % Use the scene signature to discover when an identical scene is
974 % being reused, and just point to the original image->exception instead
975 % of storing another set of pixels. This not specific to MNG
976 % but could be applied generally.
978 % Upgrade to full MNG with Delta-PNG.
982 % We will not attempt to read files containing the CgBI chunk.
983 % They are really Xcode files meant for display on the iPhone.
984 % These are not valid PNG files and it is impossible to recover
985 % the orginal PNG from files that have been converted to Xcode-PNG,
986 % since irretrievable loss of color data has occurred due to the
987 % use of premultiplied alpha.
990 #if defined(__cplusplus) || defined(c_plusplus)
995 This the function that does the actual reading of data. It is
996 the same as the one supplied in libpng, except that it receives the
997 datastream from the ReadBlob() function instead of standard input.
999 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1004 image=(Image *) png_get_io_ptr(png_ptr);
1010 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1011 if (check != length)
1016 (void) FormatMagickString(msg,MaxTextExtent,
1017 "Expected %.20g bytes; found %.20g bytes",(double) length,
1019 png_warning(png_ptr,msg);
1020 png_error(png_ptr,"Read Exception");
1025 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1026 !defined(PNG_MNG_FEATURES_SUPPORTED)
1027 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1028 * older than libpng-1.0.3a, which was the first to allow the empty
1029 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1030 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1031 * encountered after an empty PLTE, so we have to look ahead for bKGD
1032 * chunks and remove them from the datastream that is passed to libpng,
1033 * and store their contents for later use.
1035 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1050 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1051 image=(Image *) mng_info->image;
1052 while (mng_info->bytes_in_read_buffer && length)
1054 data[i]=mng_info->read_buffer[i];
1055 mng_info->bytes_in_read_buffer--;
1061 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1063 if (check != length)
1064 png_error(png_ptr,"Read Exception");
1068 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1071 check=(png_size_t) ReadBlob(image,(size_t) length,
1072 (char *) mng_info->read_buffer);
1073 mng_info->read_buffer[4]=0;
1074 mng_info->bytes_in_read_buffer=4;
1075 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1076 mng_info->found_empty_plte=MagickTrue;
1077 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1079 mng_info->found_empty_plte=MagickFalse;
1080 mng_info->have_saved_bkgd_index=MagickFalse;
1084 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1087 check=(png_size_t) ReadBlob(image,(size_t) length,
1088 (char *) mng_info->read_buffer);
1089 mng_info->read_buffer[4]=0;
1090 mng_info->bytes_in_read_buffer=4;
1091 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1092 if (mng_info->found_empty_plte)
1095 Skip the bKGD data byte and CRC.
1098 ReadBlob(image,5,(char *) mng_info->read_buffer);
1099 check=(png_size_t) ReadBlob(image,(size_t) length,
1100 (char *) mng_info->read_buffer);
1101 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1102 mng_info->have_saved_bkgd_index=MagickTrue;
1103 mng_info->bytes_in_read_buffer=0;
1111 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1116 image=(Image *) png_get_io_ptr(png_ptr);
1122 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1124 if (check != length)
1125 png_error(png_ptr,"WriteBlob Failed");
1129 static void png_flush_data(png_structp png_ptr)
1134 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1135 static int PalettesAreEqual(Image *a,Image *b)
1140 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1141 return((int) MagickFalse);
1143 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1144 return((int) MagickFalse);
1146 if (a->colors != b->colors)
1147 return((int) MagickFalse);
1149 for (i=0; i < (ssize_t) a->colors; i++)
1151 if ((a->colormap[i].red != b->colormap[i].red) ||
1152 (a->colormap[i].green != b->colormap[i].green) ||
1153 (a->colormap[i].blue != b->colormap[i].blue))
1154 return((int) MagickFalse);
1157 return((int) MagickTrue);
1161 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1163 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1164 mng_info->exists[i] && !mng_info->frozen[i])
1166 #ifdef MNG_OBJECT_BUFFERS
1167 if (mng_info->ob[i] != (MngBuffer *) NULL)
1169 if (mng_info->ob[i]->reference_count > 0)
1170 mng_info->ob[i]->reference_count--;
1172 if (mng_info->ob[i]->reference_count == 0)
1174 if (mng_info->ob[i]->image != (Image *) NULL)
1175 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1177 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1180 mng_info->ob[i]=(MngBuffer *) NULL;
1182 mng_info->exists[i]=MagickFalse;
1183 mng_info->invisible[i]=MagickFalse;
1184 mng_info->viewable[i]=MagickFalse;
1185 mng_info->frozen[i]=MagickFalse;
1186 mng_info->x_off[i]=0;
1187 mng_info->y_off[i]=0;
1188 mng_info->object_clip[i].left=0;
1189 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1190 mng_info->object_clip[i].top=0;
1191 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1195 static void MngInfoFreeStruct(MngInfo *mng_info,
1196 MagickBooleanType *have_mng_structure)
1198 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1203 for (i=1; i < MNG_MAX_OBJECTS; i++)
1204 MngInfoDiscardObject(mng_info,i);
1206 if (mng_info->global_plte != (png_colorp) NULL)
1207 mng_info->global_plte=(png_colorp)
1208 RelinquishMagickMemory(mng_info->global_plte);
1210 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1211 *have_mng_structure=MagickFalse;
1215 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1221 if (box.left < box2.left)
1224 if (box.top < box2.top)
1227 if (box.right > box2.right)
1228 box.right=box2.right;
1230 if (box.bottom > box2.bottom)
1231 box.bottom=box2.bottom;
1236 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1242 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1244 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1245 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1246 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1247 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1248 if (delta_type != 0)
1250 box.left+=previous_box.left;
1251 box.right+=previous_box.right;
1252 box.top+=previous_box.top;
1253 box.bottom+=previous_box.bottom;
1259 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1265 Read two ssize_ts from CLON, MOVE or PAST chunk
1267 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1268 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1270 if (delta_type != 0)
1272 pair.a+=previous_pair.a;
1273 pair.b+=previous_pair.b;
1279 static long mng_get_long(unsigned char *p)
1281 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1284 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1289 image=(Image *) png_get_error_ptr(ping);
1291 if (image->debug != MagickFalse)
1292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1293 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1295 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1296 message,"`%s'",image->filename);
1298 #if (PNG_LIBPNG_VER < 10500)
1299 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1300 * are building with libpng-1.4.x and can be ignored.
1302 longjmp(ping->jmpbuf,1);
1304 png_longjmp(ping,1);
1308 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1313 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1314 png_error(ping, message);
1316 image=(Image *) png_get_error_ptr(ping);
1317 if (image->debug != MagickFalse)
1318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1319 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1321 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1322 message,"`%s'",image->filename);
1325 #ifdef PNG_USER_MEM_SUPPORTED
1326 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1328 #if (PNG_LIBPNG_VER < 10011)
1333 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1336 png_error("Insufficient memory.");
1341 return((png_voidp) AcquireMagickMemory((size_t) size));
1346 Free a pointer. It is removed from the list at the same time.
1348 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1351 ptr=RelinquishMagickMemory(ptr);
1352 return((png_free_ptr) NULL);
1356 #if defined(__cplusplus) || defined(c_plusplus)
1361 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1362 png_textp text,int ii)
1367 register unsigned char
1381 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1382 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1383 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1384 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1385 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1389 /* look for newline */
1393 /* look for length */
1394 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1397 length=(png_uint_32) StringToLong(sp);
1399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1400 " length: %lu",(unsigned long) length);
1402 while (*sp != ' ' && *sp != '\n')
1405 /* allocate space */
1408 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1409 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1410 return(MagickFalse);
1413 profile=AcquireStringInfo(length);
1415 if (profile == (StringInfo *) NULL)
1417 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1418 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1419 "unable to copy profile");
1420 return(MagickFalse);
1423 /* copy profile, skipping white space and column 1 "=" signs */
1424 dp=GetStringInfoDatum(profile);
1427 for (i=0; i < (ssize_t) nibbles; i++)
1429 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1433 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1434 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1435 profile=DestroyStringInfo(profile);
1436 return(MagickFalse);
1442 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1445 (*dp++)+=unhex[(int) *sp++];
1448 We have already read "Raw profile type.
1450 (void) SetImageProfile(image,&text[ii].key[17],profile);
1451 profile=DestroyStringInfo(profile);
1453 if (image_info->verbose)
1454 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1459 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1460 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1466 /* The unknown chunk structure contains the chunk data:
1471 Note that libpng has already taken care of the CRC handling.
1475 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1476 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1477 return(0); /* Did not recognize */
1479 /* recognized vpAg */
1481 if (chunk->size != 9)
1482 return(-1); /* Error return */
1484 if (chunk->data[8] != 0)
1485 return(0); /* ImageMagick requires pixel units */
1487 image=(Image *) png_get_user_chunk_ptr(ping);
1489 image->page.width=(size_t) ((chunk->data[0] << 24) |
1490 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1492 image->page.height=(size_t) ((chunk->data[4] << 24) |
1493 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1495 /* Return one of the following: */
1496 /* return(-n); chunk had an error */
1497 /* return(0); did not recognize */
1498 /* return(n); success */
1506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510 % R e a d O n e P N G I m a g e %
1514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1516 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1517 % (minus the 8-byte signature) and returns it. It allocates the memory
1518 % necessary for the new Image structure and returns a pointer to the new
1521 % The format of the ReadOnePNGImage method is:
1523 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1524 % ExceptionInfo *exception)
1526 % A description of each parameter follows:
1528 % o mng_info: Specifies a pointer to a MngInfo structure.
1530 % o image_info: the image info.
1532 % o exception: return any errors or warnings in this structure.
1535 static Image *ReadOnePNGImage(MngInfo *mng_info,
1536 const ImageInfo *image_info, ExceptionInfo *exception)
1538 /* Read one PNG image */
1540 /* To do: Read the tIME chunk into the date:modify property */
1541 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1555 ping_interlace_method,
1556 ping_compression_method,
1604 register unsigned char
1607 register IndexPacket
1614 register PixelPacket
1624 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1625 png_byte unused_chunks[]=
1627 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1628 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1629 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1630 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1631 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1632 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1636 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1637 " Enter ReadOnePNGImage()");
1639 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1640 LockSemaphoreInfo(ping_semaphore);
1643 #if (PNG_LIBPNG_VER < 10200)
1644 if (image_info->verbose)
1645 printf("Your PNG library (libpng-%s) is rather old.\n",
1646 PNG_LIBPNG_VER_STRING);
1649 #if (PNG_LIBPNG_VER >= 10400)
1650 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1651 if (image_info->verbose)
1653 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1654 PNG_LIBPNG_VER_STRING);
1655 printf("Please update it.\n");
1661 quantum_info = (QuantumInfo *) NULL;
1662 image=mng_info->image;
1664 if (logging != MagickFalse)
1665 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1666 " image->matte=%d",(int) image->matte);
1668 /* Set to an out-of-range color unless tRNS chunk is present */
1669 transparent_color.red=65537;
1670 transparent_color.green=65537;
1671 transparent_color.blue=65537;
1672 transparent_color.opacity=65537;
1676 num_raw_profiles = 0;
1679 Allocate the PNG structures
1681 #ifdef PNG_USER_MEM_SUPPORTED
1682 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1683 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1684 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1686 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1687 MagickPNGErrorHandler,MagickPNGWarningHandler);
1689 if (ping == (png_struct *) NULL)
1690 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1692 ping_info=png_create_info_struct(ping);
1694 if (ping_info == (png_info *) NULL)
1696 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1697 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1700 end_info=png_create_info_struct(ping);
1702 if (end_info == (png_info *) NULL)
1704 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1705 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1708 ping_pixels=(unsigned char *) NULL;
1710 if (setjmp(png_jmpbuf(ping)))
1713 PNG image is corrupt.
1715 png_destroy_read_struct(&ping,&ping_info,&end_info);
1716 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1717 UnlockSemaphoreInfo(ping_semaphore);
1719 if (logging != MagickFalse)
1720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1721 " exit ReadOnePNGImage() with error.");
1723 if (image != (Image *) NULL)
1725 InheritException(exception,&image->exception);
1729 return(GetFirstImageInList(image));
1732 Prepare PNG for reading.
1735 mng_info->image_found++;
1736 png_set_sig_bytes(ping,8);
1738 if (LocaleCompare(image_info->magick,"MNG") == 0)
1740 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1741 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1742 png_set_read_fn(ping,image,png_get_data);
1744 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1745 png_permit_empty_plte(ping,MagickTrue);
1746 png_set_read_fn(ping,image,png_get_data);
1748 mng_info->image=image;
1749 mng_info->bytes_in_read_buffer=0;
1750 mng_info->found_empty_plte=MagickFalse;
1751 mng_info->have_saved_bkgd_index=MagickFalse;
1752 png_set_read_fn(ping,mng_info,mng_get_data);
1758 png_set_read_fn(ping,image,png_get_data);
1760 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1761 /* Ignore unused chunks and all unknown chunks except for vpAg */
1762 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1763 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1764 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1765 (int)sizeof(unused_chunks)/5);
1766 /* Callback for other unknown chunks */
1767 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1770 #if (PNG_LIBPNG_VER < 10400)
1771 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1772 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1773 /* Disable thread-unsafe features of pnggccrd */
1774 if (png_access_version_number() >= 10200)
1776 png_uint_32 mmx_disable_mask=0;
1777 png_uint_32 asm_flags;
1779 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1780 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1781 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1782 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1783 asm_flags=png_get_asm_flags(ping);
1784 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1789 png_read_info(ping,ping_info);
1791 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1792 &ping_bit_depth,&ping_color_type,
1793 &ping_interlace_method,&ping_compression_method,
1794 &ping_filter_method);
1796 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1799 (void) png_get_bKGD(ping, ping_info, &ping_background);
1801 if (ping_bit_depth < 8)
1803 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1805 png_set_packing(ping);
1810 image->depth=ping_bit_depth;
1811 image->depth=GetImageQuantumDepth(image,MagickFalse);
1812 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1813 if (logging != MagickFalse)
1815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1816 " PNG width: %.20g, height: %.20g",
1817 (double) ping_width, (double) ping_height);
1819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1820 " PNG color_type: %d, bit_depth: %d",
1821 ping_color_type, ping_bit_depth);
1823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " PNG compression_method: %d",
1825 ping_compression_method);
1827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1828 " PNG interlace_method: %d, filter_method: %d",
1829 ping_interlace_method,ping_filter_method);
1832 #ifdef PNG_READ_iCCP_SUPPORTED
1833 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1838 #if (PNG_LIBPNG_VER < 10500)
1852 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1855 if (profile_length != 0)
1860 if (logging != MagickFalse)
1861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " Reading PNG iCCP chunk.");
1863 profile=AcquireStringInfo(profile_length);
1864 SetStringInfoDatum(profile,(const unsigned char *) info);
1865 (void) SetImageProfile(image,"icc",profile);
1866 profile=DestroyStringInfo(profile);
1870 #if defined(PNG_READ_sRGB_SUPPORTED)
1872 if (mng_info->have_global_srgb)
1873 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1874 (mng_info->global_srgb_intent);
1876 if (png_get_sRGB(ping,ping_info,&intent))
1878 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1881 if (logging != MagickFalse)
1882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1883 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1888 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1889 if (mng_info->have_global_gama)
1890 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1892 if (png_get_gAMA(ping,ping_info,&file_gamma))
1894 image->gamma=(float) file_gamma;
1895 if (logging != MagickFalse)
1896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1897 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1900 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1902 if (mng_info->have_global_chrm != MagickFalse)
1904 (void) png_set_cHRM(ping,ping_info,
1905 mng_info->global_chrm.white_point.x,
1906 mng_info->global_chrm.white_point.y,
1907 mng_info->global_chrm.red_primary.x,
1908 mng_info->global_chrm.red_primary.y,
1909 mng_info->global_chrm.green_primary.x,
1910 mng_info->global_chrm.green_primary.y,
1911 mng_info->global_chrm.blue_primary.x,
1912 mng_info->global_chrm.blue_primary.y);
1916 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1918 (void) png_get_cHRM(ping,ping_info,
1919 &image->chromaticity.white_point.x,
1920 &image->chromaticity.white_point.y,
1921 &image->chromaticity.red_primary.x,
1922 &image->chromaticity.red_primary.y,
1923 &image->chromaticity.green_primary.x,
1924 &image->chromaticity.green_primary.y,
1925 &image->chromaticity.blue_primary.x,
1926 &image->chromaticity.blue_primary.y);
1928 if (logging != MagickFalse)
1929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1930 " Reading PNG cHRM chunk.");
1933 if (image->rendering_intent != UndefinedIntent)
1935 png_set_sRGB(ping,ping_info,
1936 Magick_RenderingIntent_to_PNG_RenderingIntent
1937 (image->rendering_intent));
1938 png_set_gAMA(ping,ping_info,0.45455f);
1939 png_set_cHRM(ping,ping_info,
1940 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1941 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1943 #if defined(PNG_oFFs_SUPPORTED)
1944 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1946 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1947 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1949 if (logging != MagickFalse)
1950 if (image->page.x || image->page.y)
1951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1952 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1953 image->page.x,(double) image->page.y);
1956 #if defined(PNG_pHYs_SUPPORTED)
1957 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1959 if (mng_info->have_global_phys)
1961 png_set_pHYs(ping,ping_info,
1962 mng_info->global_x_pixels_per_unit,
1963 mng_info->global_y_pixels_per_unit,
1964 mng_info->global_phys_unit_type);
1968 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1971 Set image resolution.
1973 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1975 image->x_resolution=(double) x_resolution;
1976 image->y_resolution=(double) y_resolution;
1978 if (unit_type == PNG_RESOLUTION_METER)
1980 image->units=PixelsPerCentimeterResolution;
1981 image->x_resolution=(double) x_resolution/100.0;
1982 image->y_resolution=(double) y_resolution/100.0;
1985 if (logging != MagickFalse)
1986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1987 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1988 (double) x_resolution,(double) y_resolution,unit_type);
1992 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2000 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2002 if ((number_colors == 0) &&
2003 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2005 if (mng_info->global_plte_length)
2007 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2008 (int) mng_info->global_plte_length);
2010 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2011 if (mng_info->global_trns_length)
2013 if (mng_info->global_trns_length >
2014 mng_info->global_plte_length)
2015 (void) ThrowMagickException(&image->exception,
2016 GetMagickModule(),CoderError,
2017 "global tRNS has more entries than global PLTE",
2018 "`%s'",image_info->filename);
2019 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2020 (int) mng_info->global_trns_length,NULL);
2022 #if defined(PNG_READ_bKGD_SUPPORTED)
2024 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2025 mng_info->have_saved_bkgd_index ||
2027 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2032 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2033 if (mng_info->have_saved_bkgd_index)
2034 background.index=mng_info->saved_bkgd_index;
2036 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2037 background.index=ping_background->index;
2039 background.red=(png_uint_16)
2040 mng_info->global_plte[background.index].red;
2042 background.green=(png_uint_16)
2043 mng_info->global_plte[background.index].green;
2045 background.blue=(png_uint_16)
2046 mng_info->global_plte[background.index].blue;
2048 png_set_bKGD(ping,ping_info,&background);
2053 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2054 CoderError,"No global PLTE in file","`%s'",
2055 image_info->filename);
2059 #if defined(PNG_READ_bKGD_SUPPORTED)
2060 if (mng_info->have_global_bkgd &&
2061 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2062 image->background_color=mng_info->mng_global_bkgd;
2064 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2067 Set image background color.
2069 if (logging != MagickFalse)
2070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2071 " Reading PNG bKGD chunk.");
2073 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2075 image->background_color.red=ping_background->red;
2076 image->background_color.green=ping_background->green;
2077 image->background_color.blue=ping_background->blue;
2080 else /* Scale background components to 16-bit */
2085 if (logging != MagickFalse)
2086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2087 " raw ping_background=(%d,%d,%d).",ping_background->red,
2088 ping_background->green,ping_background->blue);
2092 if (ping_bit_depth == 1)
2095 else if (ping_bit_depth == 2)
2098 else if (ping_bit_depth == 4)
2101 if (ping_bit_depth <= 8)
2104 ping_background->red *= bkgd_scale;
2105 ping_background->green *= bkgd_scale;
2106 ping_background->blue *= bkgd_scale;
2108 if (logging != MagickFalse)
2110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2111 " bkgd_scale=%d.",bkgd_scale);
2113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2114 " ping_background=(%d,%d,%d).",ping_background->red,
2115 ping_background->green,ping_background->blue);
2118 image->background_color.red=
2119 ScaleShortToQuantum(ping_background->red);
2121 image->background_color.green=
2122 ScaleShortToQuantum(ping_background->green);
2124 image->background_color.blue=
2125 ScaleShortToQuantum(ping_background->blue);
2127 image->background_color.opacity=OpaqueOpacity;
2129 if (logging != MagickFalse)
2130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2131 " image->background_color=(%.20g,%.20g,%.20g).",
2132 (double) image->background_color.red,
2133 (double) image->background_color.green,
2134 (double) image->background_color.blue);
2139 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2142 Image has a tRNS chunk.
2150 if (logging != MagickFalse)
2151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2152 " Reading PNG tRNS chunk.");
2154 max_sample = (int) ((one << ping_bit_depth) - 1);
2156 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2157 (int)ping_trans_color->gray > max_sample) ||
2158 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2159 ((int)ping_trans_color->red > max_sample ||
2160 (int)ping_trans_color->green > max_sample ||
2161 (int)ping_trans_color->blue > max_sample)))
2163 if (logging != MagickFalse)
2164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2165 " Ignoring PNG tRNS chunk with out-of-range sample.");
2166 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2167 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2168 image->matte=MagickFalse;
2175 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2177 /* Scale transparent_color to short */
2178 transparent_color.red= scale_to_short*ping_trans_color->red;
2179 transparent_color.green= scale_to_short*ping_trans_color->green;
2180 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2181 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2183 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2185 if (logging != MagickFalse)
2187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2188 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2191 " scaled graylevel is %d.",transparent_color.opacity);
2193 transparent_color.red=transparent_color.opacity;
2194 transparent_color.green=transparent_color.opacity;
2195 transparent_color.blue=transparent_color.opacity;
2199 #if defined(PNG_READ_sBIT_SUPPORTED)
2200 if (mng_info->have_global_sbit)
2202 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2203 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2206 num_passes=png_set_interlace_handling(ping);
2208 png_read_update_info(ping,ping_info);
2210 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2213 Initialize image structure.
2215 mng_info->image_box.left=0;
2216 mng_info->image_box.right=(ssize_t) ping_width;
2217 mng_info->image_box.top=0;
2218 mng_info->image_box.bottom=(ssize_t) ping_height;
2219 if (mng_info->mng_type == 0)
2221 mng_info->mng_width=ping_width;
2222 mng_info->mng_height=ping_height;
2223 mng_info->frame=mng_info->image_box;
2224 mng_info->clip=mng_info->image_box;
2229 image->page.y=mng_info->y_off[mng_info->object_id];
2232 image->compression=ZipCompression;
2233 image->columns=ping_width;
2234 image->rows=ping_height;
2235 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2236 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2241 image->storage_class=PseudoClass;
2243 image->colors=one << ping_bit_depth;
2244 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2245 if (image->colors > 256)
2248 if (image->colors > 65536L)
2249 image->colors=65536L;
2251 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2259 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2260 image->colors=(size_t) number_colors;
2262 if (logging != MagickFalse)
2263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2268 if (image->storage_class == PseudoClass)
2271 Initialize image colormap.
2273 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2274 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2276 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2284 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2286 for (i=0; i < (ssize_t) number_colors; i++)
2288 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2289 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2290 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2293 for ( ; i < (ssize_t) image->colors; i++)
2295 image->colormap[i].red=0;
2296 image->colormap[i].green=0;
2297 image->colormap[i].blue=0;
2306 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2311 for (i=0; i < (ssize_t) image->colors; i++)
2313 image->colormap[i].red=(Quantum) (i*scale);
2314 image->colormap[i].green=(Quantum) (i*scale);
2315 image->colormap[i].blue=(Quantum) (i*scale);
2320 /* Set some properties for reporting by "identify" */
2325 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2326 ping_interlace_method in value */
2328 (void) FormatMagickString(msg,MaxTextExtent,
2329 "%d, %d",(int) ping_width, (int) ping_height);
2330 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2332 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2333 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2335 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2336 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2338 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2339 (int) ping_interlace_method);
2340 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2344 Read image scanlines.
2346 if (image->delay != 0)
2347 mng_info->scenes_found++;
2349 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2350 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2351 (image_info->first_scene+image_info->number_scenes))))
2353 if (logging != MagickFalse)
2354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " Skipping PNG image data for scene %.20g",(double)
2356 mng_info->scenes_found-1);
2357 png_destroy_read_struct(&ping,&ping_info,&end_info);
2358 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2359 UnlockSemaphoreInfo(ping_semaphore);
2361 if (logging != MagickFalse)
2362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2363 " exit ReadOnePNGImage().");
2368 if (logging != MagickFalse)
2369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370 " Reading PNG IDAT chunk(s)");
2373 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2374 ping_rowbytes*sizeof(*ping_pixels));
2377 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2378 sizeof(*ping_pixels));
2380 if (ping_pixels == (unsigned char *) NULL)
2381 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2383 if (logging != MagickFalse)
2384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2385 " Converting PNG pixels to pixel packets");
2387 Convert PNG pixels to pixel packets.
2389 if (setjmp(png_jmpbuf(ping)))
2392 PNG image is corrupt.
2394 png_destroy_read_struct(&ping,&ping_info,&end_info);
2395 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2396 UnlockSemaphoreInfo(ping_semaphore);
2398 if (quantum_info != (QuantumInfo *) NULL)
2399 quantum_info = DestroyQuantumInfo(quantum_info);
2401 if (ping_pixels != (unsigned char *) NULL)
2402 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2404 if (logging != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " exit ReadOnePNGImage() with error.");
2408 if (image != (Image *) NULL)
2410 InheritException(exception,&image->exception);
2414 return(GetFirstImageInList(image));
2417 quantum_info=AcquireQuantumInfo(image_info,image);
2419 if (quantum_info == (QuantumInfo *) NULL)
2420 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2425 found_transparent_pixel;
2427 found_transparent_pixel=MagickFalse;
2429 if (image->storage_class == DirectClass)
2431 for (pass=0; pass < num_passes; pass++)
2434 Convert image to DirectClass pixel packets.
2436 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2440 depth=(ssize_t) ping_bit_depth;
2442 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2443 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2444 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2445 MagickTrue : MagickFalse;
2447 for (y=0; y < (ssize_t) image->rows; y++)
2450 row_offset=ping_rowbytes*y;
2455 png_read_row(ping,ping_pixels+row_offset,NULL);
2456 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2458 if (q == (PixelPacket *) NULL)
2461 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2462 /* code deleted from version 6.6.6-8 */
2463 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2465 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2466 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2467 GrayQuantum,ping_pixels+row_offset,exception);
2469 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2470 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2471 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2473 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2474 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2475 RGBAQuantum,ping_pixels+row_offset,exception);
2477 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2478 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2479 IndexQuantum,ping_pixels+row_offset,exception);
2481 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2482 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2483 RGBQuantum,ping_pixels+row_offset,exception);
2485 if (found_transparent_pixel == MagickFalse)
2487 /* Is there a transparent pixel in the row? */
2488 if (y== 0 && logging != MagickFalse)
2489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2490 " Looking for cheap transparent pixel");
2492 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2494 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2495 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2496 (q->opacity != OpaqueOpacity))
2498 if (logging != MagickFalse)
2499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2502 found_transparent_pixel = MagickTrue;
2505 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2506 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2507 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2508 ScaleQuantumToShort(q->green) == transparent_color.green &&
2509 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2511 if (logging != MagickFalse)
2512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2514 found_transparent_pixel = MagickTrue;
2521 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2523 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2526 if (status == MagickFalse)
2529 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2533 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2535 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2536 if (status == MagickFalse)
2542 else /* image->storage_class != DirectClass */
2544 for (pass=0; pass < num_passes; pass++)
2553 Convert grayscale image to PseudoClass pixel packets.
2555 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2556 MagickTrue : MagickFalse;
2558 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2559 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2561 if (quantum_scanline == (Quantum *) NULL)
2562 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2564 for (y=0; y < (ssize_t) image->rows; y++)
2567 row_offset=ping_rowbytes*y;
2572 png_read_row(ping,ping_pixels+row_offset,NULL);
2573 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2575 if (q == (PixelPacket *) NULL)
2578 indexes=GetAuthenticIndexQueue(image);
2579 p=ping_pixels+row_offset;
2582 switch (ping_bit_depth)
2589 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2591 for (bit=7; bit >= 0; bit--)
2592 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2596 if ((image->columns % 8) != 0)
2598 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2599 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2607 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2609 *r++=(*p >> 6) & 0x03;
2610 *r++=(*p >> 4) & 0x03;
2611 *r++=(*p >> 2) & 0x03;
2615 if ((image->columns % 4) != 0)
2617 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2618 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2626 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2628 *r++=(*p >> 4) & 0x0f;
2632 if ((image->columns % 2) != 0)
2633 *r++=(*p++ >> 4) & 0x0f;
2640 if (ping_color_type == 4)
2641 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2644 /* In image.h, OpaqueOpacity is 0
2645 * TransparentOpacity is QuantumRange
2646 * In a PNG datastream, Opaque is QuantumRange
2647 * and Transparent is 0.
2649 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2650 if (q->opacity != OpaqueOpacity)
2651 found_transparent_pixel = MagickTrue;
2656 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2664 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2666 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2670 if (image->colors > 256)
2678 *r=(Quantum) quantum;
2681 if (ping_color_type == 4)
2683 quantum=((*p++) << 8);
2685 q->opacity=(Quantum) (QuantumRange-quantum);
2686 if (q->opacity != OpaqueOpacity)
2687 found_transparent_pixel = MagickTrue;
2691 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2695 if (image->colors > 256)
2706 if (ping_color_type == 4)
2708 q->opacity=(*p << 8) | *(p+1);
2710 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2711 if (q->opacity != OpaqueOpacity)
2712 found_transparent_pixel = MagickTrue;
2717 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2719 p++; /* strip low byte */
2721 if (ping_color_type == 4)
2723 q->opacity=(Quantum) (QuantumRange-(*p++));
2724 if (q->opacity != OpaqueOpacity)
2725 found_transparent_pixel = MagickTrue;
2740 Transfer image scanline.
2744 for (x=0; x < (ssize_t) image->columns; x++)
2745 indexes[x]=(IndexPacket) (*r++);
2747 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2750 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2752 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2755 if (status == MagickFalse)
2760 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2762 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2764 if (status == MagickFalse)
2768 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2771 image->matte=found_transparent_pixel;
2773 if (logging != MagickFalse)
2775 if (found_transparent_pixel != MagickFalse)
2776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2777 " Found transparent pixel");
2780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2781 " No transparent pixel was found");
2783 ping_color_type&=0x03;
2788 if (quantum_info != (QuantumInfo *) NULL)
2789 quantum_info=DestroyQuantumInfo(quantum_info);
2791 if (image->storage_class == PseudoClass)
2797 image->matte=MagickFalse;
2798 (void) SyncImage(image);
2802 png_read_end(ping,end_info);
2804 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2805 (ssize_t) image_info->first_scene && image->delay != 0)
2807 png_destroy_read_struct(&ping,&ping_info,&end_info);
2808 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2810 (void) SetImageBackgroundColor(image);
2811 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2812 UnlockSemaphoreInfo(ping_semaphore);
2814 if (logging != MagickFalse)
2815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2816 " exit ReadOnePNGImage() early.");
2820 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2826 Image has a transparent background.
2828 storage_class=image->storage_class;
2829 image->matte=MagickTrue;
2831 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2833 if (storage_class == PseudoClass)
2835 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2837 for (x=0; x < ping_num_trans; x++)
2839 image->colormap[x].opacity =
2840 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2844 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2846 for (x=0; x < (int) image->colors; x++)
2848 if (ScaleQuantumToShort(image->colormap[x].red) ==
2849 transparent_color.opacity)
2851 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2855 (void) SyncImage(image);
2858 #if 1 /* Should have already been done above, but glennrp problem P10
2863 for (y=0; y < (ssize_t) image->rows; y++)
2865 image->storage_class=storage_class;
2866 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2868 if (q == (PixelPacket *) NULL)
2871 indexes=GetAuthenticIndexQueue(image);
2873 /* Caution: on a Q8 build, this does not distinguish between
2874 * 16-bit colors that differ only in the low byte
2876 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2878 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2879 ScaleQuantumToShort(q->green) == transparent_color.green &&
2880 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2882 q->opacity=(Quantum) TransparentOpacity;
2885 #if 0 /* I have not found a case where this is needed. */
2888 q->opacity=(Quantum) OpaqueOpacity;
2895 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2901 image->storage_class=DirectClass;
2904 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2905 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2906 image->colorspace=GRAYColorspace;
2908 for (j = 0; j < 2; j++)
2911 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2912 MagickTrue : MagickFalse;
2914 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2915 MagickTrue : MagickFalse;
2917 if (status != MagickFalse)
2918 for (i=0; i < (ssize_t) num_text; i++)
2920 /* Check for a profile */
2922 if (logging != MagickFalse)
2923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2924 " Reading PNG text chunk");
2926 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2928 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2937 length=text[i].text_length;
2938 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2940 if (value == (char *) NULL)
2942 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2943 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2948 (void) ConcatenateMagickString(value,text[i].text,length+2);
2950 /* Don't save "density" or "units" property if we have a pHYs
2953 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2954 (LocaleCompare(text[i].key,"density") != 0 &&
2955 LocaleCompare(text[i].key,"units") != 0))
2956 (void) SetImageProperty(image,text[i].key,value);
2958 if (logging != MagickFalse)
2960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2961 " length: %lu",(unsigned long) length);
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " Keyword: %s",text[i].key);
2966 value=DestroyString(value);
2969 num_text_total += num_text;
2972 #ifdef MNG_OBJECT_BUFFERS
2974 Store the object if necessary.
2976 if (object_id && !mng_info->frozen[object_id])
2978 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2981 create a new object buffer.
2983 mng_info->ob[object_id]=(MngBuffer *)
2984 AcquireMagickMemory(sizeof(MngBuffer));
2986 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2988 mng_info->ob[object_id]->image=(Image *) NULL;
2989 mng_info->ob[object_id]->reference_count=1;
2993 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2994 mng_info->ob[object_id]->frozen)
2996 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2997 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2998 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3001 if (mng_info->ob[object_id]->frozen)
3002 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3003 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3004 "`%s'",image->filename);
3010 if (mng_info->ob[object_id]->image != (Image *) NULL)
3011 mng_info->ob[object_id]->image=DestroyImage
3012 (mng_info->ob[object_id]->image);
3014 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3017 if (mng_info->ob[object_id]->image != (Image *) NULL)
3018 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3021 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3022 ResourceLimitError,"Cloning image for object buffer failed",
3023 "`%s'",image->filename);
3025 if (ping_width > 250000L || ping_height > 250000L)
3026 png_error(ping,"PNG Image dimensions are too large.");
3028 mng_info->ob[object_id]->width=ping_width;
3029 mng_info->ob[object_id]->height=ping_height;
3030 mng_info->ob[object_id]->color_type=ping_color_type;
3031 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3032 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3033 mng_info->ob[object_id]->compression_method=
3034 ping_compression_method;
3035 mng_info->ob[object_id]->filter_method=ping_filter_method;
3037 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3046 Copy the PLTE to the object buffer.
3048 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3049 mng_info->ob[object_id]->plte_length=number_colors;
3051 for (i=0; i < number_colors; i++)
3053 mng_info->ob[object_id]->plte[i]=plte[i];
3058 mng_info->ob[object_id]->plte_length=0;
3063 /* Set image->matte to MagickTrue if the input colortype supports
3064 * alpha or if a valid tRNS chunk is present, no matter whether there
3065 * is actual transparency present.
3067 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3068 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3069 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3070 MagickTrue : MagickFalse;
3072 /* Set more properties for identify to retrieve */
3077 if (num_text_total != 0)
3079 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3080 (void) FormatMagickString(msg,MaxTextExtent,
3081 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3082 (void) SetImageProperty(image,"PNG:text ",msg);
3085 if (num_raw_profiles != 0)
3087 (void) FormatMagickString(msg,MaxTextExtent,
3088 "%d were found", num_raw_profiles);
3089 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3092 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3094 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3095 "chunk was found (see Chromaticity, above)");
3096 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3099 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3101 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3102 "chunk was found (see Background color, above)");
3103 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3106 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3109 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3110 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3112 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3113 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3115 #if defined(PNG_sRGB_SUPPORTED)
3116 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3118 (void) FormatMagickString(msg,MaxTextExtent,
3119 "intent=%d (See Rendering intent)",
3121 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3125 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3127 (void) FormatMagickString(msg,MaxTextExtent,
3128 "gamma=%.8g (See Gamma, above)",
3130 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3133 #if defined(PNG_pHYs_SUPPORTED)
3134 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3136 (void) FormatMagickString(msg,MaxTextExtent,
3137 "x_res=%.10g, y_res=%.10g, units=%d",
3138 (double) x_resolution,(double) y_resolution, unit_type);
3139 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3143 #if defined(PNG_oFFs_SUPPORTED)
3144 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3146 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3147 (double) image->page.x,(double) image->page.y);
3148 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3152 if ((image->page.width != 0 && image->page.width != image->columns) ||
3153 (image->page.height != 0 && image->page.height != image->rows))
3155 (void) FormatMagickString(msg,MaxTextExtent,
3156 "width=%.20g, height=%.20g",
3157 (double) image->page.width,(double) image->page.height);
3158 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3163 Relinquish resources.
3165 png_destroy_read_struct(&ping,&ping_info,&end_info);
3167 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3168 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3169 UnlockSemaphoreInfo(ping_semaphore);
3172 if (logging != MagickFalse)
3173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3174 " exit ReadOnePNGImage()");
3178 /* end of reading one PNG image */
3181 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3196 magic_number[MaxTextExtent];
3204 assert(image_info != (const ImageInfo *) NULL);
3205 assert(image_info->signature == MagickSignature);
3207 if (image_info->debug != MagickFalse)
3208 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3209 image_info->filename);
3211 assert(exception != (ExceptionInfo *) NULL);
3212 assert(exception->signature == MagickSignature);
3213 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3214 image=AcquireImage(image_info);
3215 mng_info=(MngInfo *) NULL;
3216 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3218 if (status == MagickFalse)
3219 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3222 Verify PNG signature.
3224 count=ReadBlob(image,8,(unsigned char *) magic_number);
3226 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3227 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3230 Allocate a MngInfo structure.
3232 have_mng_structure=MagickFalse;
3233 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3235 if (mng_info == (MngInfo *) NULL)
3236 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3239 Initialize members of the MngInfo structure.
3241 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3242 mng_info->image=image;
3243 have_mng_structure=MagickTrue;
3246 image=ReadOnePNGImage(mng_info,image_info,exception);
3247 MngInfoFreeStruct(mng_info,&have_mng_structure);
3249 if (image == (Image *) NULL)
3251 if (previous != (Image *) NULL)
3253 if (previous->signature != MagickSignature)
3254 ThrowReaderException(CorruptImageError,"CorruptImage");
3256 (void) CloseBlob(previous);
3257 (void) DestroyImageList(previous);
3260 if (logging != MagickFalse)
3261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262 "exit ReadPNGImage() with error");
3264 return((Image *) NULL);
3267 (void) CloseBlob(image);
3269 if ((image->columns == 0) || (image->rows == 0))
3271 if (logging != MagickFalse)
3272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3273 "exit ReadPNGImage() with error.");
3275 ThrowReaderException(CorruptImageError,"CorruptImage");
3278 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3280 (void) SetImageType(image,PaletteType);
3282 if (image->matte != MagickFalse)
3284 /* To do: Reduce to binary transparency */
3288 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3290 (void) SetImageType(image,TrueColorType);
3291 image->matte=MagickFalse;
3294 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3295 (void) SetImageType(image,TrueColorMatteType);
3297 if (logging != MagickFalse)
3298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3299 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3300 (double) image->page.width,(double) image->page.height,
3301 (double) image->page.x,(double) image->page.y);
3303 if (logging != MagickFalse)
3304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3311 #if defined(JNG_SUPPORTED)
3313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3317 % R e a d O n e J N G I m a g e %
3321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3323 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3324 % (minus the 8-byte signature) and returns it. It allocates the memory
3325 % necessary for the new Image structure and returns a pointer to the new
3328 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3330 % The format of the ReadOneJNGImage method is:
3332 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3333 % ExceptionInfo *exception)
3335 % A description of each parameter follows:
3337 % o mng_info: Specifies a pointer to a MngInfo structure.
3339 % o image_info: the image info.
3341 % o exception: return any errors or warnings in this structure.
3344 static Image *ReadOneJNGImage(MngInfo *mng_info,
3345 const ImageInfo *image_info, ExceptionInfo *exception)
3372 jng_image_sample_depth,
3373 jng_image_compression_method,
3374 jng_image_interlace_method,
3375 jng_alpha_sample_depth,
3376 jng_alpha_compression_method,
3377 jng_alpha_filter_method,
3378 jng_alpha_interlace_method;
3380 register const PixelPacket
3387 register PixelPacket
3390 register unsigned char
3401 jng_alpha_compression_method=0;
3402 jng_alpha_sample_depth=8;
3406 alpha_image=(Image *) NULL;
3407 color_image=(Image *) NULL;
3408 alpha_image_info=(ImageInfo *) NULL;
3409 color_image_info=(ImageInfo *) NULL;
3411 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3412 " Enter ReadOneJNGImage()");
3414 image=mng_info->image;
3416 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3419 Allocate next image structure.
3421 if (logging != MagickFalse)
3422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3423 " AcquireNextImage()");
3425 AcquireNextImage(image_info,image);
3427 if (GetNextImageInList(image) == (Image *) NULL)
3428 return((Image *) NULL);
3430 image=SyncNextImageInList(image);
3432 mng_info->image=image;
3435 Signature bytes have already been read.
3438 read_JSEP=MagickFalse;
3439 reading_idat=MagickFalse;
3440 skip_to_iend=MagickFalse;
3444 type[MaxTextExtent];
3453 Read a new JNG chunk.
3455 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3456 2*GetBlobSize(image));
3458 if (status == MagickFalse)
3462 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3463 length=ReadBlobMSBLong(image);
3464 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3466 if (logging != MagickFalse)
3467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3468 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3469 type[0],type[1],type[2],type[3],(double) length);
3471 if (length > PNG_UINT_31_MAX || count == 0)
3472 ThrowReaderException(CorruptImageError,"CorruptImage");
3475 chunk=(unsigned char *) NULL;
3479 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3481 if (chunk == (unsigned char *) NULL)
3482 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3484 for (i=0; i < (ssize_t) length; i++)
3485 chunk[i]=(unsigned char) ReadBlobByte(image);
3490 (void) ReadBlobMSBLong(image); /* read crc word */
3495 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3500 if (memcmp(type,mng_JHDR,4) == 0)
3504 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3505 (p[2] << 8) | p[3]);
3506 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3507 (p[6] << 8) | p[7]);
3508 jng_color_type=p[8];
3509 jng_image_sample_depth=p[9];
3510 jng_image_compression_method=p[10];
3511 jng_image_interlace_method=p[11];
3513 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3516 jng_alpha_sample_depth=p[12];
3517 jng_alpha_compression_method=p[13];
3518 jng_alpha_filter_method=p[14];
3519 jng_alpha_interlace_method=p[15];
3521 if (logging != MagickFalse)
3523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " jng_width: %16lu",(unsigned long) jng_width);
3526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527 " jng_width: %16lu",(unsigned long) jng_height);
3529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3530 " jng_color_type: %16d",jng_color_type);
3532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3533 " jng_image_sample_depth: %3d",
3534 jng_image_sample_depth);
3536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3537 " jng_image_compression_method:%3d",
3538 jng_image_compression_method);
3540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3541 " jng_image_interlace_method: %3d",
3542 jng_image_interlace_method);
3544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3545 " jng_alpha_sample_depth: %3d",
3546 jng_alpha_sample_depth);
3548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3549 " jng_alpha_compression_method:%3d",
3550 jng_alpha_compression_method);
3552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3553 " jng_alpha_filter_method: %3d",
3554 jng_alpha_filter_method);
3556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3557 " jng_alpha_interlace_method: %3d",
3558 jng_alpha_interlace_method);
3563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3569 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3570 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3571 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3574 o create color_image
3575 o open color_blob, attached to color_image
3576 o if (color type has alpha)
3577 open alpha_blob, attached to alpha_image
3580 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3582 if (color_image_info == (ImageInfo *) NULL)
3583 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3585 GetImageInfo(color_image_info);
3586 color_image=AcquireImage(color_image_info);
3588 if (color_image == (Image *) NULL)
3589 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3591 if (logging != MagickFalse)
3592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3593 " Creating color_blob.");
3595 (void) AcquireUniqueFilename(color_image->filename);
3596 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3599 if (status == MagickFalse)
3600 return((Image *) NULL);
3602 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3604 alpha_image_info=(ImageInfo *)
3605 AcquireMagickMemory(sizeof(ImageInfo));
3607 if (alpha_image_info == (ImageInfo *) NULL)
3608 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3610 GetImageInfo(alpha_image_info);
3611 alpha_image=AcquireImage(alpha_image_info);
3613 if (alpha_image == (Image *) NULL)
3615 alpha_image=DestroyImage(alpha_image);
3616 ThrowReaderException(ResourceLimitError,
3617 "MemoryAllocationFailed");
3620 if (logging != MagickFalse)
3621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3622 " Creating alpha_blob.");
3624 (void) AcquireUniqueFilename(alpha_image->filename);
3625 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3628 if (status == MagickFalse)
3629 return((Image *) NULL);
3631 if (jng_alpha_compression_method == 0)
3636 if (logging != MagickFalse)
3637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3638 " Writing IHDR chunk to alpha_blob.");
3640 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3641 "\211PNG\r\n\032\n");
3643 (void) WriteBlobMSBULong(alpha_image,13L);
3644 PNGType(data,mng_IHDR);
3645 LogPNGChunk(logging,mng_IHDR,13L);
3646 PNGLong(data+4,jng_width);
3647 PNGLong(data+8,jng_height);
3648 data[12]=jng_alpha_sample_depth;
3649 data[13]=0; /* color_type gray */
3650 data[14]=0; /* compression method 0 */
3651 data[15]=0; /* filter_method 0 */
3652 data[16]=0; /* interlace_method 0 */
3653 (void) WriteBlob(alpha_image,17,data);
3654 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3657 reading_idat=MagickTrue;
3660 if (memcmp(type,mng_JDAT,4) == 0)
3662 /* Copy chunk to color_image->blob */
3664 if (logging != MagickFalse)
3665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3666 " Copying JDAT chunk data to color_blob.");
3668 (void) WriteBlob(color_image,length,chunk);
3671 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3676 if (memcmp(type,mng_IDAT,4) == 0)
3681 /* Copy IDAT header and chunk data to alpha_image->blob */
3683 if (image_info->ping == MagickFalse)
3685 if (logging != MagickFalse)
3686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3687 " Copying IDAT chunk data to alpha_blob.");
3689 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3690 PNGType(data,mng_IDAT);
3691 LogPNGChunk(logging,mng_IDAT,length);
3692 (void) WriteBlob(alpha_image,4,data);
3693 (void) WriteBlob(alpha_image,length,chunk);
3694 (void) WriteBlobMSBULong(alpha_image,
3695 crc32(crc32(0,data,4),chunk,(uInt) length));
3699 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3704 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3706 /* Copy chunk data to alpha_image->blob */
3708 if (image_info->ping == MagickFalse)
3710 if (logging != MagickFalse)
3711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3712 " Copying JDAA chunk data to alpha_blob.");
3714 (void) WriteBlob(alpha_image,length,chunk);
3718 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3723 if (memcmp(type,mng_JSEP,4) == 0)
3725 read_JSEP=MagickTrue;
3728 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3733 if (memcmp(type,mng_bKGD,4) == 0)
3737 image->background_color.red=ScaleCharToQuantum(p[1]);
3738 image->background_color.green=image->background_color.red;
3739 image->background_color.blue=image->background_color.red;
3744 image->background_color.red=ScaleCharToQuantum(p[1]);
3745 image->background_color.green=ScaleCharToQuantum(p[3]);
3746 image->background_color.blue=ScaleCharToQuantum(p[5]);
3749 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3753 if (memcmp(type,mng_gAMA,4) == 0)
3756 image->gamma=((float) mng_get_long(p))*0.00001;
3758 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3762 if (memcmp(type,mng_cHRM,4) == 0)
3766 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3767 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3768 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3769 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3770 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3771 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3772 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3773 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3776 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3780 if (memcmp(type,mng_sRGB,4) == 0)
3784 image->rendering_intent=
3785 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3786 image->gamma=0.45455f;
3787 image->chromaticity.red_primary.x=0.6400f;
3788 image->chromaticity.red_primary.y=0.3300f;
3789 image->chromaticity.green_primary.x=0.3000f;
3790 image->chromaticity.green_primary.y=0.6000f;
3791 image->chromaticity.blue_primary.x=0.1500f;
3792 image->chromaticity.blue_primary.y=0.0600f;
3793 image->chromaticity.white_point.x=0.3127f;
3794 image->chromaticity.white_point.y=0.3290f;
3797 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3801 if (memcmp(type,mng_oFFs,4) == 0)
3805 image->page.x=(ssize_t) mng_get_long(p);
3806 image->page.y=(ssize_t) mng_get_long(&p[4]);
3808 if ((int) p[8] != 0)
3810 image->page.x/=10000;
3811 image->page.y/=10000;
3816 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3821 if (memcmp(type,mng_pHYs,4) == 0)
3825 image->x_resolution=(double) mng_get_long(p);
3826 image->y_resolution=(double) mng_get_long(&p[4]);
3827 if ((int) p[8] == PNG_RESOLUTION_METER)
3829 image->units=PixelsPerCentimeterResolution;
3830 image->x_resolution=image->x_resolution/100.0f;
3831 image->y_resolution=image->y_resolution/100.0f;
3835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3840 if (memcmp(type,mng_iCCP,4) == 0)
3844 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3851 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3853 if (memcmp(type,mng_IEND,4))
3863 Finish up reading image data:
3865 o read main image from color_blob.
3869 o if (color_type has alpha)
3870 if alpha_encoding is PNG
3871 read secondary image from alpha_blob via ReadPNG
3872 if alpha_encoding is JPEG
3873 read secondary image from alpha_blob via ReadJPEG
3877 o copy intensity of secondary image into
3878 opacity samples of main image.
3880 o destroy the secondary image.
3883 (void) CloseBlob(color_image);
3885 if (logging != MagickFalse)
3886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3887 " Reading jng_image from color_blob.");
3889 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3890 color_image->filename);
3892 color_image_info->ping=MagickFalse; /* To do: avoid this */
3893 jng_image=ReadImage(color_image_info,exception);
3895 if (jng_image == (Image *) NULL)
3896 return((Image *) NULL);
3898 (void) RelinquishUniqueFileResource(color_image->filename);
3899 color_image=DestroyImage(color_image);
3900 color_image_info=DestroyImageInfo(color_image_info);
3902 if (jng_image == (Image *) NULL)
3903 return((Image *) NULL);
3905 if (logging != MagickFalse)
3906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3907 " Copying jng_image pixels to main image.");
3909 image->rows=jng_height;
3910 image->columns=jng_width;
3911 length=image->columns*sizeof(PixelPacket);
3913 for (y=0; y < (ssize_t) image->rows; y++)
3915 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3916 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3917 (void) CopyMagickMemory(q,s,length);
3919 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3923 jng_image=DestroyImage(jng_image);
3925 if (image_info->ping == MagickFalse)
3927 if (jng_color_type >= 12)
3929 if (jng_alpha_compression_method == 0)
3933 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3934 PNGType(data,mng_IEND);
3935 LogPNGChunk(logging,mng_IEND,0L);
3936 (void) WriteBlob(alpha_image,4,data);
3937 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3940 (void) CloseBlob(alpha_image);
3942 if (logging != MagickFalse)
3943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3944 " Reading opacity from alpha_blob.");
3946 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3947 "%s",alpha_image->filename);
3949 jng_image=ReadImage(alpha_image_info,exception);
3951 if (jng_image != (Image *) NULL)
3952 for (y=0; y < (ssize_t) image->rows; y++)
3954 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3956 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3958 if (image->matte != MagickFalse)
3959 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3960 q->opacity=(Quantum) QuantumRange-s->red;
3963 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3965 q->opacity=(Quantum) QuantumRange-s->red;
3966 if (q->opacity != OpaqueOpacity)
3967 image->matte=MagickTrue;
3970 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3973 (void) RelinquishUniqueFileResource(alpha_image->filename);
3974 alpha_image=DestroyImage(alpha_image);
3975 alpha_image_info=DestroyImageInfo(alpha_image_info);
3976 if (jng_image != (Image *) NULL)
3977 jng_image=DestroyImage(jng_image);
3981 /* Read the JNG image. */
3983 if (mng_info->mng_type == 0)
3985 mng_info->mng_width=jng_width;
3986 mng_info->mng_height=jng_height;
3989 if (image->page.width == 0 && image->page.height == 0)
3991 image->page.width=jng_width;
3992 image->page.height=jng_height;
3995 if (image->page.x == 0 && image->page.y == 0)
3997 image->page.x=mng_info->x_off[mng_info->object_id];
3998 image->page.y=mng_info->y_off[mng_info->object_id];
4003 image->page.y=mng_info->y_off[mng_info->object_id];
4006 mng_info->image_found++;
4007 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4008 2*GetBlobSize(image));
4010 if (logging != MagickFalse)
4011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4012 " exit ReadOneJNGImage()");
4018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4022 % R e a d J N G I m a g e %
4026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4028 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4029 % (including the 8-byte signature) and returns it. It allocates the memory
4030 % necessary for the new Image structure and returns a pointer to the new
4033 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4035 % The format of the ReadJNGImage method is:
4037 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4040 % A description of each parameter follows:
4042 % o image_info: the image info.
4044 % o exception: return any errors or warnings in this structure.
4048 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4063 magic_number[MaxTextExtent];
4071 assert(image_info != (const ImageInfo *) NULL);
4072 assert(image_info->signature == MagickSignature);
4073 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4074 assert(exception != (ExceptionInfo *) NULL);
4075 assert(exception->signature == MagickSignature);
4076 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4077 image=AcquireImage(image_info);
4078 mng_info=(MngInfo *) NULL;
4079 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4081 if (status == MagickFalse)
4082 return((Image *) NULL);
4084 if (LocaleCompare(image_info->magick,"JNG") != 0)
4085 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4087 /* Verify JNG signature. */
4089 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4091 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4092 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4094 /* Allocate a MngInfo structure. */
4096 have_mng_structure=MagickFalse;
4097 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4099 if (mng_info == (MngInfo *) NULL)
4100 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4102 /* Initialize members of the MngInfo structure. */
4104 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4105 have_mng_structure=MagickTrue;
4107 mng_info->image=image;
4109 image=ReadOneJNGImage(mng_info,image_info,exception);
4110 MngInfoFreeStruct(mng_info,&have_mng_structure);
4112 if (image == (Image *) NULL)
4114 if (IsImageObject(previous) != MagickFalse)
4116 (void) CloseBlob(previous);
4117 (void) DestroyImageList(previous);
4120 if (logging != MagickFalse)
4121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4122 "exit ReadJNGImage() with error");
4124 return((Image *) NULL);
4126 (void) CloseBlob(image);
4128 if (image->columns == 0 || image->rows == 0)
4130 if (logging != MagickFalse)
4131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4132 "exit ReadJNGImage() with error");
4134 ThrowReaderException(CorruptImageError,"CorruptImage");
4137 if (logging != MagickFalse)
4138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4144 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4147 page_geometry[MaxTextExtent];
4180 #if defined(MNG_INSERT_LAYERS)
4182 mng_background_color;
4185 register unsigned char
4200 #if defined(MNG_INSERT_LAYERS)
4205 volatile unsigned int
4206 #ifdef MNG_OBJECT_BUFFERS
4207 mng_background_object=0,
4209 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4212 default_frame_timeout,
4214 #if defined(MNG_INSERT_LAYERS)
4220 /* These delays are all measured in image ticks_per_second,
4221 * not in MNG ticks_per_second
4224 default_frame_delay,
4228 #if defined(MNG_INSERT_LAYERS)
4237 previous_fb.bottom=0;
4239 previous_fb.right=0;
4241 default_fb.bottom=0;
4245 /* Open image file. */
4247 assert(image_info != (const ImageInfo *) NULL);
4248 assert(image_info->signature == MagickSignature);
4249 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4250 assert(exception != (ExceptionInfo *) NULL);
4251 assert(exception->signature == MagickSignature);
4252 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4253 image=AcquireImage(image_info);
4254 mng_info=(MngInfo *) NULL;
4255 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4257 if (status == MagickFalse)
4258 return((Image *) NULL);
4260 first_mng_object=MagickFalse;
4262 have_mng_structure=MagickFalse;
4264 /* Allocate a MngInfo structure. */
4266 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4268 if (mng_info == (MngInfo *) NULL)
4269 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4271 /* Initialize members of the MngInfo structure. */
4273 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4274 mng_info->image=image;
4275 have_mng_structure=MagickTrue;
4277 if (LocaleCompare(image_info->magick,"MNG") == 0)
4280 magic_number[MaxTextExtent];
4282 /* Verify MNG signature. */
4283 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4284 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4285 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4287 /* Initialize some nonzero members of the MngInfo structure. */
4288 for (i=0; i < MNG_MAX_OBJECTS; i++)
4290 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4291 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4293 mng_info->exists[0]=MagickTrue;
4296 first_mng_object=MagickTrue;
4298 #if defined(MNG_INSERT_LAYERS)
4299 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4301 default_frame_delay=0;
4302 default_frame_timeout=0;
4305 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4307 skip_to_iend=MagickFalse;
4308 term_chunk_found=MagickFalse;
4309 mng_info->framing_mode=1;
4310 #if defined(MNG_INSERT_LAYERS)
4311 mandatory_back=MagickFalse;
4313 #if defined(MNG_INSERT_LAYERS)
4314 mng_background_color=image->background_color;
4316 default_fb=mng_info->frame;
4317 previous_fb=mng_info->frame;
4321 type[MaxTextExtent];
4323 if (LocaleCompare(image_info->magick,"MNG") == 0)
4332 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4333 length=ReadBlobMSBLong(image);
4334 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4336 if (logging != MagickFalse)
4337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4338 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4339 type[0],type[1],type[2],type[3],(double) length);
4341 if (length > PNG_UINT_31_MAX)
4345 ThrowReaderException(CorruptImageError,"CorruptImage");
4348 chunk=(unsigned char *) NULL;
4352 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4354 if (chunk == (unsigned char *) NULL)
4355 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4357 for (i=0; i < (ssize_t) length; i++)
4358 chunk[i]=(unsigned char) ReadBlobByte(image);
4363 (void) ReadBlobMSBLong(image); /* read crc word */
4365 #if !defined(JNG_SUPPORTED)
4366 if (memcmp(type,mng_JHDR,4) == 0)
4368 skip_to_iend=MagickTrue;
4370 if (mng_info->jhdr_warning == 0)
4371 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4372 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4374 mng_info->jhdr_warning++;
4377 if (memcmp(type,mng_DHDR,4) == 0)
4379 skip_to_iend=MagickTrue;
4381 if (mng_info->dhdr_warning == 0)
4382 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4383 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4385 mng_info->dhdr_warning++;
4387 if (memcmp(type,mng_MEND,4) == 0)
4392 if (memcmp(type,mng_IEND,4) == 0)
4393 skip_to_iend=MagickFalse;
4396 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4398 if (logging != MagickFalse)
4399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4405 if (memcmp(type,mng_MHDR,4) == 0)
4407 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4408 (p[2] << 8) | p[3]);
4410 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4411 (p[6] << 8) | p[7]);
4413 if (logging != MagickFalse)
4415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4416 " MNG width: %.20g",(double) mng_info->mng_width);
4417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4418 " MNG height: %.20g",(double) mng_info->mng_height);
4422 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4424 if (mng_info->ticks_per_second == 0)
4425 default_frame_delay=0;
4428 default_frame_delay=1UL*image->ticks_per_second/
4429 mng_info->ticks_per_second;
4431 frame_delay=default_frame_delay;
4437 simplicity=(size_t) mng_get_long(p);
4440 mng_type=1; /* Full MNG */
4442 if ((simplicity != 0) && ((simplicity | 11) == 11))
4443 mng_type=2; /* LC */
4445 if ((simplicity != 0) && ((simplicity | 9) == 9))
4446 mng_type=3; /* VLC */
4448 #if defined(MNG_INSERT_LAYERS)
4450 insert_layers=MagickTrue;
4452 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4454 /* Allocate next image structure. */
4455 AcquireNextImage(image_info,image);
4457 if (GetNextImageInList(image) == (Image *) NULL)
4458 return((Image *) NULL);
4460 image=SyncNextImageInList(image);
4461 mng_info->image=image;
4464 if ((mng_info->mng_width > 65535L) ||
4465 (mng_info->mng_height > 65535L))
4466 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4468 (void) FormatMagickString(page_geometry,MaxTextExtent,
4469 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4470 mng_info->mng_height);
4472 mng_info->frame.left=0;
4473 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4474 mng_info->frame.top=0;
4475 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4476 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4478 for (i=0; i < MNG_MAX_OBJECTS; i++)
4479 mng_info->object_clip[i]=mng_info->frame;
4481 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4485 if (memcmp(type,mng_TERM,4) == 0)
4496 final_delay=(png_uint_32) mng_get_long(&p[2]);
4497 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4499 if (mng_iterations == PNG_UINT_31_MAX)
4502 image->iterations=mng_iterations;
4503 term_chunk_found=MagickTrue;
4506 if (logging != MagickFalse)
4508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4509 " repeat=%d",repeat);
4511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4512 " final_delay=%.20g",(double) final_delay);
4514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515 " image->iterations=%.20g",(double) image->iterations);
4518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4521 if (memcmp(type,mng_DEFI,4) == 0)
4524 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4525 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4528 object_id=(p[0] << 8) | p[1];
4530 if (mng_type == 2 && object_id != 0)
4531 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4532 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4535 if (object_id > MNG_MAX_OBJECTS)
4538 Instead ofsuing a warning we should allocate a larger
4539 MngInfo structure and continue.
4541 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4542 CoderError,"object id too large","`%s'",image->filename);
4543 object_id=MNG_MAX_OBJECTS;
4546 if (mng_info->exists[object_id])
4547 if (mng_info->frozen[object_id])
4549 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4550 (void) ThrowMagickException(&image->exception,
4551 GetMagickModule(),CoderError,
4552 "DEFI cannot redefine a frozen MNG object","`%s'",
4557 mng_info->exists[object_id]=MagickTrue;
4560 mng_info->invisible[object_id]=p[2];
4563 Extract object offset info.
4567 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4568 (p[5] << 16) | (p[6] << 8) | p[7]);
4570 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4571 (p[9] << 16) | (p[10] << 8) | p[11]);
4573 if (logging != MagickFalse)
4575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4576 " x_off[%d]: %.20g",object_id,(double)
4577 mng_info->x_off[object_id]);
4579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4580 " y_off[%d]: %.20g",object_id,(double)
4581 mng_info->y_off[object_id]);
4586 Extract object clipping info.
4589 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4592 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4595 if (memcmp(type,mng_bKGD,4) == 0)
4597 mng_info->have_global_bkgd=MagickFalse;
4601 mng_info->mng_global_bkgd.red=
4602 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4604 mng_info->mng_global_bkgd.green=
4605 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4607 mng_info->mng_global_bkgd.blue=
4608 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4610 mng_info->have_global_bkgd=MagickTrue;
4613 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4616 if (memcmp(type,mng_BACK,4) == 0)
4618 #if defined(MNG_INSERT_LAYERS)
4620 mandatory_back=p[6];
4625 if (mandatory_back && length > 5)
4627 mng_background_color.red=
4628 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4630 mng_background_color.green=
4631 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4633 mng_background_color.blue=
4634 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4636 mng_background_color.opacity=OpaqueOpacity;
4639 #ifdef MNG_OBJECT_BUFFERS
4641 mng_background_object=(p[7] << 8) | p[8];
4644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4648 if (memcmp(type,mng_PLTE,4) == 0)
4650 /* Read global PLTE. */
4652 if (length && (length < 769))
4654 if (mng_info->global_plte == (png_colorp) NULL)
4655 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4656 sizeof(*mng_info->global_plte));
4658 for (i=0; i < (ssize_t) (length/3); i++)
4660 mng_info->global_plte[i].red=p[3*i];
4661 mng_info->global_plte[i].green=p[3*i+1];
4662 mng_info->global_plte[i].blue=p[3*i+2];
4665 mng_info->global_plte_length=(unsigned int) (length/3);
4668 for ( ; i < 256; i++)
4670 mng_info->global_plte[i].red=i;
4671 mng_info->global_plte[i].green=i;
4672 mng_info->global_plte[i].blue=i;
4676 mng_info->global_plte_length=256;
4679 mng_info->global_plte_length=0;
4681 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4685 if (memcmp(type,mng_tRNS,4) == 0)
4687 /* read global tRNS */
4690 for (i=0; i < (ssize_t) length; i++)
4691 mng_info->global_trns[i]=p[i];
4694 for ( ; i < 256; i++)
4695 mng_info->global_trns[i]=255;
4697 mng_info->global_trns_length=(unsigned int) length;
4698 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701 if (memcmp(type,mng_gAMA,4) == 0)
4708 igamma=mng_get_long(p);
4709 mng_info->global_gamma=((float) igamma)*0.00001;
4710 mng_info->have_global_gama=MagickTrue;
4714 mng_info->have_global_gama=MagickFalse;
4716 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4720 if (memcmp(type,mng_cHRM,4) == 0)
4722 /* Read global cHRM */
4726 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4727 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4728 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4729 mng_info->global_chrm.red_primary.y=0.00001*
4730 mng_get_long(&p[12]);
4731 mng_info->global_chrm.green_primary.x=0.00001*
4732 mng_get_long(&p[16]);
4733 mng_info->global_chrm.green_primary.y=0.00001*
4734 mng_get_long(&p[20]);
4735 mng_info->global_chrm.blue_primary.x=0.00001*
4736 mng_get_long(&p[24]);
4737 mng_info->global_chrm.blue_primary.y=0.00001*
4738 mng_get_long(&p[28]);
4739 mng_info->have_global_chrm=MagickTrue;
4742 mng_info->have_global_chrm=MagickFalse;
4744 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4748 if (memcmp(type,mng_sRGB,4) == 0)
4755 mng_info->global_srgb_intent=
4756 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4757 mng_info->have_global_srgb=MagickTrue;
4760 mng_info->have_global_srgb=MagickFalse;
4762 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4766 if (memcmp(type,mng_iCCP,4) == 0)
4774 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4779 if (memcmp(type,mng_FRAM,4) == 0)
4782 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4783 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4786 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4787 image->delay=frame_delay;
4789 frame_delay=default_frame_delay;
4790 frame_timeout=default_frame_timeout;
4795 mng_info->framing_mode=p[0];
4797 if (logging != MagickFalse)
4798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4799 " Framing_mode=%d",mng_info->framing_mode);
4803 /* Note the delay and frame clipping boundaries. */
4805 p++; /* framing mode */
4807 while (*p && ((p-chunk) < (ssize_t) length))
4808 p++; /* frame name */
4810 p++; /* frame name terminator */
4812 if ((p-chunk) < (ssize_t) (length-4))
4819 change_delay=(*p++);
4820 change_timeout=(*p++);
4821 change_clipping=(*p++);
4822 p++; /* change_sync */
4826 frame_delay=1UL*image->ticks_per_second*
4829 if (mng_info->ticks_per_second != 0)
4830 frame_delay/=mng_info->ticks_per_second;
4833 frame_delay=PNG_UINT_31_MAX;
4835 if (change_delay == 2)
4836 default_frame_delay=frame_delay;
4840 if (logging != MagickFalse)
4841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4842 " Framing_delay=%.20g",(double) frame_delay);
4847 frame_timeout=1UL*image->ticks_per_second*
4850 if (mng_info->ticks_per_second != 0)
4851 frame_timeout/=mng_info->ticks_per_second;
4854 frame_timeout=PNG_UINT_31_MAX;
4856 if (change_delay == 2)
4857 default_frame_timeout=frame_timeout;
4861 if (logging != MagickFalse)
4862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4863 " Framing_timeout=%.20g",(double) frame_timeout);
4866 if (change_clipping)
4868 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4872 if (logging != MagickFalse)
4873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4874 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4875 (double) fb.left,(double) fb.right,(double) fb.top,
4876 (double) fb.bottom);
4878 if (change_clipping == 2)
4884 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4886 subframe_width=(size_t) (mng_info->clip.right
4887 -mng_info->clip.left);
4889 subframe_height=(size_t) (mng_info->clip.bottom
4890 -mng_info->clip.top);
4892 Insert a background layer behind the frame if framing_mode is 4.
4894 #if defined(MNG_INSERT_LAYERS)
4895 if (logging != MagickFalse)
4896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4897 " subframe_width=%.20g, subframe_height=%.20g",(double)
4898 subframe_width,(double) subframe_height);
4900 if (insert_layers && (mng_info->framing_mode == 4) &&
4901 (subframe_width) && (subframe_height))
4903 /* Allocate next image structure. */
4904 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4906 AcquireNextImage(image_info,image);
4908 if (GetNextImageInList(image) == (Image *) NULL)
4910 image=DestroyImageList(image);
4911 MngInfoFreeStruct(mng_info,&have_mng_structure);
4912 return((Image *) NULL);
4915 image=SyncNextImageInList(image);
4918 mng_info->image=image;
4920 if (term_chunk_found)
4922 image->start_loop=MagickTrue;
4923 image->iterations=mng_iterations;
4924 term_chunk_found=MagickFalse;
4928 image->start_loop=MagickFalse;
4930 image->columns=subframe_width;
4931 image->rows=subframe_height;
4932 image->page.width=subframe_width;
4933 image->page.height=subframe_height;
4934 image->page.x=mng_info->clip.left;
4935 image->page.y=mng_info->clip.top;
4936 image->background_color=mng_background_color;
4937 image->matte=MagickFalse;
4939 (void) SetImageBackgroundColor(image);
4941 if (logging != MagickFalse)
4942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4943 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4944 (double) mng_info->clip.left,(double) mng_info->clip.right,
4945 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 if (memcmp(type,mng_CLIP,4) == 0)
4960 first_object=(p[0] << 8) | p[1];
4961 last_object=(p[2] << 8) | p[3];
4963 for (i=(int) first_object; i <= (int) last_object; i++)
4965 if (mng_info->exists[i] && !mng_info->frozen[i])
4970 box=mng_info->object_clip[i];
4971 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4978 if (memcmp(type,mng_SAVE,4) == 0)
4980 for (i=1; i < MNG_MAX_OBJECTS; i++)
4981 if (mng_info->exists[i])
4983 mng_info->frozen[i]=MagickTrue;
4984 #ifdef MNG_OBJECT_BUFFERS
4985 if (mng_info->ob[i] != (MngBuffer *) NULL)
4986 mng_info->ob[i]->frozen=MagickTrue;
4991 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4996 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4998 /* Read DISC or SEEK. */
5000 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5002 for (i=1; i < MNG_MAX_OBJECTS; i++)
5003 MngInfoDiscardObject(mng_info,i);
5011 for (j=0; j < (ssize_t) length; j+=2)
5013 i=p[j] << 8 | p[j+1];
5014 MngInfoDiscardObject(mng_info,i);
5019 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5024 if (memcmp(type,mng_MOVE,4) == 0)
5032 first_object=(p[0] << 8) | p[1];
5033 last_object=(p[2] << 8) | p[3];
5034 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5036 if (mng_info->exists[i] && !mng_info->frozen[i])
5044 old_pair.a=mng_info->x_off[i];
5045 old_pair.b=mng_info->y_off[i];
5046 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5047 mng_info->x_off[i]=new_pair.a;
5048 mng_info->y_off[i]=new_pair.b;
5052 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5056 if (memcmp(type,mng_LOOP,4) == 0)
5058 ssize_t loop_iters=1;
5059 loop_level=chunk[0];
5060 mng_info->loop_active[loop_level]=1; /* mark loop active */
5062 /* Record starting point. */
5063 loop_iters=mng_get_long(&chunk[1]);
5065 if (logging != MagickFalse)
5066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5067 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5068 (double) loop_iters);
5070 if (loop_iters == 0)
5071 skipping_loop=loop_level;
5075 mng_info->loop_jump[loop_level]=TellBlob(image);
5076 mng_info->loop_count[loop_level]=loop_iters;
5079 mng_info->loop_iteration[loop_level]=0;
5080 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5084 if (memcmp(type,mng_ENDL,4) == 0)
5086 loop_level=chunk[0];
5088 if (skipping_loop > 0)
5090 if (skipping_loop == loop_level)
5093 Found end of zero-iteration loop.
5096 mng_info->loop_active[loop_level]=0;
5102 if (mng_info->loop_active[loop_level] == 1)
5104 mng_info->loop_count[loop_level]--;
5105 mng_info->loop_iteration[loop_level]++;
5107 if (logging != MagickFalse)
5108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5109 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5110 (double) loop_level,(double)
5111 mng_info->loop_count[loop_level]);
5113 if (mng_info->loop_count[loop_level] != 0)
5115 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5119 ThrowReaderException(CorruptImageError,
5120 "ImproperImageHeader");
5131 mng_info->loop_active[loop_level]=0;
5133 for (i=0; i < loop_level; i++)
5134 if (mng_info->loop_active[i] == 1)
5135 last_level=(short) i;
5136 loop_level=last_level;
5141 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5145 if (memcmp(type,mng_CLON,4) == 0)
5147 if (mng_info->clon_warning == 0)
5148 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5149 CoderError,"CLON is not implemented yet","`%s'",
5152 mng_info->clon_warning++;
5155 if (memcmp(type,mng_MAGN,4) == 0)
5170 magn_first=(p[0] << 8) | p[1];
5176 magn_last=(p[2] << 8) | p[3];
5179 magn_last=magn_first;
5180 #ifndef MNG_OBJECT_BUFFERS
5181 if (magn_first || magn_last)
5182 if (mng_info->magn_warning == 0)
5184 (void) ThrowMagickException(&image->exception,
5185 GetMagickModule(),CoderError,
5186 "MAGN is not implemented yet for nonzero objects",
5187 "`%s'",image->filename);
5189 mng_info->magn_warning++;
5199 magn_mx=(p[5] << 8) | p[6];
5208 magn_my=(p[7] << 8) | p[8];
5217 magn_ml=(p[9] << 8) | p[10];
5226 magn_mr=(p[11] << 8) | p[12];
5235 magn_mt=(p[13] << 8) | p[14];
5244 magn_mb=(p[15] << 8) | p[16];
5256 magn_methy=magn_methx;
5259 if (magn_methx > 5 || magn_methy > 5)
5260 if (mng_info->magn_warning == 0)
5262 (void) ThrowMagickException(&image->exception,
5263 GetMagickModule(),CoderError,
5264 "Unknown MAGN method in MNG datastream","`%s'",
5267 mng_info->magn_warning++;
5269 #ifdef MNG_OBJECT_BUFFERS
5270 /* Magnify existing objects in the range magn_first to magn_last */
5272 if (magn_first == 0 || magn_last == 0)
5274 /* Save the magnification factors for object 0 */
5275 mng_info->magn_mb=magn_mb;
5276 mng_info->magn_ml=magn_ml;
5277 mng_info->magn_mr=magn_mr;
5278 mng_info->magn_mt=magn_mt;
5279 mng_info->magn_mx=magn_mx;
5280 mng_info->magn_my=magn_my;
5281 mng_info->magn_methx=magn_methx;
5282 mng_info->magn_methy=magn_methy;
5286 if (memcmp(type,mng_PAST,4) == 0)
5288 if (mng_info->past_warning == 0)
5289 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5290 CoderError,"PAST is not implemented yet","`%s'",
5293 mng_info->past_warning++;
5296 if (memcmp(type,mng_SHOW,4) == 0)
5298 if (mng_info->show_warning == 0)
5299 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5300 CoderError,"SHOW is not implemented yet","`%s'",
5303 mng_info->show_warning++;
5306 if (memcmp(type,mng_sBIT,4) == 0)
5309 mng_info->have_global_sbit=MagickFalse;
5313 mng_info->global_sbit.gray=p[0];
5314 mng_info->global_sbit.red=p[0];
5315 mng_info->global_sbit.green=p[1];
5316 mng_info->global_sbit.blue=p[2];
5317 mng_info->global_sbit.alpha=p[3];
5318 mng_info->have_global_sbit=MagickTrue;
5321 if (memcmp(type,mng_pHYs,4) == 0)
5325 mng_info->global_x_pixels_per_unit=
5326 (size_t) mng_get_long(p);
5327 mng_info->global_y_pixels_per_unit=
5328 (size_t) mng_get_long(&p[4]);
5329 mng_info->global_phys_unit_type=p[8];
5330 mng_info->have_global_phys=MagickTrue;
5334 mng_info->have_global_phys=MagickFalse;
5336 if (memcmp(type,mng_pHYg,4) == 0)
5338 if (mng_info->phyg_warning == 0)
5339 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5340 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5342 mng_info->phyg_warning++;
5344 if (memcmp(type,mng_BASI,4) == 0)
5346 skip_to_iend=MagickTrue;
5348 if (mng_info->basi_warning == 0)
5349 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5350 CoderError,"BASI is not implemented yet","`%s'",
5353 mng_info->basi_warning++;
5354 #ifdef MNG_BASI_SUPPORTED
5355 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5356 (p[2] << 8) | p[3]);
5357 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5358 (p[6] << 8) | p[7]);
5359 basi_color_type=p[8];
5360 basi_compression_method=p[9];
5361 basi_filter_type=p[10];
5362 basi_interlace_method=p[11];
5364 basi_red=(p[12] << 8) & p[13];
5370 basi_green=(p[14] << 8) & p[15];
5376 basi_blue=(p[16] << 8) & p[17];
5382 basi_alpha=(p[18] << 8) & p[19];
5386 if (basi_sample_depth == 16)
5393 basi_viewable=p[20];
5399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5403 if (memcmp(type,mng_IHDR,4)
5404 #if defined(JNG_SUPPORTED)
5405 && memcmp(type,mng_JHDR,4)
5409 /* Not an IHDR or JHDR chunk */
5411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5416 if (logging != MagickFalse)
5417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5418 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5420 mng_info->exists[object_id]=MagickTrue;
5421 mng_info->viewable[object_id]=MagickTrue;
5423 if (mng_info->invisible[object_id])
5425 if (logging != MagickFalse)
5426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5427 " Skipping invisible object");
5429 skip_to_iend=MagickTrue;
5430 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433 #if defined(MNG_INSERT_LAYERS)
5435 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5437 image_width=(size_t) mng_get_long(p);
5438 image_height=(size_t) mng_get_long(&p[4]);
5440 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5443 Insert a transparent background layer behind the entire animation
5444 if it is not full screen.
5446 #if defined(MNG_INSERT_LAYERS)
5447 if (insert_layers && mng_type && first_mng_object)
5449 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5450 (image_width < mng_info->mng_width) ||
5451 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5452 (image_height < mng_info->mng_height) ||
5453 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5455 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5458 Allocate next image structure.
5460 AcquireNextImage(image_info,image);
5462 if (GetNextImageInList(image) == (Image *) NULL)
5464 image=DestroyImageList(image);
5465 MngInfoFreeStruct(mng_info,&have_mng_structure);
5466 return((Image *) NULL);
5469 image=SyncNextImageInList(image);
5471 mng_info->image=image;
5473 if (term_chunk_found)
5475 image->start_loop=MagickTrue;
5476 image->iterations=mng_iterations;
5477 term_chunk_found=MagickFalse;
5481 image->start_loop=MagickFalse;
5483 /* Make a background rectangle. */
5486 image->columns=mng_info->mng_width;
5487 image->rows=mng_info->mng_height;
5488 image->page.width=mng_info->mng_width;
5489 image->page.height=mng_info->mng_height;
5492 image->background_color=mng_background_color;
5493 (void) SetImageBackgroundColor(image);
5494 if (logging != MagickFalse)
5495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5496 " Inserted transparent background layer, W=%.20g, H=%.20g",
5497 (double) mng_info->mng_width,(double) mng_info->mng_height);
5501 Insert a background layer behind the upcoming image if
5502 framing_mode is 3, and we haven't already inserted one.
5504 if (insert_layers && (mng_info->framing_mode == 3) &&
5505 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5506 (simplicity & 0x08)))
5508 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5511 Allocate next image structure.
5513 AcquireNextImage(image_info,image);
5515 if (GetNextImageInList(image) == (Image *) NULL)
5517 image=DestroyImageList(image);
5518 MngInfoFreeStruct(mng_info,&have_mng_structure);
5519 return((Image *) NULL);
5522 image=SyncNextImageInList(image);
5525 mng_info->image=image;
5527 if (term_chunk_found)
5529 image->start_loop=MagickTrue;
5530 image->iterations=mng_iterations;
5531 term_chunk_found=MagickFalse;
5535 image->start_loop=MagickFalse;
5538 image->columns=subframe_width;
5539 image->rows=subframe_height;
5540 image->page.width=subframe_width;
5541 image->page.height=subframe_height;
5542 image->page.x=mng_info->clip.left;
5543 image->page.y=mng_info->clip.top;
5544 image->background_color=mng_background_color;
5545 image->matte=MagickFalse;
5546 (void) SetImageBackgroundColor(image);
5548 if (logging != MagickFalse)
5549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5550 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5551 (double) mng_info->clip.left,(double) mng_info->clip.right,
5552 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5554 #endif /* MNG_INSERT_LAYERS */
5555 first_mng_object=MagickFalse;
5557 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5560 Allocate next image structure.
5562 AcquireNextImage(image_info,image);
5564 if (GetNextImageInList(image) == (Image *) NULL)
5566 image=DestroyImageList(image);
5567 MngInfoFreeStruct(mng_info,&have_mng_structure);
5568 return((Image *) NULL);
5571 image=SyncNextImageInList(image);
5573 mng_info->image=image;
5574 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5575 GetBlobSize(image));
5577 if (status == MagickFalse)
5580 if (term_chunk_found)
5582 image->start_loop=MagickTrue;
5583 term_chunk_found=MagickFalse;
5587 image->start_loop=MagickFalse;
5589 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5591 image->delay=frame_delay;
5592 frame_delay=default_frame_delay;
5598 image->page.width=mng_info->mng_width;
5599 image->page.height=mng_info->mng_height;
5600 image->page.x=mng_info->x_off[object_id];
5601 image->page.y=mng_info->y_off[object_id];
5602 image->iterations=mng_iterations;
5605 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5608 if (logging != MagickFalse)
5609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5610 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5613 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5616 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5620 mng_info->image=image;
5621 mng_info->mng_type=mng_type;
5622 mng_info->object_id=object_id;
5624 if (memcmp(type,mng_IHDR,4) == 0)
5625 image=ReadOnePNGImage(mng_info,image_info,exception);
5627 #if defined(JNG_SUPPORTED)
5629 image=ReadOneJNGImage(mng_info,image_info,exception);
5632 if (image == (Image *) NULL)
5634 if (IsImageObject(previous) != MagickFalse)
5636 (void) DestroyImageList(previous);
5637 (void) CloseBlob(previous);
5640 MngInfoFreeStruct(mng_info,&have_mng_structure);
5641 return((Image *) NULL);
5644 if (image->columns == 0 || image->rows == 0)
5646 (void) CloseBlob(image);
5647 image=DestroyImageList(image);
5648 MngInfoFreeStruct(mng_info,&have_mng_structure);
5649 return((Image *) NULL);
5652 mng_info->image=image;
5659 if (mng_info->magn_methx || mng_info->magn_methy)
5665 if (logging != MagickFalse)
5666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5667 " Processing MNG MAGN chunk");
5669 if (mng_info->magn_methx == 1)
5671 magnified_width=mng_info->magn_ml;
5673 if (image->columns > 1)
5674 magnified_width += mng_info->magn_mr;
5676 if (image->columns > 2)
5677 magnified_width += (png_uint_32)
5678 ((image->columns-2)*(mng_info->magn_mx));
5683 magnified_width=(png_uint_32) image->columns;
5685 if (image->columns > 1)
5686 magnified_width += mng_info->magn_ml-1;
5688 if (image->columns > 2)
5689 magnified_width += mng_info->magn_mr-1;
5691 if (image->columns > 3)
5692 magnified_width += (png_uint_32)
5693 ((image->columns-3)*(mng_info->magn_mx-1));
5696 if (mng_info->magn_methy == 1)
5698 magnified_height=mng_info->magn_mt;
5700 if (image->rows > 1)
5701 magnified_height += mng_info->magn_mb;
5703 if (image->rows > 2)
5704 magnified_height += (png_uint_32)
5705 ((image->rows-2)*(mng_info->magn_my));
5710 magnified_height=(png_uint_32) image->rows;
5712 if (image->rows > 1)
5713 magnified_height += mng_info->magn_mt-1;
5715 if (image->rows > 2)
5716 magnified_height += mng_info->magn_mb-1;
5718 if (image->rows > 3)
5719 magnified_height += (png_uint_32)
5720 ((image->rows-3)*(mng_info->magn_my-1));
5723 if (magnified_height > image->rows ||
5724 magnified_width > image->columns)
5739 register PixelPacket
5751 /* Allocate next image structure. */
5753 if (logging != MagickFalse)
5754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5755 " Allocate magnified image");
5757 AcquireNextImage(image_info,image);
5759 if (GetNextImageInList(image) == (Image *) NULL)
5761 image=DestroyImageList(image);
5762 MngInfoFreeStruct(mng_info,&have_mng_structure);
5763 return((Image *) NULL);
5766 large_image=SyncNextImageInList(image);
5768 large_image->columns=magnified_width;
5769 large_image->rows=magnified_height;
5771 magn_methx=mng_info->magn_methx;
5772 magn_methy=mng_info->magn_methy;
5774 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5775 #define QM unsigned short
5776 if (magn_methx != 1 || magn_methy != 1)
5779 Scale pixels to unsigned shorts to prevent
5780 overflow of intermediate values of interpolations
5782 for (y=0; y < (ssize_t) image->rows; y++)
5784 q=GetAuthenticPixels(image,0,y,image->columns,1,
5787 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5789 q->red=ScaleQuantumToShort(q->red);
5790 q->green=ScaleQuantumToShort(q->green);
5791 q->blue=ScaleQuantumToShort(q->blue);
5792 q->opacity=ScaleQuantumToShort(q->opacity);
5796 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5804 if (image->matte != MagickFalse)
5805 (void) SetImageBackgroundColor(large_image);
5809 large_image->background_color.opacity=OpaqueOpacity;
5810 (void) SetImageBackgroundColor(large_image);
5812 if (magn_methx == 4)
5815 if (magn_methx == 5)
5818 if (magn_methy == 4)
5821 if (magn_methy == 5)
5825 /* magnify the rows into the right side of the large image */
5827 if (logging != MagickFalse)
5828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5829 " Magnify the rows to %.20g",(double) large_image->rows);
5830 m=(ssize_t) mng_info->magn_mt;
5832 length=(size_t) image->columns;
5833 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5834 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5836 if ((prev == (PixelPacket *) NULL) ||
5837 (next == (PixelPacket *) NULL))
5839 image=DestroyImageList(image);
5840 MngInfoFreeStruct(mng_info,&have_mng_structure);
5841 ThrowReaderException(ResourceLimitError,
5842 "MemoryAllocationFailed");
5845 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5846 (void) CopyMagickMemory(next,n,length);
5848 for (y=0; y < (ssize_t) image->rows; y++)
5851 m=(ssize_t) mng_info->magn_mt;
5853 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5854 m=(ssize_t) mng_info->magn_mb;
5856 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5857 m=(ssize_t) mng_info->magn_mb;
5859 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5863 m=(ssize_t) mng_info->magn_my;
5869 if (y < (ssize_t) image->rows-1)
5871 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5873 (void) CopyMagickMemory(next,n,length);
5876 for (i=0; i < m; i++, yy++)
5878 register PixelPacket
5881 assert(yy < (ssize_t) large_image->rows);
5884 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5886 q+=(large_image->columns-image->columns);
5888 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5890 /* To do: get color as function of indexes[x] */
5892 if (image->storage_class == PseudoClass)
5897 if (magn_methy <= 1)
5899 *q=(*pixels); /* replicate previous */
5902 else if (magn_methy == 2 || magn_methy == 4)
5910 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5911 -(*pixels).red)+m))/((ssize_t) (m*2))
5913 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5914 -(*pixels).green)+m))/((ssize_t) (m*2))
5916 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5917 -(*pixels).blue)+m))/((ssize_t) (m*2))
5920 if (image->matte != MagickFalse)
5921 (*q).opacity=(QM) (((ssize_t)
5923 -(*pixels).opacity)+m))
5924 /((ssize_t) (m*2))+(*pixels).opacity);
5927 if (magn_methy == 4)
5929 /* Replicate nearest */
5930 if (i <= ((m+1) << 1))
5931 (*q).opacity=(*pixels).opacity+0;
5933 (*q).opacity=(*n).opacity+0;
5937 else /* if (magn_methy == 3 || magn_methy == 5) */
5939 /* Replicate nearest */
5940 if (i <= ((m+1) << 1))
5946 if (magn_methy == 5)
5948 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5949 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5950 +(*pixels).opacity);
5958 if (SyncAuthenticPixels(large_image,exception) == 0)
5964 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5965 next=(PixelPacket *) RelinquishMagickMemory(next);
5967 length=image->columns;
5969 if (logging != MagickFalse)
5970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5971 " Delete original image");
5973 DeleteImageFromList(&image);
5977 mng_info->image=image;
5979 /* magnify the columns */
5980 if (logging != MagickFalse)
5981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5982 " Magnify the columns to %.20g",(double) image->columns);
5984 for (y=0; y < (ssize_t) image->rows; y++)
5986 register PixelPacket
5989 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5990 pixels=q+(image->columns-length);
5993 for (x=(ssize_t) (image->columns-length);
5994 x < (ssize_t) image->columns; x++)
5996 if (x == (ssize_t) (image->columns-length))
5997 m=(ssize_t) mng_info->magn_ml;
5999 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6000 m=(ssize_t) mng_info->magn_mr;
6002 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6003 m=(ssize_t) mng_info->magn_mr;
6005 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6009 m=(ssize_t) mng_info->magn_mx;
6011 for (i=0; i < m; i++)
6013 if (magn_methx <= 1)
6015 /* replicate previous */
6019 else if (magn_methx == 2 || magn_methx == 4)
6027 (*q).red=(QM) ((2*i*((*n).red
6029 /((ssize_t) (m*2))+(*pixels).red);
6030 (*q).green=(QM) ((2*i*((*n).green
6032 +m)/((ssize_t) (m*2))+(*pixels).green);
6033 (*q).blue=(QM) ((2*i*((*n).blue
6035 /((ssize_t) (m*2))+(*pixels).blue);
6036 if (image->matte != MagickFalse)
6037 (*q).opacity=(QM) ((2*i*((*n).opacity
6038 -(*pixels).opacity)+m)/((ssize_t) (m*2))
6039 +(*pixels).opacity);
6042 if (magn_methx == 4)
6044 /* Replicate nearest */
6045 if (i <= ((m+1) << 1))
6046 (*q).opacity=(*pixels).opacity+0;
6048 (*q).opacity=(*n).opacity+0;
6052 else /* if (magn_methx == 3 || magn_methx == 5) */
6054 /* Replicate nearest */
6055 if (i <= ((m+1) << 1))
6061 if (magn_methx == 5)
6064 (*q).opacity=(QM) ((2*i*((*n).opacity
6065 -(*pixels).opacity)+m) /((ssize_t) (m*2))
6066 +(*pixels).opacity);
6075 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6078 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
6079 if (magn_methx != 1 || magn_methy != 1)
6082 Rescale pixels to Quantum
6084 for (y=0; y < (ssize_t) image->rows; y++)
6086 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6088 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6090 q->red=ScaleShortToQuantum(q->red);
6091 q->green=ScaleShortToQuantum(q->green);
6092 q->blue=ScaleShortToQuantum(q->blue);
6093 q->opacity=ScaleShortToQuantum(q->opacity);
6097 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6102 if (logging != MagickFalse)
6103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6104 " Finished MAGN processing");
6109 Crop_box is with respect to the upper left corner of the MNG.
6111 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6112 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6113 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6114 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6115 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6116 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6117 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6118 if ((crop_box.left != (mng_info->image_box.left
6119 +mng_info->x_off[object_id])) ||
6120 (crop_box.right != (mng_info->image_box.right
6121 +mng_info->x_off[object_id])) ||
6122 (crop_box.top != (mng_info->image_box.top
6123 +mng_info->y_off[object_id])) ||
6124 (crop_box.bottom != (mng_info->image_box.bottom
6125 +mng_info->y_off[object_id])))
6127 if (logging != MagickFalse)
6128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6129 " Crop the PNG image");
6131 if ((crop_box.left < crop_box.right) &&
6132 (crop_box.top < crop_box.bottom))
6141 Crop_info is with respect to the upper left corner of
6144 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6145 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6146 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6147 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6148 image->page.width=image->columns;
6149 image->page.height=image->rows;
6152 im=CropImage(image,&crop_info,exception);
6154 if (im != (Image *) NULL)
6156 image->columns=im->columns;
6157 image->rows=im->rows;
6158 im=DestroyImage(im);
6159 image->page.width=image->columns;
6160 image->page.height=image->rows;
6161 image->page.x=crop_box.left;
6162 image->page.y=crop_box.top;
6169 No pixels in crop area. The MNG spec still requires
6170 a layer, though, so make a single transparent pixel in
6171 the top left corner.
6176 (void) SetImageBackgroundColor(image);
6177 image->page.width=1;
6178 image->page.height=1;
6183 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6184 image=mng_info->image;
6188 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6189 /* PNG does not handle depths greater than 16 so reduce it even
6192 if (image->depth > 16)
6196 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6197 if (LosslessReduceDepthOK(image) != MagickFalse)
6201 GetImageException(image,exception);
6203 if (image_info->number_scenes != 0)
6205 if (mng_info->scenes_found >
6206 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Finished reading image datastream.");
6214 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6216 (void) CloseBlob(image);
6218 if (logging != MagickFalse)
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " Finished reading all image datastreams.");
6222 #if defined(MNG_INSERT_LAYERS)
6223 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6224 (mng_info->mng_height))
6227 Insert a background layer if nothing else was found.
6229 if (logging != MagickFalse)
6230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6231 " No images found. Inserting a background layer.");
6233 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6236 Allocate next image structure.
6238 AcquireNextImage(image_info,image);
6239 if (GetNextImageInList(image) == (Image *) NULL)
6241 image=DestroyImageList(image);
6242 MngInfoFreeStruct(mng_info,&have_mng_structure);
6244 if (logging != MagickFalse)
6245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6246 " Allocation failed, returning NULL.");
6248 return((Image *) NULL);
6250 image=SyncNextImageInList(image);
6252 image->columns=mng_info->mng_width;
6253 image->rows=mng_info->mng_height;
6254 image->page.width=mng_info->mng_width;
6255 image->page.height=mng_info->mng_height;
6258 image->background_color=mng_background_color;
6259 image->matte=MagickFalse;
6261 if (image_info->ping == MagickFalse)
6262 (void) SetImageBackgroundColor(image);
6264 mng_info->image_found++;
6267 image->iterations=mng_iterations;
6269 if (mng_iterations == 1)
6270 image->start_loop=MagickTrue;
6272 while (GetPreviousImageInList(image) != (Image *) NULL)
6275 if (image_count > 10*mng_info->image_found)
6277 if (logging != MagickFalse)
6278 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6280 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6281 CoderError,"Linked list is corrupted, beginning of list not found",
6282 "`%s'",image_info->filename);
6284 return((Image *) NULL);
6287 image=GetPreviousImageInList(image);
6289 if (GetNextImageInList(image) == (Image *) NULL)
6291 if (logging != MagickFalse)
6292 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6294 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6295 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6296 image_info->filename);
6300 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6301 GetNextImageInList(image) ==
6304 if (logging != MagickFalse)
6305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6306 " First image null");
6308 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6309 CoderError,"image->next for first image is NULL but shouldn't be.",
6310 "`%s'",image_info->filename);
6313 if (mng_info->image_found == 0)
6315 if (logging != MagickFalse)
6316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6317 " No visible images found.");
6319 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6320 CoderError,"No visible images in file","`%s'",image_info->filename);
6322 if (image != (Image *) NULL)
6323 image=DestroyImageList(image);
6325 MngInfoFreeStruct(mng_info,&have_mng_structure);
6326 return((Image *) NULL);
6329 if (mng_info->ticks_per_second)
6330 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6331 final_delay/mng_info->ticks_per_second;
6334 image->start_loop=MagickTrue;
6336 /* Find final nonzero image delay */
6337 final_image_delay=0;
6339 while (GetNextImageInList(image) != (Image *) NULL)
6342 final_image_delay=image->delay;
6344 image=GetNextImageInList(image);
6347 if (final_delay < final_image_delay)
6348 final_delay=final_image_delay;
6350 image->delay=final_delay;
6352 if (logging != MagickFalse)
6353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6354 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6355 (double) final_delay);
6357 if (logging != MagickFalse)
6363 image=GetFirstImageInList(image);
6365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6366 " Before coalesce:");
6368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6369 " scene 0 delay=%.20g",(double) image->delay);
6371 while (GetNextImageInList(image) != (Image *) NULL)
6373 image=GetNextImageInList(image);
6374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6375 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6379 image=GetFirstImageInList(image);
6380 #ifdef MNG_COALESCE_LAYERS
6390 if (logging != MagickFalse)
6391 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6394 next_image=CoalesceImages(image,&image->exception);
6396 if (next_image == (Image *) NULL)
6397 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6399 image=DestroyImageList(image);
6402 for (next=image; next != (Image *) NULL; next=next_image)
6404 next->page.width=mng_info->mng_width;
6405 next->page.height=mng_info->mng_height;
6408 next->scene=scene++;
6409 next_image=GetNextImageInList(next);
6411 if (next_image == (Image *) NULL)
6414 if (next->delay == 0)
6417 next_image->previous=GetPreviousImageInList(next);
6418 if (GetPreviousImageInList(next) == (Image *) NULL)
6421 next->previous->next=next_image;
6422 next=DestroyImage(next);
6428 while (GetNextImageInList(image) != (Image *) NULL)
6429 image=GetNextImageInList(image);
6431 image->dispose=BackgroundDispose;
6433 if (logging != MagickFalse)
6439 image=GetFirstImageInList(image);
6441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6442 " After coalesce:");
6444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6445 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6446 (double) image->dispose);
6448 while (GetNextImageInList(image) != (Image *) NULL)
6450 image=GetNextImageInList(image);
6452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6453 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6454 (double) image->delay,(double) image->dispose);
6458 image=GetFirstImageInList(image);
6459 MngInfoFreeStruct(mng_info,&have_mng_structure);
6460 have_mng_structure=MagickFalse;
6462 if (logging != MagickFalse)
6463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6465 return(GetFirstImageInList(image));
6467 #else /* PNG_LIBPNG_VER > 10011 */
6468 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6470 printf("Your PNG library is too old: You have libpng-%s\n",
6471 PNG_LIBPNG_VER_STRING);
6473 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6474 "PNG library is too old","`%s'",image_info->filename);
6476 return(Image *) NULL;
6479 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6481 return(ReadPNGImage(image_info,exception));
6483 #endif /* PNG_LIBPNG_VER > 10011 */
6487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6491 % R e g i s t e r P N G I m a g e %
6495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6497 % RegisterPNGImage() adds properties for the PNG image format to
6498 % the list of supported formats. The properties include the image format
6499 % tag, a method to read and/or write the format, whether the format
6500 % supports the saving of more than one frame to the same file or blob,
6501 % whether the format supports native in-memory I/O, and a brief
6502 % description of the format.
6504 % The format of the RegisterPNGImage method is:
6506 % size_t RegisterPNGImage(void)
6509 ModuleExport size_t RegisterPNGImage(void)
6512 version[MaxTextExtent];
6520 "See http://www.libpng.org/ for details about the PNG format."
6525 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6531 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6537 #if defined(PNG_LIBPNG_VER_STRING)
6538 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6539 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6541 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6543 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6544 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6549 entry=SetMagickInfo("MNG");
6550 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6552 #if defined(MAGICKCORE_PNG_DELEGATE)
6553 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6554 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6557 entry->magick=(IsImageFormatHandler *) IsMNG;
6558 entry->description=ConstantString("Multiple-image Network Graphics");
6560 if (*version != '\0')
6561 entry->version=ConstantString(version);
6563 entry->module=ConstantString("PNG");
6564 entry->note=ConstantString(MNGNote);
6565 (void) RegisterMagickInfo(entry);
6567 entry=SetMagickInfo("PNG");
6569 #if defined(MAGICKCORE_PNG_DELEGATE)
6570 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6571 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6574 entry->magick=(IsImageFormatHandler *) IsPNG;
6575 entry->adjoin=MagickFalse;
6576 entry->description=ConstantString("Portable Network Graphics");
6577 entry->module=ConstantString("PNG");
6579 if (*version != '\0')
6580 entry->version=ConstantString(version);
6582 entry->note=ConstantString(PNGNote);
6583 (void) RegisterMagickInfo(entry);
6585 entry=SetMagickInfo("PNG8");
6587 #if defined(MAGICKCORE_PNG_DELEGATE)
6588 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6589 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6592 entry->magick=(IsImageFormatHandler *) IsPNG;
6593 entry->adjoin=MagickFalse;
6594 entry->description=ConstantString(
6595 "8-bit indexed with optional binary transparency");
6596 entry->module=ConstantString("PNG");
6597 (void) RegisterMagickInfo(entry);
6599 entry=SetMagickInfo("PNG24");
6602 #if defined(ZLIB_VERSION)
6603 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6604 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6606 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6608 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6609 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6613 if (*version != '\0')
6614 entry->version=ConstantString(version);
6616 #if defined(MAGICKCORE_PNG_DELEGATE)
6617 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6618 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6621 entry->magick=(IsImageFormatHandler *) IsPNG;
6622 entry->adjoin=MagickFalse;
6623 entry->description=ConstantString("opaque 24-bit RGB");
6624 entry->module=ConstantString("PNG");
6625 (void) RegisterMagickInfo(entry);
6627 entry=SetMagickInfo("PNG32");
6629 #if defined(MAGICKCORE_PNG_DELEGATE)
6630 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6631 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6634 entry->magick=(IsImageFormatHandler *) IsPNG;
6635 entry->adjoin=MagickFalse;
6636 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6637 entry->module=ConstantString("PNG");
6638 (void) RegisterMagickInfo(entry);
6640 entry=SetMagickInfo("JNG");
6642 #if defined(JNG_SUPPORTED)
6643 #if defined(MAGICKCORE_PNG_DELEGATE)
6644 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6645 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6649 entry->magick=(IsImageFormatHandler *) IsJNG;
6650 entry->adjoin=MagickFalse;
6651 entry->description=ConstantString("JPEG Network Graphics");
6652 entry->module=ConstantString("PNG");
6653 entry->note=ConstantString(JNGNote);
6654 (void) RegisterMagickInfo(entry);
6656 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6657 ping_semaphore=AllocateSemaphoreInfo();
6660 return(MagickImageCoderSignature);
6664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6668 % U n r e g i s t e r P N G I m a g e %
6672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674 % UnregisterPNGImage() removes format registrations made by the
6675 % PNG module from the list of supported formats.
6677 % The format of the UnregisterPNGImage method is:
6679 % UnregisterPNGImage(void)
6682 ModuleExport void UnregisterPNGImage(void)
6684 (void) UnregisterMagickInfo("MNG");
6685 (void) UnregisterMagickInfo("PNG");
6686 (void) UnregisterMagickInfo("PNG8");
6687 (void) UnregisterMagickInfo("PNG24");
6688 (void) UnregisterMagickInfo("PNG32");
6689 (void) UnregisterMagickInfo("JNG");
6691 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6692 if (ping_semaphore != (SemaphoreInfo *) NULL)
6693 DestroySemaphoreInfo(&ping_semaphore);
6697 #if defined(MAGICKCORE_PNG_DELEGATE)
6698 #if PNG_LIBPNG_VER > 10011
6700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6704 % W r i t e M N G I m a g e %
6708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6710 % WriteMNGImage() writes an image in the Portable Network Graphics
6711 % Group's "Multiple-image Network Graphics" encoded image format.
6713 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6715 % The format of the WriteMNGImage method is:
6717 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6719 % A description of each parameter follows.
6721 % o image_info: the image info.
6723 % o image: The image.
6726 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6727 % "To do" under ReadPNGImage):
6729 % Preserve all unknown and not-yet-handled known chunks found in input
6730 % PNG file and copy them into output PNG files according to the PNG
6733 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6735 % Improve selection of color type (use indexed-colour or indexed-colour
6736 % with tRNS when 256 or fewer unique RGBA values are present).
6738 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6739 % This will be complicated if we limit ourselves to generating MNG-LC
6740 % files. For now we ignore disposal method 3 and simply overlay the next
6743 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6744 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6745 % [mostly done 15 June 1999 but still need to take care of tRNS]
6747 % Check for identical sRGB and replace with a global sRGB (and remove
6748 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6749 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6750 % local gAMA/cHRM with local sRGB if appropriate).
6752 % Check for identical sBIT chunks and write global ones.
6754 % Provide option to skip writing the signature tEXt chunks.
6756 % Use signatures to detect identical objects and reuse the first
6757 % instance of such objects instead of writing duplicate objects.
6759 % Use a smaller-than-32k value of compression window size when
6762 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6763 % ancillary text chunks and save profiles.
6765 % Provide an option to force LC files (to ensure exact framing rate)
6768 % Provide an option to force VLC files instead of LC, even when offsets
6769 % are present. This will involve expanding the embedded images with a
6770 % transparent region at the top and/or left.
6774 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6775 png_info *ping_info, unsigned char *profile_type, unsigned char
6776 *profile_description, unsigned char *profile_data, png_uint_32 length)
6795 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6797 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6800 if (image_info->verbose)
6802 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6803 (char *) profile_type, (double) length);
6806 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6807 description_length=(png_uint_32) strlen((const char *) profile_description);
6808 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6809 + description_length);
6810 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6811 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6812 text[0].key[0]='\0';
6813 (void) ConcatenateMagickString(text[0].key,
6814 "Raw profile type ",MaxTextExtent);
6815 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6819 (void) CopyMagickString(dp,(const char *) profile_description,
6821 dp+=description_length;
6823 (void) FormatMagickString(dp,allocated_length-
6824 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6827 for (i=0; i < (ssize_t) length; i++)
6831 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6832 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6837 text[0].text_length=(png_size_t) (dp-text[0].text);
6838 text[0].compression=image_info->compression == NoCompression ||
6839 (image_info->compression == UndefinedCompression &&
6840 text[0].text_length < 128) ? -1 : 0;
6842 if (text[0].text_length <= allocated_length)
6843 png_set_text(ping,ping_info,text,1);
6845 png_free(ping,text[0].text);
6846 png_free(ping,text[0].key);
6847 png_free(ping,text);
6850 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6851 const char *string, MagickBooleanType logging)
6864 ResetImageProfileIterator(image);
6866 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6868 profile=GetImageProfile(image,name);
6870 if (profile != (const StringInfo *) NULL)
6875 if (LocaleNCompare(name,string,11) == 0)
6877 if (logging != MagickFalse)
6878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6879 " Found %s profile",name);
6881 ping_profile=CloneStringInfo(profile);
6882 data=GetStringInfoDatum(ping_profile),
6883 length=(png_uint_32) GetStringInfoLength(ping_profile);
6888 (void) WriteBlobMSBULong(image,length-5); /* data length */
6889 (void) WriteBlob(image,length-1,data+1);
6890 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6891 ping_profile=DestroyStringInfo(ping_profile);
6895 name=GetNextImageProfile(image);
6902 /* Write one PNG image */
6903 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6904 const ImageInfo *IMimage_info,Image *IMimage)
6928 ping_trans_alpha[256];
6956 ping_have_cheap_transparency,
6967 /* ping_exclude_EXIF, */
6970 /* ping_exclude_iTXt, */
6975 /* ping_exclude_tRNS, */
6977 ping_exclude_zCCP, /* hex-encoded iCCP */
6980 ping_preserve_colormap,
6981 ping_need_colortype_warning,
7001 ping_interlace_method,
7002 ping_compression_method,
7019 number_semitransparent,
7021 ping_pHYs_unit_type;
7024 ping_pHYs_x_resolution,
7025 ping_pHYs_y_resolution;
7027 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7028 " Enter WriteOnePNGImage()");
7030 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7031 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7032 if (image_info == (ImageInfo *) NULL)
7033 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7035 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7036 LockSemaphoreInfo(ping_semaphore);
7039 /* Initialize some stuff */
7042 ping_interlace_method=0,
7043 ping_compression_method=0,
7044 ping_filter_method=0,
7047 ping_background.red = 0;
7048 ping_background.green = 0;
7049 ping_background.blue = 0;
7050 ping_background.gray = 0;
7051 ping_background.index = 0;
7053 ping_trans_color.red=0;
7054 ping_trans_color.green=0;
7055 ping_trans_color.blue=0;
7056 ping_trans_color.gray=0;
7058 ping_pHYs_unit_type = 0;
7059 ping_pHYs_x_resolution = 0;
7060 ping_pHYs_y_resolution = 0;
7062 ping_have_blob=MagickFalse;
7063 ping_have_color=MagickTrue;
7064 ping_have_non_bw=MagickTrue;
7065 ping_have_PLTE=MagickFalse;
7066 ping_have_bKGD=MagickFalse;
7067 ping_have_pHYs=MagickFalse;
7068 ping_have_tRNS=MagickFalse;
7070 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7071 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7072 ping_exclude_date=mng_info->ping_exclude_date;
7073 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7074 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7075 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7076 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7077 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7078 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7079 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7080 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7081 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7082 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7083 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7084 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7086 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7087 ping_need_colortype_warning = MagickFalse;
7090 number_semitransparent = 0;
7091 number_transparent = 0;
7093 if (logging != MagickFalse)
7095 if (image->storage_class == UndefinedClass)
7096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097 " storage_class=UndefinedClass");
7098 if (image->storage_class == DirectClass)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " storage_class=DirectClass");
7101 if (image->storage_class == PseudoClass)
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " storage_class=PseudoClass");
7106 if (image->storage_class != PseudoClass && image->colormap != NULL)
7108 /* Free the bogus colormap; it can cause trouble later */
7109 if (logging != MagickFalse)
7110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7111 " Freeing bogus colormap");
7112 (void *) RelinquishMagickMemory(image->colormap);
7113 image->colormap=NULL;
7116 if (image->colorspace != RGBColorspace)
7117 (void) TransformImageColorspace(image,RGBColorspace);
7120 Sometimes we get PseudoClass images whose RGB values don't match
7121 the colors in the colormap. This code syncs the RGB values.
7123 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7124 (void) SyncImage(image);
7126 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7127 if (image->depth > 8)
7129 if (logging != MagickFalse)
7130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7131 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7137 #if 0 /* To do: Option to use the original colormap */
7138 if (ping_preserve_colormap != MagickFalse)
7143 #if 0 /* To do: honor -depth */
7144 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7149 /* To do: set to next higher multiple of 8 */
7150 if (image->depth < 8)
7153 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7154 /* PNG does not handle depths greater than 16 so reduce it even
7157 if (image->depth > 16)
7161 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7162 if (image->depth == 16 && mng_info->write_png_depth != 16)
7163 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7167 /* Normally we run this just once, but in the case of writing PNG8
7168 * we reduce the transparency to binary and run again, then if there
7169 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7170 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7171 * palette. The final reduction can only fail if there are still 256
7172 * colors present and one of them has both transparent and opaque instances.
7175 tried_333 = MagickFalse;
7176 tried_444 = MagickFalse;
7182 * Sometimes we get DirectClass images that have 256 colors or fewer.
7183 * This code will build a colormap.
7185 * Also, sometimes we get PseudoClass images with an out-of-date
7186 * colormap. This code will replace the colormap with a new one.
7187 * Sometimes we get PseudoClass images that have more than 256 colors.
7188 * This code will delete the colormap and change the image to
7191 * If image->matte is MagickFalse, we ignore the opacity channel
7192 * even though it sometimes contains left-over non-opaque values.
7194 * Also we gather some information (number of opaque, transparent,
7195 * and semitransparent pixels, and whether the image has any non-gray
7196 * pixels or only black-and-white pixels) that we might need later.
7198 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7199 * we need to check for bogus non-opaque values, at least.
7202 # define PNGK (MAGICKCORE_QUANTUM_DEPTH-8) /* Shift */
7203 # define PNGM (ScaleCharToQuantum((unsigned char) 0x01)) /* Scale */
7213 semitransparent[260],
7216 register IndexPacket
7219 register const PixelPacket
7223 register PixelPacket
7226 if (logging != MagickFalse)
7227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7228 " Enter BUILD_PALETTE:");
7230 if (logging != MagickFalse)
7232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7233 " image->columns=%.20g",(double) image->columns);
7234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7235 " image->rows=%.20g",(double) image->rows);
7236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7237 " image->matte=%.20g",(double) image->matte);
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7239 " image->depth=%.20g",(double) image->depth);
7241 if (image->storage_class == PseudoClass && image->colormap != NULL)
7243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7244 " Original colormap:");
7245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7246 " i (red,green,blue,opacity)");
7248 for (i=0; i < 256; i++)
7250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7251 " %d (%d,%d,%d,%d)",
7253 (int) image->colormap[i].red,
7254 (int) image->colormap[i].green,
7255 (int) image->colormap[i].blue,
7256 (int) image->colormap[i].opacity);
7259 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7264 " %d (%d,%d,%d,%d)",
7266 (int) image->colormap[i].red,
7267 (int) image->colormap[i].green,
7268 (int) image->colormap[i].blue,
7269 (int) image->colormap[i].opacity);
7274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7275 " image->colors=%d",(int) image->colors);
7277 if (image->colors == 0)
7278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7279 " (zero means unknown)");
7281 if (ping_preserve_colormap == MagickFalse)
7282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7283 " Regenerate the colormap");
7286 exception=(&image->exception);
7290 number_semitransparent = 0;
7291 number_transparent = 0;
7293 for (y=0; y < (ssize_t) image->rows; y++)
7295 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7297 if (q == (PixelPacket *) NULL)
7300 for (x=0; x < (ssize_t) image->columns; x++)
7302 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7304 if (number_opaque < 259)
7306 if (number_opaque == 0)
7309 opaque[0].opacity=OpaqueOpacity;
7313 for (i=0; i< (ssize_t) number_opaque; i++)
7315 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7319 if (i == (ssize_t) number_opaque &&
7320 number_opaque < 259)
7324 opaque[i].opacity = OpaqueOpacity;
7328 else if (q->opacity == TransparentOpacity)
7330 if (number_transparent < 259)
7332 if (number_transparent == 0)
7335 ping_trans_color.red=(unsigned short)(q->red);
7336 ping_trans_color.green=(unsigned short) (q->green);
7337 ping_trans_color.blue=(unsigned short) (q->blue);
7338 ping_trans_color.gray=(unsigned short) (q->blue);
7339 number_transparent = 1;
7342 for (i=0; i< (ssize_t) number_transparent; i++)
7344 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7348 if (i == (ssize_t) number_transparent &&
7349 number_transparent < 259)
7351 number_transparent++;
7352 transparent[i] = *q;
7358 if (number_semitransparent < 259)
7360 if (number_semitransparent == 0)
7362 semitransparent[0]=*q;
7363 number_semitransparent = 1;
7366 for (i=0; i< (ssize_t) number_semitransparent; i++)
7368 if (IsColorEqual(semitransparent+i,
7369 (PixelPacket *) q) &&
7370 q->opacity == semitransparent[i].opacity)
7374 if (i == (ssize_t) number_semitransparent &&
7375 number_semitransparent < 259)
7377 number_semitransparent++;
7378 semitransparent[i] = *q;
7386 if (ping_exclude_bKGD == MagickFalse)
7388 /* Add the background color to the palette, if it
7389 * isn't already there.
7391 for (i=0; i<number_opaque; i++)
7393 if (IsColorEqual(opaque+i, &image->background_color))
7397 if (number_opaque < 259 && i == number_opaque)
7399 opaque[i]=image->background_color;
7400 opaque[i].opacity = OpaqueOpacity;
7403 else if (logging != MagickFalse)
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " No room in the colormap to add background color");
7408 image_colors=number_opaque+number_transparent+number_semitransparent;
7410 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7412 /* No room for the background color; remove it. */
7417 if (logging != MagickFalse)
7419 if (image_colors > 256)
7420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7421 " image has more than 256 colors");
7424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7425 " image has %d colors",image_colors);
7429 if (ping_preserve_colormap != MagickFalse)
7433 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7435 ping_have_color=MagickFalse;
7436 ping_have_non_bw=MagickFalse;
7438 if(image_colors > 256)
7440 for (y=0; y < (ssize_t) image->rows; y++)
7442 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7444 if (q == (PixelPacket *) NULL)
7447 /* Worst case is black-and-white; we are looking at every
7451 if (ping_have_color == MagickFalse)
7454 for (x=0; x < (ssize_t) image->columns; x++)
7456 if (s->red != s->green || s->red != s->blue)
7458 ping_have_color=MagickTrue;
7459 ping_have_non_bw=MagickTrue;
7466 if (ping_have_non_bw == MagickFalse)
7469 for (x=0; x < (ssize_t) image->columns; x++)
7471 if (s->red != 0 && s->red != QuantumRange)
7473 ping_have_non_bw=MagickTrue;
7482 if (image_colors < 257)
7488 * Initialize image colormap.
7491 if (logging != MagickFalse)
7492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7493 " Sort the new colormap");
7495 /* Sort palette, transparent first */;
7499 for (i=0; i<number_transparent; i++)
7500 colormap[n++] = transparent[i];
7502 for (i=0; i<number_semitransparent; i++)
7503 colormap[n++] = semitransparent[i];
7505 for (i=0; i<number_opaque; i++)
7506 colormap[n++] = opaque[i];
7509 /* image_colors < 257; search the colormap instead of the pixels
7510 * to get ping_have_color and ping_have_non_bw
7514 if (ping_have_color == MagickFalse)
7516 if (colormap[i].red != colormap[i].green ||
7517 colormap[i].red != colormap[i].blue)
7519 ping_have_color=MagickTrue;
7520 ping_have_non_bw=MagickTrue;
7525 if (ping_have_non_bw == MagickFalse)
7527 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7528 ping_have_non_bw=MagickTrue;
7532 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7533 (number_transparent == 0 && number_semitransparent == 0)) &&
7534 (((mng_info->write_png_colortype-1) ==
7535 PNG_COLOR_TYPE_PALETTE) ||
7536 (mng_info->write_png_colortype == 0)))
7538 if (logging != MagickFalse)
7540 if (n != (ssize_t) image_colors)
7541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7542 " image_colors (%d) and n (%d) don't match",
7545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7546 " AcquireImageColormap");
7549 image->colors = image_colors;
7551 if (AcquireImageColormap(image,image_colors) ==
7553 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7555 for (i=0; i< (ssize_t) image_colors; i++)
7556 image->colormap[i] = colormap[i];
7558 if (logging != MagickFalse)
7560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7561 " image->colors=%d (%d)",
7562 (int) image->colors, image_colors);
7564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7565 " Update the pixel indexes");
7568 /* Sync the pixel indices with the new colormap */
7570 for (y=0; y < (ssize_t) image->rows; y++)
7572 q=GetAuthenticPixels(image,0,y,image->columns,1,
7575 if (q == (PixelPacket *) NULL)
7578 indexes=GetAuthenticIndexQueue(image);
7580 for (x=0; x < (ssize_t) image->columns; x++)
7582 for (i=0; i< (ssize_t) image_colors; i++)
7584 if ((image->matte == MagickFalse ||
7585 image->colormap[i].opacity == q->opacity) &&
7586 (IsColorEqual(&image->colormap[i],
7587 (PixelPacket *) q)))
7589 indexes[x]=(IndexPacket) i;
7596 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7602 if (logging != MagickFalse)
7604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7605 " image->colors=%d", (int) image->colors);
7607 if (image->colormap != NULL)
7609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610 " i (red,green,blue,opacity)");
7612 for (i=0; i < (ssize_t) image->colors; i++)
7614 if (i < 300 || i >= (ssize_t) image->colors - 10)
7616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7617 " %d (%d,%d,%d,%d)",
7619 (int) image->colormap[i].red,
7620 (int) image->colormap[i].green,
7621 (int) image->colormap[i].blue,
7622 (int) image->colormap[i].opacity);
7627 if (number_transparent < 257)
7628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7629 " number_transparent = %d",
7630 number_transparent);
7633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7634 " number_transparent > 256");
7636 if (number_opaque < 257)
7637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7638 " number_opaque = %d",
7642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7643 " number_opaque > 256");
7645 if (number_semitransparent < 257)
7646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7647 " number_semitransparent = %d",
7648 number_semitransparent);
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " number_semitransparent > 256");
7654 if (ping_have_non_bw == MagickFalse)
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " All pixels and the background are black or white");
7658 else if (ping_have_color == MagickFalse)
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " All pixels and the background are gray");
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " At least one pixel or the background is non-gray");
7666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7667 " Exit BUILD_PALETTE:");
7670 if (mng_info->write_png8 == MagickFalse)
7673 /* Make any reductions necessary for the PNG8 format */
7674 if (image_colors <= 256 &&
7675 image_colors != 0 && image->colormap != NULL &&
7676 number_semitransparent == 0 &&
7677 number_transparent <= 1)
7680 /* PNG8 can't have semitransparent colors so we threshold the
7681 * opacity to 0 or OpaqueOpacity
7683 if (number_semitransparent != 0)
7685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7686 " Thresholding the alpha channel to binary");
7688 for (y=0; y < (ssize_t) image->rows; y++)
7690 r=GetAuthenticPixels(image,0,y,image->columns,1,
7693 if (r == (PixelPacket *) NULL)
7696 for (x=0; x < (ssize_t) image->columns; x++)
7698 r->opacity = r->opacity > TransparentOpacity/2 ?
7699 TransparentOpacity : OpaqueOpacity;
7703 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7706 if (image_colors != 0 && image_colors <= 256 &&
7707 image->colormap != NULL)
7708 for (i=0; i<image_colors; i++)
7709 image->colormap[i].opacity =
7710 image->colormap[i].opacity > TransparentOpacity/2 ?
7711 TransparentOpacity : OpaqueOpacity;
7716 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7717 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7718 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7721 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7723 if (logging != MagickFalse)
7724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7725 " Quantizing the background color to 4-4-4");
7727 tried_444 = MagickTrue;
7729 image->background_color.red=
7731 image->background_color.red) >> PNGK) & 0xf0) ) |
7733 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7734 image->background_color.green=
7736 image->background_color.green) >> PNGK) & 0xf0) ) |
7738 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7739 image->background_color.blue=
7741 image->background_color.blue) >> PNGK) & 0xf0) ) |
7743 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7745 if (logging != MagickFalse)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " Quantizing the pixel colors to 4-4-4");
7749 if (image->colormap == NULL)
7751 for (y=0; y < (ssize_t) image->rows; y++)
7753 r=GetAuthenticPixels(image,0,y,image->columns,1,
7756 if (r == (PixelPacket *) NULL)
7759 for (x=0; x < (ssize_t) image->columns; x++)
7761 if (r->opacity == TransparentOpacity)
7763 r->red = image->background_color.red;
7764 r->green = image->background_color.green;
7765 r->blue = image->background_color.blue;
7770 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7771 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7773 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7774 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7776 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7777 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7787 else /* Should not reach this; colormap already exists and
7790 if (logging != MagickFalse)
7791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7792 " Quantizing the colormap to 4-4-4");
7793 for (i=0; i<image_colors; i++)
7795 image->colormap[i].red=
7797 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7799 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7800 image->colormap[i].green=
7802 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7804 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7805 image->colormap[i].blue=
7807 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7809 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7815 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7817 if (logging != MagickFalse)
7818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7819 " Quantizing the background color to 3-3-3");
7821 tried_333 = MagickTrue;
7823 image->background_color.red=
7825 image->background_color.red) >> PNGK) & 0xe0) ) |
7827 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7829 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7830 image->background_color.green=
7832 image->background_color.green) >> PNGK) & 0xe0) ) |
7834 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7836 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7837 image->background_color.blue=
7839 image->background_color.blue) >> PNGK) & 0xe0) ) |
7841 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7843 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7845 if (logging != MagickFalse)
7846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7847 " Quantizing the pixel colors to 3-3-3-1");
7849 if (image->colormap == NULL)
7851 for (y=0; y < (ssize_t) image->rows; y++)
7853 r=GetAuthenticPixels(image,0,y,image->columns,1,
7856 if (r == (PixelPacket *) NULL)
7859 for (x=0; x < (ssize_t) image->columns; x++)
7861 if (r->opacity == TransparentOpacity)
7863 r->red = image->background_color.red;
7864 r->green = image->background_color.green;
7865 r->blue = image->background_color.blue;
7870 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7871 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7872 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7874 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7875 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7876 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7878 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7879 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7880 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7885 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7890 else /* Should not reach this; colormap already exists and
7893 if (logging != MagickFalse)
7894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7895 " Quantizing the colormap to 3-3-3-1");
7896 for (i=0; i<image_colors; i++)
7898 image->colormap[i].red=
7900 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7902 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7904 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7905 image->colormap[i].green=
7907 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7909 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7911 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7912 image->colormap[i].blue=
7914 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7916 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7918 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7924 if (image_colors == 0 || image_colors > 256)
7926 if (logging != MagickFalse)
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928 " Quantizing the background color to 3-3-2");
7930 image->background_color.red=
7932 image->background_color.red) >> PNGK) & 0xe0) ) |
7934 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7936 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7937 image->background_color.green=
7939 image->background_color.green) >> PNGK) & 0xe0) ) |
7941 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7943 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7944 image->background_color.blue=
7946 image->background_color.blue) >> PNGK) & 0xc0) ) |
7948 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7950 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7952 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7954 if (logging != MagickFalse)
7955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7956 " Quantizing the pixel colors to 3-3-2-1");
7958 if (image->colormap == NULL)
7960 for (y=0; y < (ssize_t) image->rows; y++)
7962 r=GetAuthenticPixels(image,0,y,image->columns,1,
7965 if (r == (PixelPacket *) NULL)
7968 for (x=0; x < (ssize_t) image->columns; x++)
7970 if (r->opacity == TransparentOpacity)
7972 r->red = image->background_color.red;
7973 r->green = image->background_color.green;
7974 r->blue = image->background_color.blue;
7979 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7980 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7981 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7983 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7984 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7985 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7987 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7988 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7989 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7990 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7995 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8000 else /* Should not reach this; colormap already exists and
8003 if (logging != MagickFalse)
8004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8005 " Quantizing the colormap to 3-3-2-1");
8006 for (i=0; i<image_colors; i++)
8008 image->colormap[i].red=
8010 image->colormap[i].red) >> PNGK) & 0xe0) ) |
8012 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
8014 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
8015 image->colormap[i].green=
8017 image->colormap[i].green) >> PNGK) & 0xe0) ) |
8019 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
8021 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
8022 image->colormap[i].blue=
8024 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
8026 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
8028 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
8030 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
8037 /* END OF BUILD_PALETTE */
8039 /* If we are excluding the tRNS chunk and there is transparency,
8040 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8043 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8044 (number_transparent != 0 || number_semitransparent != 0))
8046 int colortype=mng_info->write_png_colortype;
8048 if (ping_have_color == MagickFalse)
8049 mng_info->write_png_colortype = 5;
8052 mng_info->write_png_colortype = 7;
8054 if (colortype != 0 &&
8055 mng_info->write_png_colortype != (ssize_t) colortype)
8056 ping_need_colortype_warning=MagickTrue;
8060 /* See if cheap transparency is possible. It is only possible
8061 * when there is a single transparent color, no semitransparent
8062 * color, and no opaque color that has the same RGB components
8063 * as the transparent color. We only need this information if
8064 * we are writing a PNG with colortype 0 or 2, and we have not
8065 * excluded the tRNS chunk.
8067 if (number_transparent == 1 &&
8068 mng_info->write_png_colortype < 4)
8070 ping_have_cheap_transparency = MagickTrue;
8072 if (number_semitransparent != 0)
8073 ping_have_cheap_transparency = MagickFalse;
8075 else if (image_colors == 0 || image_colors > 256 ||
8076 image->colormap == NULL)
8081 register const PixelPacket
8084 exception=(&image->exception);
8086 for (y=0; y < (ssize_t) image->rows; y++)
8088 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8090 if (q == (PixelPacket *) NULL)
8093 for (x=0; x < (ssize_t) image->columns; x++)
8095 if (q->opacity != TransparentOpacity &&
8096 (unsigned short) q->red == ping_trans_color.red &&
8097 (unsigned short) q->green == ping_trans_color.green &&
8098 (unsigned short) q->blue == ping_trans_color.blue)
8100 ping_have_cheap_transparency = MagickFalse;
8107 if (ping_have_cheap_transparency == MagickFalse)
8113 /* Assuming that image->colormap[0] is the one transparent color
8114 * and that all others are opaque.
8116 if (image_colors > 1)
8117 for (i=1; i<image_colors; i++)
8118 if (image->colormap[i].red == image->colormap[0].red &&
8119 image->colormap[i].green == image->colormap[0].green &&
8120 image->colormap[i].blue == image->colormap[0].blue)
8122 ping_have_cheap_transparency = MagickFalse;
8127 if (logging != MagickFalse)
8129 if (ping_have_cheap_transparency == MagickFalse)
8130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8131 " Cheap transparency is not possible.");
8134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8135 " Cheap transparency is possible.");
8139 ping_have_cheap_transparency = MagickFalse;
8141 image_depth=image->depth;
8143 quantum_info = (QuantumInfo *) NULL;
8145 image_colors=(int) image->colors;
8146 image_matte=image->matte;
8148 mng_info->IsPalette=image->storage_class == PseudoClass &&
8149 image_colors <= 256 && image->colormap != NULL;
8151 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8152 (image->colors == 0 || image->colormap == NULL))
8154 image_info=DestroyImageInfo(image_info);
8155 image=DestroyImage(image);
8156 (void) ThrowMagickException(&IMimage->exception,
8157 GetMagickModule(),CoderError,
8158 "Cannot write PNG8 or color-type 3; colormap is NULL",
8159 "`%s'",IMimage->filename);
8160 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8161 UnlockSemaphoreInfo(ping_semaphore);
8163 return(MagickFalse);
8167 Allocate the PNG structures
8169 #ifdef PNG_USER_MEM_SUPPORTED
8170 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8171 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8172 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8175 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8176 MagickPNGErrorHandler,MagickPNGWarningHandler);
8179 if (ping == (png_struct *) NULL)
8180 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8182 ping_info=png_create_info_struct(ping);
8184 if (ping_info == (png_info *) NULL)
8186 png_destroy_write_struct(&ping,(png_info **) NULL);
8187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8190 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8191 ping_pixels=(unsigned char *) NULL;
8193 if (setjmp(png_jmpbuf(ping)))
8199 if (image_info->verbose)
8200 (void) printf("PNG write has failed.\n");
8202 png_destroy_write_struct(&ping,&ping_info);
8203 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8204 UnlockSemaphoreInfo(ping_semaphore);
8206 if (ping_have_blob != MagickFalse)
8207 (void) CloseBlob(image);
8208 image_info=DestroyImageInfo(image_info);
8209 image=DestroyImage(image);
8210 return(MagickFalse);
8213 Prepare PNG for writing.
8215 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8216 if (mng_info->write_mng)
8217 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8220 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8221 if (mng_info->write_mng)
8222 png_permit_empty_plte(ping,MagickTrue);
8229 ping_width=(png_uint_32) image->columns;
8230 ping_height=(png_uint_32) image->rows;
8232 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8235 if (mng_info->write_png_depth != 0)
8236 image_depth=mng_info->write_png_depth;
8238 /* Adjust requested depth to next higher valid depth if necessary */
8239 if (image_depth > 8)
8242 if ((image_depth > 4) && (image_depth < 8))
8245 if (image_depth == 3)
8248 if (logging != MagickFalse)
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 " width=%.20g",(double) ping_width);
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " height=%.20g",(double) ping_height);
8254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255 " image_matte=%.20g",(double) image->matte);
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8257 " image->depth=%.20g",(double) image->depth);
8258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8259 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8262 save_image_depth=image_depth;
8263 ping_bit_depth=(png_byte) save_image_depth;
8266 #if defined(PNG_pHYs_SUPPORTED)
8267 if (ping_exclude_pHYs == MagickFalse)
8269 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8270 (!mng_info->write_mng || !mng_info->equal_physs))
8272 if (logging != MagickFalse)
8273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274 " Setting up pHYs chunk");
8276 if (image->units == PixelsPerInchResolution)
8278 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8279 ping_pHYs_x_resolution=
8280 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8281 ping_pHYs_y_resolution=
8282 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8285 else if (image->units == PixelsPerCentimeterResolution)
8287 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8288 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8289 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8294 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8295 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8296 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8299 if (logging != MagickFalse)
8300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8301 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8302 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8303 (int) ping_pHYs_unit_type);
8304 ping_have_pHYs = MagickTrue;
8309 if (ping_exclude_bKGD == MagickFalse)
8311 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8317 if (ping_bit_depth == 8)
8320 if (ping_bit_depth == 4)
8323 if (ping_bit_depth == 2)
8326 if (ping_bit_depth == 1)
8329 ping_background.red=(png_uint_16)
8330 (ScaleQuantumToShort(image->background_color.red) & mask);
8332 ping_background.green=(png_uint_16)
8333 (ScaleQuantumToShort(image->background_color.green) & mask);
8335 ping_background.blue=(png_uint_16)
8336 (ScaleQuantumToShort(image->background_color.blue) & mask);
8339 if (logging != MagickFalse)
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " Setting up bKGD chunk (1)");
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " ping_bit_depth=%d",ping_bit_depth);
8348 ping_have_bKGD = MagickTrue;
8352 Select the color type.
8357 if (mng_info->IsPalette && mng_info->write_png8)
8360 /* To do: make this a function cause it's used twice, except
8361 for reducing the sample depth from 8. */
8363 number_colors=image_colors;
8365 ping_have_tRNS=MagickFalse;
8370 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8372 if (logging != MagickFalse)
8373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8374 " Setting up PLTE chunk with %d colors (%d)",
8375 number_colors, image_colors);
8377 for (i=0; i < (ssize_t) number_colors; i++)
8379 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8380 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8381 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8382 if (logging != MagickFalse)
8383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384 #if MAGICKCORE_QUANTUM_DEPTH == 8
8385 " %3ld (%3d,%3d,%3d)",
8387 " %5ld (%5d,%5d,%5d)",
8389 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8393 ping_have_PLTE=MagickTrue;
8394 image_depth=ping_bit_depth;
8397 if (matte != MagickFalse)
8400 Identify which colormap entry is transparent.
8402 assert(number_colors <= 256);
8403 assert(image->colormap != NULL);
8405 for (i=0; i < (ssize_t) number_transparent; i++)
8406 ping_trans_alpha[i]=0;
8409 ping_num_trans=(unsigned short) (number_transparent +
8410 number_semitransparent);
8412 if (ping_num_trans == 0)
8413 ping_have_tRNS=MagickFalse;
8416 ping_have_tRNS=MagickTrue;
8419 if (ping_exclude_bKGD == MagickFalse)
8422 * Identify which colormap entry is the background color.
8425 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8426 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8429 ping_background.index=(png_byte) i;
8431 } /* end of write_png8 */
8433 else if (mng_info->write_png24)
8435 image_matte=MagickFalse;
8436 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8439 else if (mng_info->write_png32)
8441 image_matte=MagickTrue;
8442 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8445 else /* mng_info->write_pngNN not specified */
8447 image_depth=ping_bit_depth;
8449 if (mng_info->write_png_colortype != 0)
8451 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8453 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8454 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8455 image_matte=MagickTrue;
8458 image_matte=MagickFalse;
8460 if (logging != MagickFalse)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " PNG colortype %d was specified:",(int) ping_color_type);
8465 else /* write_png_colortype not specified */
8467 if (logging != MagickFalse)
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8469 " Selecting PNG colortype:");
8471 ping_color_type=(png_byte) ((matte != MagickFalse)?
8472 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8474 if (image_info->type == TrueColorType)
8476 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8477 image_matte=MagickFalse;
8480 if (image_info->type == TrueColorMatteType)
8482 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8483 image_matte=MagickTrue;
8486 if (image_info->type == PaletteType ||
8487 image_info->type == PaletteMatteType)
8488 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8490 if (mng_info->write_png_colortype == 0 &&
8491 (image_info->type == UndefinedType ||
8492 image_info->type == OptimizeType))
8494 if (ping_have_color == MagickFalse)
8496 if (image_matte == MagickFalse)
8498 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8499 image_matte=MagickFalse;
8504 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8505 image_matte=MagickTrue;
8510 if (image_matte == MagickFalse)
8512 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8513 image_matte=MagickFalse;
8518 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8519 image_matte=MagickTrue;
8526 if (logging != MagickFalse)
8527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8528 " Selected PNG colortype=%d",ping_color_type);
8530 if (ping_bit_depth < 8)
8532 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8533 ping_color_type == PNG_COLOR_TYPE_RGB ||
8534 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8538 old_bit_depth=ping_bit_depth;
8540 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8542 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8546 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8551 if (image->colors == 0)
8554 (void) ThrowMagickException(&image->exception,
8555 GetMagickModule(),CoderError,
8556 "image has 0 colors", "`%s'","");
8559 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8560 ping_bit_depth <<= 1;
8563 if (logging != MagickFalse)
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Number of colors: %.20g",(double) image_colors);
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " Tentative PNG bit depth: %d",ping_bit_depth);
8572 if (ping_bit_depth < (int) mng_info->write_png_depth)
8573 ping_bit_depth = mng_info->write_png_depth;
8576 image_depth=ping_bit_depth;
8578 if (logging != MagickFalse)
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " Tentative PNG color type: %.20g",(double) ping_color_type);
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " image_info->type: %.20g",(double) image_info->type);
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " image_depth: %.20g",(double) image_depth);
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8591 " image->depth: %.20g",(double) image->depth);
8593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8594 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8597 if (matte != MagickFalse)
8599 if (mng_info->IsPalette)
8601 if (mng_info->write_png_colortype == 0)
8603 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8605 if (ping_have_color != MagickFalse)
8606 ping_color_type=PNG_COLOR_TYPE_RGBA;
8610 * Determine if there is any transparent color.
8612 if (number_transparent + number_semitransparent == 0)
8615 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8618 image_matte=MagickFalse;
8620 if (mng_info->write_png_colortype == 0)
8621 ping_color_type&=0x03;
8631 if (ping_bit_depth == 8)
8634 if (ping_bit_depth == 4)
8637 if (ping_bit_depth == 2)
8640 if (ping_bit_depth == 1)
8643 ping_trans_color.red=(png_uint_16)
8644 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8646 ping_trans_color.green=(png_uint_16)
8647 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8649 ping_trans_color.blue=(png_uint_16)
8650 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8652 ping_trans_color.gray=(png_uint_16)
8653 (ScaleQuantumToShort(PixelIntensityToQuantum(
8654 image->colormap)) & mask);
8656 ping_trans_color.index=(png_byte) 0;
8658 ping_have_tRNS=MagickTrue;
8661 if (ping_have_tRNS != MagickFalse)
8664 * Determine if there is one and only one transparent color
8665 * and if so if it is fully transparent.
8667 if (ping_have_cheap_transparency == MagickFalse)
8668 ping_have_tRNS=MagickFalse;
8671 if (ping_have_tRNS != MagickFalse)
8673 if (mng_info->write_png_colortype == 0)
8674 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8676 if (image_depth == 8)
8678 ping_trans_color.red&=0xff;
8679 ping_trans_color.green&=0xff;
8680 ping_trans_color.blue&=0xff;
8681 ping_trans_color.gray&=0xff;
8687 if (image_depth == 8)
8689 ping_trans_color.red&=0xff;
8690 ping_trans_color.green&=0xff;
8691 ping_trans_color.blue&=0xff;
8692 ping_trans_color.gray&=0xff;
8699 if (ping_have_tRNS != MagickFalse)
8700 image_matte=MagickFalse;
8702 if ((mng_info->IsPalette) &&
8703 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8704 ping_have_color == MagickFalse &&
8705 (image_matte == MagickFalse || image_depth >= 8))
8709 if (image_matte != MagickFalse)
8710 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8712 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8714 ping_color_type=PNG_COLOR_TYPE_GRAY;
8716 if (save_image_depth == 16 && image_depth == 8)
8718 if (logging != MagickFalse)
8720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8721 " Scaling ping_trans_color (0)");
8723 ping_trans_color.gray*=0x0101;
8727 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8728 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8730 if ((image_colors == 0) || ((ssize_t) image_colors-1 > MaxColormapSize))
8731 image_colors=(int) (one << image_depth);
8733 if (image_depth > 8)
8739 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8741 if(!mng_info->write_png_depth)
8745 while ((int) (one << ping_bit_depth)
8746 < (ssize_t) image_colors)
8747 ping_bit_depth <<= 1;
8751 else if (ping_color_type ==
8752 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8753 mng_info->IsPalette)
8755 /* Check if grayscale is reducible */
8758 depth_4_ok=MagickTrue,
8759 depth_2_ok=MagickTrue,
8760 depth_1_ok=MagickTrue;
8762 for (i=0; i < (ssize_t) image_colors; i++)
8767 intensity=ScaleQuantumToChar(image->colormap[i].red);
8769 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8770 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8771 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8772 depth_2_ok=depth_1_ok=MagickFalse;
8773 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8774 depth_1_ok=MagickFalse;
8777 if (depth_1_ok && mng_info->write_png_depth <= 1)
8780 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8783 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8788 image_depth=ping_bit_depth;
8793 if (mng_info->IsPalette)
8795 number_colors=image_colors;
8797 if (image_depth <= 8)
8802 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8804 if (mng_info->have_write_global_plte && matte == MagickFalse)
8806 png_set_PLTE(ping,ping_info,NULL,0);
8808 if (logging != MagickFalse)
8809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " Setting up empty PLTE chunk");
8815 for (i=0; i < (ssize_t) number_colors; i++)
8817 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8818 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8819 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8822 if (logging != MagickFalse)
8823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824 " Setting up PLTE chunk with %d colors",
8827 ping_have_PLTE=MagickTrue;
8830 /* color_type is PNG_COLOR_TYPE_PALETTE */
8831 if (mng_info->write_png_depth == 0)
8839 while ((one << ping_bit_depth) < number_colors)
8840 ping_bit_depth <<= 1;
8845 if (matte != MagickFalse)
8848 * Set up trans_colors array.
8850 assert(number_colors <= 256);
8852 ping_num_trans=(unsigned short) (number_transparent +
8853 number_semitransparent);
8855 if (ping_num_trans == 0)
8856 ping_have_tRNS=MagickFalse;
8860 if (logging != MagickFalse)
8862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8863 " Scaling ping_trans_color (1)");
8865 ping_have_tRNS=MagickTrue;
8867 for (i=0; i < ping_num_trans; i++)
8869 ping_trans_alpha[i]= (png_byte) (255-
8870 ScaleQuantumToChar(image->colormap[i].opacity));
8880 if (image_depth < 8)
8883 if ((save_image_depth == 16) && (image_depth == 8))
8885 if (logging != MagickFalse)
8887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " Scaling ping_trans_color from (%d,%d,%d)",
8889 (int) ping_trans_color.red,
8890 (int) ping_trans_color.green,
8891 (int) ping_trans_color.blue);
8894 ping_trans_color.red*=0x0101;
8895 ping_trans_color.green*=0x0101;
8896 ping_trans_color.blue*=0x0101;
8897 ping_trans_color.gray*=0x0101;
8899 if (logging != MagickFalse)
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8903 (int) ping_trans_color.red,
8904 (int) ping_trans_color.green,
8905 (int) ping_trans_color.blue);
8910 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8911 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8914 Adjust background and transparency samples in sub-8-bit grayscale files.
8916 if (ping_bit_depth < 8 && ping_color_type ==
8917 PNG_COLOR_TYPE_GRAY)
8925 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8927 if (ping_exclude_bKGD == MagickFalse)
8930 ping_background.gray=(png_uint_16)
8931 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8933 if (logging != MagickFalse)
8934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8935 " Setting up bKGD chunk (2)");
8937 ping_have_bKGD = MagickTrue;
8940 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8941 ping_trans_color.gray));
8944 if (ping_exclude_bKGD == MagickFalse)
8946 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8949 Identify which colormap entry is the background color.
8952 number_colors=image_colors;
8954 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8955 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8958 ping_background.index=(png_byte) i;
8960 if (logging != MagickFalse)
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " Setting up bKGD chunk with index=%d",(int) i);
8966 if (i < (ssize_t) number_colors)
8968 ping_have_bKGD = MagickTrue;
8970 if (logging != MagickFalse)
8972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " background =(%d,%d,%d)",
8974 (int) ping_background.red,
8975 (int) ping_background.green,
8976 (int) ping_background.blue);
8980 else /* Can't happen */
8982 if (logging != MagickFalse)
8983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " No room in PLTE to add bKGD color");
8985 ping_have_bKGD = MagickFalse;
8990 if (logging != MagickFalse)
8991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " PNG color type: %d",ping_color_type);
8994 Initialize compression level and filtering.
8996 if (logging != MagickFalse)
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Setting up deflate compression");
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " Compression buffer size: 32768");
9005 png_set_compression_buffer_size(ping,32768L);
9007 if (logging != MagickFalse)
9008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9009 " Compression mem level: 9");
9011 png_set_compression_mem_level(ping, 9);
9013 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9021 level=(int) MagickMin((ssize_t) quality/10,9);
9023 if (logging != MagickFalse)
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " Compression level: %d",level);
9027 png_set_compression_level(ping,level);
9032 if (logging != MagickFalse)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " Compression strategy: Z_HUFFMAN_ONLY");
9036 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9039 if (logging != MagickFalse)
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9041 " Setting up filtering");
9043 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9044 /* This became available in libpng-1.0.9. Output must be a MNG. */
9045 if (mng_info->write_mng && ((quality % 10) == 7))
9047 if (logging != MagickFalse)
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
9051 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9055 if (logging != MagickFalse)
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 if ((quality % 10) > 5)
9065 base_filter=PNG_ALL_FILTERS;
9068 if ((quality % 10) != 5)
9069 base_filter=(int) quality % 10;
9072 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9073 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9075 base_filter=PNG_NO_FILTERS;
9078 base_filter=PNG_ALL_FILTERS;
9080 if (logging != MagickFalse)
9082 if (base_filter == PNG_ALL_FILTERS)
9083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084 " Base filter method: ADAPTIVE");
9086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9087 " Base filter method: NONE");
9090 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9093 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9094 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9096 ResetImageProfileIterator(image);
9097 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9099 profile=GetImageProfile(image,name);
9101 if (profile != (StringInfo *) NULL)
9103 #ifdef PNG_WRITE_iCCP_SUPPORTED
9104 if ((LocaleCompare(name,"ICC") == 0) ||
9105 (LocaleCompare(name,"ICM") == 0))
9108 if (ping_exclude_iCCP == MagickFalse)
9110 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9111 #if (PNG_LIBPNG_VER < 10500)
9112 (png_charp) GetStringInfoDatum(profile),
9114 (png_const_bytep) GetStringInfoDatum(profile),
9116 (png_uint_32) GetStringInfoLength(profile));
9122 if (ping_exclude_zCCP == MagickFalse)
9124 Magick_png_write_raw_profile(image_info,ping,ping_info,
9125 (unsigned char *) name,(unsigned char *) name,
9126 GetStringInfoDatum(profile),
9127 (png_uint_32) GetStringInfoLength(profile));
9131 if (logging != MagickFalse)
9132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9133 " Setting up text chunk with %s profile",name);
9135 name=GetNextImageProfile(image);
9139 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9140 if ((mng_info->have_write_global_srgb == 0) &&
9141 ((image->rendering_intent != UndefinedIntent) ||
9142 (image->colorspace == sRGBColorspace)))
9144 if (ping_exclude_sRGB == MagickFalse)
9147 Note image rendering intent.
9149 if (logging != MagickFalse)
9150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9151 " Setting up sRGB chunk");
9153 (void) png_set_sRGB(ping,ping_info,(
9154 Magick_RenderingIntent_to_PNG_RenderingIntent(
9155 image->rendering_intent)));
9157 if (ping_exclude_gAMA == MagickFalse)
9158 png_set_gAMA(ping,ping_info,0.45455);
9162 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9165 if (ping_exclude_gAMA == MagickFalse &&
9166 (ping_exclude_sRGB == MagickFalse ||
9167 (image->gamma < .45 || image->gamma > .46)))
9169 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9173 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9175 if (logging != MagickFalse)
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " Setting up gAMA chunk");
9179 png_set_gAMA(ping,ping_info,image->gamma);
9183 if (ping_exclude_cHRM == MagickFalse)
9185 if ((mng_info->have_write_global_chrm == 0) &&
9186 (image->chromaticity.red_primary.x != 0.0))
9189 Note image chromaticity.
9190 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9198 wp=image->chromaticity.white_point;
9199 rp=image->chromaticity.red_primary;
9200 gp=image->chromaticity.green_primary;
9201 bp=image->chromaticity.blue_primary;
9203 if (logging != MagickFalse)
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205 " Setting up cHRM chunk");
9207 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9213 ping_interlace_method=image_info->interlace != NoInterlace;
9215 if (mng_info->write_mng)
9216 png_set_sig_bytes(ping,8);
9218 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9220 if (mng_info->write_png_colortype != 0)
9222 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9223 if (ping_have_color != MagickFalse)
9225 ping_color_type = PNG_COLOR_TYPE_RGB;
9227 if (ping_bit_depth < 8)
9231 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9232 if (ping_have_color != MagickFalse)
9233 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9236 if (ping_need_colortype_warning != MagickFalse ||
9237 ((mng_info->write_png_depth &&
9238 (int) mng_info->write_png_depth != ping_bit_depth) ||
9239 (mng_info->write_png_colortype &&
9240 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9241 mng_info->write_png_colortype != 7 &&
9242 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9244 if (logging != MagickFalse)
9246 if (ping_need_colortype_warning != MagickFalse)
9248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9249 " Image has transparency but tRNS chunk was excluded");
9252 if (mng_info->write_png_depth)
9254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9255 " Defined PNG:bit-depth=%u, Computed depth=%u",
9256 mng_info->write_png_depth,
9260 if (mng_info->write_png_colortype)
9262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9263 " Defined PNG:color-type=%u, Computed color type=%u",
9264 mng_info->write_png_colortype-1,
9270 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9273 if (image_matte != MagickFalse && image->matte == MagickFalse)
9275 /* Add an opaque matte channel */
9276 image->matte = MagickTrue;
9277 (void) SetImageOpacity(image,0);
9279 if (logging != MagickFalse)
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9281 " Added an opaque matte channel");
9284 if (number_transparent != 0 || number_semitransparent != 0)
9286 if (ping_color_type < 4)
9288 ping_have_tRNS=MagickTrue;
9289 if (logging != MagickFalse)
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9291 " Setting ping_have_tRNS=MagickTrue.");
9295 if (logging != MagickFalse)
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " Writing PNG header chunks");
9299 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9300 ping_bit_depth,ping_color_type,
9301 ping_interlace_method,ping_compression_method,
9302 ping_filter_method);
9304 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9306 png_set_PLTE(ping,ping_info,palette,number_colors);
9308 if (logging != MagickFalse)
9310 for (i=0; i< (ssize_t) number_colors; i++)
9312 if (i < ping_num_trans)
9313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9314 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9316 (int) palette[i].red,
9317 (int) palette[i].green,
9318 (int) palette[i].blue,
9320 (int) ping_trans_alpha[i]);
9322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9323 " PLTE[%d] = (%d,%d,%d)",
9325 (int) palette[i].red,
9326 (int) palette[i].green,
9327 (int) palette[i].blue);
9332 if (ping_exclude_bKGD == MagickFalse)
9334 if (ping_have_bKGD != MagickFalse)
9335 png_set_bKGD(ping,ping_info,&ping_background);
9338 if (ping_exclude_pHYs == MagickFalse)
9340 if (ping_have_pHYs != MagickFalse)
9342 png_set_pHYs(ping,ping_info,
9343 ping_pHYs_x_resolution,
9344 ping_pHYs_y_resolution,
9345 ping_pHYs_unit_type);
9349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350 " Setting up pHYs chunk");
9351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9352 " x_resolution=%lu",
9353 (unsigned long) ping_pHYs_x_resolution);
9354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355 " y_resolution=%lu",
9356 (unsigned long) ping_pHYs_y_resolution);
9357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359 (unsigned long) ping_pHYs_unit_type);
9364 #if defined(PNG_oFFs_SUPPORTED)
9365 if (ping_exclude_oFFs == MagickFalse)
9367 if (image->page.x || image->page.y)
9369 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9370 (png_int_32) image->page.y, 0);
9372 if (logging != MagickFalse)
9373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9374 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9375 (int) image->page.x, (int) image->page.y);
9380 if (mng_info->need_blob != MagickFalse)
9382 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9384 png_error(ping,"WriteBlob Failed");
9386 ping_have_blob=MagickTrue;
9389 png_write_info_before_PLTE(ping, ping_info);
9391 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9393 if (logging != MagickFalse)
9395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9396 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9399 if (ping_color_type == 3)
9400 (void) png_set_tRNS(ping, ping_info,
9407 (void) png_set_tRNS(ping, ping_info,
9412 if (logging != MagickFalse)
9414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9415 " tRNS color =(%d,%d,%d)",
9416 (int) ping_trans_color.red,
9417 (int) ping_trans_color.green,
9418 (int) ping_trans_color.blue);
9423 /* write any png-chunk-b profiles */
9424 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9426 png_write_info(ping,ping_info);
9428 /* write any PNG-chunk-m profiles */
9429 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9431 if (ping_exclude_vpAg == MagickFalse)
9433 if ((image->page.width != 0 && image->page.width != image->columns) ||
9434 (image->page.height != 0 && image->page.height != image->rows))
9439 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9440 PNGType(chunk,mng_vpAg);
9441 LogPNGChunk(logging,mng_vpAg,9L);
9442 PNGLong(chunk+4,(png_uint_32) image->page.width);
9443 PNGLong(chunk+8,(png_uint_32) image->page.height);
9444 chunk[12]=0; /* unit = pixels */
9445 (void) WriteBlob(image,13,chunk);
9446 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9450 #if (PNG_LIBPNG_VER == 10206)
9451 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9452 #define PNG_HAVE_IDAT 0x04
9453 ping->mode |= PNG_HAVE_IDAT;
9454 #undef PNG_HAVE_IDAT
9457 png_set_packing(ping);
9461 rowbytes=image->columns;
9462 if (image_depth > 8)
9464 switch (ping_color_type)
9466 case PNG_COLOR_TYPE_RGB:
9470 case PNG_COLOR_TYPE_GRAY_ALPHA:
9474 case PNG_COLOR_TYPE_RGBA:
9482 if (logging != MagickFalse)
9484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9485 " Writing PNG image data");
9487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9488 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9490 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9491 sizeof(*ping_pixels));
9493 if (ping_pixels == (unsigned char *) NULL)
9494 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9497 Initialize image scanlines.
9499 if (setjmp(png_jmpbuf(ping)))
9505 if (image_info->verbose)
9506 (void) printf("PNG write has failed.\n");
9508 png_destroy_write_struct(&ping,&ping_info);
9509 if (quantum_info != (QuantumInfo *) NULL)
9510 quantum_info=DestroyQuantumInfo(quantum_info);
9511 if (ping_pixels != (unsigned char *) NULL)
9512 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9513 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9514 UnlockSemaphoreInfo(ping_semaphore);
9516 if (ping_have_blob != MagickFalse)
9517 (void) CloseBlob(image);
9518 image_info=DestroyImageInfo(image_info);
9519 image=DestroyImage(image);
9520 return(MagickFalse);
9522 quantum_info=AcquireQuantumInfo(image_info,image);
9523 if (quantum_info == (QuantumInfo *) NULL)
9524 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9525 quantum_info->format=UndefinedQuantumFormat;
9526 quantum_info->depth=image_depth;
9527 num_passes=png_set_interlace_handling(ping);
9529 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9530 !mng_info->write_png32) &&
9531 (mng_info->IsPalette ||
9532 (image_info->type == BilevelType)) &&
9533 image_matte == MagickFalse &&
9534 ping_have_non_bw == MagickFalse)
9536 /* Palette, Bilevel, or Opaque Monochrome */
9537 register const PixelPacket
9540 quantum_info->depth=8;
9541 for (pass=0; pass < num_passes; pass++)
9544 Convert PseudoClass image to a PNG monochrome image.
9546 for (y=0; y < (ssize_t) image->rows; y++)
9548 if (logging != MagickFalse && y == 0)
9549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9550 " Writing row of pixels (0)");
9552 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9554 if (p == (const PixelPacket *) NULL)
9557 if (mng_info->IsPalette)
9559 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9560 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9561 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9562 mng_info->write_png_depth &&
9563 mng_info->write_png_depth != old_bit_depth)
9565 /* Undo pixel scaling */
9566 for (i=0; i < (ssize_t) image->columns; i++)
9567 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9568 >> (8-old_bit_depth));
9574 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9575 quantum_info,RedQuantum,ping_pixels,&image->exception);
9578 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9579 for (i=0; i < (ssize_t) image->columns; i++)
9580 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9583 if (logging != MagickFalse && y == 0)
9584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9585 " Writing row of pixels (1)");
9587 png_write_row(ping,ping_pixels);
9589 if (image->previous == (Image *) NULL)
9591 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9592 if (status == MagickFalse)
9598 else /* Not Palette, Bilevel, or Opaque Monochrome */
9600 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9601 !mng_info->write_png32) &&
9602 (image_matte != MagickFalse ||
9603 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9604 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9606 register const PixelPacket
9609 for (pass=0; pass < num_passes; pass++)
9612 for (y=0; y < (ssize_t) image->rows; y++)
9614 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9616 if (p == (const PixelPacket *) NULL)
9619 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9621 if (mng_info->IsPalette)
9622 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9623 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9626 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9627 quantum_info,RedQuantum,ping_pixels,&image->exception);
9629 if (logging != MagickFalse && y == 0)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Writing GRAY PNG pixels (2)");
9634 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9636 if (logging != MagickFalse && y == 0)
9637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9638 " Writing GRAY_ALPHA PNG pixels (2)");
9640 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9641 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9644 if (logging != MagickFalse && y == 0)
9645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9646 " Writing row of pixels (2)");
9648 png_write_row(ping,ping_pixels);
9651 if (image->previous == (Image *) NULL)
9653 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9654 if (status == MagickFalse)
9662 register const PixelPacket
9665 for (pass=0; pass < num_passes; pass++)
9667 if ((image_depth > 8) || (mng_info->write_png24 ||
9668 mng_info->write_png32 ||
9669 (!mng_info->write_png8 && !mng_info->IsPalette)))
9671 for (y=0; y < (ssize_t) image->rows; y++)
9673 p=GetVirtualPixels(image,0,y,image->columns,1,
9676 if (p == (const PixelPacket *) NULL)
9679 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9681 if (image->storage_class == DirectClass)
9682 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9683 quantum_info,RedQuantum,ping_pixels,&image->exception);
9686 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9687 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9690 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9692 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9693 quantum_info,GrayAlphaQuantum,ping_pixels,
9696 if (logging != MagickFalse && y == 0)
9697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9698 " Writing GRAY_ALPHA PNG pixels (3)");
9701 else if (image_matte != MagickFalse)
9702 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9703 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9706 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9707 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9709 if (logging != MagickFalse && y == 0)
9710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711 " Writing row of pixels (3)");
9713 png_write_row(ping,ping_pixels);
9718 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9719 mng_info->write_png32 ||
9720 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9722 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9723 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9725 if (logging != MagickFalse)
9726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9727 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9729 quantum_info->depth=8;
9733 for (y=0; y < (ssize_t) image->rows; y++)
9735 if (logging != MagickFalse && y == 0)
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9739 p=GetVirtualPixels(image,0,y,image->columns,1,
9742 if (p == (const PixelPacket *) NULL)
9745 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9747 quantum_info->depth=image->depth;
9749 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9750 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9753 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9755 if (logging != MagickFalse && y == 0)
9756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9757 " Writing GRAY_ALPHA PNG pixels (4)");
9759 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9760 quantum_info,GrayAlphaQuantum,ping_pixels,
9766 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9767 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9769 if (logging != MagickFalse && y <= 2)
9771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9772 " Writing row of non-gray pixels (4)");
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9776 (int)ping_pixels[0],(int)ping_pixels[1]);
9779 png_write_row(ping,ping_pixels);
9783 if (image->previous == (Image *) NULL)
9785 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9786 if (status == MagickFalse)
9793 if (quantum_info != (QuantumInfo *) NULL)
9794 quantum_info=DestroyQuantumInfo(quantum_info);
9796 if (logging != MagickFalse)
9798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799 " Wrote PNG image data");
9801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9802 " Width: %.20g",(double) ping_width);
9804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9805 " Height: %.20g",(double) ping_height);
9807 if (mng_info->write_png_depth)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " PNG bit-depth written: %d",ping_bit_depth);
9816 if (mng_info->write_png_colortype)
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9819 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " PNG color-type written: %d",ping_color_type);
9825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " PNG Interlace method: %d",ping_interlace_method);
9829 Generate text chunks after IDAT.
9831 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9833 ResetImagePropertyIterator(image);
9834 property=GetNextImageProperty(image);
9835 while (property != (const char *) NULL)
9840 value=GetImageProperty(image,property);
9842 /* Don't write any "png:" properties; those are just for "identify" */
9843 if (LocaleNCompare(property,"png:",4) != 0 &&
9845 /* Suppress density and units if we wrote a pHYs chunk */
9846 (ping_exclude_pHYs != MagickFalse ||
9847 LocaleCompare(property,"density") != 0 ||
9848 LocaleCompare(property,"units") != 0) &&
9850 /* Suppress the IM-generated Date:create and Date:modify */
9851 (ping_exclude_date == MagickFalse ||
9852 LocaleNCompare(property, "Date:",5) != 0))
9854 if (value != (const char *) NULL)
9856 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9857 text[0].key=(char *) property;
9858 text[0].text=(char *) value;
9859 text[0].text_length=strlen(value);
9861 if (ping_exclude_tEXt != MagickFalse)
9862 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9864 else if (ping_exclude_zTXt != MagickFalse)
9865 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9869 text[0].compression=image_info->compression == NoCompression ||
9870 (image_info->compression == UndefinedCompression &&
9871 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9872 PNG_TEXT_COMPRESSION_zTXt ;
9875 if (logging != MagickFalse)
9877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9878 " Setting up text chunk");
9880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9881 " keyword: %s",text[0].key);
9884 png_set_text(ping,ping_info,text,1);
9885 png_free(ping,text);
9888 property=GetNextImageProperty(image);
9892 /* write any PNG-chunk-e profiles */
9893 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9895 if (logging != MagickFalse)
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " Writing PNG end info");
9899 png_write_end(ping,ping_info);
9901 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9903 if (mng_info->page.x || mng_info->page.y ||
9904 (ping_width != mng_info->page.width) ||
9905 (ping_height != mng_info->page.height))
9911 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9913 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9914 PNGType(chunk,mng_FRAM);
9915 LogPNGChunk(logging,mng_FRAM,27L);
9917 chunk[5]=0; /* frame name separator (no name) */
9918 chunk[6]=1; /* flag for changing delay, for next frame only */
9919 chunk[7]=0; /* flag for changing frame timeout */
9920 chunk[8]=1; /* flag for changing frame clipping for next frame */
9921 chunk[9]=0; /* flag for changing frame sync_id */
9922 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9923 chunk[14]=0; /* clipping boundaries delta type */
9924 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9926 (png_uint_32) (mng_info->page.x + ping_width));
9927 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9929 (png_uint_32) (mng_info->page.y + ping_height));
9930 (void) WriteBlob(image,31,chunk);
9931 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9932 mng_info->old_framing_mode=4;
9933 mng_info->framing_mode=1;
9937 mng_info->framing_mode=3;
9939 if (mng_info->write_mng && !mng_info->need_fram &&
9940 ((int) image->dispose == 3))
9941 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9942 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9943 "`%s'",image->filename);
9949 png_destroy_write_struct(&ping,&ping_info);
9951 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9953 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9954 UnlockSemaphoreInfo(ping_semaphore);
9957 if (ping_have_blob != MagickFalse)
9958 (void) CloseBlob(image);
9960 image_info=DestroyImageInfo(image_info);
9961 image=DestroyImage(image);
9963 /* Store bit depth actually written */
9964 s[0]=(char) ping_bit_depth;
9967 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9969 if (logging != MagickFalse)
9970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9971 " exit WriteOnePNGImage()");
9974 /* End write one PNG image */
9978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9982 % W r i t e P N G I m a g e %
9986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9988 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9989 % Multiple-image Network Graphics (MNG) image file.
9991 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9993 % The format of the WritePNGImage method is:
9995 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9997 % A description of each parameter follows:
9999 % o image_info: the image info.
10001 % o image: The image.
10003 % Returns MagickTrue on success, MagickFalse on failure.
10005 % Communicating with the PNG encoder:
10007 % While the datastream written is always in PNG format and normally would
10008 % be given the "png" file extension, this method also writes the following
10009 % pseudo-formats which are subsets of PNG:
10011 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10012 % a depth greater than 8, the depth is reduced. If transparency
10013 % is present, the tRNS chunk must only have values 0 and 255
10014 % (i.e., transparency is binary: fully opaque or fully
10015 % transparent). If other values are present they will be
10016 % 50%-thresholded to binary transparency. If more than 256
10017 % colors are present, they will be quantized to the 4-4-4-1,
10018 % 3-3-3-1, or 3-3-2-1 palette.
10020 % If you want better quantization or dithering of the colors
10021 % or alpha than that, you need to do it before calling the
10022 % PNG encoder. The pixels contain 8-bit indices even if
10023 % they could be represented with 1, 2, or 4 bits. Grayscale
10024 % images will be written as indexed PNG files even though the
10025 % PNG grayscale type might be slightly more efficient. Please
10026 % note that writing to the PNG8 format may result in loss
10027 % of color and alpha data.
10029 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10030 % chunk can be present to convey binary transparency by naming
10031 % one of the colors as transparent. The only loss incurred
10032 % is reduction of sample depth to 8. If the image has more
10033 % than one transparent color, has semitransparent pixels, or
10034 % has an opaque pixel with the same RGB components as the
10035 % transparent color, an image is not written.
10037 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10038 % transparency is permitted, i.e., the alpha sample for
10039 % each pixel can have any value from 0 to 255. The alpha
10040 % channel is present even if the image is fully opaque.
10041 % The only loss in data is the reduction of the sample depth
10044 % o -define: For more precise control of the PNG output, you can use the
10045 % Image options "png:bit-depth" and "png:color-type". These
10046 % can be set from the commandline with "-define" and also
10047 % from the application programming interfaces. The options
10048 % are case-independent and are converted to lowercase before
10049 % being passed to this encoder.
10051 % png:color-type can be 0, 2, 3, 4, or 6.
10053 % When png:color-type is 0 (Grayscale), png:bit-depth can
10054 % be 1, 2, 4, 8, or 16.
10056 % When png:color-type is 2 (RGB), png:bit-depth can
10059 % When png:color-type is 3 (Indexed), png:bit-depth can
10060 % be 1, 2, 4, or 8. This refers to the number of bits
10061 % used to store the index. The color samples always have
10062 % bit-depth 8 in indexed PNG files.
10064 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10065 % png:bit-depth can be 8 or 16.
10067 % If the image cannot be written without loss with the requested bit-depth
10068 % and color-type, a PNG file will not be written, and the encoder will
10069 % return MagickFalse.
10071 % Since image encoders should not be responsible for the "heavy lifting",
10072 % the user should make sure that ImageMagick has already reduced the
10073 % image depth and number of colors and limit transparency to binary
10074 % transparency prior to attempting to write the image with depth, color,
10075 % or transparency limitations.
10077 % TODO: Enforce the previous paragraph.
10079 % Note that another definition, "png:bit-depth-written" exists, but it
10080 % is not intended for external use. It is only used internally by the
10081 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10083 % It is possible to request that the PNG encoder write previously-formatted
10084 % ancillary chunks in the output PNG file, using the "-profile" commandline
10085 % option as shown below or by setting the profile via a programming
10088 % -profile PNG-chunk-x:<file>
10090 % where x is a location flag and <file> is a file containing the chunk
10091 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10092 % This encoder will compute the chunk length and CRC, so those must not
10093 % be included in the file.
10095 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10096 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10097 % of the same type, then add a short unique string after the "x" to prevent
10098 % subsequent profiles from overwriting the preceding ones, e.g.,
10100 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10102 % As of version 6.6.6 the following optimizations are always done:
10104 % o 32-bit depth is reduced to 16.
10105 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10106 % high byte and low byte are identical.
10107 % o Palette is sorted to remove unused entries and to put a
10108 % transparent color first, if BUILD_PNG_PALETTE is defined.
10109 % o Opaque matte channel is removed when writing an indexed PNG.
10110 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10111 % this can be done without loss and a larger bit depth N was not
10112 % requested via the "-define PNG:bit-depth=N" option.
10113 % o If matte channel is present but only one transparent color is
10114 % present, RGB+tRNS is written instead of RGBA
10115 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10116 % was requested when converting an opaque image).
10118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10120 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10126 have_mng_structure,
10142 assert(image_info != (const ImageInfo *) NULL);
10143 assert(image_info->signature == MagickSignature);
10144 assert(image != (Image *) NULL);
10145 assert(image->signature == MagickSignature);
10146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10147 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10149 Allocate a MngInfo structure.
10151 have_mng_structure=MagickFalse;
10152 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10154 if (mng_info == (MngInfo *) NULL)
10155 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10158 Initialize members of the MngInfo structure.
10160 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10161 mng_info->image=image;
10162 mng_info->equal_backgrounds=MagickTrue;
10163 have_mng_structure=MagickTrue;
10165 /* See if user has requested a specific PNG subformat */
10167 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10168 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10169 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10171 if (mng_info->write_png8)
10173 mng_info->write_png_colortype = /* 3 */ 4;
10174 mng_info->write_png_depth = 8;
10178 if (mng_info->write_png24)
10180 mng_info->write_png_colortype = /* 2 */ 3;
10181 mng_info->write_png_depth = 8;
10184 if (image->matte == MagickTrue)
10185 (void) SetImageType(image,TrueColorMatteType);
10188 (void) SetImageType(image,TrueColorType);
10190 (void) SyncImage(image);
10193 if (mng_info->write_png32)
10195 mng_info->write_png_colortype = /* 6 */ 7;
10196 mng_info->write_png_depth = 8;
10199 if (image->matte == MagickTrue)
10200 (void) SetImageType(image,TrueColorMatteType);
10203 (void) SetImageType(image,TrueColorType);
10205 (void) SyncImage(image);
10208 value=GetImageOption(image_info,"png:bit-depth");
10210 if (value != (char *) NULL)
10212 if (LocaleCompare(value,"1") == 0)
10213 mng_info->write_png_depth = 1;
10215 else if (LocaleCompare(value,"2") == 0)
10216 mng_info->write_png_depth = 2;
10218 else if (LocaleCompare(value,"4") == 0)
10219 mng_info->write_png_depth = 4;
10221 else if (LocaleCompare(value,"8") == 0)
10222 mng_info->write_png_depth = 8;
10224 else if (LocaleCompare(value,"16") == 0)
10225 mng_info->write_png_depth = 16;
10228 (void) ThrowMagickException(&image->exception,
10229 GetMagickModule(),CoderWarning,
10230 "ignoring invalid defined png:bit-depth",
10233 if (logging != MagickFalse)
10234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10235 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10238 value=GetImageOption(image_info,"png:color-type");
10240 if (value != (char *) NULL)
10242 /* We must store colortype+1 because 0 is a valid colortype */
10243 if (LocaleCompare(value,"0") == 0)
10244 mng_info->write_png_colortype = 1;
10246 else if (LocaleCompare(value,"2") == 0)
10247 mng_info->write_png_colortype = 3;
10249 else if (LocaleCompare(value,"3") == 0)
10250 mng_info->write_png_colortype = 4;
10252 else if (LocaleCompare(value,"4") == 0)
10253 mng_info->write_png_colortype = 5;
10255 else if (LocaleCompare(value,"6") == 0)
10256 mng_info->write_png_colortype = 7;
10259 (void) ThrowMagickException(&image->exception,
10260 GetMagickModule(),CoderWarning,
10261 "ignoring invalid defined png:color-type",
10264 if (logging != MagickFalse)
10265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10269 /* Check for chunks to be excluded:
10271 * The default is to not exclude any known chunks except for any
10272 * listed in the "unused_chunks" array, above.
10274 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10275 * define (in the image properties or in the image artifacts)
10276 * or via a mng_info member. For convenience, in addition
10277 * to or instead of a comma-separated list of chunks, the
10278 * "exclude-chunk" string can be simply "all" or "none".
10280 * The exclude-chunk define takes priority over the mng_info.
10282 * A "PNG:include-chunk" define takes priority over both the
10283 * mng_info and the "PNG:exclude-chunk" define. Like the
10284 * "exclude-chunk" string, it can define "all" or "none" as
10285 * well as a comma-separated list. Chunks that are unknown to
10286 * ImageMagick are always excluded, regardless of their "copy-safe"
10287 * status according to the PNG specification, and even if they
10288 * appear in the "include-chunk" list.
10290 * Finally, all chunks listed in the "unused_chunks" array are
10291 * automatically excluded, regardless of the other instructions
10294 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10295 * will not be written and the gAMA chunk will only be written if it
10296 * is not between .45 and .46, or approximately (1.0/2.2).
10298 * If you exclude tRNS and the image has transparency, the colortype
10299 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10301 * The -strip option causes StripImage() to set the png:include-chunk
10302 * artifact to "none,gama".
10305 mng_info->ping_exclude_bKGD=MagickFalse;
10306 mng_info->ping_exclude_cHRM=MagickFalse;
10307 mng_info->ping_exclude_date=MagickFalse;
10308 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10309 mng_info->ping_exclude_gAMA=MagickFalse;
10310 mng_info->ping_exclude_iCCP=MagickFalse;
10311 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10312 mng_info->ping_exclude_oFFs=MagickFalse;
10313 mng_info->ping_exclude_pHYs=MagickFalse;
10314 mng_info->ping_exclude_sRGB=MagickFalse;
10315 mng_info->ping_exclude_tEXt=MagickFalse;
10316 mng_info->ping_exclude_tRNS=MagickFalse;
10317 mng_info->ping_exclude_vpAg=MagickFalse;
10318 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10319 mng_info->ping_exclude_zTXt=MagickFalse;
10321 mng_info->ping_preserve_colormap=MagickFalse;
10323 value=GetImageArtifact(image,"png:preserve-colormap");
10325 value=GetImageOption(image_info,"png:preserve-colormap");
10327 mng_info->ping_preserve_colormap=MagickTrue;
10329 excluding=MagickFalse;
10331 for (source=0; source<1; source++)
10335 value=GetImageArtifact(image,"png:exclude-chunk");
10338 value=GetImageArtifact(image,"png:exclude-chunks");
10342 value=GetImageOption(image_info,"png:exclude-chunk");
10345 value=GetImageOption(image_info,"png:exclude-chunks");
10354 excluding=MagickTrue;
10356 if (logging != MagickFalse)
10359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " png:exclude-chunk=%s found in image artifacts.\n", value);
10362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " png:exclude-chunk=%s found in image properties.\n", value);
10366 last=strlen(value);
10368 for (i=0; i<(int) last; i+=5)
10371 if (LocaleNCompare(value+i,"all",3) == 0)
10373 mng_info->ping_exclude_bKGD=MagickTrue;
10374 mng_info->ping_exclude_cHRM=MagickTrue;
10375 mng_info->ping_exclude_date=MagickTrue;
10376 mng_info->ping_exclude_EXIF=MagickTrue;
10377 mng_info->ping_exclude_gAMA=MagickTrue;
10378 mng_info->ping_exclude_iCCP=MagickTrue;
10379 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10380 mng_info->ping_exclude_oFFs=MagickTrue;
10381 mng_info->ping_exclude_pHYs=MagickTrue;
10382 mng_info->ping_exclude_sRGB=MagickTrue;
10383 mng_info->ping_exclude_tEXt=MagickTrue;
10384 mng_info->ping_exclude_tRNS=MagickTrue;
10385 mng_info->ping_exclude_vpAg=MagickTrue;
10386 mng_info->ping_exclude_zCCP=MagickTrue;
10387 mng_info->ping_exclude_zTXt=MagickTrue;
10391 if (LocaleNCompare(value+i,"none",4) == 0)
10393 mng_info->ping_exclude_bKGD=MagickFalse;
10394 mng_info->ping_exclude_cHRM=MagickFalse;
10395 mng_info->ping_exclude_date=MagickFalse;
10396 mng_info->ping_exclude_EXIF=MagickFalse;
10397 mng_info->ping_exclude_gAMA=MagickFalse;
10398 mng_info->ping_exclude_iCCP=MagickFalse;
10399 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10400 mng_info->ping_exclude_oFFs=MagickFalse;
10401 mng_info->ping_exclude_pHYs=MagickFalse;
10402 mng_info->ping_exclude_sRGB=MagickFalse;
10403 mng_info->ping_exclude_tEXt=MagickFalse;
10404 mng_info->ping_exclude_tRNS=MagickFalse;
10405 mng_info->ping_exclude_vpAg=MagickFalse;
10406 mng_info->ping_exclude_zCCP=MagickFalse;
10407 mng_info->ping_exclude_zTXt=MagickFalse;
10410 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10411 mng_info->ping_exclude_bKGD=MagickTrue;
10413 if (LocaleNCompare(value+i,"chrm",4) == 0)
10414 mng_info->ping_exclude_cHRM=MagickTrue;
10416 if (LocaleNCompare(value+i,"date",4) == 0)
10417 mng_info->ping_exclude_date=MagickTrue;
10419 if (LocaleNCompare(value+i,"exif",4) == 0)
10420 mng_info->ping_exclude_EXIF=MagickTrue;
10422 if (LocaleNCompare(value+i,"gama",4) == 0)
10423 mng_info->ping_exclude_gAMA=MagickTrue;
10425 if (LocaleNCompare(value+i,"iccp",4) == 0)
10426 mng_info->ping_exclude_iCCP=MagickTrue;
10429 if (LocaleNCompare(value+i,"itxt",4) == 0)
10430 mng_info->ping_exclude_iTXt=MagickTrue;
10433 if (LocaleNCompare(value+i,"gama",4) == 0)
10434 mng_info->ping_exclude_gAMA=MagickTrue;
10436 if (LocaleNCompare(value+i,"offs",4) == 0)
10437 mng_info->ping_exclude_oFFs=MagickTrue;
10439 if (LocaleNCompare(value+i,"phys",4) == 0)
10440 mng_info->ping_exclude_pHYs=MagickTrue;
10442 if (LocaleNCompare(value+i,"srgb",4) == 0)
10443 mng_info->ping_exclude_sRGB=MagickTrue;
10445 if (LocaleNCompare(value+i,"text",4) == 0)
10446 mng_info->ping_exclude_tEXt=MagickTrue;
10448 if (LocaleNCompare(value+i,"trns",4) == 0)
10449 mng_info->ping_exclude_tRNS=MagickTrue;
10451 if (LocaleNCompare(value+i,"vpag",4) == 0)
10452 mng_info->ping_exclude_vpAg=MagickTrue;
10454 if (LocaleNCompare(value+i,"zccp",4) == 0)
10455 mng_info->ping_exclude_zCCP=MagickTrue;
10457 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10458 mng_info->ping_exclude_zTXt=MagickTrue;
10464 for (source=0; source<1; source++)
10468 value=GetImageArtifact(image,"png:include-chunk");
10471 value=GetImageArtifact(image,"png:include-chunks");
10475 value=GetImageOption(image_info,"png:include-chunk");
10478 value=GetImageOption(image_info,"png:include-chunks");
10486 excluding=MagickTrue;
10488 if (logging != MagickFalse)
10491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10492 " png:include-chunk=%s found in image artifacts.\n", value);
10494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10495 " png:include-chunk=%s found in image properties.\n", value);
10498 last=strlen(value);
10500 for (i=0; i<(int) last; i+=5)
10502 if (LocaleNCompare(value+i,"all",3) == 0)
10504 mng_info->ping_exclude_bKGD=MagickFalse;
10505 mng_info->ping_exclude_cHRM=MagickFalse;
10506 mng_info->ping_exclude_date=MagickFalse;
10507 mng_info->ping_exclude_EXIF=MagickFalse;
10508 mng_info->ping_exclude_gAMA=MagickFalse;
10509 mng_info->ping_exclude_iCCP=MagickFalse;
10510 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10511 mng_info->ping_exclude_oFFs=MagickFalse;
10512 mng_info->ping_exclude_pHYs=MagickFalse;
10513 mng_info->ping_exclude_sRGB=MagickFalse;
10514 mng_info->ping_exclude_tEXt=MagickFalse;
10515 mng_info->ping_exclude_tRNS=MagickFalse;
10516 mng_info->ping_exclude_vpAg=MagickFalse;
10517 mng_info->ping_exclude_zCCP=MagickFalse;
10518 mng_info->ping_exclude_zTXt=MagickFalse;
10522 if (LocaleNCompare(value+i,"none",4) == 0)
10524 mng_info->ping_exclude_bKGD=MagickTrue;
10525 mng_info->ping_exclude_cHRM=MagickTrue;
10526 mng_info->ping_exclude_date=MagickTrue;
10527 mng_info->ping_exclude_EXIF=MagickTrue;
10528 mng_info->ping_exclude_gAMA=MagickTrue;
10529 mng_info->ping_exclude_iCCP=MagickTrue;
10530 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10531 mng_info->ping_exclude_oFFs=MagickTrue;
10532 mng_info->ping_exclude_pHYs=MagickTrue;
10533 mng_info->ping_exclude_sRGB=MagickTrue;
10534 mng_info->ping_exclude_tEXt=MagickTrue;
10535 mng_info->ping_exclude_tRNS=MagickTrue;
10536 mng_info->ping_exclude_vpAg=MagickTrue;
10537 mng_info->ping_exclude_zCCP=MagickTrue;
10538 mng_info->ping_exclude_zTXt=MagickTrue;
10541 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10542 mng_info->ping_exclude_bKGD=MagickFalse;
10544 if (LocaleNCompare(value+i,"chrm",4) == 0)
10545 mng_info->ping_exclude_cHRM=MagickFalse;
10547 if (LocaleNCompare(value+i,"date",4) == 0)
10548 mng_info->ping_exclude_date=MagickFalse;
10550 if (LocaleNCompare(value+i,"exif",4) == 0)
10551 mng_info->ping_exclude_EXIF=MagickFalse;
10553 if (LocaleNCompare(value+i,"gama",4) == 0)
10554 mng_info->ping_exclude_gAMA=MagickFalse;
10556 if (LocaleNCompare(value+i,"iccp",4) == 0)
10557 mng_info->ping_exclude_iCCP=MagickFalse;
10560 if (LocaleNCompare(value+i,"itxt",4) == 0)
10561 mng_info->ping_exclude_iTXt=MagickFalse;
10564 if (LocaleNCompare(value+i,"gama",4) == 0)
10565 mng_info->ping_exclude_gAMA=MagickFalse;
10567 if (LocaleNCompare(value+i,"offs",4) == 0)
10568 mng_info->ping_exclude_oFFs=MagickFalse;
10570 if (LocaleNCompare(value+i,"phys",4) == 0)
10571 mng_info->ping_exclude_pHYs=MagickFalse;
10573 if (LocaleNCompare(value+i,"srgb",4) == 0)
10574 mng_info->ping_exclude_sRGB=MagickFalse;
10576 if (LocaleNCompare(value+i,"text",4) == 0)
10577 mng_info->ping_exclude_tEXt=MagickFalse;
10579 if (LocaleNCompare(value+i,"trns",4) == 0)
10580 mng_info->ping_exclude_tRNS=MagickFalse;
10582 if (LocaleNCompare(value+i,"vpag",4) == 0)
10583 mng_info->ping_exclude_vpAg=MagickFalse;
10585 if (LocaleNCompare(value+i,"zccp",4) == 0)
10586 mng_info->ping_exclude_zCCP=MagickFalse;
10588 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10589 mng_info->ping_exclude_zTXt=MagickFalse;
10595 if (excluding != MagickFalse && logging != MagickFalse)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 " Chunks to be excluded from the output PNG:");
10599 if (mng_info->ping_exclude_bKGD != MagickFalse)
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602 if (mng_info->ping_exclude_cHRM != MagickFalse)
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10605 if (mng_info->ping_exclude_date != MagickFalse)
10606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 if (mng_info->ping_exclude_EXIF != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 if (mng_info->ping_exclude_gAMA != MagickFalse)
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614 if (mng_info->ping_exclude_iCCP != MagickFalse)
10615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618 if (mng_info->ping_exclude_iTXt != MagickFalse)
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 if (mng_info->ping_exclude_oFFs != MagickFalse)
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 if (mng_info->ping_exclude_pHYs != MagickFalse)
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 if (mng_info->ping_exclude_sRGB != MagickFalse)
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 if (mng_info->ping_exclude_tEXt != MagickFalse)
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 if (mng_info->ping_exclude_tRNS != MagickFalse)
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 if (mng_info->ping_exclude_vpAg != MagickFalse)
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 if (mng_info->ping_exclude_zCCP != MagickFalse)
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 if (mng_info->ping_exclude_zTXt != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 mng_info->need_blob = MagickTrue;
10650 status=WriteOnePNGImage(mng_info,image_info,image);
10652 MngInfoFreeStruct(mng_info,&have_mng_structure);
10654 if (logging != MagickFalse)
10655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10660 #if defined(JNG_SUPPORTED)
10662 /* Write one JNG image */
10663 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10664 const ImageInfo *image_info,Image *image)
10685 jng_alpha_compression_method,
10686 jng_alpha_sample_depth,
10693 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Enter WriteOneJNGImage()");
10696 blob=(unsigned char *) NULL;
10697 jpeg_image=(Image *) NULL;
10698 jpeg_image_info=(ImageInfo *) NULL;
10701 transparent=image_info->type==GrayscaleMatteType ||
10702 image_info->type==TrueColorMatteType;
10704 jng_alpha_sample_depth=0;
10705 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10706 jng_alpha_compression_method=0;
10708 if (image->matte != MagickFalse)
10710 /* if any pixels are transparent */
10711 transparent=MagickTrue;
10712 if (image_info->compression==JPEGCompression)
10713 jng_alpha_compression_method=8;
10720 /* Create JPEG blob, image, and image_info */
10721 if (logging != MagickFalse)
10722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10723 " Creating jpeg_image_info for opacity.");
10725 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10727 if (jpeg_image_info == (ImageInfo *) NULL)
10728 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10730 if (logging != MagickFalse)
10731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10732 " Creating jpeg_image.");
10734 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10736 if (jpeg_image == (Image *) NULL)
10737 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10739 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10740 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10741 status=NegateImage(jpeg_image,MagickFalse);
10742 jpeg_image->matte=MagickFalse;
10744 if (jng_quality >= 1000)
10745 jpeg_image_info->quality=jng_quality/1000;
10748 jpeg_image_info->quality=jng_quality;
10750 jpeg_image_info->type=GrayscaleType;
10751 (void) SetImageType(jpeg_image,GrayscaleType);
10752 (void) AcquireUniqueFilename(jpeg_image->filename);
10753 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10754 "%s",jpeg_image->filename);
10757 /* To do: check bit depth of PNG alpha channel */
10759 /* Check if image is grayscale. */
10760 if (image_info->type != TrueColorMatteType && image_info->type !=
10761 TrueColorType && ImageIsGray(image))
10766 if (jng_alpha_compression_method==0)
10771 /* Encode opacity as a grayscale PNG blob */
10772 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10773 &image->exception);
10774 if (logging != MagickFalse)
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776 " Creating PNG blob.");
10779 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10780 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10781 jpeg_image_info->interlace=NoInterlace;
10783 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10784 &image->exception);
10786 /* Retrieve sample depth used */
10787 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10788 if (value != (char *) NULL)
10789 jng_alpha_sample_depth= (unsigned int) value[0];
10793 /* Encode opacity as a grayscale JPEG blob */
10795 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10796 &image->exception);
10798 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10799 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10800 jpeg_image_info->interlace=NoInterlace;
10801 if (logging != MagickFalse)
10802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803 " Creating blob.");
10804 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10805 &image->exception);
10806 jng_alpha_sample_depth=8;
10808 if (logging != MagickFalse)
10809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 " Successfully read jpeg_image into a blob, length=%.20g.",
10814 /* Destroy JPEG image and image_info */
10815 jpeg_image=DestroyImage(jpeg_image);
10816 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10817 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10820 /* Write JHDR chunk */
10821 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10822 PNGType(chunk,mng_JHDR);
10823 LogPNGChunk(logging,mng_JHDR,16L);
10824 PNGLong(chunk+4,(png_uint_32) image->columns);
10825 PNGLong(chunk+8,(png_uint_32) image->rows);
10826 chunk[12]=jng_color_type;
10827 chunk[13]=8; /* sample depth */
10828 chunk[14]=8; /*jng_image_compression_method */
10829 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10830 chunk[16]=jng_alpha_sample_depth;
10831 chunk[17]=jng_alpha_compression_method;
10832 chunk[18]=0; /*jng_alpha_filter_method */
10833 chunk[19]=0; /*jng_alpha_interlace_method */
10834 (void) WriteBlob(image,20,chunk);
10835 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10836 if (logging != MagickFalse)
10838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10839 " JNG width:%15lu",(unsigned long) image->columns);
10841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10842 " JNG height:%14lu",(unsigned long) image->rows);
10844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845 " JNG color type:%10d",jng_color_type);
10847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10848 " JNG sample depth:%8d",8);
10850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10851 " JNG compression:%9d",8);
10853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10854 " JNG interlace:%11d",0);
10856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10857 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10860 " JNG alpha compression:%3d",jng_alpha_compression_method);
10862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863 " JNG alpha filter:%8d",0);
10865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866 " JNG alpha interlace:%5d",0);
10869 /* Write any JNG-chunk-b profiles */
10870 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10873 Write leading ancillary chunks
10879 Write JNG bKGD chunk
10890 if (jng_color_type == 8 || jng_color_type == 12)
10894 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10895 PNGType(chunk,mng_bKGD);
10896 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10897 red=ScaleQuantumToChar(image->background_color.red);
10898 green=ScaleQuantumToChar(image->background_color.green);
10899 blue=ScaleQuantumToChar(image->background_color.blue);
10906 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10907 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10910 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10913 Write JNG sRGB chunk
10915 (void) WriteBlobMSBULong(image,1L);
10916 PNGType(chunk,mng_sRGB);
10917 LogPNGChunk(logging,mng_sRGB,1L);
10919 if (image->rendering_intent != UndefinedIntent)
10920 chunk[4]=(unsigned char)
10921 Magick_RenderingIntent_to_PNG_RenderingIntent(
10922 (image->rendering_intent));
10925 chunk[4]=(unsigned char)
10926 Magick_RenderingIntent_to_PNG_RenderingIntent(
10927 (PerceptualIntent));
10929 (void) WriteBlob(image,5,chunk);
10930 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10934 if (image->gamma != 0.0)
10937 Write JNG gAMA chunk
10939 (void) WriteBlobMSBULong(image,4L);
10940 PNGType(chunk,mng_gAMA);
10941 LogPNGChunk(logging,mng_gAMA,4L);
10942 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10943 (void) WriteBlob(image,8,chunk);
10944 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10947 if ((mng_info->equal_chrms == MagickFalse) &&
10948 (image->chromaticity.red_primary.x != 0.0))
10954 Write JNG cHRM chunk
10956 (void) WriteBlobMSBULong(image,32L);
10957 PNGType(chunk,mng_cHRM);
10958 LogPNGChunk(logging,mng_cHRM,32L);
10959 primary=image->chromaticity.white_point;
10960 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10961 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10962 primary=image->chromaticity.red_primary;
10963 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10964 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10965 primary=image->chromaticity.green_primary;
10966 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10967 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10968 primary=image->chromaticity.blue_primary;
10969 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10970 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10971 (void) WriteBlob(image,36,chunk);
10972 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10976 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10979 Write JNG pHYs chunk
10981 (void) WriteBlobMSBULong(image,9L);
10982 PNGType(chunk,mng_pHYs);
10983 LogPNGChunk(logging,mng_pHYs,9L);
10984 if (image->units == PixelsPerInchResolution)
10986 PNGLong(chunk+4,(png_uint_32)
10987 (image->x_resolution*100.0/2.54+0.5));
10989 PNGLong(chunk+8,(png_uint_32)
10990 (image->y_resolution*100.0/2.54+0.5));
10997 if (image->units == PixelsPerCentimeterResolution)
10999 PNGLong(chunk+4,(png_uint_32)
11000 (image->x_resolution*100.0+0.5));
11002 PNGLong(chunk+8,(png_uint_32)
11003 (image->y_resolution*100.0+0.5));
11010 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11011 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11015 (void) WriteBlob(image,13,chunk);
11016 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11019 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11022 Write JNG oFFs chunk
11024 (void) WriteBlobMSBULong(image,9L);
11025 PNGType(chunk,mng_oFFs);
11026 LogPNGChunk(logging,mng_oFFs,9L);
11027 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11028 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11030 (void) WriteBlob(image,13,chunk);
11031 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11033 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11035 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11036 PNGType(chunk,mng_vpAg);
11037 LogPNGChunk(logging,mng_vpAg,9L);
11038 PNGLong(chunk+4,(png_uint_32) image->page.width);
11039 PNGLong(chunk+8,(png_uint_32) image->page.height);
11040 chunk[12]=0; /* unit = pixels */
11041 (void) WriteBlob(image,13,chunk);
11042 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11048 if (jng_alpha_compression_method==0)
11056 /* Write IDAT chunk header */
11057 if (logging != MagickFalse)
11058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11059 " Write IDAT chunks from blob, length=%.20g.",(double)
11062 /* Copy IDAT chunks */
11065 for (i=8; i<(ssize_t) length; i+=len+12)
11067 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11070 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11072 /* Found an IDAT chunk. */
11073 (void) WriteBlobMSBULong(image,(size_t) len);
11074 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11075 (void) WriteBlob(image,(size_t) len+4,p);
11076 (void) WriteBlobMSBULong(image,
11077 crc32(0,p,(uInt) len+4));
11082 if (logging != MagickFalse)
11083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11084 " Skipping %c%c%c%c chunk, length=%.20g.",
11085 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11092 /* Write JDAA chunk header */
11093 if (logging != MagickFalse)
11094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11095 " Write JDAA chunk, length=%.20g.",(double) length);
11096 (void) WriteBlobMSBULong(image,(size_t) length);
11097 PNGType(chunk,mng_JDAA);
11098 LogPNGChunk(logging,mng_JDAA,length);
11099 /* Write JDAT chunk(s) data */
11100 (void) WriteBlob(image,4,chunk);
11101 (void) WriteBlob(image,length,blob);
11102 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11105 blob=(unsigned char *) RelinquishMagickMemory(blob);
11108 /* Encode image as a JPEG blob */
11109 if (logging != MagickFalse)
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " Creating jpeg_image_info.");
11112 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11113 if (jpeg_image_info == (ImageInfo *) NULL)
11114 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11116 if (logging != MagickFalse)
11117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11118 " Creating jpeg_image.");
11120 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11121 if (jpeg_image == (Image *) NULL)
11122 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11123 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11125 (void) AcquireUniqueFilename(jpeg_image->filename);
11126 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11127 jpeg_image->filename);
11129 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11130 &image->exception);
11132 if (logging != MagickFalse)
11133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11134 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11135 (double) jpeg_image->rows);
11137 if (jng_color_type == 8 || jng_color_type == 12)
11138 jpeg_image_info->type=GrayscaleType;
11140 jpeg_image_info->quality=jng_quality % 1000;
11141 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11142 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11144 if (logging != MagickFalse)
11145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146 " Creating blob.");
11148 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11150 if (logging != MagickFalse)
11152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11153 " Successfully read jpeg_image into a blob, length=%.20g.",
11156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11157 " Write JDAT chunk, length=%.20g.",(double) length);
11160 /* Write JDAT chunk(s) */
11161 (void) WriteBlobMSBULong(image,(size_t) length);
11162 PNGType(chunk,mng_JDAT);
11163 LogPNGChunk(logging,mng_JDAT,length);
11164 (void) WriteBlob(image,4,chunk);
11165 (void) WriteBlob(image,length,blob);
11166 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11168 jpeg_image=DestroyImage(jpeg_image);
11169 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11170 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11171 blob=(unsigned char *) RelinquishMagickMemory(blob);
11173 /* Write any JNG-chunk-e profiles */
11174 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
11176 /* Write IEND chunk */
11177 (void) WriteBlobMSBULong(image,0L);
11178 PNGType(chunk,mng_IEND);
11179 LogPNGChunk(logging,mng_IEND,0);
11180 (void) WriteBlob(image,4,chunk);
11181 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11183 if (logging != MagickFalse)
11184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185 " exit WriteOneJNGImage()");
11192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11196 % W r i t e J N G I m a g e %
11200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11202 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11204 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
11206 % The format of the WriteJNGImage method is:
11208 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11210 % A description of each parameter follows:
11212 % o image_info: the image info.
11214 % o image: The image.
11216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11218 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11221 have_mng_structure,
11231 assert(image_info != (const ImageInfo *) NULL);
11232 assert(image_info->signature == MagickSignature);
11233 assert(image != (Image *) NULL);
11234 assert(image->signature == MagickSignature);
11235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11236 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11237 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11238 if (status == MagickFalse)
11242 Allocate a MngInfo structure.
11244 have_mng_structure=MagickFalse;
11245 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11246 if (mng_info == (MngInfo *) NULL)
11247 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11249 Initialize members of the MngInfo structure.
11251 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11252 mng_info->image=image;
11253 have_mng_structure=MagickTrue;
11255 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11257 status=WriteOneJNGImage(mng_info,image_info,image);
11258 (void) CloseBlob(image);
11260 (void) CatchImageException(image);
11261 MngInfoFreeStruct(mng_info,&have_mng_structure);
11262 if (logging != MagickFalse)
11263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11270 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11279 have_mng_structure,
11282 volatile MagickBooleanType
11294 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11295 defined(PNG_MNG_FEATURES_SUPPORTED)
11298 all_images_are_gray,
11308 volatile unsigned int
11319 #if (PNG_LIBPNG_VER < 10200)
11320 if (image_info->verbose)
11321 printf("Your PNG library (libpng-%s) is rather old.\n",
11322 PNG_LIBPNG_VER_STRING);
11328 assert(image_info != (const ImageInfo *) NULL);
11329 assert(image_info->signature == MagickSignature);
11330 assert(image != (Image *) NULL);
11331 assert(image->signature == MagickSignature);
11332 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11333 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11334 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11335 if (status == MagickFalse)
11339 Allocate a MngInfo structure.
11341 have_mng_structure=MagickFalse;
11342 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11343 if (mng_info == (MngInfo *) NULL)
11344 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11346 Initialize members of the MngInfo structure.
11348 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11349 mng_info->image=image;
11350 have_mng_structure=MagickTrue;
11351 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11354 * See if user has requested a specific PNG subformat to be used
11355 * for all of the PNGs in the MNG being written, e.g.,
11357 * convert *.png png8:animation.mng
11359 * To do: check -define png:bit_depth and png:color_type as well,
11360 * or perhaps use mng:bit_depth and mng:color_type instead for
11364 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11365 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11366 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11368 write_jng=MagickFalse;
11369 if (image_info->compression == JPEGCompression)
11370 write_jng=MagickTrue;
11372 mng_info->adjoin=image_info->adjoin &&
11373 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11375 if (logging != MagickFalse)
11377 /* Log some info about the input */
11381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11382 " Checking input image(s)");
11384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11385 " Image_info depth: %.20g",(double) image_info->depth);
11387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11388 " Type: %d",image_info->type);
11391 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394 " Scene: %.20g",(double) scene++);
11396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 " Image depth: %.20g",(double) p->depth);
11400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11407 if (p->storage_class == PseudoClass)
11408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11409 " Storage class: PseudoClass");
11412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11413 " Storage class: DirectClass");
11416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11417 " Number of colors: %.20g",(double) p->colors);
11420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11421 " Number of colors: unspecified");
11423 if (mng_info->adjoin == MagickFalse)
11428 use_global_plte=MagickFalse;
11429 all_images_are_gray=MagickFalse;
11430 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11431 need_local_plte=MagickTrue;
11433 need_defi=MagickFalse;
11434 need_matte=MagickFalse;
11435 mng_info->framing_mode=1;
11436 mng_info->old_framing_mode=1;
11439 if (image_info->page != (char *) NULL)
11442 Determine image bounding box.
11444 SetGeometry(image,&mng_info->page);
11445 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11446 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11458 mng_info->page=image->page;
11459 need_geom=MagickTrue;
11460 if (mng_info->page.width || mng_info->page.height)
11461 need_geom=MagickFalse;
11463 Check all the scenes.
11465 initial_delay=image->delay;
11466 need_iterations=MagickFalse;
11467 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11468 mng_info->equal_physs=MagickTrue,
11469 mng_info->equal_gammas=MagickTrue;
11470 mng_info->equal_srgbs=MagickTrue;
11471 mng_info->equal_backgrounds=MagickTrue;
11473 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11474 defined(PNG_MNG_FEATURES_SUPPORTED)
11475 all_images_are_gray=MagickTrue;
11476 mng_info->equal_palettes=MagickFalse;
11477 need_local_plte=MagickFalse;
11479 for (next_image=image; next_image != (Image *) NULL; )
11483 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11484 mng_info->page.width=next_image->columns+next_image->page.x;
11486 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11487 mng_info->page.height=next_image->rows+next_image->page.y;
11490 if (next_image->page.x || next_image->page.y)
11491 need_defi=MagickTrue;
11493 if (next_image->matte)
11494 need_matte=MagickTrue;
11496 if ((int) next_image->dispose >= BackgroundDispose)
11497 if (next_image->matte || next_image->page.x || next_image->page.y ||
11498 ((next_image->columns < mng_info->page.width) &&
11499 (next_image->rows < mng_info->page.height)))
11500 mng_info->need_fram=MagickTrue;
11502 if (next_image->iterations)
11503 need_iterations=MagickTrue;
11505 final_delay=next_image->delay;
11507 if (final_delay != initial_delay || final_delay > 1UL*
11508 next_image->ticks_per_second)
11509 mng_info->need_fram=1;
11511 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11512 defined(PNG_MNG_FEATURES_SUPPORTED)
11514 check for global palette possibility.
11516 if (image->matte != MagickFalse)
11517 need_local_plte=MagickTrue;
11519 if (need_local_plte == 0)
11521 if (ImageIsGray(image) == MagickFalse)
11522 all_images_are_gray=MagickFalse;
11523 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11524 if (use_global_plte == 0)
11525 use_global_plte=mng_info->equal_palettes;
11526 need_local_plte=!mng_info->equal_palettes;
11529 if (GetNextImageInList(next_image) != (Image *) NULL)
11531 if (next_image->background_color.red !=
11532 next_image->next->background_color.red ||
11533 next_image->background_color.green !=
11534 next_image->next->background_color.green ||
11535 next_image->background_color.blue !=
11536 next_image->next->background_color.blue)
11537 mng_info->equal_backgrounds=MagickFalse;
11539 if (next_image->gamma != next_image->next->gamma)
11540 mng_info->equal_gammas=MagickFalse;
11542 if (next_image->rendering_intent !=
11543 next_image->next->rendering_intent)
11544 mng_info->equal_srgbs=MagickFalse;
11546 if ((next_image->units != next_image->next->units) ||
11547 (next_image->x_resolution != next_image->next->x_resolution) ||
11548 (next_image->y_resolution != next_image->next->y_resolution))
11549 mng_info->equal_physs=MagickFalse;
11551 if (mng_info->equal_chrms)
11553 if (next_image->chromaticity.red_primary.x !=
11554 next_image->next->chromaticity.red_primary.x ||
11555 next_image->chromaticity.red_primary.y !=
11556 next_image->next->chromaticity.red_primary.y ||
11557 next_image->chromaticity.green_primary.x !=
11558 next_image->next->chromaticity.green_primary.x ||
11559 next_image->chromaticity.green_primary.y !=
11560 next_image->next->chromaticity.green_primary.y ||
11561 next_image->chromaticity.blue_primary.x !=
11562 next_image->next->chromaticity.blue_primary.x ||
11563 next_image->chromaticity.blue_primary.y !=
11564 next_image->next->chromaticity.blue_primary.y ||
11565 next_image->chromaticity.white_point.x !=
11566 next_image->next->chromaticity.white_point.x ||
11567 next_image->chromaticity.white_point.y !=
11568 next_image->next->chromaticity.white_point.y)
11569 mng_info->equal_chrms=MagickFalse;
11573 next_image=GetNextImageInList(next_image);
11575 if (image_count < 2)
11577 mng_info->equal_backgrounds=MagickFalse;
11578 mng_info->equal_chrms=MagickFalse;
11579 mng_info->equal_gammas=MagickFalse;
11580 mng_info->equal_srgbs=MagickFalse;
11581 mng_info->equal_physs=MagickFalse;
11582 use_global_plte=MagickFalse;
11583 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11584 need_local_plte=MagickTrue;
11586 need_iterations=MagickFalse;
11589 if (mng_info->need_fram == MagickFalse)
11592 Only certain framing rates 100/n are exactly representable without
11593 the FRAM chunk but we'll allow some slop in VLC files
11595 if (final_delay == 0)
11597 if (need_iterations != MagickFalse)
11600 It's probably a GIF with loop; don't run it *too* fast.
11602 if (mng_info->adjoin)
11605 (void) ThrowMagickException(&image->exception,
11606 GetMagickModule(),CoderWarning,
11607 "input has zero delay between all frames; assuming",
11612 mng_info->ticks_per_second=0;
11614 if (final_delay != 0)
11615 mng_info->ticks_per_second=(png_uint_32)
11616 (image->ticks_per_second/final_delay);
11617 if (final_delay > 50)
11618 mng_info->ticks_per_second=2;
11620 if (final_delay > 75)
11621 mng_info->ticks_per_second=1;
11623 if (final_delay > 125)
11624 mng_info->need_fram=MagickTrue;
11626 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11627 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11628 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11629 1UL*image->ticks_per_second))
11630 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11633 if (mng_info->need_fram != MagickFalse)
11634 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11636 If pseudocolor, we should also check to see if all the
11637 palettes are identical and write a global PLTE if they are.
11641 Write the MNG version 1.0 signature and MHDR chunk.
11643 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11644 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11645 PNGType(chunk,mng_MHDR);
11646 LogPNGChunk(logging,mng_MHDR,28L);
11647 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11648 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11649 PNGLong(chunk+12,mng_info->ticks_per_second);
11650 PNGLong(chunk+16,0L); /* layer count=unknown */
11651 PNGLong(chunk+20,0L); /* frame count=unknown */
11652 PNGLong(chunk+24,0L); /* play time=unknown */
11657 if (need_defi || mng_info->need_fram || use_global_plte)
11658 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11661 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11666 if (need_defi || mng_info->need_fram || use_global_plte)
11667 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11670 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11678 if (need_defi || mng_info->need_fram || use_global_plte)
11679 PNGLong(chunk+28,11L); /* simplicity=LC */
11682 PNGLong(chunk+28,9L); /* simplicity=VLC */
11687 if (need_defi || mng_info->need_fram || use_global_plte)
11688 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11691 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11694 (void) WriteBlob(image,32,chunk);
11695 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11696 option=GetImageOption(image_info,"mng:need-cacheoff");
11697 if (option != (const char *) NULL)
11703 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11705 PNGType(chunk,mng_nEED);
11706 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11707 (void) WriteBlobMSBULong(image,(size_t) length);
11708 LogPNGChunk(logging,mng_nEED,(size_t) length);
11710 (void) WriteBlob(image,length,chunk);
11711 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11713 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11714 (GetNextImageInList(image) != (Image *) NULL) &&
11715 (image->iterations != 1))
11718 Write MNG TERM chunk
11720 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11721 PNGType(chunk,mng_TERM);
11722 LogPNGChunk(logging,mng_TERM,10L);
11723 chunk[4]=3; /* repeat animation */
11724 chunk[5]=0; /* show last frame when done */
11725 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11726 final_delay/MagickMax(image->ticks_per_second,1)));
11728 if (image->iterations == 0)
11729 PNGLong(chunk+10,PNG_UINT_31_MAX);
11732 PNGLong(chunk+10,(png_uint_32) image->iterations);
11734 if (logging != MagickFalse)
11736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11737 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11738 final_delay/MagickMax(image->ticks_per_second,1)));
11740 if (image->iterations == 0)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11746 " Image iterations: %.20g",(double) image->iterations);
11748 (void) WriteBlob(image,14,chunk);
11749 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11752 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11754 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11755 mng_info->equal_srgbs)
11758 Write MNG sRGB chunk
11760 (void) WriteBlobMSBULong(image,1L);
11761 PNGType(chunk,mng_sRGB);
11762 LogPNGChunk(logging,mng_sRGB,1L);
11764 if (image->rendering_intent != UndefinedIntent)
11765 chunk[4]=(unsigned char)
11766 Magick_RenderingIntent_to_PNG_RenderingIntent(
11767 (image->rendering_intent));
11770 chunk[4]=(unsigned char)
11771 Magick_RenderingIntent_to_PNG_RenderingIntent(
11772 (PerceptualIntent));
11774 (void) WriteBlob(image,5,chunk);
11775 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11776 mng_info->have_write_global_srgb=MagickTrue;
11781 if (image->gamma && mng_info->equal_gammas)
11784 Write MNG gAMA chunk
11786 (void) WriteBlobMSBULong(image,4L);
11787 PNGType(chunk,mng_gAMA);
11788 LogPNGChunk(logging,mng_gAMA,4L);
11789 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11790 (void) WriteBlob(image,8,chunk);
11791 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11792 mng_info->have_write_global_gama=MagickTrue;
11794 if (mng_info->equal_chrms)
11800 Write MNG cHRM chunk
11802 (void) WriteBlobMSBULong(image,32L);
11803 PNGType(chunk,mng_cHRM);
11804 LogPNGChunk(logging,mng_cHRM,32L);
11805 primary=image->chromaticity.white_point;
11806 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11807 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11808 primary=image->chromaticity.red_primary;
11809 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11810 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11811 primary=image->chromaticity.green_primary;
11812 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11813 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11814 primary=image->chromaticity.blue_primary;
11815 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11816 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11817 (void) WriteBlob(image,36,chunk);
11818 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11819 mng_info->have_write_global_chrm=MagickTrue;
11822 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11825 Write MNG pHYs chunk
11827 (void) WriteBlobMSBULong(image,9L);
11828 PNGType(chunk,mng_pHYs);
11829 LogPNGChunk(logging,mng_pHYs,9L);
11831 if (image->units == PixelsPerInchResolution)
11833 PNGLong(chunk+4,(png_uint_32)
11834 (image->x_resolution*100.0/2.54+0.5));
11836 PNGLong(chunk+8,(png_uint_32)
11837 (image->y_resolution*100.0/2.54+0.5));
11844 if (image->units == PixelsPerCentimeterResolution)
11846 PNGLong(chunk+4,(png_uint_32)
11847 (image->x_resolution*100.0+0.5));
11849 PNGLong(chunk+8,(png_uint_32)
11850 (image->y_resolution*100.0+0.5));
11857 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11858 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11862 (void) WriteBlob(image,13,chunk);
11863 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11866 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11867 or does not cover the entire frame.
11869 if (write_mng && (image->matte || image->page.x > 0 ||
11870 image->page.y > 0 || (image->page.width &&
11871 (image->page.width+image->page.x < mng_info->page.width))
11872 || (image->page.height && (image->page.height+image->page.y
11873 < mng_info->page.height))))
11875 (void) WriteBlobMSBULong(image,6L);
11876 PNGType(chunk,mng_BACK);
11877 LogPNGChunk(logging,mng_BACK,6L);
11878 red=ScaleQuantumToShort(image->background_color.red);
11879 green=ScaleQuantumToShort(image->background_color.green);
11880 blue=ScaleQuantumToShort(image->background_color.blue);
11881 PNGShort(chunk+4,red);
11882 PNGShort(chunk+6,green);
11883 PNGShort(chunk+8,blue);
11884 (void) WriteBlob(image,10,chunk);
11885 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11886 if (mng_info->equal_backgrounds)
11888 (void) WriteBlobMSBULong(image,6L);
11889 PNGType(chunk,mng_bKGD);
11890 LogPNGChunk(logging,mng_bKGD,6L);
11891 (void) WriteBlob(image,10,chunk);
11892 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11896 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11897 if ((need_local_plte == MagickFalse) &&
11898 (image->storage_class == PseudoClass) &&
11899 (all_images_are_gray == MagickFalse))
11905 Write MNG PLTE chunk
11907 data_length=3*image->colors;
11908 (void) WriteBlobMSBULong(image,data_length);
11909 PNGType(chunk,mng_PLTE);
11910 LogPNGChunk(logging,mng_PLTE,data_length);
11912 for (i=0; i < (ssize_t) image->colors; i++)
11914 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11915 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11916 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11919 (void) WriteBlob(image,data_length+4,chunk);
11920 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11921 mng_info->have_write_global_plte=MagickTrue;
11927 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11928 defined(PNG_MNG_FEATURES_SUPPORTED)
11929 mng_info->equal_palettes=MagickFalse;
11933 if (mng_info->adjoin)
11935 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11936 defined(PNG_MNG_FEATURES_SUPPORTED)
11938 If we aren't using a global palette for the entire MNG, check to
11939 see if we can use one for two or more consecutive images.
11941 if (need_local_plte && use_global_plte && !all_images_are_gray)
11943 if (mng_info->IsPalette)
11946 When equal_palettes is true, this image has the same palette
11947 as the previous PseudoClass image
11949 mng_info->have_write_global_plte=mng_info->equal_palettes;
11950 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11951 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11954 Write MNG PLTE chunk
11959 data_length=3*image->colors;
11960 (void) WriteBlobMSBULong(image,data_length);
11961 PNGType(chunk,mng_PLTE);
11962 LogPNGChunk(logging,mng_PLTE,data_length);
11964 for (i=0; i < (ssize_t) image->colors; i++)
11966 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11967 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11968 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11971 (void) WriteBlob(image,data_length+4,chunk);
11972 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11973 (uInt) (data_length+4)));
11974 mng_info->have_write_global_plte=MagickTrue;
11978 mng_info->have_write_global_plte=MagickFalse;
11989 previous_x=mng_info->page.x;
11990 previous_y=mng_info->page.y;
11997 mng_info->page=image->page;
11998 if ((mng_info->page.x != previous_x) ||
11999 (mng_info->page.y != previous_y))
12001 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12002 PNGType(chunk,mng_DEFI);
12003 LogPNGChunk(logging,mng_DEFI,12L);
12004 chunk[4]=0; /* object 0 MSB */
12005 chunk[5]=0; /* object 0 LSB */
12006 chunk[6]=0; /* visible */
12007 chunk[7]=0; /* abstract */
12008 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12009 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12010 (void) WriteBlob(image,16,chunk);
12011 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12016 mng_info->write_mng=write_mng;
12018 if ((int) image->dispose >= 3)
12019 mng_info->framing_mode=3;
12021 if (mng_info->need_fram && mng_info->adjoin &&
12022 ((image->delay != mng_info->delay) ||
12023 (mng_info->framing_mode != mng_info->old_framing_mode)))
12025 if (image->delay == mng_info->delay)
12028 Write a MNG FRAM chunk with the new framing mode.
12030 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12031 PNGType(chunk,mng_FRAM);
12032 LogPNGChunk(logging,mng_FRAM,1L);
12033 chunk[4]=(unsigned char) mng_info->framing_mode;
12034 (void) WriteBlob(image,5,chunk);
12035 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12040 Write a MNG FRAM chunk with the delay.
12042 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12043 PNGType(chunk,mng_FRAM);
12044 LogPNGChunk(logging,mng_FRAM,10L);
12045 chunk[4]=(unsigned char) mng_info->framing_mode;
12046 chunk[5]=0; /* frame name separator (no name) */
12047 chunk[6]=2; /* flag for changing default delay */
12048 chunk[7]=0; /* flag for changing frame timeout */
12049 chunk[8]=0; /* flag for changing frame clipping */
12050 chunk[9]=0; /* flag for changing frame sync_id */
12051 PNGLong(chunk+10,(png_uint_32)
12052 ((mng_info->ticks_per_second*
12053 image->delay)/MagickMax(image->ticks_per_second,1)));
12054 (void) WriteBlob(image,14,chunk);
12055 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12056 mng_info->delay=(png_uint_32) image->delay;
12058 mng_info->old_framing_mode=mng_info->framing_mode;
12061 #if defined(JNG_SUPPORTED)
12062 if (image_info->compression == JPEGCompression)
12067 if (logging != MagickFalse)
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069 " Writing JNG object.");
12070 /* To do: specify the desired alpha compression method. */
12071 write_info=CloneImageInfo(image_info);
12072 write_info->compression=UndefinedCompression;
12073 status=WriteOneJNGImage(mng_info,write_info,image);
12074 write_info=DestroyImageInfo(write_info);
12079 if (logging != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12081 " Writing PNG object.");
12083 mng_info->need_blob = MagickFalse;
12084 mng_info->ping_preserve_colormap = MagickFalse;
12086 /* We don't want any ancillary chunks written */
12087 mng_info->ping_exclude_bKGD=MagickTrue;
12088 mng_info->ping_exclude_cHRM=MagickTrue;
12089 mng_info->ping_exclude_date=MagickTrue;
12090 mng_info->ping_exclude_EXIF=MagickTrue;
12091 mng_info->ping_exclude_gAMA=MagickTrue;
12092 mng_info->ping_exclude_iCCP=MagickTrue;
12093 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12094 mng_info->ping_exclude_oFFs=MagickTrue;
12095 mng_info->ping_exclude_pHYs=MagickTrue;
12096 mng_info->ping_exclude_sRGB=MagickTrue;
12097 mng_info->ping_exclude_tEXt=MagickTrue;
12098 mng_info->ping_exclude_tRNS=MagickTrue;
12099 mng_info->ping_exclude_vpAg=MagickTrue;
12100 mng_info->ping_exclude_zCCP=MagickTrue;
12101 mng_info->ping_exclude_zTXt=MagickTrue;
12103 status=WriteOnePNGImage(mng_info,image_info,image);
12106 if (status == MagickFalse)
12108 MngInfoFreeStruct(mng_info,&have_mng_structure);
12109 (void) CloseBlob(image);
12110 return(MagickFalse);
12112 (void) CatchImageException(image);
12113 if (GetNextImageInList(image) == (Image *) NULL)
12115 image=SyncNextImageInList(image);
12116 status=SetImageProgress(image,SaveImagesTag,scene++,
12117 GetImageListLength(image));
12119 if (status == MagickFalse)
12122 } while (mng_info->adjoin);
12126 while (GetPreviousImageInList(image) != (Image *) NULL)
12127 image=GetPreviousImageInList(image);
12129 Write the MEND chunk.
12131 (void) WriteBlobMSBULong(image,0x00000000L);
12132 PNGType(chunk,mng_MEND);
12133 LogPNGChunk(logging,mng_MEND,0L);
12134 (void) WriteBlob(image,4,chunk);
12135 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12138 Relinquish resources.
12140 (void) CloseBlob(image);
12141 MngInfoFreeStruct(mng_info,&have_mng_structure);
12143 if (logging != MagickFalse)
12144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12146 return(MagickTrue);
12148 #else /* PNG_LIBPNG_VER > 10011 */
12150 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12153 printf("Your PNG library is too old: You have libpng-%s\n",
12154 PNG_LIBPNG_VER_STRING);
12156 ThrowBinaryException(CoderError,"PNG library is too old",
12157 image_info->filename);
12160 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12162 return(WritePNGImage(image_info,image));
12164 #endif /* PNG_LIBPNG_VER > 10011 */