blob: 76e36126f017b981f8d6040c1351d6717470b625 [file] [log] [blame]
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% 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 %
% Cristy %
% Glenn Randers-Pehrson %
% November 1997 %
% %
% %
% Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/script/license.php %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
#define IM
/*
Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/artifact.h"
#include "MagickCore/attribute.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/channel.h"
#include "MagickCore/color.h"
#include "MagickCore/color-private.h"
#include "MagickCore/colormap.h"
#include "MagickCore/colormap-private.h"
#include "MagickCore/colorspace.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/constitute.h"
#include "MagickCore/enhance.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/geometry.h"
#include "MagickCore/histogram.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/layer.h"
#include "MagickCore/list.h"
#include "MagickCore/log.h"
#include "MagickCore/magick.h"
#include "MagickCore/magick-private.h"
#include "MagickCore/memory_.h"
#include "MagickCore/memory-private.h"
#include "MagickCore/module.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/profile.h"
#include "MagickCore/property.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/resource_.h"
#include "MagickCore/semaphore.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/statistic.h"
#include "MagickCore/string_.h"
#include "MagickCore/string-private.h"
#include "MagickCore/transform.h"
#include "MagickCore/utility.h"
#if defined(MAGICKCORE_PNG_DELEGATE)
/* Suppress libpng pedantic warnings that were added in
* libpng-1.2.41 and libpng-1.4.0. If you are working on
* migration to libpng-1.5, remove these defines and then
* fix any code that generates warnings.
*/
/* #define PNG_DEPRECATED Use of this function is deprecated */
/* #define PNG_USE_RESULT The result of this function must be checked */
/* #define PNG_NORETURN This function does not return */
/* #define PNG_ALLOCATED The result of the function is new memory */
/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
#define PNG_PTR_NORETURN
#include <png.h>
#include <zlib.h>
/* ImageMagick differences */
#define first_scene scene
#if PNG_LIBPNG_VER > 10011
/*
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 */
#if defined(MAGICKCORE_JPEG_DELEGATE)
# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
#endif
#if !defined(RGBColorMatchExact)
#define IsPNGColorEqual(color,target) \
(((color).red == (target).red) && \
((color).green == (target).green) && \
((color).blue == (target).blue))
#endif
/* Table of recognized sRGB ICC profiles */
struct sRGB_info_struct
{
png_uint_32 len;
png_uint_32 crc;
png_byte intent;
};
const struct sRGB_info_struct sRGB_info[] =
{
/* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
{ 3048, 0x3b8772b9UL, 0},
/* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
{ 3052, 0x427ebb21UL, 1},
/* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
{60988, 0x306fd8aeUL, 0},
/* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
{60960, 0xbbef7812UL, 0},
/* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
{ 3024, 0x5d5129ceUL, 1},
/* HP-Microsoft sRGB v2 perceptual */
{ 3144, 0x182ea552UL, 0},
/* HP-Microsoft sRGB v2 media-relative */
{ 3144, 0xf29e526dUL, 1},
/* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
{ 524, 0xd4938c39UL, 0},
/* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
{ 3212, 0x034af5a1UL, 0},
/* Not recognized */
{ 0, 0x00000000UL, 0},
};
/* Macros for left-bit-replication to ensure that pixels
* and PixelInfos all have the same image->depth, and for use
* in PNG8 quantization.
*/
/* LBR01: Replicate top bit */
#define LBR01PacketRed(pixelpacket) \
(pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
0 : QuantumRange);
#define LBR01PacketGreen(pixelpacket) \
(pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
0 : QuantumRange);
#define LBR01PacketBlue(pixelpacket) \
(pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
0 : QuantumRange);
#define LBR01PacketAlpha(pixelpacket) \
(pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
0 : QuantumRange);
#define LBR01PacketRGB(pixelpacket) \
{ \
LBR01PacketRed((pixelpacket)); \
LBR01PacketGreen((pixelpacket)); \
LBR01PacketBlue((pixelpacket)); \
}
#define LBR01PacketRGBA(pixelpacket) \
{ \
LBR01PacketRGB((pixelpacket)); \
LBR01PacketAlpha((pixelpacket)); \
}
#define LBR01PixelRed(pixel) \
(SetPixelRed(image, \
ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
0 : QuantumRange,(pixel)));
#define LBR01PixelGreen(pixel) \
(SetPixelGreen(image, \
ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
0 : QuantumRange,(pixel)));
#define LBR01PixelBlue(pixel) \
(SetPixelBlue(image, \
ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
0 : QuantumRange,(pixel)));
#define LBR01PixelAlpha(pixel) \
(SetPixelAlpha(image, \
ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
0 : QuantumRange,(pixel)));
#define LBR01PixelRGB(pixel) \
{ \
LBR01PixelRed((pixel)); \
LBR01PixelGreen((pixel)); \
LBR01PixelBlue((pixel)); \
}
#define LBR01PixelRGBA(pixel) \
{ \
LBR01PixelRGB((pixel)); \
LBR01PixelAlpha((pixel)); \
}
/* LBR02: Replicate top 2 bits */
#define LBR02PacketRed(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
(pixelpacket).red=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
}
#define LBR02PacketGreen(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
(pixelpacket).green=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
}
#define LBR02PacketBlue(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
(pixelpacket).blue=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
}
#define LBR02PacketAlpha(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
(pixelpacket).alpha=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
}
#define LBR02PacketRGB(pixelpacket) \
{ \
LBR02PacketRed((pixelpacket)); \
LBR02PacketGreen((pixelpacket)); \
LBR02PacketBlue((pixelpacket)); \
}
#define LBR02PacketRGBA(pixelpacket) \
{ \
LBR02PacketRGB((pixelpacket)); \
LBR02PacketAlpha((pixelpacket)); \
}
#define LBR02PixelRed(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
& 0xc0; \
SetPixelRed(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
(pixel)); \
}
#define LBR02PixelGreen(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
& 0xc0; \
SetPixelGreen(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
(pixel)); \
}
#define LBR02PixelBlue(pixel) \
{ \
unsigned char lbr_bits= \
ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
SetPixelBlue(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
(pixel)); \
}
#define LBR02PixelAlpha(pixel) \
{ \
unsigned char lbr_bits= \
ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
SetPixelAlpha(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
(pixel) ); \
}
#define LBR02PixelRGB(pixel) \
{ \
LBR02PixelRed((pixel)); \
LBR02PixelGreen((pixel)); \
LBR02PixelBlue((pixel)); \
}
#define LBR02PixelRGBA(pixel) \
{ \
LBR02PixelRGB((pixel)); \
LBR02PixelAlpha((pixel)); \
}
/* LBR03: Replicate top 3 bits (only used with opaque pixels during
PNG8 quantization) */
#define LBR03PacketRed(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
(pixelpacket).red=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
}
#define LBR03PacketGreen(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
(pixelpacket).green=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
}
#define LBR03PacketBlue(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
(pixelpacket).blue=ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
}
#define LBR03PacketRGB(pixelpacket) \
{ \
LBR03PacketRed((pixelpacket)); \
LBR03PacketGreen((pixelpacket)); \
LBR03PacketBlue((pixelpacket)); \
}
#define LBR03PixelRed(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
& 0xe0; \
SetPixelRed(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
}
#define LBR03Green(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
& 0xe0; \
SetPixelGreen(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
}
#define LBR03Blue(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
& 0xe0; \
SetPixelBlue(image, ScaleCharToQuantum( \
(lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
}
#define LBR03RGB(pixel) \
{ \
LBR03PixelRed((pixel)); \
LBR03Green((pixel)); \
LBR03Blue((pixel)); \
}
/* LBR04: Replicate top 4 bits */
#define LBR04PacketRed(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
(pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
}
#define LBR04PacketGreen(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
(pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
}
#define LBR04PacketBlue(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
(pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
}
#define LBR04PacketAlpha(pixelpacket) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
(pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
}
#define LBR04PacketRGB(pixelpacket) \
{ \
LBR04PacketRed((pixelpacket)); \
LBR04PacketGreen((pixelpacket)); \
LBR04PacketBlue((pixelpacket)); \
}
#define LBR04PacketRGBA(pixelpacket) \
{ \
LBR04PacketRGB((pixelpacket)); \
LBR04PacketAlpha((pixelpacket)); \
}
#define LBR04PixelRed(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
& 0xf0; \
SetPixelRed(image,\
ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
}
#define LBR04PixelGreen(pixel) \
{ \
unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
& 0xf0; \
SetPixelGreen(image,\
ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
}
#define LBR04PixelBlue(pixel) \
{ \
unsigned char lbr_bits= \
ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
SetPixelBlue(image,\
ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
}
#define LBR04PixelAlpha(pixel) \
{ \
unsigned char lbr_bits= \
ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
SetPixelAlpha(image,\
ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
}
#define LBR04PixelRGB(pixel) \
{ \
LBR04PixelRed((pixel)); \
LBR04PixelGreen((pixel)); \
LBR04PixelBlue((pixel)); \
}
#define LBR04PixelRGBA(pixel) \
{ \
LBR04PixelRGB((pixel)); \
LBR04PixelAlpha((pixel)); \
}
/*
Establish thread safety.
setjmp/longjmp is claimed to be safe on these platforms:
setjmp/longjmp is alleged to be unsafe on these platforms:
*/
#ifdef PNG_SETJMP_SUPPORTED
# ifndef IMPNG_SETJMP_IS_THREAD_SAFE
# define IMPNG_SETJMP_NOT_THREAD_SAFE
# endif
# ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
static SemaphoreInfo
*ping_semaphore = (SemaphoreInfo *) NULL;
# endif
#endif
/*
This 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 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.
*/
#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 size_t in PNG/MNG chunks is (2^31)-1
This macro is only defined in libpng-1.0.3 and later.
Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
*/
#ifndef PNG_UINT_31_MAX
#define PNG_UINT_31_MAX (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 const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
static const png_byte mng_orNT[5]={111, 114, 78, 84, (png_byte) '\0'};
static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
#if defined(JNG_SUPPORTED)
static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
#endif
#if 0
/* Other known chunks that are not yet supported by ImageMagick: */
static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
#endif
typedef struct _MngBox
{
long
left,
right,
top,
bottom;
} MngBox;
typedef struct _MngPair
{
volatile long
a,
b;
} MngPair;
#ifdef MNG_OBJECT_BUFFERS
typedef struct _MngBuffer
{
size_t
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,
saved_bkgd_index;
int
new_number_colors;
ssize_t
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];
MagickOffsetType
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;
MagickBooleanType
need_blob;
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_png_colortype,
write_png_depth,
write_png_compression_level,
write_png_compression_strategy,
write_png_compression_filter,
write_png8,
write_png24,
write_png32,
write_png48,
write_png64;
#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;
PixelInfo
mng_global_bkgd;
/* Added at version 6.6.6-7 */
MagickBooleanType
ping_exclude_bKGD,
ping_exclude_cHRM,
ping_exclude_date,
ping_exclude_eXIf,
ping_exclude_EXIF,
ping_exclude_gAMA,
ping_exclude_iCCP,
/* ping_exclude_iTXt, */
ping_exclude_oFFs,
ping_exclude_pHYs,
ping_exclude_sRGB,
ping_exclude_tEXt,
ping_exclude_tRNS,
ping_exclude_caNv,
ping_exclude_zCCP, /* hex-encoded iCCP */
ping_exclude_zTXt,
ping_preserve_colormap,
/* Added at version 6.8.5-7 */
ping_preserve_iCCP,
/* Added at version 6.8.9-9 */
ping_exclude_tIME;
} MngInfo;
#endif /* VER */
/*
Forward declarations.
*/
static MagickBooleanType
WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
static MagickBooleanType
WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
#if defined(JNG_SUPPORTED)
static MagickBooleanType
WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
#endif
#if PNG_LIBPNG_VER > 10011
#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
static MagickBooleanType
LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
{
/* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
*
* This is true if the high byte and the next highest byte of
* each sample of the image, the colormap, and the background color
* are equal to each other. We check this by seeing if the samples
* are unchanged when we scale them down to 8 and back up to Quantum.
*
* We don't use the method GetImageDepth() because it doesn't check
* background and doesn't handle PseudoClass specially.
*/
#define QuantumToCharToQuantumEqQuantum(quantum) \
((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
MagickBooleanType
ok_to_reduce=MagickFalse;
if (image->depth >= 16)
{
const Quantum
*p;
ok_to_reduce=
QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
MagickTrue : MagickFalse;
if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
{
int indx;
for (indx=0; indx < (ssize_t) image->colors; indx++)
{
ok_to_reduce=(
QuantumToCharToQuantumEqQuantum(
image->colormap[indx].red) &&
QuantumToCharToQuantumEqQuantum(
image->colormap[indx].green) &&
QuantumToCharToQuantumEqQuantum(
image->colormap[indx].blue)) ?
MagickTrue : MagickFalse;
if (ok_to_reduce == MagickFalse)
break;
}
}
if ((ok_to_reduce != MagickFalse) &&
(image->storage_class != PseudoClass))
{
ssize_t
y;
register ssize_t
x;
for (y=0; y < (ssize_t) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
{
ok_to_reduce = MagickFalse;
break;
}
for (x=(ssize_t) image->columns-1; x >= 0; x--)
{
ok_to_reduce=
QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
MagickTrue : MagickFalse;
if (ok_to_reduce == MagickFalse)
break;
p+=GetPixelChannels(image);
}
if (x >= 0)
break;
}
}
if (ok_to_reduce != MagickFalse)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" OK to reduce PNG bit depth to 8 without loss of info");
}
else
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Not OK to reduce PNG bit depth to 8 without losing info");
}
}
return ok_to_reduce;
}
#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
static const char* PngColorTypeToString(const unsigned int color_type)
{
const char
*result = "Unknown";
switch (color_type)
{
case PNG_COLOR_TYPE_GRAY:
result = "Gray";
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
result = "Gray+Alpha";
break;
case PNG_COLOR_TYPE_PALETTE:
result = "Palette";
break;
case PNG_COLOR_TYPE_RGB:
result = "RGB";
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
result = "RGB+Alpha";
break;
}
return result;
}
static int
Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
{
switch (orientation)
{
/* Convert to Exif orientations as defined in "Exchangeable image file
* format for digital still cameras: Exif Version 2.31",
* http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
*/
case TopLeftOrientation:
return 1;
case TopRightOrientation:
return 2;
case BottomRightOrientation:
return 3;
case BottomLeftOrientation:
return 4;
case LeftTopOrientation:
return 5;
case RightTopOrientation:
return 6;
case RightBottomOrientation:
return 7;
case LeftBottomOrientation:
return 8;
case UndefinedOrientation:
default:
return 0;
}
}
static OrientationType
Magick_Orientation_from_Exif_Orientation(const int orientation)
{
switch (orientation)
{
case 1:
return TopLeftOrientation;
case 2:
return TopRightOrientation;
case 3:
return BottomRightOrientation;
case 4:
return BottomLeftOrientation;
case 5:
return LeftTopOrientation;
case 6:
return RightTopOrientation;
case 7:
return RightBottomOrientation;
case 8:
return LeftBottomOrientation;
case 0:
default:
return UndefinedOrientation;
}
}
static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
{
switch (intent)
{
case PerceptualIntent:
return 0;
case RelativeIntent:
return 1;
case SaturationIntent:
return 2;
case AbsoluteIntent:
return 3;
default:
return -1;
}
}
static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
{
switch (ping_intent)
{
case 0:
return PerceptualIntent;
case 1:
return RelativeIntent;
case 2:
return SaturationIntent;
case 3:
return AbsoluteIntent;
default:
return UndefinedIntent;
}
}
static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
{
switch (ping_intent)
{
case 0:
return "Perceptual Intent";
case 1:
return "Relative Intent";
case 2:
return "Saturation Intent";
case 3:
return "Absolute Intent";
default:
return "Undefined Intent";
}
}
static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
{
switch (ping_colortype)
{
case 0:
return "Grayscale";
case 2:
return "Truecolor";
case 3:
return "Indexed";
case 4:
return "GrayAlpha";
case 6:
return "RGBA";
default:
return "UndefinedColorType";
}
}
#endif /* PNG_LIBPNG_VER > 10011 */
#endif /* MAGICKCORE_PNG_DELEGATE */
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s M N G %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsMNG() returns MagickTrue if the image format type, identified by the
% magick string, is MNG.
%
% The format of the IsMNG method is:
%
% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
%
*/
static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
{
if (length < 8)
return(MagickFalse);
if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s J N G %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsJNG() returns MagickTrue if the image format type, identified by the
% magick string, is JNG.
%
% The format of the IsJNG method is:
%
% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
%
*/
static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
{
if (length < 8)
return(MagickFalse);
if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s P N G %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsPNG() returns MagickTrue if the image format type, identified by the
% magick string, is PNG.
%
% The format of the IsPNG method is:
%
% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
{
if (length < 8)
return(MagickFalse);
if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
return(MagickTrue);
return(MagickFalse);
}
#if defined(MAGICKCORE_PNG_DELEGATE)
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#if (PNG_LIBPNG_VER > 10011)
static size_t WriteBlobMSBULong(Image *image,const size_t value)
{
unsigned char
buffer[4];
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
buffer[0]=(unsigned char) (value >> 24);
buffer[1]=(unsigned char) (value >> 16);
buffer[2]=(unsigned char) (value >> 8);
buffer[3]=(unsigned char) value;
return((size_t) 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,const png_byte *type)
{
(void) memcpy(p,type,4*sizeof(png_byte));
}
static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
size_t length)
{
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Writing %c%c%c%c chunk, length: %.20g",
type[0],type[1],type[2],type[3],(double) length);
}
#endif /* PNG_LIBPNG_VER > 10011 */
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#if PNG_LIBPNG_VER > 10011
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d P N G I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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, glennrp@image...
%
% The format of the ReadPNGImage method is:
%
% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% 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->exception instead
% of storing another set of pixels. This not specific to MNG
% but could be applied generally.
%
% Upgrade to full MNG with Delta-PNG.
%
% JNG tEXt/iTXt/zTXt
%
% We will not attempt to read files containing the CgBI chunk.
% They are really Xcode files meant for display on the iPhone.
% These are not valid PNG files and it is impossible to recover
% the original PNG from files that have been converted to Xcode-PNG,
% since irretrievable loss of color data has occurred due to the
% use of premultiplied alpha.
*/
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
/*
This 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 != 0)
{
png_size_t
check;
check=(png_size_t) ReadBlob(image,(size_t) length,data);
if (check != length)
{
char
msg[MagickPathExtent];
(void) FormatLocaleString(msg,MagickPathExtent,
"Expected %.20g bytes; found %.20g bytes",(double) length,
(double) 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 ssize_t
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 != 0)
{
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) == 0)
mng_info->found_empty_plte=MagickTrue;
if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
{
mng_info->found_empty_plte=MagickFalse;
mng_info->have_saved_bkgd_index=MagickFalse;
}
}
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) == 0)
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=MagickTrue;
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 != 0)
{
png_size_t
check;
check=(png_size_t) WriteBlob(image,(size_t) length,data);
if (check != length)
png_error(png_ptr,"WriteBlob Failed");
}
}
static void png_flush_data(png_structp png_ptr)
{
(void) png_ptr;
}
#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
static int PalettesAreEqual(Image *a,Image *b)
{
ssize_t
i;
if ((a == (Image *) NULL) || (b == (Image *) NULL))
return((int) MagickFalse);
if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
return((int) MagickFalse);
if (a->colors != b->colors)
return((int) MagickFalse);
for (i=0; i < (ssize_t) 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) MagickFalse);
}
return((int) MagickTrue);
}
#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)
mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
mng_info->ob[i]=DestroyString(mng_info->ob[i]);
}
}
mng_info->ob[i]=(MngBuffer *) NULL;
#endif
mng_info->exists[i]=MagickFalse;
mng_info->invisible[i]=MagickFalse;
mng_info->viewable[i]=MagickFalse;
mng_info->frozen[i]=MagickFalse;
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=(ssize_t) PNG_UINT_31_MAX;
mng_info->object_clip[i].top=0;
mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
}
}
static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
{
register ssize_t
i;
if (mng_info == (MngInfo *) NULL)
return((MngInfo *) NULL);
for (i=1; i < MNG_MAX_OBJECTS; i++)
MngInfoDiscardObject(mng_info,i);
mng_info->global_plte=(png_colorp)
RelinquishMagickMemory(mng_info->global_plte);
return((MngInfo *) RelinquishMagickMemory(mng_info));
}
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 long mng_get_long(unsigned char *p)
{
return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
}
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=mng_get_long(p);
box.right=mng_get_long(&p[4]);
box.top=mng_get_long(&p[8]);
box.bottom=mng_get_long(&p[12]);
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 ssize_t's from CLON, MOVE or PAST chunk
*/
pair.a=mng_get_long(p);
pair.b=mng_get_long(&p[4]);
if (delta_type != 0)
{
pair.a+=previous_pair.a;
pair.b+=previous_pair.b;
}
return(pair);
}
typedef struct _PNGErrorInfo
{
Image
*image;
ExceptionInfo
*exception;
} PNGErrorInfo;
static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
{
ExceptionInfo
*exception;
Image
*image;
PNGErrorInfo
*error_info;
error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
image=error_info->image;
exception=error_info->exception;
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" libpng-%s error: %s", png_get_libpng_ver(NULL),message);
(void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
"`%s'",image->filename);
#if (PNG_LIBPNG_VER < 10500)
/* A warning about deprecated use of jmpbuf here is unavoidable if you
* are building with libpng-1.4.x and can be ignored.
*/
longjmp(ping->jmpbuf,1);
#else
png_longjmp(ping,1);
#endif
}
static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
{
ExceptionInfo
*exception;
Image
*image;
PNGErrorInfo
*error_info;
if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
png_error(ping, message);
error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
image=error_info->image;
exception=error_info->exception;
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
(void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
message,"`%s'",image->filename);
}
#ifdef PNG_USER_MEM_SUPPORTED
#if PNG_LIBPNG_VER >= 10400
static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
#else
static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
#endif
{
(void) png_ptr;
return((png_voidp) AcquireMagickMemory((size_t) size));
}
/*
Free a pointer. It is removed from the list at the same time.
*/
static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
{
(void) png_ptr;
ptr=RelinquishMagickMemory(ptr);
return((png_free_ptr) NULL);
}
#endif
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
static int
Magick_png_read_raw_profile(png_struct *ping,Image *image,
const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
{
register ssize_t
i;
register unsigned char
*dp;
register png_charp
sp;
size_t
extent,
length,
nibbles;
StringInfo
*profile;
static const 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;
extent=text[ii].text_length;
/* look for newline */
while ((*sp != '\n') && extent--)
sp++;
/* look for length */
while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
sp++;
if (extent == 0)
{
png_warning(ping,"invalid profile length");
return(MagickFalse);
}
length=StringToLong(sp);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" length: %lu",(unsigned long) length);
while ((*sp != ' ' && *sp != '\n') && extent--)
sp++;
if (extent == 0)
{
png_warning(ping,"missing profile length");
return(MagickFalse);
}
/* allocate space */
if (length == 0)
{
png_warning(ping,"invalid profile length");
return(MagickFalse);
}
profile=BlobToStringInfo((const void *) NULL,length);
if (profile == (StringInfo *) NULL)
{
png_warning(ping, "unable to copy profile");
return(MagickFalse);
}
/* copy profile, skipping white space and column 1 "=" signs */
dp=GetStringInfoDatum(profile);
nibbles=length*2;
for (i=0; i < (ssize_t) nibbles; i++)
{
while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
{
if (*sp == '\0')
{
png_warning(ping, "ran out of profile data");
profile=DestroyStringInfo(profile);
return(MagickFalse);
}
sp++;
}
if (i%2 == 0)
*dp=(unsigned char) (16*unhex[(int) *sp++]);
else
(*dp++)+=unhex[(int) *sp++];
}
/*
We have already read "Raw profile type.
*/
(void) SetImageProfile(image,&text[ii].key[17],profile,exception);
profile=DestroyStringInfo(profile);
if (image_info->verbose)
(void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
return MagickTrue;
}
static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
ExceptionInfo *exception)
{
StringInfo
*profile;
unsigned char
*p;
png_byte
*s;
size_t
i;
profile=BlobToStringInfo((const void *) NULL,size+6);
if (profile == (StringInfo *) NULL)
{
(void) ThrowMagickException(exception,GetMagickModule(),
ResourceLimitError,"MemoryAllocationFailed","`%s'",
image->filename);
return(-1);
}
p=GetStringInfoDatum(profile);
/* Initialize profile with "Exif\0\0" */
*p++ ='E';
*p++ ='x';
*p++ ='i';
*p++ ='f';
*p++ ='\0';
*p++ ='\0';
s=data;
i=0;
if (size > 6)
{
/* Skip first 6 bytes if "Exif\0\0" is
already present by accident
*/
if (s[0] == 'E' && s[1] == 'x' && s[2] == 'i' &&
s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
{
s+=6;
i=6;
SetStringInfoLength(profile,size);
p=GetStringInfoDatum(profile);
}
}
/* copy chunk->data to profile */
for (; i<size; i++)
*p++ = *s++;
(void) SetImageProfile(image,"exif",profile,exception);
profile=DestroyStringInfo(profile);
return(1);
}
#if defined(PNG_READ_eXIf_SUPPORTED)
static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
ExceptionInfo *exception)
{
png_uint_32
size;
png_bytep
data;
#if PNG_LIBPNG_VER > 10631
if (png_get_eXIf_1(ping,info,&size,&data))
(void) PNGSetExifProfile(image,size,data,exception);
#endif
}
#endif
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
{
Image
*image;
PNGErrorInfo
*error_info;
/* The unknown chunk structure contains the chunk data:
png_byte name[5];
png_byte *data;
png_size_t size;
Note that libpng has already taken care of the CRC handling.
Returns one of the following:
return(-n); chunk had an error
return(0); did not recognize
return(n); success
*/
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" read_user_chunk: found %c%c%c%c chunk",
chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
if (chunk->name[0] == 101 &&
(chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
chunk->name[2] == 73 &&
chunk-> name[3] == 102)
{
/* process eXIf or exIf chunk */
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" recognized eXIf chunk");
image=(Image *) png_get_user_chunk_ptr(ping);
error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
return(PNGSetExifProfile(image,chunk->size,chunk->data,
error_info->exception));
}
/* orNT */
if (chunk->name[0] == 111 &&
chunk->name[1] == 114 &&
chunk->name[2] == 78 &&
chunk->name[3] == 84)
{
/* recognized orNT */
if (chunk->size != 1)
return(-1); /* Error return */
image=(Image *) png_get_user_chunk_ptr(ping);
image->orientation=
Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
return(1);
}
/* vpAg (deprecated, replaced by caNv) */
if (chunk->name[0] == 118 &&
chunk->name[1] == 112 &&
chunk->name[2] == 65 &&
chunk->name[3] == 103)
{
/* recognized vpAg */
if (chunk->size != 9)
return(-1); /* Error return */
if (chunk->data[8] != 0)
return(0); /* ImageMagick requires pixel units */
image=(Image *) png_get_user_chunk_ptr(ping);
image->page.width=(size_t)mng_get_long(chunk->data);
image->page.height=(size_t)mng_get_long(&chunk->data[4]);
return(1);
}
/* caNv */
if (chunk->name[0] == 99 &&
chunk->name[1] == 97 &&
chunk->name[2] == 78 &&
chunk->name[3] == 118)
{
/* recognized caNv */
if (chunk->size != 16)
return(-1); /* Error return */
image=(Image *) png_get_user_chunk_ptr(ping);
image->page.width=(size_t) mng_get_long(chunk->data);
image->page.height=(size_t) mng_get_long(&chunk->data[4]);
image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
return(1);
}
/* acTL */
if ((chunk->name[0] == 97) && (chunk->name[1] == 99) &&
(chunk->name[2] == 84) && (chunk->name[3] == 76))
{
image=(Image *) png_get_user_chunk_ptr(ping);
error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
(void) SetImageProperty(image,"png:acTL","chunk was found",
error_info->exception);
return(1);
}
return(0); /* Did not recognize */
}
#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
#if defined(PNG_tIME_SUPPORTED)
static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
ExceptionInfo *exception)
{
png_timep
time;
if (png_get_tIME(ping,info,&time))
{
char
timestamp[21];
FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
time->year,time->month,time->day,time->hour,time->minute,time->second);
SetImageProperty(image,"png:tIME",timestamp,exception);
}
}
#endif
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d O n e P N G I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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 mng_info: Specifies a pointer to a MngInfo structure.
%
% o image_info: the image info.
%
% 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 */
/* To do: Read the tEXt/Creation Time chunk into the date:create property */
Image
*image;
char
im_vers[32],
libpng_runv[32],
libpng_vers[32],
zlib_runv[32],
zlib_vers[32];
int
intent, /* "PNG Rendering intent", which is ICC intent + 1 */
num_raw_profiles,
num_text,
num_text_total,
num_passes,
number_colors,
pass,
ping_bit_depth,
ping_color_type,
ping_file_depth,
ping_interlace_method,
ping_compression_method,
ping_filter_method,
ping_num_trans,
unit_type;
double
file_gamma;
MagickBooleanType
logging,
ping_found_cHRM,
ping_found_gAMA,
ping_found_iCCP,
ping_found_sRGB,
ping_found_sRGB_cHRM,
ping_preserve_iCCP,
status;
MemoryInfo
*volatile pixel_info;
PixelInfo
transparent_color;
PNGErrorInfo
error_info;
png_bytep
ping_trans_alpha;
png_color_16p
ping_background,
ping_trans_color;
png_info
*end_info,
*ping_info;
png_struct
*ping;
png_textp
text;
png_uint_32
ping_height,
ping_width,
x_resolution,
y_resolution;
QuantumInfo
*volatile quantum_info;
Quantum
*volatile quantum_scanline;
ssize_t
ping_rowbytes,
y;
register unsigned char
*p;
register ssize_t
i,
x;
register Quantum
*q;
size_t
length,
row_offset;
ssize_t
j;
unsigned char
*ping_pixels;
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
png_byte unused_chunks[]=
{
104, 73, 83, 84, (png_byte) '\0', /* hIST */
105, 84, 88, 116, (png_byte) '\0', /* iTXt */
112, 67, 65, 76, (png_byte) '\0', /* pCAL */
115, 67, 65, 76, (png_byte) '\0', /* sCAL */
115, 80, 76, 84, (png_byte) '\0', /* sPLT */
#if !defined(PNG_tIME_SUPPORTED)
116, 73, 77, 69, (png_byte) '\0', /* tIME */
#endif
#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
/* ignore the APNG chunks */
97, 99, 84, 76, (png_byte) '\0', /* acTL */
102, 99, 84, 76, (png_byte) '\0', /* fcTL */
102, 100, 65, 84, (png_byte) '\0', /* fdAT */
#endif
};
#endif
/* Define these outside of the following "if logging()" block so they will
* show in debuggers.
*/
*im_vers='\0';
(void) ConcatenateMagickString(im_vers,
MagickLibVersionText,32);
(void) ConcatenateMagickString(im_vers,
MagickLibAddendum,32);
*libpng_vers='\0';
(void) ConcatenateMagickString(libpng_vers,
PNG_LIBPNG_VER_STRING,32);
*libpng_runv='\0';
(void) ConcatenateMagickString(libpng_runv,
png_get_libpng_ver(NULL),32);
*zlib_vers='\0';
(void) ConcatenateMagickString(zlib_vers,
ZLIB_VERSION,32);
*zlib_runv='\0';
(void) ConcatenateMagickString(zlib_runv,
zlib_version,32);
logging=LogMagickEvent(CoderEvent,GetMagickModule(),
" Enter ReadOnePNGImage()\n"
" IM version = %s\n"
" Libpng version = %s",
im_vers, libpng_vers);
if (logging != MagickFalse)
{
if (LocaleCompare(libpng_vers,libpng_runv) != 0)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" running with %s", libpng_runv);
}
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Zlib version = %s", zlib_vers);
if (LocaleCompare(zlib_vers,zlib_runv) != 0)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" running with %s", zlib_runv);
}
}
#if (PNG_LIBPNG_VER < 10200)
if (image_info->verbose)
printf("Your PNG library (libpng-%s) is rather old.\n",
PNG_LIBPNG_VER_STRING);
#endif
#if (PNG_LIBPNG_VER >= 10400)
# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
if (image_info->verbose)
{
printf("Your PNG library (libpng-%s) is an old beta version.\n",
PNG_LIBPNG_VER_STRING);
printf("Please update it.\n");
}
# endif
#endif
quantum_info = (QuantumInfo *) NULL;
image=mng_info->image;
if (logging != MagickFalse)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Before reading:\n"
" image->alpha_trait=%d\n"
" image->rendering_intent=%d\n"
" image->colorspace=%d\n"
" image->gamma=%f",
(int) image->alpha_trait, (int) image->rendering_intent,
(int) image->colorspace, image->gamma);
}
intent=
Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
/* Set to an out-of-range color unless tRNS chunk is present */
transparent_color.red=65537;
transparent_color.green=65537;
transparent_color.blue=65537;
transparent_color.alpha=65537;
number_colors=0;
num_text = 0;
num_text_total = 0;
num_raw_profiles = 0;
ping_found_cHRM = MagickFalse;
ping_found_gAMA = MagickFalse;
ping_found_iCCP = MagickFalse;
ping_found_sRGB = MagickFalse;
ping_found_sRGB_cHRM = MagickFalse;
ping_preserve_iCCP = MagickFalse;
/*
Allocate the PNG structures
*/
#ifdef PNG_USER_MEM_SUPPORTED
error_info.image=image;
error_info.exception=exception;
ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
(png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
#else
ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
MagickPNGErrorHandler,MagickPNGWarningHandler);
#endif
if (ping == (png_struct *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
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");
}
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");
}
pixel_info=(MemoryInfo *) NULL;
quantum_scanline = (Quantum *) NULL;
quantum_info = (QuantumInfo *) NULL;
if (setjmp(png_jmpbuf(ping)))
{
/*
PNG image is corrupt.
*/
png_destroy_read_struct(&ping,&ping_info,&end_info);
if (pixel_info != (MemoryInfo *) NULL)
pixel_info=RelinquishVirtualMemory(pixel_info);
if (quantum_info != (QuantumInfo *) NULL)
quantum_info=DestroyQuantumInfo(quantum_info);
quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
UnlockSemaphoreInfo(ping_semaphore);
#endif
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" exit ReadOnePNGImage() with error.");
if (image != (Image *) NULL)
image=DestroyImageList(image);
return(image);
}
/* { For navigation to end of SETJMP-protected block. Within this
* block, use png_error() instead of Throwing an Exception, to ensure
* that libpng is able to clean up, and that the semaphore is unlocked.
*/
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
LockSemaphoreInfo(ping_semaphore);
#endif
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
/* Allow benign errors */
png_set_benign_errors(ping, 1);
#endif
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
{
const char
*option;
/* Reject images with too many rows or columns */
png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
GetMagickResourceLimit(WidthResource)),(png_uint_32)
MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
#if (PNG_LIBPNG_VER >= 10400)
option=GetImageOption(image_info,"png:chunk-cache-max");
if (option != (const char *) NULL)
png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
StringToLong(option)));
else
png_set_chunk_cache_max(ping,32767);
#endif
#if (PNG_LIBPNG_VER >= 10401)
option=GetImageOption(image_info,"png:chunk-malloc-max");
if (option != (const char *) NULL)
png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
(size_t) StringToLong(option)));
else
png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
#endif
}
#endif /* PNG_SET_USER_LIMITS_SUPPORTED */
/*
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)
(void) 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,MagickTrue);
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=MagickFalse;
mng_info->have_saved_bkgd_index=MagickFalse;
png_set_read_fn(ping,mng_info,mng_get_data);
#endif
#endif
}
else
png_set_read_fn(ping,image,png_get_data);
{
const char
*value;
value=GetImageOption(image_info,"png:ignore-crc");
if (value != NULL)
{
/* Turn off CRC checking while reading */
png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
#ifdef PNG_IGNORE_ADLER32
/* Turn off ADLER32 checking while reading */
png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
#endif
}
value=GetImageOption(image_info,"profile:skip");
if (IsOptionMember("ICC",value) == MagickFalse)
{
value=GetImageOption(image_info,"png:preserve-iCCP");
if (value == NULL)
value=GetImageArtifact(image,"png:preserve-iCCP");
if (value != NULL)
ping_preserve_iCCP=MagickTrue;
#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
/* Don't let libpng check for ICC/sRGB profile because we're going
* to do that anyway. This feature was added at libpng-1.6.12.
* If logging, go ahead and check and issue a warning as appropriate.
*/
if (logging == MagickFalse)
png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
#endif
}
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
else
{
png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
}
#endif
}
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
/* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
# if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
png_set_keep_unknown_chunks(ping, 2, NULL, 0);
# else
png_set_keep_unknown_chunks(ping, 1, NULL, 0);
# endif
png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
png_set_keep_unknown_chunks(ping, 1, unused_chunks,
(int)sizeof(unused_chunks)/5);
/* Callback for other unknown chunks */
png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
#endif
#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
/* Disable new libpng-1.5.10 feature */
png_set_check_for_invalid_index (ping, 0);
#endif
#if (PNG_LIBPNG_VER < 10400)
# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
(PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
/* 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
#endif
png_read_info(ping,ping_info);
png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
&ping_bit_depth,&ping_color_type,
&ping_interlace_method,&ping_compression_method,
&ping_filter_method);
ping_file_depth = ping_bit_depth;
/* Swap bytes if requested */
if (ping_file_depth == 16)
{
const char
*value;
value=GetImageOption(image_info,"png:swap-bytes");
if (value == NULL)
value=GetImageArtifact(image,"png:swap-bytes");
if (value != NULL)
png_set_swap(ping);
}
/* Save bit-depth and color-type in case we later want to write a PNG00 */
{
char
msg[MagickPathExtent];
(void) FormatLocaleString(msg,MagickPathExtent,"%d",
(int) ping_color_type);
(void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
(void) FormatLocaleString(msg,MagickPathExtent,"%d",
(int) ping_bit_depth);
(void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
}
(void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
&ping_trans_color);
(void) png_get_bKGD(ping, ping_info, &ping_background);
if (ping_bit_depth < 8)
{
png_set_packing(ping);
ping_bit_depth = 8;
}
image->depth=ping_bit_depth;
image->depth=GetImageQuantumDepth(image,MagickFalse);
image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
{
image->rendering_intent=UndefinedIntent;
intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
(void) memset(&image->chromaticity,0,
sizeof(image->chromaticity));
}
if (logging != MagickFalse)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" PNG width: %.20g, height: %.20g\n"
" PNG color_type: %d, bit_depth: %d\n"
" PNG compression_method: %d\n"
" PNG interlace_method: %d, filter_method: %d",
(double) ping_width, (double) ping_height,
ping_color_type, ping_bit_depth,
ping_compression_method,
ping_interlace_method,ping_filter_method);
}
if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
{
ping_found_iCCP=MagickTrue;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Found PNG iCCP chunk.");
}
if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
{
ping_found_gAMA=MagickTrue;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Found PNG gAMA chunk.");
}
if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
{
ping_found_cHRM=MagickTrue;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Found PNG cHRM chunk.");
}
if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
PNG_INFO_sRGB))
{
ping_found_sRGB=MagickTrue;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Found PNG sRGB chunk.");
}
#ifdef PNG_READ_iCCP_SUPPORTED
if (ping_found_iCCP !=MagickTrue &&
ping_found_sRGB != MagickTrue &&
png_get_valid(ping,ping_info, PNG_INFO_iCCP))
{
ping_found_iCCP=MagickTrue;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Found PNG iCCP chunk.");
}
if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
{
int
compression;
#if (PNG_LIBPNG_VER < 10500)
png_charp
info;
#else
png_bytep
info;
#endif
png_charp
name;
png_uint_32
profile_length;
(void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
&profile_length);
if (profile_length != 0)
{
StringInfo
*profile;
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Reading PNG iCCP chunk.");
profile=BlobToStringInfo(info,(const size_t) profile_length);
if (profile == (StringInfo *) NULL)
{
png_warning(ping, "ICC profile is NULL");
profile=DestroyStringInfo(profile);
}
else
{
if (ping_preserve_iCCP == MagickFalse)
{
int
icheck,
got_crc=0;
png_uint_32
profile_crc=0;
unsigned char
*data;
profile_length=(png_uint_32) GetStringInfoLength(profile);
for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
{
if (profile_length == sRGB_info[icheck].len)
{
if (got_crc == 0)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Got a %lu-byte ICC profile (potentially sRGB)",
(unsigned long) profile_length);
data=GetStringInfoDatum(profile);
profile_crc=crc32(0,data,profile_length);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" with crc=%8x",(unsigned int) profile_crc);
got_crc++;
}
if (profile_crc == sRGB_info[icheck].crc)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" It is sRGB with rendering intent = %s",
Magick_RenderingIntentString_from_PNG_RenderingIntent(
sRGB_info[icheck].intent));
if (image->rendering_intent==UndefinedIntent)
{
image->rendering_intent=
Magick_RenderingIntent_from_PNG_RenderingIntent(
sRGB_info[icheck].intent);
}
break;
}
}
}
if (sRGB_info[icheck].len == 0)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Got %lu-byte ICC profile not recognized as sRGB",
(unsigned long) profile_length);
(void) SetImageProfile(image,"icc",profile,exception);
}
}
else /* Preserve-iCCP */
{
(void) SetImageProfile(image,"icc",profile,exception);
}
profile=DestroyStringInfo(profile);
}
}