/* % Copyright (C) 2003, 2004 GraphicsMagick Group % Copyright (C) 2002 ImageMagick Studio % Copyright 1991-1999 E. I. du Pont de Nemours and Company % % This program is covered by multiple licenses, which are described in % Copyright.txt. You should have received a copy of Copyright.txt with this % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % PPPP N N GGGG % % P P NN N G % % PPPP N N N G GG % % P N NN G G % % P N N GGG % % % % % % Read/Write Portable Network Graphics Image Format. % % % % % % Software Design % % John Cristy % % Glenn Randers-Pehrson % % November 1997 % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ /* GraphicsMagick differences */ #define LoadImageTag LoadImageText #define SaveImageTag SaveImageText #define LoadImagesTag LoadImagesText #define SaveImagesTag SaveImagesText #define ParseMetaGeometry GetMagickGeometry #define AcquireUniqueFilename AcquireTemporaryFileName #define LiberateUniqueFileResource LiberateTemporaryFile #define first_scene subimage #define number_scenes subrange #if !defined(RGBColorMatchExact) #define RGBColorMatchExact(color,target) \ (((color).red == (target).red) && \ ((color).green == (target).green) && \ ((color).blue == (target).blue)) #endif /* Include declarations. */ #include "magick/studio.h" #include "magick/attribute.h" #include "magick/blob.h" #include "magick/cache.h" #include "magick/color.h" #include "magick/constitute.h" #include "magick/enhance.h" #include "magick/log.h" #include "magick/magick.h" #include "magick/monitor.h" #include "magick/quantize.h" #include "magick/static.h" #include "magick/semaphore.h" #include "magick/tempfile.h" #include "magick/transform.h" #include "magick/utility.h" #if defined(HasPNG) #include "png.h" #include "zlib.h" #if PNG_LIBPNG_VER > 95 /* Optional declarations. Define or undefine them as you like. */ /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */ /* Features under construction. Define these to work on them. */ #undef MNG_OBJECT_BUFFERS #undef MNG_BASI_SUPPORTED #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */ #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */ #define PNG_BUILD_PALETTE /* This works as of 5.4.3. */ #define PNG_SORT_PALETTE /* This works as of 5.4.0. */ #if defined(HasJPEG) # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */ #endif /* Establish thread safety. setjmp/longjmp is claimed to be safe on these platforms: setjmp/longjmp is alleged to be unsafe on these platforms: */ #ifndef SETJMP_IS_THREAD_SAFE #define PNG_SETJMP_NOT_THREAD_SAFE #endif #if defined(PNG_SETJMP_NOT_THREAD_SAFE) static SemaphoreInfo *png_semaphore = (SemaphoreInfo *) NULL; #endif /* This is temporary until I set up malloc'ed object attributes array. Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but waste more memory. */ #define MNG_MAX_OBJECTS 256 /* If this is not defined, spec is interpreted strictly. If it is defined, an attempt will be made to recover from some errors, including o global PLTE too short */ #undef MNG_LOOSE /* Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8, PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here. PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and will be enabled by default in libpng-1.2.0. */ #if (PNG_LIBPNG_VER == 10009) /* work around libpng-1.0.9 bug */ # undef PNG_READ_EMPTY_PLTE_SUPPORTED # undef PNG_WRITE_EMPTY_PLTE_SUPPORTED #endif #ifdef PNG_MNG_FEATURES_SUPPORTED # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED # define PNG_READ_EMPTY_PLTE_SUPPORTED # endif # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED # define PNG_WRITE_EMPTY_PLTE_SUPPORTED # endif #endif /* Maximum valid unsigned long in PNG/MNG chunks is (2^31)-1 This macro is only defined in libpng-1.0.3a and later. */ #ifndef PNG_MAX_UINT #define PNG_MAX_UINT (png_uint_32) 0x7fffffffL #endif /* Constant strings for known chunk types. If you need to add a chunk, add a string holding the name here. To make the code more portable, we use ASCII numbers like this, not characters. */ static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, '\0'}; static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, '\0'}; static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, '\0'}; static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, '\0'}; static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, '\0'}; static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, '\0'}; static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, '\0'}; static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, '\0'}; static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, '\0'}; static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, '\0'}; static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, '\0'}; static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, '\0'}; static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, '\0'}; static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, '\0'}; static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, '\0'}; static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, '\0'}; static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, '\0'}; static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, '\0'}; static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, '\0'}; static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, '\0'}; static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, '\0'}; static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, '\0'}; static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, '\0'}; static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, '\0'}; static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, '\0'}; static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, '\0'}; static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, '\0'}; static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, '\0'}; static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, '\0'}; static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, '\0'}; static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, '\0'}; static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, '\0'}; #if defined(JNG_SUPPORTED) static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, '\0'}; static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, '\0'}; static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, '\0'}; static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, '\0'}; static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, '\0'}; static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, '\0'}; #endif /* static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, '\0'}; static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, '\0'}; static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, '\0'}; static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, '\0'}; static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, '\0'}; static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, '\0'}; static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, '\0'}; */ typedef struct _MngBox { long left, right, top, bottom; } MngBox; typedef struct _MngPair { volatile long a, b; } MngPair; #ifdef MNG_OBJECT_BUFFERS typedef struct _MngBuffer { unsigned long height, width; Image *image; png_color plte[256]; int reference_count; unsigned char alpha_sample_depth, compression_method, color_type, concrete, filter_method, frozen, image_type, interlace_method, pixel_sample_depth, plte_length, sample_depth, viewable; } MngBuffer; #endif typedef struct _MngInfo { #ifdef MNG_OBJECT_BUFFERS MngBuffer *ob[MNG_MAX_OBJECTS]; #endif Image * image; RectangleInfo page; int adjoin, #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED bytes_in_read_buffer, found_empty_plte, #endif equal_backgrounds, equal_chrms, equal_gammas, #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \ defined(PNG_MNG_FEATURES_SUPPORTED) equal_palettes, #endif equal_physs, equal_srgbs, framing_mode, have_global_bkgd, have_global_chrm, have_global_gama, have_global_phys, have_global_sbit, have_global_srgb, have_saved_bkgd_index, have_write_global_chrm, have_write_global_gama, have_write_global_plte, have_write_global_srgb, need_fram, object_id, old_framing_mode, optimize, saved_bkgd_index; int new_number_colors; long image_found, loop_count[256], loop_iteration[256], scenes_found, x_off[MNG_MAX_OBJECTS], y_off[MNG_MAX_OBJECTS]; MngBox clip, frame, image_box, object_clip[MNG_MAX_OBJECTS]; unsigned char /* These flags could be combined into one byte */ exists[MNG_MAX_OBJECTS], frozen[MNG_MAX_OBJECTS], loop_active[256], invisible[MNG_MAX_OBJECTS], viewable[MNG_MAX_OBJECTS]; ExtendedSignedIntegralType loop_jump[256]; png_colorp global_plte; png_color_8 global_sbit; png_byte #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED read_buffer[8], #endif global_trns[256]; float global_gamma; ChromaticityInfo global_chrm; RenderingIntent global_srgb_intent; unsigned long delay, global_plte_length, global_trns_length, global_x_pixels_per_unit, global_y_pixels_per_unit, mng_width, mng_height, ticks_per_second; unsigned int IsPalette, global_phys_unit_type, basi_warning, clon_warning, dhdr_warning, jhdr_warning, magn_warning, past_warning, phyg_warning, phys_warning, sbit_warning, show_warning, mng_type, write_mng, write_png8, write_png24, write_png32; #ifdef MNG_BASI_SUPPORTED unsigned long basi_width, basi_height; unsigned int basi_depth, basi_color_type, basi_compression_method, basi_filter_type, basi_interlace_method, basi_red, basi_green, basi_blue, basi_alpha, basi_viewable; #endif png_uint_16 magn_first, magn_last, magn_mb, magn_ml, magn_mr, magn_mt, magn_mx, magn_my, magn_methx, magn_methy; PixelPacket mng_global_bkgd; } MngInfo; #endif /* VER */ /* Forward declarations. */ static unsigned int WritePNGImage(const ImageInfo *,Image *); static unsigned int WriteMNGImage(const ImageInfo *,Image *); #if defined(JNG_SUPPORTED) static unsigned int WriteJNGImage(const ImageInfo *,Image *); #endif #if PNG_LIBPNG_VER > 95 #if defined(PNG_SORT_PALETTE) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p r e s s C o l o r m a p T r a n s F i r s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method CompressColormapTransFirst compresses an image colormap removing % any duplicate and unused color entries and putting the transparent colors % first. % % The format of the CompressColormapTransFirst method is: % % unsigned int CompressColormapTransFirst(Image *image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ static unsigned int CompressColormapTransFirst(Image *image) { int remap_needed, k; long j, new_number_colors, number_colors, y; PixelPacket *colormap; register const PixelPacket *p; register IndexPacket *indexes, top_used; register long i, x; IndexPacket *map, *opacity; unsigned char *marker, have_transparency; /* Determine if colormap can be compressed. */ assert(image != (Image *) NULL); if (image->storage_class != PseudoClass || image->colors > 256 || image->colors < 2) return(False); marker=MagickAllocateMemory(unsigned char *,image->colors); if (marker == (unsigned char *) NULL) ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, "Unable to compress image colormap") opacity=MagickAllocateMemory(IndexPacket *, image->colors*sizeof(IndexPacket)); if (opacity == (IndexPacket *) NULL) { MagickFreeMemory(marker); ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, "Unable to compress image colormap") } /* Mark colors that are present. */ number_colors=(long) image->colors; for (i=0; i < number_colors; i++) { marker[i]=False; opacity[i]=OpaqueOpacity; } top_used=0; for (y=0; y < (long) image->rows; y++) { p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); if (p == (const PixelPacket *) NULL) break; indexes=GetIndexes(image); if (image->matte) for (x=0; x < (long) image->columns; x++) { marker[(int) indexes[x]]=True; opacity[(int) indexes[x]]=p->opacity; if (indexes[x] > top_used) top_used=indexes[x]; p++; } else for (x=0; x < (long) image->columns; x++) { marker[(int) indexes[x]]=True; if (indexes[x] > top_used) top_used=indexes[x]; } } if (image->matte) { /* Mark background color, topmost occurrence if more than one. */ for (i=number_colors-1; i; i--) { if (RGBColorMatchExact(image->colormap[i],image->background_color)) { marker[i]=True; break; } } } /* Unmark duplicates. */ for (i=0; i < number_colors-1; i++) if (marker[i]) { for (j=i+1; j < number_colors; j++) if ((opacity[i] == opacity[j]) && (RGBColorMatchExact(image->colormap[i],image->colormap[j]))) marker[j]=False; } /* Count colors that still remain. */ have_transparency=False; new_number_colors=0; for (i=0; i < number_colors; i++) if (marker[i]) { new_number_colors++; if (opacity[i] != OpaqueOpacity) have_transparency=True; } if ((!have_transparency || (marker[0] && (opacity[0] == TransparentOpacity))) && (new_number_colors == number_colors)) { /* No duplicate or unused entries, and transparency-swap not needed */ MagickFreeMemory(marker); MagickFreeMemory(opacity); return(True); } remap_needed=False; if ((long) top_used >= new_number_colors) remap_needed=True; /* Compress colormap. */ colormap=MagickAllocateMemory(PixelPacket *,image->colors*sizeof(PixelPacket)); if (colormap == (PixelPacket *) NULL) { MagickFreeMemory(marker); MagickFreeMemory(opacity); ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, "Unable to compress image colormap") } /* Eliminate unused colormap entries. */ map=MagickAllocateMemory(IndexPacket *,number_colors*sizeof(IndexPacket)); if (map == (IndexPacket *) NULL) { MagickFreeMemory(marker); MagickFreeMemory(opacity); MagickFreeMemory(colormap); ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, "Unable to compress image colormap") } k=0; for (i=0; i < number_colors; i++) { map[i]=(IndexPacket) k; if (marker[i]) { for (j=i+1; j < number_colors; j++) { if ((opacity[i] == opacity[j]) && (RGBColorMatchExact(image->colormap[i],image->colormap[j]))) { map[j]=(IndexPacket) k; marker[j]=False; } } k++; } } j=0; for (i=0; i < number_colors; i++) { if (marker[i]) { colormap[j]=image->colormap[i]; j++; } } if (have_transparency && (opacity[0] != TransparentOpacity)) { /* Move the first transparent color to palette entry 0. */ for (i=1; i < number_colors; i++) { if (marker[i] && opacity[i] == TransparentOpacity) { PixelPacket temp_colormap; temp_colormap=colormap[0]; colormap[0]=colormap[(int) map[i]]; colormap[map[i]]=temp_colormap; for (j=0; j < number_colors; j++) { if (map[j] == 0) map[j]=map[i]; else if (map[j] == map[i]) map[j]=0; } remap_needed=True; break; } } } MagickFreeMemory(opacity); MagickFreeMemory(marker); if (remap_needed) { /* Remap pixels. */ for (y=0; y < (long) image->rows; y++) { p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); if (p == (const PixelPacket *) NULL) break; indexes=GetIndexes(image); for (x=0; x < (long) image->columns; x++) { j=(int) indexes[x]; indexes[x]=map[j]; } if (!SyncImagePixels(image)) break; } for (i=0; i < new_number_colors; i++) image->colormap[i]=colormap[i]; } MagickFreeMemory(colormap); image->colors=new_number_colors; MagickFreeMemory(map); return(True); } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I m a g e I s G r a y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % Like IsGrayImage except does not change DirectClass to PseudoClass % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ static unsigned int ImageIsGray(Image *image) { register const PixelPacket *p; register long i, x, y; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->storage_class == PseudoClass) { for (i=0; i < (long) image->colors; i++) if (!IsGray(image->colormap[i])) return(False); return(True); } for (y=0; y < (long) image->rows; y++) { p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); if (p == (const PixelPacket *) NULL) return(False); for (x=(long) image->columns; x > 0; x--) { if (!IsGray(*p)) return(False); p++; } } return(True); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I m a g e I s M o n o c h r o m e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % Like IsMonochromeImage except does not change DirectClass to PseudoClass % % and is more accurate. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ static unsigned int ImageIsMonochrome(Image *image) { register const PixelPacket *p; register long i, x, y; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->storage_class == PseudoClass) { for (i=0; i < (long) image->colors; i++) { if (!IsGray(image->colormap[i]) || ((image->colormap[i].red != 0) && (image->colormap[i].red != MaxRGB))) return(False); } return(True); } for (y=0; y < (long) image->rows; y++) { p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); if (p == (const PixelPacket *) NULL) return(False); for (x=(long) image->columns; x > 0; x--) { if ((p->red != 0) && (p->red != MaxRGB)) return(False); if (!IsGray(*p)) return(False); p++; } } return(True); } #endif /* PNG_LIBPNG_VER > 95 */ #endif /* HasPNG */ /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s M N G % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method IsMNG returns True if the image format type, identified by the % magick string, is MNG. % % The format of the IsMNG method is: % % unsigned int IsMNG(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o status: Method IsMNG returns True if the image format type is MNG. % % o magick: This string is generally the first few bytes of an image file % or blob. % % o length: Specifies the length of the magick string. % % */ static unsigned int IsMNG(const unsigned char *magick,const size_t length) { if (length < 8) return(False); if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0) return(True); return(False); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s J N G % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method IsJNG returns True if the image format type, identified by the % magick string, is JNG. % % The format of the IsJNG method is: % % unsigned int IsJNG(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o status: Method IsJNG returns True if the image format type is JNG. % % o magick: This string is generally the first few bytes of an image file % or blob. % % o length: Specifies the length of the magick string. % % */ static unsigned int IsJNG(const unsigned char *magick,const size_t length) { if (length < 8) return(False); if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0) return(True); return(False); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s P N G % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method IsPNG returns True if the image format type, identified by the % magick string, is PNG. % % The format of the IsPNG method is: % % unsigned int IsPNG(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o status: Method IsPNG returns True if the image format type is PNG. % % o magick: This string is generally the first few bytes of an image file % or blob. % % o length: Specifies the length of the magick string. % % */ static unsigned int IsPNG(const unsigned char *magick,const size_t length) { if (length < 8) return(False); if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0) return(True); return(False); } #if defined(HasPNG) #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #if (PNG_LIBPNG_VER > 95) static size_t WriteBlobMSBULong(Image *image,const unsigned long value) { unsigned char buffer[4]; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); buffer[0]=(unsigned char) (value >> 24); buffer[1]=(unsigned char) (value >> 16); buffer[2]=(unsigned char) (value >> 8); buffer[3]=(unsigned char) value; return(WriteBlob(image,4,buffer)); } static void PNGLong(png_bytep p,png_uint_32 value) { *p++=(png_byte) ((value >> 24) & 0xff); *p++=(png_byte) ((value >> 16) & 0xff); *p++=(png_byte) ((value >> 8) & 0xff); *p++=(png_byte) (value & 0xff); } static void PNGsLong(png_bytep p,png_int_32 value) { *p++=(png_byte) ((value >> 24) & 0xff); *p++=(png_byte) ((value >> 16) & 0xff); *p++=(png_byte) ((value >> 8) & 0xff); *p++=(png_byte) (value & 0xff); } static void PNGShort(png_bytep p,png_uint_16 value) { *p++=(png_byte) ((value >> 8) & 0xff); *p++=(png_byte) (value & 0xff); } static void PNGType(png_bytep p,png_bytep type) { (void) memcpy(p,type,4*sizeof(png_byte)); } static void LogPNGChunk(int logging, png_bytep type, unsigned long length) { if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Writing %c%c%c%c chunk, length: %lu", type[0],type[1],type[2],type[3],length); } #endif /* PNG_LIBPNG_VER > 95 */ #if defined(__cplusplus) || defined(c_plusplus) } #endif #if PNG_LIBPNG_VER > 95 /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d P N G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method ReadPNGImage reads a Portable Network Graphics (PNG) or % Multiple-image Network Graphics (MNG) image file and returns it. It % allocates the memory necessary for the new Image structure and returns a % pointer to the new image or set of images. % % MNG support written by Glenn Randers-Pehrson, randeg@alum.rpi.edu. % % The format of the ReadPNGImage method is: % % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: Method ReadPNGImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or % if the image cannot be read. % % o image_info: Specifies a pointer to a ImageInfo structure. % % o exception: return any errors or warnings in this structure. % % To do, more or less in chronological order (as of version 5.5.2, % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage): % % Get 16-bit cheap transparency working. % % (At this point, PNG decoding is supposed to be in full MNG-LC compliance) % % Preserve all unknown and not-yet-handled known chunks found in input % PNG file and copy them into output PNG files according to the PNG % copying rules. % % (At this point, PNG encoding should be in full MNG compliance) % % Provide options for choice of background to use when the MNG BACK % chunk is not present or is not mandatory (i.e., leave transparent, % user specified, MNG BACK, PNG bKGD) % % Implement LOOP/ENDL [done, but could do discretionary loops more % efficiently by linking in the duplicate frames.]. % % Decode and act on the MHDR simplicity profile (offer option to reject % files or attempt to process them anyway when the profile isn't LC or VLC). % % Upgrade to full MNG without Delta-PNG. % % o BACK [done a while ago except for background image ID] % o MOVE [done 15 May 1999] % o CLIP [done 15 May 1999] % o DISC [done 19 May 1999] % o SAVE [partially done 19 May 1999 (marks objects frozen)] % o SEEK [partially done 19 May 1999 (discard function only)] % o SHOW % o PAST % o BASI % o MNG-level tEXt/iTXt/zTXt % o pHYg % o pHYs % o sBIT % o bKGD % o iTXt (wait for libpng implementation). % % Use the scene signature to discover when an identical scene is % being reused, and just point to the original image->pixels instead % of storing another set of pixels. This is not specific to MNG % but could be applied generally. % % Upgrade to full MNG with Delta-PNG. % % JNG tEXt/iTXt/zTXt */ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif /* This is the function that does the actual reading of data. It is the same as the one supplied in libpng, except that it receives the datastream from the ReadBlob() function instead of standard input. */ static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length) { Image *image; image=(Image *) png_get_io_ptr(png_ptr); if (length) { png_size_t check; check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data); if (check != length) { char msg[MaxTextExtent]; sprintf(msg,"Expected %lu bytes; found %lu bytes", (unsigned long) length,(unsigned long) check); png_warning(png_ptr,msg); png_error(png_ptr,"Read Exception"); } } } #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \ !defined(PNG_MNG_FEATURES_SUPPORTED) /* We use mng_get_data() instead of png_get_data() if we have a libpng * older than libpng-1.0.3a, which was the first to allow the empty * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was * ifdef'ed out. Earlier versions would crash if the bKGD chunk was * encountered after an empty PLTE, so we have to look ahead for bKGD * chunks and remove them from the datastream that is passed to libpng, * and store their contents for later use. */ static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length) { MngInfo *mng_info; Image *image; png_size_t check; register long i; i=0; mng_info=(MngInfo *) png_get_io_ptr(png_ptr); image=(Image *) mng_info->image; while (mng_info->bytes_in_read_buffer && length) { data[i]=mng_info->read_buffer[i]; mng_info->bytes_in_read_buffer--; length--; i++; } if (length) { check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data); if (check != length) png_error(png_ptr,"Read Exception"); if (length == 4) { if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 0)) { check=(png_size_t) ReadBlob(image,(size_t) length, (char *) mng_info->read_buffer); mng_info->read_buffer[4]=0; mng_info->bytes_in_read_buffer=4; if (!memcmp(mng_info->read_buffer,mng_PLTE,4)) mng_info->found_empty_plte=True; if (!memcmp(mng_info->read_buffer,mng_IEND,4)) { mng_info->found_empty_plte=False; mng_info->have_saved_bkgd_index=False; } } if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) { check=(png_size_t) ReadBlob(image,(size_t) length, (char *) mng_info->read_buffer); mng_info->read_buffer[4]=0; mng_info->bytes_in_read_buffer=4; if (!memcmp(mng_info->read_buffer,mng_bKGD,4)) if (mng_info->found_empty_plte) { /* Skip the bKGD data byte and CRC. */ check=(png_size_t) ReadBlob(image,5,(char *) mng_info->read_buffer); check=(png_size_t) ReadBlob(image,(size_t) length, (char *) mng_info->read_buffer); mng_info->saved_bkgd_index=mng_info->read_buffer[0]; mng_info->have_saved_bkgd_index=True; mng_info->bytes_in_read_buffer=0; } } } } } #endif static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length) { Image *image; image=(Image *) png_get_io_ptr(png_ptr); if (length) { png_size_t check; check=(png_size_t) WriteBlob(image,(unsigned long) length,(char *) data); if (check != length) png_error(png_ptr,"WriteBlob Failed"); } } static void png_flush_data(png_structp png_ptr) { Image *image; image=(Image *) png_get_io_ptr(png_ptr); (void) SyncBlob(image); } #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED static int PalettesAreEqual(Image *a,Image *b) { long i; if ((a == (Image *) NULL) || (b == (Image *) NULL)) return((int) False); if (a->storage_class!=PseudoClass || b->storage_class!=PseudoClass) return((int) False); if (a->colors != b->colors) return((int) False); for (i=0; i < (long) a->colors; i++) { if ((a->colormap[i].red != b->colormap[i].red) || (a->colormap[i].green != b->colormap[i].green) || (a->colormap[i].blue != b->colormap[i].blue)) return((int) False); } return((int) True); } #endif static void MngInfoDiscardObject(MngInfo *mng_info,int i) { if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) && mng_info->exists[i] && !mng_info->frozen[i]) { #ifdef MNG_OBJECT_BUFFERS if (mng_info->ob[i] != (MngBuffer *) NULL) { if (mng_info->ob[i]->reference_count > 0) mng_info->ob[i]->reference_count--; if (mng_info->ob[i]->reference_count == 0) { if (mng_info->ob[i]->image != (Image *) NULL) DestroyImage(mng_info->ob[i]->image); MagickFreeMemory(mng_info->ob[i]); } } mng_info->ob[i]=(MngBuffer *) NULL; #endif mng_info->exists[i]=False; mng_info->invisible[i]=False; mng_info->viewable[i]=False; mng_info->frozen[i]=False; mng_info->x_off[i]=0; mng_info->y_off[i]=0; mng_info->object_clip[i].left=0; mng_info->object_clip[i].right=PNG_MAX_UINT; mng_info->object_clip[i].top=0; mng_info->object_clip[i].bottom=PNG_MAX_UINT; } } static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure) { if (*have_mng_structure && (mng_info != (MngInfo *) NULL)) { register long i; for (i=1; i < MNG_MAX_OBJECTS; i++) MngInfoDiscardObject(mng_info,i); if (mng_info->global_plte != (png_colorp) NULL) MagickFreeMemory(mng_info->global_plte); MagickFreeMemory(mng_info); *have_mng_structure=False; } } static MngBox mng_minimum_box(MngBox box1,MngBox box2) { MngBox box; box=box1; if (box.left < box2.left) box.left=box2.left; if (box.top < box2.top) box.top=box2.top; if (box.right > box2.right) box.right=box2.right; if (box.bottom > box2.bottom) box.bottom=box2.bottom; return box; } static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p) { MngBox box; /* Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk. */ box.left=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); box.right=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); box.top=(long) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]); box.bottom=(long) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]); if (delta_type != 0) { box.left+=previous_box.left; box.right+=previous_box.right; box.top+=previous_box.top; box.bottom+=previous_box.bottom; } return(box); } static MngPair mng_read_pair(MngPair previous_pair,int delta_type, unsigned char *p) { MngPair pair; /* Read two longs from CLON, MOVE or PAST chunk */ pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); if (delta_type != 0) { pair.a+=previous_pair.a; pair.b+=previous_pair.b; } return(pair); } static long mng_get_long(unsigned char *p) { return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])); } static void PNGErrorHandler(png_struct *ping,png_const_charp message) { Image *image; image=(Image *) png_get_error_ptr(ping); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " libpng-%.1024s error: %.1024s", PNG_LIBPNG_VER_STRING, message); (void) ThrowException2(&image->exception,CoderError,message,image->filename); longjmp(ping->jmpbuf,1); } static void PNGWarningHandler(png_struct *ping,png_const_charp message) { Image *image; if (LocaleCompare(message, "Missing PLTE before tRNS") == 0) png_error(ping, message); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " libpng-%.1024s warning: %.1024s", PNG_LIBPNG_VER_STRING, message); image=(Image *) png_get_error_ptr(ping); (void) ThrowException2(&image->exception,CoderWarning,message,image->filename); } #ifdef PNG_USER_MEM_SUPPORTED static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size) { #if (PNG_LIBPNG_VER < 10011) png_voidp ret; png_ptr=png_ptr; ret=MagickAllocateMemory(png_voidp,(size_t) size); if (ret == NULL) png_error("Insufficient memory."); return (ret); #else png_ptr=png_ptr; return MagickAllocateMemory(png_voidp,(size_t) size); #endif } /* Free a pointer. It is removed from the list at the same time. */ static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr) { png_ptr=png_ptr; MagickFreeMemory(ptr); return((png_free_ptr) NULL); } #endif #if defined(__cplusplus) || defined(c_plusplus) } #endif static int png_read_raw_profile(Image *image, const ImageInfo *image_info, png_textp text,int ii) { unsigned char *info; register long i; register unsigned char *dp; register png_charp sp; png_uint_32 length, nibbles; unsigned char unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12, 13,14,15}; sp=text[ii].text+1; /* look for newline */ while (*sp != '\n') sp++; /* look for length */ while (*sp == '\0' || *sp == ' ' || *sp == '\n') sp++; length=atol(sp); while (*sp != ' ' && *sp != '\n') sp++; /* allocate space */ if (length == 0) { (void) ThrowException(&image->exception,CoderError,UnableToCopyProfile, "invalid profile length"); return (False); } info=MagickAllocateMemory(unsigned char *,length); if (info == (unsigned char *) NULL) { (void) ThrowException(&image->exception,ResourceLimitError,MemoryAllocationFailed,"unable to copy profile"); return (False); } /* copy profile, skipping white space and column 1 "=" signs */ dp=info; nibbles=length*2; for (i=0; i < (long) nibbles; i++) { while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') { if (*sp == '\0') { (void) ThrowException(&image->exception,CoderError,UnableToCopyProfile, "ran out of data"); MagickFreeMemory(info); return (False); } sp++; } if (i%2 == 0) *dp=16*unhex[(int) *sp++]; else (*dp++)+=unhex[(int) *sp++]; } /* We have already read "Raw profile type " */ if (!memcmp(&text[ii].key[17], "iptc\0",5)) { image->iptc_profile.length=length; image->iptc_profile.info=info; if (image_info->verbose) (void) printf(" Found an IPTC profile.\n"); } else if (!memcmp(&text[ii].key[17], "icm\0",4)) { image->color_profile.length=length; image->color_profile.info=info; if (image_info->verbose) (void) printf(" Found an ICM (ICCP) profile.\n"); } else { i=(long) image->generic_profiles; if (image->generic_profile == (ProfileInfo *) NULL) image->generic_profile=MagickAllocateMemory(ProfileInfo *, sizeof(ProfileInfo)); else MagickReallocMemory(image->generic_profile, (i+1)*sizeof(ProfileInfo)); image->generic_profile[i].length=length; image->generic_profile[i].name=AllocateString(&text[ii].key[17]); image->generic_profile[i].info=info; image->generic_profiles++; if (image_info->verbose) (void) printf(" Found a generic profile, type %.1024s\n", &text[ii].key[17]); } return True; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d O n e P N G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method ReadOnePNGImage reads a Portable Network Graphics (PNG) image file % (minus the 8-byte signature) and returns it. It allocates the memory % necessary for the new Image structure and returns a pointer to the new % image. % % The format of the ReadOnePNGImage method is: % % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: Method ReadOnePNGImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or % if the image cannot be read. % % o mng_info: Specifies a pointer to a MngInfo structure. % % o image_info: Specifies a pointer to a ImageInfo structure. % % o exception: return any errors or warnings in this structure. % */ static Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info, ExceptionInfo *exception) { /* Read one PNG image */ Image *image; int logging, num_text, num_passes, pass; PixelPacket transparent_color; png_info *end_info, *ping_info; png_struct *ping; png_textp text; unsigned char *png_pixels; long y; register unsigned char *p; register IndexPacket *indexes; register long i, x; register PixelPacket *q; unsigned long length, row_offset; #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) png_byte unused_chunks[]= { 104, 73, 83, 84, '\0', /* hIST */ 105, 84, 88, 116, '\0', /* iTXt */ 112, 67, 65, 76, '\0', /* pCAL */ 115, 67, 65, 76, '\0', /* sCAL */ 115, 80, 76, 84, '\0', /* sPLT */ 116, 73, 77, 69, '\0', /* tIME */ }; #endif logging=LogMagickEvent(CoderEvent,GetMagickModule(), " enter ReadOnePNGImage()"); #if defined(PNG_SETJMP_NOT_THREAD_SAFE) AcquireSemaphoreInfo(&png_semaphore); #endif #if (PNG_LIBPNG_VER < 10007) if (image_info->verbose) printf("Your PNG library (libpng-%s) is rather old.\n", PNG_LIBPNG_VER_STRING); #endif image=mng_info->image; /* Allocate the PNG structures */ #ifdef PNG_USER_MEM_SUPPORTED ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image, PNGErrorHandler,PNGWarningHandler, NULL, (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free); #else ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image, PNGErrorHandler,PNGWarningHandler); #endif if (ping == (png_struct *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); ping_info=png_create_info_struct(ping); if (ping_info == (png_info *) NULL) { png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL); ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image) } end_info=png_create_info_struct(ping); if (end_info == (png_info *) NULL) { png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL); ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image) } png_pixels=(unsigned char *) NULL; if (setjmp(ping->jmpbuf)) { /* PNG image is corrupt. */ png_destroy_read_struct(&ping,&ping_info,&end_info); #if defined(PNG_SETJMP_NOT_THREAD_SAFE) LiberateSemaphoreInfo(&png_semaphore); #endif if (png_pixels != (unsigned char *) NULL) MagickFreeMemory(png_pixels); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " exit ReadOnePNGImage() with error."); if (image != (Image *) NULL) image->columns=0; return(image); } /* Prepare PNG for reading. */ mng_info->image_found++; png_set_sig_bytes(ping,8); if (LocaleCompare(image_info->magick,"MNG") == 0) { #if defined(PNG_MNG_FEATURES_SUPPORTED) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES); png_set_read_fn(ping,image,png_get_data); #else #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) png_permit_empty_plte(ping,True); png_set_read_fn(ping,image,png_get_data); #else mng_info->image=image; mng_info->bytes_in_read_buffer=0; mng_info->found_empty_plte=False; mng_info->have_saved_bkgd_index=False; png_set_read_fn(ping,mng_info,mng_get_data); #endif #endif } else png_set_read_fn(ping,image,png_get_data); #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) /* Ignore unused chunks */ png_set_keep_unknown_chunks(ping, 1, unused_chunks, (int)sizeof(unused_chunks)/5); #endif #if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) \ && (PNG_LIBPNG_VER >= 10200) /* Disable thread-unsafe features of pnggccrd */ if (png_access_version_number() >= 10200) { png_uint_32 mmx_disable_mask=0; png_uint_32 asm_flags; mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); asm_flags=png_get_asm_flags(ping); png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask); } #endif png_read_info(ping,ping_info); #if (QuantumDepth == 8) image->depth=8; #else if (ping_info->bit_depth > 8) image->depth=16; else image->depth=8; #endif if (ping_info->bit_depth < 8) { if ((ping_info->color_type == PNG_COLOR_TYPE_PALETTE)) { png_set_packing(ping); image->depth=8; } } if (logging) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), " PNG width: %lu, height: %lu", ping_info->width, ping_info->height); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " PNG color_type: %d, bit_depth: %d", ping_info->color_type, ping_info->bit_depth); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " PNG compression_method: %d", ping_info->compression_type); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " PNG interlace_method: %d, filter_method: %d", ping_info->interlace_type,ping_info->filter_type); } #if (PNG_LIBPNG_VER > 10008) && defined(PNG_READ_iCCP_SUPPORTED) if (ping_info->valid & PNG_INFO_iCCP) { int compression; png_charp info, name; (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info, (png_uint_32 *) &image->color_profile.length); if (image->color_profile.name) MagickFreeMemory(image->color_profile.name); if (image->color_profile.info) MagickFreeMemory(image->color_profile.info); if (image->color_profile.length) { if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG iCCP chunk."); #ifdef PNG_FREE_ME_SUPPORTED image->color_profile.name=AllocateString("icm"); image->color_profile.info=(unsigned char *) info; png_data_freer(ping, ping_info, PNG_USER_WILL_FREE_DATA, PNG_FREE_ICCP); #else /* libpng will destroy name and info so we must copy them now. */ image->color_profile.info=MagickAllocateMemory(unsigned char *,length); if (image->color_profile.info == (unsigned char *) NULL) { MagickError3(ResourceLimitError,MemoryAllocationFailed, UnableToAllocateICCProfile); image->color_profile.length=0; } else { (void) memcpy(image->color_profile.info, (unsigned char *) info,length); image->color_profile.name=AllocateString("icm"); /* Note that the PNG iCCP profile name gets lost. */ } #endif } } #endif #if defined(PNG_READ_sRGB_SUPPORTED) { int intent; if (mng_info->have_global_srgb) image->rendering_intent=(RenderingIntent) (mng_info->global_srgb_intent+1); if (png_get_sRGB(ping,ping_info,&intent)) { image->rendering_intent=(RenderingIntent) (intent+1); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG sRGB chunk: rendering_intent: %d",intent+1); } } #endif { double file_gamma; if (mng_info->have_global_gama) image->gamma=mng_info->global_gamma; if (png_get_gAMA(ping,ping_info,&file_gamma)) { image->gamma=(float) file_gamma; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG gAMA chunk: gamma: %f",file_gamma); } } if (mng_info->have_global_chrm) image->chromaticity=mng_info->global_chrm; if (ping_info->valid & PNG_INFO_cHRM) { (void) png_get_cHRM(ping,ping_info, &image->chromaticity.white_point.x, &image->chromaticity.white_point.y, &image->chromaticity.red_primary.x, &image->chromaticity.red_primary.y, &image->chromaticity.green_primary.x, &image->chromaticity.green_primary.y, &image->chromaticity.blue_primary.x, &image->chromaticity.blue_primary.y); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG cHRM chunk."); } if (image->rendering_intent) { image->gamma=0.45455f; image->chromaticity.red_primary.x=0.6400f; image->chromaticity.red_primary.y=0.3300f; image->chromaticity.green_primary.x=0.3000f; image->chromaticity.green_primary.y=0.6000f; image->chromaticity.blue_primary.x=0.1500f; image->chromaticity.blue_primary.y=0.0600f; image->chromaticity.white_point.x=0.3127f; image->chromaticity.white_point.y=0.3290f; } if (mng_info->have_global_gama || image->rendering_intent) ping_info->valid|=PNG_INFO_gAMA; if (mng_info->have_global_chrm || image->rendering_intent) ping_info->valid|=PNG_INFO_cHRM; #if defined(PNG_oFFs_SUPPORTED) if (mng_info->mng_type == 0 && (ping_info->valid & PNG_INFO_oFFs)) { image->page.x=png_get_x_offset_pixels(ping, ping_info); image->page.y=png_get_y_offset_pixels(ping, ping_info); if (logging) if (image->page.x || image->page.y) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG oFFs chunk: x: %ld, y: %ld.",image->page.x, image->page.y); } #endif #if defined(PNG_pHYs_SUPPORTED) if (ping_info->valid & PNG_INFO_pHYs) { int unit_type; png_uint_32 x_resolution, y_resolution; /* Set image resolution. */ (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution, &unit_type); image->x_resolution=(float) x_resolution; image->y_resolution=(float) y_resolution; if (unit_type == PNG_RESOLUTION_METER) { image->units=PixelsPerCentimeterResolution; image->x_resolution=(double) x_resolution/100.0; image->y_resolution=(double) y_resolution/100.0; } if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.", x_resolution, y_resolution, unit_type); } else { if (mng_info->have_global_phys) { image->x_resolution=(float) mng_info->global_x_pixels_per_unit; image->y_resolution=(float) mng_info->global_y_pixels_per_unit; if (mng_info->global_phys_unit_type == PNG_RESOLUTION_METER) { image->units=PixelsPerCentimeterResolution; image->x_resolution=(double) mng_info->global_x_pixels_per_unit/100.0; image->y_resolution=(double) mng_info->global_y_pixels_per_unit/100.0; } ping_info->valid|=PNG_INFO_pHYs; } } #endif if (ping_info->valid & PNG_INFO_PLTE) { int number_colors; png_colorp palette; (void) png_get_PLTE(ping,ping_info,&palette,&number_colors); if (number_colors == 0 && ping_info->color_type == PNG_COLOR_TYPE_PALETTE) { if (mng_info->global_plte_length) { png_set_PLTE(ping,ping_info,mng_info->global_plte, (int) mng_info->global_plte_length); if (!(ping_info->valid & PNG_INFO_tRNS)) if (mng_info->global_trns_length) { if (mng_info->global_trns_length > mng_info->global_plte_length) (void) ThrowException2(&image->exception,CoderError, "global tRNS has more entries than global PLTE", image_info->filename); png_set_tRNS(ping,ping_info,mng_info->global_trns, (int) mng_info->global_trns_length,NULL); } #if defined(PNG_READ_bKGD_SUPPORTED) if ( #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED mng_info->have_saved_bkgd_index || #endif ping_info->valid & PNG_INFO_bKGD) { png_color_16 background; #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED if (mng_info->have_saved_bkgd_index) background.index=mng_info->saved_bkgd_index; else #endif background.index=ping_info->background.index; background.red=(png_uint_16) mng_info->global_plte[background.index].red; background.green=(png_uint_16) mng_info->global_plte[background.index].green; background.blue=(png_uint_16) mng_info->global_plte[background.index].blue; png_set_bKGD(ping,ping_info,&background); } #endif } else (void) ThrowException2(&image->exception,CoderError, "No global PLTE in file",image_info->filename); } } #if defined(PNG_READ_bKGD_SUPPORTED) if (mng_info->have_global_bkgd && !(ping_info->valid & PNG_INFO_bKGD)) image->background_color=mng_info->mng_global_bkgd; if (ping_info->valid & PNG_INFO_bKGD) { /* Set image background color. */ if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG bKGD chunk."); if (ping_info->bit_depth <= QuantumDepth) { image->background_color.red=ping_info->background.red; image->background_color.green=ping_info->background.green; image->background_color.blue=ping_info->background.blue; } else { image->background_color.red= ScaleShortToQuantum(ping_info->background.red); image->background_color.green= ScaleShortToQuantum(ping_info->background.green); image->background_color.blue= ScaleShortToQuantum(ping_info->background.blue); } } #endif if (ping_info->valid & PNG_INFO_tRNS) { int scale; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG tRNS chunk."); scale=(int) (MaxRGB/((1L << ping_info->bit_depth)-1L)); if (scale < 1) scale=1; /* Image has a transparent background. */ transparent_color.red=(int) (ping_info->trans_values.red*scale); transparent_color.green=(int) (ping_info->trans_values.green*scale); transparent_color.blue=(int) (ping_info->trans_values.blue*scale); transparent_color.opacity=(int) (ping_info->trans_values.gray*scale); if (ping_info->color_type == PNG_COLOR_TYPE_GRAY) { transparent_color.red=transparent_color.opacity; transparent_color.green=transparent_color.opacity; transparent_color.blue=transparent_color.opacity; } } #if defined(PNG_READ_sBIT_SUPPORTED) if (mng_info->have_global_sbit) { int not_valid; not_valid=!ping_info->valid; if (not_valid & PNG_INFO_sBIT) png_set_sBIT(ping,ping_info,&mng_info->global_sbit); } #endif num_passes=png_set_interlace_handling(ping); png_read_update_info(ping,ping_info); /* Initialize image structure. */ mng_info->image_box.left=0; mng_info->image_box.right=(long) ping_info->width; mng_info->image_box.top=0; mng_info->image_box.bottom=(long) ping_info->height; if (mng_info->mng_type == 0) { mng_info->mng_width=ping_info->width; mng_info->mng_height=ping_info->height; mng_info->frame=mng_info->image_box; mng_info->clip=mng_info->image_box; } else { image->page.y=mng_info->y_off[mng_info->object_id]; } image->compression=ZipCompression; image->columns=ping_info->width; image->rows=ping_info->height; if ((ping_info->color_type == PNG_COLOR_TYPE_PALETTE) || (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (ping_info->color_type == PNG_COLOR_TYPE_GRAY)) { image->storage_class=PseudoClass; image->colors=1 << ping_info->bit_depth; #if (QuantumDepth == 8) if (image->colors > 256) image->colors=256; #else if (image->colors > 65536L) image->colors=65536L; #endif if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE) { int number_colors; png_colorp palette; (void) png_get_PLTE(ping,ping_info,&palette,&number_colors); image->colors=number_colors; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG PLTE chunk: number_colors: %d.",number_colors); } } if (image->storage_class == PseudoClass) { /* Initialize image colormap. */ if (!AllocateImageColormap(image,image->colors)) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE) { int number_colors; png_colorp palette; (void) png_get_PLTE(ping,ping_info,&palette,&number_colors); for (i=0; i < (long) image->colors; i++) { image->colormap[i].red=ScaleCharToQuantum(palette[i].red); image->colormap[i].green=ScaleCharToQuantum(palette[i].green); image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue); } } else { unsigned long scale; scale=(MaxRGB/((1 << ping_info->bit_depth)-1)); if (scale < 1) scale=1; for (i=0; i < (long) image->colors; i++) { image->colormap[i].red=(Quantum) (i*scale); image->colormap[i].green=(Quantum) (i*scale); image->colormap[i].blue=(Quantum) (i*scale); } } } /* Read image scanlines. */ if (image->delay != 0) mng_info->scenes_found++; if (image_info->ping && (image_info->number_scenes != 0) && mng_info->scenes_found > (long) (image_info->first_scene+image_info->number_scenes)) { if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Skipping PNG image data for scene %ld", mng_info->scenes_found-1); png_destroy_read_struct(&ping,&ping_info,&end_info); #if defined(PNG_SETJMP_NOT_THREAD_SAFE) LiberateSemaphoreInfo(&png_semaphore); #endif if (image != (Image *) NULL) image->columns=0; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " exit ReadOnePNGImage()."); return (image); } if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG IDAT chunk(s)"); if (num_passes > 1) png_pixels=MagickAllocateMemory(unsigned char *, ping_info->rowbytes*image->rows); else png_pixels=MagickAllocateMemory(unsigned char *, ping_info->rowbytes); if (png_pixels == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Converting PNG pixels to pixel packets"); /* Convert PNG pixels to pixel packets. */ if (image->storage_class == DirectClass) for (pass=0; pass < num_passes; pass++) { /* Convert image to DirectClass pixel packets. */ #if (QuantumDepth == 8) int depth; depth=(long) image->depth; #endif image->matte=((ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) || (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (ping_info->valid & PNG_INFO_tRNS)); for (y=0; y < (long) image->rows; y++) { if (num_passes > 1) row_offset=ping_info->rowbytes*y; else row_offset=0; png_read_row(ping,png_pixels+row_offset,NULL); if (!GetImagePixels(image,0,y,image->columns,1)) break; #if (QuantumDepth == 8) if (depth == 16) { register Quantum *p, *r; r=png_pixels+row_offset; p=r; if (ping_info->color_type == PNG_COLOR_TYPE_GRAY) { for (x=(long) image->columns; x > 0; x--) { *r++=*p++; p++; if ((ping_info->valid & PNG_INFO_tRNS) && (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity)) { /* Cheap transparency */ *r++=TransparentOpacity; } else *r++=OpaqueOpacity; } } else if (ping_info->color_type == PNG_COLOR_TYPE_RGB) { if (ping_info->valid & PNG_INFO_tRNS) for (x=(long) image->columns; x > 0; x--) { *r++=*p++; p++; *r++=*p++; p++; *r++=*p++; p++; if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) && (((*(p-4) << 8)|*(p-3)) == transparent_color.green) && (((*(p-2) << 8)|*(p-1)) == transparent_color.blue)) { /* Cheap transparency */ *r++=TransparentOpacity; } else *r++=OpaqueOpacity; } else for (x=(long) image->columns; x > 0; x--) { *r++=*p++; p++; *r++=*p++; p++; *r++=*p++; p++; *r++=OpaqueOpacity; } } else if (ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) for (x=(long) (4*image->columns); x > 0; x--) { *r++=*p++; p++; } else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) for (x=(long) (2*image->columns); x > 0; x--) { *r++=*p++; p++; } } if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_GRAY) (void) PushImagePixels(image,(QuantumType) GrayQuantum, png_pixels+row_offset); if (ping_info->color_type == PNG_COLOR_TYPE_GRAY || ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { image->depth=8; (void) PushImagePixels(image,(QuantumType) GrayAlphaQuantum, png_pixels+row_offset); image->depth=depth; } else if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_RGB) (void) PushImagePixels(image,(QuantumType) RGBQuantum, png_pixels+row_offset); else if (ping_info->color_type == PNG_COLOR_TYPE_RGB || ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { image->depth=8; (void) PushImagePixels(image,(QuantumType) RGBAQuantum, png_pixels+row_offset); image->depth=depth; } else if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE) (void) PushImagePixels(image,(QuantumType) IndexQuantum, png_pixels+row_offset); #else /* (QuantumDepth != 8) */ if (ping_info->color_type == PNG_COLOR_TYPE_GRAY) (void) PushImagePixels(image,(QuantumType) GrayQuantum, png_pixels+row_offset); else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) (void) PushImagePixels(image,(QuantumType) GrayAlphaQuantum, png_pixels+row_offset); else if (ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) (void) PushImagePixels(image,(QuantumType) RGBAQuantum, png_pixels+row_offset); else if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE) (void) PushImagePixels(image,(QuantumType) IndexQuantum, png_pixels+row_offset); else (void) PushImagePixels(image,(QuantumType) RGBQuantum, png_pixels+row_offset); #endif if (!SyncImagePixels(image)) break; } if (image->previous == (Image *) NULL) if (!MagickMonitor(LoadImageTag,pass,num_passes,exception)) break; } else /* image->storage_class != DirectClass */ for (pass=0; pass < num_passes; pass++) { Quantum *quantum_scanline; register Quantum *r; /* Convert grayscale image to PseudoClass pixel packets. */ image->matte=ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA; quantum_scanline=MagickAllocateMemory(Quantum *,(image->matte ? 2 : 1) * image->columns*sizeof(Quantum)); if (quantum_scanline == (Quantum *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); for (y=0; y < (long) image->rows; y++) { if (num_passes > 1) row_offset=ping_info->rowbytes*y; else row_offset=0; png_read_row(ping,png_pixels+row_offset,NULL); q=GetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); p=png_pixels+row_offset; r=quantum_scanline; switch (ping_info->bit_depth) { case 1: { register long bit; for (x=(long) image->columns-7; x > 0; x-=8) { for (bit=7; bit >= 0; bit--) *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00); p++; } if ((image->columns % 8) != 0) { for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--) *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00); } break; } case 2: { for (x=(long) image->columns-3; x > 0; x-=4) { *r++=(*p >> 6) & 0x03; *r++=(*p >> 4) & 0x03; *r++=(*p >> 2) & 0x03; *r++=(*p++) & 0x03; } if ((image->columns % 4) != 0) { for (i=3; i >= (long) (4-(image->columns % 4)); i--) *r++=(*p >> (i*2)) & 0x03; } break; } case 4: { for (x=(long) image->columns-1; x > 0; x-=2) { *r++=(*p >> 4) & 0x0f; *r++=(*p++) & 0x0f; } if ((image->columns % 2) != 0) *r++=(*p++ >> 4) & 0x0f; break; } case 8: { if (ping_info->color_type == 4) for (x=(long) image->columns; x > 0; x--) { *r++=*p++; /* In image.h, OpaqueOpacity is 0 * TransparentOpacity is MaxRGB * In a PNG datastream, Opaque is MaxRGB * and Transparent is 0. */ q->opacity=ScaleCharToQuantum(255-(*p++)); q++; } else for (x=(long) image->columns; x > 0; x--) *r++=*p++; break; } case 16: { for (x=(long) image->columns; x > 0; x--) { #if (QuantumDepth == 16) if (image->colors > 256) *r=((*p++) << 8); else *r=0; *r|=(*p++); r++; if (ping_info->color_type == 4) { q->opacity=((*p++) << 8); q->opacity|=(*p++); q->opacity=(Quantum) (MaxRGB-q->opacity); q++; } #else #if (QuantumDepth == 32) if (image->colors > 256) *r=((*p++) << 8); else *r=0; *r|=(*p++); r++; if (ping_info->color_type == 4) { q->opacity=((*p++) << 8); q->opacity|=(*p++); q->opacity*=65537L; q->opacity=(Quantum) (MaxRGB-q->opacity); q++; } #else /* QuantumDepth == 8 */ *r++=(*p++); p++; /* strip low byte */ if (ping_info->color_type == 4) { q->opacity=(Quantum) (MaxRGB-(*p++)); p++; q++; } #endif #endif } break; } default: break; } /* Transfer image scanline. */ r=quantum_scanline; for (x=0; x < (long) image->columns; x++) indexes[x]=(*r++); if (!SyncImagePixels(image)) break; } if (image->previous == (Image *) NULL) if (!MagickMonitor(LoadImageTag,pass,num_passes,exception)) break; MagickFreeMemory(quantum_scanline); } if (image->storage_class == PseudoClass) SyncImage(image); png_read_end(ping,ping_info); if (image_info->number_scenes != 0 && mng_info->scenes_found-1 < (long) image_info->first_scene && image->delay != 0.) { png_destroy_read_struct(&ping,&ping_info,&end_info); MagickFreeMemory(png_pixels); image->colors=2; SetImage(image,TransparentOpacity); #if defined(PNG_SETJMP_NOT_THREAD_SAFE) LiberateSemaphoreInfo(&png_semaphore); #endif if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " exit ReadOnePNGImage()."); return (image); } if (ping_info->valid & PNG_INFO_tRNS) { ClassType storage_class; /* Image has a transparent background. */ storage_class=image->storage_class; image->matte=True; for (y=0; y < (long) image->rows; y++) { image->storage_class=storage_class; q=GetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); q->opacity=OpaqueOpacity; if (storage_class == PseudoClass) { IndexPacket index; if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE) for (x=0; x < (long) image->columns; x++) { index=indexes[x]; if (index < ping_info->num_trans) q->opacity= ScaleCharToQuantum(255-ping_info->trans[index]); q++; } else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY) for (x=0; x < (long) image->columns; x++) { index=indexes[x]; q->red=image->colormap[index].red; q->green=image->colormap[index].green; q->blue=image->colormap[index].blue; if (q->red == transparent_color.opacity) q->opacity=TransparentOpacity; q++; } } else for (x=(long) image->columns; x > 0; x--) { if (q->red == transparent_color.red && q->green == transparent_color.green && q->blue == transparent_color.blue) q->opacity=TransparentOpacity; q++; } if (!SyncImagePixels(image)) break; } image->storage_class=DirectClass; } #if (QuantumDepth == 8) if (image->depth > 8) image->depth=8; #endif if (png_get_text(ping,ping_info,&text,&num_text) != 0) for (i=0; i < (long) num_text; i++) { /* Check for a profile */ if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading PNG text chunk"); if (!memcmp(text[i].key, "Raw profile type ",17)) (void) png_read_raw_profile(image,image_info,text,(int) i); else { char *value; length=text[i].text_length; value=MagickAllocateMemory(char *,length+1); if (value == (char *) NULL) { (void) ThrowException3(&image->exception,ResourceLimitError, MemoryAllocationFailed,UnableToReadTextChunk); break; } *value='\0'; (void) strncat(value,text[i].text,length); value[length]='\0'; (void) SetImageAttribute(image,text[i].key,value); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Keyword: %s",text[i].key); MagickFreeMemory(value); } } #ifdef MNG_OBJECT_BUFFERS /* Store the object if necessary. */ if (object_id && !mng_info->frozen[object_id]) { if (mng_info->ob[object_id] == (MngBuffer *) NULL) { /* create a new object buffer. */ mng_info->ob[object_id]=MagickAllocateMemory(MngBuffer *, sizeof(MngBuffer)); if (mng_info->ob[object_id] != (MngBuffer *) NULL) { mng_info->ob[object_id]->image=(Image *) NULL; mng_info->ob[object_id]->reference_count=1; } } if ((mng_info->ob[object_id] == (MngBuffer *) NULL) || mng_info->ob[object_id]->frozen) { if (mng_info->ob[object_id] == (MngBuffer *) NULL) (void) ThrowException(&image->exception,ResourceLimitError,MemoryAllocationFailed,image->filename); if (mng_info->ob[object_id]->frozen) (void) ThrowException(&image->exception,ResourceLimitError, "Cannot overwrite frozen MNG object buffer",image->filename); } else { png_uint_32 width, height; int bit_depth, color_type, interlace_method, compression_method, filter_method; if (mng_info->ob[object_id]->image != (Image *) NULL) DestroyImage(mng_info->ob[object_id]->image); mng_info->ob[object_id]->image=CloneImage(image,0,0,True, &image->exception); if (mng_info->ob[object_id]->image != (Image *) NULL) mng_info->ob[object_id]->image->file=(FILE *) NULL; else (void) ThrowException(&image->exception,ResourceLimitError, "Cloning image for object buffer failed",image->filename); png_get_IHDR(ping,ping_info,&width,&height,&bit_depth,&color_type, &interlace_method,&compression_method,&filter_method); if (width > 250000L || height > 250000L) png_error(ping,"PNG Image dimensions are too large."); mng_info->ob[object_id]->width=width; mng_info->ob[object_id]->height=height; mng_info->ob[object_id]->color_type=color_type; mng_info->ob[object_id]->sample_depth=bit_depth; mng_info->ob[object_id]->interlace_method=interlace_method; mng_info->ob[object_id]->compression_method=compression_method; mng_info->ob[object_id]->filter_method=filter_method; if (ping_info->valid & PNG_INFO_PLTE) { int number_colors; png_colorp plte; /* Copy the PLTE to the object buffer. */ png_get_PLTE(ping,ping_info,&plte,&number_colors); mng_info->ob[object_id]->plte_length=number_colors; for (i=0; i < number_colors; i++) { mng_info->ob[object_id]->plte[i]=plte[i]; } } else mng_info->ob[object_id]->plte_length=0; } } #endif /* Free memory. */ png_destroy_read_struct(&ping,&ping_info,&end_info); MagickFreeMemory(png_pixels); #if defined(PNG_SETJMP_NOT_THREAD_SAFE) LiberateSemaphoreInfo(&png_semaphore); #endif if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " exit ReadOnePNGImage()"); return (image); /* end of reading one PNG image */ } static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception) { Image *image, *previous; MngInfo *mng_info; char magic_number[MaxTextExtent]; int have_mng_structure, logging; unsigned int status; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()"); image=AllocateImage(image_info); mng_info=(MngInfo *) NULL; status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == False) ThrowReaderException(FileOpenError,UnableToOpenFile,image); /* Verify PNG signature. */ (void) ReadBlob(image,8,magic_number); if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0) ThrowReaderException(CorruptImageError,ImproperImageHeader,image); /* Allocate a MngInfo structure. */ have_mng_structure=False; mng_info=MagickAllocateMemory(MngInfo *,sizeof(MngInfo)); if (mng_info == (MngInfo *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); /* Initialize members of the MngInfo structure. */ (void) memset(mng_info,0,sizeof(MngInfo)); mng_info->image=image; have_mng_structure=True; previous=image; image=ReadOnePNGImage(mng_info,image_info,exception); MngInfoFreeStruct(mng_info,&have_mng_structure); if (image == (Image *) NULL) { if (previous != (Image *) NULL) { CloseBlob(previous); DestroyImageList(previous); } if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "exit ReadPNGImage() with error"); return((Image *) NULL); } CloseBlob(image); if (image->columns == 0 || image->rows == 0) { DestroyImageList(image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "exit ReadPNGImage() with error."); return((Image *) NULL); } if (LocaleCompare(image_info->magick,"PNG8") == 0) { SetImageType(image,PaletteType); if (image->matte) { /* To do: Reduce to binary transparency */ } } if (LocaleCompare(image_info->magick,"PNG24") == 0) { SetImageType(image,TrueColorType); image->matte=False; } if (LocaleCompare(image_info->magick,"PNG32") == 0) SetImageType(image,TrueColorMatteType); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()"); return (image); } #if defined(JNG_SUPPORTED) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d O n e J N G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method ReadOneJNGImage reads a JPEG Network Graphics (JNG) image file % (minus the 8-byte signature) and returns it. It allocates the memory % necessary for the new Image structure and returns a pointer to the new % image. % % JNG support written by Glenn Randers-Pehrson, randeg@alum.rpi.edu. % % The format of the ReadOneJNGImage method is: % % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: Method ReadOneJNGImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or % if the image cannot be read. % % o mng_info: Specifies a pointer to a MngInfo structure. % % o image_info: Specifies a pointer to a ImageInfo structure. % % o exception: return any errors or warnings in this structure. % */ static Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info, ExceptionInfo *exception) { Image *alpha_image, *color_image, *image, *jng_image; ImageInfo *alpha_image_info, *color_image_info; long y; png_uint_32 jng_height, jng_width; png_byte jng_color_type, jng_image_sample_depth, jng_image_compression_method, jng_image_interlace_method, jng_alpha_sample_depth, jng_alpha_compression_method, jng_alpha_filter_method, jng_alpha_interlace_method; register const PixelPacket *s; register long i, x; register PixelPacket *q; register unsigned char *p; unsigned int logging, read_JSEP, reading_idat, skip_to_iend, status; unsigned long length; jng_alpha_compression_method=0; jng_alpha_sample_depth=8; jng_color_type=0; jng_height=0; jng_width=0; alpha_image=(Image *) NULL; color_image=(Image *) NULL; alpha_image_info=(ImageInfo *) NULL; color_image_info=(ImageInfo *) NULL; logging=LogMagickEvent(CoderEvent,GetMagickModule(), " enter ReadOneJNGImage()"); image=mng_info->image; if (GetPixels(image) != (PixelPacket *) NULL) { /* Allocate next image structure. */ if (logging) LogMagickEvent(CoderEvent,GetMagickModule(), " AllocateNextImage()"); AllocateNextImage(image_info,image); if (image->next == (Image *) NULL) return((Image *) NULL); image=SyncNextImageInList(image); } mng_info->image=image; /* Signature bytes have already been read. */ read_JSEP=False; reading_idat=False; skip_to_iend=False; for (;;) { char type[MaxTextExtent]; unsigned char *chunk; unsigned int count; /* Read a new JNG chunk. */ if (!MagickMonitor(LoadImagesTag,TellBlob(image),2*GetBlobSize(image), exception)) break; type[0]='\0'; (void) strcat(type,"errr"); length=ReadBlobMSBLong(image); count=ReadBlob(image,4,type); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading JNG chunk type %c%c%c%c, length: %lu", type[0],type[1],type[2],type[3],length); if (length > PNG_MAX_UINT || count == 0) ThrowReaderException(CorruptImageError,CorruptImage,image); chunk=(unsigned char *) NULL; p=NULL; if (length) { chunk=MagickAllocateMemory(unsigned char *,length); if (chunk == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, image); for (i=0; i < (long) length; i++) chunk[i]=ReadBlobByte(image); p=chunk; } (void) ReadBlobMSBLong(image); /* read crc word */ if (skip_to_iend) { if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_JHDR,4)) { if (length == 16) { jng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); jng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); jng_color_type=p[8]; jng_image_sample_depth=p[9]; jng_image_compression_method=p[10]; jng_image_interlace_method=p[11]; jng_alpha_sample_depth=p[12]; jng_alpha_compression_method=p[13]; jng_alpha_filter_method=p[14]; jng_alpha_interlace_method=p[15]; if (logging) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_width: %16lu",jng_width); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_width: %16lu",jng_height); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_color_type: %16d",jng_color_type); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_image_sample_depth: %3d", jng_image_sample_depth); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_image_compression_method:%3d", jng_image_compression_method); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_image_interlace_method: %3d", jng_image_interlace_method); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_alpha_sample_depth: %3d", jng_alpha_sample_depth); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_alpha_compression_method:%3d", jng_alpha_compression_method); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_alpha_filter_method: %3d", jng_alpha_filter_method); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " jng_alpha_interlace_method: %3d", jng_alpha_interlace_method); } } if (length) MagickFreeMemory(chunk); continue; } if (!reading_idat && !read_JSEP && (!memcmp(type,mng_JDAT,4) || !memcmp(type,mng_JdAA,4) || !memcmp(type,mng_IDAT,4) || !memcmp(type,mng_JDAA,4))) { /* o create color_image o open color_blob, attached to color_image o if (color type has alpha) open alpha_blob, attached to alpha_image */ color_image_info=MagickAllocateMemory(ImageInfo *,sizeof(ImageInfo)); if (color_image_info == (ImageInfo *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, image); GetImageInfo(color_image_info); color_image=AllocateImage(color_image_info); if (color_image == (Image *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Creating color_blob."); AcquireUniqueFilename(color_image->filename); status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode, exception); if (status == False) ThrowReaderException(CoderError,UnableToOpenBlob,color_image); if (!image_info->ping && jng_color_type >= 12) { alpha_image_info=MagickAllocateMemory(ImageInfo *,sizeof(ImageInfo)); if (alpha_image_info == (ImageInfo *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, image); GetImageInfo(alpha_image_info); alpha_image=AllocateImage(alpha_image_info); if (alpha_image == (Image *) NULL) { DestroyImage(alpha_image); ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, alpha_image) } if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Creating alpha_blob."); AcquireUniqueFilename(alpha_image->filename); status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode, exception); if (status == False) ThrowReaderException(CoderError,UnableToOpenBlob,image); if (jng_alpha_compression_method == 0) { unsigned char data[18]; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Writing IHDR chunk to alpha_blob."); (void) WriteBlob(alpha_image,8,"\211PNG\r\n\032\n"); (void) WriteBlobMSBULong(alpha_image,13L); PNGType(data,mng_IHDR); LogPNGChunk(logging,mng_IHDR,13L); PNGLong(data+4,jng_width); PNGLong(data+8,jng_height); data[12]=jng_alpha_sample_depth; data[13]=0; /* color_type gray */ data[14]=0; /* compression method 0 */ data[15]=0; /* filter_method 0 */ data[16]=0; /* interlace_method 0 */ (void) WriteBlob(alpha_image,17,data); (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17)); } } reading_idat=True; } if (!memcmp(type,mng_JDAT,4)) { /* Copy chunk to color_image->blob */ if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Copying JDAT chunk data to color_blob."); (void) WriteBlob(color_image,length,(char *) chunk); if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_IDAT,4)) { png_byte data[5]; /* Copy IDAT header and chunk data to alpha_image->blob */ if (!image_info->ping) { if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Copying IDAT chunk data to alpha_blob."); (void) WriteBlobMSBULong(alpha_image,(unsigned long) length); PNGType(data,mng_IDAT); LogPNGChunk(logging,mng_IDAT,length); (void) WriteBlob(alpha_image,4,(char *) data); (void) WriteBlob(alpha_image,length,(char *) chunk); (void) WriteBlobMSBULong(alpha_image, crc32(crc32(0,data,4),chunk,length)); } if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_JDAA,4) || !memcmp(type,mng_JdAA,4)) { /* Copy chunk data to alpha_image->blob */ if (!image_info->ping) { if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Copying JDAA chunk data to alpha_blob."); (void) WriteBlob(alpha_image,length,(char *) chunk); } if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_JSEP,4)) { read_JSEP=True; if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_bKGD,4)) { if (length == 2) { image->background_color.red=ScaleCharToQuantum(p[1]); image->background_color.green=image->background_color.red; image->background_color.blue=image->background_color.red; } if (length == 6) { image->background_color.red=ScaleCharToQuantum(p[1]); image->background_color.green=ScaleCharToQuantum(p[3]); image->background_color.blue=ScaleCharToQuantum(p[5]); } MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_gAMA,4)) { if (length == 4) image->gamma=((float) mng_get_long(p))*0.00001; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_cHRM,4)) { if (length == 32) { image->chromaticity.white_point.x=0.00001*mng_get_long(p); image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]); image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]); image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]); image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]); image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]); image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]); image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]); } MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_sRGB,4)) { if (length == 1) { image->rendering_intent=(RenderingIntent) (p[0]+1); image->gamma=0.45455f; image->chromaticity.red_primary.x=0.6400f; image->chromaticity.red_primary.y=0.3300f; image->chromaticity.green_primary.x=0.3000f; image->chromaticity.green_primary.y=0.6000f; image->chromaticity.blue_primary.x=0.1500f; image->chromaticity.blue_primary.y=0.0600f; image->chromaticity.white_point.x=0.3127f; image->chromaticity.white_point.y=0.3290f; } MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_oFFs,4)) { if (length > 8) { image->page.x=mng_get_long(p); image->page.y=mng_get_long(&p[4]); if ((int) p[9] != 0) { image->page.x/=10000; image->page.y/=10000; } } if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_pHYs,4)) { if (length > 8) { image->x_resolution=(double) mng_get_long(p); image->y_resolution=(double) mng_get_long(&p[4]); if ((int) p[8] == PNG_RESOLUTION_METER) { image->units=PixelsPerCentimeterResolution; image->x_resolution=image->x_resolution/100.0f; image->y_resolution=image->y_resolution/100.0f; } } MagickFreeMemory(chunk); continue; } #if 0 if (!memcmp(type,mng_iCCP,4)) { /* To do. */ if (length) MagickFreeMemory(chunk); continue; } #endif if (length) MagickFreeMemory(chunk); if (memcmp(type,mng_IEND,4)) continue; break; } /* IEND found */ /* Finish up reading image data: o read main image from color_blob. o close color_blob. o if (color_type has alpha) if alpha_encoding is PNG read secondary image from alpha_blob via ReadPNG if alpha_encoding is JPEG read secondary image from alpha_blob via ReadJPEG o close alpha_blob. o copy intensity of secondary image into opacity samples of main image. o destroy the secondary image. */ CloseBlob(color_image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading jng_image from color_blob."); FormatString(color_image_info->filename,"%.1024s",color_image->filename); color_image_info->ping=False; /* To do: avoid this */ jng_image=ReadImage(color_image_info,exception); LiberateUniqueFileResource(color_image->filename); DestroyImage(color_image); DestroyImageInfo(color_image_info); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Copying jng_image pixels to main image."); image->rows=jng_height; image->columns=jng_width; length=image->columns*sizeof(PixelPacket); for (y=0; y < (long) image->rows; y++) { s=AcquireImagePixels(jng_image,0,y,image->columns,1,&image->exception); q=GetImagePixels(image,0,y,image->columns,1); memcpy(q,s,length); if (!SyncImagePixels(image)) break; } DestroyImage(jng_image); if (!image_info->ping) { if (jng_color_type >= 12) { if (jng_alpha_compression_method == 0) { png_byte data[5]; (void) WriteBlobMSBULong(alpha_image,0x00000000L); PNGType(data,mng_IEND); LogPNGChunk(logging,mng_IEND,0L); (void) WriteBlob(alpha_image,4,(char *) data); (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4)); } CloseBlob(alpha_image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading opacity from alpha_blob."); FormatString(alpha_image_info->filename,"%.1024s", alpha_image->filename); jng_image=ReadImage(alpha_image_info,exception); for (y=0; y < (long) image->rows; y++) { s=AcquireImagePixels(jng_image,0,y,image->columns,1, &image->exception); q=GetImagePixels(image,0,y,image->columns,1); if (image->matte) for (x=(long) image->columns; x > 0; x--,q++,s++) q->opacity=(Quantum) MaxRGB-s->red; else for (x=(long) image->columns; x > 0; x--,q++,s++) { q->opacity=(Quantum) MaxRGB-s->red; if (q->opacity != OpaqueOpacity) image->matte=True; } if (!SyncImagePixels(image)) break; } LiberateUniqueFileResource(alpha_image->filename); DestroyImage(alpha_image); DestroyImageInfo(alpha_image_info); DestroyImage(jng_image); } } /* Read the JNG image. */ image->page.width=jng_width; image->page.height=jng_height; image->page.x=mng_info->x_off[mng_info->object_id]; image->page.y=mng_info->y_off[mng_info->object_id]; mng_info->image_found++; (void) MagickMonitor(LoadImagesTag,2*GetBlobSize(image),2*GetBlobSize(image), exception); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " exit ReadOneJNGImage()"); return (image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d J N G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Method ReadJNGImage reads a JPEG Network Graphics (JNG) image file % (including the 8-byte signature) and returns it. It allocates the memory % necessary for the new Image structure and returns a pointer to the new % image. % % JNG support written by Glenn Randers-Pehrson, randeg@alum.rpi.edu. % % The format of the ReadJNGImage method is: % % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo % *exception) % % A description of each parameter follows: % % o image: Method ReadJNGImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or % if the image cannot be read. % % o image_info: Specifies a pointer to a ImageInfo structure. % % o exception: return any errors or warnings in this structure. % */ static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception) { Image *image, *previous; MngInfo *mng_info; char magic_number[MaxTextExtent]; int have_mng_structure, logging; unsigned int status; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()"); image=AllocateImage(image_info); mng_info=(MngInfo *) NULL; status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == False) ThrowReaderException(FileOpenError,UnableToOpenFile,image); if (LocaleCompare(image_info->magick,"JNG") != 0) ThrowReaderException(CorruptImageError,ImproperImageHeader,image); /* Verify JNG signature. */ (void) ReadBlob(image,8,magic_number); if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0) ThrowReaderException(CorruptImageError,ImproperImageHeader,image); /* Allocate a MngInfo structure. */ have_mng_structure=False; mng_info=MagickAllocateMemory(MngInfo *,sizeof(MngInfo)); if (mng_info == (MngInfo *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); /* Initialize members of the MngInfo structure. */ (void) memset(mng_info,0,sizeof(MngInfo)); have_mng_structure=True; mng_info->image=image; previous=image; image=ReadOneJNGImage(mng_info,image_info,exception); MngInfoFreeStruct(mng_info,&have_mng_structure); if (image == (Image *) NULL) { CloseBlob(previous); DestroyImageList(previous); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "exit ReadJNGImage() with error"); return((Image *) NULL); } CloseBlob(image); if (image->columns == 0 || image->rows == 0) { DestroyImageList(image); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "exit ReadJNGImage() with error"); return((Image *) NULL); } if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()"); return (image); } #endif static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception) { char page_geometry[MaxTextExtent]; Image *image, *previous; int have_mng_structure; volatile int first_mng_object, logging, object_id, term_chunk_found, skip_to_iend; volatile long image_count=0; MngInfo *mng_info; MngBox default_fb, fb, previous_fb; #ifdef MNG_INSERT_LAYERS PixelPacket mng_background_color; #endif register unsigned char *p; register long i; size_t count; short loop_level, loops_active; volatile short skipping_loop; unsigned int #ifdef MNG_INSERT_LAYERS mandatory_back=0, #endif status; volatile unsigned int #ifdef MNG_OBJECT_BUFFERS mng_background_object=0, #endif mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */ unsigned long default_frame_timeout, frame_timeout, #ifdef MNG_INSERT_LAYERS image_height, image_width, #endif length; volatile unsigned long default_frame_delay, final_delay, final_image_delay, frame_delay, #ifdef MNG_INSERT_LAYERS insert_layers, #endif mng_iterations=1, simplicity=0, subframe_height=0, subframe_width=0; /* Set image_info->type=OptimizeType (new in version 5.4.0) to get the following optimizations: o 16-bit depth is reduced to 8 if all pixels contain samples whose high byte and low byte are identical. o Opaque matte channel is removed. o If matte channel is present but only one transparent color is present, RGB+tRNS is written instead of RGBA o Grayscale images are reduced to 1, 2, or 4 bit depth if this can be done without loss. o Palette is sorted to remove unused entries and to put a transparent color first, if PNG_SORT_PALETTE is also defined. */ /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()"); image=AllocateImage(image_info); mng_info=(MngInfo *) NULL; status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == False) ThrowReaderException(FileOpenError,UnableToOpenFile,image); first_mng_object=False; skipping_loop=(-1); have_mng_structure=False; /* Allocate a MngInfo structure. */ mng_info=MagickAllocateMemory(MngInfo *,sizeof(MngInfo)); if (mng_info == (MngInfo *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); /* Initialize members of the MngInfo structure. */ (void) memset(mng_info,0,sizeof(MngInfo)); mng_info->image=image; have_mng_structure=True; #if (QuantumDepth == 16) mng_info->optimize=image_info->type == OptimizeType; #endif if (LocaleCompare(image_info->magick,"MNG") == 0) { char magic_number[MaxTextExtent]; /* Verify MNG signature. */ (void) ReadBlob(image,8,magic_number); if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0) ThrowReaderException(CorruptImageError,ImproperImageHeader,image); /* Initialize some nonzero members of the MngInfo structure. */ for (i=0; i < MNG_MAX_OBJECTS; i++) { mng_info->object_clip[i].right=PNG_MAX_UINT; mng_info->object_clip[i].bottom=PNG_MAX_UINT; } mng_info->exists[0]=True; } first_mng_object=True; mng_type=0; #ifdef MNG_INSERT_LAYERS insert_layers=False; /* should be False when converting or mogrifying */ #endif default_frame_delay=0; default_frame_timeout=0; frame_delay=0; final_delay=1; mng_info->ticks_per_second=100; object_id=0; skip_to_iend=False; term_chunk_found=False; mng_info->framing_mode=1; #ifdef MNG_INSERT_LAYERS mandatory_back=False; #endif #ifdef MNG_INSERT_LAYERS mng_background_color=image->background_color; #endif do { char type[MaxTextExtent]; if (LocaleCompare(image_info->magick,"MNG") == 0) { unsigned char *chunk; /* Read a new chunk. */ type[0]='\0'; (void) strcat(type,"errr"); length=ReadBlobMSBLong(image); count=ReadBlob(image,4,type); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Reading MNG chunk type %c%c%c%c, length: %lu", type[0],type[1],type[2],type[3],length); if (length > PNG_MAX_UINT) status=False; if (count == 0) ThrowReaderException(CorruptImageError,CorruptImage,image); p=NULL; chunk=(unsigned char *) NULL; if (length) { chunk=MagickAllocateMemory(unsigned char *,length); if (chunk == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, image); for (i=0; i < (long) length; i++) chunk[i]=ReadBlobByte(image); p=chunk; } (void) ReadBlobMSBLong(image); /* read crc word */ #if !defined(JNG_SUPPORTED) if (!memcmp(type,mng_JHDR,4)) { skip_to_iend=True; if (!mng_info->jhdr_warning) (void) ThrowException(&image->exception,CoderError,JNGCompressionNotSupported,image->filename); mng_info->jhdr_warning++; } #endif if (!memcmp(type,mng_DHDR,4)) { skip_to_iend=True; if (!mng_info->dhdr_warning) (void) ThrowException(&image->exception,CoderError,DeltaPNGNotSupported,image->filename); mng_info->dhdr_warning++; } if (!memcmp(type,mng_MEND,4)) break; if (skip_to_iend) { if (!memcmp(type,mng_IEND,4)) skip_to_iend=False; if (length) MagickFreeMemory(chunk); if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Skip to IEND."); continue; } if (!memcmp(type,mng_MHDR,4)) { mng_info->mng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); mng_info->mng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); if (logging) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), " MNG width: %lu",mng_info->mng_width); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " MNG height: %lu",mng_info->mng_height); } p+=8; mng_info->ticks_per_second=mng_get_long(p); if (mng_info->ticks_per_second == 0) default_frame_delay=0; else default_frame_delay=100/mng_info->ticks_per_second; frame_delay=default_frame_delay; simplicity=0; if (length > 16) { p+=16; simplicity=mng_get_long(p); } mng_type=1; /* Full MNG */ if ((simplicity != 0) && ((simplicity | 11) == 11)) mng_type=2; /* LC */ if ((simplicity != 0) && ((simplicity | 9) == 9)) mng_type=3; /* VLC */ #ifdef MNG_INSERT_LAYERS if (mng_type != 3 && !image_info->ping) insert_layers=True; #endif if (GetPixels(image) != (PixelPacket *) NULL) { /* Allocate next image structure. */ AllocateNextImage(image_info,image); if (image->next == (Image *) NULL) return((Image *) NULL); image=SyncNextImageInList(image); mng_info->image=image; } if ((mng_info->mng_width > 65535L) || (mng_info->mng_height > 65535L)) (void) ThrowException(&image->exception,ImageError,WidthOrHeightExceedsLimit,image->filename); FormatString(page_geometry,"%lux%lu+0+0",mng_info->mng_width, mng_info->mng_height); mng_info->frame.left=0; mng_info->frame.right=(long) mng_info->mng_width; mng_info->frame.top=0; mng_info->frame.bottom=(long) mng_info->mng_height; mng_info->clip=default_fb=previous_fb=mng_info->frame; for (i=0; i < MNG_MAX_OBJECTS; i++) mng_info->object_clip[i]=mng_info->frame; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_TERM,4)) { int repeat=0; if (length) repeat=p[0]; if (repeat == 3) { final_delay=(png_uint_32) mng_get_long(&p[2]); mng_iterations=(png_uint_32) mng_get_long(&p[6]); if (mng_iterations == PNG_MAX_UINT) mng_iterations=0; image->iterations=mng_iterations; term_chunk_found=True; } if (logging) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), " repeat=%d",repeat); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " final_delay=%ld",final_delay); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " image->iterations=%ld",image->iterations); } MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_DEFI,4)) { if (mng_type == 3) (void) ThrowException2(&image->exception,CoderError, "DEFI chunk found in MNG-VLC datastream",(char *) NULL); object_id=(p[0] << 8) | p[1]; if (mng_type == 2 && object_id != 0) (void) ThrowException2(&image->exception,CoderError, "Nonzero object_id in MNG-LC datastream",(char *) NULL); if (object_id > MNG_MAX_OBJECTS) { /* Instead of issuing a warning we should allocate a larger MngInfo structure and continue. */ (void) ThrowException2(&image->exception,CoderError, "object id too large",(char *) NULL); object_id=MNG_MAX_OBJECTS; } if (mng_info->exists[object_id]) if (mng_info->frozen[object_id]) { MagickFreeMemory(chunk); (void) ThrowException2(&image->exception,CoderError, "DEFI cannot redefine a frozen MNG object",(char *) NULL); continue; } mng_info->exists[object_id]=True; if (length > 2) mng_info->invisible[object_id]=p[2]; /* Extract object offset info. */ if (length > 11) { mng_info->x_off[object_id]=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); mng_info->y_off[object_id]=(long) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]); if (logging) { (void) LogMagickEvent(CoderEvent,GetMagickModule(), " x_off[%d]: %lu",object_id,mng_info->x_off[object_id]); (void) LogMagickEvent(CoderEvent,GetMagickModule(), " y_off[%d]: %lu",object_id,mng_info->y_off[object_id]); } } /* Extract object clipping info. */ if (length > 27) mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0, &p[12]); MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_bKGD,4)) { mng_info->have_global_bkgd=False; if (length > 5) { mng_info->mng_global_bkgd.red =ScaleShortToQuantum(((p[0] << 8) | p[1])); mng_info->mng_global_bkgd.green =ScaleShortToQuantum(((p[2] << 8) | p[3])); mng_info->mng_global_bkgd.blue =ScaleShortToQuantum(((p[4] << 8) | p[5])); mng_info->have_global_bkgd=True; } MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_BACK,4)) { #ifdef MNG_INSERT_LAYERS if (length > 6) mandatory_back=p[6]; else mandatory_back=0; if (mandatory_back && length > 5) { mng_background_color.red= ScaleShortToQuantum(((p[0] << 8) | p[1])); mng_background_color.green= ScaleShortToQuantum(((p[2] << 8) | p[3])); mng_background_color.blue= ScaleShortToQuantum(((p[4] << 8) | p[5])); mng_background_color.opacity=OpaqueOpacity; } #ifdef MNG_OBJECT_BUFFERS if (length > 8) mng_background_object=(p[7] << 8) | p[8]; #endif #endif MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_PLTE,4)) { register long i; /* Read global PLTE. */ if (length && (length < 769)) { if (mng_info->global_plte == (png_colorp) NULL) mng_info->global_plte= MagickAllocateMemory(png_colorp,256*sizeof(png_color)); for (i=0; i < (long) (length/3); i++) { mng_info->global_plte[i].red=p[3*i]; mng_info->global_plte[i].green=p[3*i+1]; mng_info->global_plte[i].blue=p[3*i+2]; } mng_info->global_plte_length=length/3; } #ifdef MNG_LOOSE for ( ; i < 256; i++) { mng_info->global_plte[i].red=i; mng_info->global_plte[i].green=i; mng_info->global_plte[i].blue=i; } if (length) mng_info->global_plte_length=256; #endif else mng_info->global_plte_length=0; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_tRNS,4)) { register long i; /* read global tRNS */ if (length < 257) for (i=0; i < (long) length; i++) mng_info->global_trns[i]=p[i]; #ifdef MNG_LOOSE for ( ; i < 256; i++) mng_info->global_trns[i]=255; #endif mng_info->global_trns_length=length; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_gAMA,4)) { if (length == 4) { long igamma; igamma=mng_get_long(p); mng_info->global_gamma=((float) igamma)*0.00001; mng_info->have_global_gama=True; } else mng_info->have_global_gama=False; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_cHRM,4)) { /* Read global cHRM */ if (length == 32) { mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p); mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]); mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]); mng_info->global_chrm.red_primary.y=0.00001* mng_get_long(&p[12]); mng_info->global_chrm.green_primary.x=0.00001* mng_get_long(&p[16]); mng_info->global_chrm.green_primary.y=0.00001* mng_get_long(&p[20]); mng_info->global_chrm.blue_primary.x=0.00001* mng_get_long(&p[24]); mng_info->global_chrm.blue_primary.y=0.00001* mng_get_long(&p[28]); mng_info->have_global_chrm=True; } else mng_info->have_global_chrm=False; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_sRGB,4)) { /* Read global sRGB. */ if (length) { mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1); mng_info->have_global_srgb=True; } else mng_info->have_global_srgb=False; MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_iCCP,4)) { /* To do. */ /* Read global iCCP. */ if (length) MagickFreeMemory(chunk); continue; } if (!memcmp(type,mng_FRAM,4)) { if (mng_type == 3) (void) ThrowException2(&image->exception,CoderError, "FRAM chunk found in MNG-VLC datastream",(char *) NULL); if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4)) image->delay=frame_delay; frame_delay=default_frame_delay; frame_timeout=default_frame_timeout; fb=default_fb; if (length) if (p[0]) mng_info->framing_mode=p[0]; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Framing_mode=%d",mng_info->framing_mode); if (length > 6) { /* Note the delay and frame clipping boundaries. */ p++; /* framing mode */ while (*p && ((p-chunk) < (long) length)) p++; /* frame name */ p++; /* frame name terminator */ if ((p-chunk) < (long) (length-4)) { int change_delay, change_timeout, change_clipping; change_delay=(*p++); change_timeout=(*p++); change_clipping=(*p++); p++; /* change_sync */ if (change_delay) { frame_delay=(100*(mng_get_long(p))/ mng_info->ticks_per_second); if (change_delay == 2) default_frame_delay=frame_delay; p+=4; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Framing_delay=%ld",frame_delay); } if (change_timeout) { frame_timeout= (100*(mng_get_long(p))/mng_info->ticks_per_second); if (change_delay == 2) default_frame_timeout=frame_timeout; p+=4; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Framing_timeout=%ld",frame_timeout); } if (change_clipping) { fb=mng_read_box(previous_fb,p[0],&p[1]); p+=17; previous_fb=fb; if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Frame_clipping: L=%ld R=%ld T=%ld B=%ld", fb.left, fb.right,fb.top,fb.bottom); if (change_clipping == 2) default_fb=fb; } } } mng_info->clip=fb; mng_info->clip=mng_minimum_box(fb,mng_info->frame); subframe_width=(mng_info->clip.right-mng_info->clip.left); subframe_height=(mng_info->clip.bottom-mng_info->clip.top); /* Insert a background layer behind the frame if framing_mode is 4. */ #ifdef MNG_INSERT_LAYERS if (logging) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " subframe_width=%lu, subframe_height=%lu", subframe_width, subframe_height); if (insert_layers && (mng_info->framing_mode == 4) && (subframe_width) && (subframe_height)) { /* Allocate next image structure. */ if (GetPixels(image) != (PixelPacket *) NULL) { AllocateNextImage(image_info,image); if (image->next == (Image *) NULL) {