2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "magick/studio.h"
45 #include "magick/artifact.h"
46 #include "magick/attribute.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/cache.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/colormap.h"
53 #include "magick/colorspace.h"
54 #include "magick/constitute.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/geometry.h"
59 #include "magick/histogram.h"
60 #include "magick/image.h"
61 #include "magick/image-private.h"
62 #include "magick/layer.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/magick.h"
66 #include "magick/memory_.h"
67 #include "magick/module.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/option.h"
71 #include "magick/quantum-private.h"
72 #include "magick/profile.h"
73 #include "magick/property.h"
74 #include "magick/resource_.h"
75 #include "magick/semaphore.h"
76 #include "magick/quantum-private.h"
77 #include "magick/static.h"
78 #include "magick/statistic.h"
79 #include "magick/string_.h"
80 #include "magick/string-private.h"
81 #include "magick/transform.h"
82 #include "magick/utility.h"
83 #if defined(MAGICKCORE_PNG_DELEGATE)
85 /* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
87 * migration to libpng-1.5, remove these defines and then
88 * fix any code that generates warnings.
90 /* #define PNG_DEPRECATED Use of this function is deprecated */
91 /* #define PNG_USE_RESULT The result of this function must be checked */
92 /* #define PNG_NORETURN This function does not return */
93 /* #define PNG_ALLOCATED The result of the function is new memory */
94 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
96 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
97 #define PNG_PTR_NORETURN
102 /* ImageMagick differences */
103 #define first_scene scene
105 #if PNG_LIBPNG_VER > 10011
107 Optional declarations. Define or undefine them as you like.
109 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
112 Features under construction. Define these to work on them.
114 #undef MNG_OBJECT_BUFFERS
115 #undef MNG_BASI_SUPPORTED
116 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
118 #if defined(MAGICKCORE_JPEG_DELEGATE)
119 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
121 #if !defined(RGBColorMatchExact)
122 #define IsPNGColorEqual(color,target) \
123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
129 Establish thread safety.
130 setjmp/longjmp is claimed to be safe on these platforms:
131 setjmp/longjmp is alleged to be unsafe on these platforms:
133 #ifndef SETJMP_IS_THREAD_SAFE
134 #define PNG_SETJMP_NOT_THREAD_SAFE
137 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
139 *ping_semaphore = (SemaphoreInfo *) NULL;
143 This temporary until I set up malloc'ed object attributes array.
144 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
147 #define MNG_MAX_OBJECTS 256
150 If this not defined, spec is interpreted strictly. If it is
151 defined, an attempt will be made to recover from some errors,
153 o global PLTE too short
158 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
159 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
160 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
161 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
162 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
163 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
164 will be enabled by default in libpng-1.2.0.
166 #ifdef PNG_MNG_FEATURES_SUPPORTED
167 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
168 # define PNG_READ_EMPTY_PLTE_SUPPORTED
170 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
171 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
176 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
177 This macro is only defined in libpng-1.0.3 and later.
178 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
180 #ifndef PNG_UINT_31_MAX
181 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
185 Constant strings for known chunk types. If you need to add a chunk,
186 add a string holding the name here. To make the code more
187 portable, we use ASCII numbers like this, not characters.
190 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
191 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
192 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
193 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
194 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
195 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
196 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
197 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
198 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
199 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
200 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
201 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
202 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
203 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
204 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
205 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
206 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
207 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
208 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
209 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
210 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
211 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
212 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
213 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
214 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
215 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
216 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
217 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
218 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
219 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
220 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
221 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
222 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
223 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
225 #if defined(JNG_SUPPORTED)
226 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
227 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
228 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
229 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
230 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
231 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
235 Other known chunks that are not yet supported by ImageMagick:
236 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
237 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
238 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
239 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
240 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
241 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
242 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
243 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
246 typedef struct _MngBox
255 typedef struct _MngPair
262 #ifdef MNG_OBJECT_BUFFERS
263 typedef struct _MngBuffer
295 typedef struct _MngInfo
298 #ifdef MNG_OBJECT_BUFFERS
300 *ob[MNG_MAX_OBJECTS];
311 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
312 bytes_in_read_buffer,
318 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
319 defined(PNG_MNG_FEATURES_SUPPORTED)
331 have_saved_bkgd_index,
332 have_write_global_chrm,
333 have_write_global_gama,
334 have_write_global_plte,
335 have_write_global_srgb,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
356 object_clip[MNG_MAX_OBJECTS];
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
376 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
405 global_phys_unit_type,
424 #ifdef MNG_BASI_SUPPORTED
432 basi_compression_method,
434 basi_interlace_method,
457 /* Added at version 6.6.6-7 */
465 /* ping_exclude_iTXt, */
472 ping_exclude_zCCP, /* hex-encoded iCCP */
474 ping_preserve_colormap;
480 Forward declarations.
482 static MagickBooleanType
483 WritePNGImage(const ImageInfo *,Image *);
485 static MagickBooleanType
486 WriteMNGImage(const ImageInfo *,Image *);
488 #if defined(JNG_SUPPORTED)
489 static MagickBooleanType
490 WriteJNGImage(const ImageInfo *,Image *);
493 #if PNG_LIBPNG_VER > 10011
496 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
497 static MagickBooleanType
498 LosslessReduceDepthOK(Image *image)
500 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
502 * This is true if the high byte and the next highest byte of
503 * each sample of the image, the colormap, and the background color
504 * are equal to each other. We check this by seeing if the samples
505 * are unchanged when we scale them down to 8 and back up to Quantum.
507 * We don't use the method GetImageDepth() because it doesn't check
508 * background and doesn't handle PseudoClass specially.
511 #define QuantumToCharToQuantumEqQuantum(quantum) \
512 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
515 ok_to_reduce=MagickFalse;
517 if (image->depth >= 16)
524 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
525 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
526 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
527 MagickTrue : MagickFalse;
529 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
533 for (indx=0; indx < (ssize_t) image->colors; indx++)
536 QuantumToCharToQuantumEqQuantum(
537 image->colormap[indx].red) &&
538 QuantumToCharToQuantumEqQuantum(
539 image->colormap[indx].green) &&
540 QuantumToCharToQuantumEqQuantum(
541 image->colormap[indx].blue)) ?
542 MagickTrue : MagickFalse;
544 if (ok_to_reduce == MagickFalse)
549 if ((ok_to_reduce != MagickFalse) &&
550 (image->storage_class != PseudoClass))
558 for (y=0; y < (ssize_t) image->rows; y++)
560 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
562 if (p == (const PixelPacket *) NULL)
564 ok_to_reduce = MagickFalse;
568 for (x=(ssize_t) image->columns-1; x >= 0; x--)
571 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
572 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
573 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
574 MagickTrue : MagickFalse;
576 if (ok_to_reduce == MagickFalse)
586 if (ok_to_reduce != MagickFalse)
588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
589 " OK to reduce PNG bit depth to 8 without loss of info");
593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
594 " Not OK to reduce PNG bit depth to 8 without loss of info");
600 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
603 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
607 case PerceptualIntent:
613 case SaturationIntent:
624 static RenderingIntent
625 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
630 return PerceptualIntent;
633 return RelativeIntent;
636 return SaturationIntent;
639 return AbsoluteIntent;
642 return UndefinedIntent;
646 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
654 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 % I m a g e I s G r a y %
672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
674 % Like IsGrayImage except does not change DirectClass to PseudoClass %
676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 static MagickBooleanType ImageIsGray(Image *image)
680 register const PixelPacket
688 assert(image != (Image *) NULL);
689 assert(image->signature == MagickSignature);
690 if (image->debug != MagickFalse)
691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
693 if (image->storage_class == PseudoClass)
695 for (i=0; i < (ssize_t) image->colors; i++)
696 if (IsGray(image->colormap+i) == MagickFalse)
700 for (y=0; y < (ssize_t) image->rows; y++)
702 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
703 if (p == (const PixelPacket *) NULL)
705 for (x=(ssize_t) image->columns-1; x >= 0; x--)
707 if (IsGray(p) == MagickFalse)
714 #endif /* PNG_LIBPNG_VER > 10011 */
715 #endif /* MAGICKCORE_PNG_DELEGATE */
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 % IsMNG() returns MagickTrue if the image format type, identified by the
729 % magick string, is MNG.
731 % The format of the IsMNG method is:
733 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
735 % A description of each parameter follows:
737 % o magick: compare image format pattern against these bytes.
739 % o length: Specifies the length of the magick string.
743 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
748 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765 % IsJNG() returns MagickTrue if the image format type, identified by the
766 % magick string, is JNG.
768 % The format of the IsJNG method is:
770 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
772 % A description of each parameter follows:
774 % o magick: compare image format pattern against these bytes.
776 % o length: Specifies the length of the magick string.
780 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
785 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802 % IsPNG() returns MagickTrue if the image format type, identified by the
803 % magick string, is PNG.
805 % The format of the IsPNG method is:
807 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
809 % A description of each parameter follows:
811 % o magick: compare image format pattern against these bytes.
813 % o length: Specifies the length of the magick string.
816 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
821 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
827 #if defined(MAGICKCORE_PNG_DELEGATE)
828 #if defined(__cplusplus) || defined(c_plusplus)
832 #if (PNG_LIBPNG_VER > 10011)
833 static size_t WriteBlobMSBULong(Image *image,const size_t value)
838 assert(image != (Image *) NULL);
839 assert(image->signature == MagickSignature);
840 buffer[0]=(unsigned char) (value >> 24);
841 buffer[1]=(unsigned char) (value >> 16);
842 buffer[2]=(unsigned char) (value >> 8);
843 buffer[3]=(unsigned char) value;
844 return((size_t) WriteBlob(image,4,buffer));
847 static void PNGLong(png_bytep p,png_uint_32 value)
849 *p++=(png_byte) ((value >> 24) & 0xff);
850 *p++=(png_byte) ((value >> 16) & 0xff);
851 *p++=(png_byte) ((value >> 8) & 0xff);
852 *p++=(png_byte) (value & 0xff);
855 #if defined(JNG_SUPPORTED)
856 static void PNGsLong(png_bytep p,png_int_32 value)
858 *p++=(png_byte) ((value >> 24) & 0xff);
859 *p++=(png_byte) ((value >> 16) & 0xff);
860 *p++=(png_byte) ((value >> 8) & 0xff);
861 *p++=(png_byte) (value & 0xff);
865 static void PNGShort(png_bytep p,png_uint_16 value)
867 *p++=(png_byte) ((value >> 8) & 0xff);
868 *p++=(png_byte) (value & 0xff);
871 static void PNGType(png_bytep p,png_bytep type)
873 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
876 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
879 if (logging != MagickFalse)
880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
881 " Writing %c%c%c%c chunk, length: %.20g",
882 type[0],type[1],type[2],type[3],(double) length);
884 #endif /* PNG_LIBPNG_VER > 10011 */
886 #if defined(__cplusplus) || defined(c_plusplus)
890 #if PNG_LIBPNG_VER > 10011
892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
896 % R e a d P N G I m a g e %
900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
903 % Multiple-image Network Graphics (MNG) image file and returns it. It
904 % allocates the memory necessary for the new Image structure and returns a
905 % pointer to the new image or set of images.
907 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
909 % The format of the ReadPNGImage method is:
911 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
913 % A description of each parameter follows:
915 % o image_info: the image info.
917 % o exception: return any errors or warnings in this structure.
919 % To do, more or less in chronological order (as of version 5.5.2,
920 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
922 % Get 16-bit cheap transparency working.
924 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
926 % Preserve all unknown and not-yet-handled known chunks found in input
927 % PNG file and copy them into output PNG files according to the PNG
930 % (At this point, PNG encoding should be in full MNG compliance)
932 % Provide options for choice of background to use when the MNG BACK
933 % chunk is not present or is not mandatory (i.e., leave transparent,
934 % user specified, MNG BACK, PNG bKGD)
936 % Implement LOOP/ENDL [done, but could do discretionary loops more
937 % efficiently by linking in the duplicate frames.].
939 % Decode and act on the MHDR simplicity profile (offer option to reject
940 % files or attempt to process them anyway when the profile isn't LC or VLC).
942 % Upgrade to full MNG without Delta-PNG.
944 % o BACK [done a while ago except for background image ID]
945 % o MOVE [done 15 May 1999]
946 % o CLIP [done 15 May 1999]
947 % o DISC [done 19 May 1999]
948 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
949 % o SEEK [partially done 19 May 1999 (discard function only)]
953 % o MNG-level tEXt/iTXt/zTXt
958 % o iTXt (wait for libpng implementation).
960 % Use the scene signature to discover when an identical scene is
961 % being reused, and just point to the original image->exception instead
962 % of storing another set of pixels. This not specific to MNG
963 % but could be applied generally.
965 % Upgrade to full MNG with Delta-PNG.
969 % We will not attempt to read files containing the CgBI chunk.
970 % They are really Xcode files meant for display on the iPhone.
971 % These are not valid PNG files and it is impossible to recover
972 % the orginal PNG from files that have been converted to Xcode-PNG,
973 % since irretrievable loss of color data has occurred due to the
974 % use of premultiplied alpha.
977 #if defined(__cplusplus) || defined(c_plusplus)
982 This the function that does the actual reading of data. It is
983 the same as the one supplied in libpng, except that it receives the
984 datastream from the ReadBlob() function instead of standard input.
986 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
991 image=(Image *) png_get_io_ptr(png_ptr);
997 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1003 (void) FormatMagickString(msg,MaxTextExtent,
1004 "Expected %.20g bytes; found %.20g bytes",(double) length,
1006 png_warning(png_ptr,msg);
1007 png_error(png_ptr,"Read Exception");
1012 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1013 !defined(PNG_MNG_FEATURES_SUPPORTED)
1014 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1015 * older than libpng-1.0.3a, which was the first to allow the empty
1016 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1017 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1018 * encountered after an empty PLTE, so we have to look ahead for bKGD
1019 * chunks and remove them from the datastream that is passed to libpng,
1020 * and store their contents for later use.
1022 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1037 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1038 image=(Image *) mng_info->image;
1039 while (mng_info->bytes_in_read_buffer && length)
1041 data[i]=mng_info->read_buffer[i];
1042 mng_info->bytes_in_read_buffer--;
1048 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1050 if (check != length)
1051 png_error(png_ptr,"Read Exception");
1055 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1058 check=(png_size_t) ReadBlob(image,(size_t) length,
1059 (char *) mng_info->read_buffer);
1060 mng_info->read_buffer[4]=0;
1061 mng_info->bytes_in_read_buffer=4;
1062 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1063 mng_info->found_empty_plte=MagickTrue;
1064 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1066 mng_info->found_empty_plte=MagickFalse;
1067 mng_info->have_saved_bkgd_index=MagickFalse;
1071 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1074 check=(png_size_t) ReadBlob(image,(size_t) length,
1075 (char *) mng_info->read_buffer);
1076 mng_info->read_buffer[4]=0;
1077 mng_info->bytes_in_read_buffer=4;
1078 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1079 if (mng_info->found_empty_plte)
1082 Skip the bKGD data byte and CRC.
1085 ReadBlob(image,5,(char *) mng_info->read_buffer);
1086 check=(png_size_t) ReadBlob(image,(size_t) length,
1087 (char *) mng_info->read_buffer);
1088 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1089 mng_info->have_saved_bkgd_index=MagickTrue;
1090 mng_info->bytes_in_read_buffer=0;
1098 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1103 image=(Image *) png_get_io_ptr(png_ptr);
1109 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1111 if (check != length)
1112 png_error(png_ptr,"WriteBlob Failed");
1116 static void png_flush_data(png_structp png_ptr)
1121 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1122 static int PalettesAreEqual(Image *a,Image *b)
1127 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1128 return((int) MagickFalse);
1130 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1131 return((int) MagickFalse);
1133 if (a->colors != b->colors)
1134 return((int) MagickFalse);
1136 for (i=0; i < (ssize_t) a->colors; i++)
1138 if ((a->colormap[i].red != b->colormap[i].red) ||
1139 (a->colormap[i].green != b->colormap[i].green) ||
1140 (a->colormap[i].blue != b->colormap[i].blue))
1141 return((int) MagickFalse);
1144 return((int) MagickTrue);
1148 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1150 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1151 mng_info->exists[i] && !mng_info->frozen[i])
1153 #ifdef MNG_OBJECT_BUFFERS
1154 if (mng_info->ob[i] != (MngBuffer *) NULL)
1156 if (mng_info->ob[i]->reference_count > 0)
1157 mng_info->ob[i]->reference_count--;
1159 if (mng_info->ob[i]->reference_count == 0)
1161 if (mng_info->ob[i]->image != (Image *) NULL)
1162 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1164 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1167 mng_info->ob[i]=(MngBuffer *) NULL;
1169 mng_info->exists[i]=MagickFalse;
1170 mng_info->invisible[i]=MagickFalse;
1171 mng_info->viewable[i]=MagickFalse;
1172 mng_info->frozen[i]=MagickFalse;
1173 mng_info->x_off[i]=0;
1174 mng_info->y_off[i]=0;
1175 mng_info->object_clip[i].left=0;
1176 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1177 mng_info->object_clip[i].top=0;
1178 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1182 static void MngInfoFreeStruct(MngInfo *mng_info,
1183 MagickBooleanType *have_mng_structure)
1185 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1190 for (i=1; i < MNG_MAX_OBJECTS; i++)
1191 MngInfoDiscardObject(mng_info,i);
1193 if (mng_info->global_plte != (png_colorp) NULL)
1194 mng_info->global_plte=(png_colorp)
1195 RelinquishMagickMemory(mng_info->global_plte);
1197 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1198 *have_mng_structure=MagickFalse;
1202 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1208 if (box.left < box2.left)
1211 if (box.top < box2.top)
1214 if (box.right > box2.right)
1215 box.right=box2.right;
1217 if (box.bottom > box2.bottom)
1218 box.bottom=box2.bottom;
1223 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1229 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1231 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1232 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1233 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1234 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1235 if (delta_type != 0)
1237 box.left+=previous_box.left;
1238 box.right+=previous_box.right;
1239 box.top+=previous_box.top;
1240 box.bottom+=previous_box.bottom;
1246 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1252 Read two ssize_ts from CLON, MOVE or PAST chunk
1254 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1255 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1257 if (delta_type != 0)
1259 pair.a+=previous_pair.a;
1260 pair.b+=previous_pair.b;
1266 static long mng_get_long(unsigned char *p)
1268 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1271 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1276 image=(Image *) png_get_error_ptr(ping);
1278 if (image->debug != MagickFalse)
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1282 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1283 message,"`%s'",image->filename);
1285 #if (PNG_LIBPNG_VER < 10500)
1286 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1287 * are building with libpng-1.4.x and can be ignored.
1289 longjmp(ping->jmpbuf,1);
1291 png_longjmp(ping,1);
1295 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1300 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1301 png_error(ping, message);
1303 image=(Image *) png_get_error_ptr(ping);
1304 if (image->debug != MagickFalse)
1305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1306 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1308 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1309 message,"`%s'",image->filename);
1312 #ifdef PNG_USER_MEM_SUPPORTED
1313 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1315 #if (PNG_LIBPNG_VER < 10011)
1320 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1323 png_error("Insufficient memory.");
1328 return((png_voidp) AcquireMagickMemory((size_t) size));
1333 Free a pointer. It is removed from the list at the same time.
1335 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1338 ptr=RelinquishMagickMemory(ptr);
1339 return((png_free_ptr) NULL);
1343 #if defined(__cplusplus) || defined(c_plusplus)
1348 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1349 png_textp text,int ii)
1354 register unsigned char
1368 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1371 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1372 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1376 /* look for newline */
1380 /* look for length */
1381 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1384 length=(png_uint_32) StringToLong(sp);
1386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1387 " length: %lu",(unsigned long) length);
1389 while (*sp != ' ' && *sp != '\n')
1392 /* allocate space */
1395 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1396 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1397 return(MagickFalse);
1400 profile=AcquireStringInfo(length);
1402 if (profile == (StringInfo *) NULL)
1404 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1405 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1406 "unable to copy profile");
1407 return(MagickFalse);
1410 /* copy profile, skipping white space and column 1 "=" signs */
1411 dp=GetStringInfoDatum(profile);
1414 for (i=0; i < (ssize_t) nibbles; i++)
1416 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1421 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1422 profile=DestroyStringInfo(profile);
1423 return(MagickFalse);
1429 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1432 (*dp++)+=unhex[(int) *sp++];
1435 We have already read "Raw profile type.
1437 (void) SetImageProfile(image,&text[ii].key[17],profile);
1438 profile=DestroyStringInfo(profile);
1440 if (image_info->verbose)
1441 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1446 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1447 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1453 /* The unknown chunk structure contains the chunk data:
1458 Note that libpng has already taken care of the CRC handling.
1462 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1463 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1464 return(0); /* Did not recognize */
1466 /* recognized vpAg */
1468 if (chunk->size != 9)
1469 return(-1); /* Error return */
1471 if (chunk->data[8] != 0)
1472 return(0); /* ImageMagick requires pixel units */
1474 image=(Image *) png_get_user_chunk_ptr(ping);
1476 image->page.width=(size_t) ((chunk->data[0] << 24) |
1477 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1479 image->page.height=(size_t) ((chunk->data[4] << 24) |
1480 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1482 /* Return one of the following: */
1483 /* return(-n); chunk had an error */
1484 /* return(0); did not recognize */
1485 /* return(n); success */
1493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1497 % R e a d O n e P N G I m a g e %
1501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1503 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1504 % (minus the 8-byte signature) and returns it. It allocates the memory
1505 % necessary for the new Image structure and returns a pointer to the new
1508 % The format of the ReadOnePNGImage method is:
1510 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1511 % ExceptionInfo *exception)
1513 % A description of each parameter follows:
1515 % o mng_info: Specifies a pointer to a MngInfo structure.
1517 % o image_info: the image info.
1519 % o exception: return any errors or warnings in this structure.
1522 static Image *ReadOnePNGImage(MngInfo *mng_info,
1523 const ImageInfo *image_info, ExceptionInfo *exception)
1525 /* Read one PNG image */
1527 /* To do: Read the tIME chunk into the date:modify property */
1528 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1542 ping_interlace_method,
1543 ping_compression_method,
1591 register unsigned char
1594 register IndexPacket
1601 register PixelPacket
1611 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1612 png_byte unused_chunks[]=
1614 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1615 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1616 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1617 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1618 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1619 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1623 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1624 " Enter ReadOnePNGImage()");
1626 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1627 LockSemaphoreInfo(ping_semaphore);
1630 #if (PNG_LIBPNG_VER < 10200)
1631 if (image_info->verbose)
1632 printf("Your PNG library (libpng-%s) is rather old.\n",
1633 PNG_LIBPNG_VER_STRING);
1636 #if (PNG_LIBPNG_VER >= 10400)
1637 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1638 if (image_info->verbose)
1640 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1641 PNG_LIBPNG_VER_STRING);
1642 printf("Please update it.\n");
1648 quantum_info = (QuantumInfo *) NULL;
1649 image=mng_info->image;
1651 if (logging != MagickFalse)
1652 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1653 " image->matte=%d",(int) image->matte);
1655 /* Set to an out-of-range color unless tRNS chunk is present */
1656 transparent_color.red=65537;
1657 transparent_color.green=65537;
1658 transparent_color.blue=65537;
1659 transparent_color.opacity=65537;
1663 num_raw_profiles = 0;
1666 Allocate the PNG structures
1668 #ifdef PNG_USER_MEM_SUPPORTED
1669 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1670 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1671 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1673 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1674 MagickPNGErrorHandler,MagickPNGWarningHandler);
1676 if (ping == (png_struct *) NULL)
1677 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1679 ping_info=png_create_info_struct(ping);
1681 if (ping_info == (png_info *) NULL)
1683 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1684 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1687 end_info=png_create_info_struct(ping);
1689 if (end_info == (png_info *) NULL)
1691 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1695 ping_pixels=(unsigned char *) NULL;
1697 if (setjmp(png_jmpbuf(ping)))
1700 PNG image is corrupt.
1702 png_destroy_read_struct(&ping,&ping_info,&end_info);
1703 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1704 UnlockSemaphoreInfo(ping_semaphore);
1706 if (logging != MagickFalse)
1707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1708 " exit ReadOnePNGImage() with error.");
1710 if (image != (Image *) NULL)
1712 InheritException(exception,&image->exception);
1716 return(GetFirstImageInList(image));
1719 Prepare PNG for reading.
1722 mng_info->image_found++;
1723 png_set_sig_bytes(ping,8);
1725 if (LocaleCompare(image_info->magick,"MNG") == 0)
1727 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1728 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1729 png_set_read_fn(ping,image,png_get_data);
1731 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1732 png_permit_empty_plte(ping,MagickTrue);
1733 png_set_read_fn(ping,image,png_get_data);
1735 mng_info->image=image;
1736 mng_info->bytes_in_read_buffer=0;
1737 mng_info->found_empty_plte=MagickFalse;
1738 mng_info->have_saved_bkgd_index=MagickFalse;
1739 png_set_read_fn(ping,mng_info,mng_get_data);
1745 png_set_read_fn(ping,image,png_get_data);
1747 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1748 /* Ignore unused chunks and all unknown chunks except for vpAg */
1749 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1750 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1751 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1752 (int)sizeof(unused_chunks)/5);
1753 /* Callback for other unknown chunks */
1754 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1757 #if (PNG_LIBPNG_VER < 10400)
1758 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1759 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1760 /* Disable thread-unsafe features of pnggccrd */
1761 if (png_access_version_number() >= 10200)
1763 png_uint_32 mmx_disable_mask=0;
1764 png_uint_32 asm_flags;
1766 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1767 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1768 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1769 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1770 asm_flags=png_get_asm_flags(ping);
1771 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1776 png_read_info(ping,ping_info);
1778 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1779 &ping_bit_depth,&ping_color_type,
1780 &ping_interlace_method,&ping_compression_method,
1781 &ping_filter_method);
1783 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1786 (void) png_get_bKGD(ping, ping_info, &ping_background);
1788 if (ping_bit_depth < 8)
1790 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1792 png_set_packing(ping);
1797 image->depth=ping_bit_depth;
1798 image->depth=GetImageQuantumDepth(image,MagickFalse);
1799 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1800 if (logging != MagickFalse)
1802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1803 " PNG width: %.20g, height: %.20g",
1804 (double) ping_width, (double) ping_height);
1806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1807 " PNG color_type: %d, bit_depth: %d",
1808 ping_color_type, ping_bit_depth);
1810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1811 " PNG compression_method: %d",
1812 ping_compression_method);
1814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " PNG interlace_method: %d, filter_method: %d",
1816 ping_interlace_method,ping_filter_method);
1819 #ifdef PNG_READ_iCCP_SUPPORTED
1820 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1825 #if (PNG_LIBPNG_VER < 10500)
1839 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1842 if (profile_length != 0)
1847 if (logging != MagickFalse)
1848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1849 " Reading PNG iCCP chunk.");
1850 profile=AcquireStringInfo(profile_length);
1851 SetStringInfoDatum(profile,(const unsigned char *) info);
1852 (void) SetImageProfile(image,"icc",profile);
1853 profile=DestroyStringInfo(profile);
1857 #if defined(PNG_READ_sRGB_SUPPORTED)
1859 if (mng_info->have_global_srgb)
1860 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1861 (mng_info->global_srgb_intent);
1863 if (png_get_sRGB(ping,ping_info,&intent))
1865 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1868 if (logging != MagickFalse)
1869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1870 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1875 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1876 if (mng_info->have_global_gama)
1877 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1879 if (png_get_gAMA(ping,ping_info,&file_gamma))
1881 image->gamma=(float) file_gamma;
1882 if (logging != MagickFalse)
1883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1884 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1887 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1889 if (mng_info->have_global_chrm != MagickFalse)
1891 (void) png_set_cHRM(ping,ping_info,
1892 mng_info->global_chrm.white_point.x,
1893 mng_info->global_chrm.white_point.y,
1894 mng_info->global_chrm.red_primary.x,
1895 mng_info->global_chrm.red_primary.y,
1896 mng_info->global_chrm.green_primary.x,
1897 mng_info->global_chrm.green_primary.y,
1898 mng_info->global_chrm.blue_primary.x,
1899 mng_info->global_chrm.blue_primary.y);
1903 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1905 (void) png_get_cHRM(ping,ping_info,
1906 &image->chromaticity.white_point.x,
1907 &image->chromaticity.white_point.y,
1908 &image->chromaticity.red_primary.x,
1909 &image->chromaticity.red_primary.y,
1910 &image->chromaticity.green_primary.x,
1911 &image->chromaticity.green_primary.y,
1912 &image->chromaticity.blue_primary.x,
1913 &image->chromaticity.blue_primary.y);
1915 if (logging != MagickFalse)
1916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917 " Reading PNG cHRM chunk.");
1920 if (image->rendering_intent != UndefinedIntent)
1922 png_set_sRGB(ping,ping_info,
1923 Magick_RenderingIntent_to_PNG_RenderingIntent
1924 (image->rendering_intent));
1925 png_set_gAMA(ping,ping_info,0.45455f);
1926 png_set_cHRM(ping,ping_info,
1927 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1928 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1930 #if defined(PNG_oFFs_SUPPORTED)
1931 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1933 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1934 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1936 if (logging != MagickFalse)
1937 if (image->page.x || image->page.y)
1938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1939 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1940 image->page.x,(double) image->page.y);
1943 #if defined(PNG_pHYs_SUPPORTED)
1944 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1946 if (mng_info->have_global_phys)
1948 png_set_pHYs(ping,ping_info,
1949 mng_info->global_x_pixels_per_unit,
1950 mng_info->global_y_pixels_per_unit,
1951 mng_info->global_phys_unit_type);
1955 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1958 Set image resolution.
1960 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1962 image->x_resolution=(double) x_resolution;
1963 image->y_resolution=(double) y_resolution;
1965 if (unit_type == PNG_RESOLUTION_METER)
1967 image->units=PixelsPerCentimeterResolution;
1968 image->x_resolution=(double) x_resolution/100.0;
1969 image->y_resolution=(double) y_resolution/100.0;
1972 if (logging != MagickFalse)
1973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1974 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1975 (double) x_resolution,(double) y_resolution,unit_type);
1979 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1987 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1989 if ((number_colors == 0) &&
1990 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1992 if (mng_info->global_plte_length)
1994 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1995 (int) mng_info->global_plte_length);
1997 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
1998 if (mng_info->global_trns_length)
2000 if (mng_info->global_trns_length >
2001 mng_info->global_plte_length)
2002 (void) ThrowMagickException(&image->exception,
2003 GetMagickModule(),CoderError,
2004 "global tRNS has more entries than global PLTE",
2005 "`%s'",image_info->filename);
2006 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2007 (int) mng_info->global_trns_length,NULL);
2009 #ifdef PNG_READ_bKGD_SUPPORTED
2011 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012 mng_info->have_saved_bkgd_index ||
2014 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2019 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2020 if (mng_info->have_saved_bkgd_index)
2021 background.index=mng_info->saved_bkgd_index;
2023 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2024 background.index=ping_background->index;
2026 background.red=(png_uint_16)
2027 mng_info->global_plte[background.index].red;
2029 background.green=(png_uint_16)
2030 mng_info->global_plte[background.index].green;
2032 background.blue=(png_uint_16)
2033 mng_info->global_plte[background.index].blue;
2035 background.gray=(png_uint_16)
2036 mng_info->global_plte[background.index].green;
2038 png_set_bKGD(ping,ping_info,&background);
2043 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2044 CoderError,"No global PLTE in file","`%s'",
2045 image_info->filename);
2049 #ifdef PNG_READ_bKGD_SUPPORTED
2050 if (mng_info->have_global_bkgd &&
2051 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2052 image->background_color=mng_info->mng_global_bkgd;
2054 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2060 Set image background color.
2062 if (logging != MagickFalse)
2063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2064 " Reading PNG bKGD chunk.");
2066 /* Scale background components to 16-bit, then scale
2069 if (logging != MagickFalse)
2070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2071 " raw ping_background=(%d,%d,%d).",ping_background->red,
2072 ping_background->green,ping_background->blue);
2076 if (ping_bit_depth == 1)
2079 else if (ping_bit_depth == 2)
2082 else if (ping_bit_depth == 4)
2085 if (ping_bit_depth <= 8)
2088 ping_background->red *= bkgd_scale;
2089 ping_background->green *= bkgd_scale;
2090 ping_background->blue *= bkgd_scale;
2092 if (logging != MagickFalse)
2094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " bkgd_scale=%d.",bkgd_scale);
2097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2098 " ping_background=(%d,%d,%d).",ping_background->red,
2099 ping_background->green,ping_background->blue);
2102 image->background_color.red=
2103 ScaleShortToQuantum(ping_background->red);
2105 image->background_color.green=
2106 ScaleShortToQuantum(ping_background->green);
2108 image->background_color.blue=
2109 ScaleShortToQuantum(ping_background->blue);
2111 image->background_color.opacity=OpaqueOpacity;
2113 if (logging != MagickFalse)
2114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2115 " image->background_color=(%.20g,%.20g,%.20g).",
2116 (double) image->background_color.red,
2117 (double) image->background_color.green,
2118 (double) image->background_color.blue);
2120 #endif /* PNG_READ_bKGD_SUPPORTED */
2122 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2125 Image has a tRNS chunk.
2133 if (logging != MagickFalse)
2134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2135 " Reading PNG tRNS chunk.");
2137 max_sample = (int) ((one << ping_bit_depth) - 1);
2139 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2140 (int)ping_trans_color->gray > max_sample) ||
2141 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2142 ((int)ping_trans_color->red > max_sample ||
2143 (int)ping_trans_color->green > max_sample ||
2144 (int)ping_trans_color->blue > max_sample)))
2146 if (logging != MagickFalse)
2147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2148 " Ignoring PNG tRNS chunk with out-of-range sample.");
2149 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2150 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2151 image->matte=MagickFalse;
2158 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2160 /* Scale transparent_color to short */
2161 transparent_color.red= scale_to_short*ping_trans_color->red;
2162 transparent_color.green= scale_to_short*ping_trans_color->green;
2163 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2164 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2166 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2168 if (logging != MagickFalse)
2170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2174 " scaled graylevel is %d.",transparent_color.opacity);
2176 transparent_color.red=transparent_color.opacity;
2177 transparent_color.green=transparent_color.opacity;
2178 transparent_color.blue=transparent_color.opacity;
2182 #if defined(PNG_READ_sBIT_SUPPORTED)
2183 if (mng_info->have_global_sbit)
2185 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2186 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2189 num_passes=png_set_interlace_handling(ping);
2191 png_read_update_info(ping,ping_info);
2193 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2196 Initialize image structure.
2198 mng_info->image_box.left=0;
2199 mng_info->image_box.right=(ssize_t) ping_width;
2200 mng_info->image_box.top=0;
2201 mng_info->image_box.bottom=(ssize_t) ping_height;
2202 if (mng_info->mng_type == 0)
2204 mng_info->mng_width=ping_width;
2205 mng_info->mng_height=ping_height;
2206 mng_info->frame=mng_info->image_box;
2207 mng_info->clip=mng_info->image_box;
2212 image->page.y=mng_info->y_off[mng_info->object_id];
2215 image->compression=ZipCompression;
2216 image->columns=ping_width;
2217 image->rows=ping_height;
2218 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2219 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2224 image->storage_class=PseudoClass;
2226 image->colors=one << ping_bit_depth;
2227 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2228 if (image->colors > 256)
2231 if (image->colors > 65536L)
2232 image->colors=65536L;
2234 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2242 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2243 image->colors=(size_t) number_colors;
2245 if (logging != MagickFalse)
2246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2247 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2251 if (image->storage_class == PseudoClass)
2254 Initialize image colormap.
2256 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2257 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2259 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2267 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2269 for (i=0; i < (ssize_t) number_colors; i++)
2271 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2272 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2273 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2276 for ( ; i < (ssize_t) image->colors; i++)
2278 image->colormap[i].red=0;
2279 image->colormap[i].green=0;
2280 image->colormap[i].blue=0;
2289 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2294 for (i=0; i < (ssize_t) image->colors; i++)
2296 image->colormap[i].red=(Quantum) (i*scale);
2297 image->colormap[i].green=(Quantum) (i*scale);
2298 image->colormap[i].blue=(Quantum) (i*scale);
2303 /* Set some properties for reporting by "identify" */
2308 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2309 ping_interlace_method in value */
2311 (void) FormatMagickString(msg,MaxTextExtent,
2312 "%d, %d",(int) ping_width, (int) ping_height);
2313 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
2315 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2316 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2318 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2319 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2321 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2322 (int) ping_interlace_method);
2323 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
2327 Read image scanlines.
2329 if (image->delay != 0)
2330 mng_info->scenes_found++;
2332 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2333 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2334 (image_info->first_scene+image_info->number_scenes))))
2336 if (logging != MagickFalse)
2337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2338 " Skipping PNG image data for scene %.20g",(double)
2339 mng_info->scenes_found-1);
2340 png_destroy_read_struct(&ping,&ping_info,&end_info);
2341 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2342 UnlockSemaphoreInfo(ping_semaphore);
2344 if (logging != MagickFalse)
2345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2346 " exit ReadOnePNGImage().");
2351 if (logging != MagickFalse)
2352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2353 " Reading PNG IDAT chunk(s)");
2356 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2357 ping_rowbytes*sizeof(*ping_pixels));
2360 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2361 sizeof(*ping_pixels));
2363 if (ping_pixels == (unsigned char *) NULL)
2364 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Converting PNG pixels to pixel packets");
2370 Convert PNG pixels to pixel packets.
2372 if (setjmp(png_jmpbuf(ping)))
2375 PNG image is corrupt.
2377 png_destroy_read_struct(&ping,&ping_info,&end_info);
2378 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2379 UnlockSemaphoreInfo(ping_semaphore);
2381 if (quantum_info != (QuantumInfo *) NULL)
2382 quantum_info = DestroyQuantumInfo(quantum_info);
2384 if (ping_pixels != (unsigned char *) NULL)
2385 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2387 if (logging != MagickFalse)
2388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2389 " exit ReadOnePNGImage() with error.");
2391 if (image != (Image *) NULL)
2393 InheritException(exception,&image->exception);
2397 return(GetFirstImageInList(image));
2400 quantum_info=AcquireQuantumInfo(image_info,image);
2402 if (quantum_info == (QuantumInfo *) NULL)
2403 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2408 found_transparent_pixel;
2410 found_transparent_pixel=MagickFalse;
2412 if (image->storage_class == DirectClass)
2414 for (pass=0; pass < num_passes; pass++)
2417 Convert image to DirectClass pixel packets.
2419 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2423 depth=(ssize_t) ping_bit_depth;
2425 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2426 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2427 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2428 MagickTrue : MagickFalse;
2430 for (y=0; y < (ssize_t) image->rows; y++)
2433 row_offset=ping_rowbytes*y;
2438 png_read_row(ping,ping_pixels+row_offset,NULL);
2439 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2441 if (q == (PixelPacket *) NULL)
2444 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2445 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2446 GrayQuantum,ping_pixels+row_offset,exception);
2448 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2449 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2450 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2452 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2453 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2454 RGBAQuantum,ping_pixels+row_offset,exception);
2456 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2457 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2458 IndexQuantum,ping_pixels+row_offset,exception);
2460 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2461 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2462 RGBQuantum,ping_pixels+row_offset,exception);
2464 if (found_transparent_pixel == MagickFalse)
2466 /* Is there a transparent pixel in the row? */
2467 if (y== 0 && logging != MagickFalse)
2468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2469 " Looking for cheap transparent pixel");
2471 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2473 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2474 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2475 (GetOpacityPixelComponent(q) != OpaqueOpacity))
2477 if (logging != MagickFalse)
2478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2481 found_transparent_pixel = MagickTrue;
2484 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2485 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2486 (ScaleQuantumToShort(GetRedPixelComponent(q))
2487 == transparent_color.red &&
2488 ScaleQuantumToShort(GetGreenPixelComponent(q))
2489 == transparent_color.green &&
2490 ScaleQuantumToShort(GetBluePixelComponent(q))
2491 == transparent_color.blue))
2493 if (logging != MagickFalse)
2494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 found_transparent_pixel = MagickTrue;
2503 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2505 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2508 if (status == MagickFalse)
2511 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2515 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2517 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2518 if (status == MagickFalse)
2524 else /* image->storage_class != DirectClass */
2526 for (pass=0; pass < num_passes; pass++)
2535 Convert grayscale image to PseudoClass pixel packets.
2537 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2538 MagickTrue : MagickFalse;
2540 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2541 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2543 if (quantum_scanline == (Quantum *) NULL)
2544 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2546 for (y=0; y < (ssize_t) image->rows; y++)
2549 row_offset=ping_rowbytes*y;
2554 png_read_row(ping,ping_pixels+row_offset,NULL);
2555 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2557 if (q == (PixelPacket *) NULL)
2560 indexes=GetAuthenticIndexQueue(image);
2561 p=ping_pixels+row_offset;
2564 switch (ping_bit_depth)
2571 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2573 for (bit=7; bit >= 0; bit--)
2574 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2578 if ((image->columns % 8) != 0)
2580 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2581 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2589 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2591 *r++=(*p >> 6) & 0x03;
2592 *r++=(*p >> 4) & 0x03;
2593 *r++=(*p >> 2) & 0x03;
2597 if ((image->columns % 4) != 0)
2599 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2600 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2608 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2610 *r++=(*p >> 4) & 0x0f;
2614 if ((image->columns % 2) != 0)
2615 *r++=(*p++ >> 4) & 0x0f;
2622 if (ping_color_type == 4)
2623 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2626 /* In image.h, OpaqueOpacity is 0
2627 * TransparentOpacity is QuantumRange
2628 * In a PNG datastream, Opaque is QuantumRange
2629 * and Transparent is 0.
2631 SetOpacityPixelComponent(q,
2632 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2633 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
2634 found_transparent_pixel = MagickTrue;
2639 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2647 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2649 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2653 if (image->colors > 256)
2661 *r=(Quantum) quantum;
2664 if (ping_color_type == 4)
2666 quantum=((*p++) << 8);
2668 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2669 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
2670 found_transparent_pixel = MagickTrue;
2674 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2678 if (image->colors > 256)
2689 if (ping_color_type == 4)
2691 quantum=(*p << 8) | *(p+1);
2693 SetOpacityPixelComponent(q,
2694 (Quantum) GetAlphaPixelComponent(q));
2695 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
2696 found_transparent_pixel = MagickTrue;
2701 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2703 p++; /* strip low byte */
2705 if (ping_color_type == 4)
2707 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2708 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
2709 found_transparent_pixel = MagickTrue;
2725 Transfer image scanline.
2729 for (x=0; x < (ssize_t) image->columns; x++)
2730 indexes[x]=(IndexPacket) (*r++);
2732 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2735 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2737 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2740 if (status == MagickFalse)
2745 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2747 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2749 if (status == MagickFalse)
2753 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2756 image->matte=found_transparent_pixel;
2758 if (logging != MagickFalse)
2760 if (found_transparent_pixel != MagickFalse)
2761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2762 " Found transparent pixel");
2765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2766 " No transparent pixel was found");
2768 ping_color_type&=0x03;
2773 if (quantum_info != (QuantumInfo *) NULL)
2774 quantum_info=DestroyQuantumInfo(quantum_info);
2776 if (image->storage_class == PseudoClass)
2782 image->matte=MagickFalse;
2783 (void) SyncImage(image);
2787 png_read_end(ping,end_info);
2789 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2790 (ssize_t) image_info->first_scene && image->delay != 0)
2792 png_destroy_read_struct(&ping,&ping_info,&end_info);
2793 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2795 (void) SetImageBackgroundColor(image);
2796 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2797 UnlockSemaphoreInfo(ping_semaphore);
2799 if (logging != MagickFalse)
2800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2801 " exit ReadOnePNGImage() early.");
2805 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2811 Image has a transparent background.
2813 storage_class=image->storage_class;
2814 image->matte=MagickTrue;
2816 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2818 if (storage_class == PseudoClass)
2820 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2822 for (x=0; x < ping_num_trans; x++)
2824 image->colormap[x].opacity =
2825 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2829 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2831 for (x=0; x < (int) image->colors; x++)
2833 if (ScaleQuantumToShort(image->colormap[x].red) ==
2834 transparent_color.opacity)
2836 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2840 (void) SyncImage(image);
2843 #if 1 /* Should have already been done above, but glennrp problem P10
2848 for (y=0; y < (ssize_t) image->rows; y++)
2850 image->storage_class=storage_class;
2851 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2853 if (q == (PixelPacket *) NULL)
2856 indexes=GetAuthenticIndexQueue(image);
2858 /* Caution: on a Q8 build, this does not distinguish between
2859 * 16-bit colors that differ only in the low byte
2861 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2863 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2864 == transparent_color.red &&
2865 ScaleQuantumToShort(GetGreenPixelComponent(q))
2866 == transparent_color.green &&
2867 ScaleQuantumToShort(GetBluePixelComponent(q))
2868 == transparent_color.blue)
2870 SetOpacityPixelComponent(q,TransparentOpacity);
2873 #if 0 /* I have not found a case where this is needed. */
2876 q->opacity=(Quantum) OpaqueOpacity;
2883 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2889 image->storage_class=DirectClass;
2892 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2893 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2894 image->colorspace=GRAYColorspace;
2896 for (j = 0; j < 2; j++)
2899 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
2902 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2903 MagickTrue : MagickFalse;
2905 if (status != MagickFalse)
2906 for (i=0; i < (ssize_t) num_text; i++)
2908 /* Check for a profile */
2910 if (logging != MagickFalse)
2911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2912 " Reading PNG text chunk");
2914 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2916 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2925 length=text[i].text_length;
2926 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2928 if (value == (char *) NULL)
2930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2931 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2936 (void) ConcatenateMagickString(value,text[i].text,length+2);
2938 /* Don't save "density" or "units" property if we have a pHYs
2941 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2942 (LocaleCompare(text[i].key,"density") != 0 &&
2943 LocaleCompare(text[i].key,"units") != 0))
2944 (void) SetImageProperty(image,text[i].key,value);
2946 if (logging != MagickFalse)
2948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2949 " length: %lu",(unsigned long) length);
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " Keyword: %s",text[i].key);
2954 value=DestroyString(value);
2957 num_text_total += num_text;
2960 #ifdef MNG_OBJECT_BUFFERS
2962 Store the object if necessary.
2964 if (object_id && !mng_info->frozen[object_id])
2966 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2969 create a new object buffer.
2971 mng_info->ob[object_id]=(MngBuffer *)
2972 AcquireMagickMemory(sizeof(MngBuffer));
2974 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2976 mng_info->ob[object_id]->image=(Image *) NULL;
2977 mng_info->ob[object_id]->reference_count=1;
2981 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2982 mng_info->ob[object_id]->frozen)
2984 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2985 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2986 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2989 if (mng_info->ob[object_id]->frozen)
2990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2992 "`%s'",image->filename);
2998 if (mng_info->ob[object_id]->image != (Image *) NULL)
2999 mng_info->ob[object_id]->image=DestroyImage
3000 (mng_info->ob[object_id]->image);
3002 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3005 if (mng_info->ob[object_id]->image != (Image *) NULL)
3006 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3009 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3010 ResourceLimitError,"Cloning image for object buffer failed",
3011 "`%s'",image->filename);
3013 if (ping_width > 250000L || ping_height > 250000L)
3014 png_error(ping,"PNG Image dimensions are too large.");
3016 mng_info->ob[object_id]->width=ping_width;
3017 mng_info->ob[object_id]->height=ping_height;
3018 mng_info->ob[object_id]->color_type=ping_color_type;
3019 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3020 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3021 mng_info->ob[object_id]->compression_method=
3022 ping_compression_method;
3023 mng_info->ob[object_id]->filter_method=ping_filter_method;
3025 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3034 Copy the PLTE to the object buffer.
3036 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3037 mng_info->ob[object_id]->plte_length=number_colors;
3039 for (i=0; i < number_colors; i++)
3041 mng_info->ob[object_id]->plte[i]=plte[i];
3046 mng_info->ob[object_id]->plte_length=0;
3051 /* Set image->matte to MagickTrue if the input colortype supports
3052 * alpha or if a valid tRNS chunk is present, no matter whether there
3053 * is actual transparency present.
3055 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3056 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3057 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3058 MagickTrue : MagickFalse;
3060 /* Set more properties for identify to retrieve */
3065 if (num_text_total != 0)
3067 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3068 (void) FormatMagickString(msg,MaxTextExtent,
3069 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3070 (void) SetImageProperty(image,"PNG:text ",msg);
3073 if (num_raw_profiles != 0)
3075 (void) FormatMagickString(msg,MaxTextExtent,
3076 "%d were found", num_raw_profiles);
3077 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3080 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
3082 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3083 "chunk was found (see Chromaticity, above)");
3084 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3087 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3089 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3090 "chunk was found (see Background color, above)");
3091 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3094 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3097 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3098 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3100 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3101 (void) SetImageProperty(image,"PNG:tRNS ",msg);
3103 #if defined(PNG_sRGB_SUPPORTED)
3104 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3106 (void) FormatMagickString(msg,MaxTextExtent,
3107 "intent=%d (See Rendering intent)",
3109 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3113 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3115 (void) FormatMagickString(msg,MaxTextExtent,
3116 "gamma=%.8g (See Gamma, above)",
3118 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3121 #if defined(PNG_pHYs_SUPPORTED)
3122 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3124 (void) FormatMagickString(msg,MaxTextExtent,
3125 "x_res=%.10g, y_res=%.10g, units=%d",
3126 (double) x_resolution,(double) y_resolution, unit_type);
3127 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3131 #if defined(PNG_oFFs_SUPPORTED)
3132 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3134 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3135 (double) image->page.x,(double) image->page.y);
3136 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3140 if ((image->page.width != 0 && image->page.width != image->columns) ||
3141 (image->page.height != 0 && image->page.height != image->rows))
3143 (void) FormatMagickString(msg,MaxTextExtent,
3144 "width=%.20g, height=%.20g",
3145 (double) image->page.width,(double) image->page.height);
3146 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3151 Relinquish resources.
3153 png_destroy_read_struct(&ping,&ping_info,&end_info);
3155 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3156 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3157 UnlockSemaphoreInfo(ping_semaphore);
3160 if (logging != MagickFalse)
3161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3162 " exit ReadOnePNGImage()");
3166 /* end of reading one PNG image */
3169 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3184 magic_number[MaxTextExtent];
3192 assert(image_info != (const ImageInfo *) NULL);
3193 assert(image_info->signature == MagickSignature);
3195 if (image_info->debug != MagickFalse)
3196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3197 image_info->filename);
3199 assert(exception != (ExceptionInfo *) NULL);
3200 assert(exception->signature == MagickSignature);
3201 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3202 image=AcquireImage(image_info);
3203 mng_info=(MngInfo *) NULL;
3204 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3206 if (status == MagickFalse)
3207 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3210 Verify PNG signature.
3212 count=ReadBlob(image,8,(unsigned char *) magic_number);
3214 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3215 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3218 Allocate a MngInfo structure.
3220 have_mng_structure=MagickFalse;
3221 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3223 if (mng_info == (MngInfo *) NULL)
3224 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3227 Initialize members of the MngInfo structure.
3229 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3230 mng_info->image=image;
3231 have_mng_structure=MagickTrue;
3234 image=ReadOnePNGImage(mng_info,image_info,exception);
3235 MngInfoFreeStruct(mng_info,&have_mng_structure);
3237 if (image == (Image *) NULL)
3239 if (previous != (Image *) NULL)
3241 if (previous->signature != MagickSignature)
3242 ThrowReaderException(CorruptImageError,"CorruptImage");
3244 (void) CloseBlob(previous);
3245 (void) DestroyImageList(previous);
3248 if (logging != MagickFalse)
3249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3250 "exit ReadPNGImage() with error");
3252 return((Image *) NULL);
3255 (void) CloseBlob(image);
3257 if ((image->columns == 0) || (image->rows == 0))
3259 if (logging != MagickFalse)
3260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3261 "exit ReadPNGImage() with error.");
3263 ThrowReaderException(CorruptImageError,"CorruptImage");
3266 #if 0 /* This is probably redundant now */
3267 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3269 (void) SetImageType(image,PaletteType);
3271 if (image->matte != MagickFalse)
3273 /* To do: Reduce to binary transparency */
3278 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3280 (void) SetImageType(image,TrueColorType);
3281 image->matte=MagickFalse;
3284 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3285 (void) SetImageType(image,TrueColorMatteType);
3287 if (logging != MagickFalse)
3288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3289 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3290 (double) image->page.width,(double) image->page.height,
3291 (double) image->page.x,(double) image->page.y);
3293 if (logging != MagickFalse)
3294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3301 #if defined(JNG_SUPPORTED)
3303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3307 % R e a d O n e J N G I m a g e %
3311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3313 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3314 % (minus the 8-byte signature) and returns it. It allocates the memory
3315 % necessary for the new Image structure and returns a pointer to the new
3318 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3320 % The format of the ReadOneJNGImage method is:
3322 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3323 % ExceptionInfo *exception)
3325 % A description of each parameter follows:
3327 % o mng_info: Specifies a pointer to a MngInfo structure.
3329 % o image_info: the image info.
3331 % o exception: return any errors or warnings in this structure.
3334 static Image *ReadOneJNGImage(MngInfo *mng_info,
3335 const ImageInfo *image_info, ExceptionInfo *exception)
3362 jng_image_sample_depth,
3363 jng_image_compression_method,
3364 jng_image_interlace_method,
3365 jng_alpha_sample_depth,
3366 jng_alpha_compression_method,
3367 jng_alpha_filter_method,
3368 jng_alpha_interlace_method;
3370 register const PixelPacket
3377 register PixelPacket
3380 register unsigned char
3391 jng_alpha_compression_method=0;
3392 jng_alpha_sample_depth=8;
3396 alpha_image=(Image *) NULL;
3397 color_image=(Image *) NULL;
3398 alpha_image_info=(ImageInfo *) NULL;
3399 color_image_info=(ImageInfo *) NULL;
3401 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3402 " Enter ReadOneJNGImage()");
3404 image=mng_info->image;
3406 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3409 Allocate next image structure.
3411 if (logging != MagickFalse)
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " AcquireNextImage()");
3415 AcquireNextImage(image_info,image);
3417 if (GetNextImageInList(image) == (Image *) NULL)
3418 return((Image *) NULL);
3420 image=SyncNextImageInList(image);
3422 mng_info->image=image;
3425 Signature bytes have already been read.
3428 read_JSEP=MagickFalse;
3429 reading_idat=MagickFalse;
3430 skip_to_iend=MagickFalse;
3434 type[MaxTextExtent];
3443 Read a new JNG chunk.
3445 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3446 2*GetBlobSize(image));
3448 if (status == MagickFalse)
3452 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3453 length=ReadBlobMSBLong(image);
3454 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3456 if (logging != MagickFalse)
3457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3458 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3459 type[0],type[1],type[2],type[3],(double) length);
3461 if (length > PNG_UINT_31_MAX || count == 0)
3462 ThrowReaderException(CorruptImageError,"CorruptImage");
3465 chunk=(unsigned char *) NULL;
3469 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3471 if (chunk == (unsigned char *) NULL)
3472 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3474 for (i=0; i < (ssize_t) length; i++)
3475 chunk[i]=(unsigned char) ReadBlobByte(image);
3480 (void) ReadBlobMSBLong(image); /* read crc word */
3485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3490 if (memcmp(type,mng_JHDR,4) == 0)
3494 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3495 (p[2] << 8) | p[3]);
3496 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3497 (p[6] << 8) | p[7]);
3498 jng_color_type=p[8];
3499 jng_image_sample_depth=p[9];
3500 jng_image_compression_method=p[10];
3501 jng_image_interlace_method=p[11];
3503 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3506 jng_alpha_sample_depth=p[12];
3507 jng_alpha_compression_method=p[13];
3508 jng_alpha_filter_method=p[14];
3509 jng_alpha_interlace_method=p[15];
3511 if (logging != MagickFalse)
3513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3514 " jng_width: %16lu",(unsigned long) jng_width);
3516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " jng_width: %16lu",(unsigned long) jng_height);
3519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_color_type: %16d",jng_color_type);
3522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523 " jng_image_sample_depth: %3d",
3524 jng_image_sample_depth);
3526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527 " jng_image_compression_method:%3d",
3528 jng_image_compression_method);
3530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3531 " jng_image_interlace_method: %3d",
3532 jng_image_interlace_method);
3534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3535 " jng_alpha_sample_depth: %3d",
3536 jng_alpha_sample_depth);
3538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3539 " jng_alpha_compression_method:%3d",
3540 jng_alpha_compression_method);
3542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3543 " jng_alpha_filter_method: %3d",
3544 jng_alpha_filter_method);
3546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547 " jng_alpha_interlace_method: %3d",
3548 jng_alpha_interlace_method);
3553 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3559 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3560 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3561 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3564 o create color_image
3565 o open color_blob, attached to color_image
3566 o if (color type has alpha)
3567 open alpha_blob, attached to alpha_image
3570 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3572 if (color_image_info == (ImageInfo *) NULL)
3573 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3575 GetImageInfo(color_image_info);
3576 color_image=AcquireImage(color_image_info);
3578 if (color_image == (Image *) NULL)
3579 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3581 if (logging != MagickFalse)
3582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3583 " Creating color_blob.");
3585 (void) AcquireUniqueFilename(color_image->filename);
3586 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3589 if (status == MagickFalse)
3590 return((Image *) NULL);
3592 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3594 alpha_image_info=(ImageInfo *)
3595 AcquireMagickMemory(sizeof(ImageInfo));
3597 if (alpha_image_info == (ImageInfo *) NULL)
3598 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3600 GetImageInfo(alpha_image_info);
3601 alpha_image=AcquireImage(alpha_image_info);
3603 if (alpha_image == (Image *) NULL)
3605 alpha_image=DestroyImage(alpha_image);
3606 ThrowReaderException(ResourceLimitError,
3607 "MemoryAllocationFailed");
3610 if (logging != MagickFalse)
3611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3612 " Creating alpha_blob.");
3614 (void) AcquireUniqueFilename(alpha_image->filename);
3615 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3618 if (status == MagickFalse)
3619 return((Image *) NULL);
3621 if (jng_alpha_compression_method == 0)
3626 if (logging != MagickFalse)
3627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3628 " Writing IHDR chunk to alpha_blob.");
3630 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3631 "\211PNG\r\n\032\n");
3633 (void) WriteBlobMSBULong(alpha_image,13L);
3634 PNGType(data,mng_IHDR);
3635 LogPNGChunk(logging,mng_IHDR,13L);
3636 PNGLong(data+4,jng_width);
3637 PNGLong(data+8,jng_height);
3638 data[12]=jng_alpha_sample_depth;
3639 data[13]=0; /* color_type gray */
3640 data[14]=0; /* compression method 0 */
3641 data[15]=0; /* filter_method 0 */
3642 data[16]=0; /* interlace_method 0 */
3643 (void) WriteBlob(alpha_image,17,data);
3644 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3647 reading_idat=MagickTrue;
3650 if (memcmp(type,mng_JDAT,4) == 0)
3652 /* Copy chunk to color_image->blob */
3654 if (logging != MagickFalse)
3655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3656 " Copying JDAT chunk data to color_blob.");
3658 (void) WriteBlob(color_image,length,chunk);
3661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3666 if (memcmp(type,mng_IDAT,4) == 0)
3671 /* Copy IDAT header and chunk data to alpha_image->blob */
3673 if (image_info->ping == MagickFalse)
3675 if (logging != MagickFalse)
3676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 " Copying IDAT chunk data to alpha_blob.");
3679 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3680 PNGType(data,mng_IDAT);
3681 LogPNGChunk(logging,mng_IDAT,length);
3682 (void) WriteBlob(alpha_image,4,data);
3683 (void) WriteBlob(alpha_image,length,chunk);
3684 (void) WriteBlobMSBULong(alpha_image,
3685 crc32(crc32(0,data,4),chunk,(uInt) length));
3689 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3694 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3696 /* Copy chunk data to alpha_image->blob */
3698 if (image_info->ping == MagickFalse)
3700 if (logging != MagickFalse)
3701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3702 " Copying JDAA chunk data to alpha_blob.");
3704 (void) WriteBlob(alpha_image,length,chunk);
3708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3713 if (memcmp(type,mng_JSEP,4) == 0)
3715 read_JSEP=MagickTrue;
3718 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3723 if (memcmp(type,mng_bKGD,4) == 0)
3727 image->background_color.red=ScaleCharToQuantum(p[1]);
3728 image->background_color.green=image->background_color.red;
3729 image->background_color.blue=image->background_color.red;
3734 image->background_color.red=ScaleCharToQuantum(p[1]);
3735 image->background_color.green=ScaleCharToQuantum(p[3]);
3736 image->background_color.blue=ScaleCharToQuantum(p[5]);
3739 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3743 if (memcmp(type,mng_gAMA,4) == 0)
3746 image->gamma=((float) mng_get_long(p))*0.00001;
3748 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3752 if (memcmp(type,mng_cHRM,4) == 0)
3756 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3757 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3758 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3759 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3760 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3761 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3762 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3763 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3770 if (memcmp(type,mng_sRGB,4) == 0)
3774 image->rendering_intent=
3775 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3776 image->gamma=0.45455f;
3777 image->chromaticity.red_primary.x=0.6400f;
3778 image->chromaticity.red_primary.y=0.3300f;
3779 image->chromaticity.green_primary.x=0.3000f;
3780 image->chromaticity.green_primary.y=0.6000f;
3781 image->chromaticity.blue_primary.x=0.1500f;
3782 image->chromaticity.blue_primary.y=0.0600f;
3783 image->chromaticity.white_point.x=0.3127f;
3784 image->chromaticity.white_point.y=0.3290f;
3787 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3791 if (memcmp(type,mng_oFFs,4) == 0)
3795 image->page.x=(ssize_t) mng_get_long(p);
3796 image->page.y=(ssize_t) mng_get_long(&p[4]);
3798 if ((int) p[8] != 0)
3800 image->page.x/=10000;
3801 image->page.y/=10000;
3806 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3811 if (memcmp(type,mng_pHYs,4) == 0)
3815 image->x_resolution=(double) mng_get_long(p);
3816 image->y_resolution=(double) mng_get_long(&p[4]);
3817 if ((int) p[8] == PNG_RESOLUTION_METER)
3819 image->units=PixelsPerCentimeterResolution;
3820 image->x_resolution=image->x_resolution/100.0f;
3821 image->y_resolution=image->y_resolution/100.0f;
3825 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3830 if (memcmp(type,mng_iCCP,4) == 0)
3834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3841 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3843 if (memcmp(type,mng_IEND,4))
3853 Finish up reading image data:
3855 o read main image from color_blob.
3859 o if (color_type has alpha)
3860 if alpha_encoding is PNG
3861 read secondary image from alpha_blob via ReadPNG
3862 if alpha_encoding is JPEG
3863 read secondary image from alpha_blob via ReadJPEG
3867 o copy intensity of secondary image into
3868 opacity samples of main image.
3870 o destroy the secondary image.
3873 (void) CloseBlob(color_image);
3875 if (logging != MagickFalse)
3876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3877 " Reading jng_image from color_blob.");
3879 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3880 color_image->filename);
3882 color_image_info->ping=MagickFalse; /* To do: avoid this */
3883 jng_image=ReadImage(color_image_info,exception);
3885 if (jng_image == (Image *) NULL)
3886 return((Image *) NULL);
3888 (void) RelinquishUniqueFileResource(color_image->filename);
3889 color_image=DestroyImage(color_image);
3890 color_image_info=DestroyImageInfo(color_image_info);
3892 if (jng_image == (Image *) NULL)
3893 return((Image *) NULL);
3895 if (logging != MagickFalse)
3896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3897 " Copying jng_image pixels to main image.");
3899 image->rows=jng_height;
3900 image->columns=jng_width;
3901 length=image->columns*sizeof(PixelPacket);
3903 for (y=0; y < (ssize_t) image->rows; y++)
3905 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3906 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3907 (void) CopyMagickMemory(q,s,length);
3909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3913 jng_image=DestroyImage(jng_image);
3915 if (image_info->ping == MagickFalse)
3917 if (jng_color_type >= 12)
3919 if (jng_alpha_compression_method == 0)
3923 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3924 PNGType(data,mng_IEND);
3925 LogPNGChunk(logging,mng_IEND,0L);
3926 (void) WriteBlob(alpha_image,4,data);
3927 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3930 (void) CloseBlob(alpha_image);
3932 if (logging != MagickFalse)
3933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3934 " Reading opacity from alpha_blob.");
3936 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3937 "%s",alpha_image->filename);
3939 jng_image=ReadImage(alpha_image_info,exception);
3941 if (jng_image != (Image *) NULL)
3942 for (y=0; y < (ssize_t) image->rows; y++)
3944 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3946 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3948 if (image->matte != MagickFalse)
3949 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3950 q->opacity=(Quantum) QuantumRange-
3951 GetRedPixelComponent(s);
3954 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3956 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3957 GetRedPixelComponent(s));
3958 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
3959 image->matte=MagickTrue;
3962 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3965 (void) RelinquishUniqueFileResource(alpha_image->filename);
3966 alpha_image=DestroyImage(alpha_image);
3967 alpha_image_info=DestroyImageInfo(alpha_image_info);
3968 if (jng_image != (Image *) NULL)
3969 jng_image=DestroyImage(jng_image);
3973 /* Read the JNG image. */
3975 if (mng_info->mng_type == 0)
3977 mng_info->mng_width=jng_width;
3978 mng_info->mng_height=jng_height;
3981 if (image->page.width == 0 && image->page.height == 0)
3983 image->page.width=jng_width;
3984 image->page.height=jng_height;
3987 if (image->page.x == 0 && image->page.y == 0)
3989 image->page.x=mng_info->x_off[mng_info->object_id];
3990 image->page.y=mng_info->y_off[mng_info->object_id];
3995 image->page.y=mng_info->y_off[mng_info->object_id];
3998 mng_info->image_found++;
3999 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4000 2*GetBlobSize(image));
4002 if (logging != MagickFalse)
4003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4004 " exit ReadOneJNGImage()");
4010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4014 % R e a d J N G I m a g e %
4018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4020 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4021 % (including the 8-byte signature) and returns it. It allocates the memory
4022 % necessary for the new Image structure and returns a pointer to the new
4025 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4027 % The format of the ReadJNGImage method is:
4029 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4032 % A description of each parameter follows:
4034 % o image_info: the image info.
4036 % o exception: return any errors or warnings in this structure.
4040 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4055 magic_number[MaxTextExtent];
4063 assert(image_info != (const ImageInfo *) NULL);
4064 assert(image_info->signature == MagickSignature);
4065 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4066 assert(exception != (ExceptionInfo *) NULL);
4067 assert(exception->signature == MagickSignature);
4068 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4069 image=AcquireImage(image_info);
4070 mng_info=(MngInfo *) NULL;
4071 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4073 if (status == MagickFalse)
4074 return((Image *) NULL);
4076 if (LocaleCompare(image_info->magick,"JNG") != 0)
4077 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4079 /* Verify JNG signature. */
4081 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4083 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4084 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4086 /* Allocate a MngInfo structure. */
4088 have_mng_structure=MagickFalse;
4089 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4091 if (mng_info == (MngInfo *) NULL)
4092 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4094 /* Initialize members of the MngInfo structure. */
4096 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4097 have_mng_structure=MagickTrue;
4099 mng_info->image=image;
4101 image=ReadOneJNGImage(mng_info,image_info,exception);
4102 MngInfoFreeStruct(mng_info,&have_mng_structure);
4104 if (image == (Image *) NULL)
4106 if (IsImageObject(previous) != MagickFalse)
4108 (void) CloseBlob(previous);
4109 (void) DestroyImageList(previous);
4112 if (logging != MagickFalse)
4113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4114 "exit ReadJNGImage() with error");
4116 return((Image *) NULL);
4118 (void) CloseBlob(image);
4120 if (image->columns == 0 || image->rows == 0)
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 "exit ReadJNGImage() with error");
4126 ThrowReaderException(CorruptImageError,"CorruptImage");
4129 if (logging != MagickFalse)
4130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4136 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4139 page_geometry[MaxTextExtent];
4172 #if defined(MNG_INSERT_LAYERS)
4174 mng_background_color;
4177 register unsigned char
4192 #if defined(MNG_INSERT_LAYERS)
4197 volatile unsigned int
4198 #ifdef MNG_OBJECT_BUFFERS
4199 mng_background_object=0,
4201 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4204 default_frame_timeout,
4206 #if defined(MNG_INSERT_LAYERS)
4212 /* These delays are all measured in image ticks_per_second,
4213 * not in MNG ticks_per_second
4216 default_frame_delay,
4220 #if defined(MNG_INSERT_LAYERS)
4229 previous_fb.bottom=0;
4231 previous_fb.right=0;
4233 default_fb.bottom=0;
4237 /* Open image file. */
4239 assert(image_info != (const ImageInfo *) NULL);
4240 assert(image_info->signature == MagickSignature);
4241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4242 assert(exception != (ExceptionInfo *) NULL);
4243 assert(exception->signature == MagickSignature);
4244 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4245 image=AcquireImage(image_info);
4246 mng_info=(MngInfo *) NULL;
4247 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4249 if (status == MagickFalse)
4250 return((Image *) NULL);
4252 first_mng_object=MagickFalse;
4254 have_mng_structure=MagickFalse;
4256 /* Allocate a MngInfo structure. */
4258 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4260 if (mng_info == (MngInfo *) NULL)
4261 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4263 /* Initialize members of the MngInfo structure. */
4265 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4266 mng_info->image=image;
4267 have_mng_structure=MagickTrue;
4269 if (LocaleCompare(image_info->magick,"MNG") == 0)
4272 magic_number[MaxTextExtent];
4274 /* Verify MNG signature. */
4275 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4276 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4277 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4279 /* Initialize some nonzero members of the MngInfo structure. */
4280 for (i=0; i < MNG_MAX_OBJECTS; i++)
4282 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4283 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4285 mng_info->exists[0]=MagickTrue;
4288 first_mng_object=MagickTrue;
4290 #if defined(MNG_INSERT_LAYERS)
4291 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4293 default_frame_delay=0;
4294 default_frame_timeout=0;
4297 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4299 skip_to_iend=MagickFalse;
4300 term_chunk_found=MagickFalse;
4301 mng_info->framing_mode=1;
4302 #if defined(MNG_INSERT_LAYERS)
4303 mandatory_back=MagickFalse;
4305 #if defined(MNG_INSERT_LAYERS)
4306 mng_background_color=image->background_color;
4308 default_fb=mng_info->frame;
4309 previous_fb=mng_info->frame;
4313 type[MaxTextExtent];
4315 if (LocaleCompare(image_info->magick,"MNG") == 0)
4324 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4325 length=ReadBlobMSBLong(image);
4326 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4328 if (logging != MagickFalse)
4329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4330 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4331 type[0],type[1],type[2],type[3],(double) length);
4333 if (length > PNG_UINT_31_MAX)
4337 ThrowReaderException(CorruptImageError,"CorruptImage");
4340 chunk=(unsigned char *) NULL;
4344 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4346 if (chunk == (unsigned char *) NULL)
4347 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4349 for (i=0; i < (ssize_t) length; i++)
4350 chunk[i]=(unsigned char) ReadBlobByte(image);
4355 (void) ReadBlobMSBLong(image); /* read crc word */
4357 #if !defined(JNG_SUPPORTED)
4358 if (memcmp(type,mng_JHDR,4) == 0)
4360 skip_to_iend=MagickTrue;
4362 if (mng_info->jhdr_warning == 0)
4363 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4364 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4366 mng_info->jhdr_warning++;
4369 if (memcmp(type,mng_DHDR,4) == 0)
4371 skip_to_iend=MagickTrue;
4373 if (mng_info->dhdr_warning == 0)
4374 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4375 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4377 mng_info->dhdr_warning++;
4379 if (memcmp(type,mng_MEND,4) == 0)
4384 if (memcmp(type,mng_IEND,4) == 0)
4385 skip_to_iend=MagickFalse;
4388 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4390 if (logging != MagickFalse)
4391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4397 if (memcmp(type,mng_MHDR,4) == 0)
4399 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4400 (p[2] << 8) | p[3]);
4402 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4403 (p[6] << 8) | p[7]);
4405 if (logging != MagickFalse)
4407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4408 " MNG width: %.20g",(double) mng_info->mng_width);
4409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4410 " MNG height: %.20g",(double) mng_info->mng_height);
4414 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4416 if (mng_info->ticks_per_second == 0)
4417 default_frame_delay=0;
4420 default_frame_delay=1UL*image->ticks_per_second/
4421 mng_info->ticks_per_second;
4423 frame_delay=default_frame_delay;
4429 simplicity=(size_t) mng_get_long(p);
4432 mng_type=1; /* Full MNG */
4434 if ((simplicity != 0) && ((simplicity | 11) == 11))
4435 mng_type=2; /* LC */
4437 if ((simplicity != 0) && ((simplicity | 9) == 9))
4438 mng_type=3; /* VLC */
4440 #if defined(MNG_INSERT_LAYERS)
4442 insert_layers=MagickTrue;
4444 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4446 /* Allocate next image structure. */
4447 AcquireNextImage(image_info,image);
4449 if (GetNextImageInList(image) == (Image *) NULL)
4450 return((Image *) NULL);
4452 image=SyncNextImageInList(image);
4453 mng_info->image=image;
4456 if ((mng_info->mng_width > 65535L) ||
4457 (mng_info->mng_height > 65535L))
4458 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4460 (void) FormatMagickString(page_geometry,MaxTextExtent,
4461 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4462 mng_info->mng_height);
4464 mng_info->frame.left=0;
4465 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4466 mng_info->frame.top=0;
4467 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4468 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4470 for (i=0; i < MNG_MAX_OBJECTS; i++)
4471 mng_info->object_clip[i]=mng_info->frame;
4473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4477 if (memcmp(type,mng_TERM,4) == 0)
4488 final_delay=(png_uint_32) mng_get_long(&p[2]);
4489 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4491 if (mng_iterations == PNG_UINT_31_MAX)
4494 image->iterations=mng_iterations;
4495 term_chunk_found=MagickTrue;
4498 if (logging != MagickFalse)
4500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4501 " repeat=%d",repeat);
4503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4504 " final_delay=%.20g",(double) final_delay);
4506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4507 " image->iterations=%.20g",(double) image->iterations);
4510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4513 if (memcmp(type,mng_DEFI,4) == 0)
4516 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4517 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4520 object_id=(p[0] << 8) | p[1];
4522 if (mng_type == 2 && object_id != 0)
4523 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4524 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4527 if (object_id > MNG_MAX_OBJECTS)
4530 Instead ofsuing a warning we should allocate a larger
4531 MngInfo structure and continue.
4533 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4534 CoderError,"object id too large","`%s'",image->filename);
4535 object_id=MNG_MAX_OBJECTS;
4538 if (mng_info->exists[object_id])
4539 if (mng_info->frozen[object_id])
4541 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4542 (void) ThrowMagickException(&image->exception,
4543 GetMagickModule(),CoderError,
4544 "DEFI cannot redefine a frozen MNG object","`%s'",
4549 mng_info->exists[object_id]=MagickTrue;
4552 mng_info->invisible[object_id]=p[2];
4555 Extract object offset info.
4559 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4560 (p[5] << 16) | (p[6] << 8) | p[7]);
4562 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4563 (p[9] << 16) | (p[10] << 8) | p[11]);
4565 if (logging != MagickFalse)
4567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4568 " x_off[%d]: %.20g",object_id,(double)
4569 mng_info->x_off[object_id]);
4571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4572 " y_off[%d]: %.20g",object_id,(double)
4573 mng_info->y_off[object_id]);
4578 Extract object clipping info.
4581 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4587 if (memcmp(type,mng_bKGD,4) == 0)
4589 mng_info->have_global_bkgd=MagickFalse;
4593 mng_info->mng_global_bkgd.red=
4594 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4596 mng_info->mng_global_bkgd.green=
4597 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4599 mng_info->mng_global_bkgd.blue=
4600 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4602 mng_info->have_global_bkgd=MagickTrue;
4605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608 if (memcmp(type,mng_BACK,4) == 0)
4610 #if defined(MNG_INSERT_LAYERS)
4612 mandatory_back=p[6];
4617 if (mandatory_back && length > 5)
4619 mng_background_color.red=
4620 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4622 mng_background_color.green=
4623 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4625 mng_background_color.blue=
4626 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4628 mng_background_color.opacity=OpaqueOpacity;
4631 #ifdef MNG_OBJECT_BUFFERS
4633 mng_background_object=(p[7] << 8) | p[8];
4636 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4640 if (memcmp(type,mng_PLTE,4) == 0)
4642 /* Read global PLTE. */
4644 if (length && (length < 769))
4646 if (mng_info->global_plte == (png_colorp) NULL)
4647 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4648 sizeof(*mng_info->global_plte));
4650 for (i=0; i < (ssize_t) (length/3); i++)
4652 mng_info->global_plte[i].red=p[3*i];
4653 mng_info->global_plte[i].green=p[3*i+1];
4654 mng_info->global_plte[i].blue=p[3*i+2];
4657 mng_info->global_plte_length=(unsigned int) (length/3);
4660 for ( ; i < 256; i++)
4662 mng_info->global_plte[i].red=i;
4663 mng_info->global_plte[i].green=i;
4664 mng_info->global_plte[i].blue=i;
4668 mng_info->global_plte_length=256;
4671 mng_info->global_plte_length=0;
4673 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4677 if (memcmp(type,mng_tRNS,4) == 0)
4679 /* read global tRNS */
4682 for (i=0; i < (ssize_t) length; i++)
4683 mng_info->global_trns[i]=p[i];
4686 for ( ; i < 256; i++)
4687 mng_info->global_trns[i]=255;
4689 mng_info->global_trns_length=(unsigned int) length;
4690 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4693 if (memcmp(type,mng_gAMA,4) == 0)
4700 igamma=mng_get_long(p);
4701 mng_info->global_gamma=((float) igamma)*0.00001;
4702 mng_info->have_global_gama=MagickTrue;
4706 mng_info->have_global_gama=MagickFalse;
4708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4712 if (memcmp(type,mng_cHRM,4) == 0)
4714 /* Read global cHRM */
4718 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4719 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4720 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4721 mng_info->global_chrm.red_primary.y=0.00001*
4722 mng_get_long(&p[12]);
4723 mng_info->global_chrm.green_primary.x=0.00001*
4724 mng_get_long(&p[16]);
4725 mng_info->global_chrm.green_primary.y=0.00001*
4726 mng_get_long(&p[20]);
4727 mng_info->global_chrm.blue_primary.x=0.00001*
4728 mng_get_long(&p[24]);
4729 mng_info->global_chrm.blue_primary.y=0.00001*
4730 mng_get_long(&p[28]);
4731 mng_info->have_global_chrm=MagickTrue;
4734 mng_info->have_global_chrm=MagickFalse;
4736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4740 if (memcmp(type,mng_sRGB,4) == 0)
4747 mng_info->global_srgb_intent=
4748 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4749 mng_info->have_global_srgb=MagickTrue;
4752 mng_info->have_global_srgb=MagickFalse;
4754 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4758 if (memcmp(type,mng_iCCP,4) == 0)
4766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4771 if (memcmp(type,mng_FRAM,4) == 0)
4774 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4775 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4778 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4779 image->delay=frame_delay;
4781 frame_delay=default_frame_delay;
4782 frame_timeout=default_frame_timeout;
4787 mng_info->framing_mode=p[0];
4789 if (logging != MagickFalse)
4790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4791 " Framing_mode=%d",mng_info->framing_mode);
4795 /* Note the delay and frame clipping boundaries. */
4797 p++; /* framing mode */
4799 while (*p && ((p-chunk) < (ssize_t) length))
4800 p++; /* frame name */
4802 p++; /* frame name terminator */
4804 if ((p-chunk) < (ssize_t) (length-4))
4811 change_delay=(*p++);
4812 change_timeout=(*p++);
4813 change_clipping=(*p++);
4814 p++; /* change_sync */
4818 frame_delay=1UL*image->ticks_per_second*
4821 if (mng_info->ticks_per_second != 0)
4822 frame_delay/=mng_info->ticks_per_second;
4825 frame_delay=PNG_UINT_31_MAX;
4827 if (change_delay == 2)
4828 default_frame_delay=frame_delay;
4832 if (logging != MagickFalse)
4833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4834 " Framing_delay=%.20g",(double) frame_delay);
4839 frame_timeout=1UL*image->ticks_per_second*
4842 if (mng_info->ticks_per_second != 0)
4843 frame_timeout/=mng_info->ticks_per_second;
4846 frame_timeout=PNG_UINT_31_MAX;
4848 if (change_delay == 2)
4849 default_frame_timeout=frame_timeout;
4853 if (logging != MagickFalse)
4854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4855 " Framing_timeout=%.20g",(double) frame_timeout);
4858 if (change_clipping)
4860 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4864 if (logging != MagickFalse)
4865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4866 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4867 (double) fb.left,(double) fb.right,(double) fb.top,
4868 (double) fb.bottom);
4870 if (change_clipping == 2)
4876 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4878 subframe_width=(size_t) (mng_info->clip.right
4879 -mng_info->clip.left);
4881 subframe_height=(size_t) (mng_info->clip.bottom
4882 -mng_info->clip.top);
4884 Insert a background layer behind the frame if framing_mode is 4.
4886 #if defined(MNG_INSERT_LAYERS)
4887 if (logging != MagickFalse)
4888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4889 " subframe_width=%.20g, subframe_height=%.20g",(double)
4890 subframe_width,(double) subframe_height);
4892 if (insert_layers && (mng_info->framing_mode == 4) &&
4893 (subframe_width) && (subframe_height))
4895 /* Allocate next image structure. */
4896 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4898 AcquireNextImage(image_info,image);
4900 if (GetNextImageInList(image) == (Image *) NULL)
4902 image=DestroyImageList(image);
4903 MngInfoFreeStruct(mng_info,&have_mng_structure);
4904 return((Image *) NULL);
4907 image=SyncNextImageInList(image);
4910 mng_info->image=image;
4912 if (term_chunk_found)
4914 image->start_loop=MagickTrue;
4915 image->iterations=mng_iterations;
4916 term_chunk_found=MagickFalse;
4920 image->start_loop=MagickFalse;
4922 image->columns=subframe_width;
4923 image->rows=subframe_height;
4924 image->page.width=subframe_width;
4925 image->page.height=subframe_height;
4926 image->page.x=mng_info->clip.left;
4927 image->page.y=mng_info->clip.top;
4928 image->background_color=mng_background_color;
4929 image->matte=MagickFalse;
4931 (void) SetImageBackgroundColor(image);
4933 if (logging != MagickFalse)
4934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4935 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4936 (double) mng_info->clip.left,(double) mng_info->clip.right,
4937 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4943 if (memcmp(type,mng_CLIP,4) == 0)
4952 first_object=(p[0] << 8) | p[1];
4953 last_object=(p[2] << 8) | p[3];
4955 for (i=(int) first_object; i <= (int) last_object; i++)
4957 if (mng_info->exists[i] && !mng_info->frozen[i])
4962 box=mng_info->object_clip[i];
4963 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4970 if (memcmp(type,mng_SAVE,4) == 0)
4972 for (i=1; i < MNG_MAX_OBJECTS; i++)
4973 if (mng_info->exists[i])
4975 mng_info->frozen[i]=MagickTrue;
4976 #ifdef MNG_OBJECT_BUFFERS
4977 if (mng_info->ob[i] != (MngBuffer *) NULL)
4978 mng_info->ob[i]->frozen=MagickTrue;
4983 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4988 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4990 /* Read DISC or SEEK. */
4992 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4994 for (i=1; i < MNG_MAX_OBJECTS; i++)
4995 MngInfoDiscardObject(mng_info,i);
5003 for (j=0; j < (ssize_t) length; j+=2)
5005 i=p[j] << 8 | p[j+1];
5006 MngInfoDiscardObject(mng_info,i);
5011 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5016 if (memcmp(type,mng_MOVE,4) == 0)
5024 first_object=(p[0] << 8) | p[1];
5025 last_object=(p[2] << 8) | p[3];
5026 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5028 if (mng_info->exists[i] && !mng_info->frozen[i])
5036 old_pair.a=mng_info->x_off[i];
5037 old_pair.b=mng_info->y_off[i];
5038 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5039 mng_info->x_off[i]=new_pair.a;
5040 mng_info->y_off[i]=new_pair.b;
5044 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5048 if (memcmp(type,mng_LOOP,4) == 0)
5050 ssize_t loop_iters=1;
5051 loop_level=chunk[0];
5052 mng_info->loop_active[loop_level]=1; /* mark loop active */
5054 /* Record starting point. */
5055 loop_iters=mng_get_long(&chunk[1]);
5057 if (logging != MagickFalse)
5058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5059 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5060 (double) loop_iters);
5062 if (loop_iters == 0)
5063 skipping_loop=loop_level;
5067 mng_info->loop_jump[loop_level]=TellBlob(image);
5068 mng_info->loop_count[loop_level]=loop_iters;
5071 mng_info->loop_iteration[loop_level]=0;
5072 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5076 if (memcmp(type,mng_ENDL,4) == 0)
5078 loop_level=chunk[0];
5080 if (skipping_loop > 0)
5082 if (skipping_loop == loop_level)
5085 Found end of zero-iteration loop.
5088 mng_info->loop_active[loop_level]=0;
5094 if (mng_info->loop_active[loop_level] == 1)
5096 mng_info->loop_count[loop_level]--;
5097 mng_info->loop_iteration[loop_level]++;
5099 if (logging != MagickFalse)
5100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5101 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5102 (double) loop_level,(double)
5103 mng_info->loop_count[loop_level]);
5105 if (mng_info->loop_count[loop_level] != 0)
5107 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5111 ThrowReaderException(CorruptImageError,
5112 "ImproperImageHeader");
5123 mng_info->loop_active[loop_level]=0;
5125 for (i=0; i < loop_level; i++)
5126 if (mng_info->loop_active[i] == 1)
5127 last_level=(short) i;
5128 loop_level=last_level;
5133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5137 if (memcmp(type,mng_CLON,4) == 0)
5139 if (mng_info->clon_warning == 0)
5140 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5141 CoderError,"CLON is not implemented yet","`%s'",
5144 mng_info->clon_warning++;
5147 if (memcmp(type,mng_MAGN,4) == 0)
5162 magn_first=(p[0] << 8) | p[1];
5168 magn_last=(p[2] << 8) | p[3];
5171 magn_last=magn_first;
5172 #ifndef MNG_OBJECT_BUFFERS
5173 if (magn_first || magn_last)
5174 if (mng_info->magn_warning == 0)
5176 (void) ThrowMagickException(&image->exception,
5177 GetMagickModule(),CoderError,
5178 "MAGN is not implemented yet for nonzero objects",
5179 "`%s'",image->filename);
5181 mng_info->magn_warning++;
5191 magn_mx=(p[5] << 8) | p[6];
5200 magn_my=(p[7] << 8) | p[8];
5209 magn_ml=(p[9] << 8) | p[10];
5218 magn_mr=(p[11] << 8) | p[12];
5227 magn_mt=(p[13] << 8) | p[14];
5236 magn_mb=(p[15] << 8) | p[16];
5248 magn_methy=magn_methx;
5251 if (magn_methx > 5 || magn_methy > 5)
5252 if (mng_info->magn_warning == 0)
5254 (void) ThrowMagickException(&image->exception,
5255 GetMagickModule(),CoderError,
5256 "Unknown MAGN method in MNG datastream","`%s'",
5259 mng_info->magn_warning++;
5261 #ifdef MNG_OBJECT_BUFFERS
5262 /* Magnify existing objects in the range magn_first to magn_last */
5264 if (magn_first == 0 || magn_last == 0)
5266 /* Save the magnification factors for object 0 */
5267 mng_info->magn_mb=magn_mb;
5268 mng_info->magn_ml=magn_ml;
5269 mng_info->magn_mr=magn_mr;
5270 mng_info->magn_mt=magn_mt;
5271 mng_info->magn_mx=magn_mx;
5272 mng_info->magn_my=magn_my;
5273 mng_info->magn_methx=magn_methx;
5274 mng_info->magn_methy=magn_methy;
5278 if (memcmp(type,mng_PAST,4) == 0)
5280 if (mng_info->past_warning == 0)
5281 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5282 CoderError,"PAST is not implemented yet","`%s'",
5285 mng_info->past_warning++;
5288 if (memcmp(type,mng_SHOW,4) == 0)
5290 if (mng_info->show_warning == 0)
5291 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5292 CoderError,"SHOW is not implemented yet","`%s'",
5295 mng_info->show_warning++;
5298 if (memcmp(type,mng_sBIT,4) == 0)
5301 mng_info->have_global_sbit=MagickFalse;
5305 mng_info->global_sbit.gray=p[0];
5306 mng_info->global_sbit.red=p[0];
5307 mng_info->global_sbit.green=p[1];
5308 mng_info->global_sbit.blue=p[2];
5309 mng_info->global_sbit.alpha=p[3];
5310 mng_info->have_global_sbit=MagickTrue;
5313 if (memcmp(type,mng_pHYs,4) == 0)
5317 mng_info->global_x_pixels_per_unit=
5318 (size_t) mng_get_long(p);
5319 mng_info->global_y_pixels_per_unit=
5320 (size_t) mng_get_long(&p[4]);
5321 mng_info->global_phys_unit_type=p[8];
5322 mng_info->have_global_phys=MagickTrue;
5326 mng_info->have_global_phys=MagickFalse;
5328 if (memcmp(type,mng_pHYg,4) == 0)
5330 if (mng_info->phyg_warning == 0)
5331 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5332 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5334 mng_info->phyg_warning++;
5336 if (memcmp(type,mng_BASI,4) == 0)
5338 skip_to_iend=MagickTrue;
5340 if (mng_info->basi_warning == 0)
5341 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5342 CoderError,"BASI is not implemented yet","`%s'",
5345 mng_info->basi_warning++;
5346 #ifdef MNG_BASI_SUPPORTED
5347 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5348 (p[2] << 8) | p[3]);
5349 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5350 (p[6] << 8) | p[7]);
5351 basi_color_type=p[8];
5352 basi_compression_method=p[9];
5353 basi_filter_type=p[10];
5354 basi_interlace_method=p[11];
5356 basi_red=(p[12] << 8) & p[13];
5362 basi_green=(p[14] << 8) & p[15];
5368 basi_blue=(p[16] << 8) & p[17];
5374 basi_alpha=(p[18] << 8) & p[19];
5378 if (basi_sample_depth == 16)
5385 basi_viewable=p[20];
5391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5395 if (memcmp(type,mng_IHDR,4)
5396 #if defined(JNG_SUPPORTED)
5397 && memcmp(type,mng_JHDR,4)
5401 /* Not an IHDR or JHDR chunk */
5403 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5408 if (logging != MagickFalse)
5409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5410 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5412 mng_info->exists[object_id]=MagickTrue;
5413 mng_info->viewable[object_id]=MagickTrue;
5415 if (mng_info->invisible[object_id])
5417 if (logging != MagickFalse)
5418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5419 " Skipping invisible object");
5421 skip_to_iend=MagickTrue;
5422 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5425 #if defined(MNG_INSERT_LAYERS)
5427 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5429 image_width=(size_t) mng_get_long(p);
5430 image_height=(size_t) mng_get_long(&p[4]);
5432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5435 Insert a transparent background layer behind the entire animation
5436 if it is not full screen.
5438 #if defined(MNG_INSERT_LAYERS)
5439 if (insert_layers && mng_type && first_mng_object)
5441 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5442 (image_width < mng_info->mng_width) ||
5443 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5444 (image_height < mng_info->mng_height) ||
5445 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5447 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5450 Allocate next image structure.
5452 AcquireNextImage(image_info,image);
5454 if (GetNextImageInList(image) == (Image *) NULL)
5456 image=DestroyImageList(image);
5457 MngInfoFreeStruct(mng_info,&have_mng_structure);
5458 return((Image *) NULL);
5461 image=SyncNextImageInList(image);
5463 mng_info->image=image;
5465 if (term_chunk_found)
5467 image->start_loop=MagickTrue;
5468 image->iterations=mng_iterations;
5469 term_chunk_found=MagickFalse;
5473 image->start_loop=MagickFalse;
5475 /* Make a background rectangle. */
5478 image->columns=mng_info->mng_width;
5479 image->rows=mng_info->mng_height;
5480 image->page.width=mng_info->mng_width;
5481 image->page.height=mng_info->mng_height;
5484 image->background_color=mng_background_color;
5485 (void) SetImageBackgroundColor(image);
5486 if (logging != MagickFalse)
5487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5488 " Inserted transparent background layer, W=%.20g, H=%.20g",
5489 (double) mng_info->mng_width,(double) mng_info->mng_height);
5493 Insert a background layer behind the upcoming image if
5494 framing_mode is 3, and we haven't already inserted one.
5496 if (insert_layers && (mng_info->framing_mode == 3) &&
5497 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5498 (simplicity & 0x08)))
5500 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5503 Allocate next image structure.
5505 AcquireNextImage(image_info,image);
5507 if (GetNextImageInList(image) == (Image *) NULL)
5509 image=DestroyImageList(image);
5510 MngInfoFreeStruct(mng_info,&have_mng_structure);
5511 return((Image *) NULL);
5514 image=SyncNextImageInList(image);
5517 mng_info->image=image;
5519 if (term_chunk_found)
5521 image->start_loop=MagickTrue;
5522 image->iterations=mng_iterations;
5523 term_chunk_found=MagickFalse;
5527 image->start_loop=MagickFalse;
5530 image->columns=subframe_width;
5531 image->rows=subframe_height;
5532 image->page.width=subframe_width;
5533 image->page.height=subframe_height;
5534 image->page.x=mng_info->clip.left;
5535 image->page.y=mng_info->clip.top;
5536 image->background_color=mng_background_color;
5537 image->matte=MagickFalse;
5538 (void) SetImageBackgroundColor(image);
5540 if (logging != MagickFalse)
5541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5542 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5543 (double) mng_info->clip.left,(double) mng_info->clip.right,
5544 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5546 #endif /* MNG_INSERT_LAYERS */
5547 first_mng_object=MagickFalse;
5549 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5552 Allocate next image structure.
5554 AcquireNextImage(image_info,image);
5556 if (GetNextImageInList(image) == (Image *) NULL)
5558 image=DestroyImageList(image);
5559 MngInfoFreeStruct(mng_info,&have_mng_structure);
5560 return((Image *) NULL);
5563 image=SyncNextImageInList(image);
5565 mng_info->image=image;
5566 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5567 GetBlobSize(image));
5569 if (status == MagickFalse)
5572 if (term_chunk_found)
5574 image->start_loop=MagickTrue;
5575 term_chunk_found=MagickFalse;
5579 image->start_loop=MagickFalse;
5581 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5583 image->delay=frame_delay;
5584 frame_delay=default_frame_delay;
5590 image->page.width=mng_info->mng_width;
5591 image->page.height=mng_info->mng_height;
5592 image->page.x=mng_info->x_off[object_id];
5593 image->page.y=mng_info->y_off[object_id];
5594 image->iterations=mng_iterations;
5597 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5600 if (logging != MagickFalse)
5601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5602 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5605 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5608 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5612 mng_info->image=image;
5613 mng_info->mng_type=mng_type;
5614 mng_info->object_id=object_id;
5616 if (memcmp(type,mng_IHDR,4) == 0)
5617 image=ReadOnePNGImage(mng_info,image_info,exception);
5619 #if defined(JNG_SUPPORTED)
5621 image=ReadOneJNGImage(mng_info,image_info,exception);
5624 if (image == (Image *) NULL)
5626 if (IsImageObject(previous) != MagickFalse)
5628 (void) DestroyImageList(previous);
5629 (void) CloseBlob(previous);
5632 MngInfoFreeStruct(mng_info,&have_mng_structure);
5633 return((Image *) NULL);
5636 if (image->columns == 0 || image->rows == 0)
5638 (void) CloseBlob(image);
5639 image=DestroyImageList(image);
5640 MngInfoFreeStruct(mng_info,&have_mng_structure);
5641 return((Image *) NULL);
5644 mng_info->image=image;
5651 if (mng_info->magn_methx || mng_info->magn_methy)
5657 if (logging != MagickFalse)
5658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5659 " Processing MNG MAGN chunk");
5661 if (mng_info->magn_methx == 1)
5663 magnified_width=mng_info->magn_ml;
5665 if (image->columns > 1)
5666 magnified_width += mng_info->magn_mr;
5668 if (image->columns > 2)
5669 magnified_width += (png_uint_32)
5670 ((image->columns-2)*(mng_info->magn_mx));
5675 magnified_width=(png_uint_32) image->columns;
5677 if (image->columns > 1)
5678 magnified_width += mng_info->magn_ml-1;
5680 if (image->columns > 2)
5681 magnified_width += mng_info->magn_mr-1;
5683 if (image->columns > 3)
5684 magnified_width += (png_uint_32)
5685 ((image->columns-3)*(mng_info->magn_mx-1));
5688 if (mng_info->magn_methy == 1)
5690 magnified_height=mng_info->magn_mt;
5692 if (image->rows > 1)
5693 magnified_height += mng_info->magn_mb;
5695 if (image->rows > 2)
5696 magnified_height += (png_uint_32)
5697 ((image->rows-2)*(mng_info->magn_my));
5702 magnified_height=(png_uint_32) image->rows;
5704 if (image->rows > 1)
5705 magnified_height += mng_info->magn_mt-1;
5707 if (image->rows > 2)
5708 magnified_height += mng_info->magn_mb-1;
5710 if (image->rows > 3)
5711 magnified_height += (png_uint_32)
5712 ((image->rows-3)*(mng_info->magn_my-1));
5715 if (magnified_height > image->rows ||
5716 magnified_width > image->columns)
5731 register PixelPacket
5743 /* Allocate next image structure. */
5745 if (logging != MagickFalse)
5746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5747 " Allocate magnified image");
5749 AcquireNextImage(image_info,image);
5751 if (GetNextImageInList(image) == (Image *) NULL)
5753 image=DestroyImageList(image);
5754 MngInfoFreeStruct(mng_info,&have_mng_structure);
5755 return((Image *) NULL);
5758 large_image=SyncNextImageInList(image);
5760 large_image->columns=magnified_width;
5761 large_image->rows=magnified_height;
5763 magn_methx=mng_info->magn_methx;
5764 magn_methy=mng_info->magn_methy;
5766 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
5767 #define QM unsigned short
5768 if (magn_methx != 1 || magn_methy != 1)
5771 Scale pixels to unsigned shorts to prevent
5772 overflow of intermediate values of interpolations
5774 for (y=0; y < (ssize_t) image->rows; y++)
5776 q=GetAuthenticPixels(image,0,y,image->columns,1,
5779 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5781 SetRedPixelComponent(q,ScaleQuantumToShort(
5782 GetRedPixelComponent(q));
5783 SetGreenPixelComponent(q,ScaleQuantumToShort(
5784 GetGreenPixelComponent(q));
5785 SetBluePixelComponent(q,ScaleQuantumToShort(
5786 GetBluePixelComponent(q));
5787 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5788 GetOpacityPixelComponent(q));
5792 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5800 if (image->matte != MagickFalse)
5801 (void) SetImageBackgroundColor(large_image);
5805 large_image->background_color.opacity=OpaqueOpacity;
5806 (void) SetImageBackgroundColor(large_image);
5808 if (magn_methx == 4)
5811 if (magn_methx == 5)
5814 if (magn_methy == 4)
5817 if (magn_methy == 5)
5821 /* magnify the rows into the right side of the large image */
5823 if (logging != MagickFalse)
5824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5825 " Magnify the rows to %.20g",(double) large_image->rows);
5826 m=(ssize_t) mng_info->magn_mt;
5828 length=(size_t) image->columns;
5829 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5830 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5832 if ((prev == (PixelPacket *) NULL) ||
5833 (next == (PixelPacket *) NULL))
5835 image=DestroyImageList(image);
5836 MngInfoFreeStruct(mng_info,&have_mng_structure);
5837 ThrowReaderException(ResourceLimitError,
5838 "MemoryAllocationFailed");
5841 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5842 (void) CopyMagickMemory(next,n,length);
5844 for (y=0; y < (ssize_t) image->rows; y++)
5847 m=(ssize_t) mng_info->magn_mt;
5849 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5850 m=(ssize_t) mng_info->magn_mb;
5852 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5853 m=(ssize_t) mng_info->magn_mb;
5855 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5859 m=(ssize_t) mng_info->magn_my;
5865 if (y < (ssize_t) image->rows-1)
5867 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5869 (void) CopyMagickMemory(next,n,length);
5872 for (i=0; i < m; i++, yy++)
5874 /* To do: Rewrite using Get/Set***PixelComponent() */
5875 register PixelPacket
5878 assert(yy < (ssize_t) large_image->rows);
5881 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5883 q+=(large_image->columns-image->columns);
5885 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5887 /* To do: get color as function of indexes[x] */
5889 if (image->storage_class == PseudoClass)
5894 if (magn_methy <= 1)
5896 *q=(*pixels); /* replicate previous */
5899 else if (magn_methy == 2 || magn_methy == 4)
5907 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5908 -(*pixels).red)+m))/((ssize_t) (m*2))
5910 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5911 -(*pixels).green)+m))/((ssize_t) (m*2))
5913 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5914 -(*pixels).blue)+m))/((ssize_t) (m*2))
5917 if (image->matte != MagickFalse)
5918 (*q).opacity=(QM) (((ssize_t)
5920 -(*pixels).opacity)+m))
5921 /((ssize_t) (m*2))+(*pixels).opacity);
5924 if (magn_methy == 4)
5926 /* Replicate nearest */
5927 if (i <= ((m+1) << 1))
5928 (*q).opacity=(*pixels).opacity+0;
5930 (*q).opacity=(*n).opacity+0;
5934 else /* if (magn_methy == 3 || magn_methy == 5) */
5936 /* Replicate nearest */
5937 if (i <= ((m+1) << 1))
5943 if (magn_methy == 5)
5945 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5946 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5947 +(*pixels).opacity);
5955 if (SyncAuthenticPixels(large_image,exception) == 0)
5961 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5962 next=(PixelPacket *) RelinquishMagickMemory(next);
5964 length=image->columns;
5966 if (logging != MagickFalse)
5967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5968 " Delete original image");
5970 DeleteImageFromList(&image);
5974 mng_info->image=image;
5976 /* magnify the columns */
5977 if (logging != MagickFalse)
5978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5979 " Magnify the columns to %.20g",(double) image->columns);
5981 for (y=0; y < (ssize_t) image->rows; y++)
5983 register PixelPacket
5986 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5987 pixels=q+(image->columns-length);
5990 for (x=(ssize_t) (image->columns-length);
5991 x < (ssize_t) image->columns; x++)
5993 /* To do: Rewrite using Get/Set***PixelComponent() */
5995 if (x == (ssize_t) (image->columns-length))
5996 m=(ssize_t) mng_info->magn_ml;
5998 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5999 m=(ssize_t) mng_info->magn_mr;
6001 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6002 m=(ssize_t) mng_info->magn_mr;
6004 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6008 m=(ssize_t) mng_info->magn_mx;
6010 for (i=0; i < m; i++)
6012 if (magn_methx <= 1)
6014 /* replicate previous */
6018 else if (magn_methx == 2 || magn_methx == 4)
6026 (*q).red=(QM) ((2*i*((*n).red
6028 /((ssize_t) (m*2))+(*pixels).red);
6029 (*q).green=(QM) ((2*i*((*n).green
6031 +m)/((ssize_t) (m*2))+(*pixels).green);
6032 (*q).blue=(QM) ((2*i*((*n).blue
6034 /((ssize_t) (m*2))+(*pixels).blue);
6035 if (image->matte != MagickFalse)
6036 (*q).opacity=(QM) ((2*i*((*n).opacity
6037 -(*pixels).opacity)+m)/((ssize_t) (m*2))
6038 +(*pixels).opacity);
6041 if (magn_methx == 4)
6043 /* Replicate nearest */
6044 if (i <= ((m+1) << 1))
6045 (*q).opacity=(*pixels).opacity+0;
6047 (*q).opacity=(*n).opacity+0;
6051 else /* if (magn_methx == 3 || magn_methx == 5) */
6053 /* Replicate nearest */
6054 if (i <= ((m+1) << 1))
6060 if (magn_methx == 5)
6063 (*q).opacity=(QM) ((2*i*((*n).opacity
6064 -(*pixels).opacity)+m) /((ssize_t) (m*2))
6065 +(*pixels).opacity);
6074 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6077 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6078 if (magn_methx != 1 || magn_methy != 1)
6081 Rescale pixels to Quantum
6083 for (y=0; y < (ssize_t) image->rows; y++)
6085 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6087 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6089 SetRedPixelComponent(q,ScaleShortToQuantum(
6090 GetRedPixelComponent(q));
6091 SetGreenPixelComponent(q,ScaleShortToQuantum(
6092 GetGreenPixelComponent(q));
6093 SetBluePixelComponent(q,ScaleShortToQuantum(
6094 GetBluePixelComponent(q));
6095 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6096 GetOpacityPixelComponent(q));
6100 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6105 if (logging != MagickFalse)
6106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6107 " Finished MAGN processing");
6112 Crop_box is with respect to the upper left corner of the MNG.
6114 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6115 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6116 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6117 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6118 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6119 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6120 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6121 if ((crop_box.left != (mng_info->image_box.left
6122 +mng_info->x_off[object_id])) ||
6123 (crop_box.right != (mng_info->image_box.right
6124 +mng_info->x_off[object_id])) ||
6125 (crop_box.top != (mng_info->image_box.top
6126 +mng_info->y_off[object_id])) ||
6127 (crop_box.bottom != (mng_info->image_box.bottom
6128 +mng_info->y_off[object_id])))
6130 if (logging != MagickFalse)
6131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6132 " Crop the PNG image");
6134 if ((crop_box.left < crop_box.right) &&
6135 (crop_box.top < crop_box.bottom))
6144 Crop_info is with respect to the upper left corner of
6147 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6148 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6149 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6150 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6151 image->page.width=image->columns;
6152 image->page.height=image->rows;
6155 im=CropImage(image,&crop_info,exception);
6157 if (im != (Image *) NULL)
6159 image->columns=im->columns;
6160 image->rows=im->rows;
6161 im=DestroyImage(im);
6162 image->page.width=image->columns;
6163 image->page.height=image->rows;
6164 image->page.x=crop_box.left;
6165 image->page.y=crop_box.top;
6172 No pixels in crop area. The MNG spec still requires
6173 a layer, though, so make a single transparent pixel in
6174 the top left corner.
6179 (void) SetImageBackgroundColor(image);
6180 image->page.width=1;
6181 image->page.height=1;
6186 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6187 image=mng_info->image;
6191 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6192 /* PNG does not handle depths greater than 16 so reduce it even
6195 if (image->depth > 16)
6199 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6200 if (LosslessReduceDepthOK(image) != MagickFalse)
6204 GetImageException(image,exception);
6206 if (image_info->number_scenes != 0)
6208 if (mng_info->scenes_found >
6209 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6213 if (logging != MagickFalse)
6214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6215 " Finished reading image datastream.");
6217 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6219 (void) CloseBlob(image);
6221 if (logging != MagickFalse)
6222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6223 " Finished reading all image datastreams.");
6225 #if defined(MNG_INSERT_LAYERS)
6226 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6227 (mng_info->mng_height))
6230 Insert a background layer if nothing else was found.
6232 if (logging != MagickFalse)
6233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6234 " No images found. Inserting a background layer.");
6236 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6239 Allocate next image structure.
6241 AcquireNextImage(image_info,image);
6242 if (GetNextImageInList(image) == (Image *) NULL)
6244 image=DestroyImageList(image);
6245 MngInfoFreeStruct(mng_info,&have_mng_structure);
6247 if (logging != MagickFalse)
6248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6249 " Allocation failed, returning NULL.");
6251 return((Image *) NULL);
6253 image=SyncNextImageInList(image);
6255 image->columns=mng_info->mng_width;
6256 image->rows=mng_info->mng_height;
6257 image->page.width=mng_info->mng_width;
6258 image->page.height=mng_info->mng_height;
6261 image->background_color=mng_background_color;
6262 image->matte=MagickFalse;
6264 if (image_info->ping == MagickFalse)
6265 (void) SetImageBackgroundColor(image);
6267 mng_info->image_found++;
6270 image->iterations=mng_iterations;
6272 if (mng_iterations == 1)
6273 image->start_loop=MagickTrue;
6275 while (GetPreviousImageInList(image) != (Image *) NULL)
6278 if (image_count > 10*mng_info->image_found)
6280 if (logging != MagickFalse)
6281 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6283 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6284 CoderError,"Linked list is corrupted, beginning of list not found",
6285 "`%s'",image_info->filename);
6287 return((Image *) NULL);
6290 image=GetPreviousImageInList(image);
6292 if (GetNextImageInList(image) == (Image *) NULL)
6294 if (logging != MagickFalse)
6295 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6297 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6298 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6299 image_info->filename);
6303 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6304 GetNextImageInList(image) ==
6307 if (logging != MagickFalse)
6308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6309 " First image null");
6311 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6312 CoderError,"image->next for first image is NULL but shouldn't be.",
6313 "`%s'",image_info->filename);
6316 if (mng_info->image_found == 0)
6318 if (logging != MagickFalse)
6319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6320 " No visible images found.");
6322 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6323 CoderError,"No visible images in file","`%s'",image_info->filename);
6325 if (image != (Image *) NULL)
6326 image=DestroyImageList(image);
6328 MngInfoFreeStruct(mng_info,&have_mng_structure);
6329 return((Image *) NULL);
6332 if (mng_info->ticks_per_second)
6333 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6334 final_delay/mng_info->ticks_per_second;
6337 image->start_loop=MagickTrue;
6339 /* Find final nonzero image delay */
6340 final_image_delay=0;
6342 while (GetNextImageInList(image) != (Image *) NULL)
6345 final_image_delay=image->delay;
6347 image=GetNextImageInList(image);
6350 if (final_delay < final_image_delay)
6351 final_delay=final_image_delay;
6353 image->delay=final_delay;
6355 if (logging != MagickFalse)
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6357 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6358 (double) final_delay);
6360 if (logging != MagickFalse)
6366 image=GetFirstImageInList(image);
6368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6369 " Before coalesce:");
6371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6372 " scene 0 delay=%.20g",(double) image->delay);
6374 while (GetNextImageInList(image) != (Image *) NULL)
6376 image=GetNextImageInList(image);
6377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6378 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6382 image=GetFirstImageInList(image);
6383 #ifdef MNG_COALESCE_LAYERS
6393 if (logging != MagickFalse)
6394 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6397 next_image=CoalesceImages(image,&image->exception);
6399 if (next_image == (Image *) NULL)
6400 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6402 image=DestroyImageList(image);
6405 for (next=image; next != (Image *) NULL; next=next_image)
6407 next->page.width=mng_info->mng_width;
6408 next->page.height=mng_info->mng_height;
6411 next->scene=scene++;
6412 next_image=GetNextImageInList(next);
6414 if (next_image == (Image *) NULL)
6417 if (next->delay == 0)
6420 next_image->previous=GetPreviousImageInList(next);
6421 if (GetPreviousImageInList(next) == (Image *) NULL)
6424 next->previous->next=next_image;
6425 next=DestroyImage(next);
6431 while (GetNextImageInList(image) != (Image *) NULL)
6432 image=GetNextImageInList(image);
6434 image->dispose=BackgroundDispose;
6436 if (logging != MagickFalse)
6442 image=GetFirstImageInList(image);
6444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6445 " After coalesce:");
6447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6448 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6449 (double) image->dispose);
6451 while (GetNextImageInList(image) != (Image *) NULL)
6453 image=GetNextImageInList(image);
6455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6456 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6457 (double) image->delay,(double) image->dispose);
6461 image=GetFirstImageInList(image);
6462 MngInfoFreeStruct(mng_info,&have_mng_structure);
6463 have_mng_structure=MagickFalse;
6465 if (logging != MagickFalse)
6466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6468 return(GetFirstImageInList(image));
6470 #else /* PNG_LIBPNG_VER > 10011 */
6471 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6473 printf("Your PNG library is too old: You have libpng-%s\n",
6474 PNG_LIBPNG_VER_STRING);
6476 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6477 "PNG library is too old","`%s'",image_info->filename);
6479 return(Image *) NULL;
6482 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6484 return(ReadPNGImage(image_info,exception));
6486 #endif /* PNG_LIBPNG_VER > 10011 */
6490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6494 % R e g i s t e r P N G I m a g e %
6498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6500 % RegisterPNGImage() adds properties for the PNG image format to
6501 % the list of supported formats. The properties include the image format
6502 % tag, a method to read and/or write the format, whether the format
6503 % supports the saving of more than one frame to the same file or blob,
6504 % whether the format supports native in-memory I/O, and a brief
6505 % description of the format.
6507 % The format of the RegisterPNGImage method is:
6509 % size_t RegisterPNGImage(void)
6512 ModuleExport size_t RegisterPNGImage(void)
6515 version[MaxTextExtent];
6523 "See http://www.libpng.org/ for details about the PNG format."
6528 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6534 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6540 #if defined(PNG_LIBPNG_VER_STRING)
6541 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6542 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6544 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6546 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6547 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6552 entry=SetMagickInfo("MNG");
6553 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6555 #if defined(MAGICKCORE_PNG_DELEGATE)
6556 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6557 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6560 entry->magick=(IsImageFormatHandler *) IsMNG;
6561 entry->description=ConstantString("Multiple-image Network Graphics");
6563 if (*version != '\0')
6564 entry->version=ConstantString(version);
6566 entry->module=ConstantString("PNG");
6567 entry->note=ConstantString(MNGNote);
6568 (void) RegisterMagickInfo(entry);
6570 entry=SetMagickInfo("PNG");
6572 #if defined(MAGICKCORE_PNG_DELEGATE)
6573 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6574 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6577 entry->magick=(IsImageFormatHandler *) IsPNG;
6578 entry->adjoin=MagickFalse;
6579 entry->description=ConstantString("Portable Network Graphics");
6580 entry->module=ConstantString("PNG");
6582 if (*version != '\0')
6583 entry->version=ConstantString(version);
6585 entry->note=ConstantString(PNGNote);
6586 (void) RegisterMagickInfo(entry);
6588 entry=SetMagickInfo("PNG8");
6590 #if defined(MAGICKCORE_PNG_DELEGATE)
6591 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6592 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6595 entry->magick=(IsImageFormatHandler *) IsPNG;
6596 entry->adjoin=MagickFalse;
6597 entry->description=ConstantString(
6598 "8-bit indexed with optional binary transparency");
6599 entry->module=ConstantString("PNG");
6600 (void) RegisterMagickInfo(entry);
6602 entry=SetMagickInfo("PNG24");
6605 #if defined(ZLIB_VERSION)
6606 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6607 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6609 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6611 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6612 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6616 if (*version != '\0')
6617 entry->version=ConstantString(version);
6619 #if defined(MAGICKCORE_PNG_DELEGATE)
6620 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6621 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6624 entry->magick=(IsImageFormatHandler *) IsPNG;
6625 entry->adjoin=MagickFalse;
6626 entry->description=ConstantString("opaque 24-bit RGB");
6627 entry->module=ConstantString("PNG");
6628 (void) RegisterMagickInfo(entry);
6630 entry=SetMagickInfo("PNG32");
6632 #if defined(MAGICKCORE_PNG_DELEGATE)
6633 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6634 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6637 entry->magick=(IsImageFormatHandler *) IsPNG;
6638 entry->adjoin=MagickFalse;
6639 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6640 entry->module=ConstantString("PNG");
6641 (void) RegisterMagickInfo(entry);
6643 entry=SetMagickInfo("JNG");
6645 #if defined(JNG_SUPPORTED)
6646 #if defined(MAGICKCORE_PNG_DELEGATE)
6647 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6648 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6652 entry->magick=(IsImageFormatHandler *) IsJNG;
6653 entry->adjoin=MagickFalse;
6654 entry->description=ConstantString("JPEG Network Graphics");
6655 entry->module=ConstantString("PNG");
6656 entry->note=ConstantString(JNGNote);
6657 (void) RegisterMagickInfo(entry);
6659 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6660 ping_semaphore=AllocateSemaphoreInfo();
6663 return(MagickImageCoderSignature);
6667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6671 % U n r e g i s t e r P N G I m a g e %
6675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6677 % UnregisterPNGImage() removes format registrations made by the
6678 % PNG module from the list of supported formats.
6680 % The format of the UnregisterPNGImage method is:
6682 % UnregisterPNGImage(void)
6685 ModuleExport void UnregisterPNGImage(void)
6687 (void) UnregisterMagickInfo("MNG");
6688 (void) UnregisterMagickInfo("PNG");
6689 (void) UnregisterMagickInfo("PNG8");
6690 (void) UnregisterMagickInfo("PNG24");
6691 (void) UnregisterMagickInfo("PNG32");
6692 (void) UnregisterMagickInfo("JNG");
6694 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6695 if (ping_semaphore != (SemaphoreInfo *) NULL)
6696 DestroySemaphoreInfo(&ping_semaphore);
6700 #if defined(MAGICKCORE_PNG_DELEGATE)
6701 #if PNG_LIBPNG_VER > 10011
6703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6707 % W r i t e M N G I m a g e %
6711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6713 % WriteMNGImage() writes an image in the Portable Network Graphics
6714 % Group's "Multiple-image Network Graphics" encoded image format.
6716 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6718 % The format of the WriteMNGImage method is:
6720 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6722 % A description of each parameter follows.
6724 % o image_info: the image info.
6726 % o image: The image.
6729 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6730 % "To do" under ReadPNGImage):
6732 % Preserve all unknown and not-yet-handled known chunks found in input
6733 % PNG file and copy them into output PNG files according to the PNG
6736 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6738 % Improve selection of color type (use indexed-colour or indexed-colour
6739 % with tRNS when 256 or fewer unique RGBA values are present).
6741 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6742 % This will be complicated if we limit ourselves to generating MNG-LC
6743 % files. For now we ignore disposal method 3 and simply overlay the next
6746 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6747 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6748 % [mostly done 15 June 1999 but still need to take care of tRNS]
6750 % Check for identical sRGB and replace with a global sRGB (and remove
6751 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6752 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6753 % local gAMA/cHRM with local sRGB if appropriate).
6755 % Check for identical sBIT chunks and write global ones.
6757 % Provide option to skip writing the signature tEXt chunks.
6759 % Use signatures to detect identical objects and reuse the first
6760 % instance of such objects instead of writing duplicate objects.
6762 % Use a smaller-than-32k value of compression window size when
6765 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6766 % ancillary text chunks and save profiles.
6768 % Provide an option to force LC files (to ensure exact framing rate)
6771 % Provide an option to force VLC files instead of LC, even when offsets
6772 % are present. This will involve expanding the embedded images with a
6773 % transparent region at the top and/or left.
6777 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6778 png_info *ping_info, unsigned char *profile_type, unsigned char
6779 *profile_description, unsigned char *profile_data, png_uint_32 length)
6798 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6800 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6803 if (image_info->verbose)
6805 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6806 (char *) profile_type, (double) length);
6809 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6810 description_length=(png_uint_32) strlen((const char *) profile_description);
6811 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6812 + description_length);
6813 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6814 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6815 text[0].key[0]='\0';
6816 (void) ConcatenateMagickString(text[0].key,
6817 "Raw profile type ",MaxTextExtent);
6818 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6822 (void) CopyMagickString(dp,(const char *) profile_description,
6824 dp+=description_length;
6826 (void) FormatMagickString(dp,allocated_length-
6827 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6830 for (i=0; i < (ssize_t) length; i++)
6834 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6835 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6840 text[0].text_length=(png_size_t) (dp-text[0].text);
6841 text[0].compression=image_info->compression == NoCompression ||
6842 (image_info->compression == UndefinedCompression &&
6843 text[0].text_length < 128) ? -1 : 0;
6845 if (text[0].text_length <= allocated_length)
6846 png_set_text(ping,ping_info,text,1);
6848 png_free(ping,text[0].text);
6849 png_free(ping,text[0].key);
6850 png_free(ping,text);
6853 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6854 const char *string, MagickBooleanType logging)
6867 ResetImageProfileIterator(image);
6869 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6871 profile=GetImageProfile(image,name);
6873 if (profile != (const StringInfo *) NULL)
6878 if (LocaleNCompare(name,string,11) == 0)
6880 if (logging != MagickFalse)
6881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6882 " Found %s profile",name);
6884 ping_profile=CloneStringInfo(profile);
6885 data=GetStringInfoDatum(ping_profile),
6886 length=(png_uint_32) GetStringInfoLength(ping_profile);
6891 (void) WriteBlobMSBULong(image,length-5); /* data length */
6892 (void) WriteBlob(image,length-1,data+1);
6893 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6894 ping_profile=DestroyStringInfo(ping_profile);
6898 name=GetNextImageProfile(image);
6905 /* Write one PNG image */
6906 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6907 const ImageInfo *IMimage_info,Image *IMimage)
6931 ping_trans_alpha[256];
6959 ping_have_cheap_transparency,
6970 /* ping_exclude_EXIF, */
6973 /* ping_exclude_iTXt, */
6978 /* ping_exclude_tRNS, */
6980 ping_exclude_zCCP, /* hex-encoded iCCP */
6983 ping_preserve_colormap,
6984 ping_need_colortype_warning,
7004 ping_interlace_method,
7005 ping_compression_method,
7022 number_semitransparent,
7024 ping_pHYs_unit_type;
7027 ping_pHYs_x_resolution,
7028 ping_pHYs_y_resolution;
7030 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7031 " Enter WriteOnePNGImage()");
7033 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7034 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7035 if (image_info == (ImageInfo *) NULL)
7036 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7038 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7039 LockSemaphoreInfo(ping_semaphore);
7042 /* Initialize some stuff */
7045 ping_interlace_method=0,
7046 ping_compression_method=0,
7047 ping_filter_method=0,
7050 ping_background.red = 0;
7051 ping_background.green = 0;
7052 ping_background.blue = 0;
7053 ping_background.gray = 0;
7054 ping_background.index = 0;
7056 ping_trans_color.red=0;
7057 ping_trans_color.green=0;
7058 ping_trans_color.blue=0;
7059 ping_trans_color.gray=0;
7061 ping_pHYs_unit_type = 0;
7062 ping_pHYs_x_resolution = 0;
7063 ping_pHYs_y_resolution = 0;
7065 ping_have_blob=MagickFalse;
7066 ping_have_color=MagickTrue;
7067 ping_have_non_bw=MagickTrue;
7068 ping_have_PLTE=MagickFalse;
7069 ping_have_bKGD=MagickFalse;
7070 ping_have_pHYs=MagickFalse;
7071 ping_have_tRNS=MagickFalse;
7073 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7074 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7075 ping_exclude_date=mng_info->ping_exclude_date;
7076 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7077 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7078 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7079 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7080 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7081 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7082 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7083 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7084 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7085 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7086 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7087 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7089 ping_preserve_colormap = mng_info->ping_preserve_colormap;
7090 ping_need_colortype_warning = MagickFalse;
7093 number_semitransparent = 0;
7094 number_transparent = 0;
7096 if (logging != MagickFalse)
7098 if (image->storage_class == UndefinedClass)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " storage_class=UndefinedClass");
7101 if (image->storage_class == DirectClass)
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " storage_class=DirectClass");
7104 if (image->storage_class == PseudoClass)
7105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7106 " storage_class=PseudoClass");
7109 if (ping_preserve_colormap == MagickFalse)
7111 if (image->storage_class != PseudoClass && image->colormap != NULL)
7113 /* Free the bogus colormap; it can cause trouble later */
7114 if (logging != MagickFalse)
7115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7116 " Freeing bogus colormap");
7117 (void *) RelinquishMagickMemory(image->colormap);
7118 image->colormap=NULL;
7122 if (image->colorspace != RGBColorspace)
7123 (void) TransformImageColorspace(image,RGBColorspace);
7126 Sometimes we get PseudoClass images whose RGB values don't match
7127 the colors in the colormap. This code syncs the RGB values.
7129 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7130 (void) SyncImage(image);
7132 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7133 if (image->depth > 8)
7135 if (logging != MagickFalse)
7136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7137 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7143 #if 0 /* To do: Option to use the original colormap */
7144 if (ping_preserve_colormap != MagickFalse)
7149 #if 0 /* To do: respect the -depth option */
7150 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7155 /* To do: set to next higher multiple of 8 */
7156 if (image->depth < 8)
7159 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7160 /* PNG does not handle depths greater than 16 so reduce it even
7163 if (image->depth > 16)
7167 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7168 if (image->depth == 16 && mng_info->write_png_depth != 16)
7169 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
7173 /* Normally we run this just once, but in the case of writing PNG8
7174 * we reduce the transparency to binary and run again, then if there
7175 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7176 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7177 * palette. The final reduction can only fail if there are still 256
7178 * colors present and one of them has both transparent and opaque instances.
7181 tried_333 = MagickFalse;
7182 tried_444 = MagickFalse;
7188 * Sometimes we get DirectClass images that have 256 colors or fewer.
7189 * This code will build a colormap.
7191 * Also, sometimes we get PseudoClass images with an out-of-date
7192 * colormap. This code will replace the colormap with a new one.
7193 * Sometimes we get PseudoClass images that have more than 256 colors.
7194 * This code will delete the colormap and change the image to
7197 * If image->matte is MagickFalse, we ignore the opacity channel
7198 * even though it sometimes contains left-over non-opaque values.
7200 * Also we gather some information (number of opaque, transparent,
7201 * and semitransparent pixels, and whether the image has any non-gray
7202 * pixels or only black-and-white pixels) that we might need later.
7204 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7205 * we need to check for bogus non-opaque values, at least.
7216 semitransparent[260],
7219 register IndexPacket
7222 register const PixelPacket
7226 register PixelPacket
7229 if (logging != MagickFalse)
7230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231 " Enter BUILD_PALETTE:");
7233 if (logging != MagickFalse)
7235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236 " image->columns=%.20g",(double) image->columns);
7237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7238 " image->rows=%.20g",(double) image->rows);
7239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7240 " image->matte=%.20g",(double) image->matte);
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242 " image->depth=%.20g",(double) image->depth);
7244 if (image->storage_class == PseudoClass && image->colormap != NULL)
7246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7247 " Original colormap:");
7248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7249 " i (red,green,blue,opacity)");
7251 for (i=0; i < 256; i++)
7253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " %d (%d,%d,%d,%d)",
7256 (int) image->colormap[i].red,
7257 (int) image->colormap[i].green,
7258 (int) image->colormap[i].blue,
7259 (int) image->colormap[i].opacity);
7262 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " %d (%d,%d,%d,%d)",
7269 (int) image->colormap[i].red,
7270 (int) image->colormap[i].green,
7271 (int) image->colormap[i].blue,
7272 (int) image->colormap[i].opacity);
7277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7278 " image->colors=%d",(int) image->colors);
7280 if (image->colors == 0)
7281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7282 " (zero means unknown)");
7284 if (ping_preserve_colormap == MagickFalse)
7285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7286 " Regenerate the colormap");
7289 exception=(&image->exception);
7293 number_semitransparent = 0;
7294 number_transparent = 0;
7296 for (y=0; y < (ssize_t) image->rows; y++)
7298 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7300 if (q == (PixelPacket *) NULL)
7303 for (x=0; x < (ssize_t) image->columns; x++)
7305 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7307 if (number_opaque < 259)
7309 if (number_opaque == 0)
7311 opaque[0].red=GetRedPixelComponent(q);
7312 opaque[0].green=GetGreenPixelComponent(q);
7313 opaque[0].blue=GetBluePixelComponent(q);
7314 opaque[0].opacity=OpaqueOpacity;
7318 for (i=0; i< (ssize_t) number_opaque; i++)
7320 if (IsColorEqual(q, opaque+i))
7324 if (i == (ssize_t) number_opaque &&
7325 number_opaque < 259)
7328 opaque[i].red=GetRedPixelComponent(q);
7329 opaque[i].green=GetGreenPixelComponent(q);
7330 opaque[i].blue=GetBluePixelComponent(q);
7331 opaque[i].opacity=OpaqueOpacity;
7335 else if (q->opacity == TransparentOpacity)
7337 if (number_transparent < 259)
7339 if (number_transparent == 0)
7341 transparent[0].red=GetRedPixelComponent(q);
7342 transparent[0].green=GetGreenPixelComponent(q);
7343 transparent[0].blue=GetBluePixelComponent(q);
7344 transparent[0].opacity=GetOpacityPixelComponent(q);
7345 ping_trans_color.red=
7346 (unsigned short) GetRedPixelComponent(q);
7347 ping_trans_color.green=
7348 (unsigned short) GetGreenPixelComponent(q);
7349 ping_trans_color.blue=
7350 (unsigned short) GetBluePixelComponent(q);
7351 ping_trans_color.gray=
7352 (unsigned short) GetRedPixelComponent(q);
7353 number_transparent = 1;
7356 for (i=0; i< (ssize_t) number_transparent; i++)
7358 if (IsColorEqual(q, transparent+i))
7362 if (i == (ssize_t) number_transparent &&
7363 number_transparent < 259)
7365 number_transparent++;
7366 transparent[i].red=GetRedPixelComponent(q);
7367 transparent[i].green=GetGreenPixelComponent(q);
7368 transparent[i].blue=GetBluePixelComponent(q);
7369 transparent[i].opacity=GetOpacityPixelComponent(q);
7375 if (number_semitransparent < 259)
7377 if (number_semitransparent == 0)
7379 semitransparent[0].red=GetRedPixelComponent(q);
7380 semitransparent[0].green=GetGreenPixelComponent(q);
7381 semitransparent[0].blue=GetBluePixelComponent(q);
7382 semitransparent[0].opacity=GetOpacityPixelComponent(q);
7383 number_semitransparent = 1;
7386 for (i=0; i< (ssize_t) number_semitransparent; i++)
7388 if (IsColorEqual(q, semitransparent+i)
7389 && GetOpacityPixelComponent(q) ==
7390 semitransparent[i].opacity)
7394 if (i == (ssize_t) number_semitransparent &&
7395 number_semitransparent < 259)
7397 number_semitransparent++;
7398 semitransparent[i].red=GetRedPixelComponent(q);
7399 semitransparent[i].green=GetGreenPixelComponent(q);
7400 semitransparent[i].blue=GetBluePixelComponent(q);
7401 semitransparent[i].opacity=GetOpacityPixelComponent(q);
7409 if (ping_exclude_bKGD == MagickFalse)
7411 /* Add the background color to the palette, if it
7412 * isn't already there.
7414 if (logging != MagickFalse)
7416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7417 " Check colormap for background (%d,%d,%d)",
7418 (int) image->background_color.red,
7419 (int) image->background_color.green,
7420 (int) image->background_color.blue);
7422 for (i=0; i<number_opaque; i++)
7424 if (opaque[i].red == image->background_color.red &&
7425 opaque[i].green == image->background_color.green &&
7426 opaque[i].blue == image->background_color.blue)
7429 if (number_opaque < 259 && i == number_opaque)
7431 opaque[i].red = image->background_color.red;
7432 opaque[i].green = image->background_color.green;
7433 opaque[i].blue = image->background_color.blue;
7434 ping_background.index = i;
7435 if (logging != MagickFalse)
7437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7438 " background_color index is %d",(int) i);
7442 else if (logging != MagickFalse)
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444 " No room in the colormap to add background color");
7447 image_colors=number_opaque+number_transparent+number_semitransparent;
7449 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7451 /* No room for the background color; remove it. */
7456 if (logging != MagickFalse)
7458 if (image_colors > 256)
7459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7460 " image has more than 256 colors");
7463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7464 " image has %d colors",image_colors);
7467 if (ping_preserve_colormap != MagickFalse)
7470 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7472 ping_have_color=MagickFalse;
7473 ping_have_non_bw=MagickFalse;
7475 if(image_colors > 256)
7477 for (y=0; y < (ssize_t) image->rows; y++)
7479 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7481 if (q == (PixelPacket *) NULL)
7484 /* Worst case is black-and-white; we are looking at every
7488 if (ping_have_color == MagickFalse)
7491 for (x=0; x < (ssize_t) image->columns; x++)
7493 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7494 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
7496 ping_have_color=MagickTrue;
7497 ping_have_non_bw=MagickTrue;
7504 if (ping_have_non_bw == MagickFalse)
7507 for (x=0; x < (ssize_t) image->columns; x++)
7509 if (GetRedPixelComponent(s) != 0 &&
7510 GetRedPixelComponent(s) != QuantumRange)
7512 ping_have_non_bw=MagickTrue;
7521 if (image_colors < 257)
7527 * Initialize image colormap.
7530 if (logging != MagickFalse)
7531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7532 " Sort the new colormap");
7534 /* Sort palette, transparent first */;
7538 for (i=0; i<number_transparent; i++)
7539 colormap[n++] = transparent[i];
7541 for (i=0; i<number_semitransparent; i++)
7542 colormap[n++] = semitransparent[i];
7544 for (i=0; i<number_opaque; i++)
7545 colormap[n++] = opaque[i];
7547 ping_background.index +=
7548 (number_transparent + number_semitransparent);
7550 /* image_colors < 257; search the colormap instead of the pixels
7551 * to get ping_have_color and ping_have_non_bw
7555 if (ping_have_color == MagickFalse)
7557 if (colormap[i].red != colormap[i].green ||
7558 colormap[i].red != colormap[i].blue)
7560 ping_have_color=MagickTrue;
7561 ping_have_non_bw=MagickTrue;
7566 if (ping_have_non_bw == MagickFalse)
7568 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7569 ping_have_non_bw=MagickTrue;
7573 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7574 (number_transparent == 0 && number_semitransparent == 0)) &&
7575 (((mng_info->write_png_colortype-1) ==
7576 PNG_COLOR_TYPE_PALETTE) ||
7577 (mng_info->write_png_colortype == 0)))
7579 if (logging != MagickFalse)
7581 if (n != (ssize_t) image_colors)
7582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7583 " image_colors (%d) and n (%d) don't match",
7586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587 " AcquireImageColormap");
7590 image->colors = image_colors;
7592 if (AcquireImageColormap(image,image_colors) ==
7594 ThrowWriterException(ResourceLimitError,
7595 "MemoryAllocationFailed");
7597 for (i=0; i< (ssize_t) image_colors; i++)
7598 image->colormap[i] = colormap[i];
7600 if (logging != MagickFalse)
7602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7603 " image->colors=%d (%d)",
7604 (int) image->colors, image_colors);
7606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607 " Update the pixel indexes");
7610 /* Sync the pixel indices with the new colormap */
7612 for (y=0; y < (ssize_t) image->rows; y++)
7614 q=GetAuthenticPixels(image,0,y,image->columns,1,
7617 if (q == (PixelPacket *) NULL)
7620 indexes=GetAuthenticIndexQueue(image);
7622 for (x=0; x < (ssize_t) image->columns; x++)
7624 for (i=0; i< (ssize_t) image_colors; i++)
7626 if ((image->matte == MagickFalse ||
7627 image->colormap[i].opacity ==
7628 GetOpacityPixelComponent(q)) &&
7629 image->colormap[i].red ==
7630 GetRedPixelComponent(q) &&
7631 image->colormap[i].green ==
7632 GetGreenPixelComponent(q) &&
7633 image->colormap[i].blue ==
7634 GetBluePixelComponent(q))
7636 indexes[x]=(IndexPacket) i;
7643 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7649 if (logging != MagickFalse)
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " image->colors=%d", (int) image->colors);
7654 if (image->colormap != NULL)
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " i (red,green,blue,opacity)");
7659 for (i=0; i < (ssize_t) image->colors; i++)
7661 if (i < 300 || i >= (ssize_t) image->colors - 10)
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " %d (%d,%d,%d,%d)",
7666 (int) image->colormap[i].red,
7667 (int) image->colormap[i].green,
7668 (int) image->colormap[i].blue,
7669 (int) image->colormap[i].opacity);
7674 if (number_transparent < 257)
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " number_transparent = %d",
7677 number_transparent);
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " number_transparent > 256");
7683 if (number_opaque < 257)
7684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7685 " number_opaque = %d",
7689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7690 " number_opaque > 256");
7692 if (number_semitransparent < 257)
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " number_semitransparent = %d",
7695 number_semitransparent);
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " number_semitransparent > 256");
7701 if (ping_have_non_bw == MagickFalse)
7702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7703 " All pixels and the background are black or white");
7705 else if (ping_have_color == MagickFalse)
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " All pixels and the background are gray");
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " At least one pixel or the background is non-gray");
7713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7714 " Exit BUILD_PALETTE:");
7717 if (mng_info->write_png8 == MagickFalse)
7720 /* Make any reductions necessary for the PNG8 format */
7721 if (image_colors <= 256 &&
7722 image_colors != 0 && image->colormap != NULL &&
7723 number_semitransparent == 0 &&
7724 number_transparent <= 1)
7727 /* PNG8 can't have semitransparent colors so we threshold the
7728 * opacity to 0 or OpaqueOpacity
7730 if (number_semitransparent != 0)
7732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7733 " Thresholding the alpha channel to binary");
7735 for (y=0; y < (ssize_t) image->rows; y++)
7737 r=GetAuthenticPixels(image,0,y,image->columns,1,
7740 if (r == (PixelPacket *) NULL)
7743 for (x=0; x < (ssize_t) image->columns; x++)
7745 SetOpacityPixelComponent(r,
7746 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7747 TransparentOpacity : OpaqueOpacity);
7751 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7754 if (image_colors != 0 && image_colors <= 256 &&
7755 image->colormap != NULL)
7756 for (i=0; i<image_colors; i++)
7757 image->colormap[i].opacity =
7758 image->colormap[i].opacity > TransparentOpacity/2 ?
7759 TransparentOpacity : OpaqueOpacity;
7764 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7765 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7766 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7769 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7771 if (logging != MagickFalse)
7772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7773 " Quantizing the background color to 4-4-4");
7775 tried_444 = MagickTrue;
7777 image->background_color.red=
7779 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7780 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
7781 image->background_color.green=
7783 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7784 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
7785 image->background_color.blue=
7787 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7788 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
7790 if (logging != MagickFalse)
7791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7792 " Quantizing the pixel colors to 4-4-4");
7794 if (image->colormap == NULL)
7796 for (y=0; y < (ssize_t) image->rows; y++)
7798 r=GetAuthenticPixels(image,0,y,image->columns,1,
7801 if (r == (PixelPacket *) NULL)
7804 for (x=0; x < (ssize_t) image->columns; x++)
7806 if (r->opacity == TransparentOpacity)
7808 r->red = image->background_color.red;
7809 r->green = image->background_color.green;
7810 r->blue = image->background_color.blue;
7814 r->red=ScaleCharToQuantum(
7815 (ScaleQuantumToChar(r->red) & 0xf0) |
7816 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7817 r->green=ScaleCharToQuantum(
7818 (ScaleQuantumToChar(r->green) & 0xf0) |
7819 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7820 r->blue=ScaleCharToQuantum(
7821 (ScaleQuantumToChar(r->blue) & 0xf0) |
7822 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
7827 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7832 else /* Should not reach this; colormap already exists and
7835 if (logging != MagickFalse)
7836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7837 " Quantizing the colormap to 4-4-4");
7838 for (i=0; i<image_colors; i++)
7840 image->colormap[i].red=ScaleCharToQuantum(
7841 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7842 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7843 image->colormap[i].green=ScaleCharToQuantum(
7844 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7845 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7846 image->colormap[i].blue=ScaleCharToQuantum(
7847 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7848 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
7854 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7856 if (logging != MagickFalse)
7857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7858 " Quantizing the background color to 3-3-3");
7860 tried_333 = MagickTrue;
7862 image->background_color.red=
7864 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7865 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7866 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
7867 image->background_color.green=
7869 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7870 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7871 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
7872 image->background_color.blue=
7874 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7875 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7876 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
7878 if (logging != MagickFalse)
7879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7880 " Quantizing the pixel colors to 3-3-3-1");
7882 if (image->colormap == NULL)
7884 for (y=0; y < (ssize_t) image->rows; y++)
7886 r=GetAuthenticPixels(image,0,y,image->columns,1,
7889 if (r == (PixelPacket *) NULL)
7892 for (x=0; x < (ssize_t) image->columns; x++)
7894 if (r->opacity == TransparentOpacity)
7896 r->red = image->background_color.red;
7897 r->green = image->background_color.green;
7898 r->blue = image->background_color.blue;
7902 r->red=ScaleCharToQuantum(
7903 (ScaleQuantumToChar(r->red) & 0xe0) |
7904 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7905 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7906 r->green=ScaleCharToQuantum(
7907 (ScaleQuantumToChar(r->green) & 0xe0) |
7908 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7909 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7910 r->blue=ScaleCharToQuantum(
7911 (ScaleQuantumToChar(r->blue) & 0xe0) |
7912 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7913 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
7918 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7923 else /* Should not reach this; colormap already exists and
7926 if (logging != MagickFalse)
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928 " Quantizing the colormap to 3-3-3-1");
7929 for (i=0; i<image_colors; i++)
7931 image->colormap[i].red=ScaleCharToQuantum(
7932 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7933 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7934 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7935 image->colormap[i].green=ScaleCharToQuantum(
7936 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7937 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7938 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7939 image->colormap[i].blue=ScaleCharToQuantum(
7940 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7941 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7942 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
7948 if (image_colors == 0 || image_colors > 256)
7950 if (logging != MagickFalse)
7951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7952 " Quantizing the background color to 3-3-2");
7954 /* Red and green were already done so we only quantize the blue
7958 image->background_color.blue=ScaleCharToQuantum(
7959 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7960 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7961 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7962 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
7964 if (logging != MagickFalse)
7965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7966 " Quantizing the pixel colors to 3-3-2-1");
7968 if (image->colormap == NULL)
7970 for (y=0; y < (ssize_t) image->rows; y++)
7972 r=GetAuthenticPixels(image,0,y,image->columns,1,
7975 if (r == (PixelPacket *) NULL)
7978 for (x=0; x < (ssize_t) image->columns; x++)
7980 if (r->opacity == TransparentOpacity)
7982 r->red = image->background_color.red;
7983 r->green = image->background_color.green;
7984 r->blue = image->background_color.blue;
7988 r->blue=ScaleCharToQuantum(
7989 (ScaleQuantumToChar(r->blue) & 0xc0) |
7990 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7991 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7992 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
7997 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8002 else /* Should not reach this; colormap already exists and
8005 if (logging != MagickFalse)
8006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8007 " Quantizing the colormap to 3-3-2-1");
8008 for (i=0; i<image_colors; i++)
8010 image->colormap[i].blue=ScaleCharToQuantum(
8011 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8012 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8013 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8014 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
8021 /* END OF BUILD_PALETTE */
8023 /* If we are excluding the tRNS chunk and there is transparency,
8024 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8027 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8028 (number_transparent != 0 || number_semitransparent != 0))
8030 int colortype=mng_info->write_png_colortype;
8032 if (ping_have_color == MagickFalse)
8033 mng_info->write_png_colortype = 5;
8036 mng_info->write_png_colortype = 7;
8038 if (colortype != 0 &&
8039 mng_info->write_png_colortype != (ssize_t) colortype)
8040 ping_need_colortype_warning=MagickTrue;
8044 /* See if cheap transparency is possible. It is only possible
8045 * when there is a single transparent color, no semitransparent
8046 * color, and no opaque color that has the same RGB components
8047 * as the transparent color. We only need this information if
8048 * we are writing a PNG with colortype 0 or 2, and we have not
8049 * excluded the tRNS chunk.
8051 if (number_transparent == 1 &&
8052 mng_info->write_png_colortype < 4)
8054 ping_have_cheap_transparency = MagickTrue;
8056 if (number_semitransparent != 0)
8057 ping_have_cheap_transparency = MagickFalse;
8059 else if (image_colors == 0 || image_colors > 256 ||
8060 image->colormap == NULL)
8065 register const PixelPacket
8068 exception=(&image->exception);
8070 for (y=0; y < (ssize_t) image->rows; y++)
8072 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8074 if (q == (PixelPacket *) NULL)
8077 for (x=0; x < (ssize_t) image->columns; x++)
8079 if (q->opacity != TransparentOpacity &&
8080 (unsigned short) GetRedPixelComponent(q) ==
8081 ping_trans_color.red &&
8082 (unsigned short) GetGreenPixelComponent(q) ==
8083 ping_trans_color.green &&
8084 (unsigned short) GetBluePixelComponent(q) ==
8085 ping_trans_color.blue)
8087 ping_have_cheap_transparency = MagickFalse;
8094 if (ping_have_cheap_transparency == MagickFalse)
8100 /* Assuming that image->colormap[0] is the one transparent color
8101 * and that all others are opaque.
8103 if (image_colors > 1)
8104 for (i=1; i<image_colors; i++)
8105 if (image->colormap[i].red == image->colormap[0].red &&
8106 image->colormap[i].green == image->colormap[0].green &&
8107 image->colormap[i].blue == image->colormap[0].blue)
8109 ping_have_cheap_transparency = MagickFalse;
8114 if (logging != MagickFalse)
8116 if (ping_have_cheap_transparency == MagickFalse)
8117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8118 " Cheap transparency is not possible.");
8121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8122 " Cheap transparency is possible.");
8126 ping_have_cheap_transparency = MagickFalse;
8128 image_depth=image->depth;
8130 quantum_info = (QuantumInfo *) NULL;
8132 image_colors=(int) image->colors;
8133 image_matte=image->matte;
8135 mng_info->IsPalette=image->storage_class == PseudoClass &&
8136 image_colors <= 256 && image->colormap != NULL;
8138 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8139 (image->colors == 0 || image->colormap == NULL))
8141 image_info=DestroyImageInfo(image_info);
8142 image=DestroyImage(image);
8143 (void) ThrowMagickException(&IMimage->exception,
8144 GetMagickModule(),CoderError,
8145 "Cannot write PNG8 or color-type 3; colormap is NULL",
8146 "`%s'",IMimage->filename);
8147 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8148 UnlockSemaphoreInfo(ping_semaphore);
8150 return(MagickFalse);
8154 Allocate the PNG structures
8156 #ifdef PNG_USER_MEM_SUPPORTED
8157 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8158 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8159 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8162 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8163 MagickPNGErrorHandler,MagickPNGWarningHandler);
8166 if (ping == (png_struct *) NULL)
8167 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8169 ping_info=png_create_info_struct(ping);
8171 if (ping_info == (png_info *) NULL)
8173 png_destroy_write_struct(&ping,(png_info **) NULL);
8174 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8177 png_set_write_fn(ping,image,png_put_data,png_flush_data);
8178 ping_pixels=(unsigned char *) NULL;
8180 if (setjmp(png_jmpbuf(ping)))
8186 if (image_info->verbose)
8187 (void) printf("PNG write has failed.\n");
8189 png_destroy_write_struct(&ping,&ping_info);
8190 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8191 UnlockSemaphoreInfo(ping_semaphore);
8193 if (ping_have_blob != MagickFalse)
8194 (void) CloseBlob(image);
8195 image_info=DestroyImageInfo(image_info);
8196 image=DestroyImage(image);
8197 return(MagickFalse);
8200 Prepare PNG for writing.
8202 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8203 if (mng_info->write_mng)
8204 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8207 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8208 if (mng_info->write_mng)
8209 png_permit_empty_plte(ping,MagickTrue);
8216 ping_width=(png_uint_32) image->columns;
8217 ping_height=(png_uint_32) image->rows;
8219 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8222 if (mng_info->write_png_depth != 0)
8223 image_depth=mng_info->write_png_depth;
8225 /* Adjust requested depth to next higher valid depth if necessary */
8226 if (image_depth > 8)
8229 if ((image_depth > 4) && (image_depth < 8))
8232 if (image_depth == 3)
8235 if (logging != MagickFalse)
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8238 " width=%.20g",(double) ping_width);
8239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240 " height=%.20g",(double) ping_height);
8241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8242 " image_matte=%.20g",(double) image->matte);
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8244 " image->depth=%.20g",(double) image->depth);
8245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8246 " Tentative ping_bit_depth=%.20g",(double) image_depth);
8249 save_image_depth=image_depth;
8250 ping_bit_depth=(png_byte) save_image_depth;
8253 #if defined(PNG_pHYs_SUPPORTED)
8254 if (ping_exclude_pHYs == MagickFalse)
8256 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8257 (!mng_info->write_mng || !mng_info->equal_physs))
8259 if (logging != MagickFalse)
8260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8261 " Setting up pHYs chunk");
8263 if (image->units == PixelsPerInchResolution)
8265 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8266 ping_pHYs_x_resolution=
8267 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8268 ping_pHYs_y_resolution=
8269 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8272 else if (image->units == PixelsPerCentimeterResolution)
8274 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8275 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8276 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8281 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8282 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8283 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8286 if (logging != MagickFalse)
8287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8288 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8289 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8290 (int) ping_pHYs_unit_type);
8291 ping_have_pHYs = MagickTrue;
8296 if (ping_exclude_bKGD == MagickFalse)
8298 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8304 if (ping_bit_depth == 8)
8307 if (ping_bit_depth == 4)
8310 if (ping_bit_depth == 2)
8313 if (ping_bit_depth == 1)
8316 ping_background.red=(png_uint_16)
8317 (ScaleQuantumToShort(image->background_color.red) & mask);
8319 ping_background.green=(png_uint_16)
8320 (ScaleQuantumToShort(image->background_color.green) & mask);
8322 ping_background.blue=(png_uint_16)
8323 (ScaleQuantumToShort(image->background_color.blue) & mask);
8325 ping_background.gray=(png_uint_16) ping_background.green;
8328 if (logging != MagickFalse)
8330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8331 " Setting up bKGD chunk (1)");
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " background_color index is %d",
8334 (int) ping_background.index);
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " ping_bit_depth=%d",ping_bit_depth);
8340 ping_have_bKGD = MagickTrue;
8344 Select the color type.
8349 if (mng_info->IsPalette && mng_info->write_png8)
8352 /* To do: make this a function cause it's used twice, except
8353 for reducing the sample depth from 8. */
8355 number_colors=image_colors;
8357 ping_have_tRNS=MagickFalse;
8362 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8364 if (logging != MagickFalse)
8365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8366 " Setting up PLTE chunk with %d colors (%d)",
8367 number_colors, image_colors);
8369 for (i=0; i < (ssize_t) number_colors; i++)
8371 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8372 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8373 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8374 if (logging != MagickFalse)
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 #if MAGICKCORE_QUANTUM_DEPTH == 8
8377 " %3ld (%3d,%3d,%3d)",
8379 " %5ld (%5d,%5d,%5d)",
8381 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8385 ping_have_PLTE=MagickTrue;
8386 image_depth=ping_bit_depth;
8389 if (matte != MagickFalse)
8392 Identify which colormap entry is transparent.
8394 assert(number_colors <= 256);
8395 assert(image->colormap != NULL);
8397 for (i=0; i < (ssize_t) number_transparent; i++)
8398 ping_trans_alpha[i]=0;
8401 ping_num_trans=(unsigned short) (number_transparent +
8402 number_semitransparent);
8404 if (ping_num_trans == 0)
8405 ping_have_tRNS=MagickFalse;
8408 ping_have_tRNS=MagickTrue;
8411 if (ping_exclude_bKGD == MagickFalse)
8414 * Identify which colormap entry is the background color.
8417 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8418 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8421 ping_background.index=(png_byte) i;
8423 if (logging != MagickFalse)
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " background_color index is %d",
8427 (int) ping_background.index);
8430 } /* end of write_png8 */
8432 else if (mng_info->write_png24)
8434 image_matte=MagickFalse;
8435 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8438 else if (mng_info->write_png32)
8440 image_matte=MagickTrue;
8441 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8444 else /* mng_info->write_pngNN not specified */
8446 image_depth=ping_bit_depth;
8448 if (mng_info->write_png_colortype != 0)
8450 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8452 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8453 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8454 image_matte=MagickTrue;
8457 image_matte=MagickFalse;
8459 if (logging != MagickFalse)
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " PNG colortype %d was specified:",(int) ping_color_type);
8464 else /* write_png_colortype not specified */
8466 if (logging != MagickFalse)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " Selecting PNG colortype:");
8470 ping_color_type=(png_byte) ((matte != MagickFalse)?
8471 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8473 if (image_info->type == TrueColorType)
8475 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8476 image_matte=MagickFalse;
8479 if (image_info->type == TrueColorMatteType)
8481 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8482 image_matte=MagickTrue;
8485 if (image_info->type == PaletteType ||
8486 image_info->type == PaletteMatteType)
8487 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8489 if (mng_info->write_png_colortype == 0 &&
8490 (image_info->type == UndefinedType ||
8491 image_info->type == OptimizeType))
8493 if (ping_have_color == MagickFalse)
8495 if (image_matte == MagickFalse)
8497 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8498 image_matte=MagickFalse;
8503 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8504 image_matte=MagickTrue;
8509 if (image_matte == MagickFalse)
8511 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8512 image_matte=MagickFalse;
8517 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8518 image_matte=MagickTrue;
8525 if (logging != MagickFalse)
8526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8527 " Selected PNG colortype=%d",ping_color_type);
8529 if (ping_bit_depth < 8)
8531 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8532 ping_color_type == PNG_COLOR_TYPE_RGB ||
8533 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8537 old_bit_depth=ping_bit_depth;
8539 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8541 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8545 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8550 if (image->colors == 0)
8553 (void) ThrowMagickException(&image->exception,
8554 GetMagickModule(),CoderError,
8555 "image has 0 colors", "`%s'","");
8558 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8559 ping_bit_depth <<= 1;
8562 if (logging != MagickFalse)
8564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565 " Number of colors: %.20g",(double) image_colors);
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Tentative PNG bit depth: %d",ping_bit_depth);
8571 if (ping_bit_depth < (int) mng_info->write_png_depth)
8572 ping_bit_depth = mng_info->write_png_depth;
8575 image_depth=ping_bit_depth;
8577 if (logging != MagickFalse)
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8580 " Tentative PNG color type: %.20g",(double) ping_color_type);
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " image_info->type: %.20g",(double) image_info->type);
8585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8586 " image_depth: %.20g",(double) image_depth);
8588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " image->depth: %.20g",(double) image->depth);
8592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8593 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8596 if (matte != MagickFalse)
8598 if (mng_info->IsPalette)
8600 if (mng_info->write_png_colortype == 0)
8602 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8604 if (ping_have_color != MagickFalse)
8605 ping_color_type=PNG_COLOR_TYPE_RGBA;
8609 * Determine if there is any transparent color.
8611 if (number_transparent + number_semitransparent == 0)
8614 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8617 image_matte=MagickFalse;
8619 if (mng_info->write_png_colortype == 0)
8620 ping_color_type&=0x03;
8630 if (ping_bit_depth == 8)
8633 if (ping_bit_depth == 4)
8636 if (ping_bit_depth == 2)
8639 if (ping_bit_depth == 1)
8642 ping_trans_color.red=(png_uint_16)
8643 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8645 ping_trans_color.green=(png_uint_16)
8646 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8648 ping_trans_color.blue=(png_uint_16)
8649 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8651 ping_trans_color.gray=(png_uint_16)
8652 (ScaleQuantumToShort(PixelIntensityToQuantum(
8653 image->colormap)) & mask);
8655 ping_trans_color.index=(png_byte) 0;
8657 ping_have_tRNS=MagickTrue;
8660 if (ping_have_tRNS != MagickFalse)
8663 * Determine if there is one and only one transparent color
8664 * and if so if it is fully transparent.
8666 if (ping_have_cheap_transparency == MagickFalse)
8667 ping_have_tRNS=MagickFalse;
8670 if (ping_have_tRNS != MagickFalse)
8672 if (mng_info->write_png_colortype == 0)
8673 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8675 if (image_depth == 8)
8677 ping_trans_color.red&=0xff;
8678 ping_trans_color.green&=0xff;
8679 ping_trans_color.blue&=0xff;
8680 ping_trans_color.gray&=0xff;
8686 if (image_depth == 8)
8688 ping_trans_color.red&=0xff;
8689 ping_trans_color.green&=0xff;
8690 ping_trans_color.blue&=0xff;
8691 ping_trans_color.gray&=0xff;
8698 if (ping_have_tRNS != MagickFalse)
8699 image_matte=MagickFalse;
8701 if ((mng_info->IsPalette) &&
8702 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8703 ping_have_color == MagickFalse &&
8704 (image_matte == MagickFalse || image_depth >= 8))
8708 if (image_matte != MagickFalse)
8709 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8711 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8713 ping_color_type=PNG_COLOR_TYPE_GRAY;
8715 if (save_image_depth == 16 && image_depth == 8)
8717 if (logging != MagickFalse)
8719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8720 " Scaling ping_trans_color (0)");
8722 ping_trans_color.gray*=0x0101;
8726 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8727 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8729 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
8730 image_colors=(int) (one << image_depth);
8732 if (image_depth > 8)
8738 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8740 if(!mng_info->write_png_depth)
8744 while ((int) (one << ping_bit_depth)
8745 < (ssize_t) image_colors)
8746 ping_bit_depth <<= 1;
8750 else if (ping_color_type ==
8751 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8752 mng_info->IsPalette)
8754 /* Check if grayscale is reducible */
8757 depth_4_ok=MagickTrue,
8758 depth_2_ok=MagickTrue,
8759 depth_1_ok=MagickTrue;
8761 for (i=0; i < (ssize_t) image_colors; i++)
8766 intensity=ScaleQuantumToChar(image->colormap[i].red);
8768 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8769 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8770 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8771 depth_2_ok=depth_1_ok=MagickFalse;
8772 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8773 depth_1_ok=MagickFalse;
8776 if (depth_1_ok && mng_info->write_png_depth <= 1)
8779 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8782 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8787 image_depth=ping_bit_depth;
8792 if (mng_info->IsPalette)
8794 number_colors=image_colors;
8796 if (image_depth <= 8)
8801 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8803 if (mng_info->have_write_global_plte && matte == MagickFalse)
8805 png_set_PLTE(ping,ping_info,NULL,0);
8807 if (logging != MagickFalse)
8808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8809 " Setting up empty PLTE chunk");
8814 for (i=0; i < (ssize_t) number_colors; i++)
8816 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8817 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8818 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8821 if (logging != MagickFalse)
8822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8823 " Setting up PLTE chunk with %d colors",
8826 ping_have_PLTE=MagickTrue;
8829 /* color_type is PNG_COLOR_TYPE_PALETTE */
8830 if (mng_info->write_png_depth == 0)
8838 while ((one << ping_bit_depth) < number_colors)
8839 ping_bit_depth <<= 1;
8844 if (matte != MagickFalse)
8847 * Set up trans_colors array.
8849 assert(number_colors <= 256);
8851 ping_num_trans=(unsigned short) (number_transparent +
8852 number_semitransparent);
8854 if (ping_num_trans == 0)
8855 ping_have_tRNS=MagickFalse;
8859 if (logging != MagickFalse)
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " Scaling ping_trans_color (1)");
8864 ping_have_tRNS=MagickTrue;
8866 for (i=0; i < ping_num_trans; i++)
8868 ping_trans_alpha[i]= (png_byte) (255-
8869 ScaleQuantumToChar(image->colormap[i].opacity));
8879 if (image_depth < 8)
8882 if ((save_image_depth == 16) && (image_depth == 8))
8884 if (logging != MagickFalse)
8886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887 " Scaling ping_trans_color from (%d,%d,%d)",
8888 (int) ping_trans_color.red,
8889 (int) ping_trans_color.green,
8890 (int) ping_trans_color.blue);
8893 ping_trans_color.red*=0x0101;
8894 ping_trans_color.green*=0x0101;
8895 ping_trans_color.blue*=0x0101;
8896 ping_trans_color.gray*=0x0101;
8898 if (logging != MagickFalse)
8900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 (int) ping_trans_color.red,
8903 (int) ping_trans_color.green,
8904 (int) ping_trans_color.blue);
8909 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8910 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8913 Adjust background and transparency samples in sub-8-bit grayscale files.
8915 if (ping_bit_depth < 8 && ping_color_type ==
8916 PNG_COLOR_TYPE_GRAY)
8924 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8926 if (ping_exclude_bKGD == MagickFalse)
8929 ping_background.gray=(png_uint_16)
8930 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8932 if (logging != MagickFalse)
8933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8934 " Setting up bKGD chunk (2)");
8935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8936 " background_color index is %d",
8937 (int) ping_background.index);
8939 ping_have_bKGD = MagickTrue;
8942 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8943 ping_trans_color.gray));
8946 if (ping_exclude_bKGD == MagickFalse)
8948 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8951 Identify which colormap entry is the background color.
8954 number_colors=image_colors;
8956 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8957 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8960 ping_background.index=(png_byte) i;
8962 if (logging != MagickFalse)
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8965 " Setting up bKGD chunk with index=%d",(int) i);
8968 if (i < (ssize_t) number_colors)
8970 ping_have_bKGD = MagickTrue;
8972 if (logging != MagickFalse)
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " background =(%d,%d,%d)",
8976 (int) ping_background.red,
8977 (int) ping_background.green,
8978 (int) ping_background.blue);
8982 else /* Can't happen */
8984 if (logging != MagickFalse)
8985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8986 " No room in PLTE to add bKGD color");
8987 ping_have_bKGD = MagickFalse;
8992 if (logging != MagickFalse)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994 " PNG color type: %d",ping_color_type);
8996 Initialize compression level and filtering.
8998 if (logging != MagickFalse)
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " Setting up deflate compression");
9003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " Compression buffer size: 32768");
9007 png_set_compression_buffer_size(ping,32768L);
9009 if (logging != MagickFalse)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Compression mem level: 9");
9013 png_set_compression_mem_level(ping, 9);
9015 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9023 level=(int) MagickMin((ssize_t) quality/10,9);
9025 if (logging != MagickFalse)
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Compression level: %d",level);
9029 png_set_compression_level(ping,level);
9034 if (logging != MagickFalse)
9035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9036 " Compression strategy: Z_HUFFMAN_ONLY");
9038 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9041 if (logging != MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " Setting up filtering");
9045 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9046 /* This became available in libpng-1.0.9. Output must be a MNG. */
9047 if (mng_info->write_mng && ((quality % 10) == 7))
9049 if (logging != MagickFalse)
9050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9051 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
9053 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9057 if (logging != MagickFalse)
9058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9066 if ((quality % 10) > 5)
9067 base_filter=PNG_ALL_FILTERS;
9070 if ((quality % 10) != 5)
9071 base_filter=(int) quality % 10;
9074 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9075 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9077 base_filter=PNG_NO_FILTERS;
9080 base_filter=PNG_ALL_FILTERS;
9082 if (logging != MagickFalse)
9084 if (base_filter == PNG_ALL_FILTERS)
9085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9086 " Base filter method: ADAPTIVE");
9088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9089 " Base filter method: NONE");
9092 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9095 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9096 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9098 ResetImageProfileIterator(image);
9099 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9101 profile=GetImageProfile(image,name);
9103 if (profile != (StringInfo *) NULL)
9105 #ifdef PNG_WRITE_iCCP_SUPPORTED
9106 if ((LocaleCompare(name,"ICC") == 0) ||
9107 (LocaleCompare(name,"ICM") == 0))
9110 if (ping_exclude_iCCP == MagickFalse)
9112 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9113 #if (PNG_LIBPNG_VER < 10500)
9114 (png_charp) GetStringInfoDatum(profile),
9116 (png_const_bytep) GetStringInfoDatum(profile),
9118 (png_uint_32) GetStringInfoLength(profile));
9124 if (ping_exclude_zCCP == MagickFalse)
9126 Magick_png_write_raw_profile(image_info,ping,ping_info,
9127 (unsigned char *) name,(unsigned char *) name,
9128 GetStringInfoDatum(profile),
9129 (png_uint_32) GetStringInfoLength(profile));
9133 if (logging != MagickFalse)
9134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " Setting up text chunk with %s profile",name);
9137 name=GetNextImageProfile(image);
9141 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9142 if ((mng_info->have_write_global_srgb == 0) &&
9143 ((image->rendering_intent != UndefinedIntent) ||
9144 (image->colorspace == sRGBColorspace)))
9146 if (ping_exclude_sRGB == MagickFalse)
9149 Note image rendering intent.
9151 if (logging != MagickFalse)
9152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153 " Setting up sRGB chunk");
9155 (void) png_set_sRGB(ping,ping_info,(
9156 Magick_RenderingIntent_to_PNG_RenderingIntent(
9157 image->rendering_intent)));
9159 if (ping_exclude_gAMA == MagickFalse)
9160 png_set_gAMA(ping,ping_info,0.45455);
9164 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9167 if (ping_exclude_gAMA == MagickFalse &&
9168 (ping_exclude_sRGB == MagickFalse ||
9169 (image->gamma < .45 || image->gamma > .46)))
9171 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9175 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Setting up gAMA chunk");
9181 png_set_gAMA(ping,ping_info,image->gamma);
9185 if (ping_exclude_cHRM == MagickFalse)
9187 if ((mng_info->have_write_global_chrm == 0) &&
9188 (image->chromaticity.red_primary.x != 0.0))
9191 Note image chromaticity.
9192 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9200 wp=image->chromaticity.white_point;
9201 rp=image->chromaticity.red_primary;
9202 gp=image->chromaticity.green_primary;
9203 bp=image->chromaticity.blue_primary;
9205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Setting up cHRM chunk");
9209 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9215 ping_interlace_method=image_info->interlace != NoInterlace;
9217 if (mng_info->write_mng)
9218 png_set_sig_bytes(ping,8);
9220 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9222 if (mng_info->write_png_colortype != 0)
9224 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9225 if (ping_have_color != MagickFalse)
9227 ping_color_type = PNG_COLOR_TYPE_RGB;
9229 if (ping_bit_depth < 8)
9233 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9234 if (ping_have_color != MagickFalse)
9235 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9238 if (ping_need_colortype_warning != MagickFalse ||
9239 ((mng_info->write_png_depth &&
9240 (int) mng_info->write_png_depth != ping_bit_depth) ||
9241 (mng_info->write_png_colortype &&
9242 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9243 mng_info->write_png_colortype != 7 &&
9244 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9246 if (logging != MagickFalse)
9248 if (ping_need_colortype_warning != MagickFalse)
9250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9251 " Image has transparency but tRNS chunk was excluded");
9254 if (mng_info->write_png_depth)
9256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9257 " Defined PNG:bit-depth=%u, Computed depth=%u",
9258 mng_info->write_png_depth,
9262 if (mng_info->write_png_colortype)
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265 " Defined PNG:color-type=%u, Computed color type=%u",
9266 mng_info->write_png_colortype-1,
9272 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9275 if (image_matte != MagickFalse && image->matte == MagickFalse)
9277 /* Add an opaque matte channel */
9278 image->matte = MagickTrue;
9279 (void) SetImageOpacity(image,0);
9281 if (logging != MagickFalse)
9282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9283 " Added an opaque matte channel");
9286 if (number_transparent != 0 || number_semitransparent != 0)
9288 if (ping_color_type < 4)
9290 ping_have_tRNS=MagickTrue;
9291 if (logging != MagickFalse)
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293 " Setting ping_have_tRNS=MagickTrue.");
9297 if (logging != MagickFalse)
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Writing PNG header chunks");
9301 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9302 ping_bit_depth,ping_color_type,
9303 ping_interlace_method,ping_compression_method,
9304 ping_filter_method);
9306 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9308 png_set_PLTE(ping,ping_info,palette,number_colors);
9310 if (logging != MagickFalse)
9312 for (i=0; i< (ssize_t) number_colors; i++)
9314 if (i < ping_num_trans)
9315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9316 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9318 (int) palette[i].red,
9319 (int) palette[i].green,
9320 (int) palette[i].blue,
9322 (int) ping_trans_alpha[i]);
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9325 " PLTE[%d] = (%d,%d,%d)",
9327 (int) palette[i].red,
9328 (int) palette[i].green,
9329 (int) palette[i].blue);
9334 if (ping_exclude_bKGD == MagickFalse)
9336 if (ping_have_bKGD != MagickFalse)
9338 png_set_bKGD(ping,ping_info,&ping_background);
9341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9342 " Setting up bKGD chunk");
9343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9344 " background color = (%d,%d,%d)",
9345 (int) ping_background.red,
9346 (int) ping_background.green,
9347 (int) ping_background.blue);
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " index = %d, gray=%d",
9350 (int) ping_background.index,
9351 (int) ping_background.gray);
9356 if (ping_exclude_pHYs == MagickFalse)
9358 if (ping_have_pHYs != MagickFalse)
9360 png_set_pHYs(ping,ping_info,
9361 ping_pHYs_x_resolution,
9362 ping_pHYs_y_resolution,
9363 ping_pHYs_unit_type);
9367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368 " Setting up pHYs chunk");
9369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9370 " x_resolution=%lu",
9371 (unsigned long) ping_pHYs_x_resolution);
9372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373 " y_resolution=%lu",
9374 (unsigned long) ping_pHYs_y_resolution);
9375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377 (unsigned long) ping_pHYs_unit_type);
9382 #if defined(PNG_oFFs_SUPPORTED)
9383 if (ping_exclude_oFFs == MagickFalse)
9385 if (image->page.x || image->page.y)
9387 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9388 (png_int_32) image->page.y, 0);
9390 if (logging != MagickFalse)
9391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9392 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9393 (int) image->page.x, (int) image->page.y);
9398 if (mng_info->need_blob != MagickFalse)
9400 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9402 png_error(ping,"WriteBlob Failed");
9404 ping_have_blob=MagickTrue;
9407 png_write_info_before_PLTE(ping, ping_info);
9409 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9411 if (logging != MagickFalse)
9413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9417 if (ping_color_type == 3)
9418 (void) png_set_tRNS(ping, ping_info,
9425 (void) png_set_tRNS(ping, ping_info,
9430 if (logging != MagickFalse)
9432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9433 " tRNS color =(%d,%d,%d)",
9434 (int) ping_trans_color.red,
9435 (int) ping_trans_color.green,
9436 (int) ping_trans_color.blue);
9441 /* write any png-chunk-b profiles */
9442 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9444 png_write_info(ping,ping_info);
9446 /* write any PNG-chunk-m profiles */
9447 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9449 if (ping_exclude_vpAg == MagickFalse)
9451 if ((image->page.width != 0 && image->page.width != image->columns) ||
9452 (image->page.height != 0 && image->page.height != image->rows))
9457 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9458 PNGType(chunk,mng_vpAg);
9459 LogPNGChunk(logging,mng_vpAg,9L);
9460 PNGLong(chunk+4,(png_uint_32) image->page.width);
9461 PNGLong(chunk+8,(png_uint_32) image->page.height);
9462 chunk[12]=0; /* unit = pixels */
9463 (void) WriteBlob(image,13,chunk);
9464 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9468 #if (PNG_LIBPNG_VER == 10206)
9469 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9470 #define PNG_HAVE_IDAT 0x04
9471 ping->mode |= PNG_HAVE_IDAT;
9472 #undef PNG_HAVE_IDAT
9475 png_set_packing(ping);
9479 rowbytes=image->columns;
9480 if (image_depth > 8)
9482 switch (ping_color_type)
9484 case PNG_COLOR_TYPE_RGB:
9488 case PNG_COLOR_TYPE_GRAY_ALPHA:
9492 case PNG_COLOR_TYPE_RGBA:
9500 if (logging != MagickFalse)
9502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503 " Writing PNG image data");
9505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9506 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9508 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9509 sizeof(*ping_pixels));
9511 if (ping_pixels == (unsigned char *) NULL)
9512 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9515 Initialize image scanlines.
9517 if (setjmp(png_jmpbuf(ping)))
9523 if (image_info->verbose)
9524 (void) printf("PNG write has failed.\n");
9526 png_destroy_write_struct(&ping,&ping_info);
9527 if (quantum_info != (QuantumInfo *) NULL)
9528 quantum_info=DestroyQuantumInfo(quantum_info);
9529 if (ping_pixels != (unsigned char *) NULL)
9530 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9531 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9532 UnlockSemaphoreInfo(ping_semaphore);
9534 if (ping_have_blob != MagickFalse)
9535 (void) CloseBlob(image);
9536 image_info=DestroyImageInfo(image_info);
9537 image=DestroyImage(image);
9538 return(MagickFalse);
9540 quantum_info=AcquireQuantumInfo(image_info,image);
9541 if (quantum_info == (QuantumInfo *) NULL)
9542 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9543 quantum_info->format=UndefinedQuantumFormat;
9544 quantum_info->depth=image_depth;
9545 num_passes=png_set_interlace_handling(ping);
9547 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9548 !mng_info->write_png32) &&
9549 (mng_info->IsPalette ||
9550 (image_info->type == BilevelType)) &&
9551 image_matte == MagickFalse &&
9552 ping_have_non_bw == MagickFalse)
9554 /* Palette, Bilevel, or Opaque Monochrome */
9555 register const PixelPacket
9558 quantum_info->depth=8;
9559 for (pass=0; pass < num_passes; pass++)
9562 Convert PseudoClass image to a PNG monochrome image.
9564 for (y=0; y < (ssize_t) image->rows; y++)
9566 if (logging != MagickFalse && y == 0)
9567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9568 " Writing row of pixels (0)");
9570 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9572 if (p == (const PixelPacket *) NULL)
9575 if (mng_info->IsPalette)
9577 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9578 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9579 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9580 mng_info->write_png_depth &&
9581 mng_info->write_png_depth != old_bit_depth)
9583 /* Undo pixel scaling */
9584 for (i=0; i < (ssize_t) image->columns; i++)
9585 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9586 >> (8-old_bit_depth));
9592 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9593 quantum_info,RedQuantum,ping_pixels,&image->exception);
9596 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9597 for (i=0; i < (ssize_t) image->columns; i++)
9598 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9601 if (logging != MagickFalse && y == 0)
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Writing row of pixels (1)");
9605 png_write_row(ping,ping_pixels);
9607 if (image->previous == (Image *) NULL)
9609 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9610 if (status == MagickFalse)
9616 else /* Not Palette, Bilevel, or Opaque Monochrome */
9618 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9619 !mng_info->write_png32) &&
9620 (image_matte != MagickFalse ||
9621 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9622 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9624 register const PixelPacket
9627 for (pass=0; pass < num_passes; pass++)
9630 for (y=0; y < (ssize_t) image->rows; y++)
9632 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9634 if (p == (const PixelPacket *) NULL)
9637 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9639 if (mng_info->IsPalette)
9640 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9641 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9644 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9645 quantum_info,RedQuantum,ping_pixels,&image->exception);
9647 if (logging != MagickFalse && y == 0)
9648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9649 " Writing GRAY PNG pixels (2)");
9652 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9654 if (logging != MagickFalse && y == 0)
9655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9656 " Writing GRAY_ALPHA PNG pixels (2)");
9658 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9659 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9662 if (logging != MagickFalse && y == 0)
9663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9664 " Writing row of pixels (2)");
9666 png_write_row(ping,ping_pixels);
9669 if (image->previous == (Image *) NULL)
9671 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9672 if (status == MagickFalse)
9680 register const PixelPacket
9683 for (pass=0; pass < num_passes; pass++)
9685 if ((image_depth > 8) || (mng_info->write_png24 ||
9686 mng_info->write_png32 ||
9687 (!mng_info->write_png8 && !mng_info->IsPalette)))
9689 for (y=0; y < (ssize_t) image->rows; y++)
9691 p=GetVirtualPixels(image,0,y,image->columns,1,
9694 if (p == (const PixelPacket *) NULL)
9697 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9699 if (image->storage_class == DirectClass)
9700 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9701 quantum_info,RedQuantum,ping_pixels,&image->exception);
9704 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9705 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9708 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9710 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9711 quantum_info,GrayAlphaQuantum,ping_pixels,
9714 if (logging != MagickFalse && y == 0)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " Writing GRAY_ALPHA PNG pixels (3)");
9719 else if (image_matte != MagickFalse)
9720 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9721 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9724 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9725 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9727 if (logging != MagickFalse && y == 0)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Writing row of pixels (3)");
9731 png_write_row(ping,ping_pixels);
9736 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9737 mng_info->write_png32 ||
9738 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9740 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9741 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9747 quantum_info->depth=8;
9751 for (y=0; y < (ssize_t) image->rows; y++)
9753 if (logging != MagickFalse && y == 0)
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9757 p=GetVirtualPixels(image,0,y,image->columns,1,
9760 if (p == (const PixelPacket *) NULL)
9763 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9765 quantum_info->depth=image->depth;
9767 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9768 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9771 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9773 if (logging != MagickFalse && y == 0)
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " Writing GRAY_ALPHA PNG pixels (4)");
9777 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9778 quantum_info,GrayAlphaQuantum,ping_pixels,
9784 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9785 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9787 if (logging != MagickFalse && y <= 2)
9789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9790 " Writing row of non-gray pixels (4)");
9792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9793 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9794 (int)ping_pixels[0],(int)ping_pixels[1]);
9797 png_write_row(ping,ping_pixels);
9801 if (image->previous == (Image *) NULL)
9803 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9804 if (status == MagickFalse)
9811 if (quantum_info != (QuantumInfo *) NULL)
9812 quantum_info=DestroyQuantumInfo(quantum_info);
9814 if (logging != MagickFalse)
9816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817 " Wrote PNG image data");
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Width: %.20g",(double) ping_width);
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Height: %.20g",(double) ping_height);
9825 if (mng_info->write_png_depth)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9832 " PNG bit-depth written: %d",ping_bit_depth);
9834 if (mng_info->write_png_colortype)
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " PNG color-type written: %d",ping_color_type);
9843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9844 " PNG Interlace method: %d",ping_interlace_method);
9847 Generate text chunks after IDAT.
9849 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9851 ResetImagePropertyIterator(image);
9852 property=GetNextImageProperty(image);
9853 while (property != (const char *) NULL)
9858 value=GetImageProperty(image,property);
9860 /* Don't write any "png:" properties; those are just for "identify" */
9861 if (LocaleNCompare(property,"png:",4) != 0 &&
9863 /* Suppress density and units if we wrote a pHYs chunk */
9864 (ping_exclude_pHYs != MagickFalse ||
9865 LocaleCompare(property,"density") != 0 ||
9866 LocaleCompare(property,"units") != 0) &&
9868 /* Suppress the IM-generated Date:create and Date:modify */
9869 (ping_exclude_date == MagickFalse ||
9870 LocaleNCompare(property, "Date:",5) != 0))
9872 if (value != (const char *) NULL)
9874 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9875 text[0].key=(char *) property;
9876 text[0].text=(char *) value;
9877 text[0].text_length=strlen(value);
9879 if (ping_exclude_tEXt != MagickFalse)
9880 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9882 else if (ping_exclude_zTXt != MagickFalse)
9883 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9887 text[0].compression=image_info->compression == NoCompression ||
9888 (image_info->compression == UndefinedCompression &&
9889 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9890 PNG_TEXT_COMPRESSION_zTXt ;
9893 if (logging != MagickFalse)
9895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9896 " Setting up text chunk");
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " keyword: %s",text[0].key);
9902 png_set_text(ping,ping_info,text,1);
9903 png_free(ping,text);
9906 property=GetNextImageProperty(image);
9910 /* write any PNG-chunk-e profiles */
9911 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9913 if (logging != MagickFalse)
9914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9915 " Writing PNG end info");
9917 png_write_end(ping,ping_info);
9919 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9921 if (mng_info->page.x || mng_info->page.y ||
9922 (ping_width != mng_info->page.width) ||
9923 (ping_height != mng_info->page.height))
9929 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9931 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9932 PNGType(chunk,mng_FRAM);
9933 LogPNGChunk(logging,mng_FRAM,27L);
9935 chunk[5]=0; /* frame name separator (no name) */
9936 chunk[6]=1; /* flag for changing delay, for next frame only */
9937 chunk[7]=0; /* flag for changing frame timeout */
9938 chunk[8]=1; /* flag for changing frame clipping for next frame */
9939 chunk[9]=0; /* flag for changing frame sync_id */
9940 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9941 chunk[14]=0; /* clipping boundaries delta type */
9942 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9944 (png_uint_32) (mng_info->page.x + ping_width));
9945 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9947 (png_uint_32) (mng_info->page.y + ping_height));
9948 (void) WriteBlob(image,31,chunk);
9949 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9950 mng_info->old_framing_mode=4;
9951 mng_info->framing_mode=1;
9955 mng_info->framing_mode=3;
9957 if (mng_info->write_mng && !mng_info->need_fram &&
9958 ((int) image->dispose == 3))
9959 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9960 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9961 "`%s'",image->filename);
9967 png_destroy_write_struct(&ping,&ping_info);
9969 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9971 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9972 UnlockSemaphoreInfo(ping_semaphore);
9975 if (ping_have_blob != MagickFalse)
9976 (void) CloseBlob(image);
9978 image_info=DestroyImageInfo(image_info);
9979 image=DestroyImage(image);
9981 /* Store bit depth actually written */
9982 s[0]=(char) ping_bit_depth;
9985 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9987 if (logging != MagickFalse)
9988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " exit WriteOnePNGImage()");
9992 /* End write one PNG image */
9996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10000 % W r i t e P N G I m a g e %
10004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10006 % WritePNGImage() writes a Portable Network Graphics (PNG) or
10007 % Multiple-image Network Graphics (MNG) image file.
10009 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
10011 % The format of the WritePNGImage method is:
10013 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10015 % A description of each parameter follows:
10017 % o image_info: the image info.
10019 % o image: The image.
10021 % Returns MagickTrue on success, MagickFalse on failure.
10023 % Communicating with the PNG encoder:
10025 % While the datastream written is always in PNG format and normally would
10026 % be given the "png" file extension, this method also writes the following
10027 % pseudo-formats which are subsets of PNG:
10029 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10030 % a depth greater than 8, the depth is reduced. If transparency
10031 % is present, the tRNS chunk must only have values 0 and 255
10032 % (i.e., transparency is binary: fully opaque or fully
10033 % transparent). If other values are present they will be
10034 % 50%-thresholded to binary transparency. If more than 256
10035 % colors are present, they will be quantized to the 4-4-4-1,
10036 % 3-3-3-1, or 3-3-2-1 palette.
10038 % If you want better quantization or dithering of the colors
10039 % or alpha than that, you need to do it before calling the
10040 % PNG encoder. The pixels contain 8-bit indices even if
10041 % they could be represented with 1, 2, or 4 bits. Grayscale
10042 % images will be written as indexed PNG files even though the
10043 % PNG grayscale type might be slightly more efficient. Please
10044 % note that writing to the PNG8 format may result in loss
10045 % of color and alpha data.
10047 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10048 % chunk can be present to convey binary transparency by naming
10049 % one of the colors as transparent. The only loss incurred
10050 % is reduction of sample depth to 8. If the image has more
10051 % than one transparent color, has semitransparent pixels, or
10052 % has an opaque pixel with the same RGB components as the
10053 % transparent color, an image is not written.
10055 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10056 % transparency is permitted, i.e., the alpha sample for
10057 % each pixel can have any value from 0 to 255. The alpha
10058 % channel is present even if the image is fully opaque.
10059 % The only loss in data is the reduction of the sample depth
10062 % o -define: For more precise control of the PNG output, you can use the
10063 % Image options "png:bit-depth" and "png:color-type". These
10064 % can be set from the commandline with "-define" and also
10065 % from the application programming interfaces. The options
10066 % are case-independent and are converted to lowercase before
10067 % being passed to this encoder.
10069 % png:color-type can be 0, 2, 3, 4, or 6.
10071 % When png:color-type is 0 (Grayscale), png:bit-depth can
10072 % be 1, 2, 4, 8, or 16.
10074 % When png:color-type is 2 (RGB), png:bit-depth can
10077 % When png:color-type is 3 (Indexed), png:bit-depth can
10078 % be 1, 2, 4, or 8. This refers to the number of bits
10079 % used to store the index. The color samples always have
10080 % bit-depth 8 in indexed PNG files.
10082 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10083 % png:bit-depth can be 8 or 16.
10085 % If the image cannot be written without loss with the requested bit-depth
10086 % and color-type, a PNG file will not be written, and the encoder will
10087 % return MagickFalse.
10089 % Since image encoders should not be responsible for the "heavy lifting",
10090 % the user should make sure that ImageMagick has already reduced the
10091 % image depth and number of colors and limit transparency to binary
10092 % transparency prior to attempting to write the image with depth, color,
10093 % or transparency limitations.
10095 % To do: Enforce the previous paragraph.
10097 % Note that another definition, "png:bit-depth-written" exists, but it
10098 % is not intended for external use. It is only used internally by the
10099 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10101 % It is possible to request that the PNG encoder write previously-formatted
10102 % ancillary chunks in the output PNG file, using the "-profile" commandline
10103 % option as shown below or by setting the profile via a programming
10106 % -profile PNG-chunk-x:<file>
10108 % where x is a location flag and <file> is a file containing the chunk
10109 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10110 % This encoder will compute the chunk length and CRC, so those must not
10111 % be included in the file.
10113 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10114 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10115 % of the same type, then add a short unique string after the "x" to prevent
10116 % subsequent profiles from overwriting the preceding ones, e.g.,
10118 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10120 % As of version 6.6.6 the following optimizations are always done:
10122 % o 32-bit depth is reduced to 16.
10123 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
10124 % high byte and low byte are identical.
10125 % o Palette is sorted to remove unused entries and to put a
10126 % transparent color first, if BUILD_PNG_PALETTE is defined.
10127 % o Opaque matte channel is removed when writing an indexed PNG.
10128 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
10129 % this can be done without loss and a larger bit depth N was not
10130 % requested via the "-define PNG:bit-depth=N" option.
10131 % o If matte channel is present but only one transparent color is
10132 % present, RGB+tRNS is written instead of RGBA
10133 % o Opaque matte channel is removed (or added, if color-type 4 or 6
10134 % was requested when converting an opaque image).
10136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10138 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10144 have_mng_structure,
10160 assert(image_info != (const ImageInfo *) NULL);
10161 assert(image_info->signature == MagickSignature);
10162 assert(image != (Image *) NULL);
10163 assert(image->signature == MagickSignature);
10164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10165 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10167 Allocate a MngInfo structure.
10169 have_mng_structure=MagickFalse;
10170 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10172 if (mng_info == (MngInfo *) NULL)
10173 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10176 Initialize members of the MngInfo structure.
10178 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10179 mng_info->image=image;
10180 mng_info->equal_backgrounds=MagickTrue;
10181 have_mng_structure=MagickTrue;
10183 /* See if user has requested a specific PNG subformat */
10185 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10186 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10187 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10189 if (mng_info->write_png8)
10191 mng_info->write_png_colortype = /* 3 */ 4;
10192 mng_info->write_png_depth = 8;
10196 if (mng_info->write_png24)
10198 mng_info->write_png_colortype = /* 2 */ 3;
10199 mng_info->write_png_depth = 8;
10202 if (image->matte == MagickTrue)
10203 (void) SetImageType(image,TrueColorMatteType);
10206 (void) SetImageType(image,TrueColorType);
10208 (void) SyncImage(image);
10211 if (mng_info->write_png32)
10213 mng_info->write_png_colortype = /* 6 */ 7;
10214 mng_info->write_png_depth = 8;
10217 if (image->matte == MagickTrue)
10218 (void) SetImageType(image,TrueColorMatteType);
10221 (void) SetImageType(image,TrueColorType);
10223 (void) SyncImage(image);
10226 value=GetImageOption(image_info,"png:bit-depth");
10228 if (value != (char *) NULL)
10230 if (LocaleCompare(value,"1") == 0)
10231 mng_info->write_png_depth = 1;
10233 else if (LocaleCompare(value,"2") == 0)
10234 mng_info->write_png_depth = 2;
10236 else if (LocaleCompare(value,"4") == 0)
10237 mng_info->write_png_depth = 4;
10239 else if (LocaleCompare(value,"8") == 0)
10240 mng_info->write_png_depth = 8;
10242 else if (LocaleCompare(value,"16") == 0)
10243 mng_info->write_png_depth = 16;
10246 (void) ThrowMagickException(&image->exception,
10247 GetMagickModule(),CoderWarning,
10248 "ignoring invalid defined png:bit-depth",
10251 if (logging != MagickFalse)
10252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10253 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10256 value=GetImageOption(image_info,"png:color-type");
10258 if (value != (char *) NULL)
10260 /* We must store colortype+1 because 0 is a valid colortype */
10261 if (LocaleCompare(value,"0") == 0)
10262 mng_info->write_png_colortype = 1;
10264 else if (LocaleCompare(value,"2") == 0)
10265 mng_info->write_png_colortype = 3;
10267 else if (LocaleCompare(value,"3") == 0)
10268 mng_info->write_png_colortype = 4;
10270 else if (LocaleCompare(value,"4") == 0)
10271 mng_info->write_png_colortype = 5;
10273 else if (LocaleCompare(value,"6") == 0)
10274 mng_info->write_png_colortype = 7;
10277 (void) ThrowMagickException(&image->exception,
10278 GetMagickModule(),CoderWarning,
10279 "ignoring invalid defined png:color-type",
10282 if (logging != MagickFalse)
10283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10284 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10287 /* Check for chunks to be excluded:
10289 * The default is to not exclude any known chunks except for any
10290 * listed in the "unused_chunks" array, above.
10292 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10293 * define (in the image properties or in the image artifacts)
10294 * or via a mng_info member. For convenience, in addition
10295 * to or instead of a comma-separated list of chunks, the
10296 * "exclude-chunk" string can be simply "all" or "none".
10298 * The exclude-chunk define takes priority over the mng_info.
10300 * A "PNG:include-chunk" define takes priority over both the
10301 * mng_info and the "PNG:exclude-chunk" define. Like the
10302 * "exclude-chunk" string, it can define "all" or "none" as
10303 * well as a comma-separated list. Chunks that are unknown to
10304 * ImageMagick are always excluded, regardless of their "copy-safe"
10305 * status according to the PNG specification, and even if they
10306 * appear in the "include-chunk" list.
10308 * Finally, all chunks listed in the "unused_chunks" array are
10309 * automatically excluded, regardless of the other instructions
10312 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10313 * will not be written and the gAMA chunk will only be written if it
10314 * is not between .45 and .46, or approximately (1.0/2.2).
10316 * If you exclude tRNS and the image has transparency, the colortype
10317 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10319 * The -strip option causes StripImage() to set the png:include-chunk
10320 * artifact to "none,gama".
10323 mng_info->ping_exclude_bKGD=MagickFalse;
10324 mng_info->ping_exclude_cHRM=MagickFalse;
10325 mng_info->ping_exclude_date=MagickFalse;
10326 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10327 mng_info->ping_exclude_gAMA=MagickFalse;
10328 mng_info->ping_exclude_iCCP=MagickFalse;
10329 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10330 mng_info->ping_exclude_oFFs=MagickFalse;
10331 mng_info->ping_exclude_pHYs=MagickFalse;
10332 mng_info->ping_exclude_sRGB=MagickFalse;
10333 mng_info->ping_exclude_tEXt=MagickFalse;
10334 mng_info->ping_exclude_tRNS=MagickFalse;
10335 mng_info->ping_exclude_vpAg=MagickFalse;
10336 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10337 mng_info->ping_exclude_zTXt=MagickFalse;
10339 mng_info->ping_preserve_colormap=MagickFalse;
10341 value=GetImageArtifact(image,"png:preserve-colormap");
10343 value=GetImageOption(image_info,"png:preserve-colormap");
10345 mng_info->ping_preserve_colormap=MagickTrue;
10347 excluding=MagickFalse;
10349 for (source=0; source<1; source++)
10353 value=GetImageArtifact(image,"png:exclude-chunk");
10356 value=GetImageArtifact(image,"png:exclude-chunks");
10360 value=GetImageOption(image_info,"png:exclude-chunk");
10363 value=GetImageOption(image_info,"png:exclude-chunks");
10372 excluding=MagickTrue;
10374 if (logging != MagickFalse)
10377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378 " png:exclude-chunk=%s found in image artifacts.\n", value);
10380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 " png:exclude-chunk=%s found in image properties.\n", value);
10384 last=strlen(value);
10386 for (i=0; i<(int) last; i+=5)
10389 if (LocaleNCompare(value+i,"all",3) == 0)
10391 mng_info->ping_exclude_bKGD=MagickTrue;
10392 mng_info->ping_exclude_cHRM=MagickTrue;
10393 mng_info->ping_exclude_date=MagickTrue;
10394 mng_info->ping_exclude_EXIF=MagickTrue;
10395 mng_info->ping_exclude_gAMA=MagickTrue;
10396 mng_info->ping_exclude_iCCP=MagickTrue;
10397 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10398 mng_info->ping_exclude_oFFs=MagickTrue;
10399 mng_info->ping_exclude_pHYs=MagickTrue;
10400 mng_info->ping_exclude_sRGB=MagickTrue;
10401 mng_info->ping_exclude_tEXt=MagickTrue;
10402 mng_info->ping_exclude_tRNS=MagickTrue;
10403 mng_info->ping_exclude_vpAg=MagickTrue;
10404 mng_info->ping_exclude_zCCP=MagickTrue;
10405 mng_info->ping_exclude_zTXt=MagickTrue;
10409 if (LocaleNCompare(value+i,"none",4) == 0)
10411 mng_info->ping_exclude_bKGD=MagickFalse;
10412 mng_info->ping_exclude_cHRM=MagickFalse;
10413 mng_info->ping_exclude_date=MagickFalse;
10414 mng_info->ping_exclude_EXIF=MagickFalse;
10415 mng_info->ping_exclude_gAMA=MagickFalse;
10416 mng_info->ping_exclude_iCCP=MagickFalse;
10417 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10418 mng_info->ping_exclude_oFFs=MagickFalse;
10419 mng_info->ping_exclude_pHYs=MagickFalse;
10420 mng_info->ping_exclude_sRGB=MagickFalse;
10421 mng_info->ping_exclude_tEXt=MagickFalse;
10422 mng_info->ping_exclude_tRNS=MagickFalse;
10423 mng_info->ping_exclude_vpAg=MagickFalse;
10424 mng_info->ping_exclude_zCCP=MagickFalse;
10425 mng_info->ping_exclude_zTXt=MagickFalse;
10428 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10429 mng_info->ping_exclude_bKGD=MagickTrue;
10431 if (LocaleNCompare(value+i,"chrm",4) == 0)
10432 mng_info->ping_exclude_cHRM=MagickTrue;
10434 if (LocaleNCompare(value+i,"date",4) == 0)
10435 mng_info->ping_exclude_date=MagickTrue;
10437 if (LocaleNCompare(value+i,"exif",4) == 0)
10438 mng_info->ping_exclude_EXIF=MagickTrue;
10440 if (LocaleNCompare(value+i,"gama",4) == 0)
10441 mng_info->ping_exclude_gAMA=MagickTrue;
10443 if (LocaleNCompare(value+i,"iccp",4) == 0)
10444 mng_info->ping_exclude_iCCP=MagickTrue;
10447 if (LocaleNCompare(value+i,"itxt",4) == 0)
10448 mng_info->ping_exclude_iTXt=MagickTrue;
10451 if (LocaleNCompare(value+i,"gama",4) == 0)
10452 mng_info->ping_exclude_gAMA=MagickTrue;
10454 if (LocaleNCompare(value+i,"offs",4) == 0)
10455 mng_info->ping_exclude_oFFs=MagickTrue;
10457 if (LocaleNCompare(value+i,"phys",4) == 0)
10458 mng_info->ping_exclude_pHYs=MagickTrue;
10460 if (LocaleNCompare(value+i,"srgb",4) == 0)
10461 mng_info->ping_exclude_sRGB=MagickTrue;
10463 if (LocaleNCompare(value+i,"text",4) == 0)
10464 mng_info->ping_exclude_tEXt=MagickTrue;
10466 if (LocaleNCompare(value+i,"trns",4) == 0)
10467 mng_info->ping_exclude_tRNS=MagickTrue;
10469 if (LocaleNCompare(value+i,"vpag",4) == 0)
10470 mng_info->ping_exclude_vpAg=MagickTrue;
10472 if (LocaleNCompare(value+i,"zccp",4) == 0)
10473 mng_info->ping_exclude_zCCP=MagickTrue;
10475 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10476 mng_info->ping_exclude_zTXt=MagickTrue;
10482 for (source=0; source<1; source++)
10486 value=GetImageArtifact(image,"png:include-chunk");
10489 value=GetImageArtifact(image,"png:include-chunks");
10493 value=GetImageOption(image_info,"png:include-chunk");
10496 value=GetImageOption(image_info,"png:include-chunks");
10504 excluding=MagickTrue;
10506 if (logging != MagickFalse)
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " png:include-chunk=%s found in image artifacts.\n", value);
10512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513 " png:include-chunk=%s found in image properties.\n", value);
10516 last=strlen(value);
10518 for (i=0; i<(int) last; i+=5)
10520 if (LocaleNCompare(value+i,"all",3) == 0)
10522 mng_info->ping_exclude_bKGD=MagickFalse;
10523 mng_info->ping_exclude_cHRM=MagickFalse;
10524 mng_info->ping_exclude_date=MagickFalse;
10525 mng_info->ping_exclude_EXIF=MagickFalse;
10526 mng_info->ping_exclude_gAMA=MagickFalse;
10527 mng_info->ping_exclude_iCCP=MagickFalse;
10528 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10529 mng_info->ping_exclude_oFFs=MagickFalse;
10530 mng_info->ping_exclude_pHYs=MagickFalse;
10531 mng_info->ping_exclude_sRGB=MagickFalse;
10532 mng_info->ping_exclude_tEXt=MagickFalse;
10533 mng_info->ping_exclude_tRNS=MagickFalse;
10534 mng_info->ping_exclude_vpAg=MagickFalse;
10535 mng_info->ping_exclude_zCCP=MagickFalse;
10536 mng_info->ping_exclude_zTXt=MagickFalse;
10540 if (LocaleNCompare(value+i,"none",4) == 0)
10542 mng_info->ping_exclude_bKGD=MagickTrue;
10543 mng_info->ping_exclude_cHRM=MagickTrue;
10544 mng_info->ping_exclude_date=MagickTrue;
10545 mng_info->ping_exclude_EXIF=MagickTrue;
10546 mng_info->ping_exclude_gAMA=MagickTrue;
10547 mng_info->ping_exclude_iCCP=MagickTrue;
10548 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10549 mng_info->ping_exclude_oFFs=MagickTrue;
10550 mng_info->ping_exclude_pHYs=MagickTrue;
10551 mng_info->ping_exclude_sRGB=MagickTrue;
10552 mng_info->ping_exclude_tEXt=MagickTrue;
10553 mng_info->ping_exclude_tRNS=MagickTrue;
10554 mng_info->ping_exclude_vpAg=MagickTrue;
10555 mng_info->ping_exclude_zCCP=MagickTrue;
10556 mng_info->ping_exclude_zTXt=MagickTrue;
10559 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10560 mng_info->ping_exclude_bKGD=MagickFalse;
10562 if (LocaleNCompare(value+i,"chrm",4) == 0)
10563 mng_info->ping_exclude_cHRM=MagickFalse;
10565 if (LocaleNCompare(value+i,"date",4) == 0)
10566 mng_info->ping_exclude_date=MagickFalse;
10568 if (LocaleNCompare(value+i,"exif",4) == 0)
10569 mng_info->ping_exclude_EXIF=MagickFalse;
10571 if (LocaleNCompare(value+i,"gama",4) == 0)
10572 mng_info->ping_exclude_gAMA=MagickFalse;
10574 if (LocaleNCompare(value+i,"iccp",4) == 0)
10575 mng_info->ping_exclude_iCCP=MagickFalse;
10578 if (LocaleNCompare(value+i,"itxt",4) == 0)
10579 mng_info->ping_exclude_iTXt=MagickFalse;
10582 if (LocaleNCompare(value+i,"gama",4) == 0)
10583 mng_info->ping_exclude_gAMA=MagickFalse;
10585 if (LocaleNCompare(value+i,"offs",4) == 0)
10586 mng_info->ping_exclude_oFFs=MagickFalse;
10588 if (LocaleNCompare(value+i,"phys",4) == 0)
10589 mng_info->ping_exclude_pHYs=MagickFalse;
10591 if (LocaleNCompare(value+i,"srgb",4) == 0)
10592 mng_info->ping_exclude_sRGB=MagickFalse;
10594 if (LocaleNCompare(value+i,"text",4) == 0)
10595 mng_info->ping_exclude_tEXt=MagickFalse;
10597 if (LocaleNCompare(value+i,"trns",4) == 0)
10598 mng_info->ping_exclude_tRNS=MagickFalse;
10600 if (LocaleNCompare(value+i,"vpag",4) == 0)
10601 mng_info->ping_exclude_vpAg=MagickFalse;
10603 if (LocaleNCompare(value+i,"zccp",4) == 0)
10604 mng_info->ping_exclude_zCCP=MagickFalse;
10606 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10607 mng_info->ping_exclude_zTXt=MagickFalse;
10613 if (excluding != MagickFalse && logging != MagickFalse)
10615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10616 " Chunks to be excluded from the output PNG:");
10617 if (mng_info->ping_exclude_bKGD != MagickFalse)
10618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 if (mng_info->ping_exclude_cHRM != MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 if (mng_info->ping_exclude_date != MagickFalse)
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 if (mng_info->ping_exclude_EXIF != MagickFalse)
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 if (mng_info->ping_exclude_gAMA != MagickFalse)
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 if (mng_info->ping_exclude_iCCP != MagickFalse)
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 if (mng_info->ping_exclude_iTXt != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 if (mng_info->ping_exclude_oFFs != MagickFalse)
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 if (mng_info->ping_exclude_pHYs != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 if (mng_info->ping_exclude_sRGB != MagickFalse)
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 if (mng_info->ping_exclude_tEXt != MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 if (mng_info->ping_exclude_tRNS != MagickFalse)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 if (mng_info->ping_exclude_vpAg != MagickFalse)
10656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 if (mng_info->ping_exclude_zCCP != MagickFalse)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 if (mng_info->ping_exclude_zTXt != MagickFalse)
10662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666 mng_info->need_blob = MagickTrue;
10668 status=WriteOnePNGImage(mng_info,image_info,image);
10670 MngInfoFreeStruct(mng_info,&have_mng_structure);
10672 if (logging != MagickFalse)
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10678 #if defined(JNG_SUPPORTED)
10680 /* Write one JNG image */
10681 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10682 const ImageInfo *image_info,Image *image)
10703 jng_alpha_compression_method,
10704 jng_alpha_sample_depth,
10711 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10712 " Enter WriteOneJNGImage()");
10714 blob=(unsigned char *) NULL;
10715 jpeg_image=(Image *) NULL;
10716 jpeg_image_info=(ImageInfo *) NULL;
10719 transparent=image_info->type==GrayscaleMatteType ||
10720 image_info->type==TrueColorMatteType;
10722 jng_alpha_sample_depth=0;
10723 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10724 jng_alpha_compression_method=0;
10726 if (image->matte != MagickFalse)
10728 /* if any pixels are transparent */
10729 transparent=MagickTrue;
10730 if (image_info->compression==JPEGCompression)
10731 jng_alpha_compression_method=8;
10738 /* Create JPEG blob, image, and image_info */
10739 if (logging != MagickFalse)
10740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " Creating jpeg_image_info for opacity.");
10743 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10745 if (jpeg_image_info == (ImageInfo *) NULL)
10746 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10748 if (logging != MagickFalse)
10749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10750 " Creating jpeg_image.");
10752 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10754 if (jpeg_image == (Image *) NULL)
10755 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10757 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10758 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10759 status=NegateImage(jpeg_image,MagickFalse);
10760 jpeg_image->matte=MagickFalse;
10762 if (jng_quality >= 1000)
10763 jpeg_image_info->quality=jng_quality/1000;
10766 jpeg_image_info->quality=jng_quality;
10768 jpeg_image_info->type=GrayscaleType;
10769 (void) SetImageType(jpeg_image,GrayscaleType);
10770 (void) AcquireUniqueFilename(jpeg_image->filename);
10771 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10772 "%s",jpeg_image->filename);
10775 /* To do: check bit depth of PNG alpha channel */
10777 /* Check if image is grayscale. */
10778 if (image_info->type != TrueColorMatteType && image_info->type !=
10779 TrueColorType && ImageIsGray(image))
10784 if (jng_alpha_compression_method==0)
10789 /* Encode opacity as a grayscale PNG blob */
10790 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10791 &image->exception);
10792 if (logging != MagickFalse)
10793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10794 " Creating PNG blob.");
10797 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10798 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10799 jpeg_image_info->interlace=NoInterlace;
10801 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10802 &image->exception);
10804 /* Retrieve sample depth used */
10805 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10806 if (value != (char *) NULL)
10807 jng_alpha_sample_depth= (unsigned int) value[0];
10811 /* Encode opacity as a grayscale JPEG blob */
10813 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10814 &image->exception);
10816 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10817 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10818 jpeg_image_info->interlace=NoInterlace;
10819 if (logging != MagickFalse)
10820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821 " Creating blob.");
10822 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10823 &image->exception);
10824 jng_alpha_sample_depth=8;
10826 if (logging != MagickFalse)
10827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10828 " Successfully read jpeg_image into a blob, length=%.20g.",
10832 /* Destroy JPEG image and image_info */
10833 jpeg_image=DestroyImage(jpeg_image);
10834 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10835 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10838 /* Write JHDR chunk */
10839 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10840 PNGType(chunk,mng_JHDR);
10841 LogPNGChunk(logging,mng_JHDR,16L);
10842 PNGLong(chunk+4,(png_uint_32) image->columns);
10843 PNGLong(chunk+8,(png_uint_32) image->rows);
10844 chunk[12]=jng_color_type;
10845 chunk[13]=8; /* sample depth */
10846 chunk[14]=8; /*jng_image_compression_method */
10847 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10848 chunk[16]=jng_alpha_sample_depth;
10849 chunk[17]=jng_alpha_compression_method;
10850 chunk[18]=0; /*jng_alpha_filter_method */
10851 chunk[19]=0; /*jng_alpha_interlace_method */
10852 (void) WriteBlob(image,20,chunk);
10853 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10854 if (logging != MagickFalse)
10856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10857 " JNG width:%15lu",(unsigned long) image->columns);
10859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10860 " JNG height:%14lu",(unsigned long) image->rows);
10862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863 " JNG color type:%10d",jng_color_type);
10865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866 " JNG sample depth:%8d",8);
10868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10869 " JNG compression:%9d",8);
10871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " JNG interlace:%11d",0);
10874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10875 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878 " JNG alpha compression:%3d",jng_alpha_compression_method);
10880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10881 " JNG alpha filter:%8d",0);
10883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884 " JNG alpha interlace:%5d",0);
10887 /* Write any JNG-chunk-b profiles */
10888 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10891 Write leading ancillary chunks
10897 Write JNG bKGD chunk
10908 if (jng_color_type == 8 || jng_color_type == 12)
10912 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10913 PNGType(chunk,mng_bKGD);
10914 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10915 red=ScaleQuantumToChar(image->background_color.red);
10916 green=ScaleQuantumToChar(image->background_color.green);
10917 blue=ScaleQuantumToChar(image->background_color.blue);
10924 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10925 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10928 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10931 Write JNG sRGB chunk
10933 (void) WriteBlobMSBULong(image,1L);
10934 PNGType(chunk,mng_sRGB);
10935 LogPNGChunk(logging,mng_sRGB,1L);
10937 if (image->rendering_intent != UndefinedIntent)
10938 chunk[4]=(unsigned char)
10939 Magick_RenderingIntent_to_PNG_RenderingIntent(
10940 (image->rendering_intent));
10943 chunk[4]=(unsigned char)
10944 Magick_RenderingIntent_to_PNG_RenderingIntent(
10945 (PerceptualIntent));
10947 (void) WriteBlob(image,5,chunk);
10948 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10952 if (image->gamma != 0.0)
10955 Write JNG gAMA chunk
10957 (void) WriteBlobMSBULong(image,4L);
10958 PNGType(chunk,mng_gAMA);
10959 LogPNGChunk(logging,mng_gAMA,4L);
10960 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10961 (void) WriteBlob(image,8,chunk);
10962 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10965 if ((mng_info->equal_chrms == MagickFalse) &&
10966 (image->chromaticity.red_primary.x != 0.0))
10972 Write JNG cHRM chunk
10974 (void) WriteBlobMSBULong(image,32L);
10975 PNGType(chunk,mng_cHRM);
10976 LogPNGChunk(logging,mng_cHRM,32L);
10977 primary=image->chromaticity.white_point;
10978 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10979 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10980 primary=image->chromaticity.red_primary;
10981 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10982 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10983 primary=image->chromaticity.green_primary;
10984 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10985 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10986 primary=image->chromaticity.blue_primary;
10987 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10988 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10989 (void) WriteBlob(image,36,chunk);
10990 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10994 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10997 Write JNG pHYs chunk
10999 (void) WriteBlobMSBULong(image,9L);
11000 PNGType(chunk,mng_pHYs);
11001 LogPNGChunk(logging,mng_pHYs,9L);
11002 if (image->units == PixelsPerInchResolution)
11004 PNGLong(chunk+4,(png_uint_32)
11005 (image->x_resolution*100.0/2.54+0.5));
11007 PNGLong(chunk+8,(png_uint_32)
11008 (image->y_resolution*100.0/2.54+0.5));
11015 if (image->units == PixelsPerCentimeterResolution)
11017 PNGLong(chunk+4,(png_uint_32)
11018 (image->x_resolution*100.0+0.5));
11020 PNGLong(chunk+8,(png_uint_32)
11021 (image->y_resolution*100.0+0.5));
11028 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11029 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11033 (void) WriteBlob(image,13,chunk);
11034 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11037 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11040 Write JNG oFFs chunk
11042 (void) WriteBlobMSBULong(image,9L);
11043 PNGType(chunk,mng_oFFs);
11044 LogPNGChunk(logging,mng_oFFs,9L);
11045 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11046 PNGsLong(chunk+8,(ssize_t) (image->page.y));
11048 (void) WriteBlob(image,13,chunk);
11049 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11051 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11053 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11054 PNGType(chunk,mng_vpAg);
11055 LogPNGChunk(logging,mng_vpAg,9L);
11056 PNGLong(chunk+4,(png_uint_32) image->page.width);
11057 PNGLong(chunk+8,(png_uint_32) image->page.height);
11058 chunk[12]=0; /* unit = pixels */
11059 (void) WriteBlob(image,13,chunk);
11060 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11066 if (jng_alpha_compression_method==0)
11074 /* Write IDAT chunk header */
11075 if (logging != MagickFalse)
11076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11077 " Write IDAT chunks from blob, length=%.20g.",(double)
11080 /* Copy IDAT chunks */
11083 for (i=8; i<(ssize_t) length; i+=len+12)
11085 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11088 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11090 /* Found an IDAT chunk. */
11091 (void) WriteBlobMSBULong(image,(size_t) len);
11092 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11093 (void) WriteBlob(image,(size_t) len+4,p);
11094 (void) WriteBlobMSBULong(image,
11095 crc32(0,p,(uInt) len+4));
11100 if (logging != MagickFalse)
11101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11102 " Skipping %c%c%c%c chunk, length=%.20g.",
11103 *(p),*(p+1),*(p+2),*(p+3),(double) len);
11110 /* Write JDAA chunk header */
11111 if (logging != MagickFalse)
11112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11113 " Write JDAA chunk, length=%.20g.",(double) length);
11114 (void) WriteBlobMSBULong(image,(size_t) length);
11115 PNGType(chunk,mng_JDAA);
11116 LogPNGChunk(logging,mng_JDAA,length);
11117 /* Write JDAT chunk(s) data */
11118 (void) WriteBlob(image,4,chunk);
11119 (void) WriteBlob(image,length,blob);
11120 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11123 blob=(unsigned char *) RelinquishMagickMemory(blob);
11126 /* Encode image as a JPEG blob */
11127 if (logging != MagickFalse)
11128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129 " Creating jpeg_image_info.");
11130 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11131 if (jpeg_image_info == (ImageInfo *) NULL)
11132 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11134 if (logging != MagickFalse)
11135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11136 " Creating jpeg_image.");
11138 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11139 if (jpeg_image == (Image *) NULL)
11140 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11141 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11143 (void) AcquireUniqueFilename(jpeg_image->filename);
11144 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11145 jpeg_image->filename);
11147 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11148 &image->exception);
11150 if (logging != MagickFalse)
11151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11152 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11153 (double) jpeg_image->rows);
11155 if (jng_color_type == 8 || jng_color_type == 12)
11156 jpeg_image_info->type=GrayscaleType;
11158 jpeg_image_info->quality=jng_quality % 1000;
11159 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11160 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11162 if (logging != MagickFalse)
11163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11164 " Creating blob.");
11166 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11168 if (logging != MagickFalse)
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11171 " Successfully read jpeg_image into a blob, length=%.20g.",
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175 " Write JDAT chunk, length=%.20g.",(double) length);
11178 /* Write JDAT chunk(s) */
11179 (void) WriteBlobMSBULong(image,(size_t) length);
11180 PNGType(chunk,mng_JDAT);
11181 LogPNGChunk(logging,mng_JDAT,length);
11182 (void) WriteBlob(image,4,chunk);
11183 (void) WriteBlob(image,length,blob);
11184 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11186 jpeg_image=DestroyImage(jpeg_image);
11187 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11188 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11189 blob=(unsigned char *) RelinquishMagickMemory(blob);
11191 /* Write any JNG-chunk-e profiles */
11192 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
11194 /* Write IEND chunk */
11195 (void) WriteBlobMSBULong(image,0L);
11196 PNGType(chunk,mng_IEND);
11197 LogPNGChunk(logging,mng_IEND,0);
11198 (void) WriteBlob(image,4,chunk);
11199 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11201 if (logging != MagickFalse)
11202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203 " exit WriteOneJNGImage()");
11210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11214 % W r i t e J N G I m a g e %
11218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11220 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11222 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
11224 % The format of the WriteJNGImage method is:
11226 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11228 % A description of each parameter follows:
11230 % o image_info: the image info.
11232 % o image: The image.
11234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11236 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11239 have_mng_structure,
11249 assert(image_info != (const ImageInfo *) NULL);
11250 assert(image_info->signature == MagickSignature);
11251 assert(image != (Image *) NULL);
11252 assert(image->signature == MagickSignature);
11253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11254 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11255 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11256 if (status == MagickFalse)
11260 Allocate a MngInfo structure.
11262 have_mng_structure=MagickFalse;
11263 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11264 if (mng_info == (MngInfo *) NULL)
11265 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11267 Initialize members of the MngInfo structure.
11269 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11270 mng_info->image=image;
11271 have_mng_structure=MagickTrue;
11273 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11275 status=WriteOneJNGImage(mng_info,image_info,image);
11276 (void) CloseBlob(image);
11278 (void) CatchImageException(image);
11279 MngInfoFreeStruct(mng_info,&have_mng_structure);
11280 if (logging != MagickFalse)
11281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11288 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11297 have_mng_structure,
11300 volatile MagickBooleanType
11312 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11313 defined(PNG_MNG_FEATURES_SUPPORTED)
11316 all_images_are_gray,
11326 volatile unsigned int
11337 #if (PNG_LIBPNG_VER < 10200)
11338 if (image_info->verbose)
11339 printf("Your PNG library (libpng-%s) is rather old.\n",
11340 PNG_LIBPNG_VER_STRING);
11346 assert(image_info != (const ImageInfo *) NULL);
11347 assert(image_info->signature == MagickSignature);
11348 assert(image != (Image *) NULL);
11349 assert(image->signature == MagickSignature);
11350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11351 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11352 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11353 if (status == MagickFalse)
11357 Allocate a MngInfo structure.
11359 have_mng_structure=MagickFalse;
11360 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11361 if (mng_info == (MngInfo *) NULL)
11362 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11364 Initialize members of the MngInfo structure.
11366 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11367 mng_info->image=image;
11368 have_mng_structure=MagickTrue;
11369 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11372 * See if user has requested a specific PNG subformat to be used
11373 * for all of the PNGs in the MNG being written, e.g.,
11375 * convert *.png png8:animation.mng
11377 * To do: check -define png:bit_depth and png:color_type as well,
11378 * or perhaps use mng:bit_depth and mng:color_type instead for
11382 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11383 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11384 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11386 write_jng=MagickFalse;
11387 if (image_info->compression == JPEGCompression)
11388 write_jng=MagickTrue;
11390 mng_info->adjoin=image_info->adjoin &&
11391 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11393 if (logging != MagickFalse)
11395 /* Log some info about the input */
11399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400 " Checking input image(s)");
11402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11403 " Image_info depth: %.20g",(double) image_info->depth);
11405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11406 " Type: %d",image_info->type);
11409 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11412 " Scene: %.20g",(double) scene++);
11414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11415 " Image depth: %.20g",(double) p->depth);
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425 if (p->storage_class == PseudoClass)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427 " Storage class: PseudoClass");
11430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11431 " Storage class: DirectClass");
11434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11435 " Number of colors: %.20g",(double) p->colors);
11438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11439 " Number of colors: unspecified");
11441 if (mng_info->adjoin == MagickFalse)
11446 use_global_plte=MagickFalse;
11447 all_images_are_gray=MagickFalse;
11448 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11449 need_local_plte=MagickTrue;
11451 need_defi=MagickFalse;
11452 need_matte=MagickFalse;
11453 mng_info->framing_mode=1;
11454 mng_info->old_framing_mode=1;
11457 if (image_info->page != (char *) NULL)
11460 Determine image bounding box.
11462 SetGeometry(image,&mng_info->page);
11463 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11464 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11476 mng_info->page=image->page;
11477 need_geom=MagickTrue;
11478 if (mng_info->page.width || mng_info->page.height)
11479 need_geom=MagickFalse;
11481 Check all the scenes.
11483 initial_delay=image->delay;
11484 need_iterations=MagickFalse;
11485 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11486 mng_info->equal_physs=MagickTrue,
11487 mng_info->equal_gammas=MagickTrue;
11488 mng_info->equal_srgbs=MagickTrue;
11489 mng_info->equal_backgrounds=MagickTrue;
11491 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11492 defined(PNG_MNG_FEATURES_SUPPORTED)
11493 all_images_are_gray=MagickTrue;
11494 mng_info->equal_palettes=MagickFalse;
11495 need_local_plte=MagickFalse;
11497 for (next_image=image; next_image != (Image *) NULL; )
11501 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11502 mng_info->page.width=next_image->columns+next_image->page.x;
11504 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11505 mng_info->page.height=next_image->rows+next_image->page.y;
11508 if (next_image->page.x || next_image->page.y)
11509 need_defi=MagickTrue;
11511 if (next_image->matte)
11512 need_matte=MagickTrue;
11514 if ((int) next_image->dispose >= BackgroundDispose)
11515 if (next_image->matte || next_image->page.x || next_image->page.y ||
11516 ((next_image->columns < mng_info->page.width) &&
11517 (next_image->rows < mng_info->page.height)))
11518 mng_info->need_fram=MagickTrue;
11520 if (next_image->iterations)
11521 need_iterations=MagickTrue;
11523 final_delay=next_image->delay;
11525 if (final_delay != initial_delay || final_delay > 1UL*
11526 next_image->ticks_per_second)
11527 mng_info->need_fram=1;
11529 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11530 defined(PNG_MNG_FEATURES_SUPPORTED)
11532 check for global palette possibility.
11534 if (image->matte != MagickFalse)
11535 need_local_plte=MagickTrue;
11537 if (need_local_plte == 0)
11539 if (ImageIsGray(image) == MagickFalse)
11540 all_images_are_gray=MagickFalse;
11541 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11542 if (use_global_plte == 0)
11543 use_global_plte=mng_info->equal_palettes;
11544 need_local_plte=!mng_info->equal_palettes;
11547 if (GetNextImageInList(next_image) != (Image *) NULL)
11549 if (next_image->background_color.red !=
11550 next_image->next->background_color.red ||
11551 next_image->background_color.green !=
11552 next_image->next->background_color.green ||
11553 next_image->background_color.blue !=
11554 next_image->next->background_color.blue)
11555 mng_info->equal_backgrounds=MagickFalse;
11557 if (next_image->gamma != next_image->next->gamma)
11558 mng_info->equal_gammas=MagickFalse;
11560 if (next_image->rendering_intent !=
11561 next_image->next->rendering_intent)
11562 mng_info->equal_srgbs=MagickFalse;
11564 if ((next_image->units != next_image->next->units) ||
11565 (next_image->x_resolution != next_image->next->x_resolution) ||
11566 (next_image->y_resolution != next_image->next->y_resolution))
11567 mng_info->equal_physs=MagickFalse;
11569 if (mng_info->equal_chrms)
11571 if (next_image->chromaticity.red_primary.x !=
11572 next_image->next->chromaticity.red_primary.x ||
11573 next_image->chromaticity.red_primary.y !=
11574 next_image->next->chromaticity.red_primary.y ||
11575 next_image->chromaticity.green_primary.x !=
11576 next_image->next->chromaticity.green_primary.x ||
11577 next_image->chromaticity.green_primary.y !=
11578 next_image->next->chromaticity.green_primary.y ||
11579 next_image->chromaticity.blue_primary.x !=
11580 next_image->next->chromaticity.blue_primary.x ||
11581 next_image->chromaticity.blue_primary.y !=
11582 next_image->next->chromaticity.blue_primary.y ||
11583 next_image->chromaticity.white_point.x !=
11584 next_image->next->chromaticity.white_point.x ||
11585 next_image->chromaticity.white_point.y !=
11586 next_image->next->chromaticity.white_point.y)
11587 mng_info->equal_chrms=MagickFalse;
11591 next_image=GetNextImageInList(next_image);
11593 if (image_count < 2)
11595 mng_info->equal_backgrounds=MagickFalse;
11596 mng_info->equal_chrms=MagickFalse;
11597 mng_info->equal_gammas=MagickFalse;
11598 mng_info->equal_srgbs=MagickFalse;
11599 mng_info->equal_physs=MagickFalse;
11600 use_global_plte=MagickFalse;
11601 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11602 need_local_plte=MagickTrue;
11604 need_iterations=MagickFalse;
11607 if (mng_info->need_fram == MagickFalse)
11610 Only certain framing rates 100/n are exactly representable without
11611 the FRAM chunk but we'll allow some slop in VLC files
11613 if (final_delay == 0)
11615 if (need_iterations != MagickFalse)
11618 It's probably a GIF with loop; don't run it *too* fast.
11620 if (mng_info->adjoin)
11623 (void) ThrowMagickException(&image->exception,
11624 GetMagickModule(),CoderWarning,
11625 "input has zero delay between all frames; assuming",
11630 mng_info->ticks_per_second=0;
11632 if (final_delay != 0)
11633 mng_info->ticks_per_second=(png_uint_32)
11634 (image->ticks_per_second/final_delay);
11635 if (final_delay > 50)
11636 mng_info->ticks_per_second=2;
11638 if (final_delay > 75)
11639 mng_info->ticks_per_second=1;
11641 if (final_delay > 125)
11642 mng_info->need_fram=MagickTrue;
11644 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11645 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11646 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11647 1UL*image->ticks_per_second))
11648 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11651 if (mng_info->need_fram != MagickFalse)
11652 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11654 If pseudocolor, we should also check to see if all the
11655 palettes are identical and write a global PLTE if they are.
11659 Write the MNG version 1.0 signature and MHDR chunk.
11661 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11662 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11663 PNGType(chunk,mng_MHDR);
11664 LogPNGChunk(logging,mng_MHDR,28L);
11665 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11666 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11667 PNGLong(chunk+12,mng_info->ticks_per_second);
11668 PNGLong(chunk+16,0L); /* layer count=unknown */
11669 PNGLong(chunk+20,0L); /* frame count=unknown */
11670 PNGLong(chunk+24,0L); /* play time=unknown */
11675 if (need_defi || mng_info->need_fram || use_global_plte)
11676 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11679 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11684 if (need_defi || mng_info->need_fram || use_global_plte)
11685 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11688 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11696 if (need_defi || mng_info->need_fram || use_global_plte)
11697 PNGLong(chunk+28,11L); /* simplicity=LC */
11700 PNGLong(chunk+28,9L); /* simplicity=VLC */
11705 if (need_defi || mng_info->need_fram || use_global_plte)
11706 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11709 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11712 (void) WriteBlob(image,32,chunk);
11713 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11714 option=GetImageOption(image_info,"mng:need-cacheoff");
11715 if (option != (const char *) NULL)
11721 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11723 PNGType(chunk,mng_nEED);
11724 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11725 (void) WriteBlobMSBULong(image,(size_t) length);
11726 LogPNGChunk(logging,mng_nEED,(size_t) length);
11728 (void) WriteBlob(image,length,chunk);
11729 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11731 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11732 (GetNextImageInList(image) != (Image *) NULL) &&
11733 (image->iterations != 1))
11736 Write MNG TERM chunk
11738 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11739 PNGType(chunk,mng_TERM);
11740 LogPNGChunk(logging,mng_TERM,10L);
11741 chunk[4]=3; /* repeat animation */
11742 chunk[5]=0; /* show last frame when done */
11743 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11744 final_delay/MagickMax(image->ticks_per_second,1)));
11746 if (image->iterations == 0)
11747 PNGLong(chunk+10,PNG_UINT_31_MAX);
11750 PNGLong(chunk+10,(png_uint_32) image->iterations);
11752 if (logging != MagickFalse)
11754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11755 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11756 final_delay/MagickMax(image->ticks_per_second,1)));
11758 if (image->iterations == 0)
11759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11760 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764 " Image iterations: %.20g",(double) image->iterations);
11766 (void) WriteBlob(image,14,chunk);
11767 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11770 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11772 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11773 mng_info->equal_srgbs)
11776 Write MNG sRGB chunk
11778 (void) WriteBlobMSBULong(image,1L);
11779 PNGType(chunk,mng_sRGB);
11780 LogPNGChunk(logging,mng_sRGB,1L);
11782 if (image->rendering_intent != UndefinedIntent)
11783 chunk[4]=(unsigned char)
11784 Magick_RenderingIntent_to_PNG_RenderingIntent(
11785 (image->rendering_intent));
11788 chunk[4]=(unsigned char)
11789 Magick_RenderingIntent_to_PNG_RenderingIntent(
11790 (PerceptualIntent));
11792 (void) WriteBlob(image,5,chunk);
11793 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11794 mng_info->have_write_global_srgb=MagickTrue;
11799 if (image->gamma && mng_info->equal_gammas)
11802 Write MNG gAMA chunk
11804 (void) WriteBlobMSBULong(image,4L);
11805 PNGType(chunk,mng_gAMA);
11806 LogPNGChunk(logging,mng_gAMA,4L);
11807 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11808 (void) WriteBlob(image,8,chunk);
11809 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11810 mng_info->have_write_global_gama=MagickTrue;
11812 if (mng_info->equal_chrms)
11818 Write MNG cHRM chunk
11820 (void) WriteBlobMSBULong(image,32L);
11821 PNGType(chunk,mng_cHRM);
11822 LogPNGChunk(logging,mng_cHRM,32L);
11823 primary=image->chromaticity.white_point;
11824 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11825 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11826 primary=image->chromaticity.red_primary;
11827 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11828 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11829 primary=image->chromaticity.green_primary;
11830 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11831 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11832 primary=image->chromaticity.blue_primary;
11833 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11834 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11835 (void) WriteBlob(image,36,chunk);
11836 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11837 mng_info->have_write_global_chrm=MagickTrue;
11840 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11843 Write MNG pHYs chunk
11845 (void) WriteBlobMSBULong(image,9L);
11846 PNGType(chunk,mng_pHYs);
11847 LogPNGChunk(logging,mng_pHYs,9L);
11849 if (image->units == PixelsPerInchResolution)
11851 PNGLong(chunk+4,(png_uint_32)
11852 (image->x_resolution*100.0/2.54+0.5));
11854 PNGLong(chunk+8,(png_uint_32)
11855 (image->y_resolution*100.0/2.54+0.5));
11862 if (image->units == PixelsPerCentimeterResolution)
11864 PNGLong(chunk+4,(png_uint_32)
11865 (image->x_resolution*100.0+0.5));
11867 PNGLong(chunk+8,(png_uint_32)
11868 (image->y_resolution*100.0+0.5));
11875 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11876 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11880 (void) WriteBlob(image,13,chunk);
11881 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11884 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11885 or does not cover the entire frame.
11887 if (write_mng && (image->matte || image->page.x > 0 ||
11888 image->page.y > 0 || (image->page.width &&
11889 (image->page.width+image->page.x < mng_info->page.width))
11890 || (image->page.height && (image->page.height+image->page.y
11891 < mng_info->page.height))))
11893 (void) WriteBlobMSBULong(image,6L);
11894 PNGType(chunk,mng_BACK);
11895 LogPNGChunk(logging,mng_BACK,6L);
11896 red=ScaleQuantumToShort(image->background_color.red);
11897 green=ScaleQuantumToShort(image->background_color.green);
11898 blue=ScaleQuantumToShort(image->background_color.blue);
11899 PNGShort(chunk+4,red);
11900 PNGShort(chunk+6,green);
11901 PNGShort(chunk+8,blue);
11902 (void) WriteBlob(image,10,chunk);
11903 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11904 if (mng_info->equal_backgrounds)
11906 (void) WriteBlobMSBULong(image,6L);
11907 PNGType(chunk,mng_bKGD);
11908 LogPNGChunk(logging,mng_bKGD,6L);
11909 (void) WriteBlob(image,10,chunk);
11910 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11914 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11915 if ((need_local_plte == MagickFalse) &&
11916 (image->storage_class == PseudoClass) &&
11917 (all_images_are_gray == MagickFalse))
11923 Write MNG PLTE chunk
11925 data_length=3*image->colors;
11926 (void) WriteBlobMSBULong(image,data_length);
11927 PNGType(chunk,mng_PLTE);
11928 LogPNGChunk(logging,mng_PLTE,data_length);
11930 for (i=0; i < (ssize_t) image->colors; i++)
11932 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11933 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11934 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11937 (void) WriteBlob(image,data_length+4,chunk);
11938 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11939 mng_info->have_write_global_plte=MagickTrue;
11945 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11946 defined(PNG_MNG_FEATURES_SUPPORTED)
11947 mng_info->equal_palettes=MagickFalse;
11951 if (mng_info->adjoin)
11953 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11954 defined(PNG_MNG_FEATURES_SUPPORTED)
11956 If we aren't using a global palette for the entire MNG, check to
11957 see if we can use one for two or more consecutive images.
11959 if (need_local_plte && use_global_plte && !all_images_are_gray)
11961 if (mng_info->IsPalette)
11964 When equal_palettes is true, this image has the same palette
11965 as the previous PseudoClass image
11967 mng_info->have_write_global_plte=mng_info->equal_palettes;
11968 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11969 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11972 Write MNG PLTE chunk
11977 data_length=3*image->colors;
11978 (void) WriteBlobMSBULong(image,data_length);
11979 PNGType(chunk,mng_PLTE);
11980 LogPNGChunk(logging,mng_PLTE,data_length);
11982 for (i=0; i < (ssize_t) image->colors; i++)
11984 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11985 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11986 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11989 (void) WriteBlob(image,data_length+4,chunk);
11990 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11991 (uInt) (data_length+4)));
11992 mng_info->have_write_global_plte=MagickTrue;
11996 mng_info->have_write_global_plte=MagickFalse;
12007 previous_x=mng_info->page.x;
12008 previous_y=mng_info->page.y;
12015 mng_info->page=image->page;
12016 if ((mng_info->page.x != previous_x) ||
12017 (mng_info->page.y != previous_y))
12019 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12020 PNGType(chunk,mng_DEFI);
12021 LogPNGChunk(logging,mng_DEFI,12L);
12022 chunk[4]=0; /* object 0 MSB */
12023 chunk[5]=0; /* object 0 LSB */
12024 chunk[6]=0; /* visible */
12025 chunk[7]=0; /* abstract */
12026 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12027 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12028 (void) WriteBlob(image,16,chunk);
12029 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12034 mng_info->write_mng=write_mng;
12036 if ((int) image->dispose >= 3)
12037 mng_info->framing_mode=3;
12039 if (mng_info->need_fram && mng_info->adjoin &&
12040 ((image->delay != mng_info->delay) ||
12041 (mng_info->framing_mode != mng_info->old_framing_mode)))
12043 if (image->delay == mng_info->delay)
12046 Write a MNG FRAM chunk with the new framing mode.
12048 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12049 PNGType(chunk,mng_FRAM);
12050 LogPNGChunk(logging,mng_FRAM,1L);
12051 chunk[4]=(unsigned char) mng_info->framing_mode;
12052 (void) WriteBlob(image,5,chunk);
12053 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12058 Write a MNG FRAM chunk with the delay.
12060 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12061 PNGType(chunk,mng_FRAM);
12062 LogPNGChunk(logging,mng_FRAM,10L);
12063 chunk[4]=(unsigned char) mng_info->framing_mode;
12064 chunk[5]=0; /* frame name separator (no name) */
12065 chunk[6]=2; /* flag for changing default delay */
12066 chunk[7]=0; /* flag for changing frame timeout */
12067 chunk[8]=0; /* flag for changing frame clipping */
12068 chunk[9]=0; /* flag for changing frame sync_id */
12069 PNGLong(chunk+10,(png_uint_32)
12070 ((mng_info->ticks_per_second*
12071 image->delay)/MagickMax(image->ticks_per_second,1)));
12072 (void) WriteBlob(image,14,chunk);
12073 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12074 mng_info->delay=(png_uint_32) image->delay;
12076 mng_info->old_framing_mode=mng_info->framing_mode;
12079 #if defined(JNG_SUPPORTED)
12080 if (image_info->compression == JPEGCompression)
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " Writing JNG object.");
12088 /* To do: specify the desired alpha compression method. */
12089 write_info=CloneImageInfo(image_info);
12090 write_info->compression=UndefinedCompression;
12091 status=WriteOneJNGImage(mng_info,write_info,image);
12092 write_info=DestroyImageInfo(write_info);
12097 if (logging != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " Writing PNG object.");
12101 mng_info->need_blob = MagickFalse;
12102 mng_info->ping_preserve_colormap = MagickFalse;
12104 /* We don't want any ancillary chunks written */
12105 mng_info->ping_exclude_bKGD=MagickTrue;
12106 mng_info->ping_exclude_cHRM=MagickTrue;
12107 mng_info->ping_exclude_date=MagickTrue;
12108 mng_info->ping_exclude_EXIF=MagickTrue;
12109 mng_info->ping_exclude_gAMA=MagickTrue;
12110 mng_info->ping_exclude_iCCP=MagickTrue;
12111 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12112 mng_info->ping_exclude_oFFs=MagickTrue;
12113 mng_info->ping_exclude_pHYs=MagickTrue;
12114 mng_info->ping_exclude_sRGB=MagickTrue;
12115 mng_info->ping_exclude_tEXt=MagickTrue;
12116 mng_info->ping_exclude_tRNS=MagickTrue;
12117 mng_info->ping_exclude_vpAg=MagickTrue;
12118 mng_info->ping_exclude_zCCP=MagickTrue;
12119 mng_info->ping_exclude_zTXt=MagickTrue;
12121 status=WriteOnePNGImage(mng_info,image_info,image);
12124 if (status == MagickFalse)
12126 MngInfoFreeStruct(mng_info,&have_mng_structure);
12127 (void) CloseBlob(image);
12128 return(MagickFalse);
12130 (void) CatchImageException(image);
12131 if (GetNextImageInList(image) == (Image *) NULL)
12133 image=SyncNextImageInList(image);
12134 status=SetImageProgress(image,SaveImagesTag,scene++,
12135 GetImageListLength(image));
12137 if (status == MagickFalse)
12140 } while (mng_info->adjoin);
12144 while (GetPreviousImageInList(image) != (Image *) NULL)
12145 image=GetPreviousImageInList(image);
12147 Write the MEND chunk.
12149 (void) WriteBlobMSBULong(image,0x00000000L);
12150 PNGType(chunk,mng_MEND);
12151 LogPNGChunk(logging,mng_MEND,0L);
12152 (void) WriteBlob(image,4,chunk);
12153 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12156 Relinquish resources.
12158 (void) CloseBlob(image);
12159 MngInfoFreeStruct(mng_info,&have_mng_structure);
12161 if (logging != MagickFalse)
12162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12164 return(MagickTrue);
12166 #else /* PNG_LIBPNG_VER > 10011 */
12168 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12171 printf("Your PNG library is too old: You have libpng-%s\n",
12172 PNG_LIBPNG_VER_STRING);
12174 ThrowBinaryException(CoderError,"PNG library is too old",
12175 image_info->filename);
12178 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12180 return(WritePNGImage(image_info,image));
12182 #endif /* PNG_LIBPNG_VER > 10011 */