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 #ifdef 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 #ifdef 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))
2070 Set image background color.
2072 if (logging != MagickFalse)
2073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2074 " Reading PNG bKGD chunk.");
2076 /* Scale background components to 16-bit, then scale
2079 if (logging != MagickFalse)
2080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2081 " raw ping_background=(%d,%d,%d).",ping_background->red,
2082 ping_background->green,ping_background->blue);
2086 if (ping_bit_depth == 1)
2089 else if (ping_bit_depth == 2)
2092 else if (ping_bit_depth == 4)
2095 if (ping_bit_depth <= 8)
2098 ping_background->red *= bkgd_scale;
2099 ping_background->green *= bkgd_scale;
2100 ping_background->blue *= bkgd_scale;
2102 if (logging != MagickFalse)
2104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2105 " bkgd_scale=%d.",bkgd_scale);
2107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2108 " ping_background=(%d,%d,%d).",ping_background->red,
2109 ping_background->green,ping_background->blue);
2112 image->background_color.red=
2113 ScaleShortToQuantum(ping_background->red);
2115 image->background_color.green=
2116 ScaleShortToQuantum(ping_background->green);
2118 image->background_color.blue=
2119 ScaleShortToQuantum(ping_background->blue);
2121 image->background_color.opacity=OpaqueOpacity;
2123 if (logging != MagickFalse)
2124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2125 " image->background_color=(%.20g,%.20g,%.20g).",
2126 (double) image->background_color.red,
2127 (double) image->background_color.green,
2128 (double) image->background_color.blue);
2130 #endif /* PNG_READ_bKGD_SUPPORTED */
2132 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2135 Image has a tRNS chunk.
2143 if (logging != MagickFalse)
2144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2145 " Reading PNG tRNS chunk.");
2147 max_sample = (int) ((one << ping_bit_depth) - 1);
2149 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2150 (int)ping_trans_color->gray > max_sample) ||
2151 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2152 ((int)ping_trans_color->red > max_sample ||
2153 (int)ping_trans_color->green > max_sample ||
2154 (int)ping_trans_color->blue > max_sample)))
2156 if (logging != MagickFalse)
2157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2158 " Ignoring PNG tRNS chunk with out-of-range sample.");
2159 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2160 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2161 image->matte=MagickFalse;
2168 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2170 /* Scale transparent_color to short */
2171 transparent_color.red= scale_to_short*ping_trans_color->red;
2172 transparent_color.green= scale_to_short*ping_trans_color->green;
2173 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2174 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2176 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2178 if (logging != MagickFalse)
2180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2181 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2184 " scaled graylevel is %d.",transparent_color.opacity);
2186 transparent_color.red=transparent_color.opacity;
2187 transparent_color.green=transparent_color.opacity;
2188 transparent_color.blue=transparent_color.opacity;
2192 #if defined(PNG_READ_sBIT_SUPPORTED)
2193 if (mng_info->have_global_sbit)
2195 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2196 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2199 num_passes=png_set_interlace_handling(ping);
2201 png_read_update_info(ping,ping_info);
2203 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2206 Initialize image structure.
2208 mng_info->image_box.left=0;
2209 mng_info->image_box.right=(ssize_t) ping_width;
2210 mng_info->image_box.top=0;
2211 mng_info->image_box.bottom=(ssize_t) ping_height;
2212 if (mng_info->mng_type == 0)
2214 mng_info->mng_width=ping_width;
2215 mng_info->mng_height=ping_height;
2216 mng_info->frame=mng_info->image_box;
2217 mng_info->clip=mng_info->image_box;
2222 image->page.y=mng_info->y_off[mng_info->object_id];
2225 image->compression=ZipCompression;
2226 image->columns=ping_width;
2227 image->rows=ping_height;
2228 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2229 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2234 image->storage_class=PseudoClass;
2236 image->colors=one << ping_bit_depth;
2237 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2238 if (image->colors > 256)
2241 if (image->colors > 65536L)
2242 image->colors=65536L;
2244 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2252 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2253 image->colors=(size_t) number_colors;
2255 if (logging != MagickFalse)
2256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2257 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2261 if (image->storage_class == PseudoClass)
2264 Initialize image colormap.
2266 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2267 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2269 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2277 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2279 for (i=0; i < (ssize_t) number_colors; i++)
2281 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2282 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2283 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2286 for ( ; i < (ssize_t) image->colors; i++)
2288 image->colormap[i].red=0;
2289 image->colormap[i].green=0;
2290 image->colormap[i].blue=0;
2299 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2304 for (i=0; i < (ssize_t) image->colors; i++)
2306 image->colormap[i].red=(Quantum) (i*scale);
2307 image->colormap[i].green=(Quantum) (i*scale);
2308 image->colormap[i].blue=(Quantum) (i*scale);
2313 /* Set some properties for reporting by "identify" */
2318 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2319 ping_interlace_method in value */
2321 (void) FormatMagickString(msg,MaxTextExtent,
2322 "%d, %d",(int) ping_width, (int) ping_height);
2323 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2325 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2326 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2328 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2329 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2331 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2332 (int) ping_interlace_method);
2333 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2337 Read image scanlines.
2339 if (image->delay != 0)
2340 mng_info->scenes_found++;
2342 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2343 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2344 (image_info->first_scene+image_info->number_scenes))))
2346 if (logging != MagickFalse)
2347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2348 " Skipping PNG image data for scene %.20g",(double)
2349 mng_info->scenes_found-1);
2350 png_destroy_read_struct(&ping,&ping_info,&end_info);
2351 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2352 UnlockSemaphoreInfo(ping_semaphore);
2354 if (logging != MagickFalse)
2355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2356 " exit ReadOnePNGImage().");
2361 if (logging != MagickFalse)
2362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2363 " Reading PNG IDAT chunk(s)");
2366 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2367 ping_rowbytes*sizeof(*ping_pixels));
2370 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2371 sizeof(*ping_pixels));
2373 if (ping_pixels == (unsigned char *) NULL)
2374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2376 if (logging != MagickFalse)
2377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2378 " Converting PNG pixels to pixel packets");
2380 Convert PNG pixels to pixel packets.
2382 if (setjmp(png_jmpbuf(ping)))
2385 PNG image is corrupt.
2387 png_destroy_read_struct(&ping,&ping_info,&end_info);
2388 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2389 UnlockSemaphoreInfo(ping_semaphore);
2391 if (quantum_info != (QuantumInfo *) NULL)
2392 quantum_info = DestroyQuantumInfo(quantum_info);
2394 if (ping_pixels != (unsigned char *) NULL)
2395 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2397 if (logging != MagickFalse)
2398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2399 " exit ReadOnePNGImage() with error.");
2401 if (image != (Image *) NULL)
2403 InheritException(exception,&image->exception);
2407 return(GetFirstImageInList(image));
2410 quantum_info=AcquireQuantumInfo(image_info,image);
2412 if (quantum_info == (QuantumInfo *) NULL)
2413 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2418 found_transparent_pixel;
2420 found_transparent_pixel=MagickFalse;
2422 if (image->storage_class == DirectClass)
2424 for (pass=0; pass < num_passes; pass++)
2427 Convert image to DirectClass pixel packets.
2429 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2433 depth=(ssize_t) ping_bit_depth;
2435 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2436 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2437 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2438 MagickTrue : MagickFalse;
2440 for (y=0; y < (ssize_t) image->rows; y++)
2443 row_offset=ping_rowbytes*y;
2448 png_read_row(ping,ping_pixels+row_offset,NULL);
2449 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2451 if (q == (PixelPacket *) NULL)
2454 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2455 /* code deleted from version 6.6.6-8 */
2456 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2458 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2459 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2460 GrayQuantum,ping_pixels+row_offset,exception);
2462 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2463 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2464 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2466 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2467 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2468 RGBAQuantum,ping_pixels+row_offset,exception);
2470 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2471 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2472 IndexQuantum,ping_pixels+row_offset,exception);
2474 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2475 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2476 RGBQuantum,ping_pixels+row_offset,exception);
2478 if (found_transparent_pixel == MagickFalse)
2480 /* Is there a transparent pixel in the row? */
2481 if (y== 0 && logging != MagickFalse)
2482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2483 " Looking for cheap transparent pixel");
2485 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2487 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2488 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2489 (q->opacity != OpaqueOpacity))
2491 if (logging != MagickFalse)
2492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2495 found_transparent_pixel = MagickTrue;
2498 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2499 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2500 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2501 ScaleQuantumToShort(q->green) == transparent_color.green &&
2502 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2504 if (logging != MagickFalse)
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2507 found_transparent_pixel = MagickTrue;
2514 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2516 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2519 if (status == MagickFalse)
2522 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2526 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2528 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2529 if (status == MagickFalse)
2535 else /* image->storage_class != DirectClass */
2537 for (pass=0; pass < num_passes; pass++)
2546 Convert grayscale image to PseudoClass pixel packets.
2548 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2549 MagickTrue : MagickFalse;
2551 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2552 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2554 if (quantum_scanline == (Quantum *) NULL)
2555 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2557 for (y=0; y < (ssize_t) image->rows; y++)
2560 row_offset=ping_rowbytes*y;
2565 png_read_row(ping,ping_pixels+row_offset,NULL);
2566 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2568 if (q == (PixelPacket *) NULL)
2571 indexes=GetAuthenticIndexQueue(image);
2572 p=ping_pixels+row_offset;
2575 switch (ping_bit_depth)
2582 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2584 for (bit=7; bit >= 0; bit--)
2585 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2589 if ((image->columns % 8) != 0)
2591 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2592 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2600 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2602 *r++=(*p >> 6) & 0x03;
2603 *r++=(*p >> 4) & 0x03;
2604 *r++=(*p >> 2) & 0x03;
2608 if ((image->columns % 4) != 0)
2610 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2611 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2619 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2621 *r++=(*p >> 4) & 0x0f;
2625 if ((image->columns % 2) != 0)
2626 *r++=(*p++ >> 4) & 0x0f;
2633 if (ping_color_type == 4)
2634 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2637 /* In image.h, OpaqueOpacity is 0
2638 * TransparentOpacity is QuantumRange
2639 * In a PNG datastream, Opaque is QuantumRange
2640 * and Transparent is 0.
2642 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2643 if (q->opacity != OpaqueOpacity)
2644 found_transparent_pixel = MagickTrue;
2649 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2657 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2659 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2663 if (image->colors > 256)
2671 *r=(Quantum) quantum;
2674 if (ping_color_type == 4)
2676 quantum=((*p++) << 8);
2678 q->opacity=(Quantum) (QuantumRange-quantum);
2679 if (q->opacity != OpaqueOpacity)
2680 found_transparent_pixel = MagickTrue;
2684 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2688 if (image->colors > 256)
2699 if (ping_color_type == 4)
2701 q->opacity=(*p << 8) | *(p+1);
2703 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2704 if (q->opacity != OpaqueOpacity)
2705 found_transparent_pixel = MagickTrue;
2710 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2712 p++; /* strip low byte */
2714 if (ping_color_type == 4)
2716 q->opacity=(Quantum) (QuantumRange-(*p++));
2717 if (q->opacity != OpaqueOpacity)
2718 found_transparent_pixel = MagickTrue;
2733 Transfer image scanline.
2737 for (x=0; x < (ssize_t) image->columns; x++)
2738 indexes[x]=(IndexPacket) (*r++);
2740 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2743 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2745 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2748 if (status == MagickFalse)
2753 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2755 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2757 if (status == MagickFalse)
2761 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2764 image->matte=found_transparent_pixel;
2766 if (logging != MagickFalse)
2768 if (found_transparent_pixel != MagickFalse)
2769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2770 " Found transparent pixel");
2773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774 " No transparent pixel was found");
2776 ping_color_type&=0x03;
2781 if (quantum_info != (QuantumInfo *) NULL)
2782 quantum_info=DestroyQuantumInfo(quantum_info);
2784 if (image->storage_class == PseudoClass)
2790 image->matte=MagickFalse;
2791 (void) SyncImage(image);
2795 png_read_end(ping,end_info);
2797 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2798 (ssize_t) image_info->first_scene && image->delay != 0)
2800 png_destroy_read_struct(&ping,&ping_info,&end_info);
2801 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2803 (void) SetImageBackgroundColor(image);
2804 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2805 UnlockSemaphoreInfo(ping_semaphore);
2807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " exit ReadOnePNGImage() early.");
2813 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2819 Image has a transparent background.
2821 storage_class=image->storage_class;
2822 image->matte=MagickTrue;
2824 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2826 if (storage_class == PseudoClass)
2828 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2830 for (x=0; x < ping_num_trans; x++)
2832 image->colormap[x].opacity =
2833 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2837 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2839 for (x=0; x < (int) image->colors; x++)
2841 if (ScaleQuantumToShort(image->colormap[x].red) ==
2842 transparent_color.opacity)
2844 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2848 (void) SyncImage(image);
2851 #if 1 /* Should have already been done above, but glennrp problem P10
2856 for (y=0; y < (ssize_t) image->rows; y++)
2858 image->storage_class=storage_class;
2859 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2861 if (q == (PixelPacket *) NULL)
2864 indexes=GetAuthenticIndexQueue(image);
2866 /* Caution: on a Q8 build, this does not distinguish between
2867 * 16-bit colors that differ only in the low byte
2869 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2871 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2872 ScaleQuantumToShort(q->green) == transparent_color.green &&
2873 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2875 q->opacity=(Quantum) TransparentOpacity;
2878 #if 0 /* I have not found a case where this is needed. */
2881 q->opacity=(Quantum) OpaqueOpacity;
2888 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2894 image->storage_class=DirectClass;
2897 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2898 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2899 image->colorspace=GRAYColorspace;
2901 for (j = 0; j < 2; j++)
2904 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2905 MagickTrue : MagickFalse;
2907 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2908 MagickTrue : MagickFalse;
2910 if (status != MagickFalse)
2911 for (i=0; i < (ssize_t) num_text; i++)
2913 /* Check for a profile */
2915 if (logging != MagickFalse)
2916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2917 " Reading PNG text chunk");
2919 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2921 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2930 length=text[i].text_length;
2931 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2933 if (value == (char *) NULL)
2935 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2936 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2941 (void) ConcatenateMagickString(value,text[i].text,length+2);
2943 /* Don't save "density" or "units" property if we have a pHYs
2946 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2947 (LocaleCompare(text[i].key,"density") != 0 &&
2948 LocaleCompare(text[i].key,"units") != 0))
2949 (void) SetImageProperty(image,text[i].key,value);
2951 if (logging != MagickFalse)
2953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2954 " length: %lu",(unsigned long) length);
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " Keyword: %s",text[i].key);
2959 value=DestroyString(value);
2962 num_text_total += num_text;
2965 #ifdef MNG_OBJECT_BUFFERS
2967 Store the object if necessary.
2969 if (object_id && !mng_info->frozen[object_id])
2971 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2974 create a new object buffer.
2976 mng_info->ob[object_id]=(MngBuffer *)
2977 AcquireMagickMemory(sizeof(MngBuffer));
2979 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2981 mng_info->ob[object_id]->image=(Image *) NULL;
2982 mng_info->ob[object_id]->reference_count=1;
2986 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2987 mng_info->ob[object_id]->frozen)
2989 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2994 if (mng_info->ob[object_id]->frozen)
2995 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2996 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2997 "`%s'",image->filename);
3003 if (mng_info->ob[object_id]->image != (Image *) NULL)
3004 mng_info->ob[object_id]->image=DestroyImage
3005 (mng_info->ob[object_id]->image);
3007 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3010 if (mng_info->ob[object_id]->image != (Image *) NULL)
3011 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3014 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3015 ResourceLimitError,"Cloning image for object buffer failed",
3016 "`%s'",image->filename);
3018 if (ping_width > 250000L || ping_height > 250000L)
3019 png_error(ping,"PNG Image dimensions are too large.");
3021 mng_info->ob[object_id]->width=ping_width;
3022 mng_info->ob[object_id]->height=ping_height;
3023 mng_info->ob[object_id]->color_type=ping_color_type;
3024 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3025 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3026 mng_info->ob[object_id]->compression_method=
3027 ping_compression_method;
3028 mng_info->ob[object_id]->filter_method=ping_filter_method;
3030 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3039 Copy the PLTE to the object buffer.
3041 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3042 mng_info->ob[object_id]->plte_length=number_colors;
3044 for (i=0; i < number_colors; i++)
3046 mng_info->ob[object_id]->plte[i]=plte[i];
3051 mng_info->ob[object_id]->plte_length=0;
3056 /* Set image->matte to MagickTrue if the input colortype supports
3057 * alpha or if a valid tRNS chunk is present, no matter whether there
3058 * is actual transparency present.
3060 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3061 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3062 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3063 MagickTrue : MagickFalse;
3065 /* Set more properties for identify to retrieve */
3070 if (num_text_total != 0)
3072 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3073 (void) FormatMagickString(msg,MaxTextExtent,
3074 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3075 (void) SetImageProperty(image,"PNG:text ",msg);
3078 if (num_raw_profiles != 0)
3080 (void) FormatMagickString(msg,MaxTextExtent,
3081 "%d were found", num_raw_profiles);
3082 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3085 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3087 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3088 "chunk was found (see Chromaticity, above)");
3089 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3092 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3094 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3095 "chunk was found (see Background color, above)");
3096 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3099 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3102 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3103 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3105 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3106 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3108 #if defined(PNG_sRGB_SUPPORTED)
3109 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3111 (void) FormatMagickString(msg,MaxTextExtent,
3112 "intent=%d (See Rendering intent)",
3114 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3118 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3120 (void) FormatMagickString(msg,MaxTextExtent,
3121 "gamma=%.8g (See Gamma, above)",
3123 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3126 #if defined(PNG_pHYs_SUPPORTED)
3127 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3129 (void) FormatMagickString(msg,MaxTextExtent,
3130 "x_res=%.10g, y_res=%.10g, units=%d",
3131 (double) x_resolution,(double) y_resolution, unit_type);
3132 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3136 #if defined(PNG_oFFs_SUPPORTED)
3137 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3139 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3140 (double) image->page.x,(double) image->page.y);
3141 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3145 if ((image->page.width != 0 && image->page.width != image->columns) ||
3146 (image->page.height != 0 && image->page.height != image->rows))
3148 (void) FormatMagickString(msg,MaxTextExtent,
3149 "width=%.20g, height=%.20g",
3150 (double) image->page.width,(double) image->page.height);
3151 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3156 Relinquish resources.
3158 png_destroy_read_struct(&ping,&ping_info,&end_info);
3160 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3161 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3162 UnlockSemaphoreInfo(ping_semaphore);
3165 if (logging != MagickFalse)
3166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3167 " exit ReadOnePNGImage()");
3171 /* end of reading one PNG image */
3174 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3189 magic_number[MaxTextExtent];
3197 assert(image_info != (const ImageInfo *) NULL);
3198 assert(image_info->signature == MagickSignature);
3200 if (image_info->debug != MagickFalse)
3201 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3202 image_info->filename);
3204 assert(exception != (ExceptionInfo *) NULL);
3205 assert(exception->signature == MagickSignature);
3206 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3207 image=AcquireImage(image_info);
3208 mng_info=(MngInfo *) NULL;
3209 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3211 if (status == MagickFalse)
3212 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3215 Verify PNG signature.
3217 count=ReadBlob(image,8,(unsigned char *) magic_number);
3219 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3220 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3223 Allocate a MngInfo structure.
3225 have_mng_structure=MagickFalse;
3226 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3228 if (mng_info == (MngInfo *) NULL)
3229 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3232 Initialize members of the MngInfo structure.
3234 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3235 mng_info->image=image;
3236 have_mng_structure=MagickTrue;
3239 image=ReadOnePNGImage(mng_info,image_info,exception);
3240 MngInfoFreeStruct(mng_info,&have_mng_structure);
3242 if (image == (Image *) NULL)
3244 if (previous != (Image *) NULL)
3246 if (previous->signature != MagickSignature)
3247 ThrowReaderException(CorruptImageError,"CorruptImage");
3249 (void) CloseBlob(previous);
3250 (void) DestroyImageList(previous);
3253 if (logging != MagickFalse)
3254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3255 "exit ReadPNGImage() with error");
3257 return((Image *) NULL);
3260 (void) CloseBlob(image);
3262 if ((image->columns == 0) || (image->rows == 0))
3264 if (logging != MagickFalse)
3265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3266 "exit ReadPNGImage() with error.");
3268 ThrowReaderException(CorruptImageError,"CorruptImage");
3271 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3273 (void) SetImageType(image,PaletteType);
3275 if (image->matte != MagickFalse)
3277 /* To do: Reduce to binary transparency */
3281 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3283 (void) SetImageType(image,TrueColorType);
3284 image->matte=MagickFalse;
3287 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3288 (void) SetImageType(image,TrueColorMatteType);
3290 if (logging != MagickFalse)
3291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3292 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3293 (double) image->page.width,(double) image->page.height,
3294 (double) image->page.x,(double) image->page.y);
3296 if (logging != MagickFalse)
3297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3304 #if defined(JNG_SUPPORTED)
3306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3310 % R e a d O n e J N G I m a g e %
3314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3316 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3317 % (minus the 8-byte signature) and returns it. It allocates the memory
3318 % necessary for the new Image structure and returns a pointer to the new
3321 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3323 % The format of the ReadOneJNGImage method is:
3325 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3326 % ExceptionInfo *exception)
3328 % A description of each parameter follows:
3330 % o mng_info: Specifies a pointer to a MngInfo structure.
3332 % o image_info: the image info.
3334 % o exception: return any errors or warnings in this structure.
3337 static Image *ReadOneJNGImage(MngInfo *mng_info,
3338 const ImageInfo *image_info, ExceptionInfo *exception)
3365 jng_image_sample_depth,
3366 jng_image_compression_method,
3367 jng_image_interlace_method,
3368 jng_alpha_sample_depth,
3369 jng_alpha_compression_method,
3370 jng_alpha_filter_method,
3371 jng_alpha_interlace_method;
3373 register const PixelPacket
3380 register PixelPacket
3383 register unsigned char
3394 jng_alpha_compression_method=0;
3395 jng_alpha_sample_depth=8;
3399 alpha_image=(Image *) NULL;
3400 color_image=(Image *) NULL;
3401 alpha_image_info=(ImageInfo *) NULL;
3402 color_image_info=(ImageInfo *) NULL;
3404 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3405 " Enter ReadOneJNGImage()");
3407 image=mng_info->image;
3409 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3412 Allocate next image structure.
3414 if (logging != MagickFalse)
3415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3416 " AcquireNextImage()");
3418 AcquireNextImage(image_info,image);
3420 if (GetNextImageInList(image) == (Image *) NULL)
3421 return((Image *) NULL);
3423 image=SyncNextImageInList(image);
3425 mng_info->image=image;
3428 Signature bytes have already been read.
3431 read_JSEP=MagickFalse;
3432 reading_idat=MagickFalse;
3433 skip_to_iend=MagickFalse;
3437 type[MaxTextExtent];
3446 Read a new JNG chunk.
3448 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3449 2*GetBlobSize(image));
3451 if (status == MagickFalse)
3455 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3456 length=ReadBlobMSBLong(image);
3457 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3459 if (logging != MagickFalse)
3460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3461 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3462 type[0],type[1],type[2],type[3],(double) length);
3464 if (length > PNG_UINT_31_MAX || count == 0)
3465 ThrowReaderException(CorruptImageError,"CorruptImage");
3468 chunk=(unsigned char *) NULL;
3472 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3474 if (chunk == (unsigned char *) NULL)
3475 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3477 for (i=0; i < (ssize_t) length; i++)
3478 chunk[i]=(unsigned char) ReadBlobByte(image);
3483 (void) ReadBlobMSBLong(image); /* read crc word */
3488 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3493 if (memcmp(type,mng_JHDR,4) == 0)
3497 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3498 (p[2] << 8) | p[3]);
3499 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3500 (p[6] << 8) | p[7]);
3501 jng_color_type=p[8];
3502 jng_image_sample_depth=p[9];
3503 jng_image_compression_method=p[10];
3504 jng_image_interlace_method=p[11];
3506 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3509 jng_alpha_sample_depth=p[12];
3510 jng_alpha_compression_method=p[13];
3511 jng_alpha_filter_method=p[14];
3512 jng_alpha_interlace_method=p[15];
3514 if (logging != MagickFalse)
3516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " jng_width: %16lu",(unsigned long) jng_width);
3519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_width: %16lu",(unsigned long) jng_height);
3522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523 " jng_color_type: %16d",jng_color_type);
3525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3526 " jng_image_sample_depth: %3d",
3527 jng_image_sample_depth);
3529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3530 " jng_image_compression_method:%3d",
3531 jng_image_compression_method);
3533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3534 " jng_image_interlace_method: %3d",
3535 jng_image_interlace_method);
3537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3538 " jng_alpha_sample_depth: %3d",
3539 jng_alpha_sample_depth);
3541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3542 " jng_alpha_compression_method:%3d",
3543 jng_alpha_compression_method);
3545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3546 " jng_alpha_filter_method: %3d",
3547 jng_alpha_filter_method);
3549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3550 " jng_alpha_interlace_method: %3d",
3551 jng_alpha_interlace_method);
3556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3562 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3563 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3564 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3567 o create color_image
3568 o open color_blob, attached to color_image
3569 o if (color type has alpha)
3570 open alpha_blob, attached to alpha_image
3573 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3575 if (color_image_info == (ImageInfo *) NULL)
3576 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3578 GetImageInfo(color_image_info);
3579 color_image=AcquireImage(color_image_info);
3581 if (color_image == (Image *) NULL)
3582 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3584 if (logging != MagickFalse)
3585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3586 " Creating color_blob.");
3588 (void) AcquireUniqueFilename(color_image->filename);
3589 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3592 if (status == MagickFalse)
3593 return((Image *) NULL);
3595 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3597 alpha_image_info=(ImageInfo *)
3598 AcquireMagickMemory(sizeof(ImageInfo));
3600 if (alpha_image_info == (ImageInfo *) NULL)
3601 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3603 GetImageInfo(alpha_image_info);
3604 alpha_image=AcquireImage(alpha_image_info);
3606 if (alpha_image == (Image *) NULL)
3608 alpha_image=DestroyImage(alpha_image);
3609 ThrowReaderException(ResourceLimitError,
3610 "MemoryAllocationFailed");
3613 if (logging != MagickFalse)
3614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3615 " Creating alpha_blob.");
3617 (void) AcquireUniqueFilename(alpha_image->filename);
3618 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3621 if (status == MagickFalse)
3622 return((Image *) NULL);
3624 if (jng_alpha_compression_method == 0)
3629 if (logging != MagickFalse)
3630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3631 " Writing IHDR chunk to alpha_blob.");
3633 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3634 "\211PNG\r\n\032\n");
3636 (void) WriteBlobMSBULong(alpha_image,13L);
3637 PNGType(data,mng_IHDR);
3638 LogPNGChunk(logging,mng_IHDR,13L);
3639 PNGLong(data+4,jng_width);
3640 PNGLong(data+8,jng_height);
3641 data[12]=jng_alpha_sample_depth;
3642 data[13]=0; /* color_type gray */
3643 data[14]=0; /* compression method 0 */
3644 data[15]=0; /* filter_method 0 */
3645 data[16]=0; /* interlace_method 0 */
3646 (void) WriteBlob(alpha_image,17,data);
3647 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3650 reading_idat=MagickTrue;
3653 if (memcmp(type,mng_JDAT,4) == 0)
3655 /* Copy chunk to color_image->blob */
3657 if (logging != MagickFalse)
3658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3659 " Copying JDAT chunk data to color_blob.");
3661 (void) WriteBlob(color_image,length,chunk);
3664 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3669 if (memcmp(type,mng_IDAT,4) == 0)
3674 /* Copy IDAT header and chunk data to alpha_image->blob */
3676 if (image_info->ping == MagickFalse)
3678 if (logging != MagickFalse)
3679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3680 " Copying IDAT chunk data to alpha_blob.");
3682 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3683 PNGType(data,mng_IDAT);
3684 LogPNGChunk(logging,mng_IDAT,length);
3685 (void) WriteBlob(alpha_image,4,data);
3686 (void) WriteBlob(alpha_image,length,chunk);
3687 (void) WriteBlobMSBULong(alpha_image,
3688 crc32(crc32(0,data,4),chunk,(uInt) length));
3692 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3697 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3699 /* Copy chunk data to alpha_image->blob */
3701 if (image_info->ping == MagickFalse)
3703 if (logging != MagickFalse)
3704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3705 " Copying JDAA chunk data to alpha_blob.");
3707 (void) WriteBlob(alpha_image,length,chunk);
3711 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3716 if (memcmp(type,mng_JSEP,4) == 0)
3718 read_JSEP=MagickTrue;
3721 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3726 if (memcmp(type,mng_bKGD,4) == 0)
3730 image->background_color.red=ScaleCharToQuantum(p[1]);
3731 image->background_color.green=image->background_color.red;
3732 image->background_color.blue=image->background_color.red;
3737 image->background_color.red=ScaleCharToQuantum(p[1]);
3738 image->background_color.green=ScaleCharToQuantum(p[3]);
3739 image->background_color.blue=ScaleCharToQuantum(p[5]);
3742 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3746 if (memcmp(type,mng_gAMA,4) == 0)
3749 image->gamma=((float) mng_get_long(p))*0.00001;
3751 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3755 if (memcmp(type,mng_cHRM,4) == 0)
3759 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3760 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3761 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3762 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3763 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3764 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3765 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3766 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3769 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3773 if (memcmp(type,mng_sRGB,4) == 0)
3777 image->rendering_intent=
3778 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3779 image->gamma=0.45455f;
3780 image->chromaticity.red_primary.x=0.6400f;
3781 image->chromaticity.red_primary.y=0.3300f;
3782 image->chromaticity.green_primary.x=0.3000f;
3783 image->chromaticity.green_primary.y=0.6000f;
3784 image->chromaticity.blue_primary.x=0.1500f;
3785 image->chromaticity.blue_primary.y=0.0600f;
3786 image->chromaticity.white_point.x=0.3127f;
3787 image->chromaticity.white_point.y=0.3290f;
3790 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3794 if (memcmp(type,mng_oFFs,4) == 0)
3798 image->page.x=(ssize_t) mng_get_long(p);
3799 image->page.y=(ssize_t) mng_get_long(&p[4]);
3801 if ((int) p[8] != 0)
3803 image->page.x/=10000;
3804 image->page.y/=10000;
3809 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3814 if (memcmp(type,mng_pHYs,4) == 0)
3818 image->x_resolution=(double) mng_get_long(p);
3819 image->y_resolution=(double) mng_get_long(&p[4]);
3820 if ((int) p[8] == PNG_RESOLUTION_METER)
3822 image->units=PixelsPerCentimeterResolution;
3823 image->x_resolution=image->x_resolution/100.0f;
3824 image->y_resolution=image->y_resolution/100.0f;
3828 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3833 if (memcmp(type,mng_iCCP,4) == 0)
3837 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3844 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3846 if (memcmp(type,mng_IEND,4))
3856 Finish up reading image data:
3858 o read main image from color_blob.
3862 o if (color_type has alpha)
3863 if alpha_encoding is PNG
3864 read secondary image from alpha_blob via ReadPNG
3865 if alpha_encoding is JPEG
3866 read secondary image from alpha_blob via ReadJPEG
3870 o copy intensity of secondary image into
3871 opacity samples of main image.
3873 o destroy the secondary image.
3876 (void) CloseBlob(color_image);
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3880 " Reading jng_image from color_blob.");
3882 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3883 color_image->filename);
3885 color_image_info->ping=MagickFalse; /* To do: avoid this */
3886 jng_image=ReadImage(color_image_info,exception);
3888 if (jng_image == (Image *) NULL)
3889 return((Image *) NULL);
3891 (void) RelinquishUniqueFileResource(color_image->filename);
3892 color_image=DestroyImage(color_image);
3893 color_image_info=DestroyImageInfo(color_image_info);
3895 if (jng_image == (Image *) NULL)
3896 return((Image *) NULL);
3898 if (logging != MagickFalse)
3899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3900 " Copying jng_image pixels to main image.");
3902 image->rows=jng_height;
3903 image->columns=jng_width;
3904 length=image->columns*sizeof(PixelPacket);
3906 for (y=0; y < (ssize_t) image->rows; y++)
3908 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3909 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3910 (void) CopyMagickMemory(q,s,length);
3912 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3916 jng_image=DestroyImage(jng_image);
3918 if (image_info->ping == MagickFalse)
3920 if (jng_color_type >= 12)
3922 if (jng_alpha_compression_method == 0)
3926 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3927 PNGType(data,mng_IEND);
3928 LogPNGChunk(logging,mng_IEND,0L);
3929 (void) WriteBlob(alpha_image,4,data);
3930 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3933 (void) CloseBlob(alpha_image);
3935 if (logging != MagickFalse)
3936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3937 " Reading opacity from alpha_blob.");
3939 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3940 "%s",alpha_image->filename);
3942 jng_image=ReadImage(alpha_image_info,exception);
3944 if (jng_image != (Image *) NULL)
3945 for (y=0; y < (ssize_t) image->rows; y++)
3947 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3949 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3951 if (image->matte != MagickFalse)
3952 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3953 q->opacity=(Quantum) QuantumRange-s->red;
3956 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3958 q->opacity=(Quantum) QuantumRange-s->red;
3959 if (q->opacity != OpaqueOpacity)
3960 image->matte=MagickTrue;
3963 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3966 (void) RelinquishUniqueFileResource(alpha_image->filename);
3967 alpha_image=DestroyImage(alpha_image);
3968 alpha_image_info=DestroyImageInfo(alpha_image_info);
3969 if (jng_image != (Image *) NULL)
3970 jng_image=DestroyImage(jng_image);
3974 /* Read the JNG image. */
3976 if (mng_info->mng_type == 0)
3978 mng_info->mng_width=jng_width;
3979 mng_info->mng_height=jng_height;
3982 if (image->page.width == 0 && image->page.height == 0)
3984 image->page.width=jng_width;
3985 image->page.height=jng_height;
3988 if (image->page.x == 0 && image->page.y == 0)
3990 image->page.x=mng_info->x_off[mng_info->object_id];
3991 image->page.y=mng_info->y_off[mng_info->object_id];
3996 image->page.y=mng_info->y_off[mng_info->object_id];
3999 mng_info->image_found++;
4000 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4001 2*GetBlobSize(image));
4003 if (logging != MagickFalse)
4004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " exit ReadOneJNGImage()");
4011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4015 % R e a d J N G I m a g e %
4019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4021 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4022 % (including the 8-byte signature) and returns it. It allocates the memory
4023 % necessary for the new Image structure and returns a pointer to the new
4026 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4028 % The format of the ReadJNGImage method is:
4030 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4033 % A description of each parameter follows:
4035 % o image_info: the image info.
4037 % o exception: return any errors or warnings in this structure.
4041 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4056 magic_number[MaxTextExtent];
4064 assert(image_info != (const ImageInfo *) NULL);
4065 assert(image_info->signature == MagickSignature);
4066 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4067 assert(exception != (ExceptionInfo *) NULL);
4068 assert(exception->signature == MagickSignature);
4069 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4070 image=AcquireImage(image_info);
4071 mng_info=(MngInfo *) NULL;
4072 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4074 if (status == MagickFalse)
4075 return((Image *) NULL);
4077 if (LocaleCompare(image_info->magick,"JNG") != 0)
4078 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4080 /* Verify JNG signature. */
4082 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4084 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4085 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4087 /* Allocate a MngInfo structure. */
4089 have_mng_structure=MagickFalse;
4090 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4092 if (mng_info == (MngInfo *) NULL)
4093 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4095 /* Initialize members of the MngInfo structure. */
4097 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4098 have_mng_structure=MagickTrue;
4100 mng_info->image=image;
4102 image=ReadOneJNGImage(mng_info,image_info,exception);
4103 MngInfoFreeStruct(mng_info,&have_mng_structure);
4105 if (image == (Image *) NULL)
4107 if (IsImageObject(previous) != MagickFalse)
4109 (void) CloseBlob(previous);
4110 (void) DestroyImageList(previous);
4113 if (logging != MagickFalse)
4114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4115 "exit ReadJNGImage() with error");
4117 return((Image *) NULL);
4119 (void) CloseBlob(image);
4121 if (image->columns == 0 || image->rows == 0)
4123 if (logging != MagickFalse)
4124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4125 "exit ReadJNGImage() with error");
4127 ThrowReaderException(CorruptImageError,"CorruptImage");
4130 if (logging != MagickFalse)
4131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4137 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4140 page_geometry[MaxTextExtent];
4173 #if defined(MNG_INSERT_LAYERS)
4175 mng_background_color;
4178 register unsigned char
4193 #if defined(MNG_INSERT_LAYERS)
4198 volatile unsigned int
4199 #ifdef MNG_OBJECT_BUFFERS
4200 mng_background_object=0,
4202 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4205 default_frame_timeout,
4207 #if defined(MNG_INSERT_LAYERS)
4213 /* These delays are all measured in image ticks_per_second,
4214 * not in MNG ticks_per_second
4217 default_frame_delay,
4221 #if defined(MNG_INSERT_LAYERS)
4230 previous_fb.bottom=0;
4232 previous_fb.right=0;
4234 default_fb.bottom=0;
4238 /* Open image file. */
4240 assert(image_info != (const ImageInfo *) NULL);
4241 assert(image_info->signature == MagickSignature);
4242 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4243 assert(exception != (ExceptionInfo *) NULL);
4244 assert(exception->signature == MagickSignature);
4245 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4246 image=AcquireImage(image_info);
4247 mng_info=(MngInfo *) NULL;
4248 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4250 if (status == MagickFalse)
4251 return((Image *) NULL);
4253 first_mng_object=MagickFalse;
4255 have_mng_structure=MagickFalse;
4257 /* Allocate a MngInfo structure. */
4259 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4261 if (mng_info == (MngInfo *) NULL)
4262 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4264 /* Initialize members of the MngInfo structure. */
4266 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4267 mng_info->image=image;
4268 have_mng_structure=MagickTrue;
4270 if (LocaleCompare(image_info->magick,"MNG") == 0)
4273 magic_number[MaxTextExtent];
4275 /* Verify MNG signature. */
4276 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4277 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4278 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4280 /* Initialize some nonzero members of the MngInfo structure. */
4281 for (i=0; i < MNG_MAX_OBJECTS; i++)
4283 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4284 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4286 mng_info->exists[0]=MagickTrue;
4289 first_mng_object=MagickTrue;
4291 #if defined(MNG_INSERT_LAYERS)
4292 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4294 default_frame_delay=0;
4295 default_frame_timeout=0;
4298 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4300 skip_to_iend=MagickFalse;
4301 term_chunk_found=MagickFalse;
4302 mng_info->framing_mode=1;
4303 #if defined(MNG_INSERT_LAYERS)
4304 mandatory_back=MagickFalse;
4306 #if defined(MNG_INSERT_LAYERS)
4307 mng_background_color=image->background_color;
4309 default_fb=mng_info->frame;
4310 previous_fb=mng_info->frame;
4314 type[MaxTextExtent];
4316 if (LocaleCompare(image_info->magick,"MNG") == 0)
4325 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4326 length=ReadBlobMSBLong(image);
4327 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4332 type[0],type[1],type[2],type[3],(double) length);
4334 if (length > PNG_UINT_31_MAX)
4338 ThrowReaderException(CorruptImageError,"CorruptImage");
4341 chunk=(unsigned char *) NULL;
4345 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4347 if (chunk == (unsigned char *) NULL)
4348 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4350 for (i=0; i < (ssize_t) length; i++)
4351 chunk[i]=(unsigned char) ReadBlobByte(image);
4356 (void) ReadBlobMSBLong(image); /* read crc word */
4358 #if !defined(JNG_SUPPORTED)
4359 if (memcmp(type,mng_JHDR,4) == 0)
4361 skip_to_iend=MagickTrue;
4363 if (mng_info->jhdr_warning == 0)
4364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4365 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4367 mng_info->jhdr_warning++;
4370 if (memcmp(type,mng_DHDR,4) == 0)
4372 skip_to_iend=MagickTrue;
4374 if (mng_info->dhdr_warning == 0)
4375 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4376 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4378 mng_info->dhdr_warning++;
4380 if (memcmp(type,mng_MEND,4) == 0)
4385 if (memcmp(type,mng_IEND,4) == 0)
4386 skip_to_iend=MagickFalse;
4389 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4391 if (logging != MagickFalse)
4392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4398 if (memcmp(type,mng_MHDR,4) == 0)
4400 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4401 (p[2] << 8) | p[3]);
4403 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4404 (p[6] << 8) | p[7]);
4406 if (logging != MagickFalse)
4408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4409 " MNG width: %.20g",(double) mng_info->mng_width);
4410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4411 " MNG height: %.20g",(double) mng_info->mng_height);
4415 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4417 if (mng_info->ticks_per_second == 0)
4418 default_frame_delay=0;
4421 default_frame_delay=1UL*image->ticks_per_second/
4422 mng_info->ticks_per_second;
4424 frame_delay=default_frame_delay;
4430 simplicity=(size_t) mng_get_long(p);
4433 mng_type=1; /* Full MNG */
4435 if ((simplicity != 0) && ((simplicity | 11) == 11))
4436 mng_type=2; /* LC */
4438 if ((simplicity != 0) && ((simplicity | 9) == 9))
4439 mng_type=3; /* VLC */
4441 #if defined(MNG_INSERT_LAYERS)
4443 insert_layers=MagickTrue;
4445 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4447 /* Allocate next image structure. */
4448 AcquireNextImage(image_info,image);
4450 if (GetNextImageInList(image) == (Image *) NULL)
4451 return((Image *) NULL);
4453 image=SyncNextImageInList(image);
4454 mng_info->image=image;
4457 if ((mng_info->mng_width > 65535L) ||
4458 (mng_info->mng_height > 65535L))
4459 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4461 (void) FormatMagickString(page_geometry,MaxTextExtent,
4462 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4463 mng_info->mng_height);
4465 mng_info->frame.left=0;
4466 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4467 mng_info->frame.top=0;
4468 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4469 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4471 for (i=0; i < MNG_MAX_OBJECTS; i++)
4472 mng_info->object_clip[i]=mng_info->frame;
4474 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4478 if (memcmp(type,mng_TERM,4) == 0)
4489 final_delay=(png_uint_32) mng_get_long(&p[2]);
4490 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4492 if (mng_iterations == PNG_UINT_31_MAX)
4495 image->iterations=mng_iterations;
4496 term_chunk_found=MagickTrue;
4499 if (logging != MagickFalse)
4501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4502 " repeat=%d",repeat);
4504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4505 " final_delay=%.20g",(double) final_delay);
4507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4508 " image->iterations=%.20g",(double) image->iterations);
4511 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4514 if (memcmp(type,mng_DEFI,4) == 0)
4517 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4518 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4521 object_id=(p[0] << 8) | p[1];
4523 if (mng_type == 2 && object_id != 0)
4524 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4525 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4528 if (object_id > MNG_MAX_OBJECTS)
4531 Instead ofsuing a warning we should allocate a larger
4532 MngInfo structure and continue.
4534 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4535 CoderError,"object id too large","`%s'",image->filename);
4536 object_id=MNG_MAX_OBJECTS;
4539 if (mng_info->exists[object_id])
4540 if (mng_info->frozen[object_id])
4542 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4543 (void) ThrowMagickException(&image->exception,
4544 GetMagickModule(),CoderError,
4545 "DEFI cannot redefine a frozen MNG object","`%s'",
4550 mng_info->exists[object_id]=MagickTrue;
4553 mng_info->invisible[object_id]=p[2];
4556 Extract object offset info.
4560 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4561 (p[5] << 16) | (p[6] << 8) | p[7]);
4563 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4564 (p[9] << 16) | (p[10] << 8) | p[11]);
4566 if (logging != MagickFalse)
4568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4569 " x_off[%d]: %.20g",object_id,(double)
4570 mng_info->x_off[object_id]);
4572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4573 " y_off[%d]: %.20g",object_id,(double)
4574 mng_info->y_off[object_id]);
4579 Extract object clipping info.
4582 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4585 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4588 if (memcmp(type,mng_bKGD,4) == 0)
4590 mng_info->have_global_bkgd=MagickFalse;
4594 mng_info->mng_global_bkgd.red=
4595 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4597 mng_info->mng_global_bkgd.green=
4598 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4600 mng_info->mng_global_bkgd.blue=
4601 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4603 mng_info->have_global_bkgd=MagickTrue;
4606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4609 if (memcmp(type,mng_BACK,4) == 0)
4611 #if defined(MNG_INSERT_LAYERS)
4613 mandatory_back=p[6];
4618 if (mandatory_back && length > 5)
4620 mng_background_color.red=
4621 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4623 mng_background_color.green=
4624 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4626 mng_background_color.blue=
4627 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4629 mng_background_color.opacity=OpaqueOpacity;
4632 #ifdef MNG_OBJECT_BUFFERS
4634 mng_background_object=(p[7] << 8) | p[8];
4637 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4641 if (memcmp(type,mng_PLTE,4) == 0)
4643 /* Read global PLTE. */
4645 if (length && (length < 769))
4647 if (mng_info->global_plte == (png_colorp) NULL)
4648 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4649 sizeof(*mng_info->global_plte));
4651 for (i=0; i < (ssize_t) (length/3); i++)
4653 mng_info->global_plte[i].red=p[3*i];
4654 mng_info->global_plte[i].green=p[3*i+1];
4655 mng_info->global_plte[i].blue=p[3*i+2];
4658 mng_info->global_plte_length=(unsigned int) (length/3);
4661 for ( ; i < 256; i++)
4663 mng_info->global_plte[i].red=i;
4664 mng_info->global_plte[i].green=i;
4665 mng_info->global_plte[i].blue=i;
4669 mng_info->global_plte_length=256;
4672 mng_info->global_plte_length=0;
4674 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4678 if (memcmp(type,mng_tRNS,4) == 0)
4680 /* read global tRNS */
4683 for (i=0; i < (ssize_t) length; i++)
4684 mng_info->global_trns[i]=p[i];
4687 for ( ; i < 256; i++)
4688 mng_info->global_trns[i]=255;
4690 mng_info->global_trns_length=(unsigned int) length;
4691 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4694 if (memcmp(type,mng_gAMA,4) == 0)
4701 igamma=mng_get_long(p);
4702 mng_info->global_gamma=((float) igamma)*0.00001;
4703 mng_info->have_global_gama=MagickTrue;
4707 mng_info->have_global_gama=MagickFalse;
4709 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4713 if (memcmp(type,mng_cHRM,4) == 0)
4715 /* Read global cHRM */
4719 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4720 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4721 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4722 mng_info->global_chrm.red_primary.y=0.00001*
4723 mng_get_long(&p[12]);
4724 mng_info->global_chrm.green_primary.x=0.00001*
4725 mng_get_long(&p[16]);
4726 mng_info->global_chrm.green_primary.y=0.00001*
4727 mng_get_long(&p[20]);
4728 mng_info->global_chrm.blue_primary.x=0.00001*
4729 mng_get_long(&p[24]);
4730 mng_info->global_chrm.blue_primary.y=0.00001*
4731 mng_get_long(&p[28]);
4732 mng_info->have_global_chrm=MagickTrue;
4735 mng_info->have_global_chrm=MagickFalse;
4737 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4741 if (memcmp(type,mng_sRGB,4) == 0)
4748 mng_info->global_srgb_intent=
4749 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4750 mng_info->have_global_srgb=MagickTrue;
4753 mng_info->have_global_srgb=MagickFalse;
4755 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4759 if (memcmp(type,mng_iCCP,4) == 0)
4767 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4772 if (memcmp(type,mng_FRAM,4) == 0)
4775 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4776 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4779 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4780 image->delay=frame_delay;
4782 frame_delay=default_frame_delay;
4783 frame_timeout=default_frame_timeout;
4788 mng_info->framing_mode=p[0];
4790 if (logging != MagickFalse)
4791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4792 " Framing_mode=%d",mng_info->framing_mode);
4796 /* Note the delay and frame clipping boundaries. */
4798 p++; /* framing mode */
4800 while (*p && ((p-chunk) < (ssize_t) length))
4801 p++; /* frame name */
4803 p++; /* frame name terminator */
4805 if ((p-chunk) < (ssize_t) (length-4))
4812 change_delay=(*p++);
4813 change_timeout=(*p++);
4814 change_clipping=(*p++);
4815 p++; /* change_sync */
4819 frame_delay=1UL*image->ticks_per_second*
4822 if (mng_info->ticks_per_second != 0)
4823 frame_delay/=mng_info->ticks_per_second;
4826 frame_delay=PNG_UINT_31_MAX;
4828 if (change_delay == 2)
4829 default_frame_delay=frame_delay;
4833 if (logging != MagickFalse)
4834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4835 " Framing_delay=%.20g",(double) frame_delay);
4840 frame_timeout=1UL*image->ticks_per_second*
4843 if (mng_info->ticks_per_second != 0)
4844 frame_timeout/=mng_info->ticks_per_second;
4847 frame_timeout=PNG_UINT_31_MAX;
4849 if (change_delay == 2)
4850 default_frame_timeout=frame_timeout;
4854 if (logging != MagickFalse)
4855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4856 " Framing_timeout=%.20g",(double) frame_timeout);
4859 if (change_clipping)
4861 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4865 if (logging != MagickFalse)
4866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4867 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4868 (double) fb.left,(double) fb.right,(double) fb.top,
4869 (double) fb.bottom);
4871 if (change_clipping == 2)
4877 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4879 subframe_width=(size_t) (mng_info->clip.right
4880 -mng_info->clip.left);
4882 subframe_height=(size_t) (mng_info->clip.bottom
4883 -mng_info->clip.top);
4885 Insert a background layer behind the frame if framing_mode is 4.
4887 #if defined(MNG_INSERT_LAYERS)
4888 if (logging != MagickFalse)
4889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4890 " subframe_width=%.20g, subframe_height=%.20g",(double)
4891 subframe_width,(double) subframe_height);
4893 if (insert_layers && (mng_info->framing_mode == 4) &&
4894 (subframe_width) && (subframe_height))
4896 /* Allocate next image structure. */
4897 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4899 AcquireNextImage(image_info,image);
4901 if (GetNextImageInList(image) == (Image *) NULL)
4903 image=DestroyImageList(image);
4904 MngInfoFreeStruct(mng_info,&have_mng_structure);
4905 return((Image *) NULL);
4908 image=SyncNextImageInList(image);
4911 mng_info->image=image;
4913 if (term_chunk_found)
4915 image->start_loop=MagickTrue;
4916 image->iterations=mng_iterations;
4917 term_chunk_found=MagickFalse;
4921 image->start_loop=MagickFalse;
4923 image->columns=subframe_width;
4924 image->rows=subframe_height;
4925 image->page.width=subframe_width;
4926 image->page.height=subframe_height;
4927 image->page.x=mng_info->clip.left;
4928 image->page.y=mng_info->clip.top;
4929 image->background_color=mng_background_color;
4930 image->matte=MagickFalse;
4932 (void) SetImageBackgroundColor(image);
4934 if (logging != MagickFalse)
4935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4936 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4937 (double) mng_info->clip.left,(double) mng_info->clip.right,
4938 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4941 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4944 if (memcmp(type,mng_CLIP,4) == 0)
4953 first_object=(p[0] << 8) | p[1];
4954 last_object=(p[2] << 8) | p[3];
4956 for (i=(int) first_object; i <= (int) last_object; i++)
4958 if (mng_info->exists[i] && !mng_info->frozen[i])
4963 box=mng_info->object_clip[i];
4964 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4968 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971 if (memcmp(type,mng_SAVE,4) == 0)
4973 for (i=1; i < MNG_MAX_OBJECTS; i++)
4974 if (mng_info->exists[i])
4976 mng_info->frozen[i]=MagickTrue;
4977 #ifdef MNG_OBJECT_BUFFERS
4978 if (mng_info->ob[i] != (MngBuffer *) NULL)
4979 mng_info->ob[i]->frozen=MagickTrue;
4984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4989 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4991 /* Read DISC or SEEK. */
4993 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4995 for (i=1; i < MNG_MAX_OBJECTS; i++)
4996 MngInfoDiscardObject(mng_info,i);
5004 for (j=0; j < (ssize_t) length; j+=2)
5006 i=p[j] << 8 | p[j+1];
5007 MngInfoDiscardObject(mng_info,i);
5012 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5017 if (memcmp(type,mng_MOVE,4) == 0)
5025 first_object=(p[0] << 8) | p[1];
5026 last_object=(p[2] << 8) | p[3];
5027 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5029 if (mng_info->exists[i] && !mng_info->frozen[i])
5037 old_pair.a=mng_info->x_off[i];
5038 old_pair.b=mng_info->y_off[i];
5039 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5040 mng_info->x_off[i]=new_pair.a;
5041 mng_info->y_off[i]=new_pair.b;
5045 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5049 if (memcmp(type,mng_LOOP,4) == 0)
5051 ssize_t loop_iters=1;
5052 loop_level=chunk[0];
5053 mng_info->loop_active[loop_level]=1; /* mark loop active */
5055 /* Record starting point. */
5056 loop_iters=mng_get_long(&chunk[1]);
5058 if (logging != MagickFalse)
5059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5060 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5061 (double) loop_iters);
5063 if (loop_iters == 0)
5064 skipping_loop=loop_level;
5068 mng_info->loop_jump[loop_level]=TellBlob(image);
5069 mng_info->loop_count[loop_level]=loop_iters;
5072 mng_info->loop_iteration[loop_level]=0;
5073 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5077 if (memcmp(type,mng_ENDL,4) == 0)
5079 loop_level=chunk[0];
5081 if (skipping_loop > 0)
5083 if (skipping_loop == loop_level)
5086 Found end of zero-iteration loop.
5089 mng_info->loop_active[loop_level]=0;
5095 if (mng_info->loop_active[loop_level] == 1)
5097 mng_info->loop_count[loop_level]--;
5098 mng_info->loop_iteration[loop_level]++;
5100 if (logging != MagickFalse)
5101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5102 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5103 (double) loop_level,(double)
5104 mng_info->loop_count[loop_level]);
5106 if (mng_info->loop_count[loop_level] != 0)
5108 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5112 ThrowReaderException(CorruptImageError,
5113 "ImproperImageHeader");
5124 mng_info->loop_active[loop_level]=0;
5126 for (i=0; i < loop_level; i++)
5127 if (mng_info->loop_active[i] == 1)
5128 last_level=(short) i;
5129 loop_level=last_level;
5134 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5138 if (memcmp(type,mng_CLON,4) == 0)
5140 if (mng_info->clon_warning == 0)
5141 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5142 CoderError,"CLON is not implemented yet","`%s'",
5145 mng_info->clon_warning++;
5148 if (memcmp(type,mng_MAGN,4) == 0)
5163 magn_first=(p[0] << 8) | p[1];
5169 magn_last=(p[2] << 8) | p[3];
5172 magn_last=magn_first;
5173 #ifndef MNG_OBJECT_BUFFERS
5174 if (magn_first || magn_last)
5175 if (mng_info->magn_warning == 0)
5177 (void) ThrowMagickException(&image->exception,
5178 GetMagickModule(),CoderError,
5179 "MAGN is not implemented yet for nonzero objects",
5180 "`%s'",image->filename);
5182 mng_info->magn_warning++;
5192 magn_mx=(p[5] << 8) | p[6];
5201 magn_my=(p[7] << 8) | p[8];
5210 magn_ml=(p[9] << 8) | p[10];
5219 magn_mr=(p[11] << 8) | p[12];
5228 magn_mt=(p[13] << 8) | p[14];
5237 magn_mb=(p[15] << 8) | p[16];
5249 magn_methy=magn_methx;
5252 if (magn_methx > 5 || magn_methy > 5)
5253 if (mng_info->magn_warning == 0)
5255 (void) ThrowMagickException(&image->exception,
5256 GetMagickModule(),CoderError,
5257 "Unknown MAGN method in MNG datastream","`%s'",
5260 mng_info->magn_warning++;
5262 #ifdef MNG_OBJECT_BUFFERS
5263 /* Magnify existing objects in the range magn_first to magn_last */
5265 if (magn_first == 0 || magn_last == 0)
5267 /* Save the magnification factors for object 0 */
5268 mng_info->magn_mb=magn_mb;
5269 mng_info->magn_ml=magn_ml;
5270 mng_info->magn_mr=magn_mr;
5271 mng_info->magn_mt=magn_mt;
5272 mng_info->magn_mx=magn_mx;
5273 mng_info->magn_my=magn_my;
5274 mng_info->magn_methx=magn_methx;
5275 mng_info->magn_methy=magn_methy;
5279 if (memcmp(type,mng_PAST,4) == 0)
5281 if (mng_info->past_warning == 0)
5282 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5283 CoderError,"PAST is not implemented yet","`%s'",
5286 mng_info->past_warning++;
5289 if (memcmp(type,mng_SHOW,4) == 0)
5291 if (mng_info->show_warning == 0)
5292 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5293 CoderError,"SHOW is not implemented yet","`%s'",
5296 mng_info->show_warning++;
5299 if (memcmp(type,mng_sBIT,4) == 0)
5302 mng_info->have_global_sbit=MagickFalse;
5306 mng_info->global_sbit.gray=p[0];
5307 mng_info->global_sbit.red=p[0];
5308 mng_info->global_sbit.green=p[1];
5309 mng_info->global_sbit.blue=p[2];
5310 mng_info->global_sbit.alpha=p[3];
5311 mng_info->have_global_sbit=MagickTrue;
5314 if (memcmp(type,mng_pHYs,4) == 0)
5318 mng_info->global_x_pixels_per_unit=
5319 (size_t) mng_get_long(p);
5320 mng_info->global_y_pixels_per_unit=
5321 (size_t) mng_get_long(&p[4]);
5322 mng_info->global_phys_unit_type=p[8];
5323 mng_info->have_global_phys=MagickTrue;
5327 mng_info->have_global_phys=MagickFalse;
5329 if (memcmp(type,mng_pHYg,4) == 0)
5331 if (mng_info->phyg_warning == 0)
5332 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5333 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5335 mng_info->phyg_warning++;
5337 if (memcmp(type,mng_BASI,4) == 0)
5339 skip_to_iend=MagickTrue;
5341 if (mng_info->basi_warning == 0)
5342 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5343 CoderError,"BASI is not implemented yet","`%s'",
5346 mng_info->basi_warning++;
5347 #ifdef MNG_BASI_SUPPORTED
5348 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5349 (p[2] << 8) | p[3]);
5350 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5351 (p[6] << 8) | p[7]);
5352 basi_color_type=p[8];
5353 basi_compression_method=p[9];
5354 basi_filter_type=p[10];
5355 basi_interlace_method=p[11];
5357 basi_red=(p[12] << 8) & p[13];
5363 basi_green=(p[14] << 8) & p[15];
5369 basi_blue=(p[16] << 8) & p[17];
5375 basi_alpha=(p[18] << 8) & p[19];
5379 if (basi_sample_depth == 16)
5386 basi_viewable=p[20];
5392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5396 if (memcmp(type,mng_IHDR,4)
5397 #if defined(JNG_SUPPORTED)
5398 && memcmp(type,mng_JHDR,4)
5402 /* Not an IHDR or JHDR chunk */
5404 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5409 if (logging != MagickFalse)
5410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5411 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5413 mng_info->exists[object_id]=MagickTrue;
5414 mng_info->viewable[object_id]=MagickTrue;
5416 if (mng_info->invisible[object_id])
5418 if (logging != MagickFalse)
5419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5420 " Skipping invisible object");
5422 skip_to_iend=MagickTrue;
5423 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5426 #if defined(MNG_INSERT_LAYERS)
5428 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5430 image_width=(size_t) mng_get_long(p);
5431 image_height=(size_t) mng_get_long(&p[4]);
5433 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436 Insert a transparent background layer behind the entire animation
5437 if it is not full screen.
5439 #if defined(MNG_INSERT_LAYERS)
5440 if (insert_layers && mng_type && first_mng_object)
5442 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5443 (image_width < mng_info->mng_width) ||
5444 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5445 (image_height < mng_info->mng_height) ||
5446 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5448 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5451 Allocate next image structure.
5453 AcquireNextImage(image_info,image);
5455 if (GetNextImageInList(image) == (Image *) NULL)
5457 image=DestroyImageList(image);
5458 MngInfoFreeStruct(mng_info,&have_mng_structure);
5459 return((Image *) NULL);
5462 image=SyncNextImageInList(image);
5464 mng_info->image=image;
5466 if (term_chunk_found)
5468 image->start_loop=MagickTrue;
5469 image->iterations=mng_iterations;
5470 term_chunk_found=MagickFalse;
5474 image->start_loop=MagickFalse;
5476 /* Make a background rectangle. */
5479 image->columns=mng_info->mng_width;
5480 image->rows=mng_info->mng_height;
5481 image->page.width=mng_info->mng_width;
5482 image->page.height=mng_info->mng_height;
5485 image->background_color=mng_background_color;
5486 (void) SetImageBackgroundColor(image);
5487 if (logging != MagickFalse)
5488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5489 " Inserted transparent background layer, W=%.20g, H=%.20g",
5490 (double) mng_info->mng_width,(double) mng_info->mng_height);
5494 Insert a background layer behind the upcoming image if
5495 framing_mode is 3, and we haven't already inserted one.
5497 if (insert_layers && (mng_info->framing_mode == 3) &&
5498 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5499 (simplicity & 0x08)))
5501 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5504 Allocate next image structure.
5506 AcquireNextImage(image_info,image);
5508 if (GetNextImageInList(image) == (Image *) NULL)
5510 image=DestroyImageList(image);
5511 MngInfoFreeStruct(mng_info,&have_mng_structure);
5512 return((Image *) NULL);
5515 image=SyncNextImageInList(image);
5518 mng_info->image=image;
5520 if (term_chunk_found)
5522 image->start_loop=MagickTrue;
5523 image->iterations=mng_iterations;
5524 term_chunk_found=MagickFalse;
5528 image->start_loop=MagickFalse;
5531 image->columns=subframe_width;
5532 image->rows=subframe_height;
5533 image->page.width=subframe_width;
5534 image->page.height=subframe_height;
5535 image->page.x=mng_info->clip.left;
5536 image->page.y=mng_info->clip.top;
5537 image->background_color=mng_background_color;
5538 image->matte=MagickFalse;
5539 (void) SetImageBackgroundColor(image);
5541 if (logging != MagickFalse)
5542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5543 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5544 (double) mng_info->clip.left,(double) mng_info->clip.right,
5545 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5547 #endif /* MNG_INSERT_LAYERS */
5548 first_mng_object=MagickFalse;
5550 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5553 Allocate next image structure.
5555 AcquireNextImage(image_info,image);
5557 if (GetNextImageInList(image) == (Image *) NULL)
5559 image=DestroyImageList(image);
5560 MngInfoFreeStruct(mng_info,&have_mng_structure);
5561 return((Image *) NULL);
5564 image=SyncNextImageInList(image);
5566 mng_info->image=image;
5567 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5568 GetBlobSize(image));
5570 if (status == MagickFalse)
5573 if (term_chunk_found)
5575 image->start_loop=MagickTrue;
5576 term_chunk_found=MagickFalse;
5580 image->start_loop=MagickFalse;
5582 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5584 image->delay=frame_delay;
5585 frame_delay=default_frame_delay;
5591 image->page.width=mng_info->mng_width;
5592 image->page.height=mng_info->mng_height;
5593 image->page.x=mng_info->x_off[object_id];
5594 image->page.y=mng_info->y_off[object_id];
5595 image->iterations=mng_iterations;
5598 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5601 if (logging != MagickFalse)
5602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5603 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5606 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5609 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5613 mng_info->image=image;
5614 mng_info->mng_type=mng_type;
5615 mng_info->object_id=object_id;
5617 if (memcmp(type,mng_IHDR,4) == 0)
5618 image=ReadOnePNGImage(mng_info,image_info,exception);
5620 #if defined(JNG_SUPPORTED)
5622 image=ReadOneJNGImage(mng_info,image_info,exception);
5625 if (image == (Image *) NULL)
5627 if (IsImageObject(previous) != MagickFalse)
5629 (void) DestroyImageList(previous);
5630 (void) CloseBlob(previous);
5633 MngInfoFreeStruct(mng_info,&have_mng_structure);
5634 return((Image *) NULL);
5637 if (image->columns == 0 || image->rows == 0)
5639 (void) CloseBlob(image);
5640 image=DestroyImageList(image);
5641 MngInfoFreeStruct(mng_info,&have_mng_structure);
5642 return((Image *) NULL);
5645 mng_info->image=image;
5652 if (mng_info->magn_methx || mng_info->magn_methy)
5658 if (logging != MagickFalse)
5659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5660 " Processing MNG MAGN chunk");
5662 if (mng_info->magn_methx == 1)
5664 magnified_width=mng_info->magn_ml;
5666 if (image->columns > 1)
5667 magnified_width += mng_info->magn_mr;
5669 if (image->columns > 2)
5670 magnified_width += (png_uint_32)
5671 ((image->columns-2)*(mng_info->magn_mx));
5676 magnified_width=(png_uint_32) image->columns;
5678 if (image->columns > 1)
5679 magnified_width += mng_info->magn_ml-1;
5681 if (image->columns > 2)
5682 magnified_width += mng_info->magn_mr-1;
5684 if (image->columns > 3)
5685 magnified_width += (png_uint_32)
5686 ((image->columns-3)*(mng_info->magn_mx-1));
5689 if (mng_info->magn_methy == 1)
5691 magnified_height=mng_info->magn_mt;
5693 if (image->rows > 1)
5694 magnified_height += mng_info->magn_mb;
5696 if (image->rows > 2)
5697 magnified_height += (png_uint_32)
5698 ((image->rows-2)*(mng_info->magn_my));
5703 magnified_height=(png_uint_32) image->rows;
5705 if (image->rows > 1)
5706 magnified_height += mng_info->magn_mt-1;
5708 if (image->rows > 2)
5709 magnified_height += mng_info->magn_mb-1;
5711 if (image->rows > 3)
5712 magnified_height += (png_uint_32)
5713 ((image->rows-3)*(mng_info->magn_my-1));
5716 if (magnified_height > image->rows ||
5717 magnified_width > image->columns)
5732 register PixelPacket
5744 /* Allocate next image structure. */
5746 if (logging != MagickFalse)
5747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5748 " Allocate magnified image");
5750 AcquireNextImage(image_info,image);
5752 if (GetNextImageInList(image) == (Image *) NULL)
5754 image=DestroyImageList(image);
5755 MngInfoFreeStruct(mng_info,&have_mng_structure);
5756 return((Image *) NULL);
5759 large_image=SyncNextImageInList(image);
5761 large_image->columns=magnified_width;
5762 large_image->rows=magnified_height;
5764 magn_methx=mng_info->magn_methx;
5765 magn_methy=mng_info->magn_methy;
5767 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5768 #define QM unsigned short
5769 if (magn_methx != 1 || magn_methy != 1)
5772 Scale pixels to unsigned shorts to prevent
5773 overflow of intermediate values of interpolations
5775 for (y=0; y < (ssize_t) image->rows; y++)
5777 q=GetAuthenticPixels(image,0,y,image->columns,1,
5780 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5782 q->red=ScaleQuantumToShort(q->red);
5783 q->green=ScaleQuantumToShort(q->green);
5784 q->blue=ScaleQuantumToShort(q->blue);
5785 q->opacity=ScaleQuantumToShort(q->opacity);
5789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5797 if (image->matte != MagickFalse)
5798 (void) SetImageBackgroundColor(large_image);
5802 large_image->background_color.opacity=OpaqueOpacity;
5803 (void) SetImageBackgroundColor(large_image);
5805 if (magn_methx == 4)
5808 if (magn_methx == 5)
5811 if (magn_methy == 4)
5814 if (magn_methy == 5)
5818 /* magnify the rows into the right side of the large image */
5820 if (logging != MagickFalse)
5821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5822 " Magnify the rows to %.20g",(double) large_image->rows);
5823 m=(ssize_t) mng_info->magn_mt;
5825 length=(size_t) image->columns;
5826 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5827 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5829 if ((prev == (PixelPacket *) NULL) ||
5830 (next == (PixelPacket *) NULL))
5832 image=DestroyImageList(image);
5833 MngInfoFreeStruct(mng_info,&have_mng_structure);
5834 ThrowReaderException(ResourceLimitError,
5835 "MemoryAllocationFailed");
5838 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5839 (void) CopyMagickMemory(next,n,length);
5841 for (y=0; y < (ssize_t) image->rows; y++)
5844 m=(ssize_t) mng_info->magn_mt;
5846 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5847 m=(ssize_t) mng_info->magn_mb;
5849 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5850 m=(ssize_t) mng_info->magn_mb;
5852 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5856 m=(ssize_t) mng_info->magn_my;
5862 if (y < (ssize_t) image->rows-1)
5864 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5866 (void) CopyMagickMemory(next,n,length);
5869 for (i=0; i < m; i++, yy++)
5871 register PixelPacket
5874 assert(yy < (ssize_t) large_image->rows);
5877 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5879 q+=(large_image->columns-image->columns);
5881 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5883 /* To do: get color as function of indexes[x] */
5885 if (image->storage_class == PseudoClass)
5890 if (magn_methy <= 1)
5892 *q=(*pixels); /* replicate previous */
5895 else if (magn_methy == 2 || magn_methy == 4)
5903 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5904 -(*pixels).red)+m))/((ssize_t) (m*2))
5906 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5907 -(*pixels).green)+m))/((ssize_t) (m*2))
5909 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5910 -(*pixels).blue)+m))/((ssize_t) (m*2))
5913 if (image->matte != MagickFalse)
5914 (*q).opacity=(QM) (((ssize_t)
5916 -(*pixels).opacity)+m))
5917 /((ssize_t) (m*2))+(*pixels).opacity);
5920 if (magn_methy == 4)
5922 /* Replicate nearest */
5923 if (i <= ((m+1) << 1))
5924 (*q).opacity=(*pixels).opacity+0;
5926 (*q).opacity=(*n).opacity+0;
5930 else /* if (magn_methy == 3 || magn_methy == 5) */
5932 /* Replicate nearest */
5933 if (i <= ((m+1) << 1))
5939 if (magn_methy == 5)
5941 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5942 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5943 +(*pixels).opacity);
5951 if (SyncAuthenticPixels(large_image,exception) == 0)
5957 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5958 next=(PixelPacket *) RelinquishMagickMemory(next);
5960 length=image->columns;
5962 if (logging != MagickFalse)
5963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5964 " Delete original image");
5966 DeleteImageFromList(&image);
5970 mng_info->image=image;
5972 /* magnify the columns */
5973 if (logging != MagickFalse)
5974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5975 " Magnify the columns to %.20g",(double) image->columns);
5977 for (y=0; y < (ssize_t) image->rows; y++)
5979 register PixelPacket
5982 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5983 pixels=q+(image->columns-length);
5986 for (x=(ssize_t) (image->columns-length);
5987 x < (ssize_t) image->columns; x++)
5989 if (x == (ssize_t) (image->columns-length))
5990 m=(ssize_t) mng_info->magn_ml;
5992 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5993 m=(ssize_t) mng_info->magn_mr;
5995 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5996 m=(ssize_t) mng_info->magn_mr;
5998 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6002 m=(ssize_t) mng_info->magn_mx;
6004 for (i=0; i < m; i++)
6006 if (magn_methx <= 1)
6008 /* replicate previous */
6012 else if (magn_methx == 2 || magn_methx == 4)
6020 (*q).red=(QM) ((2*i*((*n).red
6022 /((ssize_t) (m*2))+(*pixels).red);
6023 (*q).green=(QM) ((2*i*((*n).green
6025 +m)/((ssize_t) (m*2))+(*pixels).green);
6026 (*q).blue=(QM) ((2*i*((*n).blue
6028 /((ssize_t) (m*2))+(*pixels).blue);
6029 if (image->matte != MagickFalse)
6030 (*q).opacity=(QM) ((2*i*((*n).opacity
6031 -(*pixels).opacity)+m)/((ssize_t) (m*2))
6032 +(*pixels).opacity);
6035 if (magn_methx == 4)
6037 /* Replicate nearest */
6038 if (i <= ((m+1) << 1))
6039 (*q).opacity=(*pixels).opacity+0;
6041 (*q).opacity=(*n).opacity+0;
6045 else /* if (magn_methx == 3 || magn_methx == 5) */
6047 /* Replicate nearest */
6048 if (i <= ((m+1) << 1))
6054 if (magn_methx == 5)
6057 (*q).opacity=(QM) ((2*i*((*n).opacity
6058 -(*pixels).opacity)+m) /((ssize_t) (m*2))
6059 +(*pixels).opacity);
6068 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6071 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
6072 if (magn_methx != 1 || magn_methy != 1)
6075 Rescale pixels to Quantum
6077 for (y=0; y < (ssize_t) image->rows; y++)
6079 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6081 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6083 q->red=ScaleShortToQuantum(q->red);
6084 q->green=ScaleShortToQuantum(q->green);
6085 q->blue=ScaleShortToQuantum(q->blue);
6086 q->opacity=ScaleShortToQuantum(q->opacity);
6090 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6095 if (logging != MagickFalse)
6096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6097 " Finished MAGN processing");
6102 Crop_box is with respect to the upper left corner of the MNG.
6104 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6105 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6106 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6107 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6108 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6109 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6110 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6111 if ((crop_box.left != (mng_info->image_box.left
6112 +mng_info->x_off[object_id])) ||
6113 (crop_box.right != (mng_info->image_box.right
6114 +mng_info->x_off[object_id])) ||
6115 (crop_box.top != (mng_info->image_box.top
6116 +mng_info->y_off[object_id])) ||
6117 (crop_box.bottom != (mng_info->image_box.bottom
6118 +mng_info->y_off[object_id])))
6120 if (logging != MagickFalse)
6121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6122 " Crop the PNG image");
6124 if ((crop_box.left < crop_box.right) &&
6125 (crop_box.top < crop_box.bottom))
6134 Crop_info is with respect to the upper left corner of
6137 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6138 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6139 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6140 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6141 image->page.width=image->columns;
6142 image->page.height=image->rows;
6145 im=CropImage(image,&crop_info,exception);
6147 if (im != (Image *) NULL)
6149 image->columns=im->columns;
6150 image->rows=im->rows;
6151 im=DestroyImage(im);
6152 image->page.width=image->columns;
6153 image->page.height=image->rows;
6154 image->page.x=crop_box.left;
6155 image->page.y=crop_box.top;
6162 No pixels in crop area. The MNG spec still requires
6163 a layer, though, so make a single transparent pixel in
6164 the top left corner.
6169 (void) SetImageBackgroundColor(image);
6170 image->page.width=1;
6171 image->page.height=1;
6176 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6177 image=mng_info->image;
6181 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6182 /* PNG does not handle depths greater than 16 so reduce it even
6185 if (image->depth > 16)
6189 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6190 if (LosslessReduceDepthOK(image) != MagickFalse)
6194 GetImageException(image,exception);
6196 if (image_info->number_scenes != 0)
6198 if (mng_info->scenes_found >
6199 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6203 if (logging != MagickFalse)
6204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6205 " Finished reading image datastream.");
6207 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6209 (void) CloseBlob(image);
6211 if (logging != MagickFalse)
6212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6213 " Finished reading all image datastreams.");
6215 #if defined(MNG_INSERT_LAYERS)
6216 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6217 (mng_info->mng_height))
6220 Insert a background layer if nothing else was found.
6222 if (logging != MagickFalse)
6223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6224 " No images found. Inserting a background layer.");
6226 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6229 Allocate next image structure.
6231 AcquireNextImage(image_info,image);
6232 if (GetNextImageInList(image) == (Image *) NULL)
6234 image=DestroyImageList(image);
6235 MngInfoFreeStruct(mng_info,&have_mng_structure);
6237 if (logging != MagickFalse)
6238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6239 " Allocation failed, returning NULL.");
6241 return((Image *) NULL);
6243 image=SyncNextImageInList(image);
6245 image->columns=mng_info->mng_width;
6246 image->rows=mng_info->mng_height;
6247 image->page.width=mng_info->mng_width;
6248 image->page.height=mng_info->mng_height;
6251 image->background_color=mng_background_color;
6252 image->matte=MagickFalse;
6254 if (image_info->ping == MagickFalse)
6255 (void) SetImageBackgroundColor(image);
6257 mng_info->image_found++;
6260 image->iterations=mng_iterations;
6262 if (mng_iterations == 1)
6263 image->start_loop=MagickTrue;
6265 while (GetPreviousImageInList(image) != (Image *) NULL)
6268 if (image_count > 10*mng_info->image_found)
6270 if (logging != MagickFalse)
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6273 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6274 CoderError,"Linked list is corrupted, beginning of list not found",
6275 "`%s'",image_info->filename);
6277 return((Image *) NULL);
6280 image=GetPreviousImageInList(image);
6282 if (GetNextImageInList(image) == (Image *) NULL)
6284 if (logging != MagickFalse)
6285 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6287 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6288 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6289 image_info->filename);
6293 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6294 GetNextImageInList(image) ==
6297 if (logging != MagickFalse)
6298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6299 " First image null");
6301 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6302 CoderError,"image->next for first image is NULL but shouldn't be.",
6303 "`%s'",image_info->filename);
6306 if (mng_info->image_found == 0)
6308 if (logging != MagickFalse)
6309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6310 " No visible images found.");
6312 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6313 CoderError,"No visible images in file","`%s'",image_info->filename);
6315 if (image != (Image *) NULL)
6316 image=DestroyImageList(image);
6318 MngInfoFreeStruct(mng_info,&have_mng_structure);
6319 return((Image *) NULL);
6322 if (mng_info->ticks_per_second)
6323 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6324 final_delay/mng_info->ticks_per_second;
6327 image->start_loop=MagickTrue;
6329 /* Find final nonzero image delay */
6330 final_image_delay=0;
6332 while (GetNextImageInList(image) != (Image *) NULL)
6335 final_image_delay=image->delay;
6337 image=GetNextImageInList(image);
6340 if (final_delay < final_image_delay)
6341 final_delay=final_image_delay;
6343 image->delay=final_delay;
6345 if (logging != MagickFalse)
6346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6347 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6348 (double) final_delay);
6350 if (logging != MagickFalse)
6356 image=GetFirstImageInList(image);
6358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6359 " Before coalesce:");
6361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6362 " scene 0 delay=%.20g",(double) image->delay);
6364 while (GetNextImageInList(image) != (Image *) NULL)
6366 image=GetNextImageInList(image);
6367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6368 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6372 image=GetFirstImageInList(image);
6373 #ifdef MNG_COALESCE_LAYERS
6383 if (logging != MagickFalse)
6384 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6387 next_image=CoalesceImages(image,&image->exception);
6389 if (next_image == (Image *) NULL)
6390 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6392 image=DestroyImageList(image);
6395 for (next=image; next != (Image *) NULL; next=next_image)
6397 next->page.width=mng_info->mng_width;
6398 next->page.height=mng_info->mng_height;
6401 next->scene=scene++;
6402 next_image=GetNextImageInList(next);
6404 if (next_image == (Image *) NULL)
6407 if (next->delay == 0)
6410 next_image->previous=GetPreviousImageInList(next);
6411 if (GetPreviousImageInList(next) == (Image *) NULL)
6414 next->previous->next=next_image;
6415 next=DestroyImage(next);
6421 while (GetNextImageInList(image) != (Image *) NULL)
6422 image=GetNextImageInList(image);
6424 image->dispose=BackgroundDispose;
6426 if (logging != MagickFalse)
6432 image=GetFirstImageInList(image);
6434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6435 " After coalesce:");
6437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6438 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6439 (double) image->dispose);
6441 while (GetNextImageInList(image) != (Image *) NULL)
6443 image=GetNextImageInList(image);
6445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6446 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6447 (double) image->delay,(double) image->dispose);
6451 image=GetFirstImageInList(image);
6452 MngInfoFreeStruct(mng_info,&have_mng_structure);
6453 have_mng_structure=MagickFalse;
6455 if (logging != MagickFalse)
6456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6458 return(GetFirstImageInList(image));
6460 #else /* PNG_LIBPNG_VER > 10011 */
6461 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6463 printf("Your PNG library is too old: You have libpng-%s\n",
6464 PNG_LIBPNG_VER_STRING);
6466 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6467 "PNG library is too old","`%s'",image_info->filename);
6469 return(Image *) NULL;
6472 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6474 return(ReadPNGImage(image_info,exception));
6476 #endif /* PNG_LIBPNG_VER > 10011 */
6480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6484 % R e g i s t e r P N G I m a g e %
6488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6490 % RegisterPNGImage() adds properties for the PNG image format to
6491 % the list of supported formats. The properties include the image format
6492 % tag, a method to read and/or write the format, whether the format
6493 % supports the saving of more than one frame to the same file or blob,
6494 % whether the format supports native in-memory I/O, and a brief
6495 % description of the format.
6497 % The format of the RegisterPNGImage method is:
6499 % size_t RegisterPNGImage(void)
6502 ModuleExport size_t RegisterPNGImage(void)
6505 version[MaxTextExtent];
6513 "See http://www.libpng.org/ for details about the PNG format."
6518 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6524 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6530 #if defined(PNG_LIBPNG_VER_STRING)
6531 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6532 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6534 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6536 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6537 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6542 entry=SetMagickInfo("MNG");
6543 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6545 #if defined(MAGICKCORE_PNG_DELEGATE)
6546 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6547 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6550 entry->magick=(IsImageFormatHandler *) IsMNG;
6551 entry->description=ConstantString("Multiple-image Network Graphics");
6553 if (*version != '\0')
6554 entry->version=ConstantString(version);
6556 entry->module=ConstantString("PNG");
6557 entry->note=ConstantString(MNGNote);
6558 (void) RegisterMagickInfo(entry);
6560 entry=SetMagickInfo("PNG");
6562 #if defined(MAGICKCORE_PNG_DELEGATE)
6563 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6564 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6567 entry->magick=(IsImageFormatHandler *) IsPNG;
6568 entry->adjoin=MagickFalse;
6569 entry->description=ConstantString("Portable Network Graphics");
6570 entry->module=ConstantString("PNG");
6572 if (*version != '\0')
6573 entry->version=ConstantString(version);
6575 entry->note=ConstantString(PNGNote);
6576 (void) RegisterMagickInfo(entry);
6578 entry=SetMagickInfo("PNG8");
6580 #if defined(MAGICKCORE_PNG_DELEGATE)
6581 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6582 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6585 entry->magick=(IsImageFormatHandler *) IsPNG;
6586 entry->adjoin=MagickFalse;
6587 entry->description=ConstantString(
6588 "8-bit indexed with optional binary transparency");
6589 entry->module=ConstantString("PNG");
6590 (void) RegisterMagickInfo(entry);
6592 entry=SetMagickInfo("PNG24");
6595 #if defined(ZLIB_VERSION)
6596 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6597 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6599 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6601 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6602 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6606 if (*version != '\0')
6607 entry->version=ConstantString(version);
6609 #if defined(MAGICKCORE_PNG_DELEGATE)
6610 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6611 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6614 entry->magick=(IsImageFormatHandler *) IsPNG;
6615 entry->adjoin=MagickFalse;
6616 entry->description=ConstantString("opaque 24-bit RGB");
6617 entry->module=ConstantString("PNG");
6618 (void) RegisterMagickInfo(entry);
6620 entry=SetMagickInfo("PNG32");
6622 #if defined(MAGICKCORE_PNG_DELEGATE)
6623 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6624 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6627 entry->magick=(IsImageFormatHandler *) IsPNG;
6628 entry->adjoin=MagickFalse;
6629 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6630 entry->module=ConstantString("PNG");
6631 (void) RegisterMagickInfo(entry);
6633 entry=SetMagickInfo("JNG");
6635 #if defined(JNG_SUPPORTED)
6636 #if defined(MAGICKCORE_PNG_DELEGATE)
6637 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6638 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6642 entry->magick=(IsImageFormatHandler *) IsJNG;
6643 entry->adjoin=MagickFalse;
6644 entry->description=ConstantString("JPEG Network Graphics");
6645 entry->module=ConstantString("PNG");
6646 entry->note=ConstantString(JNGNote);
6647 (void) RegisterMagickInfo(entry);
6649 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6650 ping_semaphore=AllocateSemaphoreInfo();
6653 return(MagickImageCoderSignature);
6657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6661 % U n r e g i s t e r P N G I m a g e %
6665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6667 % UnregisterPNGImage() removes format registrations made by the
6668 % PNG module from the list of supported formats.
6670 % The format of the UnregisterPNGImage method is:
6672 % UnregisterPNGImage(void)
6675 ModuleExport void UnregisterPNGImage(void)
6677 (void) UnregisterMagickInfo("MNG");
6678 (void) UnregisterMagickInfo("PNG");
6679 (void) UnregisterMagickInfo("PNG8");
6680 (void) UnregisterMagickInfo("PNG24");
6681 (void) UnregisterMagickInfo("PNG32");
6682 (void) UnregisterMagickInfo("JNG");
6684 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6685 if (ping_semaphore != (SemaphoreInfo *) NULL)
6686 DestroySemaphoreInfo(&ping_semaphore);
6690 #if defined(MAGICKCORE_PNG_DELEGATE)
6691 #if PNG_LIBPNG_VER > 10011
6693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6697 % W r i t e M N G I m a g e %
6701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6703 % WriteMNGImage() writes an image in the Portable Network Graphics
6704 % Group's "Multiple-image Network Graphics" encoded image format.
6706 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6708 % The format of the WriteMNGImage method is:
6710 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6712 % A description of each parameter follows.
6714 % o image_info: the image info.
6716 % o image: The image.
6719 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6720 % "To do" under ReadPNGImage):
6722 % Preserve all unknown and not-yet-handled known chunks found in input
6723 % PNG file and copy them into output PNG files according to the PNG
6726 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6728 % Improve selection of color type (use indexed-colour or indexed-colour
6729 % with tRNS when 256 or fewer unique RGBA values are present).
6731 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6732 % This will be complicated if we limit ourselves to generating MNG-LC
6733 % files. For now we ignore disposal method 3 and simply overlay the next
6736 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6737 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6738 % [mostly done 15 June 1999 but still need to take care of tRNS]
6740 % Check for identical sRGB and replace with a global sRGB (and remove
6741 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6742 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6743 % local gAMA/cHRM with local sRGB if appropriate).
6745 % Check for identical sBIT chunks and write global ones.
6747 % Provide option to skip writing the signature tEXt chunks.
6749 % Use signatures to detect identical objects and reuse the first
6750 % instance of such objects instead of writing duplicate objects.
6752 % Use a smaller-than-32k value of compression window size when
6755 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6756 % ancillary text chunks and save profiles.
6758 % Provide an option to force LC files (to ensure exact framing rate)
6761 % Provide an option to force VLC files instead of LC, even when offsets
6762 % are present. This will involve expanding the embedded images with a
6763 % transparent region at the top and/or left.
6767 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6768 png_info *ping_info, unsigned char *profile_type, unsigned char
6769 *profile_description, unsigned char *profile_data, png_uint_32 length)
6788 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6790 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6793 if (image_info->verbose)
6795 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6796 (char *) profile_type, (double) length);
6799 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6800 description_length=(png_uint_32) strlen((const char *) profile_description);
6801 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6802 + description_length);
6803 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6804 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6805 text[0].key[0]='\0';
6806 (void) ConcatenateMagickString(text[0].key,
6807 "Raw profile type ",MaxTextExtent);
6808 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6812 (void) CopyMagickString(dp,(const char *) profile_description,
6814 dp+=description_length;
6816 (void) FormatMagickString(dp,allocated_length-
6817 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6820 for (i=0; i < (ssize_t) length; i++)
6824 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6825 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6830 text[0].text_length=(png_size_t) (dp-text[0].text);
6831 text[0].compression=image_info->compression == NoCompression ||
6832 (image_info->compression == UndefinedCompression &&
6833 text[0].text_length < 128) ? -1 : 0;
6835 if (text[0].text_length <= allocated_length)
6836 png_set_text(ping,ping_info,text,1);
6838 png_free(ping,text[0].text);
6839 png_free(ping,text[0].key);
6840 png_free(ping,text);
6843 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6844 const char *string, MagickBooleanType logging)
6857 ResetImageProfileIterator(image);
6859 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6861 profile=GetImageProfile(image,name);
6863 if (profile != (const StringInfo *) NULL)
6868 if (LocaleNCompare(name,string,11) == 0)
6870 if (logging != MagickFalse)
6871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6872 " Found %s profile",name);
6874 ping_profile=CloneStringInfo(profile);
6875 data=GetStringInfoDatum(ping_profile),
6876 length=(png_uint_32) GetStringInfoLength(ping_profile);
6881 (void) WriteBlobMSBULong(image,length-5); /* data length */
6882 (void) WriteBlob(image,length-1,data+1);
6883 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6884 ping_profile=DestroyStringInfo(ping_profile);
6888 name=GetNextImageProfile(image);
6895 /* Write one PNG image */
6896 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6897 const ImageInfo *IMimage_info,Image *IMimage)
6921 ping_trans_alpha[256];
6949 ping_have_cheap_transparency,
6960 /* ping_exclude_EXIF, */
6963 /* ping_exclude_iTXt, */
6968 /* ping_exclude_tRNS, */
6970 ping_exclude_zCCP, /* hex-encoded iCCP */
6973 ping_preserve_colormap,
6974 ping_need_colortype_warning,
6994 ping_interlace_method,
6995 ping_compression_method,
7012 number_semitransparent,
7014 ping_pHYs_unit_type;
7017 ping_pHYs_x_resolution,
7018 ping_pHYs_y_resolution;
7020 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7021 " Enter WriteOnePNGImage()");
7023 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7024 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7025 if (image_info == (ImageInfo *) NULL)
7026 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7028 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7029 LockSemaphoreInfo(ping_semaphore);
7032 /* Initialize some stuff */
7035 ping_interlace_method=0,
7036 ping_compression_method=0,
7037 ping_filter_method=0,
7040 ping_background.red = 0;
7041 ping_background.green = 0;
7042 ping_background.blue = 0;
7043 ping_background.gray = 0;
7044 ping_background.index = 0;
7046 ping_trans_color.red=0;
7047 ping_trans_color.green=0;
7048 ping_trans_color.blue=0;
7049 ping_trans_color.gray=0;
7051 ping_pHYs_unit_type = 0;
7052 ping_pHYs_x_resolution = 0;
7053 ping_pHYs_y_resolution = 0;
7055 ping_have_blob=MagickFalse;
7056 ping_have_color=MagickTrue;
7057 ping_have_non_bw=MagickTrue;
7058 ping_have_PLTE=MagickFalse;
7059 ping_have_bKGD=MagickFalse;
7060 ping_have_pHYs=MagickFalse;
7061 ping_have_tRNS=MagickFalse;
7063 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7064 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7065 ping_exclude_date=mng_info->ping_exclude_date;
7066 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7067 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7068 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7069 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7070 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7071 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7072 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7073 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7074 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7075 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7076 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7077 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7079 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7080 ping_need_colortype_warning = MagickFalse;
7083 number_semitransparent = 0;
7084 number_transparent = 0;
7086 if (logging != MagickFalse)
7088 if (image->storage_class == UndefinedClass)
7089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7090 " storage_class=UndefinedClass");
7091 if (image->storage_class == DirectClass)
7092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7093 " storage_class=DirectClass");
7094 if (image->storage_class == PseudoClass)
7095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7096 " storage_class=PseudoClass");
7099 if (image->storage_class != PseudoClass && image->colormap != NULL)
7101 /* Free the bogus colormap; it can cause trouble later */
7102 if (logging != MagickFalse)
7103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7104 " Freeing bogus colormap");
7105 (void *) RelinquishMagickMemory(image->colormap);
7106 image->colormap=NULL;
7109 if (image->colorspace != RGBColorspace)
7110 (void) TransformImageColorspace(image,RGBColorspace);
7113 Sometimes we get PseudoClass images whose RGB values don't match
7114 the colors in the colormap. This code syncs the RGB values.
7116 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7117 (void) SyncImage(image);
7119 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7120 if (image->depth > 8)
7122 if (logging != MagickFalse)
7123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7124 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7130 #if 0 /* To do: Option to use the original colormap */
7131 if (ping_preserve_colormap != MagickFalse)
7136 #if 0 /* To do: honor -depth */
7137 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7142 /* To do: set to next higher multiple of 8 */
7143 if (image->depth < 8)
7146 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7147 /* PNG does not handle depths greater than 16 so reduce it even
7150 if (image->depth > 16)
7154 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7155 if (image->depth == 16 && mng_info->write_png_depth != 16)
7156 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7160 /* Normally we run this just once, but in the case of writing PNG8
7161 * we reduce the transparency to binary and run again, then if there
7162 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7163 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7164 * palette. The final reduction can only fail if there are still 256
7165 * colors present and one of them has both transparent and opaque instances.
7168 tried_333 = MagickFalse;
7169 tried_444 = MagickFalse;
7175 * Sometimes we get DirectClass images that have 256 colors or fewer.
7176 * This code will build a colormap.
7178 * Also, sometimes we get PseudoClass images with an out-of-date
7179 * colormap. This code will replace the colormap with a new one.
7180 * Sometimes we get PseudoClass images that have more than 256 colors.
7181 * This code will delete the colormap and change the image to
7184 * If image->matte is MagickFalse, we ignore the opacity channel
7185 * even though it sometimes contains left-over non-opaque values.
7187 * Also we gather some information (number of opaque, transparent,
7188 * and semitransparent pixels, and whether the image has any non-gray
7189 * pixels or only black-and-white pixels) that we might need later.
7191 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7192 * we need to check for bogus non-opaque values, at least.
7195 # define PNGK (MAGICKCORE_QUANTUM_DEPTH-8) /* Shift */
7196 # define PNGM (ScaleCharToQuantum((unsigned char) 0x01)) /* Scale */
7206 semitransparent[260],
7209 register IndexPacket
7212 register const PixelPacket
7216 register PixelPacket
7219 if (logging != MagickFalse)
7220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221 " Enter BUILD_PALETTE:");
7223 if (logging != MagickFalse)
7225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7226 " image->columns=%.20g",(double) image->columns);
7227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7228 " image->rows=%.20g",(double) image->rows);
7229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7230 " image->matte=%.20g",(double) image->matte);
7231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232 " image->depth=%.20g",(double) image->depth);
7234 if (image->storage_class == PseudoClass && image->colormap != NULL)
7236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7237 " Original colormap:");
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7239 " i (red,green,blue,opacity)");
7241 for (i=0; i < 256; i++)
7243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7244 " %d (%d,%d,%d,%d)",
7246 (int) image->colormap[i].red,
7247 (int) image->colormap[i].green,
7248 (int) image->colormap[i].blue,
7249 (int) image->colormap[i].opacity);
7252 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7257 " %d (%d,%d,%d,%d)",
7259 (int) image->colormap[i].red,
7260 (int) image->colormap[i].green,
7261 (int) image->colormap[i].blue,
7262 (int) image->colormap[i].opacity);
7267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7268 " image->colors=%d",(int) image->colors);
7270 if (image->colors == 0)
7271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " (zero means unknown)");
7274 if (ping_preserve_colormap == MagickFalse)
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7276 " Regenerate the colormap");
7279 exception=(&image->exception);
7283 number_semitransparent = 0;
7284 number_transparent = 0;
7286 for (y=0; y < (ssize_t) image->rows; y++)
7288 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7290 if (q == (PixelPacket *) NULL)
7293 for (x=0; x < (ssize_t) image->columns; x++)
7295 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7297 if (number_opaque < 259)
7299 if (number_opaque == 0)
7302 opaque[0].opacity=OpaqueOpacity;
7306 for (i=0; i< (ssize_t) number_opaque; i++)
7308 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7312 if (i == (ssize_t) number_opaque &&
7313 number_opaque < 259)
7317 opaque[i].opacity = OpaqueOpacity;
7321 else if (q->opacity == TransparentOpacity)
7323 if (number_transparent < 259)
7325 if (number_transparent == 0)
7328 ping_trans_color.red=(unsigned short)(q->red);
7329 ping_trans_color.green=(unsigned short) (q->green);
7330 ping_trans_color.blue=(unsigned short) (q->blue);
7331 ping_trans_color.gray=(unsigned short) (q->blue);
7332 number_transparent = 1;
7335 for (i=0; i< (ssize_t) number_transparent; i++)
7337 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7341 if (i == (ssize_t) number_transparent &&
7342 number_transparent < 259)
7344 number_transparent++;
7345 transparent[i] = *q;
7351 if (number_semitransparent < 259)
7353 if (number_semitransparent == 0)
7355 semitransparent[0]=*q;
7356 number_semitransparent = 1;
7359 for (i=0; i< (ssize_t) number_semitransparent; i++)
7361 if (IsColorEqual(semitransparent+i,
7362 (PixelPacket *) q) &&
7363 q->opacity == semitransparent[i].opacity)
7367 if (i == (ssize_t) number_semitransparent &&
7368 number_semitransparent < 259)
7370 number_semitransparent++;
7371 semitransparent[i] = *q;
7379 if (ping_exclude_bKGD == MagickFalse)
7381 /* Add the background color to the palette, if it
7382 * isn't already there.
7384 for (i=0; i<number_opaque; i++)
7386 if (IsColorEqual(opaque+i, &image->background_color))
7390 if (number_opaque < 259 && i == number_opaque)
7392 opaque[i]=image->background_color;
7393 opaque[i].opacity = OpaqueOpacity;
7396 else if (logging != MagickFalse)
7397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398 " No room in the colormap to add background color");
7401 image_colors=number_opaque+number_transparent+number_semitransparent;
7403 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7405 /* No room for the background color; remove it. */
7410 if (logging != MagickFalse)
7412 if (image_colors > 256)
7413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7414 " image has more than 256 colors");
7417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7418 " image has %d colors",image_colors);
7422 if (ping_preserve_colormap != MagickFalse)
7426 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7428 ping_have_color=MagickFalse;
7429 ping_have_non_bw=MagickFalse;
7431 if(image_colors > 256)
7433 for (y=0; y < (ssize_t) image->rows; y++)
7435 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7437 if (q == (PixelPacket *) NULL)
7440 /* Worst case is black-and-white; we are looking at every
7444 if (ping_have_color == MagickFalse)
7447 for (x=0; x < (ssize_t) image->columns; x++)
7449 if (s->red != s->green || s->red != s->blue)
7451 ping_have_color=MagickTrue;
7452 ping_have_non_bw=MagickTrue;
7459 if (ping_have_non_bw == MagickFalse)
7462 for (x=0; x < (ssize_t) image->columns; x++)
7464 if (s->red != 0 && s->red != QuantumRange)
7466 ping_have_non_bw=MagickTrue;
7475 if (image_colors < 257)
7481 * Initialize image colormap.
7484 if (logging != MagickFalse)
7485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7486 " Sort the new colormap");
7488 /* Sort palette, transparent first */;
7492 for (i=0; i<number_transparent; i++)
7493 colormap[n++] = transparent[i];
7495 for (i=0; i<number_semitransparent; i++)
7496 colormap[n++] = semitransparent[i];
7498 for (i=0; i<number_opaque; i++)
7499 colormap[n++] = opaque[i];
7502 /* image_colors < 257; search the colormap instead of the pixels
7503 * to get ping_have_color and ping_have_non_bw
7507 if (ping_have_color == MagickFalse)
7509 if (colormap[i].red != colormap[i].green ||
7510 colormap[i].red != colormap[i].blue)
7512 ping_have_color=MagickTrue;
7513 ping_have_non_bw=MagickTrue;
7518 if (ping_have_non_bw == MagickFalse)
7520 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7521 ping_have_non_bw=MagickTrue;
7525 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7526 (number_transparent == 0 && number_semitransparent == 0)) &&
7527 (((mng_info->write_png_colortype-1) ==
7528 PNG_COLOR_TYPE_PALETTE) ||
7529 (mng_info->write_png_colortype == 0)))
7531 if (logging != MagickFalse)
7533 if (n != (ssize_t) image_colors)
7534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7535 " image_colors (%d) and n (%d) don't match",
7538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7539 " AcquireImageColormap");
7542 image->colors = image_colors;
7544 if (AcquireImageColormap(image,image_colors) ==
7546 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7548 for (i=0; i< (ssize_t) image_colors; i++)
7549 image->colormap[i] = colormap[i];
7551 if (logging != MagickFalse)
7553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7554 " image->colors=%d (%d)",
7555 (int) image->colors, image_colors);
7557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7558 " Update the pixel indexes");
7561 /* Sync the pixel indices with the new colormap */
7563 for (y=0; y < (ssize_t) image->rows; y++)
7565 q=GetAuthenticPixels(image,0,y,image->columns,1,
7568 if (q == (PixelPacket *) NULL)
7571 indexes=GetAuthenticIndexQueue(image);
7573 for (x=0; x < (ssize_t) image->columns; x++)
7575 for (i=0; i< (ssize_t) image_colors; i++)
7577 if ((image->matte == MagickFalse ||
7578 image->colormap[i].opacity == q->opacity) &&
7579 (IsColorEqual(&image->colormap[i],
7580 (PixelPacket *) q)))
7582 indexes[x]=(IndexPacket) i;
7589 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7595 if (logging != MagickFalse)
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " image->colors=%d", (int) image->colors);
7600 if (image->colormap != NULL)
7602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7603 " i (red,green,blue,opacity)");
7605 for (i=0; i < (ssize_t) image->colors; i++)
7607 if (i < 300 || i >= (ssize_t) image->colors - 10)
7609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610 " %d (%d,%d,%d,%d)",
7612 (int) image->colormap[i].red,
7613 (int) image->colormap[i].green,
7614 (int) image->colormap[i].blue,
7615 (int) image->colormap[i].opacity);
7620 if (number_transparent < 257)
7621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7622 " number_transparent = %d",
7623 number_transparent);
7626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7627 " number_transparent > 256");
7629 if (number_opaque < 257)
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " number_opaque = %d",
7635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7636 " number_opaque > 256");
7638 if (number_semitransparent < 257)
7639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7640 " number_semitransparent = %d",
7641 number_semitransparent);
7644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645 " number_semitransparent > 256");
7647 if (ping_have_non_bw == MagickFalse)
7648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7649 " All pixels and the background are black or white");
7651 else if (ping_have_color == MagickFalse)
7652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7653 " All pixels and the background are gray");
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " At least one pixel or the background is non-gray");
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " Exit BUILD_PALETTE:");
7663 if (mng_info->write_png8 == MagickFalse)
7666 /* Make any reductions necessary for the PNG8 format */
7667 if (image_colors <= 256 &&
7668 image_colors != 0 && image->colormap != NULL &&
7669 number_semitransparent == 0 &&
7670 number_transparent <= 1)
7673 /* PNG8 can't have semitransparent colors so we threshold the
7674 * opacity to 0 or OpaqueOpacity
7676 if (number_semitransparent != 0)
7678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7679 " Thresholding the alpha channel to binary");
7681 for (y=0; y < (ssize_t) image->rows; y++)
7683 r=GetAuthenticPixels(image,0,y,image->columns,1,
7686 if (r == (PixelPacket *) NULL)
7689 for (x=0; x < (ssize_t) image->columns; x++)
7691 r->opacity = r->opacity > TransparentOpacity/2 ?
7692 TransparentOpacity : OpaqueOpacity;
7696 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7699 if (image_colors != 0 && image_colors <= 256 &&
7700 image->colormap != NULL)
7701 for (i=0; i<image_colors; i++)
7702 image->colormap[i].opacity =
7703 image->colormap[i].opacity > TransparentOpacity/2 ?
7704 TransparentOpacity : OpaqueOpacity;
7709 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7710 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7711 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7714 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7716 if (logging != MagickFalse)
7717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7718 " Quantizing the background color to 4-4-4");
7720 tried_444 = MagickTrue;
7722 image->background_color.red=
7724 image->background_color.red) >> PNGK) & 0xf0) ) |
7726 image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7727 image->background_color.green=
7729 image->background_color.green) >> PNGK) & 0xf0) ) |
7731 image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7732 image->background_color.blue=
7734 image->background_color.blue) >> PNGK) & 0xf0) ) |
7736 image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7738 if (logging != MagickFalse)
7739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7740 " Quantizing the pixel colors to 4-4-4");
7742 if (image->colormap == NULL)
7744 for (y=0; y < (ssize_t) image->rows; y++)
7746 r=GetAuthenticPixels(image,0,y,image->columns,1,
7749 if (r == (PixelPacket *) NULL)
7752 for (x=0; x < (ssize_t) image->columns; x++)
7754 if (r->opacity == TransparentOpacity)
7756 r->red = image->background_color.red;
7757 r->green = image->background_color.green;
7758 r->blue = image->background_color.blue;
7763 ((((((size_t) r->red) >> PNGK) & 0xf0) ) |
7764 (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7766 ((((((size_t) r->green) >> PNGK) & 0xf0) ) |
7767 (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7769 ((((((size_t) r->blue) >> PNGK) & 0xf0) ) |
7770 (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7780 else /* Should not reach this; colormap already exists and
7783 if (logging != MagickFalse)
7784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7785 " Quantizing the colormap to 4-4-4");
7786 for (i=0; i<image_colors; i++)
7788 image->colormap[i].red=
7790 image->colormap[i].red) >> PNGK) & 0xf0) ) |
7792 image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7793 image->colormap[i].green=
7795 image->colormap[i].green) >> PNGK) & 0xf0) ) |
7797 image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7798 image->colormap[i].blue=
7800 image->colormap[i].blue) >> PNGK) & 0xf0) ) |
7802 image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7808 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7810 if (logging != MagickFalse)
7811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7812 " Quantizing the background color to 3-3-3");
7814 tried_333 = MagickTrue;
7816 image->background_color.red=
7818 image->background_color.red) >> PNGK) & 0xe0) ) |
7820 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7822 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7823 image->background_color.green=
7825 image->background_color.green) >> PNGK) & 0xe0) ) |
7827 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7829 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7830 image->background_color.blue=
7832 image->background_color.blue) >> PNGK) & 0xe0) ) |
7834 image->background_color.blue) >> PNGK) & 0xe0) >> 3) |
7836 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7838 if (logging != MagickFalse)
7839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7840 " Quantizing the pixel colors to 3-3-3-1");
7842 if (image->colormap == NULL)
7844 for (y=0; y < (ssize_t) image->rows; y++)
7846 r=GetAuthenticPixels(image,0,y,image->columns,1,
7849 if (r == (PixelPacket *) NULL)
7852 for (x=0; x < (ssize_t) image->columns; x++)
7854 if (r->opacity == TransparentOpacity)
7856 r->red = image->background_color.red;
7857 r->green = image->background_color.green;
7858 r->blue = image->background_color.blue;
7863 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7864 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7865 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7867 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7868 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7869 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7871 ((((((size_t) r->blue) >> PNGK) & 0xe0) ) |
7872 (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3) |
7873 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7883 else /* Should not reach this; colormap already exists and
7886 if (logging != MagickFalse)
7887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7888 " Quantizing the colormap to 3-3-3-1");
7889 for (i=0; i<image_colors; i++)
7891 image->colormap[i].red=
7893 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7895 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7897 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7898 image->colormap[i].green=
7900 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7902 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7904 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7905 image->colormap[i].blue=
7907 image->colormap[i].blue) >> PNGK) & 0xe0) ) |
7909 image->colormap[i].blue) >> PNGK) & 0xe0) >> 3) |
7911 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7917 if (image_colors == 0 || image_colors > 256)
7919 if (logging != MagickFalse)
7920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7921 " Quantizing the background color to 3-3-2");
7923 image->background_color.red=
7925 image->background_color.red) >> PNGK) & 0xe0) ) |
7927 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7929 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7930 image->background_color.green=
7932 image->background_color.green) >> PNGK) & 0xe0) ) |
7934 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7936 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7937 image->background_color.blue=
7939 image->background_color.blue) >> PNGK) & 0xc0) ) |
7941 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7943 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7945 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7947 if (logging != MagickFalse)
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " Quantizing the pixel colors to 3-3-2-1");
7951 if (image->colormap == NULL)
7953 for (y=0; y < (ssize_t) image->rows; y++)
7955 r=GetAuthenticPixels(image,0,y,image->columns,1,
7958 if (r == (PixelPacket *) NULL)
7961 for (x=0; x < (ssize_t) image->columns; x++)
7963 if (r->opacity == TransparentOpacity)
7965 r->red = image->background_color.red;
7966 r->green = image->background_color.green;
7967 r->blue = image->background_color.blue;
7972 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7973 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7974 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7976 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7977 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7978 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7980 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7981 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7982 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7983 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7988 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7993 else /* Should not reach this; colormap already exists and
7996 if (logging != MagickFalse)
7997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7998 " Quantizing the colormap to 3-3-2-1");
7999 for (i=0; i<image_colors; i++)
8001 image->colormap[i].red=
8003 image->colormap[i].red) >> PNGK) & 0xe0) ) |
8005 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
8007 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
8008 image->colormap[i].green=
8010 image->colormap[i].green) >> PNGK) & 0xe0) ) |
8012 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
8014 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
8015 image->colormap[i].blue=
8017 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
8019 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
8021 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
8023 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
8030 /* END OF BUILD_PALETTE */
8032 /* If we are excluding the tRNS chunk and there is transparency,
8033 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8036 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8037 (number_transparent != 0 || number_semitransparent != 0))
8039 int colortype=mng_info->write_png_colortype;
8041 if (ping_have_color == MagickFalse)
8042 mng_info->write_png_colortype = 5;
8045 mng_info->write_png_colortype = 7;
8047 if (colortype != 0 &&
8048 mng_info->write_png_colortype != (ssize_t) colortype)
8049 ping_need_colortype_warning=MagickTrue;
8053 /* See if cheap transparency is possible. It is only possible
8054 * when there is a single transparent color, no semitransparent
8055 * color, and no opaque color that has the same RGB components
8056 * as the transparent color. We only need this information if
8057 * we are writing a PNG with colortype 0 or 2, and we have not
8058 * excluded the tRNS chunk.
8060 if (number_transparent == 1 &&
8061 mng_info->write_png_colortype < 4)
8063 ping_have_cheap_transparency = MagickTrue;
8065 if (number_semitransparent != 0)
8066 ping_have_cheap_transparency = MagickFalse;
8068 else if (image_colors == 0 || image_colors > 256 ||
8069 image->colormap == NULL)
8074 register const PixelPacket
8077 exception=(&image->exception);
8079 for (y=0; y < (ssize_t) image->rows; y++)
8081 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8083 if (q == (PixelPacket *) NULL)
8086 for (x=0; x < (ssize_t) image->columns; x++)
8088 if (q->opacity != TransparentOpacity &&
8089 (unsigned short) q->red == ping_trans_color.red &&
8090 (unsigned short) q->green == ping_trans_color.green &&
8091 (unsigned short) q->blue == ping_trans_color.blue)
8093 ping_have_cheap_transparency = MagickFalse;
8100 if (ping_have_cheap_transparency == MagickFalse)
8106 /* Assuming that image->colormap[0] is the one transparent color
8107 * and that all others are opaque.
8109 if (image_colors > 1)
8110 for (i=1; i<image_colors; i++)
8111 if (image->colormap[i].red == image->colormap[0].red &&
8112 image->colormap[i].green == image->colormap[0].green &&
8113 image->colormap[i].blue == image->colormap[0].blue)
8115 ping_have_cheap_transparency = MagickFalse;
8120 if (logging != MagickFalse)
8122 if (ping_have_cheap_transparency == MagickFalse)
8123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8124 " Cheap transparency is not possible.");
8127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8128 " Cheap transparency is possible.");
8132 ping_have_cheap_transparency = MagickFalse;
8134 image_depth=image->depth;
8136 quantum_info = (QuantumInfo *) NULL;
8138 image_colors=(int) image->colors;
8139 image_matte=image->matte;
8141 mng_info->IsPalette=image->storage_class == PseudoClass &&
8142 image_colors <= 256 && image->colormap != NULL;
8144 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8145 (image->colors == 0 || image->colormap == NULL))
8147 image_info=DestroyImageInfo(image_info);
8148 image=DestroyImage(image);
8149 (void) ThrowMagickException(&IMimage->exception,
8150 GetMagickModule(),CoderError,
8151 "Cannot write PNG8 or color-type 3; colormap is NULL",
8152 "`%s'",IMimage->filename);
8153 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8154 UnlockSemaphoreInfo(ping_semaphore);
8156 return(MagickFalse);
8160 Allocate the PNG structures
8162 #ifdef PNG_USER_MEM_SUPPORTED
8163 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8164 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8165 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8168 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8169 MagickPNGErrorHandler,MagickPNGWarningHandler);
8172 if (ping == (png_struct *) NULL)
8173 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8175 ping_info=png_create_info_struct(ping);
8177 if (ping_info == (png_info *) NULL)
8179 png_destroy_write_struct(&ping,(png_info **) NULL);
8180 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8183 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8184 ping_pixels=(unsigned char *) NULL;
8186 if (setjmp(png_jmpbuf(ping)))
8192 if (image_info->verbose)
8193 (void) printf("PNG write has failed.\n");
8195 png_destroy_write_struct(&ping,&ping_info);
8196 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8197 UnlockSemaphoreInfo(ping_semaphore);
8199 if (ping_have_blob != MagickFalse)
8200 (void) CloseBlob(image);
8201 image_info=DestroyImageInfo(image_info);
8202 image=DestroyImage(image);
8203 return(MagickFalse);
8206 Prepare PNG for writing.
8208 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8209 if (mng_info->write_mng)
8210 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8213 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8214 if (mng_info->write_mng)
8215 png_permit_empty_plte(ping,MagickTrue);
8222 ping_width=(png_uint_32) image->columns;
8223 ping_height=(png_uint_32) image->rows;
8225 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8228 if (mng_info->write_png_depth != 0)
8229 image_depth=mng_info->write_png_depth;
8231 /* Adjust requested depth to next higher valid depth if necessary */
8232 if (image_depth > 8)
8235 if ((image_depth > 4) && (image_depth < 8))
8238 if (image_depth == 3)
8241 if (logging != MagickFalse)
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8244 " width=%.20g",(double) ping_width);
8245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8246 " height=%.20g",(double) ping_height);
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " image_matte=%.20g",(double) image->matte);
8249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8250 " image->depth=%.20g",(double) image->depth);
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8255 save_image_depth=image_depth;
8256 ping_bit_depth=(png_byte) save_image_depth;
8259 #if defined(PNG_pHYs_SUPPORTED)
8260 if (ping_exclude_pHYs == MagickFalse)
8262 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8263 (!mng_info->write_mng || !mng_info->equal_physs))
8265 if (logging != MagickFalse)
8266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " Setting up pHYs chunk");
8269 if (image->units == PixelsPerInchResolution)
8271 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8272 ping_pHYs_x_resolution=
8273 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8274 ping_pHYs_y_resolution=
8275 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8278 else if (image->units == PixelsPerCentimeterResolution)
8280 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8281 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8282 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8287 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8288 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8289 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8292 if (logging != MagickFalse)
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8295 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8296 (int) ping_pHYs_unit_type);
8297 ping_have_pHYs = MagickTrue;
8302 if (ping_exclude_bKGD == MagickFalse)
8304 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8310 if (ping_bit_depth == 8)
8313 if (ping_bit_depth == 4)
8316 if (ping_bit_depth == 2)
8319 if (ping_bit_depth == 1)
8322 ping_background.red=(png_uint_16)
8323 (ScaleQuantumToShort(image->background_color.red) & mask);
8325 ping_background.green=(png_uint_16)
8326 (ScaleQuantumToShort(image->background_color.green) & mask);
8328 ping_background.blue=(png_uint_16)
8329 (ScaleQuantumToShort(image->background_color.blue) & mask);
8332 if (logging != MagickFalse)
8334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8335 " Setting up bKGD chunk (1)");
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " ping_bit_depth=%d",ping_bit_depth);
8341 ping_have_bKGD = MagickTrue;
8345 Select the color type.
8350 if (mng_info->IsPalette && mng_info->write_png8)
8353 /* To do: make this a function cause it's used twice, except
8354 for reducing the sample depth from 8. */
8356 number_colors=image_colors;
8358 ping_have_tRNS=MagickFalse;
8363 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8365 if (logging != MagickFalse)
8366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8367 " Setting up PLTE chunk with %d colors (%d)",
8368 number_colors, image_colors);
8370 for (i=0; i < (ssize_t) number_colors; i++)
8372 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8373 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8374 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8375 if (logging != MagickFalse)
8376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8377 #if MAGICKCORE_QUANTUM_DEPTH == 8
8378 " %3ld (%3d,%3d,%3d)",
8380 " %5ld (%5d,%5d,%5d)",
8382 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8386 ping_have_PLTE=MagickTrue;
8387 image_depth=ping_bit_depth;
8390 if (matte != MagickFalse)
8393 Identify which colormap entry is transparent.
8395 assert(number_colors <= 256);
8396 assert(image->colormap != NULL);
8398 for (i=0; i < (ssize_t) number_transparent; i++)
8399 ping_trans_alpha[i]=0;
8402 ping_num_trans=(unsigned short) (number_transparent +
8403 number_semitransparent);
8405 if (ping_num_trans == 0)
8406 ping_have_tRNS=MagickFalse;
8409 ping_have_tRNS=MagickTrue;
8412 if (ping_exclude_bKGD == MagickFalse)
8415 * Identify which colormap entry is the background color.
8418 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8419 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8422 ping_background.index=(png_byte) i;
8424 } /* end of write_png8 */
8426 else if (mng_info->write_png24)
8428 image_matte=MagickFalse;
8429 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8432 else if (mng_info->write_png32)
8434 image_matte=MagickTrue;
8435 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8438 else /* mng_info->write_pngNN not specified */
8440 image_depth=ping_bit_depth;
8442 if (mng_info->write_png_colortype != 0)
8444 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8446 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8447 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8448 image_matte=MagickTrue;
8451 image_matte=MagickFalse;
8453 if (logging != MagickFalse)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " PNG colortype %d was specified:",(int) ping_color_type);
8458 else /* write_png_colortype not specified */
8460 if (logging != MagickFalse)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " Selecting PNG colortype:");
8464 ping_color_type=(png_byte) ((matte != MagickFalse)?
8465 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8467 if (image_info->type == TrueColorType)
8469 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8470 image_matte=MagickFalse;
8473 if (image_info->type == TrueColorMatteType)
8475 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8476 image_matte=MagickTrue;
8479 if (image_info->type == PaletteType ||
8480 image_info->type == PaletteMatteType)
8481 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8483 if (mng_info->write_png_colortype == 0 &&
8484 (image_info->type == UndefinedType ||
8485 image_info->type == OptimizeType))
8487 if (ping_have_color == MagickFalse)
8489 if (image_matte == MagickFalse)
8491 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8492 image_matte=MagickFalse;
8497 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8498 image_matte=MagickTrue;
8503 if (image_matte == MagickFalse)
8505 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8506 image_matte=MagickFalse;
8511 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8512 image_matte=MagickTrue;
8519 if (logging != MagickFalse)
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " Selected PNG colortype=%d",ping_color_type);
8523 if (ping_bit_depth < 8)
8525 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8526 ping_color_type == PNG_COLOR_TYPE_RGB ||
8527 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8531 old_bit_depth=ping_bit_depth;
8533 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8535 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8539 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8544 if (image->colors == 0)
8547 (void) ThrowMagickException(&image->exception,
8548 GetMagickModule(),CoderError,
8549 "image has 0 colors", "`%s'","");
8552 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8553 ping_bit_depth <<= 1;
8556 if (logging != MagickFalse)
8558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559 " Number of colors: %.20g",(double) image_colors);
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Tentative PNG bit depth: %d",ping_bit_depth);
8565 if (ping_bit_depth < (int) mng_info->write_png_depth)
8566 ping_bit_depth = mng_info->write_png_depth;
8569 image_depth=ping_bit_depth;
8571 if (logging != MagickFalse)
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " Tentative PNG color type: %.20g",(double) ping_color_type);
8576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8577 " image_info->type: %.20g",(double) image_info->type);
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8580 " image_depth: %.20g",(double) image_depth);
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " image->depth: %.20g",(double) image->depth);
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8590 if (matte != MagickFalse)
8592 if (mng_info->IsPalette)
8594 if (mng_info->write_png_colortype == 0)
8596 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8598 if (ping_have_color != MagickFalse)
8599 ping_color_type=PNG_COLOR_TYPE_RGBA;
8603 * Determine if there is any transparent color.
8605 if (number_transparent + number_semitransparent == 0)
8608 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8611 image_matte=MagickFalse;
8613 if (mng_info->write_png_colortype == 0)
8614 ping_color_type&=0x03;
8624 if (ping_bit_depth == 8)
8627 if (ping_bit_depth == 4)
8630 if (ping_bit_depth == 2)
8633 if (ping_bit_depth == 1)
8636 ping_trans_color.red=(png_uint_16)
8637 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8639 ping_trans_color.green=(png_uint_16)
8640 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8642 ping_trans_color.blue=(png_uint_16)
8643 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8645 ping_trans_color.gray=(png_uint_16)
8646 (ScaleQuantumToShort(PixelIntensityToQuantum(
8647 image->colormap)) & mask);
8649 ping_trans_color.index=(png_byte) 0;
8651 ping_have_tRNS=MagickTrue;
8654 if (ping_have_tRNS != MagickFalse)
8657 * Determine if there is one and only one transparent color
8658 * and if so if it is fully transparent.
8660 if (ping_have_cheap_transparency == MagickFalse)
8661 ping_have_tRNS=MagickFalse;
8664 if (ping_have_tRNS != MagickFalse)
8666 if (mng_info->write_png_colortype == 0)
8667 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8669 if (image_depth == 8)
8671 ping_trans_color.red&=0xff;
8672 ping_trans_color.green&=0xff;
8673 ping_trans_color.blue&=0xff;
8674 ping_trans_color.gray&=0xff;
8680 if (image_depth == 8)
8682 ping_trans_color.red&=0xff;
8683 ping_trans_color.green&=0xff;
8684 ping_trans_color.blue&=0xff;
8685 ping_trans_color.gray&=0xff;
8692 if (ping_have_tRNS != MagickFalse)
8693 image_matte=MagickFalse;
8695 if ((mng_info->IsPalette) &&
8696 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8697 ping_have_color == MagickFalse &&
8698 (image_matte == MagickFalse || image_depth >= 8))
8702 if (image_matte != MagickFalse)
8703 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8705 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8707 ping_color_type=PNG_COLOR_TYPE_GRAY;
8709 if (save_image_depth == 16 && image_depth == 8)
8711 if (logging != MagickFalse)
8713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714 " Scaling ping_trans_color (0)");
8716 ping_trans_color.gray*=0x0101;
8720 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8721 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8723 if ((image_colors == 0) || ((ssize_t) image_colors-1 > MaxColormapSize))
8724 image_colors=(int) (one << image_depth);
8726 if (image_depth > 8)
8732 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8734 if(!mng_info->write_png_depth)
8738 while ((int) (one << ping_bit_depth)
8739 < (ssize_t) image_colors)
8740 ping_bit_depth <<= 1;
8744 else if (ping_color_type ==
8745 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8746 mng_info->IsPalette)
8748 /* Check if grayscale is reducible */
8751 depth_4_ok=MagickTrue,
8752 depth_2_ok=MagickTrue,
8753 depth_1_ok=MagickTrue;
8755 for (i=0; i < (ssize_t) image_colors; i++)
8760 intensity=ScaleQuantumToChar(image->colormap[i].red);
8762 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8763 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8764 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8765 depth_2_ok=depth_1_ok=MagickFalse;
8766 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8767 depth_1_ok=MagickFalse;
8770 if (depth_1_ok && mng_info->write_png_depth <= 1)
8773 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8776 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8781 image_depth=ping_bit_depth;
8786 if (mng_info->IsPalette)
8788 number_colors=image_colors;
8790 if (image_depth <= 8)
8795 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8797 if (mng_info->have_write_global_plte && matte == MagickFalse)
8799 png_set_PLTE(ping,ping_info,NULL,0);
8801 if (logging != MagickFalse)
8802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8803 " Setting up empty PLTE chunk");
8808 for (i=0; i < (ssize_t) number_colors; i++)
8810 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8811 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8812 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8815 if (logging != MagickFalse)
8816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817 " Setting up PLTE chunk with %d colors",
8820 ping_have_PLTE=MagickTrue;
8823 /* color_type is PNG_COLOR_TYPE_PALETTE */
8824 if (mng_info->write_png_depth == 0)
8832 while ((one << ping_bit_depth) < number_colors)
8833 ping_bit_depth <<= 1;
8838 if (matte != MagickFalse)
8841 * Set up trans_colors array.
8843 assert(number_colors <= 256);
8845 ping_num_trans=(unsigned short) (number_transparent +
8846 number_semitransparent);
8848 if (ping_num_trans == 0)
8849 ping_have_tRNS=MagickFalse;
8853 if (logging != MagickFalse)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Scaling ping_trans_color (1)");
8858 ping_have_tRNS=MagickTrue;
8860 for (i=0; i < ping_num_trans; i++)
8862 ping_trans_alpha[i]= (png_byte) (255-
8863 ScaleQuantumToChar(image->colormap[i].opacity));
8873 if (image_depth < 8)
8876 if ((save_image_depth == 16) && (image_depth == 8))
8878 if (logging != MagickFalse)
8880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8881 " Scaling ping_trans_color from (%d,%d,%d)",
8882 (int) ping_trans_color.red,
8883 (int) ping_trans_color.green,
8884 (int) ping_trans_color.blue);
8887 ping_trans_color.red*=0x0101;
8888 ping_trans_color.green*=0x0101;
8889 ping_trans_color.blue*=0x0101;
8890 ping_trans_color.gray*=0x0101;
8892 if (logging != MagickFalse)
8894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8896 (int) ping_trans_color.red,
8897 (int) ping_trans_color.green,
8898 (int) ping_trans_color.blue);
8903 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8904 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8907 Adjust background and transparency samples in sub-8-bit grayscale files.
8909 if (ping_bit_depth < 8 && ping_color_type ==
8910 PNG_COLOR_TYPE_GRAY)
8918 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8920 if (ping_exclude_bKGD == MagickFalse)
8923 ping_background.gray=(png_uint_16)
8924 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8926 if (logging != MagickFalse)
8927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8928 " Setting up bKGD chunk (2)");
8930 ping_have_bKGD = MagickTrue;
8933 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8934 ping_trans_color.gray));
8937 if (ping_exclude_bKGD == MagickFalse)
8939 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8942 Identify which colormap entry is the background color.
8945 number_colors=image_colors;
8947 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8948 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8951 ping_background.index=(png_byte) i;
8953 if (logging != MagickFalse)
8955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956 " Setting up bKGD chunk with index=%d",(int) i);
8959 if (i < (ssize_t) number_colors)
8961 ping_have_bKGD = MagickTrue;
8963 if (logging != MagickFalse)
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966 " background =(%d,%d,%d)",
8967 (int) ping_background.red,
8968 (int) ping_background.green,
8969 (int) ping_background.blue);
8973 else /* Can't happen */
8975 if (logging != MagickFalse)
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " No room in PLTE to add bKGD color");
8978 ping_have_bKGD = MagickFalse;
8983 if (logging != MagickFalse)
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8985 " PNG color type: %d",ping_color_type);
8987 Initialize compression level and filtering.
8989 if (logging != MagickFalse)
8991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " Setting up deflate compression");
8994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8995 " Compression buffer size: 32768");
8998 png_set_compression_buffer_size(ping,32768L);
9000 if (logging != MagickFalse)
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " Compression mem level: 9");
9004 png_set_compression_mem_level(ping, 9);
9006 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9014 level=(int) MagickMin((ssize_t) quality/10,9);
9016 if (logging != MagickFalse)
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " Compression level: %d",level);
9020 png_set_compression_level(ping,level);
9025 if (logging != MagickFalse)
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Compression strategy: Z_HUFFMAN_ONLY");
9029 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9032 if (logging != MagickFalse)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " Setting up filtering");
9036 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9037 /* This became available in libpng-1.0.9. Output must be a MNG. */
9038 if (mng_info->write_mng && ((quality % 10) == 7))
9040 if (logging != MagickFalse)
9041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9042 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
9044 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9048 if (logging != MagickFalse)
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 if ((quality % 10) > 5)
9058 base_filter=PNG_ALL_FILTERS;
9061 if ((quality % 10) != 5)
9062 base_filter=(int) quality % 10;
9065 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9066 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9068 base_filter=PNG_NO_FILTERS;
9071 base_filter=PNG_ALL_FILTERS;
9073 if (logging != MagickFalse)
9075 if (base_filter == PNG_ALL_FILTERS)
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " Base filter method: ADAPTIVE");
9079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9080 " Base filter method: NONE");
9083 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9086 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9087 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9089 ResetImageProfileIterator(image);
9090 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9092 profile=GetImageProfile(image,name);
9094 if (profile != (StringInfo *) NULL)
9096 #ifdef PNG_WRITE_iCCP_SUPPORTED
9097 if ((LocaleCompare(name,"ICC") == 0) ||
9098 (LocaleCompare(name,"ICM") == 0))
9101 if (ping_exclude_iCCP == MagickFalse)
9103 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9104 #if (PNG_LIBPNG_VER < 10500)
9105 (png_charp) GetStringInfoDatum(profile),
9107 (png_const_bytep) GetStringInfoDatum(profile),
9109 (png_uint_32) GetStringInfoLength(profile));
9115 if (ping_exclude_zCCP == MagickFalse)
9117 Magick_png_write_raw_profile(image_info,ping,ping_info,
9118 (unsigned char *) name,(unsigned char *) name,
9119 GetStringInfoDatum(profile),
9120 (png_uint_32) GetStringInfoLength(profile));
9124 if (logging != MagickFalse)
9125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9126 " Setting up text chunk with %s profile",name);
9128 name=GetNextImageProfile(image);
9132 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9133 if ((mng_info->have_write_global_srgb == 0) &&
9134 ((image->rendering_intent != UndefinedIntent) ||
9135 (image->colorspace == sRGBColorspace)))
9137 if (ping_exclude_sRGB == MagickFalse)
9140 Note image rendering intent.
9142 if (logging != MagickFalse)
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Setting up sRGB chunk");
9146 (void) png_set_sRGB(ping,ping_info,(
9147 Magick_RenderingIntent_to_PNG_RenderingIntent(
9148 image->rendering_intent)));
9150 if (ping_exclude_gAMA == MagickFalse)
9151 png_set_gAMA(ping,ping_info,0.45455);
9155 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9158 if (ping_exclude_gAMA == MagickFalse &&
9159 (ping_exclude_sRGB == MagickFalse ||
9160 (image->gamma < .45 || image->gamma > .46)))
9162 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9166 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9168 if (logging != MagickFalse)
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " Setting up gAMA chunk");
9172 png_set_gAMA(ping,ping_info,image->gamma);
9176 if (ping_exclude_cHRM == MagickFalse)
9178 if ((mng_info->have_write_global_chrm == 0) &&
9179 (image->chromaticity.red_primary.x != 0.0))
9182 Note image chromaticity.
9183 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9191 wp=image->chromaticity.white_point;
9192 rp=image->chromaticity.red_primary;
9193 gp=image->chromaticity.green_primary;
9194 bp=image->chromaticity.blue_primary;
9196 if (logging != MagickFalse)
9197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198 " Setting up cHRM chunk");
9200 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9206 ping_interlace_method=image_info->interlace != NoInterlace;
9208 if (mng_info->write_mng)
9209 png_set_sig_bytes(ping,8);
9211 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9213 if (mng_info->write_png_colortype != 0)
9215 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9216 if (ping_have_color != MagickFalse)
9218 ping_color_type = PNG_COLOR_TYPE_RGB;
9220 if (ping_bit_depth < 8)
9224 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9225 if (ping_have_color != MagickFalse)
9226 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9229 if (ping_need_colortype_warning != MagickFalse ||
9230 ((mng_info->write_png_depth &&
9231 (int) mng_info->write_png_depth != ping_bit_depth) ||
9232 (mng_info->write_png_colortype &&
9233 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9234 mng_info->write_png_colortype != 7 &&
9235 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9237 if (logging != MagickFalse)
9239 if (ping_need_colortype_warning != MagickFalse)
9241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9242 " Image has transparency but tRNS chunk was excluded");
9245 if (mng_info->write_png_depth)
9247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9248 " Defined PNG:bit-depth=%u, Computed depth=%u",
9249 mng_info->write_png_depth,
9253 if (mng_info->write_png_colortype)
9255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256 " Defined PNG:color-type=%u, Computed color type=%u",
9257 mng_info->write_png_colortype-1,
9263 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9266 if (image_matte != MagickFalse && image->matte == MagickFalse)
9268 /* Add an opaque matte channel */
9269 image->matte = MagickTrue;
9270 (void) SetImageOpacity(image,0);
9272 if (logging != MagickFalse)
9273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9274 " Added an opaque matte channel");
9277 if (number_transparent != 0 || number_semitransparent != 0)
9279 if (ping_color_type < 4)
9281 ping_have_tRNS=MagickTrue;
9282 if (logging != MagickFalse)
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Setting ping_have_tRNS=MagickTrue.");
9288 if (logging != MagickFalse)
9289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9290 " Writing PNG header chunks");
9292 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9293 ping_bit_depth,ping_color_type,
9294 ping_interlace_method,ping_compression_method,
9295 ping_filter_method);
9297 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9299 png_set_PLTE(ping,ping_info,palette,number_colors);
9301 if (logging != MagickFalse)
9303 for (i=0; i< (ssize_t) number_colors; i++)
9305 if (i < ping_num_trans)
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9309 (int) palette[i].red,
9310 (int) palette[i].green,
9311 (int) palette[i].blue,
9313 (int) ping_trans_alpha[i]);
9315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9316 " PLTE[%d] = (%d,%d,%d)",
9318 (int) palette[i].red,
9319 (int) palette[i].green,
9320 (int) palette[i].blue);
9325 if (ping_exclude_bKGD == MagickFalse)
9327 if (ping_have_bKGD != MagickFalse)
9328 png_set_bKGD(ping,ping_info,&ping_background);
9331 if (ping_exclude_pHYs == MagickFalse)
9333 if (ping_have_pHYs != MagickFalse)
9335 png_set_pHYs(ping,ping_info,
9336 ping_pHYs_x_resolution,
9337 ping_pHYs_y_resolution,
9338 ping_pHYs_unit_type);
9342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9343 " Setting up pHYs chunk");
9344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9345 " x_resolution=%lu",
9346 (unsigned long) ping_pHYs_x_resolution);
9347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9348 " y_resolution=%lu",
9349 (unsigned long) ping_pHYs_y_resolution);
9350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9352 (unsigned long) ping_pHYs_unit_type);
9357 #if defined(PNG_oFFs_SUPPORTED)
9358 if (ping_exclude_oFFs == MagickFalse)
9360 if (image->page.x || image->page.y)
9362 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9363 (png_int_32) image->page.y, 0);
9365 if (logging != MagickFalse)
9366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9367 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9368 (int) image->page.x, (int) image->page.y);
9373 if (mng_info->need_blob != MagickFalse)
9375 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9377 png_error(ping,"WriteBlob Failed");
9379 ping_have_blob=MagickTrue;
9382 png_write_info_before_PLTE(ping, ping_info);
9384 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9386 if (logging != MagickFalse)
9388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9389 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9392 if (ping_color_type == 3)
9393 (void) png_set_tRNS(ping, ping_info,
9400 (void) png_set_tRNS(ping, ping_info,
9405 if (logging != MagickFalse)
9407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9408 " tRNS color =(%d,%d,%d)",
9409 (int) ping_trans_color.red,
9410 (int) ping_trans_color.green,
9411 (int) ping_trans_color.blue);
9416 /* write any png-chunk-b profiles */
9417 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9419 png_write_info(ping,ping_info);
9421 /* write any PNG-chunk-m profiles */
9422 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9424 if (ping_exclude_vpAg == MagickFalse)
9426 if ((image->page.width != 0 && image->page.width != image->columns) ||
9427 (image->page.height != 0 && image->page.height != image->rows))
9432 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9433 PNGType(chunk,mng_vpAg);
9434 LogPNGChunk(logging,mng_vpAg,9L);
9435 PNGLong(chunk+4,(png_uint_32) image->page.width);
9436 PNGLong(chunk+8,(png_uint_32) image->page.height);
9437 chunk[12]=0; /* unit = pixels */
9438 (void) WriteBlob(image,13,chunk);
9439 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9443 #if (PNG_LIBPNG_VER == 10206)
9444 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9445 #define PNG_HAVE_IDAT 0x04
9446 ping->mode |= PNG_HAVE_IDAT;
9447 #undef PNG_HAVE_IDAT
9450 png_set_packing(ping);
9454 rowbytes=image->columns;
9455 if (image_depth > 8)
9457 switch (ping_color_type)
9459 case PNG_COLOR_TYPE_RGB:
9463 case PNG_COLOR_TYPE_GRAY_ALPHA:
9467 case PNG_COLOR_TYPE_RGBA:
9475 if (logging != MagickFalse)
9477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9478 " Writing PNG image data");
9480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9481 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9483 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9484 sizeof(*ping_pixels));
9486 if (ping_pixels == (unsigned char *) NULL)
9487 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9490 Initialize image scanlines.
9492 if (setjmp(png_jmpbuf(ping)))
9498 if (image_info->verbose)
9499 (void) printf("PNG write has failed.\n");
9501 png_destroy_write_struct(&ping,&ping_info);
9502 if (quantum_info != (QuantumInfo *) NULL)
9503 quantum_info=DestroyQuantumInfo(quantum_info);
9504 if (ping_pixels != (unsigned char *) NULL)
9505 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9506 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9507 UnlockSemaphoreInfo(ping_semaphore);
9509 if (ping_have_blob != MagickFalse)
9510 (void) CloseBlob(image);
9511 image_info=DestroyImageInfo(image_info);
9512 image=DestroyImage(image);
9513 return(MagickFalse);
9515 quantum_info=AcquireQuantumInfo(image_info,image);
9516 if (quantum_info == (QuantumInfo *) NULL)
9517 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9518 quantum_info->format=UndefinedQuantumFormat;
9519 quantum_info->depth=image_depth;
9520 num_passes=png_set_interlace_handling(ping);
9522 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9523 !mng_info->write_png32) &&
9524 (mng_info->IsPalette ||
9525 (image_info->type == BilevelType)) &&
9526 image_matte == MagickFalse &&
9527 ping_have_non_bw == MagickFalse)
9529 /* Palette, Bilevel, or Opaque Monochrome */
9530 register const PixelPacket
9533 quantum_info->depth=8;
9534 for (pass=0; pass < num_passes; pass++)
9537 Convert PseudoClass image to a PNG monochrome image.
9539 for (y=0; y < (ssize_t) image->rows; y++)
9541 if (logging != MagickFalse && y == 0)
9542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9543 " Writing row of pixels (0)");
9545 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9547 if (p == (const PixelPacket *) NULL)
9550 if (mng_info->IsPalette)
9552 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9553 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9554 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9555 mng_info->write_png_depth &&
9556 mng_info->write_png_depth != old_bit_depth)
9558 /* Undo pixel scaling */
9559 for (i=0; i < (ssize_t) image->columns; i++)
9560 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9561 >> (8-old_bit_depth));
9567 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9568 quantum_info,RedQuantum,ping_pixels,&image->exception);
9571 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9572 for (i=0; i < (ssize_t) image->columns; i++)
9573 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9576 if (logging != MagickFalse && y == 0)
9577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9578 " Writing row of pixels (1)");
9580 png_write_row(ping,ping_pixels);
9582 if (image->previous == (Image *) NULL)
9584 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9585 if (status == MagickFalse)
9591 else /* Not Palette, Bilevel, or Opaque Monochrome */
9593 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9594 !mng_info->write_png32) &&
9595 (image_matte != MagickFalse ||
9596 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9597 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9599 register const PixelPacket
9602 for (pass=0; pass < num_passes; pass++)
9605 for (y=0; y < (ssize_t) image->rows; y++)
9607 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9609 if (p == (const PixelPacket *) NULL)
9612 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9614 if (mng_info->IsPalette)
9615 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9616 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9619 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9620 quantum_info,RedQuantum,ping_pixels,&image->exception);
9622 if (logging != MagickFalse && y == 0)
9623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624 " Writing GRAY PNG pixels (2)");
9627 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9629 if (logging != MagickFalse && y == 0)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Writing GRAY_ALPHA PNG pixels (2)");
9633 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9634 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9637 if (logging != MagickFalse && y == 0)
9638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9639 " Writing row of pixels (2)");
9641 png_write_row(ping,ping_pixels);
9644 if (image->previous == (Image *) NULL)
9646 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9647 if (status == MagickFalse)
9655 register const PixelPacket
9658 for (pass=0; pass < num_passes; pass++)
9660 if ((image_depth > 8) || (mng_info->write_png24 ||
9661 mng_info->write_png32 ||
9662 (!mng_info->write_png8 && !mng_info->IsPalette)))
9664 for (y=0; y < (ssize_t) image->rows; y++)
9666 p=GetVirtualPixels(image,0,y,image->columns,1,
9669 if (p == (const PixelPacket *) NULL)
9672 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9674 if (image->storage_class == DirectClass)
9675 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9676 quantum_info,RedQuantum,ping_pixels,&image->exception);
9679 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9680 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9683 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9685 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9686 quantum_info,GrayAlphaQuantum,ping_pixels,
9689 if (logging != MagickFalse && y == 0)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " Writing GRAY_ALPHA PNG pixels (3)");
9694 else if (image_matte != MagickFalse)
9695 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9696 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9699 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9700 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9702 if (logging != MagickFalse && y == 0)
9703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9704 " Writing row of pixels (3)");
9706 png_write_row(ping,ping_pixels);
9711 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9712 mng_info->write_png32 ||
9713 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9715 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9716 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9718 if (logging != MagickFalse)
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9722 quantum_info->depth=8;
9726 for (y=0; y < (ssize_t) image->rows; y++)
9728 if (logging != MagickFalse && y == 0)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9732 p=GetVirtualPixels(image,0,y,image->columns,1,
9735 if (p == (const PixelPacket *) NULL)
9738 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9740 quantum_info->depth=image->depth;
9742 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9743 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9746 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9748 if (logging != MagickFalse && y == 0)
9749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9750 " Writing GRAY_ALPHA PNG pixels (4)");
9752 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9753 quantum_info,GrayAlphaQuantum,ping_pixels,
9759 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9760 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9762 if (logging != MagickFalse && y <= 2)
9764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9765 " Writing row of non-gray pixels (4)");
9767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9768 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9769 (int)ping_pixels[0],(int)ping_pixels[1]);
9772 png_write_row(ping,ping_pixels);
9776 if (image->previous == (Image *) NULL)
9778 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9779 if (status == MagickFalse)
9786 if (quantum_info != (QuantumInfo *) NULL)
9787 quantum_info=DestroyQuantumInfo(quantum_info);
9789 if (logging != MagickFalse)
9791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9792 " Wrote PNG image data");
9794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9795 " Width: %.20g",(double) ping_width);
9797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " Height: %.20g",(double) ping_height);
9800 if (mng_info->write_png_depth)
9802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9803 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9807 " PNG bit-depth written: %d",ping_bit_depth);
9809 if (mng_info->write_png_colortype)
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9816 " PNG color-type written: %d",ping_color_type);
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9819 " PNG Interlace method: %d",ping_interlace_method);
9822 Generate text chunks after IDAT.
9824 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9826 ResetImagePropertyIterator(image);
9827 property=GetNextImageProperty(image);
9828 while (property != (const char *) NULL)
9833 value=GetImageProperty(image,property);
9835 /* Don't write any "png:" properties; those are just for "identify" */
9836 if (LocaleNCompare(property,"png:",4) != 0 &&
9838 /* Suppress density and units if we wrote a pHYs chunk */
9839 (ping_exclude_pHYs != MagickFalse ||
9840 LocaleCompare(property,"density") != 0 ||
9841 LocaleCompare(property,"units") != 0) &&
9843 /* Suppress the IM-generated Date:create and Date:modify */
9844 (ping_exclude_date == MagickFalse ||
9845 LocaleNCompare(property, "Date:",5) != 0))
9847 if (value != (const char *) NULL)
9849 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9850 text[0].key=(char *) property;
9851 text[0].text=(char *) value;
9852 text[0].text_length=strlen(value);
9854 if (ping_exclude_tEXt != MagickFalse)
9855 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9857 else if (ping_exclude_zTXt != MagickFalse)
9858 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9862 text[0].compression=image_info->compression == NoCompression ||
9863 (image_info->compression == UndefinedCompression &&
9864 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9865 PNG_TEXT_COMPRESSION_zTXt ;
9868 if (logging != MagickFalse)
9870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9871 " Setting up text chunk");
9873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9874 " keyword: %s",text[0].key);
9877 png_set_text(ping,ping_info,text,1);
9878 png_free(ping,text);
9881 property=GetNextImageProperty(image);
9885 /* write any PNG-chunk-e profiles */
9886 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9888 if (logging != MagickFalse)
9889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9890 " Writing PNG end info");
9892 png_write_end(ping,ping_info);
9894 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9896 if (mng_info->page.x || mng_info->page.y ||
9897 (ping_width != mng_info->page.width) ||
9898 (ping_height != mng_info->page.height))
9904 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9906 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9907 PNGType(chunk,mng_FRAM);
9908 LogPNGChunk(logging,mng_FRAM,27L);
9910 chunk[5]=0; /* frame name separator (no name) */
9911 chunk[6]=1; /* flag for changing delay, for next frame only */
9912 chunk[7]=0; /* flag for changing frame timeout */
9913 chunk[8]=1; /* flag for changing frame clipping for next frame */
9914 chunk[9]=0; /* flag for changing frame sync_id */
9915 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9916 chunk[14]=0; /* clipping boundaries delta type */
9917 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9919 (png_uint_32) (mng_info->page.x + ping_width));
9920 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9922 (png_uint_32) (mng_info->page.y + ping_height));
9923 (void) WriteBlob(image,31,chunk);
9924 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9925 mng_info->old_framing_mode=4;
9926 mng_info->framing_mode=1;
9930 mng_info->framing_mode=3;
9932 if (mng_info->write_mng && !mng_info->need_fram &&
9933 ((int) image->dispose == 3))
9934 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9935 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9936 "`%s'",image->filename);
9942 png_destroy_write_struct(&ping,&ping_info);
9944 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9946 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9947 UnlockSemaphoreInfo(ping_semaphore);
9950 if (ping_have_blob != MagickFalse)
9951 (void) CloseBlob(image);
9953 image_info=DestroyImageInfo(image_info);
9954 image=DestroyImage(image);
9956 /* Store bit depth actually written */
9957 s[0]=(char) ping_bit_depth;
9960 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " exit WriteOnePNGImage()");
9967 /* End write one PNG image */
9971 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9975 % W r i t e P N G I m a g e %
9979 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9981 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9982 % Multiple-image Network Graphics (MNG) image file.
9984 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9986 % The format of the WritePNGImage method is:
9988 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9990 % A description of each parameter follows:
9992 % o image_info: the image info.
9994 % o image: The image.
9996 % Returns MagickTrue on success, MagickFalse on failure.
9998 % Communicating with the PNG encoder:
10000 % While the datastream written is always in PNG format and normally would
10001 % be given the "png" file extension, this method also writes the following
10002 % pseudo-formats which are subsets of PNG:
10004 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10005 % a depth greater than 8, the depth is reduced. If transparency
10006 % is present, the tRNS chunk must only have values 0 and 255
10007 % (i.e., transparency is binary: fully opaque or fully
10008 % transparent). If other values are present they will be
10009 % 50%-thresholded to binary transparency. If more than 256
10010 % colors are present, they will be quantized to the 4-4-4-1,
10011 % 3-3-3-1, or 3-3-2-1 palette.
10013 % If you want better quantization or dithering of the colors
10014 % or alpha than that, you need to do it before calling the
10015 % PNG encoder. The pixels contain 8-bit indices even if
10016 % they could be represented with 1, 2, or 4 bits. Grayscale
10017 % images will be written as indexed PNG files even though the
10018 % PNG grayscale type might be slightly more efficient. Please
10019 % note that writing to the PNG8 format may result in loss
10020 % of color and alpha data.
10022 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10023 % chunk can be present to convey binary transparency by naming
10024 % one of the colors as transparent. The only loss incurred
10025 % is reduction of sample depth to 8. If the image has more
10026 % than one transparent color, has semitransparent pixels, or
10027 % has an opaque pixel with the same RGB components as the
10028 % transparent color, an image is not written.
10030 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10031 % transparency is permitted, i.e., the alpha sample for
10032 % each pixel can have any value from 0 to 255. The alpha
10033 % channel is present even if the image is fully opaque.
10034 % The only loss in data is the reduction of the sample depth
10037 % o -define: For more precise control of the PNG output, you can use the
10038 % Image options "png:bit-depth" and "png:color-type". These
10039 % can be set from the commandline with "-define" and also
10040 % from the application programming interfaces. The options
10041 % are case-independent and are converted to lowercase before
10042 % being passed to this encoder.
10044 % png:color-type can be 0, 2, 3, 4, or 6.
10046 % When png:color-type is 0 (Grayscale), png:bit-depth can
10047 % be 1, 2, 4, 8, or 16.
10049 % When png:color-type is 2 (RGB), png:bit-depth can
10052 % When png:color-type is 3 (Indexed), png:bit-depth can
10053 % be 1, 2, 4, or 8. This refers to the number of bits
10054 % used to store the index. The color samples always have
10055 % bit-depth 8 in indexed PNG files.
10057 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10058 % png:bit-depth can be 8 or 16.
10060 % If the image cannot be written without loss with the requested bit-depth
10061 % and color-type, a PNG file will not be written, and the encoder will
10062 % return MagickFalse.
10064 % Since image encoders should not be responsible for the "heavy lifting",
10065 % the user should make sure that ImageMagick has already reduced the
10066 % image depth and number of colors and limit transparency to binary
10067 % transparency prior to attempting to write the image with depth, color,
10068 % or transparency limitations.
10070 % TODO: Enforce the previous paragraph.
10072 % Note that another definition, "png:bit-depth-written" exists, but it
10073 % is not intended for external use. It is only used internally by the
10074 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10076 % It is possible to request that the PNG encoder write previously-formatted
10077 % ancillary chunks in the output PNG file, using the "-profile" commandline
10078 % option as shown below or by setting the profile via a programming
10081 % -profile PNG-chunk-x:<file>
10083 % where x is a location flag and <file> is a file containing the chunk
10084 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10085 % This encoder will compute the chunk length and CRC, so those must not
10086 % be included in the file.
10088 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10089 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10090 % of the same type, then add a short unique string after the "x" to prevent
10091 % subsequent profiles from overwriting the preceding ones, e.g.,
10093 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10095 % As of version 6.6.6 the following optimizations are always done:
10097 % o 32-bit depth is reduced to 16.
10098 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10099 % high byte and low byte are identical.
10100 % o Palette is sorted to remove unused entries and to put a
10101 % transparent color first, if BUILD_PNG_PALETTE is defined.
10102 % o Opaque matte channel is removed when writing an indexed PNG.
10103 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10104 % this can be done without loss and a larger bit depth N was not
10105 % requested via the "-define PNG:bit-depth=N" option.
10106 % o If matte channel is present but only one transparent color is
10107 % present, RGB+tRNS is written instead of RGBA
10108 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10109 % was requested when converting an opaque image).
10111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10113 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10119 have_mng_structure,
10135 assert(image_info != (const ImageInfo *) NULL);
10136 assert(image_info->signature == MagickSignature);
10137 assert(image != (Image *) NULL);
10138 assert(image->signature == MagickSignature);
10139 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10140 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10142 Allocate a MngInfo structure.
10144 have_mng_structure=MagickFalse;
10145 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10147 if (mng_info == (MngInfo *) NULL)
10148 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10151 Initialize members of the MngInfo structure.
10153 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10154 mng_info->image=image;
10155 mng_info->equal_backgrounds=MagickTrue;
10156 have_mng_structure=MagickTrue;
10158 /* See if user has requested a specific PNG subformat */
10160 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10161 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10162 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10164 if (mng_info->write_png8)
10166 mng_info->write_png_colortype = /* 3 */ 4;
10167 mng_info->write_png_depth = 8;
10171 if (mng_info->write_png24)
10173 mng_info->write_png_colortype = /* 2 */ 3;
10174 mng_info->write_png_depth = 8;
10177 if (image->matte == MagickTrue)
10178 (void) SetImageType(image,TrueColorMatteType);
10181 (void) SetImageType(image,TrueColorType);
10183 (void) SyncImage(image);
10186 if (mng_info->write_png32)
10188 mng_info->write_png_colortype = /* 6 */ 7;
10189 mng_info->write_png_depth = 8;
10192 if (image->matte == MagickTrue)
10193 (void) SetImageType(image,TrueColorMatteType);
10196 (void) SetImageType(image,TrueColorType);
10198 (void) SyncImage(image);
10201 value=GetImageOption(image_info,"png:bit-depth");
10203 if (value != (char *) NULL)
10205 if (LocaleCompare(value,"1") == 0)
10206 mng_info->write_png_depth = 1;
10208 else if (LocaleCompare(value,"2") == 0)
10209 mng_info->write_png_depth = 2;
10211 else if (LocaleCompare(value,"4") == 0)
10212 mng_info->write_png_depth = 4;
10214 else if (LocaleCompare(value,"8") == 0)
10215 mng_info->write_png_depth = 8;
10217 else if (LocaleCompare(value,"16") == 0)
10218 mng_info->write_png_depth = 16;
10221 (void) ThrowMagickException(&image->exception,
10222 GetMagickModule(),CoderWarning,
10223 "ignoring invalid defined png:bit-depth",
10226 if (logging != MagickFalse)
10227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10228 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10231 value=GetImageOption(image_info,"png:color-type");
10233 if (value != (char *) NULL)
10235 /* We must store colortype+1 because 0 is a valid colortype */
10236 if (LocaleCompare(value,"0") == 0)
10237 mng_info->write_png_colortype = 1;
10239 else if (LocaleCompare(value,"2") == 0)
10240 mng_info->write_png_colortype = 3;
10242 else if (LocaleCompare(value,"3") == 0)
10243 mng_info->write_png_colortype = 4;
10245 else if (LocaleCompare(value,"4") == 0)
10246 mng_info->write_png_colortype = 5;
10248 else if (LocaleCompare(value,"6") == 0)
10249 mng_info->write_png_colortype = 7;
10252 (void) ThrowMagickException(&image->exception,
10253 GetMagickModule(),CoderWarning,
10254 "ignoring invalid defined png:color-type",
10257 if (logging != MagickFalse)
10258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10262 /* Check for chunks to be excluded:
10264 * The default is to not exclude any known chunks except for any
10265 * listed in the "unused_chunks" array, above.
10267 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10268 * define (in the image properties or in the image artifacts)
10269 * or via a mng_info member. For convenience, in addition
10270 * to or instead of a comma-separated list of chunks, the
10271 * "exclude-chunk" string can be simply "all" or "none".
10273 * The exclude-chunk define takes priority over the mng_info.
10275 * A "PNG:include-chunk" define takes priority over both the
10276 * mng_info and the "PNG:exclude-chunk" define. Like the
10277 * "exclude-chunk" string, it can define "all" or "none" as
10278 * well as a comma-separated list. Chunks that are unknown to
10279 * ImageMagick are always excluded, regardless of their "copy-safe"
10280 * status according to the PNG specification, and even if they
10281 * appear in the "include-chunk" list.
10283 * Finally, all chunks listed in the "unused_chunks" array are
10284 * automatically excluded, regardless of the other instructions
10287 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10288 * will not be written and the gAMA chunk will only be written if it
10289 * is not between .45 and .46, or approximately (1.0/2.2).
10291 * If you exclude tRNS and the image has transparency, the colortype
10292 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10294 * The -strip option causes StripImage() to set the png:include-chunk
10295 * artifact to "none,gama".
10298 mng_info->ping_exclude_bKGD=MagickFalse;
10299 mng_info->ping_exclude_cHRM=MagickFalse;
10300 mng_info->ping_exclude_date=MagickFalse;
10301 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10302 mng_info->ping_exclude_gAMA=MagickFalse;
10303 mng_info->ping_exclude_iCCP=MagickFalse;
10304 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10305 mng_info->ping_exclude_oFFs=MagickFalse;
10306 mng_info->ping_exclude_pHYs=MagickFalse;
10307 mng_info->ping_exclude_sRGB=MagickFalse;
10308 mng_info->ping_exclude_tEXt=MagickFalse;
10309 mng_info->ping_exclude_tRNS=MagickFalse;
10310 mng_info->ping_exclude_vpAg=MagickFalse;
10311 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10312 mng_info->ping_exclude_zTXt=MagickFalse;
10314 mng_info->ping_preserve_colormap=MagickFalse;
10316 value=GetImageArtifact(image,"png:preserve-colormap");
10318 value=GetImageOption(image_info,"png:preserve-colormap");
10320 mng_info->ping_preserve_colormap=MagickTrue;
10322 excluding=MagickFalse;
10324 for (source=0; source<1; source++)
10328 value=GetImageArtifact(image,"png:exclude-chunk");
10331 value=GetImageArtifact(image,"png:exclude-chunks");
10335 value=GetImageOption(image_info,"png:exclude-chunk");
10338 value=GetImageOption(image_info,"png:exclude-chunks");
10347 excluding=MagickTrue;
10349 if (logging != MagickFalse)
10352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353 " png:exclude-chunk=%s found in image artifacts.\n", value);
10355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356 " png:exclude-chunk=%s found in image properties.\n", value);
10359 last=strlen(value);
10361 for (i=0; i<(int) last; i+=5)
10364 if (LocaleNCompare(value+i,"all",3) == 0)
10366 mng_info->ping_exclude_bKGD=MagickTrue;
10367 mng_info->ping_exclude_cHRM=MagickTrue;
10368 mng_info->ping_exclude_date=MagickTrue;
10369 mng_info->ping_exclude_EXIF=MagickTrue;
10370 mng_info->ping_exclude_gAMA=MagickTrue;
10371 mng_info->ping_exclude_iCCP=MagickTrue;
10372 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10373 mng_info->ping_exclude_oFFs=MagickTrue;
10374 mng_info->ping_exclude_pHYs=MagickTrue;
10375 mng_info->ping_exclude_sRGB=MagickTrue;
10376 mng_info->ping_exclude_tEXt=MagickTrue;
10377 mng_info->ping_exclude_tRNS=MagickTrue;
10378 mng_info->ping_exclude_vpAg=MagickTrue;
10379 mng_info->ping_exclude_zCCP=MagickTrue;
10380 mng_info->ping_exclude_zTXt=MagickTrue;
10384 if (LocaleNCompare(value+i,"none",4) == 0)
10386 mng_info->ping_exclude_bKGD=MagickFalse;
10387 mng_info->ping_exclude_cHRM=MagickFalse;
10388 mng_info->ping_exclude_date=MagickFalse;
10389 mng_info->ping_exclude_EXIF=MagickFalse;
10390 mng_info->ping_exclude_gAMA=MagickFalse;
10391 mng_info->ping_exclude_iCCP=MagickFalse;
10392 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10393 mng_info->ping_exclude_oFFs=MagickFalse;
10394 mng_info->ping_exclude_pHYs=MagickFalse;
10395 mng_info->ping_exclude_sRGB=MagickFalse;
10396 mng_info->ping_exclude_tEXt=MagickFalse;
10397 mng_info->ping_exclude_tRNS=MagickFalse;
10398 mng_info->ping_exclude_vpAg=MagickFalse;
10399 mng_info->ping_exclude_zCCP=MagickFalse;
10400 mng_info->ping_exclude_zTXt=MagickFalse;
10403 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10404 mng_info->ping_exclude_bKGD=MagickTrue;
10406 if (LocaleNCompare(value+i,"chrm",4) == 0)
10407 mng_info->ping_exclude_cHRM=MagickTrue;
10409 if (LocaleNCompare(value+i,"date",4) == 0)
10410 mng_info->ping_exclude_date=MagickTrue;
10412 if (LocaleNCompare(value+i,"exif",4) == 0)
10413 mng_info->ping_exclude_EXIF=MagickTrue;
10415 if (LocaleNCompare(value+i,"gama",4) == 0)
10416 mng_info->ping_exclude_gAMA=MagickTrue;
10418 if (LocaleNCompare(value+i,"iccp",4) == 0)
10419 mng_info->ping_exclude_iCCP=MagickTrue;
10422 if (LocaleNCompare(value+i,"itxt",4) == 0)
10423 mng_info->ping_exclude_iTXt=MagickTrue;
10426 if (LocaleNCompare(value+i,"gama",4) == 0)
10427 mng_info->ping_exclude_gAMA=MagickTrue;
10429 if (LocaleNCompare(value+i,"offs",4) == 0)
10430 mng_info->ping_exclude_oFFs=MagickTrue;
10432 if (LocaleNCompare(value+i,"phys",4) == 0)
10433 mng_info->ping_exclude_pHYs=MagickTrue;
10435 if (LocaleNCompare(value+i,"srgb",4) == 0)
10436 mng_info->ping_exclude_sRGB=MagickTrue;
10438 if (LocaleNCompare(value+i,"text",4) == 0)
10439 mng_info->ping_exclude_tEXt=MagickTrue;
10441 if (LocaleNCompare(value+i,"trns",4) == 0)
10442 mng_info->ping_exclude_tRNS=MagickTrue;
10444 if (LocaleNCompare(value+i,"vpag",4) == 0)
10445 mng_info->ping_exclude_vpAg=MagickTrue;
10447 if (LocaleNCompare(value+i,"zccp",4) == 0)
10448 mng_info->ping_exclude_zCCP=MagickTrue;
10450 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10451 mng_info->ping_exclude_zTXt=MagickTrue;
10457 for (source=0; source<1; source++)
10461 value=GetImageArtifact(image,"png:include-chunk");
10464 value=GetImageArtifact(image,"png:include-chunks");
10468 value=GetImageOption(image_info,"png:include-chunk");
10471 value=GetImageOption(image_info,"png:include-chunks");
10479 excluding=MagickTrue;
10481 if (logging != MagickFalse)
10484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10485 " png:include-chunk=%s found in image artifacts.\n", value);
10487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10488 " png:include-chunk=%s found in image properties.\n", value);
10491 last=strlen(value);
10493 for (i=0; i<(int) last; i+=5)
10495 if (LocaleNCompare(value+i,"all",3) == 0)
10497 mng_info->ping_exclude_bKGD=MagickFalse;
10498 mng_info->ping_exclude_cHRM=MagickFalse;
10499 mng_info->ping_exclude_date=MagickFalse;
10500 mng_info->ping_exclude_EXIF=MagickFalse;
10501 mng_info->ping_exclude_gAMA=MagickFalse;
10502 mng_info->ping_exclude_iCCP=MagickFalse;
10503 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10504 mng_info->ping_exclude_oFFs=MagickFalse;
10505 mng_info->ping_exclude_pHYs=MagickFalse;
10506 mng_info->ping_exclude_sRGB=MagickFalse;
10507 mng_info->ping_exclude_tEXt=MagickFalse;
10508 mng_info->ping_exclude_tRNS=MagickFalse;
10509 mng_info->ping_exclude_vpAg=MagickFalse;
10510 mng_info->ping_exclude_zCCP=MagickFalse;
10511 mng_info->ping_exclude_zTXt=MagickFalse;
10515 if (LocaleNCompare(value+i,"none",4) == 0)
10517 mng_info->ping_exclude_bKGD=MagickTrue;
10518 mng_info->ping_exclude_cHRM=MagickTrue;
10519 mng_info->ping_exclude_date=MagickTrue;
10520 mng_info->ping_exclude_EXIF=MagickTrue;
10521 mng_info->ping_exclude_gAMA=MagickTrue;
10522 mng_info->ping_exclude_iCCP=MagickTrue;
10523 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10524 mng_info->ping_exclude_oFFs=MagickTrue;
10525 mng_info->ping_exclude_pHYs=MagickTrue;
10526 mng_info->ping_exclude_sRGB=MagickTrue;
10527 mng_info->ping_exclude_tEXt=MagickTrue;
10528 mng_info->ping_exclude_tRNS=MagickTrue;
10529 mng_info->ping_exclude_vpAg=MagickTrue;
10530 mng_info->ping_exclude_zCCP=MagickTrue;
10531 mng_info->ping_exclude_zTXt=MagickTrue;
10534 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10535 mng_info->ping_exclude_bKGD=MagickFalse;
10537 if (LocaleNCompare(value+i,"chrm",4) == 0)
10538 mng_info->ping_exclude_cHRM=MagickFalse;
10540 if (LocaleNCompare(value+i,"date",4) == 0)
10541 mng_info->ping_exclude_date=MagickFalse;
10543 if (LocaleNCompare(value+i,"exif",4) == 0)
10544 mng_info->ping_exclude_EXIF=MagickFalse;
10546 if (LocaleNCompare(value+i,"gama",4) == 0)
10547 mng_info->ping_exclude_gAMA=MagickFalse;
10549 if (LocaleNCompare(value+i,"iccp",4) == 0)
10550 mng_info->ping_exclude_iCCP=MagickFalse;
10553 if (LocaleNCompare(value+i,"itxt",4) == 0)
10554 mng_info->ping_exclude_iTXt=MagickFalse;
10557 if (LocaleNCompare(value+i,"gama",4) == 0)
10558 mng_info->ping_exclude_gAMA=MagickFalse;
10560 if (LocaleNCompare(value+i,"offs",4) == 0)
10561 mng_info->ping_exclude_oFFs=MagickFalse;
10563 if (LocaleNCompare(value+i,"phys",4) == 0)
10564 mng_info->ping_exclude_pHYs=MagickFalse;
10566 if (LocaleNCompare(value+i,"srgb",4) == 0)
10567 mng_info->ping_exclude_sRGB=MagickFalse;
10569 if (LocaleNCompare(value+i,"text",4) == 0)
10570 mng_info->ping_exclude_tEXt=MagickFalse;
10572 if (LocaleNCompare(value+i,"trns",4) == 0)
10573 mng_info->ping_exclude_tRNS=MagickFalse;
10575 if (LocaleNCompare(value+i,"vpag",4) == 0)
10576 mng_info->ping_exclude_vpAg=MagickFalse;
10578 if (LocaleNCompare(value+i,"zccp",4) == 0)
10579 mng_info->ping_exclude_zCCP=MagickFalse;
10581 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10582 mng_info->ping_exclude_zTXt=MagickFalse;
10588 if (excluding != MagickFalse && logging != MagickFalse)
10590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591 " Chunks to be excluded from the output PNG:");
10592 if (mng_info->ping_exclude_bKGD != MagickFalse)
10593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10595 if (mng_info->ping_exclude_cHRM != MagickFalse)
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 if (mng_info->ping_exclude_date != MagickFalse)
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 if (mng_info->ping_exclude_EXIF != MagickFalse)
10602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604 if (mng_info->ping_exclude_gAMA != MagickFalse)
10605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10607 if (mng_info->ping_exclude_iCCP != MagickFalse)
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 if (mng_info->ping_exclude_iTXt != MagickFalse)
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615 if (mng_info->ping_exclude_oFFs != MagickFalse)
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618 if (mng_info->ping_exclude_pHYs != MagickFalse)
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621 if (mng_info->ping_exclude_sRGB != MagickFalse)
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 if (mng_info->ping_exclude_tEXt != MagickFalse)
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 if (mng_info->ping_exclude_tRNS != MagickFalse)
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 if (mng_info->ping_exclude_vpAg != MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 if (mng_info->ping_exclude_zCCP != MagickFalse)
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 if (mng_info->ping_exclude_zTXt != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 mng_info->need_blob = MagickTrue;
10643 status=WriteOnePNGImage(mng_info,image_info,image);
10645 MngInfoFreeStruct(mng_info,&have_mng_structure);
10647 if (logging != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10653 #if defined(JNG_SUPPORTED)
10655 /* Write one JNG image */
10656 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10657 const ImageInfo *image_info,Image *image)
10678 jng_alpha_compression_method,
10679 jng_alpha_sample_depth,
10686 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10687 " Enter WriteOneJNGImage()");
10689 blob=(unsigned char *) NULL;
10690 jpeg_image=(Image *) NULL;
10691 jpeg_image_info=(ImageInfo *) NULL;
10694 transparent=image_info->type==GrayscaleMatteType ||
10695 image_info->type==TrueColorMatteType;
10697 jng_alpha_sample_depth=0;
10698 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10699 jng_alpha_compression_method=0;
10701 if (image->matte != MagickFalse)
10703 /* if any pixels are transparent */
10704 transparent=MagickTrue;
10705 if (image_info->compression==JPEGCompression)
10706 jng_alpha_compression_method=8;
10713 /* Create JPEG blob, image, and image_info */
10714 if (logging != MagickFalse)
10715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10716 " Creating jpeg_image_info for opacity.");
10718 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10720 if (jpeg_image_info == (ImageInfo *) NULL)
10721 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10723 if (logging != MagickFalse)
10724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10725 " Creating jpeg_image.");
10727 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10729 if (jpeg_image == (Image *) NULL)
10730 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10732 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10733 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10734 status=NegateImage(jpeg_image,MagickFalse);
10735 jpeg_image->matte=MagickFalse;
10737 if (jng_quality >= 1000)
10738 jpeg_image_info->quality=jng_quality/1000;
10741 jpeg_image_info->quality=jng_quality;
10743 jpeg_image_info->type=GrayscaleType;
10744 (void) SetImageType(jpeg_image,GrayscaleType);
10745 (void) AcquireUniqueFilename(jpeg_image->filename);
10746 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10747 "%s",jpeg_image->filename);
10750 /* To do: check bit depth of PNG alpha channel */
10752 /* Check if image is grayscale. */
10753 if (image_info->type != TrueColorMatteType && image_info->type !=
10754 TrueColorType && ImageIsGray(image))
10759 if (jng_alpha_compression_method==0)
10764 /* Encode opacity as a grayscale PNG blob */
10765 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10766 &image->exception);
10767 if (logging != MagickFalse)
10768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769 " Creating PNG blob.");
10772 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10773 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10774 jpeg_image_info->interlace=NoInterlace;
10776 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10777 &image->exception);
10779 /* Retrieve sample depth used */
10780 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10781 if (value != (char *) NULL)
10782 jng_alpha_sample_depth= (unsigned int) value[0];
10786 /* Encode opacity as a grayscale JPEG blob */
10788 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10789 &image->exception);
10791 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10792 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10793 jpeg_image_info->interlace=NoInterlace;
10794 if (logging != MagickFalse)
10795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 " Creating blob.");
10797 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10798 &image->exception);
10799 jng_alpha_sample_depth=8;
10801 if (logging != MagickFalse)
10802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803 " Successfully read jpeg_image into a blob, length=%.20g.",
10807 /* Destroy JPEG image and image_info */
10808 jpeg_image=DestroyImage(jpeg_image);
10809 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10810 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10813 /* Write JHDR chunk */
10814 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10815 PNGType(chunk,mng_JHDR);
10816 LogPNGChunk(logging,mng_JHDR,16L);
10817 PNGLong(chunk+4,(png_uint_32) image->columns);
10818 PNGLong(chunk+8,(png_uint_32) image->rows);
10819 chunk[12]=jng_color_type;
10820 chunk[13]=8; /* sample depth */
10821 chunk[14]=8; /*jng_image_compression_method */
10822 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10823 chunk[16]=jng_alpha_sample_depth;
10824 chunk[17]=jng_alpha_compression_method;
10825 chunk[18]=0; /*jng_alpha_filter_method */
10826 chunk[19]=0; /*jng_alpha_interlace_method */
10827 (void) WriteBlob(image,20,chunk);
10828 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10829 if (logging != MagickFalse)
10831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10832 " JNG width:%15lu",(unsigned long) image->columns);
10834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835 " JNG height:%14lu",(unsigned long) image->rows);
10837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838 " JNG color type:%10d",jng_color_type);
10840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10841 " JNG sample depth:%8d",8);
10843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10844 " JNG compression:%9d",8);
10846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10847 " JNG interlace:%11d",0);
10849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10850 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10853 " JNG alpha compression:%3d",jng_alpha_compression_method);
10855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10856 " JNG alpha filter:%8d",0);
10858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10859 " JNG alpha interlace:%5d",0);
10862 /* Write any JNG-chunk-b profiles */
10863 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10866 Write leading ancillary chunks
10872 Write JNG bKGD chunk
10883 if (jng_color_type == 8 || jng_color_type == 12)
10887 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10888 PNGType(chunk,mng_bKGD);
10889 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10890 red=ScaleQuantumToChar(image->background_color.red);
10891 green=ScaleQuantumToChar(image->background_color.green);
10892 blue=ScaleQuantumToChar(image->background_color.blue);
10899 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10900 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10903 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10906 Write JNG sRGB chunk
10908 (void) WriteBlobMSBULong(image,1L);
10909 PNGType(chunk,mng_sRGB);
10910 LogPNGChunk(logging,mng_sRGB,1L);
10912 if (image->rendering_intent != UndefinedIntent)
10913 chunk[4]=(unsigned char)
10914 Magick_RenderingIntent_to_PNG_RenderingIntent(
10915 (image->rendering_intent));
10918 chunk[4]=(unsigned char)
10919 Magick_RenderingIntent_to_PNG_RenderingIntent(
10920 (PerceptualIntent));
10922 (void) WriteBlob(image,5,chunk);
10923 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10927 if (image->gamma != 0.0)
10930 Write JNG gAMA chunk
10932 (void) WriteBlobMSBULong(image,4L);
10933 PNGType(chunk,mng_gAMA);
10934 LogPNGChunk(logging,mng_gAMA,4L);
10935 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10936 (void) WriteBlob(image,8,chunk);
10937 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10940 if ((mng_info->equal_chrms == MagickFalse) &&
10941 (image->chromaticity.red_primary.x != 0.0))
10947 Write JNG cHRM chunk
10949 (void) WriteBlobMSBULong(image,32L);
10950 PNGType(chunk,mng_cHRM);
10951 LogPNGChunk(logging,mng_cHRM,32L);
10952 primary=image->chromaticity.white_point;
10953 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10954 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10955 primary=image->chromaticity.red_primary;
10956 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10957 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10958 primary=image->chromaticity.green_primary;
10959 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10960 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10961 primary=image->chromaticity.blue_primary;
10962 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10963 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10964 (void) WriteBlob(image,36,chunk);
10965 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10969 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10972 Write JNG pHYs chunk
10974 (void) WriteBlobMSBULong(image,9L);
10975 PNGType(chunk,mng_pHYs);
10976 LogPNGChunk(logging,mng_pHYs,9L);
10977 if (image->units == PixelsPerInchResolution)
10979 PNGLong(chunk+4,(png_uint_32)
10980 (image->x_resolution*100.0/2.54+0.5));
10982 PNGLong(chunk+8,(png_uint_32)
10983 (image->y_resolution*100.0/2.54+0.5));
10990 if (image->units == PixelsPerCentimeterResolution)
10992 PNGLong(chunk+4,(png_uint_32)
10993 (image->x_resolution*100.0+0.5));
10995 PNGLong(chunk+8,(png_uint_32)
10996 (image->y_resolution*100.0+0.5));
11003 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11004 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11008 (void) WriteBlob(image,13,chunk);
11009 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11012 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11015 Write JNG oFFs chunk
11017 (void) WriteBlobMSBULong(image,9L);
11018 PNGType(chunk,mng_oFFs);
11019 LogPNGChunk(logging,mng_oFFs,9L);
11020 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11021 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11023 (void) WriteBlob(image,13,chunk);
11024 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11026 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11028 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11029 PNGType(chunk,mng_vpAg);
11030 LogPNGChunk(logging,mng_vpAg,9L);
11031 PNGLong(chunk+4,(png_uint_32) image->page.width);
11032 PNGLong(chunk+8,(png_uint_32) image->page.height);
11033 chunk[12]=0; /* unit = pixels */
11034 (void) WriteBlob(image,13,chunk);
11035 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11041 if (jng_alpha_compression_method==0)
11049 /* Write IDAT chunk header */
11050 if (logging != MagickFalse)
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11052 " Write IDAT chunks from blob, length=%.20g.",(double)
11055 /* Copy IDAT chunks */
11058 for (i=8; i<(ssize_t) length; i+=len+12)
11060 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11063 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11065 /* Found an IDAT chunk. */
11066 (void) WriteBlobMSBULong(image,(size_t) len);
11067 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11068 (void) WriteBlob(image,(size_t) len+4,p);
11069 (void) WriteBlobMSBULong(image,
11070 crc32(0,p,(uInt) len+4));
11075 if (logging != MagickFalse)
11076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11077 " Skipping %c%c%c%c chunk, length=%.20g.",
11078 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11085 /* Write JDAA chunk header */
11086 if (logging != MagickFalse)
11087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11088 " Write JDAA chunk, length=%.20g.",(double) length);
11089 (void) WriteBlobMSBULong(image,(size_t) length);
11090 PNGType(chunk,mng_JDAA);
11091 LogPNGChunk(logging,mng_JDAA,length);
11092 /* Write JDAT chunk(s) data */
11093 (void) WriteBlob(image,4,chunk);
11094 (void) WriteBlob(image,length,blob);
11095 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11098 blob=(unsigned char *) RelinquishMagickMemory(blob);
11101 /* Encode image as a JPEG blob */
11102 if (logging != MagickFalse)
11103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104 " Creating jpeg_image_info.");
11105 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11106 if (jpeg_image_info == (ImageInfo *) NULL)
11107 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11109 if (logging != MagickFalse)
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " Creating jpeg_image.");
11113 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11114 if (jpeg_image == (Image *) NULL)
11115 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11116 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11118 (void) AcquireUniqueFilename(jpeg_image->filename);
11119 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11120 jpeg_image->filename);
11122 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11123 &image->exception);
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11128 (double) jpeg_image->rows);
11130 if (jng_color_type == 8 || jng_color_type == 12)
11131 jpeg_image_info->type=GrayscaleType;
11133 jpeg_image_info->quality=jng_quality % 1000;
11134 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11135 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11137 if (logging != MagickFalse)
11138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11139 " Creating blob.");
11141 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11143 if (logging != MagickFalse)
11145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146 " Successfully read jpeg_image into a blob, length=%.20g.",
11149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11150 " Write JDAT chunk, length=%.20g.",(double) length);
11153 /* Write JDAT chunk(s) */
11154 (void) WriteBlobMSBULong(image,(size_t) length);
11155 PNGType(chunk,mng_JDAT);
11156 LogPNGChunk(logging,mng_JDAT,length);
11157 (void) WriteBlob(image,4,chunk);
11158 (void) WriteBlob(image,length,blob);
11159 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11161 jpeg_image=DestroyImage(jpeg_image);
11162 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11163 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11164 blob=(unsigned char *) RelinquishMagickMemory(blob);
11166 /* Write any JNG-chunk-e profiles */
11167 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
11169 /* Write IEND chunk */
11170 (void) WriteBlobMSBULong(image,0L);
11171 PNGType(chunk,mng_IEND);
11172 LogPNGChunk(logging,mng_IEND,0);
11173 (void) WriteBlob(image,4,chunk);
11174 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11176 if (logging != MagickFalse)
11177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11178 " exit WriteOneJNGImage()");
11185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11189 % W r i t e J N G I m a g e %
11193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11195 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11197 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
11199 % The format of the WriteJNGImage method is:
11201 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11203 % A description of each parameter follows:
11205 % o image_info: the image info.
11207 % o image: The image.
11209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11211 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11214 have_mng_structure,
11224 assert(image_info != (const ImageInfo *) NULL);
11225 assert(image_info->signature == MagickSignature);
11226 assert(image != (Image *) NULL);
11227 assert(image->signature == MagickSignature);
11228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11229 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11230 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11231 if (status == MagickFalse)
11235 Allocate a MngInfo structure.
11237 have_mng_structure=MagickFalse;
11238 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11239 if (mng_info == (MngInfo *) NULL)
11240 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11242 Initialize members of the MngInfo structure.
11244 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11245 mng_info->image=image;
11246 have_mng_structure=MagickTrue;
11248 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11250 status=WriteOneJNGImage(mng_info,image_info,image);
11251 (void) CloseBlob(image);
11253 (void) CatchImageException(image);
11254 MngInfoFreeStruct(mng_info,&have_mng_structure);
11255 if (logging != MagickFalse)
11256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11263 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11272 have_mng_structure,
11275 volatile MagickBooleanType
11287 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11288 defined(PNG_MNG_FEATURES_SUPPORTED)
11291 all_images_are_gray,
11301 volatile unsigned int
11312 #if (PNG_LIBPNG_VER < 10200)
11313 if (image_info->verbose)
11314 printf("Your PNG library (libpng-%s) is rather old.\n",
11315 PNG_LIBPNG_VER_STRING);
11321 assert(image_info != (const ImageInfo *) NULL);
11322 assert(image_info->signature == MagickSignature);
11323 assert(image != (Image *) NULL);
11324 assert(image->signature == MagickSignature);
11325 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11326 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11327 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11328 if (status == MagickFalse)
11332 Allocate a MngInfo structure.
11334 have_mng_structure=MagickFalse;
11335 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11336 if (mng_info == (MngInfo *) NULL)
11337 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11339 Initialize members of the MngInfo structure.
11341 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11342 mng_info->image=image;
11343 have_mng_structure=MagickTrue;
11344 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11347 * See if user has requested a specific PNG subformat to be used
11348 * for all of the PNGs in the MNG being written, e.g.,
11350 * convert *.png png8:animation.mng
11352 * To do: check -define png:bit_depth and png:color_type as well,
11353 * or perhaps use mng:bit_depth and mng:color_type instead for
11357 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11358 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11359 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11361 write_jng=MagickFalse;
11362 if (image_info->compression == JPEGCompression)
11363 write_jng=MagickTrue;
11365 mng_info->adjoin=image_info->adjoin &&
11366 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11368 if (logging != MagickFalse)
11370 /* Log some info about the input */
11374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11375 " Checking input image(s)");
11377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11378 " Image_info depth: %.20g",(double) image_info->depth);
11380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11381 " Type: %d",image_info->type);
11384 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11387 " Scene: %.20g",(double) scene++);
11389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11390 " Image depth: %.20g",(double) p->depth);
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400 if (p->storage_class == PseudoClass)
11401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11402 " Storage class: PseudoClass");
11405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11406 " Storage class: DirectClass");
11409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11410 " Number of colors: %.20g",(double) p->colors);
11413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11414 " Number of colors: unspecified");
11416 if (mng_info->adjoin == MagickFalse)
11421 use_global_plte=MagickFalse;
11422 all_images_are_gray=MagickFalse;
11423 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11424 need_local_plte=MagickTrue;
11426 need_defi=MagickFalse;
11427 need_matte=MagickFalse;
11428 mng_info->framing_mode=1;
11429 mng_info->old_framing_mode=1;
11432 if (image_info->page != (char *) NULL)
11435 Determine image bounding box.
11437 SetGeometry(image,&mng_info->page);
11438 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11439 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11451 mng_info->page=image->page;
11452 need_geom=MagickTrue;
11453 if (mng_info->page.width || mng_info->page.height)
11454 need_geom=MagickFalse;
11456 Check all the scenes.
11458 initial_delay=image->delay;
11459 need_iterations=MagickFalse;
11460 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11461 mng_info->equal_physs=MagickTrue,
11462 mng_info->equal_gammas=MagickTrue;
11463 mng_info->equal_srgbs=MagickTrue;
11464 mng_info->equal_backgrounds=MagickTrue;
11466 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11467 defined(PNG_MNG_FEATURES_SUPPORTED)
11468 all_images_are_gray=MagickTrue;
11469 mng_info->equal_palettes=MagickFalse;
11470 need_local_plte=MagickFalse;
11472 for (next_image=image; next_image != (Image *) NULL; )
11476 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11477 mng_info->page.width=next_image->columns+next_image->page.x;
11479 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11480 mng_info->page.height=next_image->rows+next_image->page.y;
11483 if (next_image->page.x || next_image->page.y)
11484 need_defi=MagickTrue;
11486 if (next_image->matte)
11487 need_matte=MagickTrue;
11489 if ((int) next_image->dispose >= BackgroundDispose)
11490 if (next_image->matte || next_image->page.x || next_image->page.y ||
11491 ((next_image->columns < mng_info->page.width) &&
11492 (next_image->rows < mng_info->page.height)))
11493 mng_info->need_fram=MagickTrue;
11495 if (next_image->iterations)
11496 need_iterations=MagickTrue;
11498 final_delay=next_image->delay;
11500 if (final_delay != initial_delay || final_delay > 1UL*
11501 next_image->ticks_per_second)
11502 mng_info->need_fram=1;
11504 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11505 defined(PNG_MNG_FEATURES_SUPPORTED)
11507 check for global palette possibility.
11509 if (image->matte != MagickFalse)
11510 need_local_plte=MagickTrue;
11512 if (need_local_plte == 0)
11514 if (ImageIsGray(image) == MagickFalse)
11515 all_images_are_gray=MagickFalse;
11516 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11517 if (use_global_plte == 0)
11518 use_global_plte=mng_info->equal_palettes;
11519 need_local_plte=!mng_info->equal_palettes;
11522 if (GetNextImageInList(next_image) != (Image *) NULL)
11524 if (next_image->background_color.red !=
11525 next_image->next->background_color.red ||
11526 next_image->background_color.green !=
11527 next_image->next->background_color.green ||
11528 next_image->background_color.blue !=
11529 next_image->next->background_color.blue)
11530 mng_info->equal_backgrounds=MagickFalse;
11532 if (next_image->gamma != next_image->next->gamma)
11533 mng_info->equal_gammas=MagickFalse;
11535 if (next_image->rendering_intent !=
11536 next_image->next->rendering_intent)
11537 mng_info->equal_srgbs=MagickFalse;
11539 if ((next_image->units != next_image->next->units) ||
11540 (next_image->x_resolution != next_image->next->x_resolution) ||
11541 (next_image->y_resolution != next_image->next->y_resolution))
11542 mng_info->equal_physs=MagickFalse;
11544 if (mng_info->equal_chrms)
11546 if (next_image->chromaticity.red_primary.x !=
11547 next_image->next->chromaticity.red_primary.x ||
11548 next_image->chromaticity.red_primary.y !=
11549 next_image->next->chromaticity.red_primary.y ||
11550 next_image->chromaticity.green_primary.x !=
11551 next_image->next->chromaticity.green_primary.x ||
11552 next_image->chromaticity.green_primary.y !=
11553 next_image->next->chromaticity.green_primary.y ||
11554 next_image->chromaticity.blue_primary.x !=
11555 next_image->next->chromaticity.blue_primary.x ||
11556 next_image->chromaticity.blue_primary.y !=
11557 next_image->next->chromaticity.blue_primary.y ||
11558 next_image->chromaticity.white_point.x !=
11559 next_image->next->chromaticity.white_point.x ||
11560 next_image->chromaticity.white_point.y !=
11561 next_image->next->chromaticity.white_point.y)
11562 mng_info->equal_chrms=MagickFalse;
11566 next_image=GetNextImageInList(next_image);
11568 if (image_count < 2)
11570 mng_info->equal_backgrounds=MagickFalse;
11571 mng_info->equal_chrms=MagickFalse;
11572 mng_info->equal_gammas=MagickFalse;
11573 mng_info->equal_srgbs=MagickFalse;
11574 mng_info->equal_physs=MagickFalse;
11575 use_global_plte=MagickFalse;
11576 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11577 need_local_plte=MagickTrue;
11579 need_iterations=MagickFalse;
11582 if (mng_info->need_fram == MagickFalse)
11585 Only certain framing rates 100/n are exactly representable without
11586 the FRAM chunk but we'll allow some slop in VLC files
11588 if (final_delay == 0)
11590 if (need_iterations != MagickFalse)
11593 It's probably a GIF with loop; don't run it *too* fast.
11595 if (mng_info->adjoin)
11598 (void) ThrowMagickException(&image->exception,
11599 GetMagickModule(),CoderWarning,
11600 "input has zero delay between all frames; assuming",
11605 mng_info->ticks_per_second=0;
11607 if (final_delay != 0)
11608 mng_info->ticks_per_second=(png_uint_32)
11609 (image->ticks_per_second/final_delay);
11610 if (final_delay > 50)
11611 mng_info->ticks_per_second=2;
11613 if (final_delay > 75)
11614 mng_info->ticks_per_second=1;
11616 if (final_delay > 125)
11617 mng_info->need_fram=MagickTrue;
11619 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11620 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11621 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11622 1UL*image->ticks_per_second))
11623 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11626 if (mng_info->need_fram != MagickFalse)
11627 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11629 If pseudocolor, we should also check to see if all the
11630 palettes are identical and write a global PLTE if they are.
11634 Write the MNG version 1.0 signature and MHDR chunk.
11636 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11637 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11638 PNGType(chunk,mng_MHDR);
11639 LogPNGChunk(logging,mng_MHDR,28L);
11640 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11641 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11642 PNGLong(chunk+12,mng_info->ticks_per_second);
11643 PNGLong(chunk+16,0L); /* layer count=unknown */
11644 PNGLong(chunk+20,0L); /* frame count=unknown */
11645 PNGLong(chunk+24,0L); /* play time=unknown */
11650 if (need_defi || mng_info->need_fram || use_global_plte)
11651 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11654 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11659 if (need_defi || mng_info->need_fram || use_global_plte)
11660 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11663 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11671 if (need_defi || mng_info->need_fram || use_global_plte)
11672 PNGLong(chunk+28,11L); /* simplicity=LC */
11675 PNGLong(chunk+28,9L); /* simplicity=VLC */
11680 if (need_defi || mng_info->need_fram || use_global_plte)
11681 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11684 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11687 (void) WriteBlob(image,32,chunk);
11688 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11689 option=GetImageOption(image_info,"mng:need-cacheoff");
11690 if (option != (const char *) NULL)
11696 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11698 PNGType(chunk,mng_nEED);
11699 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11700 (void) WriteBlobMSBULong(image,(size_t) length);
11701 LogPNGChunk(logging,mng_nEED,(size_t) length);
11703 (void) WriteBlob(image,length,chunk);
11704 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11706 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11707 (GetNextImageInList(image) != (Image *) NULL) &&
11708 (image->iterations != 1))
11711 Write MNG TERM chunk
11713 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11714 PNGType(chunk,mng_TERM);
11715 LogPNGChunk(logging,mng_TERM,10L);
11716 chunk[4]=3; /* repeat animation */
11717 chunk[5]=0; /* show last frame when done */
11718 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11719 final_delay/MagickMax(image->ticks_per_second,1)));
11721 if (image->iterations == 0)
11722 PNGLong(chunk+10,PNG_UINT_31_MAX);
11725 PNGLong(chunk+10,(png_uint_32) image->iterations);
11727 if (logging != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11731 final_delay/MagickMax(image->ticks_per_second,1)));
11733 if (image->iterations == 0)
11734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11739 " Image iterations: %.20g",(double) image->iterations);
11741 (void) WriteBlob(image,14,chunk);
11742 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11745 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11747 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11748 mng_info->equal_srgbs)
11751 Write MNG sRGB chunk
11753 (void) WriteBlobMSBULong(image,1L);
11754 PNGType(chunk,mng_sRGB);
11755 LogPNGChunk(logging,mng_sRGB,1L);
11757 if (image->rendering_intent != UndefinedIntent)
11758 chunk[4]=(unsigned char)
11759 Magick_RenderingIntent_to_PNG_RenderingIntent(
11760 (image->rendering_intent));
11763 chunk[4]=(unsigned char)
11764 Magick_RenderingIntent_to_PNG_RenderingIntent(
11765 (PerceptualIntent));
11767 (void) WriteBlob(image,5,chunk);
11768 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11769 mng_info->have_write_global_srgb=MagickTrue;
11774 if (image->gamma && mng_info->equal_gammas)
11777 Write MNG gAMA chunk
11779 (void) WriteBlobMSBULong(image,4L);
11780 PNGType(chunk,mng_gAMA);
11781 LogPNGChunk(logging,mng_gAMA,4L);
11782 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11783 (void) WriteBlob(image,8,chunk);
11784 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11785 mng_info->have_write_global_gama=MagickTrue;
11787 if (mng_info->equal_chrms)
11793 Write MNG cHRM chunk
11795 (void) WriteBlobMSBULong(image,32L);
11796 PNGType(chunk,mng_cHRM);
11797 LogPNGChunk(logging,mng_cHRM,32L);
11798 primary=image->chromaticity.white_point;
11799 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11800 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11801 primary=image->chromaticity.red_primary;
11802 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11803 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11804 primary=image->chromaticity.green_primary;
11805 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11806 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11807 primary=image->chromaticity.blue_primary;
11808 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11809 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11810 (void) WriteBlob(image,36,chunk);
11811 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11812 mng_info->have_write_global_chrm=MagickTrue;
11815 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11818 Write MNG pHYs chunk
11820 (void) WriteBlobMSBULong(image,9L);
11821 PNGType(chunk,mng_pHYs);
11822 LogPNGChunk(logging,mng_pHYs,9L);
11824 if (image->units == PixelsPerInchResolution)
11826 PNGLong(chunk+4,(png_uint_32)
11827 (image->x_resolution*100.0/2.54+0.5));
11829 PNGLong(chunk+8,(png_uint_32)
11830 (image->y_resolution*100.0/2.54+0.5));
11837 if (image->units == PixelsPerCentimeterResolution)
11839 PNGLong(chunk+4,(png_uint_32)
11840 (image->x_resolution*100.0+0.5));
11842 PNGLong(chunk+8,(png_uint_32)
11843 (image->y_resolution*100.0+0.5));
11850 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11851 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11855 (void) WriteBlob(image,13,chunk);
11856 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11859 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11860 or does not cover the entire frame.
11862 if (write_mng && (image->matte || image->page.x > 0 ||
11863 image->page.y > 0 || (image->page.width &&
11864 (image->page.width+image->page.x < mng_info->page.width))
11865 || (image->page.height && (image->page.height+image->page.y
11866 < mng_info->page.height))))
11868 (void) WriteBlobMSBULong(image,6L);
11869 PNGType(chunk,mng_BACK);
11870 LogPNGChunk(logging,mng_BACK,6L);
11871 red=ScaleQuantumToShort(image->background_color.red);
11872 green=ScaleQuantumToShort(image->background_color.green);
11873 blue=ScaleQuantumToShort(image->background_color.blue);
11874 PNGShort(chunk+4,red);
11875 PNGShort(chunk+6,green);
11876 PNGShort(chunk+8,blue);
11877 (void) WriteBlob(image,10,chunk);
11878 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11879 if (mng_info->equal_backgrounds)
11881 (void) WriteBlobMSBULong(image,6L);
11882 PNGType(chunk,mng_bKGD);
11883 LogPNGChunk(logging,mng_bKGD,6L);
11884 (void) WriteBlob(image,10,chunk);
11885 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11889 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11890 if ((need_local_plte == MagickFalse) &&
11891 (image->storage_class == PseudoClass) &&
11892 (all_images_are_gray == MagickFalse))
11898 Write MNG PLTE chunk
11900 data_length=3*image->colors;
11901 (void) WriteBlobMSBULong(image,data_length);
11902 PNGType(chunk,mng_PLTE);
11903 LogPNGChunk(logging,mng_PLTE,data_length);
11905 for (i=0; i < (ssize_t) image->colors; i++)
11907 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11908 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11909 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11912 (void) WriteBlob(image,data_length+4,chunk);
11913 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11914 mng_info->have_write_global_plte=MagickTrue;
11920 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11921 defined(PNG_MNG_FEATURES_SUPPORTED)
11922 mng_info->equal_palettes=MagickFalse;
11926 if (mng_info->adjoin)
11928 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11929 defined(PNG_MNG_FEATURES_SUPPORTED)
11931 If we aren't using a global palette for the entire MNG, check to
11932 see if we can use one for two or more consecutive images.
11934 if (need_local_plte && use_global_plte && !all_images_are_gray)
11936 if (mng_info->IsPalette)
11939 When equal_palettes is true, this image has the same palette
11940 as the previous PseudoClass image
11942 mng_info->have_write_global_plte=mng_info->equal_palettes;
11943 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11944 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11947 Write MNG PLTE chunk
11952 data_length=3*image->colors;
11953 (void) WriteBlobMSBULong(image,data_length);
11954 PNGType(chunk,mng_PLTE);
11955 LogPNGChunk(logging,mng_PLTE,data_length);
11957 for (i=0; i < (ssize_t) image->colors; i++)
11959 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11960 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11961 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11964 (void) WriteBlob(image,data_length+4,chunk);
11965 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11966 (uInt) (data_length+4)));
11967 mng_info->have_write_global_plte=MagickTrue;
11971 mng_info->have_write_global_plte=MagickFalse;
11982 previous_x=mng_info->page.x;
11983 previous_y=mng_info->page.y;
11990 mng_info->page=image->page;
11991 if ((mng_info->page.x != previous_x) ||
11992 (mng_info->page.y != previous_y))
11994 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11995 PNGType(chunk,mng_DEFI);
11996 LogPNGChunk(logging,mng_DEFI,12L);
11997 chunk[4]=0; /* object 0 MSB */
11998 chunk[5]=0; /* object 0 LSB */
11999 chunk[6]=0; /* visible */
12000 chunk[7]=0; /* abstract */
12001 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12002 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12003 (void) WriteBlob(image,16,chunk);
12004 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12009 mng_info->write_mng=write_mng;
12011 if ((int) image->dispose >= 3)
12012 mng_info->framing_mode=3;
12014 if (mng_info->need_fram && mng_info->adjoin &&
12015 ((image->delay != mng_info->delay) ||
12016 (mng_info->framing_mode != mng_info->old_framing_mode)))
12018 if (image->delay == mng_info->delay)
12021 Write a MNG FRAM chunk with the new framing mode.
12023 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12024 PNGType(chunk,mng_FRAM);
12025 LogPNGChunk(logging,mng_FRAM,1L);
12026 chunk[4]=(unsigned char) mng_info->framing_mode;
12027 (void) WriteBlob(image,5,chunk);
12028 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12033 Write a MNG FRAM chunk with the delay.
12035 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12036 PNGType(chunk,mng_FRAM);
12037 LogPNGChunk(logging,mng_FRAM,10L);
12038 chunk[4]=(unsigned char) mng_info->framing_mode;
12039 chunk[5]=0; /* frame name separator (no name) */
12040 chunk[6]=2; /* flag for changing default delay */
12041 chunk[7]=0; /* flag for changing frame timeout */
12042 chunk[8]=0; /* flag for changing frame clipping */
12043 chunk[9]=0; /* flag for changing frame sync_id */
12044 PNGLong(chunk+10,(png_uint_32)
12045 ((mng_info->ticks_per_second*
12046 image->delay)/MagickMax(image->ticks_per_second,1)));
12047 (void) WriteBlob(image,14,chunk);
12048 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12049 mng_info->delay=(png_uint_32) image->delay;
12051 mng_info->old_framing_mode=mng_info->framing_mode;
12054 #if defined(JNG_SUPPORTED)
12055 if (image_info->compression == JPEGCompression)
12060 if (logging != MagickFalse)
12061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12062 " Writing JNG object.");
12063 /* To do: specify the desired alpha compression method. */
12064 write_info=CloneImageInfo(image_info);
12065 write_info->compression=UndefinedCompression;
12066 status=WriteOneJNGImage(mng_info,write_info,image);
12067 write_info=DestroyImageInfo(write_info);
12072 if (logging != MagickFalse)
12073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12074 " Writing PNG object.");
12076 mng_info->need_blob = MagickFalse;
12077 mng_info->ping_preserve_colormap = MagickFalse;
12079 /* We don't want any ancillary chunks written */
12080 mng_info->ping_exclude_bKGD=MagickTrue;
12081 mng_info->ping_exclude_cHRM=MagickTrue;
12082 mng_info->ping_exclude_date=MagickTrue;
12083 mng_info->ping_exclude_EXIF=MagickTrue;
12084 mng_info->ping_exclude_gAMA=MagickTrue;
12085 mng_info->ping_exclude_iCCP=MagickTrue;
12086 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12087 mng_info->ping_exclude_oFFs=MagickTrue;
12088 mng_info->ping_exclude_pHYs=MagickTrue;
12089 mng_info->ping_exclude_sRGB=MagickTrue;
12090 mng_info->ping_exclude_tEXt=MagickTrue;
12091 mng_info->ping_exclude_tRNS=MagickTrue;
12092 mng_info->ping_exclude_vpAg=MagickTrue;
12093 mng_info->ping_exclude_zCCP=MagickTrue;
12094 mng_info->ping_exclude_zTXt=MagickTrue;
12096 status=WriteOnePNGImage(mng_info,image_info,image);
12099 if (status == MagickFalse)
12101 MngInfoFreeStruct(mng_info,&have_mng_structure);
12102 (void) CloseBlob(image);
12103 return(MagickFalse);
12105 (void) CatchImageException(image);
12106 if (GetNextImageInList(image) == (Image *) NULL)
12108 image=SyncNextImageInList(image);
12109 status=SetImageProgress(image,SaveImagesTag,scene++,
12110 GetImageListLength(image));
12112 if (status == MagickFalse)
12115 } while (mng_info->adjoin);
12119 while (GetPreviousImageInList(image) != (Image *) NULL)
12120 image=GetPreviousImageInList(image);
12122 Write the MEND chunk.
12124 (void) WriteBlobMSBULong(image,0x00000000L);
12125 PNGType(chunk,mng_MEND);
12126 LogPNGChunk(logging,mng_MEND,0L);
12127 (void) WriteBlob(image,4,chunk);
12128 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12131 Relinquish resources.
12133 (void) CloseBlob(image);
12134 MngInfoFreeStruct(mng_info,&have_mng_structure);
12136 if (logging != MagickFalse)
12137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12139 return(MagickTrue);
12141 #else /* PNG_LIBPNG_VER > 10011 */
12143 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12146 printf("Your PNG library is too old: You have libpng-%s\n",
12147 PNG_LIBPNG_VER_STRING);
12149 ThrowBinaryException(CoderError,"PNG library is too old",
12150 image_info->filename);
12153 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12155 return(WritePNGImage(image_info,image));
12157 #endif /* PNG_LIBPNG_VER > 10011 */