14315 lines
445 KiB
C
14315 lines
445 KiB
C
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% 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-2021 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/timer-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;
|
||
|
||
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;
|
||
|
||
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)
|
||
{
|
||
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) AcquireQuantumMemory(1,(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)
|
||
{
|
||
png_charp
|
||
ep;
|
||
|
||
ssize_t
|
||
i;
|
||
|
||
unsigned char
|
||
*dp;
|
||
|
||
png_charp
|
||
sp;
|
||
|
||
size_t
|
||
extent,
|
||
nibbles;
|
||
|
||
ssize_t
|
||
length;
|
||
|
||
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;
|
||
ep=text[ii].text+extent;
|
||
if (ep <= sp)
|
||
{
|
||
png_warning(ping,"invalid profile length");
|
||
return(MagickFalse);
|
||
}
|
||
/* 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;
|
||
|
||
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 = NULL;
|
||
|
||
png_color_16p
|
||
ping_background = (png_color_16p) NULL,
|
||
ping_trans_color = (png_color_16p) NULL;
|
||
|
||
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
|
||
y;
|
||
|
||
unsigned char
|
||
*p;
|
||
|
||
ssize_t
|
||
i,
|
||
x;
|
||
|
||
Quantum
|
||
*q;
|
||
|
||
size_t
|
||
length,
|
||
ping_rowbytes,
|
||
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
|
||
|
||
logging=IsEventLogging();
|
||
if (logging != MagickFalse)
|
||
{
|
||
char
|
||
im_vers[32],
|
||
libpng_runv[32],
|
||
libpng_vers[32],
|
||
zlib_runv[32],
|
||
zlib_vers[32];
|
||
|
||
*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);
|
||
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Enter ReadOnePNGImage()\n"
|
||
" IM version = %s\n"
|
||
" Libpng version = %s",
|
||
im_vers, libpng_vers);
|
||
|
||
if (LocaleCompare(libpng_vers,libpng_runv) != 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" running with %s", libpng_runv);
|
||
if (LocaleCompare(libpng_vers,zlib_vers) != 0)
|
||
(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 (IsStringTrue(value) != MagickFalse)
|
||
{
|
||
/* 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);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if defined(PNG_READ_sRGB_SUPPORTED)
|
||
{
|
||
if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
|
||
PNG_INFO_sRGB))
|
||
{
|
||
if (png_get_sRGB(ping,ping_info,&intent))
|
||
{
|
||
if (image->rendering_intent == UndefinedIntent)
|
||
image->rendering_intent=
|
||
Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG sRGB chunk: rendering_intent: %d",intent);
|
||
}
|
||
}
|
||
|
||
else if (mng_info->have_global_srgb)
|
||
{
|
||
if (image->rendering_intent == UndefinedIntent)
|
||
image->rendering_intent=
|
||
Magick_RenderingIntent_from_PNG_RenderingIntent
|
||
(mng_info->global_srgb_intent);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
{
|
||
if (!png_get_gAMA(ping,ping_info,&file_gamma))
|
||
if (mng_info->have_global_gama)
|
||
png_set_gAMA(ping,ping_info,mng_info->global_gamma);
|
||
|
||
if (png_get_gAMA(ping,ping_info,&file_gamma))
|
||
{
|
||
image->gamma=file_gamma;
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG gAMA chunk: gamma: %f",file_gamma);
|
||
}
|
||
}
|
||
|
||
if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
|
||
{
|
||
if (mng_info->have_global_chrm != MagickFalse)
|
||
{
|
||
(void) png_set_cHRM(ping,ping_info,
|
||
mng_info->global_chrm.white_point.x,
|
||
mng_info->global_chrm.white_point.y,
|
||
mng_info->global_chrm.red_primary.x,
|
||
mng_info->global_chrm.red_primary.y,
|
||
mng_info->global_chrm.green_primary.x,
|
||
mng_info->global_chrm.green_primary.y,
|
||
mng_info->global_chrm.blue_primary.x,
|
||
mng_info->global_chrm.blue_primary.y);
|
||
}
|
||
}
|
||
|
||
if (png_get_valid(ping,ping_info,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);
|
||
|
||
ping_found_cHRM=MagickTrue;
|
||
|
||
if (image->chromaticity.red_primary.x>0.6399f &&
|
||
image->chromaticity.red_primary.x<0.6401f &&
|
||
image->chromaticity.red_primary.y>0.3299f &&
|
||
image->chromaticity.red_primary.y<0.3301f &&
|
||
image->chromaticity.green_primary.x>0.2999f &&
|
||
image->chromaticity.green_primary.x<0.3001f &&
|
||
image->chromaticity.green_primary.y>0.5999f &&
|
||
image->chromaticity.green_primary.y<0.6001f &&
|
||
image->chromaticity.blue_primary.x>0.1499f &&
|
||
image->chromaticity.blue_primary.x<0.1501f &&
|
||
image->chromaticity.blue_primary.y>0.0599f &&
|
||
image->chromaticity.blue_primary.y<0.0601f &&
|
||
image->chromaticity.white_point.x>0.3126f &&
|
||
image->chromaticity.white_point.x<0.3128f &&
|
||
image->chromaticity.white_point.y>0.3289f &&
|
||
image->chromaticity.white_point.y<0.3291f)
|
||
ping_found_sRGB_cHRM=MagickTrue;
|
||
}
|
||
|
||
if (image->rendering_intent != UndefinedIntent)
|
||
{
|
||
if (ping_found_sRGB != MagickTrue &&
|
||
(ping_found_gAMA != MagickTrue ||
|
||
(image->gamma > .45 && image->gamma < .46)) &&
|
||
(ping_found_cHRM != MagickTrue ||
|
||
ping_found_sRGB_cHRM != MagickFalse) &&
|
||
ping_found_iCCP != MagickTrue)
|
||
{
|
||
png_set_sRGB(ping,ping_info,
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent
|
||
(image->rendering_intent));
|
||
file_gamma=0.45455f;
|
||
ping_found_sRGB=MagickTrue;
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting sRGB as if in input");
|
||
}
|
||
}
|
||
|
||
#if defined(PNG_oFFs_SUPPORTED)
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
|
||
{
|
||
image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
|
||
image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
|
||
|
||
if (logging != MagickFalse)
|
||
if (image->page.x || image->page.y)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
|
||
image->page.x,(double) image->page.y);
|
||
}
|
||
#endif
|
||
#if defined(PNG_pHYs_SUPPORTED)
|
||
if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
|
||
{
|
||
if (mng_info->have_global_phys)
|
||
{
|
||
png_set_pHYs(ping,ping_info,
|
||
mng_info->global_x_pixels_per_unit,
|
||
mng_info->global_y_pixels_per_unit,
|
||
mng_info->global_phys_unit_type);
|
||
}
|
||
}
|
||
|
||
x_resolution=0;
|
||
y_resolution=0;
|
||
unit_type=0;
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
|
||
{
|
||
/*
|
||
Set image resolution.
|
||
*/
|
||
(void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
|
||
&unit_type);
|
||
image->resolution.x=(double) x_resolution;
|
||
image->resolution.y=(double) y_resolution;
|
||
|
||
if (unit_type == PNG_RESOLUTION_METER)
|
||
{
|
||
image->units=PixelsPerCentimeterResolution;
|
||
image->resolution.x=(double) x_resolution/100.0;
|
||
image->resolution.y=(double) y_resolution/100.0;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
|
||
(double) x_resolution,(double) y_resolution,unit_type);
|
||
}
|
||
#endif
|
||
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
|
||
{
|
||
png_colorp
|
||
palette;
|
||
|
||
(void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
|
||
|
||
if ((number_colors == 0) &&
|
||
((int) ping_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 (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
|
||
{
|
||
if (mng_info->global_trns_length)
|
||
{
|
||
png_warning(ping,
|
||
"global tRNS has more entries than global PLTE");
|
||
}
|
||
else
|
||
{
|
||
png_set_tRNS(ping,ping_info,mng_info->global_trns,
|
||
(int) mng_info->global_trns_length,NULL);
|
||
}
|
||
}
|
||
#ifdef PNG_READ_bKGD_SUPPORTED
|
||
if (
|
||
#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
|
||
mng_info->have_saved_bkgd_index ||
|
||
#endif
|
||
png_get_valid(ping,ping_info,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;
|
||
#endif
|
||
if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
|
||
background.index=ping_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;
|
||
|
||
background.gray=(png_uint_16)
|
||
mng_info->global_plte[background.index].green;
|
||
|
||
png_set_bKGD(ping,ping_info,&background);
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
png_error(ping,"No global PLTE in file");
|
||
}
|
||
}
|
||
|
||
#ifdef PNG_READ_bKGD_SUPPORTED
|
||
if (mng_info->have_global_bkgd &&
|
||
(!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
|
||
image->background_color=mng_info->mng_global_bkgd;
|
||
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
|
||
{
|
||
unsigned int
|
||
bkgd_scale;
|
||
|
||
/* Set image background color.
|
||
* Scale background components to 16-bit, then scale
|
||
* to quantum depth
|
||
*/
|
||
|
||
bkgd_scale = 1;
|
||
|
||
if (ping_file_depth == 1)
|
||
bkgd_scale = 255;
|
||
|
||
else if (ping_file_depth == 2)
|
||
bkgd_scale = 85;
|
||
|
||
else if (ping_file_depth == 4)
|
||
bkgd_scale = 17;
|
||
|
||
if (ping_file_depth <= 8)
|
||
bkgd_scale *= 257;
|
||
|
||
ping_background->red *= bkgd_scale;
|
||
ping_background->green *= bkgd_scale;
|
||
ping_background->blue *= bkgd_scale;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
|
||
" bkgd_scale=%d. ping_background=(%d,%d,%d)",
|
||
ping_background->red,ping_background->green,
|
||
ping_background->blue,bkgd_scale,ping_background->red,
|
||
ping_background->green,ping_background->blue);
|
||
|
||
image->background_color.red=
|
||
ScaleShortToQuantum(ping_background->red);
|
||
|
||
image->background_color.green=
|
||
ScaleShortToQuantum(ping_background->green);
|
||
|
||
image->background_color.blue=
|
||
ScaleShortToQuantum(ping_background->blue);
|
||
|
||
image->background_color.alpha=OpaqueAlpha;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->background_color=(%.20g,%.20g,%.20g).",
|
||
(double) image->background_color.red,
|
||
(double) image->background_color.green,
|
||
(double) image->background_color.blue);
|
||
}
|
||
#endif /* PNG_READ_bKGD_SUPPORTED */
|
||
|
||
if ((png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
|
||
(ping_trans_color != (png_color_16p) NULL))
|
||
{
|
||
/*
|
||
Image has a tRNS chunk.
|
||
*/
|
||
int
|
||
max_sample;
|
||
|
||
size_t
|
||
one = 1;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG tRNS chunk.");
|
||
|
||
max_sample = (int) ((one << ping_file_depth) - 1);
|
||
|
||
if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
|
||
(int)ping_trans_color->gray > max_sample) ||
|
||
(ping_color_type == PNG_COLOR_TYPE_RGB &&
|
||
((int)ping_trans_color->red > max_sample ||
|
||
(int)ping_trans_color->green > max_sample ||
|
||
(int)ping_trans_color->blue > max_sample)))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Ignoring PNG tRNS chunk with out-of-range sample.");
|
||
png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
|
||
png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
|
||
image->alpha_trait=UndefinedPixelTrait;
|
||
}
|
||
else
|
||
{
|
||
int
|
||
scale_to_short;
|
||
|
||
scale_to_short = 65535L/((1UL << ping_file_depth)-1);
|
||
|
||
/* Scale transparent_color to short */
|
||
transparent_color.red= scale_to_short*ping_trans_color->red;
|
||
transparent_color.green= scale_to_short*ping_trans_color->green;
|
||
transparent_color.blue= scale_to_short*ping_trans_color->blue;
|
||
transparent_color.alpha= scale_to_short*ping_trans_color->gray;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Raw tRNS graylevel = %d, scaled graylevel = %d.",
|
||
(int) ping_trans_color->gray,(int) transparent_color.alpha);
|
||
|
||
}
|
||
transparent_color.red=transparent_color.alpha;
|
||
transparent_color.green=transparent_color.alpha;
|
||
transparent_color.blue=transparent_color.alpha;
|
||
}
|
||
}
|
||
}
|
||
#if defined(PNG_READ_sBIT_SUPPORTED)
|
||
if (mng_info->have_global_sbit)
|
||
{
|
||
if (!png_get_valid(ping,ping_info,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);
|
||
|
||
ping_rowbytes=png_get_rowbytes(ping,ping_info);
|
||
|
||
/*
|
||
Initialize image structure.
|
||
*/
|
||
mng_info->image_box.left=0;
|
||
mng_info->image_box.right=(ssize_t) ping_width;
|
||
mng_info->image_box.top=0;
|
||
mng_info->image_box.bottom=(ssize_t) ping_height;
|
||
if (mng_info->mng_type == 0)
|
||
{
|
||
mng_info->mng_width=ping_width;
|
||
mng_info->mng_height=ping_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_width;
|
||
image->rows=ping_height;
|
||
|
||
if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
|
||
{
|
||
double
|
||
image_gamma = image->gamma;
|
||
|
||
(void)LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->gamma=%f",(float) image_gamma);
|
||
|
||
if (image_gamma > 0.75)
|
||
{
|
||
/* Set image->rendering_intent to Undefined,
|
||
* image->colorspace to GRAY, and reset image->chromaticity.
|
||
*/
|
||
SetImageColorspace(image,LinearGRAYColorspace,exception);
|
||
}
|
||
else
|
||
{
|
||
RenderingIntent
|
||
save_rendering_intent = image->rendering_intent;
|
||
ChromaticityInfo
|
||
save_chromaticity = image->chromaticity;
|
||
|
||
SetImageColorspace(image,GRAYColorspace,exception);
|
||
image->rendering_intent = save_rendering_intent;
|
||
image->chromaticity = save_chromaticity;
|
||
}
|
||
|
||
image->gamma = image_gamma;
|
||
}
|
||
else
|
||
{
|
||
double
|
||
image_gamma = image->gamma;
|
||
|
||
(void)LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->gamma=%f",(float) image_gamma);
|
||
|
||
if (image_gamma > 0.75)
|
||
{
|
||
/* Set image->rendering_intent to Undefined,
|
||
* image->colorspace to GRAY, and reset image->chromaticity.
|
||
*/
|
||
image->intensity = Rec709LuminancePixelIntensityMethod;
|
||
SetImageColorspace(image,RGBColorspace,exception);
|
||
}
|
||
else
|
||
{
|
||
RenderingIntent
|
||
save_rendering_intent = image->rendering_intent;
|
||
ChromaticityInfo
|
||
save_chromaticity = image->chromaticity;
|
||
|
||
SetImageColorspace(image,sRGBColorspace,exception);
|
||
image->rendering_intent = save_rendering_intent;
|
||
image->chromaticity = save_chromaticity;
|
||
}
|
||
|
||
image->gamma = image_gamma;
|
||
}
|
||
|
||
(void)LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->colorspace=%d",(int) image->colorspace);
|
||
|
||
if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
|
||
((int) ping_bit_depth < 16 &&
|
||
(int) ping_color_type == PNG_COLOR_TYPE_GRAY))
|
||
{
|
||
size_t
|
||
one;
|
||
|
||
image->storage_class=PseudoClass;
|
||
one=1;
|
||
image->colors=one << ping_file_depth;
|
||
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
||
if (image->colors > 256)
|
||
image->colors=256;
|
||
#else
|
||
if (image->colors > 65536L)
|
||
image->colors=65536L;
|
||
#endif
|
||
if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
png_colorp
|
||
palette;
|
||
|
||
(void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
|
||
image->colors=(size_t) number_colors;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG PLTE chunk: number_colors: %d.",number_colors);
|
||
}
|
||
}
|
||
|
||
if (image->storage_class == PseudoClass)
|
||
{
|
||
/*
|
||
Initialize image colormap.
|
||
*/
|
||
if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
|
||
png_error(ping,"Memory allocation failed");
|
||
|
||
if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
png_colorp
|
||
palette;
|
||
|
||
(void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
|
||
|
||
for (i=0; i < (ssize_t) number_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);
|
||
}
|
||
|
||
for ( ; i < (ssize_t) image->colors; i++)
|
||
{
|
||
image->colormap[i].red=0;
|
||
image->colormap[i].green=0;
|
||
image->colormap[i].blue=0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Set some properties for reporting by "identify" */
|
||
{
|
||
char
|
||
msg[MagickPathExtent];
|
||
|
||
/* encode ping_width, ping_height, ping_file_depth, ping_color_type,
|
||
ping_interlace_method in value */
|
||
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"%d, %d",(int) ping_width, (int) ping_height);
|
||
(void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
|
||
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
|
||
(void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
|
||
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
|
||
(int) ping_color_type,
|
||
Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
|
||
(void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
|
||
|
||
if (ping_interlace_method == 0)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
|
||
(int) ping_interlace_method);
|
||
}
|
||
else if (ping_interlace_method == 1)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
|
||
(int) ping_interlace_method);
|
||
}
|
||
else
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
|
||
(int) ping_interlace_method);
|
||
}
|
||
(void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
|
||
|
||
if (number_colors != 0)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%d",
|
||
(int) number_colors);
|
||
(void) SetImageProperty(image,"png:PLTE.number_colors",msg,exception);
|
||
}
|
||
}
|
||
#if defined(PNG_tIME_SUPPORTED)
|
||
read_tIME_chunk(image,ping,ping_info,exception);
|
||
#endif
|
||
#if defined(PNG_READ_eXIf_SUPPORTED)
|
||
read_eXIf_chunk(image,ping,ping_info,exception);
|
||
#endif
|
||
|
||
|
||
/*
|
||
Read image scanlines.
|
||
*/
|
||
if (image->delay != 0)
|
||
mng_info->scenes_found++;
|
||
|
||
if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
|
||
(image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
|
||
(image_info->first_scene+image_info->number_scenes))))
|
||
{
|
||
/* This happens later in non-ping decodes */
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
|
||
image->storage_class=DirectClass;
|
||
image->alpha_trait=
|
||
(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
|
||
(png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
|
||
BlendPixelTrait : UndefinedPixelTrait;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Skipping PNG image data for scene %.20g",(double)
|
||
mng_info->scenes_found-1);
|
||
png_destroy_read_struct(&ping,&ping_info,&end_info);
|
||
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit ReadOnePNGImage().");
|
||
|
||
return(image);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG IDAT chunk(s)");
|
||
|
||
status=SetImageExtent(image,image->columns,image->rows,exception);
|
||
if (status != MagickFalse)
|
||
status=ResetImagePixels(image,exception);
|
||
if (status == MagickFalse)
|
||
{
|
||
png_destroy_read_struct(&ping,&ping_info,&end_info);
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
if (num_passes > 1)
|
||
pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
|
||
sizeof(*ping_pixels));
|
||
else
|
||
pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
|
||
|
||
if (pixel_info == (MemoryInfo *) NULL)
|
||
png_error(ping,"Memory allocation failed");
|
||
ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Converting PNG pixels to pixel packets");
|
||
/*
|
||
Convert PNG pixels to pixel packets.
|
||
*/
|
||
quantum_info=AcquireQuantumInfo(image_info,image);
|
||
|
||
if (quantum_info == (QuantumInfo *) NULL)
|
||
png_error(ping,"Failed to allocate quantum_info");
|
||
|
||
(void) SetQuantumEndian(image,quantum_info,MSBEndian);
|
||
|
||
{
|
||
|
||
MagickBooleanType
|
||
found_transparent_pixel;
|
||
|
||
found_transparent_pixel=MagickFalse;
|
||
|
||
if ((image->storage_class == DirectClass) ||
|
||
(image_info->stream != (StreamHandler) NULL))
|
||
{
|
||
for (pass=0; pass < num_passes; pass++)
|
||
{
|
||
/*
|
||
Convert image to DirectClass pixel packets.
|
||
*/
|
||
image->alpha_trait=
|
||
(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
|
||
(png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
|
||
BlendPixelTrait : UndefinedPixelTrait;
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
if (num_passes > 1)
|
||
row_offset=ping_rowbytes*y;
|
||
|
||
else
|
||
row_offset=0;
|
||
|
||
png_read_row(ping,ping_pixels+row_offset,NULL);
|
||
|
||
if (pass < num_passes-1)
|
||
continue;
|
||
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
(void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
|
||
GrayQuantum,ping_pixels+row_offset,exception);
|
||
|
||
else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
(void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
|
||
GrayAlphaQuantum,ping_pixels+row_offset,exception);
|
||
|
||
else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||
(void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
|
||
RGBAQuantum,ping_pixels+row_offset,exception);
|
||
|
||
else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
(void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
|
||
IndexQuantum,ping_pixels+row_offset,exception);
|
||
|
||
else /* ping_color_type == PNG_COLOR_TYPE_RGB */
|
||
(void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
|
||
RGBQuantum,ping_pixels+row_offset,exception);
|
||
|
||
if (found_transparent_pixel == MagickFalse)
|
||
{
|
||
/* Is there a transparent pixel in the row? */
|
||
if (y== 0 && logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Looking for cheap transparent pixel");
|
||
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
|
||
ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
|
||
(GetPixelAlpha(image,q) != OpaqueAlpha))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ...got one.");
|
||
|
||
found_transparent_pixel = MagickTrue;
|
||
break;
|
||
}
|
||
if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
|
||
ping_color_type == PNG_COLOR_TYPE_GRAY) &&
|
||
(ScaleQuantumToShort(GetPixelRed(image,q)) ==
|
||
transparent_color.red &&
|
||
ScaleQuantumToShort(GetPixelGreen(image,q)) ==
|
||
transparent_color.green &&
|
||
ScaleQuantumToShort(GetPixelBlue(image,q)) ==
|
||
transparent_color.blue))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ...got one.");
|
||
found_transparent_pixel = MagickTrue;
|
||
break;
|
||
}
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
}
|
||
|
||
if (num_passes == 1)
|
||
{
|
||
status=SetImageProgress(image,LoadImageTag,
|
||
(MagickOffsetType) y, image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
if (y < (long) image->rows)
|
||
break;
|
||
|
||
if (num_passes != 1)
|
||
{
|
||
status=SetImageProgress(image,LoadImageTag,pass,num_passes);
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
else /* image->storage_class != DirectClass */
|
||
|
||
for (pass=0; pass < num_passes; pass++)
|
||
{
|
||
Quantum
|
||
*r;
|
||
|
||
/*
|
||
Convert grayscale image to PseudoClass pixel packets.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Converting grayscale pixels to pixel packets");
|
||
|
||
image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
|
||
BlendPixelTrait : UndefinedPixelTrait;
|
||
|
||
quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
|
||
(image->alpha_trait == BlendPixelTrait? 2 : 1)*
|
||
sizeof(*quantum_scanline));
|
||
|
||
if (quantum_scanline == (Quantum *) NULL)
|
||
png_error(ping,"Memory allocation failed");
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
Quantum
|
||
alpha;
|
||
|
||
if (num_passes > 1)
|
||
row_offset=ping_rowbytes*y;
|
||
|
||
else
|
||
row_offset=0;
|
||
|
||
png_read_row(ping,ping_pixels+row_offset,NULL);
|
||
|
||
if (pass < num_passes-1)
|
||
continue;
|
||
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
p=ping_pixels+row_offset;
|
||
r=quantum_scanline;
|
||
|
||
switch (ping_bit_depth)
|
||
{
|
||
case 8:
|
||
{
|
||
|
||
if (ping_color_type == 4)
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
*r++=*p++;
|
||
|
||
alpha=ScaleCharToQuantum((unsigned char)*p++);
|
||
|
||
SetPixelAlpha(image,alpha,q);
|
||
|
||
if (alpha != OpaqueAlpha)
|
||
found_transparent_pixel = MagickTrue;
|
||
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
else
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
*r++=*p++;
|
||
|
||
break;
|
||
}
|
||
|
||
case 16:
|
||
{
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
|
||
unsigned long
|
||
quantum;
|
||
|
||
if (image->colors > 256)
|
||
quantum=(((unsigned int) *p++) << 8);
|
||
|
||
else
|
||
quantum=0;
|
||
|
||
quantum|=(*p++);
|
||
*r=ScaleShortToQuantum(quantum);
|
||
r++;
|
||
|
||
if (ping_color_type == 4)
|
||
{
|
||
if (image->colors > 256)
|
||
quantum=(((unsigned int) *p++) << 8);
|
||
else
|
||
quantum=0;
|
||
|
||
quantum|=(*p++);
|
||
|
||
alpha=ScaleShortToQuantum(quantum);
|
||
SetPixelAlpha(image,alpha,q);
|
||
|
||
if (alpha != OpaqueAlpha)
|
||
found_transparent_pixel = MagickTrue;
|
||
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
|
||
*r++=(*p++);
|
||
p++; /* strip low byte */
|
||
|
||
if (ping_color_type == 4)
|
||
{
|
||
SetPixelAlpha(image,*p++,q);
|
||
|
||
if (GetPixelAlpha(image,q) != OpaqueAlpha)
|
||
found_transparent_pixel = MagickTrue;
|
||
|
||
p++;
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
|
||
/*
|
||
Transfer image scanline.
|
||
*/
|
||
r=quantum_scanline;
|
||
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
|
||
SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
|
||
SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
|
||
SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
|
||
SetPixelIndex(image,index,q);
|
||
r++;
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
|
||
if (num_passes == 1)
|
||
{
|
||
status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
|
||
image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
|
||
if (y < (long) image->rows)
|
||
break;
|
||
if (num_passes != 1)
|
||
{
|
||
status=SetImageProgress(image,LoadImageTag,pass,num_passes);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
|
||
UndefinedPixelTrait;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (found_transparent_pixel != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Found transparent pixel");
|
||
else
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" No transparent pixel was found");
|
||
|
||
ping_color_type&=0x03;
|
||
}
|
||
}
|
||
}
|
||
quantum_info=DestroyQuantumInfo(quantum_info);
|
||
|
||
png_read_end(ping,end_info);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->storage_class=%d\n",(int) image->storage_class);
|
||
}
|
||
|
||
if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
|
||
(ssize_t) image_info->first_scene && image->delay != 0)
|
||
{
|
||
png_destroy_read_struct(&ping,&ping_info,&end_info);
|
||
pixel_info=RelinquishVirtualMemory(pixel_info);
|
||
if (AcquireImageColormap(image,2,exception) == MagickFalse)
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
(void) SetImageBackgroundColor(image,exception);
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit ReadOnePNGImage() early.");
|
||
return(image);
|
||
}
|
||
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
|
||
{
|
||
ClassType
|
||
storage_class;
|
||
|
||
/*
|
||
Image has a transparent background.
|
||
*/
|
||
storage_class=image->storage_class;
|
||
image->alpha_trait=BlendPixelTrait;
|
||
|
||
/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
|
||
|
||
if (storage_class == PseudoClass)
|
||
{
|
||
if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
for (x=0; x < ping_num_trans; x++)
|
||
{
|
||
image->colormap[x].alpha_trait=BlendPixelTrait;
|
||
image->colormap[x].alpha=OpaqueAlpha;
|
||
if (ping_trans_alpha != (png_bytep) NULL)
|
||
image->colormap[x].alpha=ScaleCharToQuantum(
|
||
(unsigned char) ping_trans_alpha[x]);
|
||
}
|
||
}
|
||
|
||
else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
for (x=0; x < (int) image->colors; x++)
|
||
{
|
||
if (ScaleQuantumToShort(image->colormap[x].red) ==
|
||
transparent_color.alpha)
|
||
{
|
||
image->colormap[x].alpha_trait=BlendPixelTrait;
|
||
image->colormap[x].alpha = (Quantum) TransparentAlpha;
|
||
}
|
||
}
|
||
}
|
||
(void) SyncImage(image,exception);
|
||
}
|
||
|
||
#if 1 /* Should have already been done above, but glennrp problem P10
|
||
* needs this.
|
||
*/
|
||
else
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
image->storage_class=storage_class;
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
|
||
/* Caution: on a Q8 build, this does not distinguish between
|
||
* 16-bit colors that differ only in the low byte
|
||
*/
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
|
||
transparent_color.red &&
|
||
ScaleQuantumToShort(GetPixelGreen(image,q)) ==
|
||
transparent_color.green &&
|
||
ScaleQuantumToShort(GetPixelBlue(image,q)) ==
|
||
transparent_color.blue)
|
||
{
|
||
SetPixelAlpha(image,TransparentAlpha,q);
|
||
}
|
||
else
|
||
{
|
||
SetPixelAlpha(image,OpaqueAlpha,q);
|
||
}
|
||
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
image->storage_class=DirectClass;
|
||
}
|
||
|
||
for (j = 0; j < 2; j++)
|
||
{
|
||
if (j == 0)
|
||
status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
|
||
MagickTrue : MagickFalse;
|
||
else
|
||
status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
|
||
MagickTrue : MagickFalse;
|
||
|
||
if (status != MagickFalse)
|
||
for (i=0; i < (ssize_t) num_text; i++)
|
||
{
|
||
/* Check for a profile */
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading PNG text chunk");
|
||
|
||
if (strlen(text[i].key) > 16 &&
|
||
memcmp(text[i].key, "Raw profile type ",17) == 0)
|
||
{
|
||
const char
|
||
*value;
|
||
|
||
value=GetImageOption(image_info,"profile:skip");
|
||
|
||
if (IsOptionMember(text[i].key+17,value) == MagickFalse)
|
||
{
|
||
(void) Magick_png_read_raw_profile(ping,image,image_info,text,
|
||
(int) i,exception);
|
||
num_raw_profiles++;
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Read raw profile %s",text[i].key+17);
|
||
}
|
||
else
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Skipping raw profile %s",text[i].key+17);
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
char
|
||
*value;
|
||
|
||
length=text[i].text_length;
|
||
value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
|
||
sizeof(*value));
|
||
if (value == (char *) NULL)
|
||
{
|
||
png_error(ping,"Memory allocation failed");
|
||
break;
|
||
}
|
||
*value='\0';
|
||
(void) ConcatenateMagickString(value,text[i].text,length+2);
|
||
|
||
/* Don't save "density" or "units" property if we have a pHYs
|
||
* chunk
|
||
*/
|
||
if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
|
||
(LocaleCompare(text[i].key,"density") != 0 &&
|
||
LocaleCompare(text[i].key,"units") != 0))
|
||
{
|
||
char
|
||
key[MagickPathExtent];
|
||
|
||
(void) FormatLocaleString(key,MagickPathExtent,"%s",
|
||
text[i].key);
|
||
if ((LocaleCompare(key,"version") == 0) ||
|
||
(LocaleCompare(key,"width") == 0))
|
||
(void) FormatLocaleString(key,MagickPathExtent,"png:%s",
|
||
text[i].key);
|
||
(void) SetImageProperty(image,key,value,exception);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" length: %lu\n"
|
||
" Keyword: %s",
|
||
(unsigned long) length,
|
||
text[i].key);
|
||
}
|
||
|
||
value=DestroyString(value);
|
||
}
|
||
}
|
||
num_text_total += num_text;
|
||
}
|
||
|
||
#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]=(MngBuffer *)
|
||
AcquireMagickMemory(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)
|
||
png_error(ping,"Memory allocation failed");
|
||
|
||
if (mng_info->ob[object_id]->frozen)
|
||
png_error(ping,"Cannot overwrite frozen MNG object buffer");
|
||
}
|
||
|
||
else
|
||
{
|
||
|
||
if (mng_info->ob[object_id]->image != (Image *) NULL)
|
||
mng_info->ob[object_id]->image=DestroyImage
|
||
(mng_info->ob[object_id]->image);
|
||
|
||
mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
|
||
exception);
|
||
|
||
if (mng_info->ob[object_id]->image != (Image *) NULL)
|
||
mng_info->ob[object_id]->image->file=(FILE *) NULL;
|
||
|
||
else
|
||
png_error(ping, "Cloning image for object buffer failed");
|
||
|
||
if (ping_width > 250000L || ping_height > 250000L)
|
||
png_error(ping,"PNG Image dimensions are too large.");
|
||
|
||
mng_info->ob[object_id]->width=ping_width;
|
||
mng_info->ob[object_id]->height=ping_height;
|
||
mng_info->ob[object_id]->color_type=ping_color_type;
|
||
mng_info->ob[object_id]->sample_depth=ping_bit_depth;
|
||
mng_info->ob[object_id]->interlace_method=ping_interlace_method;
|
||
mng_info->ob[object_id]->compression_method=
|
||
ping_compression_method;
|
||
mng_info->ob[object_id]->filter_method=ping_filter_method;
|
||
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
|
||
{
|
||
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
|
||
|
||
/* Set image->alpha_trait to MagickTrue if the input colortype supports
|
||
* alpha or if a valid tRNS chunk is present, no matter whether there
|
||
* is actual transparency present.
|
||
*/
|
||
image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
|
||
(png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
|
||
BlendPixelTrait : UndefinedPixelTrait;
|
||
if (image->alpha_trait == BlendPixelTrait)
|
||
(void) SetImageStorageClass(image,DirectClass,exception);
|
||
|
||
#if 0 /* I'm not sure what's wrong here but it does not work. */
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
{
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
(void) SetImageType(image,GrayscaleAlphaType,exception);
|
||
|
||
else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
(void) SetImageType(image,PaletteAlphaType,exception);
|
||
|
||
else
|
||
(void) SetImageType(image,TrueColorAlphaType,exception);
|
||
}
|
||
|
||
else
|
||
{
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
(void) SetImageType(image,GrayscaleType,exception);
|
||
|
||
else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
(void) SetImageType(image,PaletteType,exception);
|
||
|
||
else
|
||
(void) SetImageType(image,TrueColorType,exception);
|
||
}
|
||
#endif
|
||
|
||
/* Set more properties for identify to retrieve */
|
||
{
|
||
char
|
||
msg[MagickPathExtent];
|
||
|
||
if (num_text_total != 0)
|
||
{
|
||
/* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"%d tEXt/zTXt/iTXt chunks were found", num_text_total);
|
||
(void) SetImageProperty(image,"png:text",msg,exception);
|
||
}
|
||
|
||
if (num_raw_profiles != 0)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"%d were found", num_raw_profiles);
|
||
(void) SetImageProperty(image,"png:text-encoded profiles",msg,
|
||
exception);
|
||
}
|
||
|
||
/* cHRM chunk: */
|
||
if (ping_found_cHRM != MagickFalse)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%s",
|
||
"chunk was found (see Chromaticity, above)");
|
||
(void) SetImageProperty(image,"png:cHRM",msg,exception);
|
||
}
|
||
|
||
/* bKGD chunk: */
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%s",
|
||
"chunk was found (see Background color, above)");
|
||
(void) SetImageProperty(image,"png:bKGD",msg,exception);
|
||
}
|
||
|
||
(void) FormatLocaleString(msg,MagickPathExtent,"%s",
|
||
"chunk was found");
|
||
|
||
#if defined(PNG_iCCP_SUPPORTED)
|
||
/* iCCP chunk: */
|
||
if (ping_found_iCCP != MagickFalse)
|
||
(void) SetImageProperty(image,"png:iCCP",msg,exception);
|
||
#endif
|
||
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
|
||
(void) SetImageProperty(image,"png:tRNS",msg,exception);
|
||
|
||
#if defined(PNG_sRGB_SUPPORTED)
|
||
/* sRGB chunk: */
|
||
if (ping_found_sRGB != MagickFalse)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"intent=%d (%s)",
|
||
(int) intent,
|
||
Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
|
||
(void) SetImageProperty(image,"png:sRGB",msg,exception);
|
||
}
|
||
#endif
|
||
|
||
/* gAMA chunk: */
|
||
if (ping_found_gAMA != MagickFalse)
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"gamma=%.8g (See Gamma, above)",
|
||
file_gamma);
|
||
(void) SetImageProperty(image,"png:gAMA",msg,exception);
|
||
}
|
||
|
||
#if defined(PNG_pHYs_SUPPORTED)
|
||
/* pHYs chunk: */
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"x_res=%.10g, y_res=%.10g, units=%d",
|
||
(double) x_resolution,(double) y_resolution, unit_type);
|
||
(void) SetImageProperty(image,"png:pHYs",msg,exception);
|
||
}
|
||
#endif
|
||
|
||
#if defined(PNG_oFFs_SUPPORTED)
|
||
/* oFFs chunk: */
|
||
if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"x_off=%.20g, y_off=%.20g",
|
||
(double) image->page.x,(double) image->page.y);
|
||
(void) SetImageProperty(image,"png:oFFs",msg,exception);
|
||
}
|
||
#endif
|
||
|
||
#if defined(PNG_tIME_SUPPORTED)
|
||
read_tIME_chunk(image,ping,end_info,exception);
|
||
#endif
|
||
#if defined(PNG_READ_eXIf_SUPPORTED)
|
||
read_eXIf_chunk(image,ping,end_info,exception);
|
||
#endif
|
||
|
||
/* caNv chunk: */
|
||
if ((image->page.width != 0 && image->page.width != image->columns) ||
|
||
(image->page.height != 0 && image->page.height != image->rows) ||
|
||
(image->page.x != 0 || image->page.y != 0))
|
||
{
|
||
(void) FormatLocaleString(msg,MagickPathExtent,
|
||
"width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
|
||
(double) image->page.width,(double) image->page.height,
|
||
(double) image->page.x,(double) image->page.y);
|
||
(void) SetImageProperty(image,"png:caNv",msg,exception);
|
||
}
|
||
}
|
||
|
||
/*
|
||
Relinquish resources.
|
||
*/
|
||
png_destroy_read_struct(&ping,&ping_info,&end_info);
|
||
|
||
pixel_info=RelinquishVirtualMemory(pixel_info);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit ReadOnePNGImage()");
|
||
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
|
||
/* } for navigation to beginning of SETJMP-protected block, revert to
|
||
* Throwing an Exception when an error occurs.
|
||
*/
|
||
|
||
return(image);
|
||
|
||
/* end of reading one PNG image */
|
||
}
|
||
|
||
static Image *ReadPNGImage(const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
Image
|
||
*image;
|
||
|
||
MagickBooleanType
|
||
logging,
|
||
status;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
char
|
||
magic_number[MagickPathExtent];
|
||
|
||
ssize_t
|
||
count;
|
||
|
||
/*
|
||
Open image file.
|
||
*/
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
|
||
if (image_info->debug != MagickFalse)
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
|
||
image_info->filename);
|
||
|
||
assert(exception != (ExceptionInfo *) NULL);
|
||
assert(exception->signature == MagickCoreSignature);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
|
||
image=AcquireImage(image_info,exception);
|
||
mng_info=(MngInfo *) NULL;
|
||
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
|
||
|
||
if (status == MagickFalse)
|
||
return(DestroyImageList(image));
|
||
|
||
/*
|
||
Verify PNG signature.
|
||
*/
|
||
count=ReadBlob(image,8,(unsigned char *) magic_number);
|
||
|
||
if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
|
||
/*
|
||
Verify that file size large enough to contain a PNG datastream.
|
||
*/
|
||
if (GetBlobSize(image) < 61)
|
||
ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
|
||
|
||
/*
|
||
Allocate a MngInfo structure.
|
||
*/
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
|
||
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
/*
|
||
Initialize members of the MngInfo structure.
|
||
*/
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
mng_info->image=image;
|
||
|
||
image=ReadOnePNGImage(mng_info,image_info,exception);
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
|
||
if (image == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadPNGImage() with error");
|
||
|
||
return((Image *) NULL);
|
||
}
|
||
|
||
(void) CloseBlob(image);
|
||
|
||
if ((image->columns == 0) || (image->rows == 0))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadPNGImage() with error.");
|
||
|
||
ThrowReaderException(CorruptImageError,"CorruptImage");
|
||
}
|
||
|
||
if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
|
||
(image->gamma > .75) &&
|
||
!(image->chromaticity.red_primary.x>0.6399f &&
|
||
image->chromaticity.red_primary.x<0.6401f &&
|
||
image->chromaticity.red_primary.y>0.3299f &&
|
||
image->chromaticity.red_primary.y<0.3301f &&
|
||
image->chromaticity.green_primary.x>0.2999f &&
|
||
image->chromaticity.green_primary.x<0.3001f &&
|
||
image->chromaticity.green_primary.y>0.5999f &&
|
||
image->chromaticity.green_primary.y<0.6001f &&
|
||
image->chromaticity.blue_primary.x>0.1499f &&
|
||
image->chromaticity.blue_primary.x<0.1501f &&
|
||
image->chromaticity.blue_primary.y>0.0599f &&
|
||
image->chromaticity.blue_primary.y<0.0601f &&
|
||
image->chromaticity.white_point.x>0.3126f &&
|
||
image->chromaticity.white_point.x<0.3128f &&
|
||
image->chromaticity.white_point.y>0.3289f &&
|
||
image->chromaticity.white_point.y<0.3291f))
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"SetImageColorspace to RGBColorspace");
|
||
SetImageColorspace(image,RGBColorspace,exception);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
|
||
(double) image->page.width,(double) image->page.height,
|
||
(double) image->page.x,(double) image->page.y);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->colorspace: %d", (int) image->colorspace);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(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 %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% 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, glennrp@image...
|
||
%
|
||
% 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 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 void
|
||
DestroyJNG(unsigned char *chunk,Image **color_image,
|
||
ImageInfo **color_image_info,Image **alpha_image,
|
||
ImageInfo **alpha_image_info)
|
||
{
|
||
(void) RelinquishMagickMemory(chunk);
|
||
if (color_image_info && *color_image_info)
|
||
{
|
||
DestroyImageInfo(*color_image_info);
|
||
*color_image_info = (ImageInfo *)NULL;
|
||
}
|
||
if (alpha_image_info && *alpha_image_info)
|
||
{
|
||
DestroyImageInfo(*alpha_image_info);
|
||
*alpha_image_info = (ImageInfo *)NULL;
|
||
}
|
||
if (color_image && *color_image)
|
||
{
|
||
DestroyImageList(*color_image);
|
||
*color_image = (Image *)NULL;
|
||
}
|
||
if (alpha_image && *alpha_image)
|
||
{
|
||
DestroyImageList(*alpha_image);
|
||
*alpha_image = (Image *)NULL;
|
||
}
|
||
}
|
||
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;
|
||
|
||
MagickBooleanType
|
||
logging;
|
||
|
||
ssize_t
|
||
y;
|
||
|
||
MagickBooleanType
|
||
status;
|
||
|
||
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;
|
||
|
||
const Quantum
|
||
*s;
|
||
|
||
ssize_t
|
||
i,
|
||
x;
|
||
|
||
Quantum
|
||
*q;
|
||
|
||
unsigned char
|
||
*p;
|
||
|
||
unsigned int
|
||
read_JSEP,
|
||
reading_idat;
|
||
|
||
size_t
|
||
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 (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/*
|
||
Allocate next image structure.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" AcquireNextImage()");
|
||
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
mng_info->image=image;
|
||
|
||
/*
|
||
Signature bytes have already been read.
|
||
*/
|
||
|
||
read_JSEP=MagickFalse;
|
||
reading_idat=MagickFalse;
|
||
for (;;)
|
||
{
|
||
char
|
||
type[MagickPathExtent];
|
||
|
||
unsigned char
|
||
*chunk;
|
||
|
||
unsigned int
|
||
count;
|
||
|
||
/*
|
||
Read a new JNG chunk.
|
||
*/
|
||
status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
|
||
2*GetBlobSize(image));
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
|
||
type[0]='\0';
|
||
(void) ConcatenateMagickString(type,"errr",MagickPathExtent);
|
||
length=(size_t) ReadBlobMSBLong(image);
|
||
count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading JNG chunk type %c%c%c%c, length: %.20g",
|
||
type[0],type[1],type[2],type[3],(double) length);
|
||
|
||
if (length > PNG_UINT_31_MAX || count == 0)
|
||
{
|
||
DestroyJNG(NULL,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
if (length > GetBlobSize(image))
|
||
{
|
||
DestroyJNG(NULL,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(CorruptImageError,
|
||
"InsufficientImageDataInFile");
|
||
}
|
||
|
||
p=NULL;
|
||
chunk=(unsigned char *) NULL;
|
||
|
||
if (length != 0)
|
||
{
|
||
chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
|
||
|
||
if (chunk == (unsigned char *) NULL)
|
||
{
|
||
DestroyJNG(NULL,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
|
||
for (i=0; i < (ssize_t) length; i++)
|
||
{
|
||
int
|
||
c;
|
||
|
||
c=ReadBlobByte(image);
|
||
if (c == EOF)
|
||
break;
|
||
chunk[i]=(unsigned char) c;
|
||
}
|
||
for ( ; i < (ssize_t) length; i++)
|
||
chunk[i]='\0';
|
||
|
||
p=chunk;
|
||
}
|
||
|
||
(void) ReadBlobMSBLong(image); /* read crc word */
|
||
|
||
if (memcmp(type,mng_JHDR,4) == 0)
|
||
{
|
||
if (length == 16)
|
||
{
|
||
jng_width=(png_uint_32)mng_get_long(p);
|
||
jng_height=(png_uint_32)mng_get_long(&p[4]);
|
||
if ((jng_width == 0) || (jng_height == 0))
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(CorruptImageError,
|
||
"NegativeOrZeroImageSize");
|
||
}
|
||
jng_color_type=p[8];
|
||
jng_image_sample_depth=p[9];
|
||
jng_image_compression_method=p[10];
|
||
jng_image_interlace_method=p[11];
|
||
|
||
image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
|
||
NoInterlace;
|
||
|
||
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 != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" jng_width: %16lu, jng_height: %16lu\n"
|
||
" jng_color_type: %16d, jng_image_sample_depth: %3d\n"
|
||
" jng_image_compression_method:%3d",
|
||
(unsigned long) jng_width, (unsigned long) jng_height,
|
||
jng_color_type, jng_image_sample_depth,
|
||
jng_image_compression_method);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" jng_image_interlace_method: %3d"
|
||
" jng_alpha_sample_depth: %3d",
|
||
jng_image_interlace_method,
|
||
jng_alpha_sample_depth);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" jng_alpha_compression_method:%3d\n"
|
||
" jng_alpha_filter_method: %3d\n"
|
||
" jng_alpha_interlace_method: %3d",
|
||
jng_alpha_compression_method,
|
||
jng_alpha_filter_method,
|
||
jng_alpha_interlace_method);
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
if ((jng_width > 65535) || (jng_height > 65535) ||
|
||
(MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
|
||
(MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG width or height too large: (%lu x %lu)",
|
||
(long) jng_width, (long) jng_height);
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
|
||
((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
|
||
(memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
|
||
{
|
||
/*
|
||
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=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
|
||
|
||
if (color_image_info == (ImageInfo *) NULL)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
|
||
GetImageInfo(color_image_info);
|
||
color_image=AcquireImage(color_image_info,exception);
|
||
|
||
if (color_image == (Image *) NULL)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating color_blob.");
|
||
|
||
(void) AcquireUniqueFilename(color_image->filename);
|
||
status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
|
||
exception);
|
||
|
||
if (status == MagickFalse)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
|
||
{
|
||
if ((jng_alpha_compression_method != 0) &&
|
||
(jng_alpha_compression_method != 8))
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,&alpha_image,
|
||
&alpha_image_info);
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
alpha_image_info=(ImageInfo *)
|
||
AcquireMagickMemory(sizeof(ImageInfo));
|
||
|
||
if (alpha_image_info == (ImageInfo *) NULL)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(ResourceLimitError,
|
||
"MemoryAllocationFailed");
|
||
}
|
||
|
||
GetImageInfo(alpha_image_info);
|
||
alpha_image=AcquireImage(alpha_image_info,exception);
|
||
|
||
if (alpha_image == (Image *) NULL)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
ThrowReaderException(ResourceLimitError,
|
||
"MemoryAllocationFailed");
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating alpha_blob.");
|
||
|
||
(void) AcquireUniqueFilename(alpha_image->filename);
|
||
status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
|
||
exception);
|
||
|
||
if (status == MagickFalse)
|
||
{
|
||
DestroyJNG(chunk,&color_image,&color_image_info,
|
||
&alpha_image,&alpha_image_info);
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
if (jng_alpha_compression_method == 0)
|
||
{
|
||
unsigned char
|
||
data[18];
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing IHDR chunk to alpha_blob.");
|
||
|
||
(void) WriteBlob(alpha_image,8,(const unsigned char *)
|
||
"\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=MagickTrue;
|
||
}
|
||
|
||
if (memcmp(type,mng_JDAT,4) == 0)
|
||
{
|
||
/* Copy chunk to color_image->blob */
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Copying JDAT chunk data to color_blob.");
|
||
|
||
if ((length != 0) && (color_image != (Image *) NULL))
|
||
(void) WriteBlob(color_image,length,chunk);
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_IDAT,4) == 0)
|
||
{
|
||
png_byte
|
||
data[5];
|
||
|
||
/* Copy IDAT header and chunk data to alpha_image->blob */
|
||
|
||
if (alpha_image != NULL && image_info->ping == MagickFalse)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Copying IDAT chunk data to alpha_blob.");
|
||
|
||
(void) WriteBlobMSBULong(alpha_image,(size_t) length);
|
||
PNGType(data,mng_IDAT);
|
||
LogPNGChunk(logging,mng_IDAT,length);
|
||
(void) WriteBlob(alpha_image,4,data);
|
||
(void) WriteBlob(alpha_image,length,chunk);
|
||
(void) WriteBlobMSBULong(alpha_image,
|
||
crc32(crc32(0,data,4),chunk,(uInt) length));
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
|
||
{
|
||
/* Copy chunk data to alpha_image->blob */
|
||
|
||
if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
|
||
(length != 0))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Copying JDAA chunk data to alpha_blob.");
|
||
|
||
(void) WriteBlob(alpha_image,length,chunk);
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_JSEP,4) == 0)
|
||
{
|
||
read_JSEP=MagickTrue;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_bKGD,4) == 0)
|
||
{
|
||
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]);
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_gAMA,4) == 0)
|
||
{
|
||
if (length == 4)
|
||
image->gamma=((float) mng_get_long(p))*0.00001;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_cHRM,4) == 0)
|
||
{
|
||
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]);
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_sRGB,4) == 0)
|
||
{
|
||
if (length == 1)
|
||
{
|
||
image->rendering_intent=
|
||
Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
|
||
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;
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_oFFs,4) == 0)
|
||
{
|
||
if (length > 8)
|
||
{
|
||
image->page.x=(ssize_t) mng_get_long(p);
|
||
image->page.y=(ssize_t) mng_get_long(&p[4]);
|
||
|
||
if ((int) p[8] != 0)
|
||
{
|
||
image->page.x/=10000;
|
||
image->page.y/=10000;
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_pHYs,4) == 0)
|
||
{
|
||
if (length > 8)
|
||
{
|
||
image->resolution.x=(double) mng_get_long(p);
|
||
image->resolution.y=(double) mng_get_long(&p[4]);
|
||
if ((int) p[8] == PNG_RESOLUTION_METER)
|
||
{
|
||
image->units=PixelsPerCentimeterResolution;
|
||
image->resolution.x=image->resolution.x/100.0f;
|
||
image->resolution.y=image->resolution.y/100.0f;
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
#if 0
|
||
if (memcmp(type,mng_iCCP,4) == 0)
|
||
{
|
||
/* To do: */
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
#endif
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(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
|
||
alpha samples of main image.
|
||
|
||
o destroy the secondary image.
|
||
*/
|
||
|
||
if (color_image_info == (ImageInfo *) NULL)
|
||
{
|
||
assert(color_image == (Image *) NULL);
|
||
assert(alpha_image == (Image *) NULL);
|
||
if (color_image != (Image *) NULL)
|
||
color_image=DestroyImageList(color_image);
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
if (color_image == (Image *) NULL)
|
||
{
|
||
assert(alpha_image == (Image *) NULL);
|
||
ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
|
||
}
|
||
|
||
(void) SeekBlob(color_image,0,SEEK_SET);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading jng_image from color_blob.");
|
||
|
||
assert(color_image_info != (ImageInfo *) NULL);
|
||
(void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
|
||
"jpeg:%s",color_image->filename);
|
||
|
||
color_image_info->ping=MagickFalse; /* To do: avoid this */
|
||
jng_image=ReadImage(color_image_info,exception);
|
||
|
||
(void) RelinquishUniqueFileResource(color_image->filename);
|
||
color_image=DestroyImageList(color_image);
|
||
color_image_info=DestroyImageInfo(color_image_info);
|
||
|
||
if (jng_image == (Image *) NULL)
|
||
{
|
||
DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Copying jng_image pixels to main image.");
|
||
|
||
image->rows=jng_height;
|
||
image->columns=jng_width;
|
||
|
||
status=SetImageExtent(image,image->columns,image->rows,exception);
|
||
if (status == MagickFalse)
|
||
{
|
||
DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
|
||
&alpha_image_info);
|
||
jng_image=DestroyImageList(jng_image);
|
||
return(DestroyImageList(image));
|
||
}
|
||
if ((image->columns != jng_image->columns) ||
|
||
(image->rows != jng_image->rows))
|
||
{
|
||
DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
|
||
&alpha_image_info);
|
||
jng_image=DestroyImageList(jng_image);
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
|
||
break;
|
||
for (x=(ssize_t) image->columns; x != 0; x--)
|
||
{
|
||
SetPixelRed(image,GetPixelRed(jng_image,s),q);
|
||
SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
|
||
SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
|
||
q+=GetPixelChannels(image);
|
||
s+=GetPixelChannels(jng_image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
|
||
jng_image=DestroyImage(jng_image);
|
||
|
||
if ((image_info->ping == MagickFalse) && (alpha_image != (Image *) NULL) &&
|
||
(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,data);
|
||
(void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
|
||
}
|
||
|
||
(void) CloseBlob(alpha_image);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading alpha from alpha_blob.");
|
||
|
||
(void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
|
||
"%s",alpha_image->filename);
|
||
|
||
jng_image=ReadImage(alpha_image_info,exception);
|
||
|
||
if (jng_image != (Image *) NULL)
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
|
||
break;
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
for (x=(ssize_t) image->columns; x != 0; x--)
|
||
{
|
||
SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
|
||
q+=GetPixelChannels(image);
|
||
s+=GetPixelChannels(jng_image);
|
||
}
|
||
|
||
else
|
||
for (x=(ssize_t) image->columns; x != 0; x--)
|
||
{
|
||
SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
|
||
if (GetPixelAlpha(image,q) != OpaqueAlpha)
|
||
image->alpha_trait=BlendPixelTrait;
|
||
q+=GetPixelChannels(image);
|
||
s+=GetPixelChannels(jng_image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
(void) RelinquishUniqueFileResource(alpha_image->filename);
|
||
alpha_image=DestroyImageList(alpha_image);
|
||
alpha_image_info=DestroyImageInfo(alpha_image_info);
|
||
if (jng_image != (Image *) NULL)
|
||
jng_image=DestroyImageList(jng_image);
|
||
}
|
||
|
||
/* Read the JNG image. */
|
||
|
||
if (mng_info->mng_type == 0)
|
||
{
|
||
mng_info->mng_width=jng_width;
|
||
mng_info->mng_height=jng_height;
|
||
}
|
||
|
||
if (image->page.width == 0 && image->page.height == 0)
|
||
{
|
||
image->page.width=jng_width;
|
||
image->page.height=jng_height;
|
||
}
|
||
|
||
if (image->page.x == 0 && image->page.y == 0)
|
||
{
|
||
image->page.x=mng_info->x_off[mng_info->object_id];
|
||
image->page.y=mng_info->y_off[mng_info->object_id];
|
||
}
|
||
|
||
else
|
||
{
|
||
image->page.y=mng_info->y_off[mng_info->object_id];
|
||
}
|
||
|
||
mng_info->image_found++;
|
||
status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
|
||
2*GetBlobSize(image));
|
||
|
||
if (status == MagickFalse)
|
||
return(DestroyImageList(image));
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit ReadOneJNGImage()");
|
||
|
||
return(image);
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% R e a d J N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% 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, glennrp@image...
|
||
%
|
||
% The format of the ReadJNGImage method is:
|
||
%
|
||
% Image *ReadJNGImage(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.
|
||
%
|
||
*/
|
||
|
||
static Image *ReadJNGImage(const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
Image
|
||
*image;
|
||
|
||
MagickBooleanType
|
||
logging,
|
||
status;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
char
|
||
magic_number[MagickPathExtent];
|
||
|
||
size_t
|
||
count;
|
||
|
||
/*
|
||
Open image file.
|
||
*/
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
|
||
image_info->filename);
|
||
assert(exception != (ExceptionInfo *) NULL);
|
||
assert(exception->signature == MagickCoreSignature);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
|
||
image=AcquireImage(image_info,exception);
|
||
mng_info=(MngInfo *) NULL;
|
||
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
|
||
|
||
if (status == MagickFalse)
|
||
return(DestroyImageList(image));
|
||
|
||
if (LocaleCompare(image_info->magick,"JNG") != 0)
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
|
||
/* Verify JNG signature. */
|
||
|
||
count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
|
||
|
||
if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
|
||
/*
|
||
Verify that file size large enough to contain a JNG datastream.
|
||
*/
|
||
if (GetBlobSize(image) < 147)
|
||
ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
|
||
|
||
/* Allocate a MngInfo structure. */
|
||
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
|
||
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
/* Initialize members of the MngInfo structure. */
|
||
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
|
||
mng_info->image=image;
|
||
image=ReadOneJNGImage(mng_info,image_info,exception);
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
|
||
if (image == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadJNGImage() with error");
|
||
|
||
return((Image *) NULL);
|
||
}
|
||
(void) CloseBlob(image);
|
||
|
||
if (image->columns == 0 || image->rows == 0)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadJNGImage() with error");
|
||
|
||
ThrowReaderException(CorruptImageError,"CorruptImage");
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
|
||
|
||
return(image);
|
||
}
|
||
#endif
|
||
|
||
static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
char
|
||
page_geometry[MagickPathExtent];
|
||
|
||
Image
|
||
*image;
|
||
|
||
MagickBooleanType
|
||
logging;
|
||
|
||
volatile int
|
||
first_mng_object,
|
||
object_id,
|
||
term_chunk_found,
|
||
skip_to_iend;
|
||
|
||
volatile ssize_t
|
||
image_count=0;
|
||
|
||
MagickBooleanType
|
||
status;
|
||
|
||
MagickOffsetType
|
||
offset;
|
||
|
||
MngBox
|
||
default_fb,
|
||
fb,
|
||
previous_fb;
|
||
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
PixelInfo
|
||
mng_background_color;
|
||
#endif
|
||
|
||
unsigned char
|
||
*p;
|
||
|
||
ssize_t
|
||
i;
|
||
|
||
size_t
|
||
count;
|
||
|
||
ssize_t
|
||
loop_level;
|
||
|
||
volatile short
|
||
skipping_loop;
|
||
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
unsigned int
|
||
mandatory_back=0;
|
||
#endif
|
||
|
||
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 */
|
||
|
||
size_t
|
||
default_frame_timeout,
|
||
frame_timeout,
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
image_height,
|
||
image_width,
|
||
#endif
|
||
length;
|
||
|
||
/* These delays are all measured in image ticks_per_second,
|
||
* not in MNG ticks_per_second
|
||
*/
|
||
volatile size_t
|
||
default_frame_delay,
|
||
final_delay,
|
||
final_image_delay,
|
||
frame_delay,
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
insert_layers,
|
||
#endif
|
||
mng_iterations=1,
|
||
simplicity=0,
|
||
subframe_height=0,
|
||
subframe_width=0;
|
||
|
||
previous_fb.top=0;
|
||
previous_fb.bottom=0;
|
||
previous_fb.left=0;
|
||
previous_fb.right=0;
|
||
default_fb.top=0;
|
||
default_fb.bottom=0;
|
||
default_fb.left=0;
|
||
default_fb.right=0;
|
||
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Enter ReadOneMNGImage()");
|
||
|
||
image=mng_info->image;
|
||
|
||
if (LocaleCompare(image_info->magick,"MNG") == 0)
|
||
{
|
||
char
|
||
magic_number[MagickPathExtent];
|
||
|
||
/* Verify MNG signature. */
|
||
count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
|
||
if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
|
||
/* Initialize some nonzero members of the MngInfo structure. */
|
||
for (i=0; i < MNG_MAX_OBJECTS; i++)
|
||
{
|
||
mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
|
||
mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
|
||
}
|
||
mng_info->exists[0]=MagickTrue;
|
||
}
|
||
|
||
skipping_loop=(-1);
|
||
first_mng_object=MagickTrue;
|
||
mng_type=0;
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
insert_layers=MagickFalse; /* should be False during convert or mogrify */
|
||
#endif
|
||
default_frame_delay=0;
|
||
default_frame_timeout=0;
|
||
frame_delay=0;
|
||
final_delay=1;
|
||
mng_info->ticks_per_second=1UL*image->ticks_per_second;
|
||
object_id=0;
|
||
skip_to_iend=MagickFalse;
|
||
term_chunk_found=MagickFalse;
|
||
mng_info->framing_mode=1;
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
mandatory_back=MagickFalse;
|
||
#endif
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
mng_background_color=image->background_color;
|
||
#endif
|
||
default_fb=mng_info->frame;
|
||
previous_fb=mng_info->frame;
|
||
do
|
||
{
|
||
char
|
||
type[MagickPathExtent];
|
||
|
||
if (LocaleCompare(image_info->magick,"MNG") == 0)
|
||
{
|
||
unsigned char
|
||
*chunk;
|
||
|
||
/*
|
||
Read a new chunk.
|
||
*/
|
||
type[0]='\0';
|
||
(void) ConcatenateMagickString(type,"errr",MagickPathExtent);
|
||
length=(size_t) ReadBlobMSBLong(image);
|
||
count=(size_t) ReadBlob(image,4,(unsigned char *) type);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reading MNG chunk type %c%c%c%c, length: %.20g",
|
||
type[0],type[1],type[2],type[3],(double) length);
|
||
|
||
if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
|
||
(count < 4))
|
||
ThrowReaderException(CorruptImageError,"CorruptImage");
|
||
|
||
p=NULL;
|
||
chunk=(unsigned char *) NULL;
|
||
|
||
if (length != 0)
|
||
{
|
||
chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
|
||
|
||
if (chunk == (unsigned char *) NULL)
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
for (i=0; i < (ssize_t) length; i++)
|
||
{
|
||
int
|
||
c;
|
||
|
||
c=ReadBlobByte(image);
|
||
if (c == EOF)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(CorruptImageError,
|
||
"InsufficientImageDataInFile");
|
||
}
|
||
chunk[i]=(unsigned char) c;
|
||
}
|
||
|
||
p=chunk;
|
||
}
|
||
|
||
(void) ReadBlobMSBLong(image); /* read crc word */
|
||
|
||
#if !defined(JNG_SUPPORTED)
|
||
if (memcmp(type,mng_JHDR,4) == 0)
|
||
{
|
||
skip_to_iend=MagickTrue;
|
||
|
||
if (mng_info->jhdr_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"JNGCompressNotSupported","`%s'",image->filename);
|
||
|
||
mng_info->jhdr_warning++;
|
||
}
|
||
#endif
|
||
if (memcmp(type,mng_DHDR,4) == 0)
|
||
{
|
||
skip_to_iend=MagickTrue;
|
||
|
||
if (mng_info->dhdr_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
|
||
|
||
mng_info->dhdr_warning++;
|
||
}
|
||
if (memcmp(type,mng_MEND,4) == 0)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
break;
|
||
}
|
||
|
||
if (skip_to_iend)
|
||
{
|
||
if (memcmp(type,mng_IEND,4) == 0)
|
||
skip_to_iend=MagickFalse;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Skip to IEND.");
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_MHDR,4) == 0)
|
||
{
|
||
if (length != 28)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(CorruptImageError,"CorruptImage");
|
||
}
|
||
|
||
mng_info->mng_width=(unsigned long)mng_get_long(p);
|
||
mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" MNG width: %.20g",(double) mng_info->mng_width);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" MNG height: %.20g",(double) mng_info->mng_height);
|
||
}
|
||
|
||
p+=8;
|
||
mng_info->ticks_per_second=(size_t) mng_get_long(p);
|
||
|
||
if (mng_info->ticks_per_second == 0)
|
||
default_frame_delay=0;
|
||
|
||
else
|
||
default_frame_delay=1UL*image->ticks_per_second/
|
||
mng_info->ticks_per_second;
|
||
|
||
frame_delay=default_frame_delay;
|
||
simplicity=0;
|
||
|
||
p+=16;
|
||
simplicity=(size_t) 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 */
|
||
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (mng_type != 3)
|
||
insert_layers=MagickTrue;
|
||
#endif
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/* Allocate next image structure. */
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return((Image *) NULL);
|
||
|
||
image=SyncNextImageInList(image);
|
||
mng_info->image=image;
|
||
}
|
||
|
||
if ((mng_info->mng_width > 65535L) ||
|
||
(mng_info->mng_height > 65535L))
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
|
||
}
|
||
|
||
(void) FormatLocaleString(page_geometry,MagickPathExtent,
|
||
"%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
|
||
mng_info->mng_height);
|
||
|
||
mng_info->frame.left=0;
|
||
mng_info->frame.right=(ssize_t) mng_info->mng_width;
|
||
mng_info->frame.top=0;
|
||
mng_info->frame.bottom=(ssize_t) 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;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_TERM,4) == 0)
|
||
{
|
||
int
|
||
repeat=0;
|
||
|
||
if (length != 0)
|
||
repeat=p[0];
|
||
|
||
if (repeat == 3 && length > 9)
|
||
{
|
||
final_delay=(png_uint_32) mng_get_long(&p[2]);
|
||
mng_iterations=(png_uint_32) mng_get_long(&p[6]);
|
||
|
||
if (mng_iterations == PNG_UINT_31_MAX)
|
||
mng_iterations=0;
|
||
|
||
image->iterations=mng_iterations;
|
||
term_chunk_found=MagickTrue;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" repeat=%d, final_delay=%.20g, iterations=%.20g",
|
||
repeat,(double) final_delay, (double) image->iterations);
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
if (memcmp(type,mng_DEFI,4) == 0)
|
||
{
|
||
if (mng_type == 3)
|
||
{
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
|
||
image->filename);
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (length < 2)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(CorruptImageError,"CorruptImage");
|
||
}
|
||
|
||
object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
|
||
|
||
if (mng_type == 2 && object_id != 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
|
||
image->filename);
|
||
|
||
if (object_id >= MNG_MAX_OBJECTS)
|
||
{
|
||
/*
|
||
Instead of using a warning we should allocate a larger
|
||
MngInfo structure and continue.
|
||
*/
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"object id too large","`%s'",image->filename);
|
||
object_id=MNG_MAX_OBJECTS-1;
|
||
}
|
||
|
||
if (mng_info->exists[object_id])
|
||
if (mng_info->frozen[object_id])
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderError,
|
||
"DEFI cannot redefine a frozen MNG object","`%s'",
|
||
image->filename);
|
||
continue;
|
||
}
|
||
|
||
mng_info->exists[object_id]=MagickTrue;
|
||
|
||
if (length > 2)
|
||
mng_info->invisible[object_id]=p[2];
|
||
|
||
/*
|
||
Extract object offset info.
|
||
*/
|
||
if (length > 11)
|
||
{
|
||
mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
|
||
mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" x_off[%d]: %.20g, y_off[%d]: %.20g",
|
||
object_id,(double) mng_info->x_off[object_id],
|
||
object_id,(double) 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]);
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
if (memcmp(type,mng_bKGD,4) == 0)
|
||
{
|
||
mng_info->have_global_bkgd=MagickFalse;
|
||
|
||
if (length > 5)
|
||
{
|
||
mng_info->mng_global_bkgd.red=
|
||
ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
|
||
|
||
mng_info->mng_global_bkgd.green=
|
||
ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
|
||
|
||
mng_info->mng_global_bkgd.blue=
|
||
ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
|
||
|
||
mng_info->have_global_bkgd=MagickTrue;
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
if (memcmp(type,mng_BACK,4) == 0)
|
||
{
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (length > 6)
|
||
mandatory_back=p[6];
|
||
|
||
else
|
||
mandatory_back=0;
|
||
|
||
if (mandatory_back && length > 5)
|
||
{
|
||
mng_background_color.red=
|
||
ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
|
||
|
||
mng_background_color.green=
|
||
ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
|
||
|
||
mng_background_color.blue=
|
||
ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
|
||
|
||
mng_background_color.alpha=OpaqueAlpha;
|
||
}
|
||
|
||
#ifdef MNG_OBJECT_BUFFERS
|
||
if (length > 8)
|
||
mng_background_object=(p[7] << 8) | p[8];
|
||
#endif
|
||
#endif
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_PLTE,4) == 0)
|
||
{
|
||
/* Read global PLTE. */
|
||
|
||
if (length && (length < 769))
|
||
{
|
||
/* Read global PLTE. */
|
||
|
||
if (mng_info->global_plte == (png_colorp) NULL)
|
||
mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
|
||
sizeof(*mng_info->global_plte));
|
||
|
||
if (mng_info->global_plte == (png_colorp) NULL)
|
||
{
|
||
mng_info->global_plte_length=0;
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(ResourceLimitError,
|
||
"MemoryAllocationFailed");
|
||
}
|
||
|
||
for (i=0; i < (ssize_t) (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=(unsigned int) (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 != 0)
|
||
mng_info->global_plte_length=256;
|
||
#endif
|
||
else
|
||
mng_info->global_plte_length=0;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_tRNS,4) == 0)
|
||
{
|
||
/* read global tRNS */
|
||
|
||
if (length > 0 && length < 257)
|
||
for (i=0; i < (ssize_t) 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=(unsigned int) length;
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
if (memcmp(type,mng_gAMA,4) == 0)
|
||
{
|
||
if (length == 4)
|
||
{
|
||
ssize_t
|
||
igamma;
|
||
|
||
igamma=mng_get_long(p);
|
||
mng_info->global_gamma=((float) igamma)*0.00001;
|
||
mng_info->have_global_gama=MagickTrue;
|
||
}
|
||
|
||
else
|
||
mng_info->have_global_gama=MagickFalse;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_cHRM,4) == 0)
|
||
{
|
||
/* 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=MagickTrue;
|
||
}
|
||
else
|
||
mng_info->have_global_chrm=MagickFalse;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_sRGB,4) == 0)
|
||
{
|
||
/*
|
||
Read global sRGB.
|
||
*/
|
||
if (length != 0)
|
||
{
|
||
mng_info->global_srgb_intent=
|
||
Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
|
||
mng_info->have_global_srgb=MagickTrue;
|
||
}
|
||
else
|
||
mng_info->have_global_srgb=MagickFalse;
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_iCCP,4) == 0)
|
||
{
|
||
/* To do: */
|
||
|
||
/*
|
||
Read global iCCP.
|
||
*/
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_FRAM,4) == 0)
|
||
{
|
||
if (mng_type == 3)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
|
||
image->filename);
|
||
|
||
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 != 0)
|
||
if (p[0])
|
||
mng_info->framing_mode=p[0];
|
||
|
||
if (logging != MagickFalse)
|
||
(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-chunk) < (long) length) && *p)
|
||
p++; /* frame name */
|
||
|
||
p++; /* frame name terminator */
|
||
|
||
if ((p-chunk) < (ssize_t) (length-4))
|
||
{
|
||
int
|
||
change_delay,
|
||
change_timeout,
|
||
change_clipping;
|
||
|
||
change_delay=(*p++);
|
||
change_timeout=(*p++);
|
||
change_clipping=(*p++);
|
||
p++; /* change_sync */
|
||
|
||
if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
|
||
{
|
||
frame_delay=1UL*image->ticks_per_second*
|
||
mng_get_long(p);
|
||
|
||
if (mng_info->ticks_per_second != 0)
|
||
frame_delay/=mng_info->ticks_per_second;
|
||
|
||
else
|
||
frame_delay=PNG_UINT_31_MAX;
|
||
|
||
if (change_delay == 2)
|
||
default_frame_delay=frame_delay;
|
||
|
||
p+=4;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Framing_delay=%.20g",(double) frame_delay);
|
||
}
|
||
|
||
if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
|
||
{
|
||
frame_timeout=1UL*image->ticks_per_second*
|
||
mng_get_long(p);
|
||
|
||
if (mng_info->ticks_per_second != 0)
|
||
frame_timeout/=mng_info->ticks_per_second;
|
||
|
||
else
|
||
frame_timeout=PNG_UINT_31_MAX;
|
||
|
||
if (change_timeout == 2)
|
||
default_frame_timeout=frame_timeout;
|
||
|
||
p+=4;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Framing_timeout=%.20g",(double) frame_timeout);
|
||
}
|
||
|
||
if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
|
||
{
|
||
fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
|
||
p+=16;
|
||
previous_fb=fb;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
|
||
(double) fb.left,(double) fb.right,(double) fb.top,
|
||
(double) 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=(size_t) (mng_info->clip.right
|
||
-mng_info->clip.left);
|
||
|
||
subframe_height=(size_t) (mng_info->clip.bottom
|
||
-mng_info->clip.top);
|
||
/*
|
||
Insert a background layer behind the frame if framing_mode is 4.
|
||
*/
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" subframe_width=%.20g, subframe_height=%.20g",(double)
|
||
subframe_width,(double) subframe_height);
|
||
|
||
if (insert_layers && (mng_info->framing_mode == 4) &&
|
||
(subframe_width) && (subframe_height))
|
||
{
|
||
/* Allocate next image structure. */
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
|
||
mng_info->image=image;
|
||
|
||
if (term_chunk_found)
|
||
{
|
||
image->start_loop=MagickTrue;
|
||
image->iterations=mng_iterations;
|
||
term_chunk_found=MagickFalse;
|
||
}
|
||
|
||
else
|
||
image->start_loop=MagickFalse;
|
||
|
||
image->columns=subframe_width;
|
||
image->rows=subframe_height;
|
||
image->page.width=subframe_width;
|
||
image->page.height=subframe_height;
|
||
image->page.x=mng_info->clip.left;
|
||
image->page.y=mng_info->clip.top;
|
||
image->background_color=mng_background_color;
|
||
image->alpha_trait=UndefinedPixelTrait;
|
||
image->delay=0;
|
||
if (SetImageBackgroundColor(image,exception) == MagickFalse)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
return(DestroyImageList(image));
|
||
}
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
|
||
(double) mng_info->clip.left,
|
||
(double) mng_info->clip.right,
|
||
(double) mng_info->clip.top,
|
||
(double) mng_info->clip.bottom);
|
||
}
|
||
#endif
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_CLIP,4) == 0)
|
||
{
|
||
unsigned int
|
||
first_object,
|
||
last_object;
|
||
|
||
/*
|
||
Read CLIP.
|
||
*/
|
||
if (length > 3)
|
||
{
|
||
first_object=(p[0] << 8) | p[1];
|
||
last_object=(p[2] << 8) | p[3];
|
||
p+=4;
|
||
|
||
for (i=(int) first_object; i <= (int) last_object; i++)
|
||
{
|
||
if ((i < 0) || (i >= MNG_MAX_OBJECTS))
|
||
continue;
|
||
|
||
if (mng_info->exists[i] && !mng_info->frozen[i])
|
||
{
|
||
MngBox
|
||
box;
|
||
|
||
box=mng_info->object_clip[i];
|
||
if ((p-chunk) < (ssize_t) (length-17))
|
||
mng_info->object_clip[i]=
|
||
mng_read_box(box,(char) p[0],&p[1]);
|
||
}
|
||
}
|
||
|
||
}
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_SAVE,4) == 0)
|
||
{
|
||
for (i=1; i < MNG_MAX_OBJECTS; i++)
|
||
if (mng_info->exists[i])
|
||
{
|
||
mng_info->frozen[i]=MagickTrue;
|
||
#ifdef MNG_OBJECT_BUFFERS
|
||
if (mng_info->ob[i] != (MngBuffer *) NULL)
|
||
mng_info->ob[i]->frozen=MagickTrue;
|
||
#endif
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
|
||
{
|
||
/* Read DISC or SEEK. */
|
||
|
||
if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
|
||
{
|
||
for (i=1; i < MNG_MAX_OBJECTS; i++)
|
||
MngInfoDiscardObject(mng_info,i);
|
||
}
|
||
|
||
else
|
||
{
|
||
ssize_t
|
||
j;
|
||
|
||
for (j=1; j < (ssize_t) length; j+=2)
|
||
{
|
||
i=p[j-1] << 8 | p[j];
|
||
MngInfoDiscardObject(mng_info,i);
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_MOVE,4) == 0)
|
||
{
|
||
size_t
|
||
first_object,
|
||
last_object;
|
||
|
||
/* read MOVE */
|
||
|
||
if (length > 3)
|
||
{
|
||
first_object=(p[0] << 8) | p[1];
|
||
last_object=(p[2] << 8) | p[3];
|
||
p+=4;
|
||
|
||
for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
|
||
{
|
||
if ((i < 0) || (i >= MNG_MAX_OBJECTS))
|
||
continue;
|
||
|
||
if (mng_info->exists[i] && !mng_info->frozen[i] &&
|
||
(p-chunk) < (ssize_t) (length-8))
|
||
{
|
||
MngPair
|
||
new_pair;
|
||
|
||
MngPair
|
||
old_pair;
|
||
|
||
old_pair.a=mng_info->x_off[i];
|
||
old_pair.b=mng_info->y_off[i];
|
||
new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
|
||
mng_info->x_off[i]=new_pair.a;
|
||
mng_info->y_off[i]=new_pair.b;
|
||
}
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_LOOP,4) == 0)
|
||
{
|
||
ssize_t loop_iters=1;
|
||
if (length > 4)
|
||
{
|
||
loop_level=chunk[0];
|
||
mng_info->loop_active[loop_level]=1; /* mark loop active */
|
||
|
||
/* Record starting point. */
|
||
loop_iters=mng_get_long(&chunk[1]);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" LOOP level %.20g has %.20g iterations ",
|
||
(double) loop_level, (double) loop_iters);
|
||
|
||
if (loop_iters <= 0)
|
||
skipping_loop=loop_level;
|
||
|
||
else
|
||
{
|
||
if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
|
||
loop_iters=GetMagickResourceLimit(ListLengthResource);
|
||
if (loop_iters >= 2147483647L)
|
||
loop_iters=2147483647L;
|
||
mng_info->loop_jump[loop_level]=TellBlob(image);
|
||
mng_info->loop_count[loop_level]=loop_iters;
|
||
}
|
||
|
||
mng_info->loop_iteration[loop_level]=0;
|
||
}
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_ENDL,4) == 0)
|
||
{
|
||
if (length > 0)
|
||
{
|
||
loop_level=chunk[0];
|
||
|
||
if (skipping_loop > 0)
|
||
{
|
||
if (skipping_loop == loop_level)
|
||
{
|
||
/*
|
||
Found end of zero-iteration loop.
|
||
*/
|
||
skipping_loop=(-1);
|
||
mng_info->loop_active[loop_level]=0;
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
if (mng_info->loop_active[loop_level] == 1)
|
||
{
|
||
mng_info->loop_count[loop_level]--;
|
||
mng_info->loop_iteration[loop_level]++;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ENDL: LOOP level %.20g has %.20g remaining iters",
|
||
(double) loop_level,(double)
|
||
mng_info->loop_count[loop_level]);
|
||
|
||
if (mng_info->loop_count[loop_level] > 0)
|
||
{
|
||
offset=
|
||
SeekBlob(image,mng_info->loop_jump[loop_level],
|
||
SEEK_SET);
|
||
|
||
if (offset < 0)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(
|
||
chunk);
|
||
ThrowReaderException(CorruptImageError,
|
||
"ImproperImageHeader");
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
short
|
||
last_level;
|
||
|
||
/*
|
||
Finished loop.
|
||
*/
|
||
mng_info->loop_active[loop_level]=0;
|
||
last_level=(-1);
|
||
for (i=0; i < loop_level; i++)
|
||
if (mng_info->loop_active[i] == 1)
|
||
last_level=(short) i;
|
||
loop_level=last_level;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_CLON,4) == 0)
|
||
{
|
||
if (mng_info->clon_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"CLON is not implemented yet","`%s'",
|
||
image->filename);
|
||
|
||
mng_info->clon_warning++;
|
||
}
|
||
|
||
if (memcmp(type,mng_MAGN,4) == 0)
|
||
{
|
||
png_uint_16
|
||
magn_first,
|
||
magn_last,
|
||
magn_mb,
|
||
magn_ml,
|
||
magn_mr,
|
||
magn_mt,
|
||
magn_mx,
|
||
magn_my,
|
||
magn_methx,
|
||
magn_methy;
|
||
|
||
if (length > 1)
|
||
magn_first=(p[0] << 8) | p[1];
|
||
|
||
else
|
||
magn_first=0;
|
||
|
||
if (length > 3)
|
||
magn_last=(p[2] << 8) | p[3];
|
||
|
||
else
|
||
magn_last=magn_first;
|
||
#ifndef MNG_OBJECT_BUFFERS
|
||
if (magn_first || magn_last)
|
||
if (mng_info->magn_warning == 0)
|
||
{
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderError,
|
||
"MAGN is not implemented yet for nonzero objects",
|
||
"`%s'",image->filename);
|
||
|
||
mng_info->magn_warning++;
|
||
}
|
||
#endif
|
||
if (length > 4)
|
||
magn_methx=p[4];
|
||
|
||
else
|
||
magn_methx=0;
|
||
|
||
if (length > 6)
|
||
magn_mx=(p[5] << 8) | p[6];
|
||
|
||
else
|
||
magn_mx=1;
|
||
|
||
if (magn_mx == 0)
|
||
magn_mx=1;
|
||
|
||
if (length > 8)
|
||
magn_my=(p[7] << 8) | p[8];
|
||
|
||
else
|
||
magn_my=magn_mx;
|
||
|
||
if (magn_my == 0)
|
||
magn_my=1;
|
||
|
||
if (length > 10)
|
||
magn_ml=(p[9] << 8) | p[10];
|
||
|
||
else
|
||
magn_ml=magn_mx;
|
||
|
||
if (magn_ml == 0)
|
||
magn_ml=1;
|
||
|
||
if (length > 12)
|
||
magn_mr=(p[11] << 8) | p[12];
|
||
|
||
else
|
||
magn_mr=magn_mx;
|
||
|
||
if (magn_mr == 0)
|
||
magn_mr=1;
|
||
|
||
if (length > 14)
|
||
magn_mt=(p[13] << 8) | p[14];
|
||
|
||
else
|
||
magn_mt=magn_my;
|
||
|
||
if (magn_mt == 0)
|
||
magn_mt=1;
|
||
|
||
if (length > 16)
|
||
magn_mb=(p[15] << 8) | p[16];
|
||
|
||
else
|
||
magn_mb=magn_my;
|
||
|
||
if (magn_mb == 0)
|
||
magn_mb=1;
|
||
|
||
if (length > 17)
|
||
magn_methy=p[17];
|
||
|
||
else
|
||
magn_methy=magn_methx;
|
||
|
||
|
||
if (magn_methx > 5 || magn_methy > 5)
|
||
if (mng_info->magn_warning == 0)
|
||
{
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderError,
|
||
"Unknown MAGN method in MNG datastream","`%s'",
|
||
image->filename);
|
||
|
||
mng_info->magn_warning++;
|
||
}
|
||
#ifdef MNG_OBJECT_BUFFERS
|
||
/* Magnify existing objects in the range magn_first to magn_last */
|
||
#endif
|
||
if (magn_first == 0 || magn_last == 0)
|
||
{
|
||
/* Save the magnification factors for object 0 */
|
||
mng_info->magn_mb=magn_mb;
|
||
mng_info->magn_ml=magn_ml;
|
||
mng_info->magn_mr=magn_mr;
|
||
mng_info->magn_mt=magn_mt;
|
||
mng_info->magn_mx=magn_mx;
|
||
mng_info->magn_my=magn_my;
|
||
mng_info->magn_methx=magn_methx;
|
||
mng_info->magn_methy=magn_methy;
|
||
}
|
||
}
|
||
|
||
if (memcmp(type,mng_PAST,4) == 0)
|
||
{
|
||
if (mng_info->past_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"PAST is not implemented yet","`%s'",
|
||
image->filename);
|
||
|
||
mng_info->past_warning++;
|
||
}
|
||
|
||
if (memcmp(type,mng_SHOW,4) == 0)
|
||
{
|
||
if (mng_info->show_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"SHOW is not implemented yet","`%s'",
|
||
image->filename);
|
||
|
||
mng_info->show_warning++;
|
||
}
|
||
|
||
if (memcmp(type,mng_sBIT,4) == 0)
|
||
{
|
||
if (length < 4)
|
||
mng_info->have_global_sbit=MagickFalse;
|
||
|
||
else
|
||
{
|
||
mng_info->global_sbit.gray=p[0];
|
||
mng_info->global_sbit.red=p[0];
|
||
mng_info->global_sbit.green=p[1];
|
||
mng_info->global_sbit.blue=p[2];
|
||
mng_info->global_sbit.alpha=p[3];
|
||
mng_info->have_global_sbit=MagickTrue;
|
||
}
|
||
}
|
||
if (memcmp(type,mng_pHYs,4) == 0)
|
||
{
|
||
if (length > 8)
|
||
{
|
||
mng_info->global_x_pixels_per_unit=
|
||
(size_t) mng_get_long(p);
|
||
mng_info->global_y_pixels_per_unit=
|
||
(size_t) mng_get_long(&p[4]);
|
||
mng_info->global_phys_unit_type=p[8];
|
||
mng_info->have_global_phys=MagickTrue;
|
||
}
|
||
|
||
else
|
||
mng_info->have_global_phys=MagickFalse;
|
||
}
|
||
if (memcmp(type,mng_pHYg,4) == 0)
|
||
{
|
||
if (mng_info->phyg_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"pHYg is not implemented.","`%s'",image->filename);
|
||
|
||
mng_info->phyg_warning++;
|
||
}
|
||
if (memcmp(type,mng_BASI,4) == 0)
|
||
{
|
||
skip_to_iend=MagickTrue;
|
||
|
||
if (mng_info->basi_warning == 0)
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"BASI is not implemented yet","`%s'",
|
||
image->filename);
|
||
|
||
mng_info->basi_warning++;
|
||
#ifdef MNG_BASI_SUPPORTED
|
||
basi_width=(unsigned long) mng_get_long(p);
|
||
basi_width=(unsigned long) mng_get_long(&p[4]);
|
||
basi_color_type=p[8];
|
||
basi_compression_method=p[9];
|
||
basi_filter_type=p[10];
|
||
basi_interlace_method=p[11];
|
||
if (length > 11)
|
||
basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
|
||
|
||
else
|
||
basi_red=0;
|
||
|
||
if (length > 13)
|
||
basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
|
||
|
||
else
|
||
basi_green=0;
|
||
|
||
if (length > 15)
|
||
basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
|
||
|
||
else
|
||
basi_blue=0;
|
||
|
||
if (length > 17)
|
||
basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
|
||
|
||
else
|
||
{
|
||
if (basi_sample_depth == 16)
|
||
basi_alpha=65535L;
|
||
else
|
||
basi_alpha=255;
|
||
}
|
||
|
||
if (length > 19)
|
||
basi_viewable=p[20];
|
||
|
||
else
|
||
basi_viewable=0;
|
||
|
||
#endif
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(type,mng_IHDR,4)
|
||
#if defined(JNG_SUPPORTED)
|
||
&& memcmp(type,mng_JHDR,4)
|
||
#endif
|
||
)
|
||
{
|
||
/* Not an IHDR or JHDR chunk */
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
continue;
|
||
}
|
||
/* Process IHDR */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
|
||
|
||
mng_info->exists[object_id]=MagickTrue;
|
||
mng_info->viewable[object_id]=MagickTrue;
|
||
|
||
if (mng_info->invisible[object_id])
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Skipping invisible object");
|
||
|
||
skip_to_iend=MagickTrue;
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
continue;
|
||
}
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (length < 8)
|
||
{
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
|
||
image_width=(size_t) mng_get_long(p);
|
||
image_height=(size_t) mng_get_long(&p[4]);
|
||
#endif
|
||
chunk=(unsigned char *) RelinquishMagickMemory(chunk);
|
||
|
||
/*
|
||
Insert a transparent background layer behind the entire animation
|
||
if it is not full screen.
|
||
*/
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (insert_layers && mng_type && first_mng_object)
|
||
{
|
||
if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
|
||
(image_width < mng_info->mng_width) ||
|
||
(mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
|
||
(image_height < mng_info->mng_height) ||
|
||
(mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
|
||
{
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/*
|
||
Allocate next image structure.
|
||
*/
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
mng_info->image=image;
|
||
|
||
if (term_chunk_found)
|
||
{
|
||
image->start_loop=MagickTrue;
|
||
image->iterations=mng_iterations;
|
||
term_chunk_found=MagickFalse;
|
||
}
|
||
|
||
else
|
||
image->start_loop=MagickFalse;
|
||
|
||
/* Make a background rectangle. */
|
||
|
||
image->delay=0;
|
||
image->columns=mng_info->mng_width;
|
||
image->rows=mng_info->mng_height;
|
||
image->page.width=mng_info->mng_width;
|
||
image->page.height=mng_info->mng_height;
|
||
image->page.x=0;
|
||
image->page.y=0;
|
||
image->background_color=mng_background_color;
|
||
(void) SetImageBackgroundColor(image,exception);
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Inserted transparent background layer, W=%.20g, H=%.20g",
|
||
(double) mng_info->mng_width,(double) mng_info->mng_height);
|
||
}
|
||
}
|
||
/*
|
||
Insert a background layer behind the upcoming image if
|
||
framing_mode is 3, and we haven't already inserted one.
|
||
*/
|
||
if (insert_layers && (mng_info->framing_mode == 3) &&
|
||
(subframe_width) && (subframe_height) && (simplicity == 0 ||
|
||
(simplicity & 0x08)))
|
||
{
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/*
|
||
Allocate next image structure.
|
||
*/
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
|
||
mng_info->image=image;
|
||
|
||
if (term_chunk_found)
|
||
{
|
||
image->start_loop=MagickTrue;
|
||
image->iterations=mng_iterations;
|
||
term_chunk_found=MagickFalse;
|
||
}
|
||
|
||
else
|
||
image->start_loop=MagickFalse;
|
||
|
||
image->delay=0;
|
||
image->columns=subframe_width;
|
||
image->rows=subframe_height;
|
||
image->page.width=subframe_width;
|
||
image->page.height=subframe_height;
|
||
image->page.x=mng_info->clip.left;
|
||
image->page.y=mng_info->clip.top;
|
||
image->background_color=mng_background_color;
|
||
image->alpha_trait=UndefinedPixelTrait;
|
||
(void) SetImageBackgroundColor(image,exception);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
|
||
(double) mng_info->clip.left,(double) mng_info->clip.right,
|
||
(double) mng_info->clip.top,(double) mng_info->clip.bottom);
|
||
}
|
||
#endif /* MNG_INSERT_LAYERS */
|
||
first_mng_object=MagickFalse;
|
||
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/*
|
||
Allocate next image structure.
|
||
*/
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
mng_info->image=image;
|
||
status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
|
||
GetBlobSize(image));
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
|
||
if (term_chunk_found)
|
||
{
|
||
image->start_loop=MagickTrue;
|
||
term_chunk_found=MagickFalse;
|
||
}
|
||
|
||
else
|
||
image->start_loop=MagickFalse;
|
||
|
||
if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
|
||
{
|
||
image->delay=frame_delay;
|
||
frame_delay=default_frame_delay;
|
||
}
|
||
|
||
else
|
||
image->delay=0;
|
||
|
||
image->page.width=mng_info->mng_width;
|
||
image->page.height=mng_info->mng_height;
|
||
image->page.x=mng_info->x_off[object_id];
|
||
image->page.y=mng_info->y_off[object_id];
|
||
image->iterations=mng_iterations;
|
||
|
||
/*
|
||
Seek back to the beginning of the IHDR or JHDR chunk's length field.
|
||
*/
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
|
||
type[2],type[3]);
|
||
|
||
offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
|
||
|
||
if (offset < 0)
|
||
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
|
||
}
|
||
|
||
mng_info->image=image;
|
||
mng_info->mng_type=mng_type;
|
||
mng_info->object_id=object_id;
|
||
|
||
if (memcmp(type,mng_IHDR,4) == 0)
|
||
image=ReadOnePNGImage(mng_info,image_info,exception);
|
||
|
||
#if defined(JNG_SUPPORTED)
|
||
else
|
||
image=ReadOneJNGImage(mng_info,image_info,exception);
|
||
#endif
|
||
|
||
if (image == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadJNGImage() with error");
|
||
|
||
return((Image *) NULL);
|
||
}
|
||
|
||
if (image->columns == 0 || image->rows == 0)
|
||
{
|
||
(void) CloseBlob(image);
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
mng_info->image=image;
|
||
|
||
if (mng_type)
|
||
{
|
||
MngBox
|
||
crop_box;
|
||
|
||
if (((mng_info->magn_methx > 0) && (mng_info->magn_methx <= 5)) &&
|
||
((mng_info->magn_methy > 0) && (mng_info->magn_methy <= 5)))
|
||
{
|
||
png_uint_32
|
||
magnified_height,
|
||
magnified_width;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Processing MNG MAGN chunk");
|
||
|
||
if (image->columns == 1)
|
||
mng_info->magn_methx = 1;
|
||
if (image->rows == 1)
|
||
mng_info->magn_methy = 1;
|
||
if (mng_info->magn_methx == 1)
|
||
{
|
||
magnified_width=mng_info->magn_ml;
|
||
|
||
if (image->columns > 1)
|
||
magnified_width += mng_info->magn_mr;
|
||
|
||
if (image->columns > 2)
|
||
magnified_width += (png_uint_32)
|
||
((image->columns-2)*(mng_info->magn_mx));
|
||
}
|
||
|
||
else
|
||
{
|
||
magnified_width=(png_uint_32) image->columns;
|
||
|
||
if (image->columns > 1)
|
||
magnified_width += mng_info->magn_ml-1;
|
||
|
||
if (image->columns > 2)
|
||
magnified_width += mng_info->magn_mr-1;
|
||
|
||
if (image->columns > 3)
|
||
magnified_width += (png_uint_32)
|
||
((image->columns-3)*(mng_info->magn_mx-1));
|
||
}
|
||
|
||
if (mng_info->magn_methy == 1)
|
||
{
|
||
magnified_height=mng_info->magn_mt;
|
||
|
||
if (image->rows > 1)
|
||
magnified_height += mng_info->magn_mb;
|
||
|
||
if (image->rows > 2)
|
||
magnified_height += (png_uint_32)
|
||
((image->rows-2)*(mng_info->magn_my));
|
||
}
|
||
|
||
else
|
||
{
|
||
magnified_height=(png_uint_32) image->rows;
|
||
|
||
if (image->rows > 1)
|
||
magnified_height += mng_info->magn_mt-1;
|
||
|
||
if (image->rows > 2)
|
||
magnified_height += mng_info->magn_mb-1;
|
||
|
||
if (image->rows > 3)
|
||
magnified_height += (png_uint_32)
|
||
((image->rows-3)*(mng_info->magn_my-1));
|
||
}
|
||
|
||
if (magnified_height > image->rows ||
|
||
magnified_width > image->columns)
|
||
{
|
||
Image
|
||
*large_image;
|
||
|
||
int
|
||
yy;
|
||
|
||
Quantum
|
||
*next,
|
||
*prev;
|
||
|
||
png_uint_16
|
||
magn_methx,
|
||
magn_methy;
|
||
|
||
ssize_t
|
||
m,
|
||
y;
|
||
|
||
Quantum
|
||
*n,
|
||
*q;
|
||
|
||
ssize_t
|
||
x;
|
||
|
||
/* Allocate next image structure. */
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Allocate magnified image");
|
||
|
||
AcquireNextImage(image_info,image,exception);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
return(DestroyImageList(image));
|
||
|
||
large_image=SyncNextImageInList(image);
|
||
|
||
large_image->columns=magnified_width;
|
||
large_image->rows=magnified_height;
|
||
|
||
magn_methx=mng_info->magn_methx;
|
||
magn_methy=mng_info->magn_methy;
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 16)
|
||
#define QM unsigned short
|
||
if (magn_methx != 1 || magn_methy != 1)
|
||
{
|
||
/*
|
||
Scale pixels to unsigned shorts to prevent
|
||
overflow of intermediate values of interpolations
|
||
*/
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,
|
||
exception);
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
SetPixelRed(image,ScaleQuantumToShort(
|
||
GetPixelRed(image,q)),q);
|
||
SetPixelGreen(image,ScaleQuantumToShort(
|
||
GetPixelGreen(image,q)),q);
|
||
SetPixelBlue(image,ScaleQuantumToShort(
|
||
GetPixelBlue(image,q)),q);
|
||
SetPixelAlpha(image,ScaleQuantumToShort(
|
||
GetPixelAlpha(image,q)),q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
#else
|
||
#define QM Quantum
|
||
#endif
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
(void) SetImageBackgroundColor(large_image,exception);
|
||
|
||
else
|
||
{
|
||
large_image->background_color.alpha=OpaqueAlpha;
|
||
(void) SetImageBackgroundColor(large_image,exception);
|
||
|
||
if (magn_methx == 4)
|
||
magn_methx=2;
|
||
|
||
if (magn_methx == 5)
|
||
magn_methx=3;
|
||
|
||
if (magn_methy == 4)
|
||
magn_methy=2;
|
||
|
||
if (magn_methy == 5)
|
||
magn_methy=3;
|
||
}
|
||
|
||
/* magnify the rows into the right side of the large image */
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Magnify the rows to %.20g",
|
||
(double) large_image->rows);
|
||
m=(ssize_t) mng_info->magn_mt;
|
||
yy=0;
|
||
length=(size_t) GetPixelChannels(image)*image->columns;
|
||
next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
|
||
prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
|
||
|
||
if ((prev == (Quantum *) NULL) ||
|
||
(next == (Quantum *) NULL))
|
||
{
|
||
if (prev != (Quantum *) NULL)
|
||
prev=(Quantum *) RelinquishMagickMemory(prev);
|
||
if (next != (Quantum *) NULL)
|
||
next=(Quantum *) RelinquishMagickMemory(next);
|
||
image=DestroyImageList(image);
|
||
ThrowReaderException(ResourceLimitError,
|
||
"MemoryAllocationFailed");
|
||
}
|
||
|
||
n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
|
||
(void) memcpy(next,n,length);
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
if (y == 0)
|
||
m=(ssize_t) mng_info->magn_mt;
|
||
|
||
else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
|
||
m=(ssize_t) mng_info->magn_mb;
|
||
|
||
else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
|
||
m=(ssize_t) mng_info->magn_mb;
|
||
|
||
else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
|
||
m=1;
|
||
|
||
else
|
||
m=(ssize_t) mng_info->magn_my;
|
||
|
||
n=prev;
|
||
prev=next;
|
||
next=n;
|
||
|
||
if (y < (ssize_t) image->rows-1)
|
||
{
|
||
n=GetAuthenticPixels(image,0,y+1,image->columns,1,
|
||
exception);
|
||
(void) memcpy(next,n,length);
|
||
}
|
||
|
||
for (i=0; i < m; i++, yy++)
|
||
{
|
||
Quantum
|
||
*pixels;
|
||
|
||
assert(yy < (ssize_t) large_image->rows);
|
||
pixels=prev;
|
||
n=next;
|
||
q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
|
||
1,exception);
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
q+=(large_image->columns-image->columns)*
|
||
GetPixelChannels(large_image);
|
||
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
/* To do: get color as function of indexes[x] */
|
||
/*
|
||
if (image->storage_class == PseudoClass)
|
||
{
|
||
}
|
||
*/
|
||
|
||
if (magn_methy <= 1)
|
||
{
|
||
/* replicate previous */
|
||
SetPixelRed(large_image,GetPixelRed(image,pixels),q);
|
||
SetPixelGreen(large_image,GetPixelGreen(image,
|
||
pixels),q);
|
||
SetPixelBlue(large_image,GetPixelBlue(image,
|
||
pixels),q);
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,
|
||
pixels),q);
|
||
}
|
||
|
||
else if (magn_methy == 2 || magn_methy == 4)
|
||
{
|
||
if (i == 0)
|
||
{
|
||
SetPixelRed(large_image,GetPixelRed(image,
|
||
pixels),q);
|
||
SetPixelGreen(large_image,GetPixelGreen(image,
|
||
pixels),q);
|
||
SetPixelBlue(large_image,GetPixelBlue(image,
|
||
pixels),q);
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,
|
||
pixels),q);
|
||
}
|
||
|
||
else
|
||
{
|
||
/* Interpolate */
|
||
SetPixelRed(large_image,((QM) (((ssize_t)
|
||
(2*i*(GetPixelRed(image,n)
|
||
-GetPixelRed(image,pixels)+m))/
|
||
((ssize_t) (m*2))
|
||
+GetPixelRed(image,pixels)))),q);
|
||
SetPixelGreen(large_image,((QM) (((ssize_t)
|
||
(2*i*(GetPixelGreen(image,n)
|
||
-GetPixelGreen(image,pixels)+m))/
|
||
((ssize_t) (m*2))
|
||
+GetPixelGreen(image,pixels)))),q);
|
||
SetPixelBlue(large_image,((QM) (((ssize_t)
|
||
(2*i*(GetPixelBlue(image,n)
|
||
-GetPixelBlue(image,pixels)+m))/
|
||
((ssize_t) (m*2))
|
||
+GetPixelBlue(image,pixels)))),q);
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
SetPixelAlpha(large_image, ((QM) (((ssize_t)
|
||
(2*i*(GetPixelAlpha(image,n)
|
||
-GetPixelAlpha(image,pixels)+m))
|
||
/((ssize_t) (m*2))+
|
||
GetPixelAlpha(image,pixels)))),q);
|
||
}
|
||
|
||
if (magn_methy == 4)
|
||
{
|
||
/* Replicate nearest */
|
||
if (i <= ((m+1) << 1))
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,
|
||
pixels),q);
|
||
else
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,
|
||
n),q);
|
||
}
|
||
}
|
||
|
||
else /* if (magn_methy == 3 || magn_methy == 5) */
|
||
{
|
||
/* Replicate nearest */
|
||
if (i <= ((m+1) << 1))
|
||
{
|
||
SetPixelRed(large_image,GetPixelRed(image,
|
||
pixels),q);
|
||
SetPixelGreen(large_image,GetPixelGreen(image,
|
||
pixels),q);
|
||
SetPixelBlue(large_image,GetPixelBlue(image,
|
||
pixels),q);
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,
|
||
pixels),q);
|
||
}
|
||
|
||
else
|
||
{
|
||
SetPixelRed(large_image,GetPixelRed(image,n),q);
|
||
SetPixelGreen(large_image,GetPixelGreen(image,n),
|
||
q);
|
||
SetPixelBlue(large_image,GetPixelBlue(image,n),
|
||
q);
|
||
SetPixelAlpha(large_image,GetPixelAlpha(image,n),
|
||
q);
|
||
}
|
||
|
||
if (magn_methy == 5)
|
||
{
|
||
SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
|
||
(GetPixelAlpha(image,n)
|
||
-GetPixelAlpha(image,pixels))
|
||
+m))/((ssize_t) (m*2))
|
||
+GetPixelAlpha(image,pixels)),q);
|
||
}
|
||
}
|
||
n+=GetPixelChannels(image);
|
||
q+=GetPixelChannels(large_image);
|
||
pixels+=GetPixelChannels(image);
|
||
} /* x */
|
||
|
||
if (SyncAuthenticPixels(large_image,exception) == 0)
|
||
break;
|
||
|
||
} /* i */
|
||
} /* y */
|
||
|
||
prev=(Quantum *) RelinquishMagickMemory(prev);
|
||
next=(Quantum *) RelinquishMagickMemory(next);
|
||
|
||
length=image->columns;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Delete original image");
|
||
|
||
DeleteImageFromList(&image);
|
||
|
||
image=large_image;
|
||
|
||
mng_info->image=image;
|
||
|
||
/* magnify the columns */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Magnify the columns to %.20g",
|
||
(double) image->columns);
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
Quantum
|
||
*pixels;
|
||
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
pixels=q+(image->columns-length)*GetPixelChannels(image);
|
||
n=pixels+GetPixelChannels(image);
|
||
|
||
for (x=(ssize_t) (image->columns-length);
|
||
x < (ssize_t) image->columns; x++)
|
||
{
|
||
/* To do: Rewrite using Get/Set***PixelChannel() */
|
||
|
||
if (x == (ssize_t) (image->columns-length))
|
||
m=(ssize_t) mng_info->magn_ml;
|
||
|
||
else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
|
||
m=(ssize_t) mng_info->magn_mr;
|
||
|
||
else if (magn_methx <= 1 &&
|
||
x == (ssize_t) image->columns-1)
|
||
m=(ssize_t) mng_info->magn_mr;
|
||
|
||
else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
|
||
m=1;
|
||
|
||
else
|
||
m=(ssize_t) mng_info->magn_mx;
|
||
|
||
for (i=0; i < m; i++)
|
||
{
|
||
if (magn_methx <= 1)
|
||
{
|
||
/* replicate previous */
|
||
SetPixelRed(image,GetPixelRed(image,pixels),q);
|
||
SetPixelGreen(image,GetPixelGreen(image,pixels),q);
|
||
SetPixelBlue(image,GetPixelBlue(image,pixels),q);
|
||
SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
|
||
}
|
||
|
||
else if (magn_methx == 2 || magn_methx == 4)
|
||
{
|
||
if (i == 0)
|
||
{
|
||
SetPixelRed(image,GetPixelRed(image,pixels),q);
|
||
SetPixelGreen(image,GetPixelGreen(image,pixels),q);
|
||
SetPixelBlue(image,GetPixelBlue(image,pixels),q);
|
||
SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
|
||
}
|
||
|
||
/* To do: Rewrite using Get/Set***PixelChannel() */
|
||
else
|
||
{
|
||
/* Interpolate */
|
||
SetPixelRed(image,(QM) ((2*i*(
|
||
GetPixelRed(image,n)
|
||
-GetPixelRed(image,pixels))+m)
|
||
/((ssize_t) (m*2))+
|
||
GetPixelRed(image,pixels)),q);
|
||
|
||
SetPixelGreen(image,(QM) ((2*i*(
|
||
GetPixelGreen(image,n)
|
||
-GetPixelGreen(image,pixels))+m)
|
||
/((ssize_t) (m*2))+
|
||
GetPixelGreen(image,pixels)),q);
|
||
|
||
SetPixelBlue(image,(QM) ((2*i*(
|
||
GetPixelBlue(image,n)
|
||
-GetPixelBlue(image,pixels))+m)
|
||
/((ssize_t) (m*2))+
|
||
GetPixelBlue(image,pixels)),q);
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
SetPixelAlpha(image,(QM) ((2*i*(
|
||
GetPixelAlpha(image,n)
|
||
-GetPixelAlpha(image,pixels))+m)
|
||
/((ssize_t) (m*2))+
|
||
GetPixelAlpha(image,pixels)),q);
|
||
}
|
||
|
||
if (magn_methx == 4)
|
||
{
|
||
/* Replicate nearest */
|
||
if (i <= ((m+1) << 1))
|
||
{
|
||
SetPixelAlpha(image,
|
||
GetPixelAlpha(image,pixels)+0,q);
|
||
}
|
||
else
|
||
{
|
||
SetPixelAlpha(image,
|
||
GetPixelAlpha(image,n)+0,q);
|
||
}
|
||
}
|
||
}
|
||
|
||
else /* if (magn_methx == 3 || magn_methx == 5) */
|
||
{
|
||
/* Replicate nearest */
|
||
if (i <= ((m+1) << 1))
|
||
{
|
||
SetPixelRed(image,GetPixelRed(image,pixels),q);
|
||
SetPixelGreen(image,GetPixelGreen(image,
|
||
pixels),q);
|
||
SetPixelBlue(image,GetPixelBlue(image,pixels),q);
|
||
SetPixelAlpha(image,GetPixelAlpha(image,
|
||
pixels),q);
|
||
}
|
||
|
||
else
|
||
{
|
||
SetPixelRed(image,GetPixelRed(image,n),q);
|
||
SetPixelGreen(image,GetPixelGreen(image,n),q);
|
||
SetPixelBlue(image,GetPixelBlue(image,n),q);
|
||
SetPixelAlpha(image,GetPixelAlpha(image,n),q);
|
||
}
|
||
|
||
if (magn_methx == 5)
|
||
{
|
||
/* Interpolate */
|
||
SetPixelAlpha(image,
|
||
(QM) ((2*i*( GetPixelAlpha(image,n)
|
||
-GetPixelAlpha(image,pixels))+m)/
|
||
((ssize_t) (m*2))
|
||
+GetPixelAlpha(image,pixels)),q);
|
||
}
|
||
}
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
n+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 16)
|
||
if (magn_methx != 1 || magn_methy != 1)
|
||
{
|
||
/*
|
||
Rescale pixels to Quantum
|
||
*/
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,
|
||
exception);
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=(ssize_t) image->columns-1; x >= 0; x--)
|
||
{
|
||
SetPixelRed(image,ScaleShortToQuantum(
|
||
GetPixelRed(image,q)),q);
|
||
SetPixelGreen(image,ScaleShortToQuantum(
|
||
GetPixelGreen(image,q)),q);
|
||
SetPixelBlue(image,ScaleShortToQuantum(
|
||
GetPixelBlue(image,q)),q);
|
||
SetPixelAlpha(image,ScaleShortToQuantum(
|
||
GetPixelAlpha(image,q)),q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Finished MAGN processing");
|
||
}
|
||
}
|
||
|
||
/*
|
||
Crop_box is with respect to the upper left corner of the MNG.
|
||
*/
|
||
crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
|
||
crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
|
||
crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
|
||
crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
|
||
crop_box=mng_minimum_box(crop_box,mng_info->clip);
|
||
crop_box=mng_minimum_box(crop_box,mng_info->frame);
|
||
crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
|
||
if ((crop_box.left != (mng_info->image_box.left
|
||
+mng_info->x_off[object_id])) ||
|
||
(crop_box.right != (mng_info->image_box.right
|
||
+mng_info->x_off[object_id])) ||
|
||
(crop_box.top != (mng_info->image_box.top
|
||
+mng_info->y_off[object_id])) ||
|
||
(crop_box.bottom != (mng_info->image_box.bottom
|
||
+mng_info->y_off[object_id])))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Crop the PNG image");
|
||
|
||
if ((crop_box.left < crop_box.right) &&
|
||
(crop_box.top < crop_box.bottom))
|
||
{
|
||
Image
|
||
*im;
|
||
|
||
RectangleInfo
|
||
crop_info;
|
||
|
||
/*
|
||
Crop_info is with respect to the upper left corner of
|
||
the image.
|
||
*/
|
||
crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
|
||
crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
|
||
crop_info.width=(size_t) (crop_box.right-crop_box.left);
|
||
crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
|
||
image->page.width=image->columns;
|
||
image->page.height=image->rows;
|
||
image->page.x=0;
|
||
image->page.y=0;
|
||
im=CropImage(image,&crop_info,exception);
|
||
|
||
if (im != (Image *) NULL)
|
||
{
|
||
image->columns=im->columns;
|
||
image->rows=im->rows;
|
||
im=DestroyImage(im);
|
||
image->page.width=image->columns;
|
||
image->page.height=image->rows;
|
||
image->page.x=crop_box.left;
|
||
image->page.y=crop_box.top;
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
/*
|
||
No pixels in crop area. The MNG spec still requires
|
||
a layer, though, so make a single transparent pixel in
|
||
the top left corner.
|
||
*/
|
||
image->columns=1;
|
||
image->rows=1;
|
||
image->colors=2;
|
||
(void) SetImageBackgroundColor(image,exception);
|
||
image->page.width=1;
|
||
image->page.height=1;
|
||
image->page.x=0;
|
||
image->page.y=0;
|
||
}
|
||
}
|
||
#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
|
||
image=mng_info->image;
|
||
#endif
|
||
}
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 16)
|
||
/* PNG does not handle depths greater than 16 so reduce it even
|
||
* if lossy.
|
||
*/
|
||
if (image->depth > 16)
|
||
image->depth=16;
|
||
#endif
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 8)
|
||
if (image->depth > 8)
|
||
{
|
||
/* To do: fill low byte properly */
|
||
image->depth=16;
|
||
}
|
||
|
||
if (LosslessReduceDepthOK(image,exception) != MagickFalse)
|
||
image->depth = 8;
|
||
#endif
|
||
|
||
if (image_info->number_scenes != 0)
|
||
{
|
||
if (mng_info->scenes_found >
|
||
(ssize_t) (image_info->first_scene+image_info->number_scenes))
|
||
break;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Finished reading image datastream.");
|
||
|
||
} while (LocaleCompare(image_info->magick,"MNG") == 0);
|
||
|
||
(void) CloseBlob(image);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Finished reading all image datastreams.");
|
||
|
||
#if defined(MNG_INSERT_LAYERS)
|
||
if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
|
||
(mng_info->mng_height))
|
||
{
|
||
/*
|
||
Insert a background layer if nothing else was found.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" No images found. Inserting a background layer.");
|
||
|
||
if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
|
||
{
|
||
/*
|
||
Allocate next image structure.
|
||
*/
|
||
AcquireNextImage(image_info,image,exception);
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Allocation failed, returning NULL.");
|
||
|
||
return(DestroyImageList(image));;
|
||
}
|
||
image=SyncNextImageInList(image);
|
||
}
|
||
image->columns=mng_info->mng_width;
|
||
image->rows=mng_info->mng_height;
|
||
image->page.width=mng_info->mng_width;
|
||
image->page.height=mng_info->mng_height;
|
||
image->page.x=0;
|
||
image->page.y=0;
|
||
image->background_color=mng_background_color;
|
||
image->alpha_trait=UndefinedPixelTrait;
|
||
|
||
if (image_info->ping == MagickFalse)
|
||
(void) SetImageBackgroundColor(image,exception);
|
||
|
||
mng_info->image_found++;
|
||
}
|
||
#endif
|
||
image->iterations=mng_iterations;
|
||
|
||
if (mng_iterations == 1)
|
||
image->start_loop=MagickTrue;
|
||
|
||
while (GetPreviousImageInList(image) != (Image *) NULL)
|
||
{
|
||
image_count++;
|
||
if (image_count > 10*mng_info->image_found)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
|
||
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"Linked list is corrupted, beginning of list not found",
|
||
"`%s'",image_info->filename);
|
||
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
image=GetPreviousImageInList(image);
|
||
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
|
||
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
|
||
image_info->filename);
|
||
}
|
||
}
|
||
|
||
if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
|
||
GetNextImageInList(image) ==
|
||
(Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" First image null");
|
||
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"image->next for first image is NULL but shouldn't be.",
|
||
"`%s'",image_info->filename);
|
||
}
|
||
|
||
if (mng_info->image_found == 0)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" No visible images found.");
|
||
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderError,"No visible images in file","`%s'",image_info->filename);
|
||
|
||
return(DestroyImageList(image));
|
||
}
|
||
|
||
if (mng_info->ticks_per_second)
|
||
final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
|
||
final_delay/mng_info->ticks_per_second;
|
||
|
||
else
|
||
image->start_loop=MagickTrue;
|
||
|
||
/* Find final nonzero image delay */
|
||
final_image_delay=0;
|
||
|
||
while (GetNextImageInList(image) != (Image *) NULL)
|
||
{
|
||
if (image->delay)
|
||
final_image_delay=image->delay;
|
||
|
||
image=GetNextImageInList(image);
|
||
}
|
||
|
||
if (final_delay < final_image_delay)
|
||
final_delay=final_image_delay;
|
||
|
||
image->delay=final_delay;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->delay=%.20g, final_delay=%.20g",(double) image->delay,
|
||
(double) final_delay);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
int
|
||
scene;
|
||
|
||
scene=0;
|
||
image=GetFirstImageInList(image);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Before coalesce:");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" scene 0 delay=%.20g",(double) image->delay);
|
||
|
||
while (GetNextImageInList(image) != (Image *) NULL)
|
||
{
|
||
image=GetNextImageInList(image);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" scene %.20g delay=%.20g",(double) scene++,
|
||
(double) image->delay);
|
||
}
|
||
}
|
||
|
||
image=GetFirstImageInList(image);
|
||
#ifdef MNG_COALESCE_LAYERS
|
||
if (insert_layers && image->next)
|
||
{
|
||
Image
|
||
*next_image,
|
||
*next;
|
||
|
||
size_t
|
||
scene;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Coalesce Images");
|
||
|
||
scene=image->scene;
|
||
next_image=CoalesceImages(image,exception);
|
||
image=DestroyImageList(image);
|
||
if (next_image == (Image *) NULL)
|
||
return((Image *) NULL);
|
||
image=next_image;
|
||
|
||
for (next=image; next != (Image *) NULL; next=next_image)
|
||
{
|
||
next->page.width=mng_info->mng_width;
|
||
next->page.height=mng_info->mng_height;
|
||
next->page.x=0;
|
||
next->page.y=0;
|
||
next->scene=scene++;
|
||
next_image=GetNextImageInList(next);
|
||
|
||
if (next_image == (Image *) NULL)
|
||
break;
|
||
|
||
if (next->delay == 0)
|
||
{
|
||
scene--;
|
||
next_image->previous=GetPreviousImageInList(next);
|
||
if (GetPreviousImageInList(next) == (Image *) NULL)
|
||
image=next_image;
|
||
else
|
||
next->previous->next=next_image;
|
||
next=DestroyImage(next);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
while (GetNextImageInList(image) != (Image *) NULL)
|
||
image=GetNextImageInList(image);
|
||
|
||
image->dispose=BackgroundDispose;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
int
|
||
scene;
|
||
|
||
scene=0;
|
||
image=GetFirstImageInList(image);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" After coalesce:");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
|
||
(double) image->dispose);
|
||
|
||
while (GetNextImageInList(image) != (Image *) NULL)
|
||
{
|
||
image=GetNextImageInList(image);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
|
||
(double) image->delay,(double) image->dispose);
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit ReadOneMNGImage();");
|
||
|
||
return(image);
|
||
}
|
||
|
||
static Image *ReadMNGImage(const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
Image
|
||
*image;
|
||
|
||
MagickBooleanType
|
||
logging,
|
||
status;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
/* Open image file. */
|
||
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
|
||
image_info->filename);
|
||
assert(exception != (ExceptionInfo *) NULL);
|
||
assert(exception->signature == MagickCoreSignature);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
|
||
image=AcquireImage(image_info,exception);
|
||
mng_info=(MngInfo *) NULL;
|
||
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
|
||
|
||
if (status == MagickFalse)
|
||
return(DestroyImageList(image));
|
||
|
||
/* Allocate a MngInfo structure. */
|
||
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
|
||
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
/* Initialize members of the MngInfo structure. */
|
||
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
mng_info->image=image;
|
||
image=ReadOneMNGImage(mng_info,image_info,exception);
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
|
||
if (image == (Image *) NULL)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"exit ReadMNGImage() with error");
|
||
|
||
return((Image *) NULL);
|
||
}
|
||
(void) CloseBlob(image);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
|
||
|
||
return(GetFirstImageInList(image));
|
||
}
|
||
#else /* PNG_LIBPNG_VER > 10011 */
|
||
static Image *ReadPNGImage(const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
printf("Your PNG library is too old: You have libpng-%s\n",
|
||
PNG_LIBPNG_VER_STRING);
|
||
|
||
(void) ThrowMagickException(exception,GetMagickModule(),CoderError,
|
||
"PNG library is too old","`%s'",image_info->filename);
|
||
|
||
return(Image *) NULL;
|
||
}
|
||
|
||
static Image *ReadMNGImage(const ImageInfo *image_info,
|
||
ExceptionInfo *exception)
|
||
{
|
||
return(ReadPNGImage(image_info,exception));
|
||
}
|
||
#endif /* PNG_LIBPNG_VER > 10011 */
|
||
#endif
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% R e g i s t e r P N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% RegisterPNGImage() adds properties for the PNG image format to
|
||
% the list of supported formats. The properties include the image format
|
||
% tag, a method to read and/or write the format, whether the format
|
||
% supports the saving of more than one frame to the same file or blob,
|
||
% whether the format supports native in-memory I/O, and a brief
|
||
% description of the format.
|
||
%
|
||
% The format of the RegisterPNGImage method is:
|
||
%
|
||
% size_t RegisterPNGImage(void)
|
||
%
|
||
*/
|
||
ModuleExport size_t RegisterPNGImage(void)
|
||
{
|
||
char
|
||
version[MagickPathExtent];
|
||
|
||
MagickInfo
|
||
*entry;
|
||
|
||
static const char
|
||
PNGNote[] =
|
||
{
|
||
"See http://www.libpng.org/ for details about the PNG format."
|
||
},
|
||
|
||
JNGNote[] =
|
||
{
|
||
"See http://www.libpng.org/pub/mng/ for details about the JNG\n"
|
||
"format."
|
||
},
|
||
|
||
MNGNote[] =
|
||
{
|
||
"See http://www.libpng.org/pub/mng/ for details about the MNG\n"
|
||
"format."
|
||
};
|
||
|
||
*version='\0';
|
||
|
||
#if defined(PNG_LIBPNG_VER_STRING)
|
||
(void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
|
||
(void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
|
||
MagickPathExtent);
|
||
|
||
if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
|
||
{
|
||
(void) ConcatenateMagickString(version,",",MagickPathExtent);
|
||
(void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
|
||
MagickPathExtent);
|
||
}
|
||
#endif
|
||
|
||
entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadMNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WriteMNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsMNG;
|
||
|
||
if (*version != '\0')
|
||
entry->version=ConstantString(version);
|
||
|
||
entry->mime_type=ConstantString("video/x-mng");
|
||
entry->note=ConstantString(MNGNote);
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
|
||
if (*version != '\0')
|
||
entry->version=ConstantString(version);
|
||
|
||
entry->note=ConstantString(PNGNote);
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG8",
|
||
"8-bit indexed with optional binary transparency");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG24",
|
||
"opaque or binary transparent 24-bit RGB");
|
||
*version='\0';
|
||
|
||
#if defined(ZLIB_VERSION)
|
||
(void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
|
||
(void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
|
||
|
||
if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
|
||
{
|
||
(void) ConcatenateMagickString(version,",",MagickPathExtent);
|
||
(void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
|
||
}
|
||
#endif
|
||
|
||
if (*version != '\0')
|
||
entry->version=ConstantString(version);
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG48",
|
||
"opaque or binary transparent 48-bit RGB");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","PNG00",
|
||
"PNG inheriting bit-depth, color-type from original, if possible");
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadPNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WritePNGImage;
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsPNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/png");
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
|
||
|
||
#if defined(JNG_SUPPORTED)
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
entry->decoder=(DecodeImageHandler *) ReadJNGImage;
|
||
entry->encoder=(EncodeImageHandler *) WriteJNGImage;
|
||
#endif
|
||
#endif
|
||
|
||
entry->magick=(IsImageFormatHandler *) IsJNG;
|
||
entry->flags|=CoderDecoderSeekableStreamFlag;
|
||
entry->flags^=CoderAdjoinFlag;
|
||
entry->mime_type=ConstantString("image/x-jng");
|
||
entry->note=ConstantString(JNGNote);
|
||
(void) RegisterMagickInfo(entry);
|
||
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
ping_semaphore=AcquireSemaphoreInfo();
|
||
#endif
|
||
|
||
return(MagickImageCoderSignature);
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% U n r e g i s t e r P N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% UnregisterPNGImage() removes format registrations made by the
|
||
% PNG module from the list of supported formats.
|
||
%
|
||
% The format of the UnregisterPNGImage method is:
|
||
%
|
||
% UnregisterPNGImage(void)
|
||
%
|
||
*/
|
||
ModuleExport void UnregisterPNGImage(void)
|
||
{
|
||
(void) UnregisterMagickInfo("MNG");
|
||
(void) UnregisterMagickInfo("PNG");
|
||
(void) UnregisterMagickInfo("PNG8");
|
||
(void) UnregisterMagickInfo("PNG24");
|
||
(void) UnregisterMagickInfo("PNG32");
|
||
(void) UnregisterMagickInfo("PNG48");
|
||
(void) UnregisterMagickInfo("PNG64");
|
||
(void) UnregisterMagickInfo("PNG00");
|
||
(void) UnregisterMagickInfo("JNG");
|
||
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
if (ping_semaphore != (SemaphoreInfo *) NULL)
|
||
RelinquishSemaphoreInfo(&ping_semaphore);
|
||
#endif
|
||
}
|
||
|
||
#if defined(MAGICKCORE_PNG_DELEGATE)
|
||
#if PNG_LIBPNG_VER > 10011
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% W r i t e M N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% WriteMNGImage() writes an image in the Portable Network Graphics
|
||
% Group's "Multiple-image Network Graphics" encoded image format.
|
||
%
|
||
% MNG support written by Glenn Randers-Pehrson, glennrp@image...
|
||
%
|
||
% The format of the WriteMNGImage method is:
|
||
%
|
||
% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
|
||
% Image *image,ExceptionInfo *exception)
|
||
%
|
||
% A description of each parameter follows.
|
||
%
|
||
% o image_info: the image info.
|
||
%
|
||
% o image: The image.
|
||
%
|
||
% o exception: return any errors or warnings in this structure.
|
||
%
|
||
% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
|
||
% "To do" under ReadPNGImage):
|
||
%
|
||
% 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.
|
||
%
|
||
% Write the iCCP chunk at MNG level when (icc profile length > 0)
|
||
%
|
||
% Improve selection of color type (use indexed-color or indexed-color
|
||
% with tRNS when 256 or fewer unique RGBA values are present).
|
||
%
|
||
% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
|
||
% This will be complicated if we limit ourselves to generating MNG-LC
|
||
% files. For now we ignore disposal method 3 and simply overlay the next
|
||
% image on it.
|
||
%
|
||
% Check for identical PLTE's or PLTE/tRNS combinations and use a
|
||
% global MNG PLTE or PLTE/tRNS combination when appropriate.
|
||
% [mostly done 15 June 1999 but still need to take care of tRNS]
|
||
%
|
||
% Check for identical sRGB and replace with a global sRGB (and remove
|
||
% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
|
||
% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
|
||
% local gAMA/cHRM with local sRGB if appropriate).
|
||
%
|
||
% Check for identical sBIT chunks and write global ones.
|
||
%
|
||
% Provide option to skip writing the signature tEXt chunks.
|
||
%
|
||
% Use signatures to detect identical objects and reuse the first
|
||
% instance of such objects instead of writing duplicate objects.
|
||
%
|
||
% Use a smaller-than-32k value of compression window size when
|
||
% appropriate.
|
||
%
|
||
% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
|
||
% ancillary text chunks and save profiles.
|
||
%
|
||
% Provide an option to force LC files (to ensure exact framing rate)
|
||
% instead of VLC.
|
||
%
|
||
% Provide an option to force VLC files instead of LC, even when offsets
|
||
% are present. This will involve expanding the embedded images with a
|
||
% transparent region at the top and/or left.
|
||
*/
|
||
|
||
static void
|
||
Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
|
||
png_info *ping_info, unsigned char *profile_type, unsigned char
|
||
*profile_description, unsigned char *profile_data, png_uint_32 length,
|
||
ExceptionInfo *exception)
|
||
{
|
||
png_charp
|
||
dp;
|
||
|
||
png_textp
|
||
text;
|
||
|
||
png_uint_32
|
||
allocated_length,
|
||
description_length;
|
||
|
||
ssize_t
|
||
i;
|
||
|
||
unsigned char
|
||
hex[16] =
|
||
{ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
|
||
*sp;
|
||
|
||
if (length > 1)
|
||
{
|
||
if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
|
||
return;
|
||
}
|
||
if (image_info->verbose != MagickFalse)
|
||
{
|
||
(void) printf("writing raw profile: type=%s, length=%.20g\n",
|
||
(char *) profile_type, (double) length);
|
||
}
|
||
description_length=(png_uint_32) strlen((const char *) profile_description);
|
||
allocated_length=(png_uint_32) (2*length+(length >> 5)+description_length+
|
||
20);
|
||
if (allocated_length < length)
|
||
{
|
||
(void) ThrowMagickException(exception,GetMagickModule(),CoderError,
|
||
"maximum profile length exceeded","`%s'",image_info->filename);
|
||
return;
|
||
}
|
||
#if PNG_LIBPNG_VER >= 10400
|
||
text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
|
||
#else
|
||
text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
|
||
#endif
|
||
#if PNG_LIBPNG_VER >= 10400
|
||
text[0].text=(png_charp) png_malloc(ping,(png_alloc_size_t)
|
||
allocated_length);
|
||
text[0].key=(png_charp) png_malloc(ping,(png_alloc_size_t) 80);
|
||
#else
|
||
text[0].text=(png_charp) png_malloc(ping,(png_size_t) allocated_length);
|
||
text[0].key=(png_charp) png_malloc(ping,(png_size_t) 80);
|
||
#endif
|
||
text[0].key[0]='\0';
|
||
(void) ConcatenateMagickString(text[0].key,"Raw profile type ",
|
||
MagickPathExtent);
|
||
(void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
|
||
sp=profile_data;
|
||
dp=text[0].text;
|
||
*dp++='\n';
|
||
(void) CopyMagickString(dp,(const char *) profile_description,
|
||
allocated_length);
|
||
dp+=description_length;
|
||
*dp++='\n';
|
||
(void) FormatLocaleString(dp,allocated_length-
|
||
(png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
|
||
dp+=8;
|
||
|
||
for (i=0; i < (ssize_t) length; i++)
|
||
{
|
||
if (i%36 == 0)
|
||
*dp++='\n';
|
||
*(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
|
||
*(dp++)=(char) hex[((*sp++ ) & 0x0f)];
|
||
}
|
||
|
||
*dp++='\n';
|
||
*dp='\0';
|
||
text[0].text_length=(png_size_t) (dp-text[0].text);
|
||
text[0].compression=image_info->compression == NoCompression ||
|
||
(image_info->compression == UndefinedCompression &&
|
||
text[0].text_length < 128) ? -1 : 0;
|
||
|
||
if (text[0].text_length <= allocated_length)
|
||
png_set_text(ping,ping_info,text,1);
|
||
|
||
png_free(ping,text[0].text);
|
||
png_free(ping,text[0].key);
|
||
png_free(ping,text);
|
||
}
|
||
|
||
static inline MagickBooleanType IsColorEqual(const Image *image,
|
||
const Quantum *p, const PixelInfo *q)
|
||
{
|
||
MagickRealType
|
||
blue,
|
||
green,
|
||
red;
|
||
|
||
red=(MagickRealType) GetPixelRed(image,p);
|
||
green=(MagickRealType) GetPixelGreen(image,p);
|
||
blue=(MagickRealType) GetPixelBlue(image,p);
|
||
if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
|
||
(AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
|
||
(AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
|
||
return(MagickTrue);
|
||
return(MagickFalse);
|
||
}
|
||
|
||
#if defined(PNG_tIME_SUPPORTED)
|
||
static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
|
||
const char *timestamp,ExceptionInfo *exception)
|
||
{
|
||
int
|
||
ret;
|
||
|
||
int
|
||
day,
|
||
hour,
|
||
minute,
|
||
month,
|
||
second,
|
||
year;
|
||
|
||
int
|
||
addhours=0,
|
||
addminutes=0;
|
||
|
||
png_time
|
||
ptime;
|
||
|
||
assert(timestamp != (const char *) NULL);
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing tIME chunk: timestamp property is %30s\n",timestamp);
|
||
ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
|
||
&minute, &second);
|
||
addhours=0;
|
||
addminutes=0;
|
||
ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
|
||
&minute, &second, &addhours, &addminutes);
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Date format specified for png:tIME=%s" ,timestamp);
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
|
||
ret,year,month,day,hour,minute,second,addhours,addminutes);
|
||
if (ret < 6)
|
||
{
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Invalid date, ret=%d",ret);
|
||
(void) ThrowMagickException(exception,GetMagickModule(),CoderError,
|
||
"Invalid date format specified for png:tIME","`%s' (ret=%d)",
|
||
image->filename,ret);
|
||
return;
|
||
}
|
||
if (addhours < 0)
|
||
{
|
||
addhours+=24;
|
||
addminutes=-addminutes;
|
||
day--;
|
||
}
|
||
hour+=addhours;
|
||
minute+=addminutes;
|
||
if (day == 0)
|
||
{
|
||
month--;
|
||
day=31;
|
||
if(month == 2)
|
||
day=28;
|
||
else
|
||
{
|
||
if(month == 4 || month == 6 || month == 9 || month == 11)
|
||
day=30;
|
||
else
|
||
day=31;
|
||
}
|
||
}
|
||
if (month == 0)
|
||
{
|
||
month++;
|
||
year--;
|
||
}
|
||
if (minute > 59)
|
||
{
|
||
hour++;
|
||
minute-=60;
|
||
}
|
||
if (hour > 23)
|
||
{
|
||
day ++;
|
||
hour -=24;
|
||
}
|
||
if (hour < 0)
|
||
{
|
||
day --;
|
||
hour +=24;
|
||
}
|
||
/* To do: fix this for leap years */
|
||
if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
|
||
month == 9 || month == 11) && day > 30))
|
||
{
|
||
month++;
|
||
day = 1;
|
||
}
|
||
if (month > 12)
|
||
{
|
||
year++;
|
||
month=1;
|
||
}
|
||
|
||
ptime.year = year;
|
||
ptime.month = month;
|
||
ptime.day = day;
|
||
ptime.hour = hour;
|
||
ptime.minute = minute;
|
||
ptime.second = second;
|
||
png_convert_from_time_t(&ptime,GetMagickTime());
|
||
LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
|
||
ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
|
||
ptime.second, addhours, addminutes);
|
||
png_set_tIME(ping,info,&ptime);
|
||
}
|
||
#endif
|
||
|
||
/* Write one PNG image */
|
||
static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
|
||
const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
|
||
{
|
||
char
|
||
im_vers[32],
|
||
libpng_runv[32],
|
||
libpng_vers[32],
|
||
zlib_runv[32],
|
||
zlib_vers[32];
|
||
|
||
Image
|
||
*image;
|
||
|
||
ImageInfo
|
||
*image_info;
|
||
|
||
char
|
||
*name,
|
||
s[2];
|
||
|
||
const char
|
||
*property,
|
||
*value;
|
||
|
||
const StringInfo
|
||
*profile;
|
||
|
||
int
|
||
num_passes,
|
||
pass,
|
||
ping_wrote_caNv;
|
||
|
||
png_byte
|
||
ping_trans_alpha[256];
|
||
|
||
png_color
|
||
palette[257];
|
||
|
||
png_color_16
|
||
ping_background,
|
||
ping_trans_color;
|
||
|
||
png_info
|
||
*ping_info;
|
||
|
||
png_struct
|
||
*ping;
|
||
|
||
png_uint_32
|
||
ping_height,
|
||
ping_width;
|
||
|
||
ssize_t
|
||
y;
|
||
|
||
MagickBooleanType
|
||
image_matte,
|
||
logging,
|
||
matte,
|
||
|
||
ping_have_blob,
|
||
ping_have_cheap_transparency,
|
||
ping_have_color,
|
||
ping_have_non_bw,
|
||
ping_have_PLTE,
|
||
ping_have_bKGD,
|
||
ping_have_eXIf,
|
||
ping_have_iCCP,
|
||
ping_have_pHYs,
|
||
ping_have_sRGB,
|
||
ping_have_tRNS,
|
||
|
||
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_tIME,
|
||
/* ping_exclude_tRNS, */
|
||
ping_exclude_caNv,
|
||
ping_exclude_zCCP, /* hex-encoded iCCP */
|
||
ping_exclude_zTXt,
|
||
|
||
ping_preserve_colormap,
|
||
ping_preserve_iCCP,
|
||
ping_need_colortype_warning,
|
||
|
||
status,
|
||
tried_332,
|
||
tried_333,
|
||
tried_444;
|
||
|
||
MemoryInfo
|
||
*volatile pixel_info;
|
||
|
||
QuantumInfo
|
||
*quantum_info;
|
||
|
||
PNGErrorInfo
|
||
error_info;
|
||
|
||
ssize_t
|
||
i,
|
||
x;
|
||
|
||
unsigned char
|
||
*ping_pixels;
|
||
|
||
volatile int
|
||
image_colors,
|
||
ping_bit_depth,
|
||
ping_color_type,
|
||
ping_interlace_method,
|
||
ping_compression_method,
|
||
ping_filter_method,
|
||
ping_num_trans;
|
||
|
||
volatile size_t
|
||
image_depth,
|
||
old_bit_depth;
|
||
|
||
size_t
|
||
quality,
|
||
rowbytes,
|
||
save_image_depth;
|
||
|
||
int
|
||
j,
|
||
number_colors,
|
||
number_opaque,
|
||
number_semitransparent,
|
||
number_transparent,
|
||
ping_pHYs_unit_type;
|
||
|
||
png_uint_32
|
||
ping_pHYs_x_resolution,
|
||
ping_pHYs_y_resolution;
|
||
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Enter WriteOnePNGImage()");
|
||
|
||
image = CloneImage(IMimage,0,0,MagickFalse,exception);
|
||
if (image == (Image *) NULL)
|
||
return(MagickFalse);
|
||
image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
|
||
|
||
/* Define these outside of the following "if logging()" block so they will
|
||
* show in debuggers.
|
||
*/
|
||
*im_vers='\0';
|
||
(void) ConcatenateMagickString(im_vers,
|
||
MagickLibVersionText,MagickPathExtent);
|
||
(void) ConcatenateMagickString(im_vers,
|
||
MagickLibAddendum,MagickPathExtent);
|
||
|
||
*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);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" IM version = %s", im_vers);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Libpng version = %s", libpng_vers);
|
||
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);
|
||
}
|
||
}
|
||
|
||
/* Initialize some stuff */
|
||
ping_bit_depth=0,
|
||
ping_color_type=0,
|
||
ping_interlace_method=0,
|
||
ping_compression_method=0,
|
||
ping_filter_method=0,
|
||
ping_num_trans = 0;
|
||
|
||
ping_background.red = 0;
|
||
ping_background.green = 0;
|
||
ping_background.blue = 0;
|
||
ping_background.gray = 0;
|
||
ping_background.index = 0;
|
||
|
||
ping_trans_color.red=0;
|
||
ping_trans_color.green=0;
|
||
ping_trans_color.blue=0;
|
||
ping_trans_color.gray=0;
|
||
|
||
ping_pHYs_unit_type = 0;
|
||
ping_pHYs_x_resolution = 0;
|
||
ping_pHYs_y_resolution = 0;
|
||
|
||
ping_have_blob=MagickFalse;
|
||
ping_have_cheap_transparency=MagickFalse;
|
||
ping_have_color=MagickTrue;
|
||
ping_have_non_bw=MagickTrue;
|
||
ping_have_PLTE=MagickFalse;
|
||
ping_have_bKGD=MagickFalse;
|
||
ping_have_eXIf=MagickTrue;
|
||
ping_have_iCCP=MagickFalse;
|
||
ping_have_pHYs=MagickFalse;
|
||
ping_have_sRGB=MagickFalse;
|
||
ping_have_tRNS=MagickFalse;
|
||
|
||
ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
|
||
ping_exclude_caNv=mng_info->ping_exclude_caNv;
|
||
ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
|
||
ping_exclude_date=mng_info->ping_exclude_date;
|
||
ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
|
||
ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
|
||
ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
|
||
/* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
|
||
ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
|
||
ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
|
||
ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
|
||
ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
|
||
ping_exclude_tIME=mng_info->ping_exclude_tIME;
|
||
/* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
|
||
ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
|
||
ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
|
||
|
||
ping_preserve_colormap = mng_info->ping_preserve_colormap;
|
||
ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
|
||
ping_need_colortype_warning = MagickFalse;
|
||
|
||
/* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
|
||
* i.e., eliminate the ICC profile and set image->rendering_intent.
|
||
* Note that this will not involve any changes to the actual pixels
|
||
* but merely passes information to applications that read the resulting
|
||
* PNG image.
|
||
*
|
||
* To do: recognize other variants of the sRGB profile, using the CRC to
|
||
* verify all recognized variants including the 7 already known.
|
||
*
|
||
* Work around libpng16+ rejecting some "known invalid sRGB profiles".
|
||
*
|
||
* Use something other than image->rendering_intent to record the fact
|
||
* that the sRGB profile was found.
|
||
*
|
||
* Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
|
||
* profile. Record the Blackpoint Compensation, if any.
|
||
*/
|
||
if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
|
||
{
|
||
ResetImageProfileIterator(image);
|
||
for (name=GetNextImageProfile(image); name != (char *) NULL; )
|
||
{
|
||
profile=GetImageProfile(image,name);
|
||
|
||
if (profile != (StringInfo *) NULL)
|
||
{
|
||
if ((LocaleCompare(name,"ICC") == 0) ||
|
||
(LocaleCompare(name,"ICM") == 0))
|
||
|
||
{
|
||
int
|
||
icheck,
|
||
got_crc=0;
|
||
|
||
|
||
png_uint_32
|
||
length,
|
||
profile_crc=0;
|
||
|
||
unsigned char
|
||
*data;
|
||
|
||
length=(png_uint_32) GetStringInfoLength(profile);
|
||
|
||
for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
|
||
{
|
||
if (length == sRGB_info[icheck].len)
|
||
{
|
||
if (got_crc == 0)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Got a %lu-byte ICC profile (potentially sRGB)",
|
||
(unsigned long) length);
|
||
|
||
data=GetStringInfoDatum(profile);
|
||
profile_crc=crc32(0,data,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);
|
||
}
|
||
ping_exclude_iCCP = MagickTrue;
|
||
ping_exclude_zCCP = MagickTrue;
|
||
ping_have_sRGB = MagickTrue;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (sRGB_info[icheck].len == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Got %lu-byte ICC profile not recognized as sRGB",
|
||
(unsigned long) length);
|
||
}
|
||
}
|
||
name=GetNextImageProfile(image);
|
||
}
|
||
}
|
||
|
||
number_opaque = 0;
|
||
number_semitransparent = 0;
|
||
number_transparent = 0;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (image->storage_class == UndefinedClass)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->storage_class=UndefinedClass");
|
||
if (image->storage_class == DirectClass)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->storage_class=DirectClass");
|
||
if (image->storage_class == PseudoClass)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->storage_class=PseudoClass");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
|
||
" image->taint=MagickTrue":
|
||
" image->taint=MagickFalse");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->gamma=%g", image->gamma);
|
||
}
|
||
|
||
if (image->storage_class == PseudoClass &&
|
||
(mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
|
||
mng_info->write_png48 || mng_info->write_png64 ||
|
||
(mng_info->write_png_colortype != 1 &&
|
||
mng_info->write_png_colortype != 5)))
|
||
{
|
||
(void) SyncImage(image,exception);
|
||
image->storage_class = DirectClass;
|
||
}
|
||
|
||
if (ping_preserve_colormap == MagickFalse)
|
||
{
|
||
if ((image->storage_class != PseudoClass) &&
|
||
(image->colormap != (PixelInfo *) NULL))
|
||
{
|
||
/* Free the bogus colormap; it can cause trouble later */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Freeing bogus colormap");
|
||
image->colormap=(PixelInfo *) RelinquishMagickMemory(
|
||
image->colormap);
|
||
}
|
||
}
|
||
|
||
if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
|
||
(void) TransformImageColorspace(image,sRGBColorspace,exception);
|
||
|
||
/*
|
||
Sometimes we get PseudoClass images whose RGB values don't match
|
||
the colors in the colormap. This code syncs the RGB values.
|
||
*/
|
||
image->depth=GetImageQuantumDepth(image,MagickFalse);
|
||
if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
|
||
(void) SyncImage(image,exception);
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
||
if (image->depth > 8)
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reducing PNG bit depth to 8 since this is a Q8 build.");
|
||
|
||
image->depth=8;
|
||
}
|
||
#endif
|
||
|
||
/* Respect the -depth option */
|
||
if (image->depth < 4)
|
||
{
|
||
Quantum
|
||
*r;
|
||
|
||
if (image->depth > 2)
|
||
{
|
||
/* Scale to 4-bit */
|
||
LBR04PacketRGBA(image->background_color);
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (r == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
LBR04PixelRGBA(r);
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
|
||
if (image->storage_class == PseudoClass && image->colormap != NULL)
|
||
{
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
LBR04PacketRGBA(image->colormap[i]);
|
||
}
|
||
}
|
||
}
|
||
else if (image->depth > 1)
|
||
{
|
||
/* Scale to 2-bit */
|
||
LBR02PacketRGBA(image->background_color);
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (r == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
LBR02PixelRGBA(r);
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
|
||
if (image->storage_class == PseudoClass && image->colormap != NULL)
|
||
{
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
LBR02PacketRGBA(image->colormap[i]);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Scale to 1-bit */
|
||
LBR01PacketRGBA(image->background_color);
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (r == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
LBR01PixelRGBA(r);
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
|
||
if (image->storage_class == PseudoClass && image->colormap != NULL)
|
||
{
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
LBR01PacketRGBA(image->colormap[i]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* To do: set to next higher multiple of 8 */
|
||
if (image->depth < 8)
|
||
image->depth=8;
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 16)
|
||
/* PNG does not handle depths greater than 16 so reduce it even
|
||
* if lossy
|
||
*/
|
||
if (image->depth > 8)
|
||
image->depth=16;
|
||
#endif
|
||
|
||
#if (MAGICKCORE_QUANTUM_DEPTH > 8)
|
||
if (image->depth > 8)
|
||
{
|
||
/* To do: fill low byte properly */
|
||
image->depth=16;
|
||
}
|
||
|
||
if (image->depth == 16 && mng_info->write_png_depth != 16)
|
||
if (mng_info->write_png8 ||
|
||
LosslessReduceDepthOK(image,exception) != MagickFalse)
|
||
image->depth = 8;
|
||
#endif
|
||
|
||
image_colors = (int) image->colors;
|
||
number_opaque = (int) image->colors;
|
||
number_transparent = 0;
|
||
number_semitransparent = 0;
|
||
|
||
if (mng_info->write_png_colortype &&
|
||
(mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
|
||
mng_info->write_png_colortype < 4 &&
|
||
image->alpha_trait == UndefinedPixelTrait)))
|
||
{
|
||
/* Avoid the expensive BUILD_PALETTE operation if we're sure that we
|
||
* are not going to need the result.
|
||
*/
|
||
if (mng_info->write_png_colortype == 1 ||
|
||
mng_info->write_png_colortype == 5)
|
||
ping_have_color=MagickFalse;
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
{
|
||
number_transparent = 2;
|
||
number_semitransparent = 1;
|
||
}
|
||
}
|
||
|
||
if (mng_info->write_png_colortype < 7)
|
||
{
|
||
/* BUILD_PALETTE
|
||
*
|
||
* Normally we run this just once, but in the case of writing PNG8
|
||
* we reduce the transparency to binary and run again, then if there
|
||
* are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
|
||
* RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
|
||
* palette. Then (To do) we take care of a final reduction that is only
|
||
* needed if there are still 256 colors present and one of them has both
|
||
* transparent and opaque instances.
|
||
*/
|
||
|
||
tried_332 = MagickFalse;
|
||
tried_333 = MagickFalse;
|
||
tried_444 = MagickFalse;
|
||
|
||
if (image->depth != GetImageDepth(image,exception))
|
||
(void) SetImageDepth(image,image->depth,exception);
|
||
for (j=0; j<6; j++)
|
||
{
|
||
/*
|
||
* Sometimes we get DirectClass images that have 256 colors or fewer.
|
||
* This code will build a colormap.
|
||
*
|
||
* Also, sometimes we get PseudoClass images with an out-of-date
|
||
* colormap. This code will replace the colormap with a new one.
|
||
* Sometimes we get PseudoClass images that have more than 256 colors.
|
||
* This code will delete the colormap and change the image to
|
||
* DirectClass.
|
||
*
|
||
* If image->alpha_trait is MagickFalse, we ignore the alpha channel
|
||
* even though it sometimes contains left-over non-opaque values.
|
||
*
|
||
* Also we gather some information (number of opaque, transparent,
|
||
* and semitransparent pixels, and whether the image has any non-gray
|
||
* pixels or only black-and-white pixels) that we might need later.
|
||
*
|
||
* Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
|
||
* we need to check for bogus non-opaque values, at least.
|
||
*/
|
||
|
||
int
|
||
n;
|
||
|
||
PixelInfo
|
||
opaque[260],
|
||
semitransparent[260],
|
||
transparent[260];
|
||
|
||
const Quantum
|
||
*r;
|
||
|
||
Quantum
|
||
*q;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Enter BUILD_PALETTE:");
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->columns=%.20g",(double) image->columns);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->rows=%.20g",(double) image->rows);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->alpha_trait=%.20g",(double) image->alpha_trait);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->depth=%.20g",(double) image->depth);
|
||
|
||
if (image->storage_class == PseudoClass && image->colormap != NULL)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Original colormap:");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" i (red,green,blue,alpha)");
|
||
|
||
for (i=0; i < (ssize_t) MagickMin(image->colors,256); i++)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" %d (%d,%d,%d,%d)",
|
||
(int) i,
|
||
(int) image->colormap[i].red,
|
||
(int) image->colormap[i].green,
|
||
(int) image->colormap[i].blue,
|
||
(int) image->colormap[i].alpha);
|
||
}
|
||
|
||
for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
|
||
{
|
||
if (i > 255)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" %d (%d,%d,%d,%d)",
|
||
(int) i,
|
||
(int) image->colormap[i].red,
|
||
(int) image->colormap[i].green,
|
||
(int) image->colormap[i].blue,
|
||
(int) image->colormap[i].alpha);
|
||
}
|
||
}
|
||
}
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->colors=%d",(int) image->colors);
|
||
|
||
if (image->colors == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" (zero means unknown)");
|
||
|
||
if (ping_preserve_colormap == MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Regenerate the colormap");
|
||
}
|
||
|
||
image_colors=0;
|
||
number_opaque = 0;
|
||
number_semitransparent = 0;
|
||
number_transparent = 0;
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
r=GetVirtualPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (r == (const Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (image->alpha_trait == UndefinedPixelTrait ||
|
||
GetPixelAlpha(image,r) == OpaqueAlpha)
|
||
{
|
||
if (number_opaque < 259)
|
||
{
|
||
if (number_opaque == 0)
|
||
{
|
||
GetPixelInfoPixel(image,r,opaque);
|
||
opaque[0].alpha=OpaqueAlpha;
|
||
number_opaque=1;
|
||
}
|
||
|
||
for (i=0; i< (ssize_t) number_opaque; i++)
|
||
{
|
||
if (IsColorEqual(image,r,opaque+i))
|
||
break;
|
||
}
|
||
|
||
if (i == (ssize_t) number_opaque && number_opaque < 259)
|
||
{
|
||
number_opaque++;
|
||
GetPixelInfoPixel(image,r,opaque+i);
|
||
opaque[i].alpha=OpaqueAlpha;
|
||
}
|
||
}
|
||
}
|
||
else if (GetPixelAlpha(image,r) == TransparentAlpha)
|
||
{
|
||
if (number_transparent < 259)
|
||
{
|
||
if (number_transparent == 0)
|
||
{
|
||
GetPixelInfoPixel(image,r,transparent);
|
||
ping_trans_color.red=(unsigned short)
|
||
GetPixelRed(image,r);
|
||
ping_trans_color.green=(unsigned short)
|
||
GetPixelGreen(image,r);
|
||
ping_trans_color.blue=(unsigned short)
|
||
GetPixelBlue(image,r);
|
||
ping_trans_color.gray=(unsigned short)
|
||
GetPixelGray(image,r);
|
||
number_transparent = 1;
|
||
}
|
||
|
||
for (i=0; i< (ssize_t) number_transparent; i++)
|
||
{
|
||
if (IsColorEqual(image,r,transparent+i))
|
||
break;
|
||
}
|
||
|
||
if (i == (ssize_t) number_transparent &&
|
||
number_transparent < 259)
|
||
{
|
||
number_transparent++;
|
||
GetPixelInfoPixel(image,r,transparent+i);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (number_semitransparent < 259)
|
||
{
|
||
if (number_semitransparent == 0)
|
||
{
|
||
GetPixelInfoPixel(image,r,semitransparent);
|
||
number_semitransparent = 1;
|
||
}
|
||
|
||
for (i=0; i< (ssize_t) number_semitransparent; i++)
|
||
{
|
||
if (IsColorEqual(image,r,semitransparent+i)
|
||
&& GetPixelAlpha(image,r) ==
|
||
semitransparent[i].alpha)
|
||
break;
|
||
}
|
||
|
||
if (i == (ssize_t) number_semitransparent &&
|
||
number_semitransparent < 259)
|
||
{
|
||
number_semitransparent++;
|
||
GetPixelInfoPixel(image,r,semitransparent+i);
|
||
}
|
||
}
|
||
}
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
}
|
||
|
||
if (mng_info->write_png8 == MagickFalse &&
|
||
ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
/* Add the background color to the palette, if it
|
||
* isn't already there.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Check colormap for background (%d,%d,%d)",
|
||
(int) image->background_color.red,
|
||
(int) image->background_color.green,
|
||
(int) image->background_color.blue);
|
||
}
|
||
if (number_opaque < 259)
|
||
{
|
||
for (i=0; i<number_opaque; i++)
|
||
{
|
||
if (opaque[i].red == image->background_color.red &&
|
||
opaque[i].green == image->background_color.green &&
|
||
opaque[i].blue == image->background_color.blue)
|
||
break;
|
||
}
|
||
if (i == number_opaque)
|
||
{
|
||
opaque[i] = image->background_color;
|
||
ping_background.index = i;
|
||
number_opaque++;
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background_color index is %d",(int) i);
|
||
}
|
||
|
||
}
|
||
}
|
||
else if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" No room in the colormap to add background color");
|
||
}
|
||
|
||
image_colors=number_opaque+number_transparent+number_semitransparent;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (image_colors > 256)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image has more than 256 colors");
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image has %d colors",image_colors);
|
||
}
|
||
|
||
if (ping_preserve_colormap != MagickFalse)
|
||
break;
|
||
|
||
if (mng_info->write_png_colortype != 7) /* We won't need this info */
|
||
{
|
||
ping_have_color=MagickFalse;
|
||
ping_have_non_bw=MagickFalse;
|
||
|
||
if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
"incompatible colorspace");
|
||
ping_have_color=MagickTrue;
|
||
ping_have_non_bw=MagickTrue;
|
||
}
|
||
|
||
if(image_colors > 256)
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
r=q;
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
|
||
GetPixelRed(image,r) != GetPixelBlue(image,r))
|
||
{
|
||
ping_have_color=MagickTrue;
|
||
ping_have_non_bw=MagickTrue;
|
||
break;
|
||
}
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (ping_have_color != MagickFalse)
|
||
break;
|
||
|
||
/* Worst case is black-and-white; we are looking at every
|
||
* pixel twice.
|
||
*/
|
||
|
||
if (ping_have_non_bw == MagickFalse)
|
||
{
|
||
r=q;
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelRed(image,r) != 0 &&
|
||
GetPixelRed(image,r) != QuantumRange)
|
||
{
|
||
ping_have_non_bw=MagickTrue;
|
||
break;
|
||
}
|
||
r+=GetPixelChannels(image);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (image_colors < 257)
|
||
{
|
||
PixelInfo
|
||
colormap[260];
|
||
|
||
/*
|
||
* Initialize image colormap.
|
||
*/
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Sort the new colormap");
|
||
|
||
/* Sort palette, transparent first */;
|
||
|
||
n = 0;
|
||
|
||
for (i=0; i<number_transparent; i++)
|
||
colormap[n++] = transparent[i];
|
||
|
||
for (i=0; i<number_semitransparent; i++)
|
||
colormap[n++] = semitransparent[i];
|
||
|
||
for (i=0; i<number_opaque; i++)
|
||
colormap[n++] = opaque[i];
|
||
|
||
ping_background.index +=
|
||
(number_transparent + number_semitransparent);
|
||
|
||
/* image_colors < 257; search the colormap instead of the pixels
|
||
* to get ping_have_color and ping_have_non_bw
|
||
*/
|
||
for (i=0; i<n; i++)
|
||
{
|
||
if (ping_have_color == MagickFalse)
|
||
{
|
||
if (colormap[i].red != colormap[i].green ||
|
||
colormap[i].red != colormap[i].blue)
|
||
{
|
||
ping_have_color=MagickTrue;
|
||
ping_have_non_bw=MagickTrue;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (ping_have_non_bw == MagickFalse)
|
||
{
|
||
if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
|
||
ping_have_non_bw=MagickTrue;
|
||
}
|
||
}
|
||
|
||
if ((mng_info->ping_exclude_tRNS == MagickFalse ||
|
||
(number_transparent == 0 && number_semitransparent == 0)) &&
|
||
(((mng_info->write_png_colortype-1) ==
|
||
PNG_COLOR_TYPE_PALETTE) ||
|
||
(mng_info->write_png_colortype == 0)))
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (n != (ssize_t) image_colors)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image_colors (%d) and n (%d) don't match",
|
||
image_colors, n);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" AcquireImageColormap");
|
||
}
|
||
|
||
image->colors = image_colors;
|
||
|
||
if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
|
||
{
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
ResourceLimitError,"MemoryAllocationFailed","`%s'",
|
||
image->filename);
|
||
break;
|
||
}
|
||
|
||
for (i=0; i< (ssize_t) image_colors; i++)
|
||
image->colormap[i] = colormap[i];
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->colors=%d (%d)",
|
||
(int) image->colors, image_colors);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Update the pixel indexes");
|
||
}
|
||
|
||
/* Sync the pixel indices with the new colormap */
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
for (i=0; i< (ssize_t) image_colors; i++)
|
||
{
|
||
if ((image->alpha_trait == UndefinedPixelTrait ||
|
||
image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
|
||
image->colormap[i].red == GetPixelRed(image,q) &&
|
||
image->colormap[i].green == GetPixelGreen(image,q) &&
|
||
image->colormap[i].blue == GetPixelBlue(image,q))
|
||
{
|
||
SetPixelIndex(image,i,q);
|
||
break;
|
||
}
|
||
}
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->colors=%d", (int) image->colors);
|
||
|
||
if (image->colormap != NULL)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" i (red,green,blue,alpha)");
|
||
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
if (i < 300 || i >= (ssize_t) image->colors - 10)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" %d (%d,%d,%d,%d)",
|
||
(int) i,
|
||
(int) image->colormap[i].red,
|
||
(int) image->colormap[i].green,
|
||
(int) image->colormap[i].blue,
|
||
(int) image->colormap[i].alpha);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (number_transparent < 257)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_transparent = %d",
|
||
number_transparent);
|
||
else
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_transparent > 256");
|
||
|
||
if (number_opaque < 257)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_opaque = %d",
|
||
number_opaque);
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_opaque > 256");
|
||
|
||
if (number_semitransparent < 257)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_semitransparent = %d",
|
||
number_semitransparent);
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" number_semitransparent > 256");
|
||
|
||
if (ping_have_non_bw == MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" All pixels and the background are black or white");
|
||
|
||
else if (ping_have_color == MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" All pixels and the background are gray");
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" At least one pixel or the background is non-gray");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Exit BUILD_PALETTE:");
|
||
}
|
||
|
||
if (mng_info->write_png8 == MagickFalse)
|
||
break;
|
||
|
||
/* Make any reductions necessary for the PNG8 format */
|
||
if (image_colors <= 256 &&
|
||
image_colors != 0 && image->colormap != NULL &&
|
||
number_semitransparent == 0 &&
|
||
number_transparent <= 1)
|
||
break;
|
||
|
||
/* PNG8 can't have semitransparent colors so we threshold the
|
||
* opacity to 0 or OpaqueOpacity, and PNG8 can only have one
|
||
* transparent color so if more than one is transparent we merge
|
||
* them into image->background_color.
|
||
*/
|
||
if (number_semitransparent != 0 || number_transparent > 1)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Thresholding the alpha channel to binary");
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
|
||
{
|
||
SetPixelViaPixelInfo(image,&image->background_color,q);
|
||
SetPixelAlpha(image,TransparentAlpha,q);
|
||
}
|
||
else
|
||
SetPixelAlpha(image,OpaqueAlpha,q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
|
||
if (image_colors != 0 && image_colors <= 256 &&
|
||
image->colormap != NULL)
|
||
for (i=0; i<image_colors; i++)
|
||
image->colormap[i].alpha =
|
||
(image->colormap[i].alpha > TransparentAlpha/2 ?
|
||
TransparentAlpha : OpaqueAlpha);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
/* PNG8 can't have more than 256 colors so we quantize the pixels and
|
||
* background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
|
||
* image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
|
||
* colors or less.
|
||
*/
|
||
if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the background color to 4-4-4");
|
||
|
||
tried_444 = MagickTrue;
|
||
|
||
LBR04PacketRGB(image->background_color);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the pixel colors to 4-4-4");
|
||
|
||
if (image->colormap == NULL)
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelAlpha(image,q) == OpaqueAlpha)
|
||
LBR04PixelRGB(q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
else /* Should not reach this; colormap already exists and
|
||
must be <= 256 */
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the colormap to 4-4-4");
|
||
|
||
for (i=0; i<image_colors; i++)
|
||
{
|
||
LBR04PacketRGB(image->colormap[i]);
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the background color to 3-3-3");
|
||
|
||
tried_333 = MagickTrue;
|
||
|
||
LBR03PacketRGB(image->background_color);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the pixel colors to 3-3-3-1");
|
||
|
||
if (image->colormap == NULL)
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelAlpha(image,q) == OpaqueAlpha)
|
||
LBR03RGB(q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
else /* Should not reach this; colormap already exists and
|
||
must be <= 256 */
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the colormap to 3-3-3-1");
|
||
for (i=0; i<image_colors; i++)
|
||
{
|
||
LBR03PacketRGB(image->colormap[i]);
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the background color to 3-3-2");
|
||
|
||
tried_332 = MagickTrue;
|
||
|
||
/* Red and green were already done so we only quantize the blue
|
||
* channel
|
||
*/
|
||
|
||
LBR02PacketBlue(image->background_color);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the pixel colors to 3-3-2-1");
|
||
|
||
if (image->colormap == NULL)
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelAlpha(image,q) == OpaqueAlpha)
|
||
LBR02PixelBlue(q);
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
else /* Should not reach this; colormap already exists and
|
||
must be <= 256 */
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Quantizing the colormap to 3-3-2-1");
|
||
for (i=0; i<image_colors; i++)
|
||
{
|
||
LBR02PacketBlue(image->colormap[i]);
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (image_colors == 0 || image_colors > 256)
|
||
{
|
||
/* Take care of special case with 256 opaque colors + 1 transparent
|
||
* color. We don't need to quantize to 2-3-2-1; we only need to
|
||
* eliminate one color, so we'll merge the two darkest red
|
||
* colors (0x49, 0, 0) -> (0x24, 0, 0).
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Merging two dark red background colors to 3-3-2-1");
|
||
|
||
if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
|
||
ScaleQuantumToChar(image->background_color.green) == 0x00 &&
|
||
ScaleQuantumToChar(image->background_color.blue) == 0x00)
|
||
{
|
||
image->background_color.red=ScaleCharToQuantum(0x24);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Merging two dark red pixel colors to 3-3-2-1");
|
||
|
||
if (image->colormap == NULL)
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
|
||
ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
|
||
ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
|
||
GetPixelAlpha(image,q) == OpaqueAlpha)
|
||
{
|
||
SetPixelRed(image,ScaleCharToQuantum(0x24),q);
|
||
}
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (SyncAuthenticPixels(image,exception) == MagickFalse)
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
for (i=0; i<image_colors; i++)
|
||
{
|
||
if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
|
||
ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
|
||
ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
|
||
{
|
||
image->colormap[i].red=ScaleCharToQuantum(0x24);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/* END OF BUILD_PALETTE */
|
||
|
||
/* If we are excluding the tRNS chunk and there is transparency,
|
||
* then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
|
||
* PNG.
|
||
*/
|
||
if (mng_info->ping_exclude_tRNS != MagickFalse &&
|
||
(number_transparent != 0 || number_semitransparent != 0))
|
||
{
|
||
unsigned int colortype=mng_info->write_png_colortype;
|
||
|
||
if (ping_have_color == MagickFalse)
|
||
mng_info->write_png_colortype = 5;
|
||
|
||
else
|
||
mng_info->write_png_colortype = 7;
|
||
|
||
if (colortype != 0 &&
|
||
mng_info->write_png_colortype != colortype)
|
||
ping_need_colortype_warning=MagickTrue;
|
||
|
||
}
|
||
|
||
/* See if cheap transparency is possible. It is only possible
|
||
* when there is a single transparent color, no semitransparent
|
||
* color, and no opaque color that has the same RGB components
|
||
* as the transparent color. We only need this information if
|
||
* we are writing a PNG with colortype 0 or 2, and we have not
|
||
* excluded the tRNS chunk.
|
||
*/
|
||
if (number_transparent == 1 &&
|
||
mng_info->write_png_colortype < 4)
|
||
{
|
||
ping_have_cheap_transparency = MagickTrue;
|
||
|
||
if (number_semitransparent != 0)
|
||
ping_have_cheap_transparency = MagickFalse;
|
||
|
||
else if (image_colors == 0 || image_colors > 256 ||
|
||
image->colormap == NULL)
|
||
{
|
||
const Quantum
|
||
*q;
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
q=GetVirtualPixels(image,0,y,image->columns,1, exception);
|
||
|
||
if (q == (Quantum *) NULL)
|
||
break;
|
||
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
if (GetPixelAlpha(image,q) != TransparentAlpha &&
|
||
(unsigned short) GetPixelRed(image,q) ==
|
||
ping_trans_color.red &&
|
||
(unsigned short) GetPixelGreen(image,q) ==
|
||
ping_trans_color.green &&
|
||
(unsigned short) GetPixelBlue(image,q) ==
|
||
ping_trans_color.blue)
|
||
{
|
||
ping_have_cheap_transparency = MagickFalse;
|
||
break;
|
||
}
|
||
|
||
q+=GetPixelChannels(image);
|
||
}
|
||
|
||
if (ping_have_cheap_transparency == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Assuming that image->colormap[0] is the one transparent color
|
||
* and that all others are opaque.
|
||
*/
|
||
if (image_colors > 1)
|
||
for (i=1; i<image_colors; i++)
|
||
if (image->colormap[i].red == image->colormap[0].red &&
|
||
image->colormap[i].green == image->colormap[0].green &&
|
||
image->colormap[i].blue == image->colormap[0].blue)
|
||
{
|
||
ping_have_cheap_transparency = MagickFalse;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (ping_have_cheap_transparency == MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Cheap transparency is not possible.");
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Cheap transparency is possible.");
|
||
}
|
||
}
|
||
else
|
||
ping_have_cheap_transparency = MagickFalse;
|
||
|
||
image_depth=image->depth;
|
||
|
||
quantum_info = (QuantumInfo *) NULL;
|
||
number_colors=0;
|
||
image_colors=(int) image->colors;
|
||
image_matte=image->alpha_trait !=
|
||
UndefinedPixelTrait ? MagickTrue : MagickFalse;
|
||
|
||
if (mng_info->write_png_colortype < 5)
|
||
mng_info->IsPalette=image->storage_class == PseudoClass &&
|
||
image_colors <= 256 && image->colormap != NULL;
|
||
else
|
||
mng_info->IsPalette = MagickFalse;
|
||
|
||
if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
|
||
(image->colors == 0 || image->colormap == NULL))
|
||
{
|
||
image_info=DestroyImageInfo(image_info);
|
||
image=DestroyImage(image);
|
||
(void) ThrowMagickException(exception,GetMagickModule(),CoderError,
|
||
"Cannot write PNG8 or color-type 3; colormap is NULL",
|
||
"`%s'",IMimage->filename);
|
||
return(MagickFalse);
|
||
}
|
||
|
||
/*
|
||
Allocate the PNG structures
|
||
*/
|
||
#ifdef PNG_USER_MEM_SUPPORTED
|
||
error_info.image=image;
|
||
error_info.exception=exception;
|
||
ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
|
||
MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
|
||
(png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
|
||
|
||
#else
|
||
ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
|
||
MagickPNGErrorHandler,MagickPNGWarningHandler);
|
||
|
||
#endif
|
||
if (ping == (png_struct *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
ping_info=png_create_info_struct(ping);
|
||
|
||
if (ping_info == (png_info *) NULL)
|
||
{
|
||
png_destroy_write_struct(&ping,(png_info **) NULL);
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
|
||
png_set_write_fn(ping,image,png_put_data,png_flush_data);
|
||
pixel_info=(MemoryInfo *) NULL;
|
||
|
||
if (setjmp(png_jmpbuf(ping)))
|
||
{
|
||
/*
|
||
PNG write failed.
|
||
*/
|
||
#ifdef PNG_DEBUG
|
||
if (image_info->verbose)
|
||
(void) printf("PNG write has failed.\n");
|
||
#endif
|
||
png_destroy_write_struct(&ping,&ping_info);
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
|
||
if (pixel_info != (MemoryInfo *) NULL)
|
||
pixel_info=RelinquishVirtualMemory(pixel_info);
|
||
|
||
if (quantum_info != (QuantumInfo *) NULL)
|
||
quantum_info=DestroyQuantumInfo(quantum_info);
|
||
|
||
if (ping_have_blob != MagickFalse)
|
||
(void) CloseBlob(image);
|
||
image_info=DestroyImageInfo(image_info);
|
||
image=DestroyImage(image);
|
||
return(MagickFalse);
|
||
}
|
||
|
||
/* { 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
|
||
/* Reject images with too many rows or columns */
|
||
png_set_user_limits(ping,
|
||
(png_uint_32) MagickMin(0x7fffffffL,
|
||
GetMagickResourceLimit(WidthResource)),
|
||
(png_uint_32) MagickMin(0x7fffffffL,
|
||
GetMagickResourceLimit(HeightResource)));
|
||
#endif /* PNG_SET_USER_LIMITS_SUPPORTED */
|
||
|
||
/*
|
||
Prepare PNG for writing.
|
||
*/
|
||
|
||
#if defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
if (mng_info->write_mng)
|
||
{
|
||
(void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
|
||
# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
||
/* Disable new libpng-1.5.10 feature when writing a MNG because
|
||
* zero-length PLTE is OK
|
||
*/
|
||
png_set_check_for_invalid_index (ping, 0);
|
||
# endif
|
||
}
|
||
|
||
#else
|
||
# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
|
||
if (mng_info->write_mng)
|
||
png_permit_empty_plte(ping,MagickTrue);
|
||
|
||
# endif
|
||
#endif
|
||
|
||
x=0;
|
||
|
||
ping_width=(png_uint_32) image->columns;
|
||
ping_height=(png_uint_32) image->rows;
|
||
|
||
if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
|
||
image_depth=8;
|
||
|
||
if (mng_info->write_png48 || mng_info->write_png64)
|
||
image_depth=16;
|
||
|
||
if (mng_info->write_png_depth != 0)
|
||
image_depth=mng_info->write_png_depth;
|
||
|
||
/* Adjust requested depth to next higher valid depth if necessary */
|
||
if (image_depth > 8)
|
||
image_depth=16;
|
||
|
||
if ((image_depth > 4) && (image_depth < 8))
|
||
image_depth=8;
|
||
|
||
if (image_depth == 3)
|
||
image_depth=4;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" width=%.20g",(double) ping_width);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" height=%.20g",(double) ping_height);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image_matte=%.20g",(double) image->alpha_trait);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image->depth=%.20g",(double) image->depth);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Tentative ping_bit_depth=%.20g",(double) image_depth);
|
||
}
|
||
|
||
save_image_depth=image_depth;
|
||
ping_bit_depth=(png_byte) save_image_depth;
|
||
|
||
|
||
#if defined(PNG_pHYs_SUPPORTED)
|
||
if (ping_exclude_pHYs == MagickFalse)
|
||
{
|
||
if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
|
||
(!mng_info->write_mng || !mng_info->equal_physs))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up pHYs chunk");
|
||
|
||
if (image->units == PixelsPerInchResolution)
|
||
{
|
||
ping_pHYs_unit_type=PNG_RESOLUTION_METER;
|
||
ping_pHYs_x_resolution=
|
||
(png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
|
||
ping_pHYs_y_resolution=
|
||
(png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
|
||
}
|
||
|
||
else if (image->units == PixelsPerCentimeterResolution)
|
||
{
|
||
ping_pHYs_unit_type=PNG_RESOLUTION_METER;
|
||
ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
|
||
ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
|
||
}
|
||
|
||
else
|
||
{
|
||
ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
|
||
ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
|
||
ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
|
||
(double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
|
||
(int) ping_pHYs_unit_type);
|
||
ping_have_pHYs = MagickTrue;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
|
||
{
|
||
unsigned int
|
||
mask;
|
||
|
||
mask=0xffff;
|
||
if (ping_bit_depth == 8)
|
||
mask=0x00ff;
|
||
|
||
if (ping_bit_depth == 4)
|
||
mask=0x000f;
|
||
|
||
if (ping_bit_depth == 2)
|
||
mask=0x0003;
|
||
|
||
if (ping_bit_depth == 1)
|
||
mask=0x0001;
|
||
|
||
ping_background.red=(png_uint_16)
|
||
(ScaleQuantumToShort(image->background_color.red) & mask);
|
||
|
||
ping_background.green=(png_uint_16)
|
||
(ScaleQuantumToShort(image->background_color.green) & mask);
|
||
|
||
ping_background.blue=(png_uint_16)
|
||
(ScaleQuantumToShort(image->background_color.blue) & mask);
|
||
|
||
ping_background.gray=(png_uint_16) ping_background.green;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up bKGD chunk (1)");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background_color index is %d",
|
||
(int) ping_background.index);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ping_bit_depth=%d",ping_bit_depth);
|
||
}
|
||
|
||
ping_have_bKGD = MagickTrue;
|
||
}
|
||
|
||
/*
|
||
Select the color type.
|
||
*/
|
||
matte=image_matte;
|
||
old_bit_depth=0;
|
||
|
||
if (mng_info->IsPalette && mng_info->write_png8)
|
||
{
|
||
/* To do: make this a function cause it's used twice, except
|
||
for reducing the sample depth from 8. */
|
||
|
||
number_colors=image_colors;
|
||
|
||
ping_have_tRNS=MagickFalse;
|
||
|
||
/*
|
||
Set image palette.
|
||
*/
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up PLTE chunk with %d colors (%d)",
|
||
number_colors, image_colors);
|
||
|
||
for (i=0; i < (ssize_t) number_colors; i++)
|
||
{
|
||
palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
|
||
palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
|
||
palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
#if MAGICKCORE_QUANTUM_DEPTH == 8
|
||
" %3ld (%3d,%3d,%3d)",
|
||
#else
|
||
" %5ld (%5d,%5d,%5d)",
|
||
#endif
|
||
(long) i,palette[i].red,palette[i].green,palette[i].blue);
|
||
|
||
}
|
||
|
||
ping_have_PLTE=MagickTrue;
|
||
image_depth=ping_bit_depth;
|
||
ping_num_trans=0;
|
||
|
||
if (matte != MagickFalse)
|
||
{
|
||
/*
|
||
Identify which colormap entry is transparent.
|
||
*/
|
||
assert(number_colors <= 256);
|
||
assert(image->colormap != NULL);
|
||
|
||
for (i=0; i < (ssize_t) number_transparent; i++)
|
||
ping_trans_alpha[i]=0;
|
||
|
||
|
||
ping_num_trans=(unsigned short) (number_transparent +
|
||
number_semitransparent);
|
||
|
||
if (ping_num_trans == 0)
|
||
ping_have_tRNS=MagickFalse;
|
||
|
||
else
|
||
ping_have_tRNS=MagickTrue;
|
||
}
|
||
|
||
if (ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
/*
|
||
* Identify which colormap entry is the background color.
|
||
*/
|
||
|
||
for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
|
||
if (IsPNGColorEqual(ping_background,image->colormap[i]))
|
||
break;
|
||
|
||
ping_background.index=(png_byte) i;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background_color index is %d",
|
||
(int) ping_background.index);
|
||
}
|
||
}
|
||
} /* end of write_png8 */
|
||
|
||
else if (mng_info->write_png_colortype == 1)
|
||
{
|
||
image_matte=MagickFalse;
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
|
||
}
|
||
|
||
else if (mng_info->write_png24 || mng_info->write_png48 ||
|
||
mng_info->write_png_colortype == 3)
|
||
{
|
||
image_matte=MagickFalse;
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
|
||
}
|
||
|
||
else if (mng_info->write_png32 || mng_info->write_png64 ||
|
||
mng_info->write_png_colortype == 7)
|
||
{
|
||
image_matte=MagickTrue;
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
|
||
}
|
||
|
||
else /* mng_info->write_pngNN not specified */
|
||
{
|
||
image_depth=ping_bit_depth;
|
||
|
||
if (mng_info->write_png_colortype != 0)
|
||
{
|
||
ping_color_type=(png_byte) mng_info->write_png_colortype-1;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
|
||
ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||
image_matte=MagickTrue;
|
||
|
||
else
|
||
image_matte=MagickFalse;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PNG colortype %d was specified:",(int) ping_color_type);
|
||
}
|
||
|
||
else /* write_png_colortype not specified */
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Selecting PNG colortype:");
|
||
|
||
if (image_info->type == TrueColorType)
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
|
||
image_matte=MagickFalse;
|
||
}
|
||
else if (image_info->type == TrueColorAlphaType)
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
|
||
image_matte=MagickTrue;
|
||
}
|
||
else if (image_info->type == PaletteType ||
|
||
image_info->type == PaletteAlphaType)
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
|
||
else
|
||
{
|
||
if (ping_have_color == MagickFalse)
|
||
{
|
||
if (image_matte == MagickFalse)
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
|
||
image_matte=MagickFalse;
|
||
}
|
||
|
||
else
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
|
||
image_matte=MagickTrue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (image_matte == MagickFalse)
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
|
||
image_matte=MagickFalse;
|
||
}
|
||
|
||
else
|
||
{
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
|
||
image_matte=MagickTrue;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Selected PNG colortype=%d",ping_color_type);
|
||
|
||
if (ping_bit_depth < 8)
|
||
{
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
|
||
ping_color_type == PNG_COLOR_TYPE_RGB ||
|
||
ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||
ping_bit_depth=8;
|
||
}
|
||
|
||
old_bit_depth=ping_bit_depth;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
if (image->alpha_trait == UndefinedPixelTrait &&
|
||
ping_have_non_bw == MagickFalse)
|
||
ping_bit_depth=1;
|
||
}
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
size_t one = 1;
|
||
ping_bit_depth=1;
|
||
|
||
if (image->colors == 0)
|
||
{
|
||
/* DO SOMETHING */
|
||
png_error(ping,"image has 0 colors");
|
||
}
|
||
|
||
while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
|
||
ping_bit_depth <<= 1;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Number of colors: %.20g",(double) image_colors);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Tentative PNG bit depth: %d",ping_bit_depth);
|
||
}
|
||
|
||
if (ping_bit_depth < (int) mng_info->write_png_depth)
|
||
ping_bit_depth = mng_info->write_png_depth;
|
||
}
|
||
(void) old_bit_depth;
|
||
image_depth=ping_bit_depth;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Tentative PNG color type: %s (%.20g)",
|
||
PngColorTypeToString(ping_color_type),
|
||
(double) ping_color_type);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image_info->type: %.20g",(double) image_info->type);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" image_depth: %.20g",(double) image_depth);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
|
||
" image->depth: %.20g",(double) image->depth);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ping_bit_depth: %.20g",(double) ping_bit_depth);
|
||
}
|
||
|
||
if (matte != MagickFalse)
|
||
{
|
||
if (mng_info->IsPalette)
|
||
{
|
||
if (mng_info->write_png_colortype == 0)
|
||
{
|
||
ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
|
||
|
||
if (ping_have_color != MagickFalse)
|
||
ping_color_type=PNG_COLOR_TYPE_RGBA;
|
||
}
|
||
|
||
/*
|
||
* Determine if there is any transparent color.
|
||
*/
|
||
if (number_transparent + number_semitransparent == 0)
|
||
{
|
||
/*
|
||
No transparent pixels are present. Change 4 or 6 to 0 or 2.
|
||
*/
|
||
|
||
image_matte=MagickFalse;
|
||
|
||
if (mng_info->write_png_colortype == 0)
|
||
ping_color_type&=0x03;
|
||
}
|
||
|
||
else
|
||
{
|
||
unsigned int
|
||
mask;
|
||
|
||
mask=0xffff;
|
||
|
||
if (ping_bit_depth == 8)
|
||
mask=0x00ff;
|
||
|
||
if (ping_bit_depth == 4)
|
||
mask=0x000f;
|
||
|
||
if (ping_bit_depth == 2)
|
||
mask=0x0003;
|
||
|
||
if (ping_bit_depth == 1)
|
||
mask=0x0001;
|
||
|
||
ping_trans_color.red=(png_uint_16)
|
||
(ScaleQuantumToShort(image->colormap[0].red) & mask);
|
||
|
||
ping_trans_color.green=(png_uint_16)
|
||
(ScaleQuantumToShort(image->colormap[0].green) & mask);
|
||
|
||
ping_trans_color.blue=(png_uint_16)
|
||
(ScaleQuantumToShort(image->colormap[0].blue) & mask);
|
||
|
||
ping_trans_color.gray=(png_uint_16)
|
||
(ScaleQuantumToShort(GetPixelInfoIntensity(image,
|
||
image->colormap)) & mask);
|
||
|
||
ping_trans_color.index=(png_byte) 0;
|
||
|
||
ping_have_tRNS=MagickTrue;
|
||
}
|
||
|
||
if (ping_have_tRNS != MagickFalse)
|
||
{
|
||
/*
|
||
* Determine if there is one and only one transparent color
|
||
* and if so if it is fully transparent.
|
||
*/
|
||
if (ping_have_cheap_transparency == MagickFalse)
|
||
ping_have_tRNS=MagickFalse;
|
||
}
|
||
|
||
if (ping_have_tRNS != MagickFalse)
|
||
{
|
||
if (mng_info->write_png_colortype == 0)
|
||
ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
|
||
|
||
if (image_depth == 8)
|
||
{
|
||
ping_trans_color.red&=0xff;
|
||
ping_trans_color.green&=0xff;
|
||
ping_trans_color.blue&=0xff;
|
||
ping_trans_color.gray&=0xff;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (image_depth == 8)
|
||
{
|
||
ping_trans_color.red&=0xff;
|
||
ping_trans_color.green&=0xff;
|
||
ping_trans_color.blue&=0xff;
|
||
ping_trans_color.gray&=0xff;
|
||
}
|
||
}
|
||
}
|
||
|
||
matte=image_matte;
|
||
|
||
if (ping_have_tRNS != MagickFalse)
|
||
image_matte=MagickFalse;
|
||
|
||
if ((mng_info->IsPalette) &&
|
||
mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
|
||
ping_have_color == MagickFalse &&
|
||
(image_matte == MagickFalse || image_depth >= 8))
|
||
{
|
||
size_t one=1;
|
||
|
||
if (image_matte != MagickFalse)
|
||
ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
|
||
|
||
else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
{
|
||
ping_color_type=PNG_COLOR_TYPE_GRAY;
|
||
|
||
if (save_image_depth == 16 && image_depth == 8)
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Scaling ping_trans_color (0)");
|
||
}
|
||
ping_trans_color.gray*=0x0101;
|
||
}
|
||
}
|
||
|
||
if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
|
||
image_depth=MAGICKCORE_QUANTUM_DEPTH;
|
||
|
||
if ((image_colors == 0) ||
|
||
((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
|
||
image_colors=(int) (one << image_depth);
|
||
|
||
if (image_depth > 8)
|
||
ping_bit_depth=16;
|
||
|
||
else
|
||
{
|
||
ping_bit_depth=8;
|
||
if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
if(!mng_info->write_png_depth)
|
||
{
|
||
ping_bit_depth=1;
|
||
|
||
while ((int) (one << ping_bit_depth)
|
||
< (ssize_t) image_colors)
|
||
ping_bit_depth <<= 1;
|
||
}
|
||
}
|
||
|
||
else if (ping_color_type ==
|
||
PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
|
||
mng_info->IsPalette)
|
||
{
|
||
/* Check if grayscale is reducible */
|
||
|
||
int
|
||
depth_4_ok=MagickTrue,
|
||
depth_2_ok=MagickTrue,
|
||
depth_1_ok=MagickTrue;
|
||
|
||
for (i=0; i < (ssize_t) image_colors; i++)
|
||
{
|
||
unsigned char
|
||
intensity;
|
||
|
||
intensity=ScaleQuantumToChar(image->colormap[i].red);
|
||
|
||
if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
|
||
depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
|
||
else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
|
||
depth_2_ok=depth_1_ok=MagickFalse;
|
||
else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
|
||
depth_1_ok=MagickFalse;
|
||
}
|
||
|
||
if (depth_1_ok && mng_info->write_png_depth <= 1)
|
||
ping_bit_depth=1;
|
||
|
||
else if (depth_2_ok && mng_info->write_png_depth <= 2)
|
||
ping_bit_depth=2;
|
||
|
||
else if (depth_4_ok && mng_info->write_png_depth <= 4)
|
||
ping_bit_depth=4;
|
||
}
|
||
}
|
||
|
||
image_depth=ping_bit_depth;
|
||
}
|
||
|
||
else
|
||
|
||
if (mng_info->IsPalette)
|
||
{
|
||
number_colors=image_colors;
|
||
|
||
if (image_depth <= 8)
|
||
{
|
||
/*
|
||
Set image palette.
|
||
*/
|
||
ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
|
||
|
||
if (!(mng_info->have_write_global_plte && matte == MagickFalse))
|
||
{
|
||
for (i=0; i < (ssize_t) number_colors; i++)
|
||
{
|
||
palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
|
||
palette[i].green=
|
||
ScaleQuantumToChar(image->colormap[i].green);
|
||
palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up PLTE chunk with %d colors",
|
||
number_colors);
|
||
|
||
ping_have_PLTE=MagickTrue;
|
||
}
|
||
|
||
/* color_type is PNG_COLOR_TYPE_PALETTE */
|
||
if (mng_info->write_png_depth == 0)
|
||
{
|
||
size_t
|
||
one;
|
||
|
||
ping_bit_depth=1;
|
||
one=1;
|
||
|
||
while ((one << ping_bit_depth) < (size_t) number_colors)
|
||
ping_bit_depth <<= 1;
|
||
}
|
||
|
||
ping_num_trans=0;
|
||
|
||
if (matte != MagickFalse)
|
||
{
|
||
/*
|
||
* Set up trans_colors array.
|
||
*/
|
||
assert(number_colors <= 256);
|
||
|
||
ping_num_trans=(unsigned short) (number_transparent +
|
||
number_semitransparent);
|
||
|
||
if (ping_num_trans == 0)
|
||
ping_have_tRNS=MagickFalse;
|
||
|
||
else
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Scaling ping_trans_color (1)");
|
||
}
|
||
ping_have_tRNS=MagickTrue;
|
||
|
||
for (i=0; i < ping_num_trans; i++)
|
||
{
|
||
ping_trans_alpha[i]= (png_byte)
|
||
ScaleQuantumToChar(image->colormap[i].alpha);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
|
||
if (image_depth < 8)
|
||
image_depth=8;
|
||
|
||
if ((save_image_depth == 16) && (image_depth == 8))
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Scaling ping_trans_color from (%d,%d,%d)",
|
||
(int) ping_trans_color.red,
|
||
(int) ping_trans_color.green,
|
||
(int) ping_trans_color.blue);
|
||
}
|
||
|
||
ping_trans_color.red*=0x0101;
|
||
ping_trans_color.green*=0x0101;
|
||
ping_trans_color.blue*=0x0101;
|
||
ping_trans_color.gray*=0x0101;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" to (%d,%d,%d)",
|
||
(int) ping_trans_color.red,
|
||
(int) ping_trans_color.green,
|
||
(int) ping_trans_color.blue);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
|
||
ping_bit_depth = (ssize_t) mng_info->write_png_depth;
|
||
|
||
/*
|
||
Adjust background and transparency samples in sub-8-bit grayscale files.
|
||
*/
|
||
if (ping_bit_depth < 8 && ping_color_type ==
|
||
PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
png_uint_16
|
||
maxval;
|
||
|
||
size_t
|
||
one=1;
|
||
|
||
maxval=(png_uint_16) ((one << ping_bit_depth)-1);
|
||
|
||
if (ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
|
||
ping_background.gray=(png_uint_16) ((maxval/65535.)*
|
||
(ScaleQuantumToShort(((GetPixelInfoIntensity(image,
|
||
&image->background_color))) +.5)));
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up bKGD chunk (2)");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background_color index is %d",
|
||
(int) ping_background.index);
|
||
|
||
ping_have_bKGD = MagickTrue;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Scaling ping_trans_color.gray from %d",
|
||
(int)ping_trans_color.gray);
|
||
|
||
ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
|
||
ping_trans_color.gray)+.5);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" to %d", (int)ping_trans_color.gray);
|
||
}
|
||
|
||
if (ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
|
||
{
|
||
/*
|
||
Identify which colormap entry is the background color.
|
||
*/
|
||
|
||
number_colors=image_colors;
|
||
|
||
for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
|
||
if (IsPNGColorEqual(image->background_color,image->colormap[i]))
|
||
break;
|
||
|
||
ping_background.index=(png_byte) i;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up bKGD chunk with index=%d",(int) i);
|
||
}
|
||
|
||
if (i < (ssize_t) number_colors)
|
||
{
|
||
ping_have_bKGD = MagickTrue;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background =(%d,%d,%d)",
|
||
(int) ping_background.red,
|
||
(int) ping_background.green,
|
||
(int) ping_background.blue);
|
||
}
|
||
}
|
||
|
||
else /* Can't happen */
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" No room in PLTE to add bKGD color");
|
||
ping_have_bKGD = MagickFalse;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
|
||
ping_color_type);
|
||
/*
|
||
Initialize compression level and filtering.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up deflate compression");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Compression buffer size: 32768");
|
||
}
|
||
|
||
png_set_compression_buffer_size(ping,32768L);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Compression mem level: 9");
|
||
|
||
png_set_compression_mem_level(ping, 9);
|
||
|
||
/* Untangle the "-quality" setting:
|
||
|
||
Undefined is 0; the default is used.
|
||
Default is 75
|
||
|
||
10's digit:
|
||
|
||
0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
|
||
zlib default compression level
|
||
|
||
1-9: the zlib compression level
|
||
|
||
1's digit:
|
||
|
||
0-4: the PNG filter method
|
||
|
||
5: libpng adaptive filtering if compression level > 5
|
||
libpng filter type "none" if compression level <= 5
|
||
or if image is grayscale or palette
|
||
|
||
6: libpng adaptive filtering
|
||
|
||
7: "LOCO" filtering (intrapixel differing) if writing
|
||
a MNG, otherwise "none". Did not work in IM-6.7.0-9
|
||
and earlier because of a missing "else".
|
||
|
||
8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
|
||
filtering. Unused prior to IM-6.7.0-10, was same as 6
|
||
|
||
9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
|
||
Unused prior to IM-6.7.0-10, was same as 6
|
||
|
||
Note that using the -quality option, not all combinations of
|
||
PNG filter type, zlib compression level, and zlib compression
|
||
strategy are possible. This will be addressed soon in a
|
||
release that accomodates "-define png:compression-strategy", etc.
|
||
|
||
*/
|
||
|
||
quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
|
||
image_info->quality;
|
||
|
||
if (quality <= 9)
|
||
{
|
||
if (mng_info->write_png_compression_strategy == 0)
|
||
mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
|
||
}
|
||
|
||
else if (mng_info->write_png_compression_level == 0)
|
||
{
|
||
int
|
||
level;
|
||
|
||
level=(int) MagickMin((ssize_t) quality/10,9);
|
||
|
||
mng_info->write_png_compression_level = level+1;
|
||
}
|
||
|
||
if (mng_info->write_png_compression_strategy == 0)
|
||
{
|
||
if ((quality %10) == 8 || (quality %10) == 9)
|
||
#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
|
||
mng_info->write_png_compression_strategy=Z_RLE+1;
|
||
#else
|
||
mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
|
||
#endif
|
||
}
|
||
|
||
if (mng_info->write_png_compression_filter == 0)
|
||
mng_info->write_png_compression_filter=((int) quality % 10) + 1;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (mng_info->write_png_compression_level)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Compression level: %d",
|
||
(int) mng_info->write_png_compression_level-1);
|
||
|
||
if (mng_info->write_png_compression_strategy)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Compression strategy: %d",
|
||
(int) mng_info->write_png_compression_strategy-1);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up filtering");
|
||
|
||
if (mng_info->write_png_compression_filter == 6)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Base filter method: ADAPTIVE");
|
||
else if (mng_info->write_png_compression_filter == 0 ||
|
||
mng_info->write_png_compression_filter == 1)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Base filter method: NONE");
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Base filter method: %d",
|
||
(int) mng_info->write_png_compression_filter-1);
|
||
}
|
||
|
||
if (mng_info->write_png_compression_level != 0)
|
||
png_set_compression_level(ping,mng_info->write_png_compression_level-1);
|
||
|
||
if (mng_info->write_png_compression_filter == 6)
|
||
{
|
||
if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
|
||
(quality < 50))
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
|
||
else
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
|
||
}
|
||
else if (mng_info->write_png_compression_filter == 7 ||
|
||
mng_info->write_png_compression_filter == 10)
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
|
||
|
||
else if (mng_info->write_png_compression_filter == 8)
|
||
{
|
||
#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
|
||
if (mng_info->write_mng)
|
||
{
|
||
if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
|
||
((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
|
||
ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
|
||
}
|
||
#endif
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
|
||
}
|
||
|
||
else if (mng_info->write_png_compression_filter == 9)
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
|
||
|
||
else if (mng_info->write_png_compression_filter != 0)
|
||
png_set_filter(ping,PNG_FILTER_TYPE_BASE,
|
||
mng_info->write_png_compression_filter-1);
|
||
|
||
if (mng_info->write_png_compression_strategy != 0)
|
||
png_set_compression_strategy(ping,
|
||
mng_info->write_png_compression_strategy-1);
|
||
|
||
ping_interlace_method=image_info->interlace != NoInterlace;
|
||
|
||
if (mng_info->write_mng)
|
||
png_set_sig_bytes(ping,8);
|
||
|
||
/* Bail out if cannot meet defined png:bit-depth or png:color-type */
|
||
|
||
if (mng_info->write_png_colortype != 0)
|
||
{
|
||
if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
|
||
if (ping_have_color != MagickFalse)
|
||
{
|
||
ping_color_type = PNG_COLOR_TYPE_RGB;
|
||
|
||
if (ping_bit_depth < 8)
|
||
ping_bit_depth=8;
|
||
}
|
||
|
||
if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
if (ping_have_color != MagickFalse)
|
||
ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
||
}
|
||
|
||
if (ping_need_colortype_warning != MagickFalse ||
|
||
((mng_info->write_png_depth &&
|
||
(int) mng_info->write_png_depth != ping_bit_depth) ||
|
||
(mng_info->write_png_colortype &&
|
||
((int) mng_info->write_png_colortype-1 != ping_color_type &&
|
||
mng_info->write_png_colortype != 7 &&
|
||
!(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (ping_need_colortype_warning != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Image has transparency but tRNS chunk was excluded");
|
||
}
|
||
|
||
if (mng_info->write_png_depth)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Defined png:bit-depth=%u, Computed depth=%u",
|
||
mng_info->write_png_depth,
|
||
ping_bit_depth);
|
||
}
|
||
|
||
if (mng_info->write_png_colortype)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Defined png:color-type=%u, Computed color type=%u",
|
||
mng_info->write_png_colortype-1,
|
||
ping_color_type);
|
||
}
|
||
}
|
||
|
||
png_warning(ping,
|
||
"Cannot write image with defined png:bit-depth or png:color-type.");
|
||
}
|
||
|
||
if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
|
||
{
|
||
/* Add an opaque matte channel */
|
||
image->alpha_trait = BlendPixelTrait;
|
||
(void) SetImageAlpha(image,OpaqueAlpha,exception);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Added an opaque matte channel");
|
||
}
|
||
|
||
if (number_transparent != 0 || number_semitransparent != 0)
|
||
{
|
||
if (ping_color_type < 4)
|
||
{
|
||
ping_have_tRNS=MagickTrue;
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting ping_have_tRNS=MagickTrue.");
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing PNG header chunks");
|
||
|
||
png_set_IHDR(ping,ping_info,ping_width,ping_height,
|
||
ping_bit_depth,ping_color_type,
|
||
ping_interlace_method,ping_compression_method,
|
||
ping_filter_method);
|
||
|
||
if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
|
||
{
|
||
png_set_PLTE(ping,ping_info,palette,number_colors);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
for (i=0; i< (ssize_t) number_colors; i++)
|
||
{
|
||
if (i < ping_num_trans)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
|
||
(int) i,
|
||
(int) palette[i].red,
|
||
(int) palette[i].green,
|
||
(int) palette[i].blue,
|
||
(int) i,
|
||
(int) ping_trans_alpha[i]);
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PLTE[%d] = (%d,%d,%d)",
|
||
(int) i,
|
||
(int) palette[i].red,
|
||
(int) palette[i].green,
|
||
(int) palette[i].blue);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Only write the iCCP chunk if we are not writing the sRGB chunk. */
|
||
if (ping_exclude_sRGB != MagickFalse ||
|
||
(!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
|
||
{
|
||
if ((ping_exclude_tEXt == MagickFalse ||
|
||
ping_exclude_zTXt == MagickFalse) &&
|
||
(ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
|
||
{
|
||
ResetImageProfileIterator(image);
|
||
for (name=GetNextImageProfile(image); name != (char *) NULL; )
|
||
{
|
||
profile=GetImageProfile(image,name);
|
||
|
||
if (profile != (StringInfo *) NULL)
|
||
{
|
||
#ifdef PNG_WRITE_iCCP_SUPPORTED
|
||
if ((LocaleCompare(name,"ICC") == 0) ||
|
||
(LocaleCompare(name,"ICM") == 0))
|
||
{
|
||
ping_have_iCCP = MagickTrue;
|
||
if (ping_exclude_iCCP == MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up iCCP chunk");
|
||
|
||
png_set_iCCP(ping,ping_info,(png_charp) name,0,
|
||
#if (PNG_LIBPNG_VER < 10500)
|
||
(png_charp) GetStringInfoDatum(profile),
|
||
#else
|
||
(const png_byte *) GetStringInfoDatum(profile),
|
||
#endif
|
||
(png_uint_32) GetStringInfoLength(profile));
|
||
}
|
||
else
|
||
{
|
||
/* Do not write hex-encoded ICC chunk */
|
||
name=GetNextImageProfile(image);
|
||
continue;
|
||
}
|
||
}
|
||
#endif /* WRITE_iCCP */
|
||
|
||
if (LocaleCompare(name,"exif") == 0)
|
||
{
|
||
/* Do not write hex-encoded ICC chunk; we will
|
||
write it later as an eXIf chunk */
|
||
name=GetNextImageProfile(image);
|
||
continue;
|
||
}
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up zTXt chunk with uuencoded %s profile",
|
||
name);
|
||
Magick_png_write_raw_profile(image_info,ping,ping_info,
|
||
(unsigned char *) name,(unsigned char *) name,
|
||
GetStringInfoDatum(profile),(png_uint_32)
|
||
GetStringInfoLength(profile),exception);
|
||
}
|
||
name=GetNextImageProfile(image);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if defined(PNG_WRITE_sRGB_SUPPORTED)
|
||
if ((mng_info->have_write_global_srgb == 0) &&
|
||
ping_have_iCCP != MagickTrue &&
|
||
(ping_have_sRGB != MagickFalse ||
|
||
png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
|
||
{
|
||
if (ping_exclude_sRGB == MagickFalse)
|
||
{
|
||
/*
|
||
Note image rendering intent.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up sRGB chunk");
|
||
|
||
(void) png_set_sRGB(ping,ping_info,(
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent(
|
||
image->rendering_intent)));
|
||
|
||
ping_have_sRGB = MagickTrue;
|
||
}
|
||
}
|
||
|
||
if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
|
||
#endif
|
||
{
|
||
if (ping_exclude_gAMA == MagickFalse &&
|
||
ping_have_iCCP == MagickFalse &&
|
||
ping_have_sRGB == MagickFalse &&
|
||
(ping_exclude_sRGB == MagickFalse ||
|
||
(image->gamma < .45 || image->gamma > .46)))
|
||
{
|
||
if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
|
||
{
|
||
/*
|
||
Note image gamma.
|
||
To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
|
||
*/
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up gAMA chunk");
|
||
|
||
png_set_gAMA(ping,ping_info,image->gamma);
|
||
}
|
||
}
|
||
|
||
if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
|
||
{
|
||
if ((mng_info->have_write_global_chrm == 0) &&
|
||
(image->chromaticity.red_primary.x != 0.0))
|
||
{
|
||
/*
|
||
Note image chromaticity.
|
||
Note: if cHRM+gAMA == sRGB write sRGB instead.
|
||
*/
|
||
PrimaryInfo
|
||
bp,
|
||
gp,
|
||
rp,
|
||
wp;
|
||
|
||
wp=image->chromaticity.white_point;
|
||
rp=image->chromaticity.red_primary;
|
||
gp=image->chromaticity.green_primary;
|
||
bp=image->chromaticity.blue_primary;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up cHRM chunk");
|
||
|
||
png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
|
||
bp.x,bp.y);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ping_exclude_bKGD == MagickFalse)
|
||
{
|
||
if (ping_have_bKGD != MagickFalse)
|
||
{
|
||
png_set_bKGD(ping,ping_info,&ping_background);
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up bKGD chunk");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" background color = (%d,%d,%d)",
|
||
(int) ping_background.red,
|
||
(int) ping_background.green,
|
||
(int) ping_background.blue);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" index = %d, gray=%d",
|
||
(int) ping_background.index,
|
||
(int) ping_background.gray);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ping_exclude_pHYs == MagickFalse)
|
||
{
|
||
if (ping_have_pHYs != MagickFalse)
|
||
{
|
||
png_set_pHYs(ping,ping_info,
|
||
ping_pHYs_x_resolution,
|
||
ping_pHYs_y_resolution,
|
||
ping_pHYs_unit_type);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up pHYs chunk");
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" x_resolution=%lu",
|
||
(unsigned long) ping_pHYs_x_resolution);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" y_resolution=%lu",
|
||
(unsigned long) ping_pHYs_y_resolution);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" unit_type=%lu",
|
||
(unsigned long) ping_pHYs_unit_type);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if defined(PNG_tIME_SUPPORTED)
|
||
if (ping_exclude_tIME == MagickFalse)
|
||
{
|
||
const char
|
||
*timestamp;
|
||
|
||
if (image->taint == MagickFalse)
|
||
{
|
||
timestamp=GetImageOption(image_info,"png:tIME");
|
||
|
||
if (timestamp == (const char *) NULL)
|
||
timestamp=GetImageProperty(image,"png:tIME",exception);
|
||
}
|
||
|
||
else
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Reset tIME in tainted image");
|
||
|
||
timestamp=GetImageProperty(image,"date:modify",exception);
|
||
}
|
||
|
||
if (timestamp != (const char *) NULL)
|
||
write_tIME_chunk(image,ping,ping_info,timestamp,exception);
|
||
}
|
||
#endif
|
||
|
||
if (mng_info->need_blob != MagickFalse)
|
||
{
|
||
if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
|
||
MagickFalse)
|
||
png_error(ping,"WriteBlob Failed");
|
||
|
||
ping_have_blob=MagickTrue;
|
||
}
|
||
|
||
png_write_info_before_PLTE(ping, ping_info);
|
||
|
||
if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
|
||
{
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Calling png_set_tRNS with num_trans=%d",ping_num_trans);
|
||
}
|
||
|
||
if (ping_color_type == 3)
|
||
(void) png_set_tRNS(ping, ping_info,
|
||
ping_trans_alpha,
|
||
ping_num_trans,
|
||
NULL);
|
||
|
||
else
|
||
{
|
||
(void) png_set_tRNS(ping, ping_info,
|
||
NULL,
|
||
0,
|
||
&ping_trans_color);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" tRNS color =(%d,%d,%d)",
|
||
(int) ping_trans_color.red,
|
||
(int) ping_trans_color.green,
|
||
(int) ping_trans_color.blue);
|
||
}
|
||
}
|
||
}
|
||
|
||
png_write_info(ping,ping_info);
|
||
|
||
/* write orNT if image->orientation is defined */
|
||
if (image->orientation != UndefinedOrientation)
|
||
{
|
||
unsigned char
|
||
chunk[6];
|
||
(void) WriteBlobMSBULong(image,1L); /* data length=1 */
|
||
PNGType(chunk,mng_orNT);
|
||
LogPNGChunk(logging,mng_orNT,1L);
|
||
/* PNG uses Exif orientation values */
|
||
chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
|
||
(void) WriteBlob(image,5,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,5));
|
||
}
|
||
|
||
ping_wrote_caNv = MagickFalse;
|
||
|
||
/* write caNv chunk */
|
||
if (ping_exclude_caNv == MagickFalse)
|
||
{
|
||
if ((image->page.width != 0 && image->page.width != image->columns) ||
|
||
(image->page.height != 0 && image->page.height != image->rows) ||
|
||
image->page.x != 0 || image->page.y != 0)
|
||
{
|
||
unsigned char
|
||
chunk[20];
|
||
|
||
(void) WriteBlobMSBULong(image,16L); /* data length=8 */
|
||
PNGType(chunk,mng_caNv);
|
||
LogPNGChunk(logging,mng_caNv,16L);
|
||
PNGLong(chunk+4,(png_uint_32) image->page.width);
|
||
PNGLong(chunk+8,(png_uint_32) image->page.height);
|
||
PNGsLong(chunk+12,(png_int_32) image->page.x);
|
||
PNGsLong(chunk+16,(png_int_32) image->page.y);
|
||
(void) WriteBlob(image,20,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,20));
|
||
ping_wrote_caNv = MagickTrue;
|
||
}
|
||
}
|
||
|
||
#if defined(PNG_oFFs_SUPPORTED)
|
||
if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
|
||
{
|
||
if (image->page.x || image->page.y)
|
||
{
|
||
png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
|
||
(png_int_32) image->page.y, 0);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up oFFs chunk with x=%d, y=%d, units=0",
|
||
(int) image->page.x, (int) image->page.y);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if (PNG_LIBPNG_VER == 10206)
|
||
/* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
|
||
#define PNG_HAVE_IDAT 0x04
|
||
ping->mode |= PNG_HAVE_IDAT;
|
||
#undef PNG_HAVE_IDAT
|
||
#endif
|
||
|
||
png_set_packing(ping);
|
||
/*
|
||
Allocate memory.
|
||
*/
|
||
rowbytes=image->columns;
|
||
if (image_depth > 8)
|
||
rowbytes*=2;
|
||
switch (ping_color_type)
|
||
{
|
||
case PNG_COLOR_TYPE_RGB:
|
||
rowbytes*=3;
|
||
break;
|
||
|
||
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||
rowbytes*=2;
|
||
break;
|
||
|
||
case PNG_COLOR_TYPE_RGBA:
|
||
rowbytes*=4;
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing PNG image data");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Allocating %.20g bytes of memory for pixels",(double) rowbytes);
|
||
}
|
||
pixel_info=AcquireVirtualMemory(rowbytes,GetPixelChannels(image)*
|
||
sizeof(*ping_pixels));
|
||
if (pixel_info == (MemoryInfo *) NULL)
|
||
png_error(ping,"Allocation of memory for pixels failed");
|
||
ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
|
||
(void) memset(ping_pixels,0,rowbytes*GetPixelChannels(image)*
|
||
sizeof(*ping_pixels));
|
||
/*
|
||
Initialize image scanlines.
|
||
*/
|
||
quantum_info=AcquireQuantumInfo(image_info,image);
|
||
if (quantum_info == (QuantumInfo *) NULL)
|
||
png_error(ping,"Memory allocation for quantum_info failed");
|
||
quantum_info->format=UndefinedQuantumFormat;
|
||
SetQuantumDepth(image,quantum_info,image_depth);
|
||
(void) SetQuantumEndian(image,quantum_info,MSBEndian);
|
||
num_passes=png_set_interlace_handling(ping);
|
||
|
||
if ((mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE) ||
|
||
((!mng_info->write_png8 && !mng_info->write_png24 &&
|
||
!mng_info->write_png48 && !mng_info->write_png64 &&
|
||
!mng_info->write_png32) &&
|
||
(mng_info->IsPalette ||
|
||
(image_info->type == BilevelType)) &&
|
||
image_matte == MagickFalse &&
|
||
ping_have_non_bw == MagickFalse))
|
||
{
|
||
/* Palette, Bilevel, or Opaque Monochrome */
|
||
QuantumType
|
||
quantum_type;
|
||
|
||
const Quantum
|
||
*p;
|
||
|
||
quantum_type=RedQuantum;
|
||
if (mng_info->IsPalette)
|
||
{
|
||
quantum_type=GrayQuantum;
|
||
if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE)
|
||
quantum_type=IndexQuantum;
|
||
}
|
||
SetQuantumDepth(image,quantum_info,8);
|
||
for (pass=0; pass < num_passes; pass++)
|
||
{
|
||
/*
|
||
Convert PseudoClass image to a PNG monochrome image.
|
||
*/
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing row of pixels (0)");
|
||
|
||
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (p == (const Quantum *) NULL)
|
||
break;
|
||
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,quantum_type,ping_pixels,exception);
|
||
if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
|
||
for (i=0; i < (ssize_t) image->columns; i++)
|
||
*(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
|
||
255 : 0);
|
||
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing row of pixels (1)");
|
||
|
||
png_write_row(ping,ping_pixels);
|
||
|
||
status=SetImageProgress(image,SaveImageTag,
|
||
(MagickOffsetType) (pass * image->rows + y),
|
||
num_passes * image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
else /* Not Palette, Bilevel, or Opaque Monochrome */
|
||
{
|
||
if ((!mng_info->write_png8 && !mng_info->write_png24 &&
|
||
!mng_info->write_png48 && !mng_info->write_png64 &&
|
||
!mng_info->write_png32) && (image_matte != MagickFalse ||
|
||
(ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
|
||
(mng_info->IsPalette) && ping_have_color == MagickFalse)
|
||
{
|
||
const Quantum
|
||
*p;
|
||
|
||
for (pass=0; pass < num_passes; pass++)
|
||
{
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
|
||
|
||
if (p == (const Quantum *) NULL)
|
||
break;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
if (mng_info->IsPalette)
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayQuantum,ping_pixels,exception);
|
||
|
||
else
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,RedQuantum,ping_pixels,exception);
|
||
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing GRAY PNG pixels (2)");
|
||
}
|
||
|
||
else /* PNG_COLOR_TYPE_GRAY_ALPHA */
|
||
{
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing GRAY_ALPHA PNG pixels (2)");
|
||
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayAlphaQuantum,ping_pixels,exception);
|
||
}
|
||
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing row of pixels (2)");
|
||
|
||
png_write_row(ping,ping_pixels);
|
||
|
||
status=SetImageProgress(image,SaveImageTag,
|
||
(MagickOffsetType) (pass * image->rows + y),
|
||
num_passes * image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
const Quantum
|
||
*p;
|
||
|
||
for (pass=0; pass < num_passes; pass++)
|
||
{
|
||
if ((image_depth > 8) ||
|
||
mng_info->write_png24 ||
|
||
mng_info->write_png32 ||
|
||
mng_info->write_png48 ||
|
||
mng_info->write_png64 ||
|
||
(!mng_info->write_png8 && !mng_info->IsPalette))
|
||
{
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
p=GetVirtualPixels(image,0,y,image->columns,1, exception);
|
||
|
||
if (p == (const Quantum *) NULL)
|
||
break;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
if (image->storage_class == DirectClass)
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,RedQuantum,ping_pixels,exception);
|
||
|
||
else
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayQuantum,ping_pixels,exception);
|
||
}
|
||
|
||
else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
{
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayAlphaQuantum,ping_pixels,
|
||
exception);
|
||
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing GRAY_ALPHA PNG pixels (3)");
|
||
}
|
||
|
||
else if (image_matte != MagickFalse)
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,RGBAQuantum,ping_pixels,exception);
|
||
|
||
else
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,RGBQuantum,ping_pixels,exception);
|
||
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing row of pixels (3)");
|
||
|
||
png_write_row(ping,ping_pixels);
|
||
|
||
status=SetImageProgress(image,SaveImageTag,
|
||
(MagickOffsetType) (pass * image->rows + y),
|
||
num_passes * image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
else
|
||
/* not ((image_depth > 8) ||
|
||
mng_info->write_png24 || mng_info->write_png32 ||
|
||
mng_info->write_png48 || mng_info->write_png64 ||
|
||
(!mng_info->write_png8 && !mng_info->IsPalette))
|
||
*/
|
||
{
|
||
if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
|
||
(ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
|
||
|
||
SetQuantumDepth(image,quantum_info,8);
|
||
image_depth=8;
|
||
}
|
||
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
|
||
pass);
|
||
|
||
p=GetVirtualPixels(image,0,y,image->columns,1, exception);
|
||
|
||
if (p == (const Quantum *) NULL)
|
||
break;
|
||
|
||
if (ping_color_type == PNG_COLOR_TYPE_GRAY)
|
||
{
|
||
SetQuantumDepth(image,quantum_info,image->depth);
|
||
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayQuantum,ping_pixels,exception);
|
||
}
|
||
|
||
else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||
{
|
||
if (logging != MagickFalse && y == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing GRAY_ALPHA PNG pixels (4)");
|
||
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,GrayAlphaQuantum,ping_pixels,
|
||
exception);
|
||
}
|
||
|
||
else
|
||
{
|
||
(void) ExportQuantumPixels(image,(CacheView *) NULL,
|
||
quantum_info,IndexQuantum,ping_pixels,exception);
|
||
|
||
if (logging != MagickFalse && y <= 2)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing row of non-gray pixels (4)");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" ping_pixels[0]=%d,ping_pixels[1]=%d",
|
||
(int)ping_pixels[0],(int)ping_pixels[1]);
|
||
}
|
||
}
|
||
png_write_row(ping,ping_pixels);
|
||
|
||
status=SetImageProgress(image,SaveImageTag,
|
||
(MagickOffsetType) (pass * image->rows + y),
|
||
num_passes * image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (quantum_info != (QuantumInfo *) NULL)
|
||
quantum_info=DestroyQuantumInfo(quantum_info);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Wrote PNG image data");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Width: %.20g",(double) ping_width);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Height: %.20g",(double) ping_height);
|
||
|
||
if (mng_info->write_png_depth)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Defined png:bit-depth: %d",mng_info->write_png_depth);
|
||
}
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PNG bit-depth written: %d",ping_bit_depth);
|
||
|
||
if (mng_info->write_png_colortype)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Defined png:color-type: %d",mng_info->write_png_colortype-1);
|
||
}
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PNG color-type written: %d",ping_color_type);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" PNG Interlace method: %d",ping_interlace_method);
|
||
}
|
||
/*
|
||
Generate text chunks after IDAT.
|
||
*/
|
||
if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
|
||
{
|
||
ResetImagePropertyIterator(image);
|
||
property=GetNextImageProperty(image);
|
||
while (property != (const char *) NULL)
|
||
{
|
||
png_textp
|
||
text;
|
||
|
||
value=GetImageProperty(image,property,exception);
|
||
|
||
/* Don't write any "png:" or "jpeg:" properties; those are just for
|
||
* "identify" or for passing through to another JPEG
|
||
*/
|
||
if ((LocaleNCompare(property,"png:",4) != 0 &&
|
||
LocaleNCompare(property,"jpeg:",5) != 0) &&
|
||
|
||
|
||
/* Suppress density and units if we wrote a pHYs chunk */
|
||
(ping_exclude_pHYs != MagickFalse ||
|
||
LocaleCompare(property,"density") != 0 ||
|
||
LocaleCompare(property,"units") != 0) &&
|
||
|
||
/* Suppress the IM-generated Date:create and Date:modify */
|
||
(ping_exclude_date == MagickFalse ||
|
||
LocaleNCompare(property, "Date:",5) != 0))
|
||
{
|
||
if (value != (const char *) NULL)
|
||
{
|
||
|
||
#if PNG_LIBPNG_VER >= 10400
|
||
text=(png_textp) png_malloc(ping,
|
||
(png_alloc_size_t) sizeof(png_text));
|
||
#else
|
||
text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
|
||
#endif
|
||
text[0].key=(char *) property;
|
||
text[0].text=(char *) value;
|
||
text[0].text_length=strlen(value);
|
||
|
||
if (ping_exclude_tEXt != MagickFalse)
|
||
text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
|
||
|
||
else if (ping_exclude_zTXt != MagickFalse)
|
||
text[0].compression=PNG_TEXT_COMPRESSION_NONE;
|
||
|
||
else
|
||
{
|
||
text[0].compression=image_info->compression == NoCompression ||
|
||
(image_info->compression == UndefinedCompression &&
|
||
text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
|
||
PNG_TEXT_COMPRESSION_zTXt ;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Setting up text chunk");
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" keyword: '%s'",text[0].key);
|
||
}
|
||
|
||
png_set_text(ping,ping_info,text,1);
|
||
png_free(ping,text);
|
||
}
|
||
}
|
||
property=GetNextImageProperty(image);
|
||
}
|
||
}
|
||
|
||
/* write eXIf profile */
|
||
if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
|
||
{
|
||
ResetImageProfileIterator(image);
|
||
|
||
for (name=GetNextImageProfile(image); name != (char *) NULL; )
|
||
{
|
||
if (LocaleCompare(name,"exif") == 0)
|
||
{
|
||
profile=GetImageProfile(image,name);
|
||
|
||
if (profile != (StringInfo *) NULL)
|
||
{
|
||
png_uint_32
|
||
length;
|
||
|
||
unsigned char
|
||
chunk[4],
|
||
*data;
|
||
|
||
StringInfo
|
||
*ping_profile;
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Have eXIf profile");
|
||
|
||
ping_profile=CloneStringInfo(profile);
|
||
data=GetStringInfoDatum(ping_profile),
|
||
length=(png_uint_32) GetStringInfoLength(ping_profile);
|
||
|
||
PNGType(chunk,mng_eXIf);
|
||
if (length < 7)
|
||
{
|
||
ping_profile=DestroyStringInfo(ping_profile);
|
||
break; /* otherwise crashes */
|
||
}
|
||
|
||
if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
|
||
*(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
|
||
{
|
||
/* skip the "Exif\0\0" JFIF Exif Header ID */
|
||
length -= 6;
|
||
data += 6;
|
||
}
|
||
|
||
LogPNGChunk(logging,chunk,length);
|
||
(void) WriteBlobMSBULong(image,length);
|
||
(void) WriteBlob(image,4,chunk);
|
||
(void) WriteBlob(image,length,data);
|
||
(void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
|
||
(uInt) length));
|
||
ping_profile=DestroyStringInfo(ping_profile);
|
||
break;
|
||
}
|
||
}
|
||
name=GetNextImageProfile(image);
|
||
}
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing PNG end info");
|
||
|
||
png_write_end(ping,ping_info);
|
||
|
||
if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
|
||
{
|
||
if (mng_info->page.x || mng_info->page.y ||
|
||
(ping_width != mng_info->page.width) ||
|
||
(ping_height != mng_info->page.height))
|
||
{
|
||
unsigned char
|
||
chunk[32];
|
||
|
||
/*
|
||
Write FRAM 4 with clipping boundaries followed by FRAM 1.
|
||
*/
|
||
(void) WriteBlobMSBULong(image,27L); /* data length=27 */
|
||
PNGType(chunk,mng_FRAM);
|
||
LogPNGChunk(logging,mng_FRAM,27L);
|
||
chunk[4]=4;
|
||
chunk[5]=0; /* frame name separator (no name) */
|
||
chunk[6]=1; /* flag for changing delay, for next frame only */
|
||
chunk[7]=0; /* flag for changing frame timeout */
|
||
chunk[8]=1; /* flag for changing frame clipping for next frame */
|
||
chunk[9]=0; /* flag for changing frame sync_id */
|
||
PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
|
||
chunk[14]=0; /* clipping boundaries delta type */
|
||
PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
|
||
PNGLong(chunk+19,
|
||
(png_uint_32) (mng_info->page.x + ping_width));
|
||
PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
|
||
PNGLong(chunk+27,
|
||
(png_uint_32) (mng_info->page.y + ping_height));
|
||
(void) WriteBlob(image,31,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,31));
|
||
mng_info->old_framing_mode=4;
|
||
mng_info->framing_mode=1;
|
||
}
|
||
|
||
else
|
||
mng_info->framing_mode=3;
|
||
}
|
||
if (mng_info->write_mng && !mng_info->need_fram &&
|
||
((int) image->dispose == 3))
|
||
png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
|
||
|
||
/*
|
||
Free PNG resources.
|
||
*/
|
||
|
||
png_destroy_write_struct(&ping,&ping_info);
|
||
|
||
pixel_info=RelinquishVirtualMemory(pixel_info);
|
||
|
||
if (ping_have_blob != MagickFalse)
|
||
(void) CloseBlob(image);
|
||
|
||
image_info=DestroyImageInfo(image_info);
|
||
image=DestroyImage(image);
|
||
|
||
/* Store bit depth actually written */
|
||
s[0]=(char) ping_bit_depth;
|
||
s[1]='\0';
|
||
|
||
(void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit WriteOnePNGImage()");
|
||
|
||
#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
|
||
UnlockSemaphoreInfo(ping_semaphore);
|
||
#endif
|
||
|
||
/* } for navigation to beginning of SETJMP-protected block. Revert to
|
||
* Throwing an Exception when an error occurs.
|
||
*/
|
||
|
||
return(MagickTrue);
|
||
/* End write one PNG image */
|
||
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% W r i t e P N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% WritePNGImage() writes a Portable Network Graphics (PNG) or
|
||
% Multiple-image Network Graphics (MNG) image file.
|
||
%
|
||
% MNG support written by Glenn Randers-Pehrson, glennrp@image...
|
||
%
|
||
% The format of the WritePNGImage method is:
|
||
%
|
||
% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
|
||
% Image *image,ExceptionInfo *exception)
|
||
%
|
||
% A description of each parameter follows:
|
||
%
|
||
% o image_info: the image info.
|
||
%
|
||
% o image: The image.
|
||
%
|
||
% o exception: return any errors or warnings in this structure.
|
||
%
|
||
% Returns MagickTrue on success, MagickFalse on failure.
|
||
%
|
||
% Communicating with the PNG encoder:
|
||
%
|
||
% While the datastream written is always in PNG format and normally would
|
||
% be given the "png" file extension, this method also writes the following
|
||
% pseudo-formats which are subsets of png:
|
||
%
|
||
% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
|
||
% a depth greater than 8, the depth is reduced. If transparency
|
||
% is present, the tRNS chunk must only have values 0 and 255
|
||
% (i.e., transparency is binary: fully opaque or fully
|
||
% transparent). If other values are present they will be
|
||
% 50%-thresholded to binary transparency. If more than 256
|
||
% colors are present, they will be quantized to the 4-4-4-1,
|
||
% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
|
||
% of any resulting fully-transparent pixels is changed to
|
||
% the image's background color.
|
||
%
|
||
% If you want better quantization or dithering of the colors
|
||
% or alpha than that, you need to do it before calling the
|
||
% PNG encoder. The pixels contain 8-bit indices even if
|
||
% they could be represented with 1, 2, or 4 bits. Grayscale
|
||
% images will be written as indexed PNG files even though the
|
||
% PNG grayscale type might be slightly more efficient. Please
|
||
% note that writing to the PNG8 format may result in loss
|
||
% of color and alpha data.
|
||
%
|
||
% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
|
||
% chunk can be present to convey binary transparency by naming
|
||
% one of the colors as transparent. The only loss incurred
|
||
% is reduction of sample depth to 8. If the image has more
|
||
% than one transparent color, has semitransparent pixels, or
|
||
% has an opaque pixel with the same RGB components as the
|
||
% transparent color, an image is not written.
|
||
%
|
||
% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
|
||
% transparency is permitted, i.e., the alpha sample for
|
||
% each pixel can have any value from 0 to 255. The alpha
|
||
% channel is present even if the image is fully opaque.
|
||
% The only loss in data is the reduction of the sample depth
|
||
% to 8.
|
||
%
|
||
% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
|
||
% chunk can be present to convey binary transparency by naming
|
||
% one of the colors as transparent. If the image has more
|
||
% than one transparent color, has semitransparent pixels, or
|
||
% has an opaque pixel with the same RGB components as the
|
||
% transparent color, an image is not written.
|
||
%
|
||
% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
|
||
% transparency is permitted, i.e., the alpha sample for
|
||
% each pixel can have any value from 0 to 65535. The alpha
|
||
% channel is present even if the image is fully opaque.
|
||
%
|
||
% o PNG00: A PNG that inherits its colortype and bit-depth from the input
|
||
% image, if the input was a PNG, is written. If these values
|
||
% cannot be found, or if the pixels have been changed in a way
|
||
% that makes this impossible, then "PNG00" falls back to the
|
||
% regular "PNG" format.
|
||
%
|
||
% o -define: For more precise control of the PNG output, you can use the
|
||
% Image options "png:bit-depth" and "png:color-type". These
|
||
% can be set from the commandline with "-define" and also
|
||
% from the application programming interfaces. The options
|
||
% are case-independent and are converted to lowercase before
|
||
% being passed to this encoder.
|
||
%
|
||
% png:color-type can be 0, 2, 3, 4, or 6.
|
||
%
|
||
% When png:color-type is 0 (Grayscale), png:bit-depth can
|
||
% be 1, 2, 4, 8, or 16.
|
||
%
|
||
% When png:color-type is 2 (RGB), png:bit-depth can
|
||
% be 8 or 16.
|
||
%
|
||
% When png:color-type is 3 (Indexed), png:bit-depth can
|
||
% be 1, 2, 4, or 8. This refers to the number of bits
|
||
% used to store the index. The color samples always have
|
||
% bit-depth 8 in indexed PNG files.
|
||
%
|
||
% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
|
||
% png:bit-depth can be 8 or 16.
|
||
%
|
||
% If the image cannot be written without loss with the
|
||
% requested bit-depth and color-type, a PNG file will not
|
||
% be written, a warning will be issued, and the encoder will
|
||
% return MagickFalse.
|
||
%
|
||
% Since image encoders should not be responsible for the "heavy lifting",
|
||
% the user should make sure that ImageMagick has already reduced the
|
||
% image depth and number of colors and limit transparency to binary
|
||
% transparency prior to attempting to write the image with depth, color,
|
||
% or transparency limitations.
|
||
%
|
||
% Note that another definition, "png:bit-depth-written" exists, but it
|
||
% is not intended for external use. It is only used internally by the
|
||
% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
|
||
%
|
||
% As of version 6.6.6 the following optimizations are always done:
|
||
%
|
||
% o 32-bit depth is reduced to 16.
|
||
% o 16-bit depth is reduced to 8 if all pixels contain samples whose
|
||
% high byte and low byte are identical.
|
||
% o Palette is sorted to remove unused entries and to put a
|
||
% transparent color first, if BUILD_PNG_PALETTE is defined.
|
||
% o Opaque matte channel is removed when writing an indexed PNG.
|
||
% o Grayscale images are reduced to 1, 2, or 4 bit depth if
|
||
% this can be done without loss and a larger bit depth N was not
|
||
% requested via the "-define png:bit-depth=N" option.
|
||
% o If matte channel is present but only one transparent color is
|
||
% present, RGB+tRNS is written instead of RGBA
|
||
% o Opaque matte channel is removed (or added, if color-type 4 or 6
|
||
% was requested when converting an opaque image).
|
||
%
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
*/
|
||
static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
|
||
Image *image,ExceptionInfo *exception)
|
||
{
|
||
MagickBooleanType
|
||
excluding,
|
||
logging,
|
||
status;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
const char
|
||
*value;
|
||
|
||
int
|
||
source;
|
||
|
||
/*
|
||
Open image file.
|
||
*/
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
assert(image != (Image *) NULL);
|
||
assert(image->signature == MagickCoreSignature);
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
|
||
/*
|
||
Allocate a MngInfo structure.
|
||
*/
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
|
||
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
/*
|
||
Initialize members of the MngInfo structure.
|
||
*/
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
mng_info->image=image;
|
||
mng_info->equal_backgrounds=MagickTrue;
|
||
|
||
/* See if user has requested a specific PNG subformat */
|
||
|
||
mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
|
||
mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
|
||
mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
|
||
mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
|
||
mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
|
||
|
||
value=GetImageOption(image_info,"png:format");
|
||
|
||
if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Format=%s",value);
|
||
|
||
mng_info->write_png8 = MagickFalse;
|
||
mng_info->write_png24 = MagickFalse;
|
||
mng_info->write_png32 = MagickFalse;
|
||
mng_info->write_png48 = MagickFalse;
|
||
mng_info->write_png64 = MagickFalse;
|
||
|
||
if (LocaleCompare(value,"png8") == 0)
|
||
mng_info->write_png8 = MagickTrue;
|
||
|
||
else if (LocaleCompare(value,"png24") == 0)
|
||
mng_info->write_png24 = MagickTrue;
|
||
|
||
else if (LocaleCompare(value,"png32") == 0)
|
||
mng_info->write_png32 = MagickTrue;
|
||
|
||
else if (LocaleCompare(value,"png48") == 0)
|
||
mng_info->write_png48 = MagickTrue;
|
||
|
||
else if (LocaleCompare(value,"png64") == 0)
|
||
mng_info->write_png64 = MagickTrue;
|
||
|
||
else if ((LocaleCompare(value,"png00") == 0) ||
|
||
LocaleCompare(image_info->magick,"PNG00") == 0)
|
||
{
|
||
/* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
|
||
value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
|
||
|
||
if (value != (char *) NULL)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png00 inherited bit depth=%s",value);
|
||
|
||
if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_depth = 1;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_depth = 2;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_depth = 4;
|
||
|
||
else if (LocaleCompare(value,"8") == 0)
|
||
mng_info->write_png_depth = 8;
|
||
|
||
else if (LocaleCompare(value,"16") == 0)
|
||
mng_info->write_png_depth = 16;
|
||
}
|
||
|
||
value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
|
||
|
||
if (value != (char *) NULL)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png00 inherited color type=%s",value);
|
||
|
||
if (LocaleCompare(value,"0") == 0)
|
||
mng_info->write_png_colortype = 1;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_colortype = 3;
|
||
|
||
else if (LocaleCompare(value,"3") == 0)
|
||
mng_info->write_png_colortype = 4;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_colortype = 5;
|
||
|
||
else if (LocaleCompare(value,"6") == 0)
|
||
mng_info->write_png_colortype = 7;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (mng_info->write_png8)
|
||
{
|
||
mng_info->write_png_colortype = /* 3 */ 4;
|
||
mng_info->write_png_depth = 8;
|
||
image->depth = 8;
|
||
}
|
||
|
||
if (mng_info->write_png24)
|
||
{
|
||
mng_info->write_png_colortype = /* 2 */ 3;
|
||
mng_info->write_png_depth = 8;
|
||
image->depth = 8;
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
(void) SetImageType(image,TrueColorAlphaType,exception);
|
||
|
||
else
|
||
(void) SetImageType(image,TrueColorType,exception);
|
||
|
||
(void) SyncImage(image,exception);
|
||
}
|
||
|
||
if (mng_info->write_png32)
|
||
{
|
||
mng_info->write_png_colortype = /* 6 */ 7;
|
||
mng_info->write_png_depth = 8;
|
||
image->depth = 8;
|
||
image->alpha_trait = BlendPixelTrait;
|
||
|
||
(void) SetImageType(image,TrueColorAlphaType,exception);
|
||
(void) SyncImage(image,exception);
|
||
}
|
||
|
||
if (mng_info->write_png48)
|
||
{
|
||
mng_info->write_png_colortype = /* 2 */ 3;
|
||
mng_info->write_png_depth = 16;
|
||
image->depth = 16;
|
||
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
(void) SetImageType(image,TrueColorAlphaType,exception);
|
||
|
||
else
|
||
(void) SetImageType(image,TrueColorType,exception);
|
||
|
||
(void) SyncImage(image,exception);
|
||
}
|
||
|
||
if (mng_info->write_png64)
|
||
{
|
||
mng_info->write_png_colortype = /* 6 */ 7;
|
||
mng_info->write_png_depth = 16;
|
||
image->depth = 16;
|
||
image->alpha_trait = BlendPixelTrait;
|
||
|
||
(void) SetImageType(image,TrueColorAlphaType,exception);
|
||
(void) SyncImage(image,exception);
|
||
}
|
||
|
||
value=GetImageOption(image_info,"png:bit-depth");
|
||
|
||
if (value != (char *) NULL)
|
||
{
|
||
if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_depth = 1;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_depth = 2;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_depth = 4;
|
||
|
||
else if (LocaleCompare(value,"8") == 0)
|
||
mng_info->write_png_depth = 8;
|
||
|
||
else if (LocaleCompare(value,"16") == 0)
|
||
mng_info->write_png_depth = 16;
|
||
|
||
else
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderWarning,
|
||
"ignoring invalid defined png:bit-depth",
|
||
"=%s",value);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
|
||
}
|
||
|
||
value=GetImageOption(image_info,"png:color-type");
|
||
|
||
if (value != (char *) NULL)
|
||
{
|
||
/* We must store colortype+1 because 0 is a valid colortype */
|
||
if (LocaleCompare(value,"0") == 0)
|
||
mng_info->write_png_colortype = 1;
|
||
|
||
else if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_colortype = 2;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_colortype = 3;
|
||
|
||
else if (LocaleCompare(value,"3") == 0)
|
||
mng_info->write_png_colortype = 4;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_colortype = 5;
|
||
|
||
else if (LocaleCompare(value,"6") == 0)
|
||
mng_info->write_png_colortype = 7;
|
||
|
||
else
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderWarning,
|
||
"ignoring invalid defined png:color-type",
|
||
"=%s",value);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:color-type=%d was defined.\n",
|
||
mng_info->write_png_colortype-1);
|
||
}
|
||
|
||
/* Check for chunks to be excluded:
|
||
*
|
||
* The default is to not exclude any known chunks except for any
|
||
* listed in the "unused_chunks" array, above.
|
||
*
|
||
* Chunks can be listed for exclusion via a "png:exclude-chunk"
|
||
* define (in the image properties or in the image artifacts)
|
||
* or via a mng_info member. For convenience, in addition
|
||
* to or instead of a comma-separated list of chunks, the
|
||
* "exclude-chunk" string can be simply "all" or "none".
|
||
*
|
||
* Note that the "-strip" option provides a convenient way of
|
||
* doing the equivalent of
|
||
*
|
||
* -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
|
||
* iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
|
||
*
|
||
* The exclude-chunk define takes priority over the mng_info.
|
||
*
|
||
* A "png:include-chunk" define takes priority over both the
|
||
* mng_info and the "png:exclude-chunk" define. Like the
|
||
* "exclude-chunk" string, it can define "all" or "none" as
|
||
* well as a comma-separated list. Chunks that are unknown to
|
||
* ImageMagick are always excluded, regardless of their "copy-safe"
|
||
* status according to the PNG specification, and even if they
|
||
* appear in the "include-chunk" list. Such defines appearing among
|
||
* the image options take priority over those found among the image
|
||
* artifacts.
|
||
*
|
||
* Finally, all chunks listed in the "unused_chunks" array are
|
||
* automatically excluded, regardless of the other instructions
|
||
* or lack thereof.
|
||
*
|
||
* if you exclude sRGB but not gAMA (recommended), then sRGB chunk
|
||
* will not be written and the gAMA chunk will only be written if it
|
||
* is not between .45 and .46, or approximately (1.0/2.2).
|
||
*
|
||
* If you exclude tRNS and the image has transparency, the colortype
|
||
* is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
|
||
*
|
||
* The -strip option causes StripImage() to set the png:include-chunk
|
||
* artifact to "none,trns,gama".
|
||
*/
|
||
|
||
mng_info->ping_exclude_bKGD=MagickFalse;
|
||
mng_info->ping_exclude_caNv=MagickFalse;
|
||
mng_info->ping_exclude_cHRM=MagickFalse;
|
||
mng_info->ping_exclude_date=MagickFalse;
|
||
mng_info->ping_exclude_eXIf=MagickFalse;
|
||
mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
|
||
mng_info->ping_exclude_gAMA=MagickFalse;
|
||
mng_info->ping_exclude_iCCP=MagickFalse;
|
||
/* mng_info->ping_exclude_iTXt=MagickFalse; */
|
||
mng_info->ping_exclude_oFFs=MagickFalse;
|
||
mng_info->ping_exclude_pHYs=MagickFalse;
|
||
mng_info->ping_exclude_sRGB=MagickFalse;
|
||
mng_info->ping_exclude_tEXt=MagickFalse;
|
||
mng_info->ping_exclude_tIME=MagickFalse;
|
||
mng_info->ping_exclude_tRNS=MagickFalse;
|
||
mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
|
||
mng_info->ping_exclude_zTXt=MagickFalse;
|
||
|
||
mng_info->ping_preserve_colormap=MagickFalse;
|
||
|
||
value=GetImageOption(image_info,"png:preserve-colormap");
|
||
if (value == NULL)
|
||
value=GetImageArtifact(image,"png:preserve-colormap");
|
||
if (value != NULL)
|
||
mng_info->ping_preserve_colormap=MagickTrue;
|
||
|
||
mng_info->ping_preserve_iCCP=MagickFalse;
|
||
|
||
value=GetImageOption(image_info,"png:preserve-iCCP");
|
||
if (value == NULL)
|
||
value=GetImageArtifact(image,"png:preserve-iCCP");
|
||
if (value != NULL)
|
||
mng_info->ping_preserve_iCCP=MagickTrue;
|
||
|
||
/* These compression-level, compression-strategy, and compression-filter
|
||
* defines take precedence over values from the -quality option.
|
||
*/
|
||
value=GetImageOption(image_info,"png:compression-level");
|
||
if (value == NULL)
|
||
value=GetImageArtifact(image,"png:compression-level");
|
||
if (value != NULL)
|
||
{
|
||
/* We have to add 1 to everything because 0 is a valid input,
|
||
* and we want to use 0 (the default) to mean undefined.
|
||
*/
|
||
if (LocaleCompare(value,"0") == 0)
|
||
mng_info->write_png_compression_level = 1;
|
||
|
||
else if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_compression_level = 2;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_compression_level = 3;
|
||
|
||
else if (LocaleCompare(value,"3") == 0)
|
||
mng_info->write_png_compression_level = 4;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_compression_level = 5;
|
||
|
||
else if (LocaleCompare(value,"5") == 0)
|
||
mng_info->write_png_compression_level = 6;
|
||
|
||
else if (LocaleCompare(value,"6") == 0)
|
||
mng_info->write_png_compression_level = 7;
|
||
|
||
else if (LocaleCompare(value,"7") == 0)
|
||
mng_info->write_png_compression_level = 8;
|
||
|
||
else if (LocaleCompare(value,"8") == 0)
|
||
mng_info->write_png_compression_level = 9;
|
||
|
||
else if (LocaleCompare(value,"9") == 0)
|
||
mng_info->write_png_compression_level = 10;
|
||
|
||
else
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderWarning,
|
||
"ignoring invalid defined png:compression-level",
|
||
"=%s",value);
|
||
}
|
||
|
||
value=GetImageOption(image_info,"png:compression-strategy");
|
||
if (value == NULL)
|
||
value=GetImageArtifact(image,"png:compression-strategy");
|
||
if (value != NULL)
|
||
{
|
||
if (LocaleCompare(value,"0") == 0)
|
||
mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
|
||
|
||
else if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_compression_strategy = Z_FILTERED+1;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
|
||
|
||
else if (LocaleCompare(value,"3") == 0)
|
||
#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
|
||
mng_info->write_png_compression_strategy = Z_RLE+1;
|
||
#else
|
||
mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
|
||
#endif
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
|
||
mng_info->write_png_compression_strategy = Z_FIXED+1;
|
||
#else
|
||
mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
|
||
#endif
|
||
|
||
else
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderWarning,
|
||
"ignoring invalid defined png:compression-strategy",
|
||
"=%s",value);
|
||
}
|
||
|
||
value=GetImageOption(image_info,"png:compression-filter");
|
||
if (value == NULL)
|
||
value=GetImageArtifact(image,"png:compression-filter");
|
||
if (value != NULL)
|
||
{
|
||
/* To do: combinations of filters allowed by libpng
|
||
* masks 0x08 through 0xf8
|
||
*
|
||
* Implement this as a comma-separated list of 0,1,2,3,4,5
|
||
* where 5 is a special case meaning PNG_ALL_FILTERS.
|
||
*/
|
||
|
||
if (LocaleCompare(value,"0") == 0)
|
||
mng_info->write_png_compression_filter = 1;
|
||
|
||
else if (LocaleCompare(value,"1") == 0)
|
||
mng_info->write_png_compression_filter = 2;
|
||
|
||
else if (LocaleCompare(value,"2") == 0)
|
||
mng_info->write_png_compression_filter = 3;
|
||
|
||
else if (LocaleCompare(value,"3") == 0)
|
||
mng_info->write_png_compression_filter = 4;
|
||
|
||
else if (LocaleCompare(value,"4") == 0)
|
||
mng_info->write_png_compression_filter = 5;
|
||
|
||
else if (LocaleCompare(value,"5") == 0)
|
||
mng_info->write_png_compression_filter = 6;
|
||
|
||
else
|
||
(void) ThrowMagickException(exception,
|
||
GetMagickModule(),CoderWarning,
|
||
"ignoring invalid defined png:compression-filter",
|
||
"=%s",value);
|
||
}
|
||
|
||
for (source=0; source<8; source++)
|
||
{
|
||
value = (const char *) NULL;
|
||
|
||
switch(source)
|
||
{
|
||
case 0:
|
||
value=GetImageOption(image_info,"png:exclude-chunks");
|
||
break;
|
||
case 1:
|
||
value=GetImageArtifact(image,"png:exclude-chunks");
|
||
break;
|
||
case 2:
|
||
value=GetImageOption(image_info,"png:exclude-chunk");
|
||
break;
|
||
case 3:
|
||
value=GetImageArtifact(image,"png:exclude-chunk");
|
||
break;
|
||
case 4:
|
||
value=GetImageOption(image_info,"png:include-chunks");
|
||
break;
|
||
case 5:
|
||
value=GetImageArtifact(image,"png:include-chunks");
|
||
break;
|
||
case 6:
|
||
value=GetImageOption(image_info,"png:include-chunk");
|
||
break;
|
||
case 7:
|
||
value=GetImageArtifact(image,"png:include-chunk");
|
||
break;
|
||
}
|
||
|
||
if (value == NULL)
|
||
continue;
|
||
|
||
if (source < 4)
|
||
excluding = MagickTrue;
|
||
else
|
||
excluding = MagickFalse;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
if (source == 0 || source == 2)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:exclude-chunk=%s found in image options.\n", value);
|
||
else if (source == 1 || source == 3)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:exclude-chunk=%s found in image artifacts.\n", value);
|
||
else if (source == 4 || source == 6)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:include-chunk=%s found in image options.\n", value);
|
||
else /* if (source == 5 || source == 7) */
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" png:include-chunk=%s found in image artifacts.\n", value);
|
||
}
|
||
|
||
if (IsOptionMember("all",value) != MagickFalse)
|
||
{
|
||
mng_info->ping_exclude_bKGD=excluding;
|
||
mng_info->ping_exclude_caNv=excluding;
|
||
mng_info->ping_exclude_cHRM=excluding;
|
||
mng_info->ping_exclude_date=excluding;
|
||
mng_info->ping_exclude_EXIF=excluding;
|
||
mng_info->ping_exclude_eXIf=excluding;
|
||
mng_info->ping_exclude_gAMA=excluding;
|
||
mng_info->ping_exclude_iCCP=excluding;
|
||
/* mng_info->ping_exclude_iTXt=excluding; */
|
||
mng_info->ping_exclude_oFFs=excluding;
|
||
mng_info->ping_exclude_pHYs=excluding;
|
||
mng_info->ping_exclude_sRGB=excluding;
|
||
mng_info->ping_exclude_tEXt=excluding;
|
||
mng_info->ping_exclude_tIME=excluding;
|
||
mng_info->ping_exclude_tRNS=excluding;
|
||
mng_info->ping_exclude_zCCP=excluding;
|
||
mng_info->ping_exclude_zTXt=excluding;
|
||
}
|
||
|
||
if (IsOptionMember("none",value) != MagickFalse)
|
||
{
|
||
mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
/* mng_info->ping_exclude_iTXt=!excluding; */
|
||
mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
|
||
MagickTrue;
|
||
}
|
||
|
||
if (IsOptionMember("bkgd",value) != MagickFalse)
|
||
mng_info->ping_exclude_bKGD=excluding;
|
||
|
||
if (IsOptionMember("caNv",value) != MagickFalse)
|
||
mng_info->ping_exclude_caNv=excluding;
|
||
|
||
if (IsOptionMember("chrm",value) != MagickFalse)
|
||
mng_info->ping_exclude_cHRM=excluding;
|
||
|
||
if (IsOptionMember("date",value) != MagickFalse)
|
||
mng_info->ping_exclude_date=excluding;
|
||
|
||
if (IsOptionMember("exif",value) != MagickFalse)
|
||
{
|
||
mng_info->ping_exclude_EXIF=excluding;
|
||
mng_info->ping_exclude_eXIf=excluding;
|
||
}
|
||
|
||
if (IsOptionMember("gama",value) != MagickFalse)
|
||
mng_info->ping_exclude_gAMA=excluding;
|
||
|
||
if (IsOptionMember("iccp",value) != MagickFalse)
|
||
mng_info->ping_exclude_iCCP=excluding;
|
||
|
||
#if 0
|
||
if (IsOptionMember("itxt",value) != MagickFalse)
|
||
mng_info->ping_exclude_iTXt=excluding;
|
||
#endif
|
||
|
||
if (IsOptionMember("offs",value) != MagickFalse)
|
||
mng_info->ping_exclude_oFFs=excluding;
|
||
|
||
if (IsOptionMember("phys",value) != MagickFalse)
|
||
mng_info->ping_exclude_pHYs=excluding;
|
||
|
||
if (IsOptionMember("srgb",value) != MagickFalse)
|
||
mng_info->ping_exclude_sRGB=excluding;
|
||
|
||
if (IsOptionMember("text",value) != MagickFalse)
|
||
mng_info->ping_exclude_tEXt=excluding;
|
||
|
||
if (IsOptionMember("time",value) != MagickFalse)
|
||
mng_info->ping_exclude_tIME=excluding;
|
||
|
||
if (IsOptionMember("trns",value) != MagickFalse)
|
||
mng_info->ping_exclude_tRNS=excluding;
|
||
|
||
if (IsOptionMember("zccp",value) != MagickFalse)
|
||
mng_info->ping_exclude_zCCP=excluding;
|
||
|
||
if (IsOptionMember("ztxt",value) != MagickFalse)
|
||
mng_info->ping_exclude_zTXt=excluding;
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Chunks to be excluded from the output png:");
|
||
if (mng_info->ping_exclude_bKGD != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" bKGD");
|
||
if (mng_info->ping_exclude_caNv != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" caNv");
|
||
if (mng_info->ping_exclude_cHRM != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" cHRM");
|
||
if (mng_info->ping_exclude_date != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" date");
|
||
if (mng_info->ping_exclude_EXIF != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" EXIF");
|
||
if (mng_info->ping_exclude_eXIf != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" eXIf");
|
||
if (mng_info->ping_exclude_gAMA != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" gAMA");
|
||
if (mng_info->ping_exclude_iCCP != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" iCCP");
|
||
#if 0
|
||
if (mng_info->ping_exclude_iTXt != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" iTXt");
|
||
#endif
|
||
|
||
if (mng_info->ping_exclude_oFFs != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" oFFs");
|
||
if (mng_info->ping_exclude_pHYs != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" pHYs");
|
||
if (mng_info->ping_exclude_sRGB != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" sRGB");
|
||
if (mng_info->ping_exclude_tEXt != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" tEXt");
|
||
if (mng_info->ping_exclude_tIME != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" tIME");
|
||
if (mng_info->ping_exclude_tRNS != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" tRNS");
|
||
if (mng_info->ping_exclude_zCCP != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" zCCP");
|
||
if (mng_info->ping_exclude_zTXt != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" zTXt");
|
||
}
|
||
|
||
mng_info->need_blob = MagickTrue;
|
||
|
||
status=WriteOnePNGImage(mng_info,image_info,image,exception);
|
||
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
|
||
|
||
return(status);
|
||
}
|
||
|
||
#if defined(JNG_SUPPORTED)
|
||
|
||
/* Write one JNG image */
|
||
static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
|
||
const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
|
||
{
|
||
Image
|
||
*jpeg_image;
|
||
|
||
ImageInfo
|
||
*jpeg_image_info;
|
||
|
||
MagickBooleanType
|
||
logging,
|
||
status;
|
||
|
||
size_t
|
||
length;
|
||
|
||
unsigned char
|
||
*blob,
|
||
chunk[80],
|
||
*p;
|
||
|
||
unsigned int
|
||
jng_alpha_compression_method,
|
||
jng_alpha_sample_depth,
|
||
jng_color_type,
|
||
transparent;
|
||
|
||
size_t
|
||
jng_alpha_quality,
|
||
jng_quality;
|
||
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Enter WriteOneJNGImage()");
|
||
|
||
if ((image->columns > 65500U) || (image->rows > 65500U))
|
||
ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
|
||
|
||
blob=(unsigned char *) NULL;
|
||
jpeg_image=(Image *) NULL;
|
||
jpeg_image_info=(ImageInfo *) NULL;
|
||
length=0;
|
||
|
||
status=MagickTrue;
|
||
transparent=image_info->type==GrayscaleAlphaType ||
|
||
image_info->type==TrueColorAlphaType ||
|
||
image->alpha_trait != UndefinedPixelTrait;
|
||
|
||
jng_alpha_sample_depth = 0;
|
||
|
||
jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
|
||
|
||
jng_alpha_compression_method=(image->compression==JPEGCompression ||
|
||
image_info->compression==JPEGCompression) ? 8 : 0;
|
||
|
||
jng_alpha_quality=image_info->quality == 0UL ? 75UL :
|
||
image_info->quality;
|
||
|
||
if (jng_alpha_quality >= 1000)
|
||
jng_alpha_quality /= 1000;
|
||
|
||
length=0;
|
||
|
||
if (transparent != 0)
|
||
{
|
||
jng_color_type=14;
|
||
|
||
/* Create JPEG blob, image, and image_info */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating jpeg_image_info for alpha.");
|
||
|
||
jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
|
||
|
||
if (jpeg_image_info == (ImageInfo *) NULL)
|
||
{
|
||
jpeg_image_info=DestroyImageInfo(jpeg_image_info);
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating jpeg_image.");
|
||
|
||
jpeg_image=SeparateImage(image,AlphaChannel,exception);
|
||
if (jpeg_image == (Image *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
(void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
|
||
jpeg_image->alpha_trait=UndefinedPixelTrait;
|
||
jpeg_image->quality=jng_alpha_quality;
|
||
jpeg_image_info->type=GrayscaleType;
|
||
(void) SetImageType(jpeg_image,GrayscaleType,exception);
|
||
(void) AcquireUniqueFilename(jpeg_image->filename);
|
||
(void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
|
||
"%s",jpeg_image->filename);
|
||
}
|
||
else
|
||
{
|
||
jng_alpha_compression_method=0;
|
||
jng_color_type=10;
|
||
jng_alpha_sample_depth=0;
|
||
}
|
||
|
||
/* To do: check bit depth of PNG alpha channel */
|
||
|
||
/* Check if image is grayscale. */
|
||
if (image_info->type != TrueColorAlphaType && image_info->type !=
|
||
TrueColorType && SetImageGray(image,exception))
|
||
jng_color_type-=2;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG Quality = %d",(int) jng_quality);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG Color Type = %d",jng_color_type);
|
||
if (transparent != 0)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG Alpha Compression = %d",jng_alpha_compression_method);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG Alpha Depth = %d",jng_alpha_sample_depth);
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG Alpha Quality = %d",(int) jng_alpha_quality);
|
||
}
|
||
}
|
||
|
||
if (transparent != 0)
|
||
{
|
||
if (jng_alpha_compression_method==0)
|
||
{
|
||
const char
|
||
*value;
|
||
|
||
/* Encode alpha as a grayscale PNG blob */
|
||
status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
|
||
exception);
|
||
if (status == MagickFalse)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating PNG blob.");
|
||
|
||
(void) CopyMagickString(jpeg_image_info->magick,"PNG",
|
||
MagickPathExtent);
|
||
(void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
|
||
jpeg_image_info->interlace=NoInterlace;
|
||
|
||
/* Exclude all ancillary chunks */
|
||
(void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
|
||
|
||
blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
|
||
&length,exception);
|
||
|
||
/* Retrieve sample depth used */
|
||
value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
|
||
if (value != (char *) NULL)
|
||
jng_alpha_sample_depth= (unsigned int) value[0];
|
||
}
|
||
else
|
||
{
|
||
/* Encode alpha as a grayscale JPEG blob */
|
||
|
||
status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
|
||
exception);
|
||
if (status == MagickFalse)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
(void) CopyMagickString(jpeg_image_info->magick,"JPEG",
|
||
MagickPathExtent);
|
||
(void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
|
||
jpeg_image_info->interlace=NoInterlace;
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating blob.");
|
||
blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
|
||
&length,exception);
|
||
if (blob == (unsigned char *) NULL)
|
||
{
|
||
if (jpeg_image != (Image *)NULL)
|
||
jpeg_image=DestroyImage(jpeg_image);
|
||
if (jpeg_image_info != (ImageInfo *)NULL)
|
||
jpeg_image_info=DestroyImageInfo(jpeg_image_info);
|
||
return(MagickFalse);
|
||
}
|
||
jng_alpha_sample_depth=8;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Successfully read jpeg_image into a blob, length=%.20g.",
|
||
(double) length);
|
||
|
||
}
|
||
/* Destroy JPEG image and image_info */
|
||
jpeg_image=DestroyImage(jpeg_image);
|
||
(void) RelinquishUniqueFileResource(jpeg_image_info->filename);
|
||
jpeg_image_info=DestroyImageInfo(jpeg_image_info);
|
||
}
|
||
|
||
/* Write JHDR chunk */
|
||
(void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
|
||
PNGType(chunk,mng_JHDR);
|
||
LogPNGChunk(logging,mng_JHDR,16L);
|
||
PNGLong(chunk+4,(png_uint_32) image->columns);
|
||
PNGLong(chunk+8,(png_uint_32) image->rows);
|
||
chunk[12]=jng_color_type;
|
||
chunk[13]=8; /* sample depth */
|
||
chunk[14]=8; /*jng_image_compression_method */
|
||
chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
|
||
chunk[16]=jng_alpha_sample_depth;
|
||
chunk[17]=jng_alpha_compression_method;
|
||
chunk[18]=0; /*jng_alpha_filter_method */
|
||
chunk[19]=0; /*jng_alpha_interlace_method */
|
||
(void) WriteBlob(image,20,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,20));
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG width:%15lu",(unsigned long) image->columns);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG height:%14lu",(unsigned long) image->rows);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG color type:%10d",jng_color_type);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG sample depth:%8d",8);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG compression:%9d",8);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG interlace:%11d",0);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG alpha depth:%9d",jng_alpha_sample_depth);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG alpha compression:%3d",jng_alpha_compression_method);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG alpha filter:%8d",0);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" JNG alpha interlace:%5d",0);
|
||
}
|
||
|
||
/*
|
||
Write leading ancillary chunks
|
||
*/
|
||
|
||
if (transparent != 0)
|
||
{
|
||
/*
|
||
Write JNG bKGD chunk
|
||
*/
|
||
|
||
unsigned char
|
||
blue,
|
||
green,
|
||
red;
|
||
|
||
ssize_t
|
||
num_bytes;
|
||
|
||
if (jng_color_type == 8 || jng_color_type == 12)
|
||
num_bytes=6L;
|
||
else
|
||
num_bytes=10L;
|
||
(void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
|
||
PNGType(chunk,mng_bKGD);
|
||
LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
|
||
red=ScaleQuantumToChar(image->background_color.red);
|
||
green=ScaleQuantumToChar(image->background_color.green);
|
||
blue=ScaleQuantumToChar(image->background_color.blue);
|
||
*(chunk+4)=0;
|
||
*(chunk+5)=red;
|
||
*(chunk+6)=0;
|
||
*(chunk+7)=green;
|
||
*(chunk+8)=0;
|
||
*(chunk+9)=blue;
|
||
(void) WriteBlob(image,(size_t) num_bytes,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
|
||
}
|
||
|
||
if ((image->colorspace == sRGBColorspace || image->rendering_intent))
|
||
{
|
||
/*
|
||
Write JNG sRGB chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,1L);
|
||
PNGType(chunk,mng_sRGB);
|
||
LogPNGChunk(logging,mng_sRGB,1L);
|
||
|
||
if (image->rendering_intent != UndefinedIntent)
|
||
chunk[4]=(unsigned char)
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent(
|
||
(image->rendering_intent));
|
||
|
||
else
|
||
chunk[4]=(unsigned char)
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent(
|
||
(PerceptualIntent));
|
||
|
||
(void) WriteBlob(image,5,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,5));
|
||
}
|
||
else
|
||
{
|
||
if (image->gamma != 0.0)
|
||
{
|
||
/*
|
||
Write JNG gAMA chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,4L);
|
||
PNGType(chunk,mng_gAMA);
|
||
LogPNGChunk(logging,mng_gAMA,4L);
|
||
PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
|
||
(void) WriteBlob(image,8,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,8));
|
||
}
|
||
|
||
if ((mng_info->equal_chrms == MagickFalse) &&
|
||
(image->chromaticity.red_primary.x != 0.0))
|
||
{
|
||
PrimaryInfo
|
||
primary;
|
||
|
||
/*
|
||
Write JNG cHRM chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,32L);
|
||
PNGType(chunk,mng_cHRM);
|
||
LogPNGChunk(logging,mng_cHRM,32L);
|
||
primary=image->chromaticity.white_point;
|
||
PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.red_primary;
|
||
PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.green_primary;
|
||
PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.blue_primary;
|
||
PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
|
||
(void) WriteBlob(image,36,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,36));
|
||
}
|
||
}
|
||
|
||
if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
|
||
{
|
||
/*
|
||
Write JNG pHYs chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,9L);
|
||
PNGType(chunk,mng_pHYs);
|
||
LogPNGChunk(logging,mng_pHYs,9L);
|
||
if (image->units == PixelsPerInchResolution)
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32)
|
||
(image->resolution.x*100.0/2.54+0.5));
|
||
|
||
PNGLong(chunk+8,(png_uint_32)
|
||
(image->resolution.y*100.0/2.54+0.5));
|
||
|
||
chunk[12]=1;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (image->units == PixelsPerCentimeterResolution)
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32)
|
||
(image->resolution.x*100.0+0.5));
|
||
|
||
PNGLong(chunk+8,(png_uint_32)
|
||
(image->resolution.y*100.0+0.5));
|
||
|
||
chunk[12]=1;
|
||
}
|
||
|
||
else
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
|
||
PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
|
||
chunk[12]=0;
|
||
}
|
||
}
|
||
(void) WriteBlob(image,13,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,13));
|
||
}
|
||
|
||
if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
|
||
{
|
||
/*
|
||
Write JNG oFFs chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,9L);
|
||
PNGType(chunk,mng_oFFs);
|
||
LogPNGChunk(logging,mng_oFFs,9L);
|
||
PNGsLong(chunk+4,(ssize_t) (image->page.x));
|
||
PNGsLong(chunk+8,(ssize_t) (image->page.y));
|
||
chunk[12]=0;
|
||
(void) WriteBlob(image,13,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,13));
|
||
}
|
||
|
||
if (transparent != 0)
|
||
{
|
||
if (jng_alpha_compression_method==0)
|
||
{
|
||
ssize_t
|
||
i;
|
||
|
||
size_t
|
||
len;
|
||
|
||
/* Write IDAT chunk header */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Write IDAT chunks from blob, length=%.20g.",(double)
|
||
length);
|
||
|
||
/* Copy IDAT chunks */
|
||
len=0;
|
||
p=blob+8;
|
||
for (i=8; i<(ssize_t) length; i+=len+12)
|
||
{
|
||
len=(((unsigned int) *(p ) & 0xff) << 24) +
|
||
(((unsigned int) *(p + 1) & 0xff) << 16) +
|
||
(((unsigned int) *(p + 2) & 0xff) << 8) +
|
||
(((unsigned int) *(p + 3) & 0xff) ) ;
|
||
p+=4;
|
||
|
||
if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
|
||
{
|
||
/* Found an IDAT chunk. */
|
||
(void) WriteBlobMSBULong(image,len);
|
||
LogPNGChunk(logging,mng_IDAT,len);
|
||
(void) WriteBlob(image,len+4,p);
|
||
(void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
|
||
}
|
||
|
||
else
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Skipping %c%c%c%c chunk, length=%.20g.",
|
||
*(p),*(p+1),*(p+2),*(p+3),(double) len);
|
||
}
|
||
p+=(8+len);
|
||
}
|
||
}
|
||
else if (length != 0)
|
||
{
|
||
/* Write JDAA chunk header */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Write JDAA chunk, length=%.20g.",(double) length);
|
||
(void) WriteBlobMSBULong(image,(size_t) length);
|
||
PNGType(chunk,mng_JDAA);
|
||
LogPNGChunk(logging,mng_JDAA,length);
|
||
/* Write JDAT chunk(s) data */
|
||
(void) WriteBlob(image,4,chunk);
|
||
(void) WriteBlob(image,length,blob);
|
||
(void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
|
||
(uInt) length));
|
||
}
|
||
blob=(unsigned char *) RelinquishMagickMemory(blob);
|
||
}
|
||
|
||
/* Encode image as a JPEG blob */
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating jpeg_image_info.");
|
||
jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
|
||
if (jpeg_image_info == (ImageInfo *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating jpeg_image.");
|
||
|
||
jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
|
||
if (jpeg_image == (Image *) NULL)
|
||
{
|
||
jpeg_image_info=DestroyImageInfo(jpeg_image_info);
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
}
|
||
(void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
|
||
|
||
(void) AcquireUniqueFilename(jpeg_image->filename);
|
||
(void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
|
||
jpeg_image->filename);
|
||
|
||
status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
|
||
exception);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
|
||
(double) jpeg_image->rows);
|
||
|
||
if (status == MagickFalse)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
|
||
if (jng_color_type == 8 || jng_color_type == 12)
|
||
jpeg_image_info->type=GrayscaleType;
|
||
|
||
jpeg_image_info->quality=jng_quality;
|
||
jpeg_image->quality=jng_quality;
|
||
(void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
|
||
(void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Creating blob.");
|
||
|
||
blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
|
||
exception);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Successfully read jpeg_image into a blob, length=%.20g.",
|
||
(double) length);
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Write JDAT chunk, length=%.20g.",(double) length);
|
||
}
|
||
|
||
/* Write JDAT chunk(s) */
|
||
(void) WriteBlobMSBULong(image,(size_t) length);
|
||
PNGType(chunk,mng_JDAT);
|
||
LogPNGChunk(logging,mng_JDAT,length);
|
||
(void) WriteBlob(image,4,chunk);
|
||
(void) WriteBlob(image,length,blob);
|
||
(void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
|
||
|
||
jpeg_image=DestroyImage(jpeg_image);
|
||
(void) RelinquishUniqueFileResource(jpeg_image_info->filename);
|
||
jpeg_image_info=DestroyImageInfo(jpeg_image_info);
|
||
blob=(unsigned char *) RelinquishMagickMemory(blob);
|
||
|
||
/* Write IEND chunk */
|
||
(void) WriteBlobMSBULong(image,0L);
|
||
PNGType(chunk,mng_IEND);
|
||
LogPNGChunk(logging,mng_IEND,0);
|
||
(void) WriteBlob(image,4,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,4));
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" exit WriteOneJNGImage()");
|
||
|
||
return(status);
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% W r i t e J N G I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
|
||
%
|
||
% JNG support written by Glenn Randers-Pehrson, glennrp@image...
|
||
%
|
||
% The format of the WriteJNGImage method is:
|
||
%
|
||
% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
|
||
% Image *image,ExceptionInfo *exception)
|
||
%
|
||
% A description of each parameter follows:
|
||
%
|
||
% o image_info: the image info.
|
||
%
|
||
% o image: The image.
|
||
%
|
||
% o exception: return any errors or warnings in this structure.
|
||
%
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
*/
|
||
static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
|
||
Image *image, ExceptionInfo *exception)
|
||
{
|
||
MagickBooleanType
|
||
logging,
|
||
status;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
/*
|
||
Open image file.
|
||
*/
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
assert(image != (Image *) NULL);
|
||
assert(image->signature == MagickCoreSignature);
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
|
||
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
|
||
if (status == MagickFalse)
|
||
return(status);
|
||
if ((image->columns > 65535UL) || (image->rows > 65535UL))
|
||
ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
|
||
|
||
/*
|
||
Allocate a MngInfo structure.
|
||
*/
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
/*
|
||
Initialize members of the MngInfo structure.
|
||
*/
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
mng_info->image=image;
|
||
|
||
(void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
|
||
|
||
status=WriteOneJNGImage(mng_info,image_info,image,exception);
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
(void) CloseBlob(image);
|
||
|
||
(void) CatchImageException(image);
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
|
||
return(status);
|
||
}
|
||
#endif
|
||
|
||
static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
|
||
Image *image, ExceptionInfo *exception)
|
||
{
|
||
Image
|
||
*next_image;
|
||
|
||
MagickBooleanType
|
||
status;
|
||
|
||
volatile MagickBooleanType
|
||
logging;
|
||
|
||
MngInfo
|
||
*mng_info;
|
||
|
||
int
|
||
image_count,
|
||
need_iterations,
|
||
need_matte;
|
||
|
||
volatile int
|
||
#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
|
||
defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
need_local_plte,
|
||
#endif
|
||
all_images_are_gray,
|
||
need_defi,
|
||
use_global_plte;
|
||
|
||
ssize_t
|
||
i;
|
||
|
||
unsigned char
|
||
chunk[800];
|
||
|
||
volatile unsigned int
|
||
write_jng,
|
||
write_mng;
|
||
|
||
volatile size_t
|
||
scene;
|
||
|
||
size_t
|
||
final_delay=0,
|
||
imageListLength,
|
||
initial_delay;
|
||
|
||
#if (PNG_LIBPNG_VER < 10200)
|
||
if (image_info->verbose)
|
||
printf("Your PNG library (libpng-%s) is rather old.\n",
|
||
PNG_LIBPNG_VER_STRING);
|
||
#endif
|
||
|
||
/*
|
||
Open image file.
|
||
*/
|
||
assert(image_info != (const ImageInfo *) NULL);
|
||
assert(image_info->signature == MagickCoreSignature);
|
||
assert(image != (Image *) NULL);
|
||
assert(image->signature == MagickCoreSignature);
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||
logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
|
||
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
|
||
if (status == MagickFalse)
|
||
return(status);
|
||
|
||
/*
|
||
Allocate a MngInfo structure.
|
||
*/
|
||
mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
|
||
if (mng_info == (MngInfo *) NULL)
|
||
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
|
||
/*
|
||
Initialize members of the MngInfo structure.
|
||
*/
|
||
(void) memset(mng_info,0,sizeof(MngInfo));
|
||
mng_info->image=image;
|
||
write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
|
||
|
||
/*
|
||
* See if user has requested a specific PNG subformat to be used
|
||
* for all of the PNGs in the MNG being written, e.g.,
|
||
*
|
||
* convert *.png png8:animation.mng
|
||
*
|
||
* To do: check -define png:bit_depth and png:color_type as well,
|
||
* or perhaps use mng:bit_depth and mng:color_type instead for
|
||
* global settings.
|
||
*/
|
||
|
||
mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
|
||
mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
|
||
mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
|
||
|
||
write_jng=MagickFalse;
|
||
if (image_info->compression == JPEGCompression)
|
||
write_jng=MagickTrue;
|
||
|
||
mng_info->adjoin=image_info->adjoin &&
|
||
(GetNextImageInList(image) != (Image *) NULL) && write_mng;
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
/* Log some info about the input */
|
||
Image
|
||
*p;
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Checking input image(s)\n"
|
||
" Image_info depth: %.20g, Type: %d",
|
||
(double) image_info->depth, image_info->type);
|
||
|
||
scene=0;
|
||
for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
|
||
{
|
||
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Scene: %.20g\n, Image depth: %.20g",
|
||
(double) scene++, (double) p->depth);
|
||
|
||
if (p->alpha_trait != UndefinedPixelTrait)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Matte: True");
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Matte: False");
|
||
|
||
if (p->storage_class == PseudoClass)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Storage class: PseudoClass");
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Storage class: DirectClass");
|
||
|
||
if (p->colors)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Number of colors: %.20g",(double) p->colors);
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Number of colors: unspecified");
|
||
|
||
if (mng_info->adjoin == MagickFalse)
|
||
break;
|
||
}
|
||
}
|
||
|
||
use_global_plte=MagickFalse;
|
||
all_images_are_gray=MagickFalse;
|
||
#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
|
||
need_local_plte=MagickTrue;
|
||
#endif
|
||
need_defi=MagickFalse;
|
||
need_matte=MagickFalse;
|
||
mng_info->framing_mode=1;
|
||
mng_info->old_framing_mode=1;
|
||
|
||
if (write_mng)
|
||
if (image_info->page != (char *) NULL)
|
||
{
|
||
/*
|
||
Determine image bounding box.
|
||
*/
|
||
SetGeometry(image,&mng_info->page);
|
||
(void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
|
||
&mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
|
||
}
|
||
if (write_mng)
|
||
{
|
||
unsigned int
|
||
need_geom;
|
||
|
||
unsigned short
|
||
red,
|
||
green,
|
||
blue;
|
||
|
||
const char *
|
||
option;
|
||
|
||
mng_info->page=image->page;
|
||
need_geom=MagickTrue;
|
||
if (mng_info->page.width || mng_info->page.height)
|
||
need_geom=MagickFalse;
|
||
/*
|
||
Check all the scenes.
|
||
*/
|
||
initial_delay=image->delay;
|
||
need_iterations=MagickFalse;
|
||
mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
|
||
mng_info->equal_physs=MagickTrue,
|
||
mng_info->equal_gammas=MagickTrue;
|
||
mng_info->equal_srgbs=MagickTrue;
|
||
mng_info->equal_backgrounds=MagickTrue;
|
||
image_count=0;
|
||
#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
|
||
defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
all_images_are_gray=MagickTrue;
|
||
mng_info->equal_palettes=MagickFalse;
|
||
need_local_plte=MagickFalse;
|
||
#endif
|
||
for (next_image=image; next_image != (Image *) NULL; )
|
||
{
|
||
if (need_geom)
|
||
{
|
||
if ((next_image->columns+next_image->page.x) > mng_info->page.width)
|
||
mng_info->page.width=next_image->columns+next_image->page.x;
|
||
|
||
if ((next_image->rows+next_image->page.y) > mng_info->page.height)
|
||
mng_info->page.height=next_image->rows+next_image->page.y;
|
||
}
|
||
|
||
if (next_image->page.x || next_image->page.y)
|
||
need_defi=MagickTrue;
|
||
|
||
if (next_image->alpha_trait != UndefinedPixelTrait)
|
||
need_matte=MagickTrue;
|
||
|
||
if ((int) next_image->dispose >= BackgroundDispose)
|
||
if ((next_image->alpha_trait != UndefinedPixelTrait) ||
|
||
next_image->page.x || next_image->page.y ||
|
||
((next_image->columns < mng_info->page.width) &&
|
||
(next_image->rows < mng_info->page.height)))
|
||
mng_info->need_fram=MagickTrue;
|
||
|
||
if (next_image->iterations)
|
||
need_iterations=MagickTrue;
|
||
|
||
final_delay=next_image->delay;
|
||
|
||
if (final_delay != initial_delay || final_delay > 1UL*
|
||
next_image->ticks_per_second)
|
||
mng_info->need_fram=1;
|
||
|
||
#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
|
||
defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
/*
|
||
check for global palette possibility.
|
||
*/
|
||
if (image->alpha_trait != UndefinedPixelTrait)
|
||
need_local_plte=MagickTrue;
|
||
|
||
if (need_local_plte == 0)
|
||
{
|
||
if (SetImageGray(image,exception) == MagickFalse)
|
||
all_images_are_gray=MagickFalse;
|
||
mng_info->equal_palettes=PalettesAreEqual(image,next_image);
|
||
if (use_global_plte == 0)
|
||
use_global_plte=mng_info->equal_palettes;
|
||
need_local_plte=!mng_info->equal_palettes;
|
||
}
|
||
#endif
|
||
if (GetNextImageInList(next_image) != (Image *) NULL)
|
||
{
|
||
if (next_image->background_color.red !=
|
||
next_image->next->background_color.red ||
|
||
next_image->background_color.green !=
|
||
next_image->next->background_color.green ||
|
||
next_image->background_color.blue !=
|
||
next_image->next->background_color.blue)
|
||
mng_info->equal_backgrounds=MagickFalse;
|
||
|
||
if (next_image->gamma != next_image->next->gamma)
|
||
mng_info->equal_gammas=MagickFalse;
|
||
|
||
if (next_image->rendering_intent !=
|
||
next_image->next->rendering_intent)
|
||
mng_info->equal_srgbs=MagickFalse;
|
||
|
||
if ((next_image->units != next_image->next->units) ||
|
||
(next_image->resolution.x != next_image->next->resolution.x) ||
|
||
(next_image->resolution.y != next_image->next->resolution.y))
|
||
mng_info->equal_physs=MagickFalse;
|
||
|
||
if (mng_info->equal_chrms)
|
||
{
|
||
if (next_image->chromaticity.red_primary.x !=
|
||
next_image->next->chromaticity.red_primary.x ||
|
||
next_image->chromaticity.red_primary.y !=
|
||
next_image->next->chromaticity.red_primary.y ||
|
||
next_image->chromaticity.green_primary.x !=
|
||
next_image->next->chromaticity.green_primary.x ||
|
||
next_image->chromaticity.green_primary.y !=
|
||
next_image->next->chromaticity.green_primary.y ||
|
||
next_image->chromaticity.blue_primary.x !=
|
||
next_image->next->chromaticity.blue_primary.x ||
|
||
next_image->chromaticity.blue_primary.y !=
|
||
next_image->next->chromaticity.blue_primary.y ||
|
||
next_image->chromaticity.white_point.x !=
|
||
next_image->next->chromaticity.white_point.x ||
|
||
next_image->chromaticity.white_point.y !=
|
||
next_image->next->chromaticity.white_point.y)
|
||
mng_info->equal_chrms=MagickFalse;
|
||
}
|
||
}
|
||
image_count++;
|
||
next_image=GetNextImageInList(next_image);
|
||
}
|
||
if (image_count < 2)
|
||
{
|
||
mng_info->equal_backgrounds=MagickFalse;
|
||
mng_info->equal_chrms=MagickFalse;
|
||
mng_info->equal_gammas=MagickFalse;
|
||
mng_info->equal_srgbs=MagickFalse;
|
||
mng_info->equal_physs=MagickFalse;
|
||
use_global_plte=MagickFalse;
|
||
#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
|
||
need_local_plte=MagickTrue;
|
||
#endif
|
||
need_iterations=MagickFalse;
|
||
}
|
||
|
||
if (mng_info->need_fram == MagickFalse)
|
||
{
|
||
/*
|
||
Only certain framing rates 100/n are exactly representable without
|
||
the FRAM chunk but we'll allow some slop in VLC files
|
||
*/
|
||
if (final_delay == 0)
|
||
{
|
||
if (need_iterations != MagickFalse)
|
||
{
|
||
/*
|
||
It's probably a GIF with loop; don't run it *too* fast.
|
||
*/
|
||
if (mng_info->adjoin)
|
||
{
|
||
final_delay=10;
|
||
(void) ThrowMagickException(exception,GetMagickModule(),
|
||
CoderWarning,
|
||
"input has zero delay between all frames; assuming",
|
||
" 10 cs `%s'","");
|
||
}
|
||
}
|
||
else
|
||
mng_info->ticks_per_second=0;
|
||
}
|
||
if (final_delay != 0)
|
||
mng_info->ticks_per_second=(png_uint_32)
|
||
(image->ticks_per_second/final_delay);
|
||
if (final_delay > 50)
|
||
mng_info->ticks_per_second=2;
|
||
|
||
if (final_delay > 75)
|
||
mng_info->ticks_per_second=1;
|
||
|
||
if (final_delay > 125)
|
||
mng_info->need_fram=MagickTrue;
|
||
|
||
if (need_defi && final_delay > 2 && (final_delay != 4) &&
|
||
(final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
|
||
(final_delay != 25) && (final_delay != 50) &&
|
||
(final_delay != (size_t) image->ticks_per_second))
|
||
mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
|
||
}
|
||
|
||
if (mng_info->need_fram != MagickFalse)
|
||
mng_info->ticks_per_second=image->ticks_per_second;
|
||
/*
|
||
If pseudocolor, we should also check to see if all the
|
||
palettes are identical and write a global PLTE if they are.
|
||
../glennrp Feb 99.
|
||
*/
|
||
/*
|
||
Write the MNG version 1.0 signature and MHDR chunk.
|
||
*/
|
||
(void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
|
||
(void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
|
||
PNGType(chunk,mng_MHDR);
|
||
LogPNGChunk(logging,mng_MHDR,28L);
|
||
PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
|
||
PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
|
||
PNGLong(chunk+12,mng_info->ticks_per_second);
|
||
PNGLong(chunk+16,0L); /* layer count=unknown */
|
||
PNGLong(chunk+20,0L); /* frame count=unknown */
|
||
PNGLong(chunk+24,0L); /* play time=unknown */
|
||
if (write_jng)
|
||
{
|
||
if (need_matte)
|
||
{
|
||
if (need_defi || mng_info->need_fram || use_global_plte)
|
||
PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
|
||
|
||
else
|
||
PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
|
||
}
|
||
|
||
else
|
||
{
|
||
if (need_defi || mng_info->need_fram || use_global_plte)
|
||
PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
|
||
|
||
else
|
||
PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
if (need_matte)
|
||
{
|
||
if (need_defi || mng_info->need_fram || use_global_plte)
|
||
PNGLong(chunk+28,11L); /* simplicity=LC */
|
||
|
||
else
|
||
PNGLong(chunk+28,9L); /* simplicity=VLC */
|
||
}
|
||
|
||
else
|
||
{
|
||
if (need_defi || mng_info->need_fram || use_global_plte)
|
||
PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
|
||
|
||
else
|
||
PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
|
||
}
|
||
}
|
||
(void) WriteBlob(image,32,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,32));
|
||
option=GetImageOption(image_info,"mng:need-cacheoff");
|
||
if (option != (const char *) NULL)
|
||
{
|
||
size_t
|
||
length;
|
||
/*
|
||
Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
|
||
*/
|
||
PNGType(chunk,mng_nEED);
|
||
length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
|
||
(void) WriteBlobMSBULong(image,(size_t) length);
|
||
LogPNGChunk(logging,mng_nEED,(size_t) length);
|
||
length+=4;
|
||
(void) WriteBlob(image,length,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
|
||
}
|
||
if ((GetPreviousImageInList(image) == (Image *) NULL) &&
|
||
(GetNextImageInList(image) != (Image *) NULL) &&
|
||
(image->iterations != 1))
|
||
{
|
||
/*
|
||
Write MNG TERM chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,10L); /* data length=10 */
|
||
PNGType(chunk,mng_TERM);
|
||
LogPNGChunk(logging,mng_TERM,10L);
|
||
chunk[4]=3; /* repeat animation */
|
||
chunk[5]=0; /* show last frame when done */
|
||
PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
|
||
final_delay/MagickMax(image->ticks_per_second,1)));
|
||
|
||
if (image->iterations == 0)
|
||
PNGLong(chunk+10,PNG_UINT_31_MAX);
|
||
|
||
else
|
||
PNGLong(chunk+10,(png_uint_32) image->iterations);
|
||
|
||
if (logging != MagickFalse)
|
||
{
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" TERM delay: %.20g",(double) (mng_info->ticks_per_second*
|
||
final_delay/MagickMax(image->ticks_per_second,1)));
|
||
|
||
if (image->iterations == 0)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
|
||
|
||
else
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Image iterations: %.20g",(double) image->iterations);
|
||
}
|
||
(void) WriteBlob(image,14,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,14));
|
||
}
|
||
/*
|
||
To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
|
||
*/
|
||
if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
|
||
mng_info->equal_srgbs)
|
||
{
|
||
/*
|
||
Write MNG sRGB chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,1L);
|
||
PNGType(chunk,mng_sRGB);
|
||
LogPNGChunk(logging,mng_sRGB,1L);
|
||
|
||
if (image->rendering_intent != UndefinedIntent)
|
||
chunk[4]=(unsigned char)
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent(
|
||
(image->rendering_intent));
|
||
|
||
else
|
||
chunk[4]=(unsigned char)
|
||
Magick_RenderingIntent_to_PNG_RenderingIntent(
|
||
(PerceptualIntent));
|
||
|
||
(void) WriteBlob(image,5,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,5));
|
||
mng_info->have_write_global_srgb=MagickTrue;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (image->gamma && mng_info->equal_gammas)
|
||
{
|
||
/*
|
||
Write MNG gAMA chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,4L);
|
||
PNGType(chunk,mng_gAMA);
|
||
LogPNGChunk(logging,mng_gAMA,4L);
|
||
PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
|
||
(void) WriteBlob(image,8,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,8));
|
||
mng_info->have_write_global_gama=MagickTrue;
|
||
}
|
||
if (mng_info->equal_chrms)
|
||
{
|
||
PrimaryInfo
|
||
primary;
|
||
|
||
/*
|
||
Write MNG cHRM chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,32L);
|
||
PNGType(chunk,mng_cHRM);
|
||
LogPNGChunk(logging,mng_cHRM,32L);
|
||
primary=image->chromaticity.white_point;
|
||
PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.red_primary;
|
||
PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.green_primary;
|
||
PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
|
||
primary=image->chromaticity.blue_primary;
|
||
PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
|
||
PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
|
||
(void) WriteBlob(image,36,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,36));
|
||
mng_info->have_write_global_chrm=MagickTrue;
|
||
}
|
||
}
|
||
if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
|
||
{
|
||
/*
|
||
Write MNG pHYs chunk
|
||
*/
|
||
(void) WriteBlobMSBULong(image,9L);
|
||
PNGType(chunk,mng_pHYs);
|
||
LogPNGChunk(logging,mng_pHYs,9L);
|
||
|
||
if (image->units == PixelsPerInchResolution)
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32)
|
||
(image->resolution.x*100.0/2.54+0.5));
|
||
|
||
PNGLong(chunk+8,(png_uint_32)
|
||
(image->resolution.y*100.0/2.54+0.5));
|
||
|
||
chunk[12]=1;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (image->units == PixelsPerCentimeterResolution)
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32)
|
||
(image->resolution.x*100.0+0.5));
|
||
|
||
PNGLong(chunk+8,(png_uint_32)
|
||
(image->resolution.y*100.0+0.5));
|
||
|
||
chunk[12]=1;
|
||
}
|
||
|
||
else
|
||
{
|
||
PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
|
||
PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
|
||
chunk[12]=0;
|
||
}
|
||
}
|
||
(void) WriteBlob(image,13,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,13));
|
||
}
|
||
/*
|
||
Write MNG BACK chunk and global bKGD chunk, if the image is transparent
|
||
or does not cover the entire frame.
|
||
*/
|
||
if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
|
||
image->page.x > 0 || image->page.y > 0 || (image->page.width &&
|
||
(image->page.width+image->page.x < mng_info->page.width))
|
||
|| (image->page.height && (image->page.height+image->page.y
|
||
< mng_info->page.height))))
|
||
{
|
||
(void) WriteBlobMSBULong(image,6L);
|
||
PNGType(chunk,mng_BACK);
|
||
LogPNGChunk(logging,mng_BACK,6L);
|
||
red=ScaleQuantumToShort(image->background_color.red);
|
||
green=ScaleQuantumToShort(image->background_color.green);
|
||
blue=ScaleQuantumToShort(image->background_color.blue);
|
||
PNGShort(chunk+4,red);
|
||
PNGShort(chunk+6,green);
|
||
PNGShort(chunk+8,blue);
|
||
(void) WriteBlob(image,10,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,10));
|
||
if (mng_info->equal_backgrounds)
|
||
{
|
||
(void) WriteBlobMSBULong(image,6L);
|
||
PNGType(chunk,mng_bKGD);
|
||
LogPNGChunk(logging,mng_bKGD,6L);
|
||
(void) WriteBlob(image,10,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,10));
|
||
}
|
||
}
|
||
|
||
#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
|
||
if ((need_local_plte == MagickFalse) &&
|
||
(image->storage_class == PseudoClass) &&
|
||
(all_images_are_gray == MagickFalse))
|
||
{
|
||
size_t
|
||
data_length;
|
||
|
||
/*
|
||
Write MNG PLTE chunk
|
||
*/
|
||
data_length=3*image->colors;
|
||
(void) WriteBlobMSBULong(image,data_length);
|
||
PNGType(chunk,mng_PLTE);
|
||
LogPNGChunk(logging,mng_PLTE,data_length);
|
||
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
|
||
image->colormap[i].red) & 0xff);
|
||
chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
|
||
image->colormap[i].green) & 0xff);
|
||
chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
|
||
image->colormap[i].blue) & 0xff);
|
||
}
|
||
|
||
(void) WriteBlob(image,data_length+4,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
|
||
mng_info->have_write_global_plte=MagickTrue;
|
||
}
|
||
#endif
|
||
}
|
||
scene=0;
|
||
mng_info->delay=0;
|
||
#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
|
||
defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
mng_info->equal_palettes=MagickFalse;
|
||
#endif
|
||
imageListLength=GetImageListLength(image);
|
||
do
|
||
{
|
||
if (mng_info->adjoin)
|
||
{
|
||
#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
|
||
defined(PNG_MNG_FEATURES_SUPPORTED)
|
||
/*
|
||
If we aren't using a global palette for the entire MNG, check to
|
||
see if we can use one for two or more consecutive images.
|
||
*/
|
||
if (need_local_plte && use_global_plte && !all_images_are_gray)
|
||
{
|
||
if (mng_info->IsPalette)
|
||
{
|
||
/*
|
||
When equal_palettes is true, this image has the same palette
|
||
as the previous PseudoClass image
|
||
*/
|
||
mng_info->have_write_global_plte=mng_info->equal_palettes;
|
||
mng_info->equal_palettes=PalettesAreEqual(image,image->next);
|
||
if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
|
||
{
|
||
/*
|
||
Write MNG PLTE chunk
|
||
*/
|
||
size_t
|
||
data_length;
|
||
|
||
data_length=3*image->colors;
|
||
(void) WriteBlobMSBULong(image,data_length);
|
||
PNGType(chunk,mng_PLTE);
|
||
LogPNGChunk(logging,mng_PLTE,data_length);
|
||
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
|
||
chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
|
||
chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
|
||
}
|
||
|
||
(void) WriteBlob(image,data_length+4,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,
|
||
(uInt) (data_length+4)));
|
||
mng_info->have_write_global_plte=MagickTrue;
|
||
}
|
||
}
|
||
else
|
||
mng_info->have_write_global_plte=MagickFalse;
|
||
}
|
||
#endif
|
||
if (need_defi)
|
||
{
|
||
ssize_t
|
||
previous_x,
|
||
previous_y;
|
||
|
||
if (scene != 0)
|
||
{
|
||
previous_x=mng_info->page.x;
|
||
previous_y=mng_info->page.y;
|
||
}
|
||
else
|
||
{
|
||
previous_x=0;
|
||
previous_y=0;
|
||
}
|
||
mng_info->page=image->page;
|
||
if ((mng_info->page.x != previous_x) ||
|
||
(mng_info->page.y != previous_y))
|
||
{
|
||
(void) WriteBlobMSBULong(image,12L); /* data length=12 */
|
||
PNGType(chunk,mng_DEFI);
|
||
LogPNGChunk(logging,mng_DEFI,12L);
|
||
chunk[4]=0; /* object 0 MSB */
|
||
chunk[5]=0; /* object 0 LSB */
|
||
chunk[6]=0; /* visible */
|
||
chunk[7]=0; /* abstract */
|
||
PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
|
||
PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
|
||
(void) WriteBlob(image,16,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,16));
|
||
}
|
||
}
|
||
}
|
||
|
||
mng_info->write_mng=write_mng;
|
||
|
||
if ((int) image->dispose >= 3)
|
||
mng_info->framing_mode=3;
|
||
|
||
if (mng_info->need_fram && mng_info->adjoin &&
|
||
((image->delay != mng_info->delay) ||
|
||
(mng_info->framing_mode != mng_info->old_framing_mode)))
|
||
{
|
||
if (image->delay == mng_info->delay)
|
||
{
|
||
/*
|
||
Write a MNG FRAM chunk with the new framing mode.
|
||
*/
|
||
(void) WriteBlobMSBULong(image,1L); /* data length=1 */
|
||
PNGType(chunk,mng_FRAM);
|
||
LogPNGChunk(logging,mng_FRAM,1L);
|
||
chunk[4]=(unsigned char) mng_info->framing_mode;
|
||
(void) WriteBlob(image,5,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,5));
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
Write a MNG FRAM chunk with the delay.
|
||
*/
|
||
(void) WriteBlobMSBULong(image,10L); /* data length=10 */
|
||
PNGType(chunk,mng_FRAM);
|
||
LogPNGChunk(logging,mng_FRAM,10L);
|
||
chunk[4]=(unsigned char) mng_info->framing_mode;
|
||
chunk[5]=0; /* frame name separator (no name) */
|
||
chunk[6]=2; /* flag for changing default delay */
|
||
chunk[7]=0; /* flag for changing frame timeout */
|
||
chunk[8]=0; /* flag for changing frame clipping */
|
||
chunk[9]=0; /* flag for changing frame sync_id */
|
||
PNGLong(chunk+10,(png_uint_32)
|
||
((mng_info->ticks_per_second*
|
||
image->delay)/MagickMax(image->ticks_per_second,1)));
|
||
(void) WriteBlob(image,14,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,14));
|
||
mng_info->delay=(png_uint_32) image->delay;
|
||
}
|
||
mng_info->old_framing_mode=mng_info->framing_mode;
|
||
}
|
||
|
||
#if defined(JNG_SUPPORTED)
|
||
if (image_info->compression == JPEGCompression)
|
||
{
|
||
ImageInfo
|
||
*write_info;
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing JNG object.");
|
||
/* To do: specify the desired alpha compression method. */
|
||
write_info=CloneImageInfo(image_info);
|
||
write_info->compression=UndefinedCompression;
|
||
status=WriteOneJNGImage(mng_info,write_info,image,exception);
|
||
write_info=DestroyImageInfo(write_info);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
|
||
" Writing PNG object.");
|
||
|
||
mng_info->need_blob = MagickFalse;
|
||
mng_info->ping_preserve_colormap = MagickFalse;
|
||
|
||
/* We don't want any ancillary chunks written */
|
||
mng_info->ping_exclude_bKGD=MagickTrue;
|
||
mng_info->ping_exclude_caNv=MagickTrue;
|
||
mng_info->ping_exclude_cHRM=MagickTrue;
|
||
mng_info->ping_exclude_date=MagickTrue;
|
||
mng_info->ping_exclude_EXIF=MagickTrue;
|
||
mng_info->ping_exclude_gAMA=MagickTrue;
|
||
mng_info->ping_exclude_iCCP=MagickTrue;
|
||
/* mng_info->ping_exclude_iTXt=MagickTrue; */
|
||
mng_info->ping_exclude_oFFs=MagickTrue;
|
||
mng_info->ping_exclude_pHYs=MagickTrue;
|
||
mng_info->ping_exclude_sRGB=MagickTrue;
|
||
mng_info->ping_exclude_tEXt=MagickTrue;
|
||
mng_info->ping_exclude_tRNS=MagickTrue;
|
||
mng_info->ping_exclude_zCCP=MagickTrue;
|
||
mng_info->ping_exclude_zTXt=MagickTrue;
|
||
|
||
status=WriteOnePNGImage(mng_info,image_info,image,exception);
|
||
}
|
||
|
||
if (status == MagickFalse)
|
||
{
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
(void) CloseBlob(image);
|
||
return(MagickFalse);
|
||
}
|
||
(void) CatchImageException(image);
|
||
if (GetNextImageInList(image) == (Image *) NULL)
|
||
break;
|
||
image=SyncNextImageInList(image);
|
||
status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
|
||
|
||
if (status == MagickFalse)
|
||
break;
|
||
|
||
} while (mng_info->adjoin);
|
||
|
||
if (write_mng)
|
||
{
|
||
while (GetPreviousImageInList(image) != (Image *) NULL)
|
||
image=GetPreviousImageInList(image);
|
||
/*
|
||
Write the MEND chunk.
|
||
*/
|
||
(void) WriteBlobMSBULong(image,0x00000000L);
|
||
PNGType(chunk,mng_MEND);
|
||
LogPNGChunk(logging,mng_MEND,0L);
|
||
(void) WriteBlob(image,4,chunk);
|
||
(void) WriteBlobMSBULong(image,crc32(0,chunk,4));
|
||
}
|
||
/*
|
||
Relinquish resources.
|
||
*/
|
||
(void) CloseBlob(image);
|
||
mng_info=MngInfoFreeStruct(mng_info);
|
||
|
||
if (logging != MagickFalse)
|
||
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
|
||
|
||
return(MagickTrue);
|
||
}
|
||
#else /* PNG_LIBPNG_VER > 10011 */
|
||
|
||
static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
|
||
Image *image)
|
||
{
|
||
(void) image;
|
||
printf("Your PNG library is too old: You have libpng-%s\n",
|
||
PNG_LIBPNG_VER_STRING);
|
||
|
||
ThrowBinaryException(CoderError,"PNG library is too old",
|
||
image_info->filename);
|
||
}
|
||
|
||
static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
|
||
Image *image)
|
||
{
|
||
return(WritePNGImage(image_info,image));
|
||
}
|
||
#endif /* PNG_LIBPNG_VER > 10011 */
|
||
#endif
|