2d62f7406f
replacing the value that is illegal in the PNG spec, in both signed and unsigned values, with 0. Illegal unsigned values (anything greater than or equal to 0x80000000) can still pass through, but since these are not illegal in ANSI-C (unlike 0x80000000 in the signed case) the checking that occurs later can catch them (John Bowler).
4486 lines
138 KiB
C
4486 lines
138 KiB
C
|
|
/* pngrutil.c - utilities to read a PNG file
|
|
*
|
|
* Last changed in libpng 1.6.19 [(PENDING RELEASE)]
|
|
* Copyright (c) 1998-2015 Glenn Randers-Pehrson
|
|
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
|
|
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
|
|
*
|
|
* This code is released under the libpng license.
|
|
* For conditions of distribution and use, see the disclaimer
|
|
* and license in png.h
|
|
*
|
|
* This file contains routines that are only called from within
|
|
* libpng itself during the course of reading an image.
|
|
*/
|
|
|
|
#include "pngpriv.h"
|
|
|
|
#ifdef PNG_READ_SUPPORTED
|
|
|
|
png_uint_32 PNGAPI
|
|
png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
|
|
{
|
|
png_uint_32 uval = png_get_uint_32(buf);
|
|
|
|
if (uval > PNG_UINT_31_MAX)
|
|
png_error(png_ptr, "PNG unsigned integer out of range");
|
|
|
|
return (uval);
|
|
}
|
|
|
|
#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
|
|
/* The following is a variation on the above for use with the fixed
|
|
* point values used for gAMA and cHRM. Instead of png_error it
|
|
* issues a warning and returns (-1) - an invalid value because both
|
|
* gAMA and cHRM use *unsigned* integers for fixed point values.
|
|
*/
|
|
#define PNG_FIXED_ERROR (-1)
|
|
|
|
static png_fixed_point /* PRIVATE */
|
|
png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)
|
|
{
|
|
png_uint_32 uval = png_get_uint_32(buf);
|
|
|
|
if (uval <= PNG_UINT_31_MAX)
|
|
return (png_fixed_point)uval; /* known to be in range */
|
|
|
|
/* The caller can turn off the warning by passing NULL. */
|
|
if (png_ptr != NULL)
|
|
png_warning(png_ptr, "PNG fixed point integer out of range");
|
|
|
|
return PNG_FIXED_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
|
|
/* NOTE: the read macros will obscure these definitions, so that if
|
|
* PNG_USE_READ_MACROS is set the library will not use them internally,
|
|
* but the APIs will still be available externally.
|
|
*
|
|
* The parentheses around "PNGAPI function_name" in the following three
|
|
* functions are necessary because they allow the macros to co-exist with
|
|
* these (unused but exported) functions.
|
|
*/
|
|
|
|
/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
|
|
png_uint_32 (PNGAPI
|
|
png_get_uint_32)(png_const_bytep buf)
|
|
{
|
|
png_uint_32 uval =
|
|
((png_uint_32)(*(buf )) << 24) +
|
|
((png_uint_32)(*(buf + 1)) << 16) +
|
|
((png_uint_32)(*(buf + 2)) << 8) +
|
|
((png_uint_32)(*(buf + 3)) ) ;
|
|
|
|
return uval;
|
|
}
|
|
|
|
/* Grab a signed 32-bit integer from a buffer in big-endian format. The
|
|
* data is stored in the PNG file in two's complement format and there
|
|
* is no guarantee that a 'png_int_32' is exactly 32 bits, therefore
|
|
* the following code does a two's complement to native conversion.
|
|
*/
|
|
png_int_32 (PNGAPI
|
|
png_get_int_32)(png_const_bytep buf)
|
|
{
|
|
png_uint_32 uval = png_get_uint_32(buf);
|
|
if ((uval & 0x80000000) == 0) /* non-negative */
|
|
return uval;
|
|
|
|
uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */
|
|
if ((uval & 0x80000000) == 0) /* no overflow */
|
|
return -(png_int_32)uval;
|
|
/* The following has to be safe; this function only gets called on PNG data
|
|
* and if we get here that data is invalid. 0 is the most safe value and
|
|
* if not then an attacker would surely just generate a PNG with 0 instead.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
|
|
png_uint_16 (PNGAPI
|
|
png_get_uint_16)(png_const_bytep buf)
|
|
{
|
|
/* ANSI-C requires an int value to accomodate at least 16 bits so this
|
|
* works and allows the compiler not to worry about possible narrowing
|
|
* on 32-bit systems. (Pre-ANSI systems did not make integers smaller
|
|
* than 16 bits either.)
|
|
*/
|
|
unsigned int val =
|
|
((unsigned int)(*buf) << 8) +
|
|
((unsigned int)(*(buf + 1)));
|
|
|
|
return (png_uint_16)val;
|
|
}
|
|
|
|
#endif /* READ_INT_FUNCTIONS */
|
|
|
|
/* Read and check the PNG file signature */
|
|
void /* PRIVATE */
|
|
png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
|
|
{
|
|
png_size_t num_checked, num_to_check;
|
|
|
|
/* Exit if the user application does not expect a signature. */
|
|
if (png_ptr->sig_bytes >= 8)
|
|
return;
|
|
|
|
num_checked = png_ptr->sig_bytes;
|
|
num_to_check = 8 - num_checked;
|
|
|
|
#ifdef PNG_IO_STATE_SUPPORTED
|
|
png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE;
|
|
#endif
|
|
|
|
/* The signature must be serialized in a single I/O call. */
|
|
png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
|
|
png_ptr->sig_bytes = 8;
|
|
|
|
if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0)
|
|
{
|
|
if (num_checked < 4 &&
|
|
png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
|
|
png_error(png_ptr, "Not a PNG file");
|
|
else
|
|
png_error(png_ptr, "PNG file corrupted by ASCII conversion");
|
|
}
|
|
if (num_checked < 3)
|
|
png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
|
|
}
|
|
|
|
/* Read the chunk header (length + type name).
|
|
* Put the type name into png_ptr->chunk_name, and return the length.
|
|
*/
|
|
png_uint_32 /* PRIVATE */
|
|
png_read_chunk_header(png_structrp png_ptr)
|
|
{
|
|
png_byte buf[8];
|
|
png_uint_32 length;
|
|
|
|
#ifdef PNG_IO_STATE_SUPPORTED
|
|
png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;
|
|
#endif
|
|
|
|
/* Read the length and the chunk name.
|
|
* This must be performed in a single I/O call.
|
|
*/
|
|
png_read_data(png_ptr, buf, 8);
|
|
length = png_get_uint_31(png_ptr, buf);
|
|
|
|
/* Put the chunk name into png_ptr->chunk_name. */
|
|
png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
|
|
|
|
png_debug2(0, "Reading %lx chunk, length = %lu",
|
|
(unsigned long)png_ptr->chunk_name, (unsigned long)length);
|
|
|
|
/* Reset the crc and run it over the chunk name. */
|
|
png_reset_crc(png_ptr);
|
|
png_calculate_crc(png_ptr, buf + 4, 4);
|
|
|
|
/* Check to see if chunk name is valid. */
|
|
png_check_chunk_name(png_ptr, png_ptr->chunk_name);
|
|
|
|
#ifdef PNG_IO_STATE_SUPPORTED
|
|
png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
|
|
#endif
|
|
|
|
return length;
|
|
}
|
|
|
|
/* Read data, and (optionally) run it through the CRC. */
|
|
void /* PRIVATE */
|
|
png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)
|
|
{
|
|
if (png_ptr == NULL)
|
|
return;
|
|
|
|
png_read_data(png_ptr, buf, length);
|
|
png_calculate_crc(png_ptr, buf, length);
|
|
}
|
|
|
|
/* Optionally skip data and then check the CRC. Depending on whether we
|
|
* are reading an ancillary or critical chunk, and how the program has set
|
|
* things up, we may calculate the CRC on the data and print a message.
|
|
* Returns '1' if there was a CRC error, '0' otherwise.
|
|
*/
|
|
int /* PRIVATE */
|
|
png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
|
|
{
|
|
/* The size of the local buffer for inflate is a good guess as to a
|
|
* reasonable size to use for buffering reads from the application.
|
|
*/
|
|
while (skip > 0)
|
|
{
|
|
png_uint_32 len;
|
|
png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
|
|
|
|
len = (sizeof tmpbuf);
|
|
if (len > skip)
|
|
len = skip;
|
|
skip -= len;
|
|
|
|
png_crc_read(png_ptr, tmpbuf, len);
|
|
}
|
|
|
|
if (png_crc_error(png_ptr) != 0)
|
|
{
|
|
if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ?
|
|
(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 :
|
|
(png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0)
|
|
{
|
|
png_chunk_warning(png_ptr, "CRC error");
|
|
}
|
|
|
|
else
|
|
png_chunk_error(png_ptr, "CRC error");
|
|
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Compare the CRC stored in the PNG file with that calculated by libpng from
|
|
* the data it has read thus far.
|
|
*/
|
|
int /* PRIVATE */
|
|
png_crc_error(png_structrp png_ptr)
|
|
{
|
|
png_byte crc_bytes[4];
|
|
png_uint_32 crc;
|
|
int need_crc = 1;
|
|
|
|
if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
|
|
{
|
|
if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
|
|
(PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
|
|
need_crc = 0;
|
|
}
|
|
|
|
else /* critical */
|
|
{
|
|
if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
|
|
need_crc = 0;
|
|
}
|
|
|
|
#ifdef PNG_IO_STATE_SUPPORTED
|
|
png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
|
|
#endif
|
|
|
|
/* The chunk CRC must be serialized in a single I/O call. */
|
|
png_read_data(png_ptr, crc_bytes, 4);
|
|
|
|
if (need_crc != 0)
|
|
{
|
|
crc = png_get_uint_32(crc_bytes);
|
|
return ((int)(crc != png_ptr->crc));
|
|
}
|
|
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
#if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
|
|
defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\
|
|
defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\
|
|
defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED)
|
|
/* Manage the read buffer; this simply reallocates the buffer if it is not small
|
|
* enough (or if it is not allocated). The routine returns a pointer to the
|
|
* buffer; if an error occurs and 'warn' is set the routine returns NULL, else
|
|
* it will call png_error (via png_malloc) on failure. (warn == 2 means
|
|
* 'silent').
|
|
*/
|
|
static png_bytep
|
|
png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
|
|
{
|
|
png_bytep buffer = png_ptr->read_buffer;
|
|
|
|
if (buffer != NULL && new_size > png_ptr->read_buffer_size)
|
|
{
|
|
png_ptr->read_buffer = NULL;
|
|
png_ptr->read_buffer = NULL;
|
|
png_ptr->read_buffer_size = 0;
|
|
png_free(png_ptr, buffer);
|
|
buffer = NULL;
|
|
}
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
|
|
|
|
if (buffer != NULL)
|
|
{
|
|
png_ptr->read_buffer = buffer;
|
|
png_ptr->read_buffer_size = new_size;
|
|
}
|
|
|
|
else if (warn < 2) /* else silent */
|
|
{
|
|
if (warn != 0)
|
|
png_chunk_warning(png_ptr, "insufficient memory to read chunk");
|
|
|
|
else
|
|
png_chunk_error(png_ptr, "insufficient memory to read chunk");
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */
|
|
|
|
/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
|
|
* decompression. Returns Z_OK on success, else a zlib error code. It checks
|
|
* the owner but, in final release builds, just issues a warning if some other
|
|
* chunk apparently owns the stream. Prior to release it does a png_error.
|
|
*/
|
|
static int
|
|
png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
|
|
{
|
|
if (png_ptr->zowner != 0)
|
|
{
|
|
char msg[64];
|
|
|
|
PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);
|
|
/* So the message that results is "<chunk> using zstream"; this is an
|
|
* internal error, but is very useful for debugging. i18n requirements
|
|
* are minimal.
|
|
*/
|
|
(void)png_safecat(msg, (sizeof msg), 4, " using zstream");
|
|
#if PNG_RELEASE_BUILD
|
|
png_chunk_warning(png_ptr, msg);
|
|
png_ptr->zowner = 0;
|
|
#else
|
|
png_chunk_error(png_ptr, msg);
|
|
#endif
|
|
}
|
|
|
|
/* Implementation note: unlike 'png_deflate_claim' this internal function
|
|
* does not take the size of the data as an argument. Some efficiency could
|
|
* be gained by using this when it is known *if* the zlib stream itself does
|
|
* not record the number; however, this is an illusion: the original writer
|
|
* of the PNG may have selected a lower window size, and we really must
|
|
* follow that because, for systems with with limited capabilities, we
|
|
* would otherwise reject the application's attempts to use a smaller window
|
|
* size (zlib doesn't have an interface to say "this or lower"!).
|
|
*
|
|
* inflateReset2 was added to zlib 1.2.4; before this the window could not be
|
|
* reset, therefore it is necessary to always allocate the maximum window
|
|
* size with earlier zlibs just in case later compressed chunks need it.
|
|
*/
|
|
{
|
|
int ret; /* zlib return code */
|
|
#if PNG_ZLIB_VERNUM >= 0x1240
|
|
|
|
# if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
|
|
int window_bits;
|
|
|
|
if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
|
|
PNG_OPTION_ON)
|
|
window_bits = 15;
|
|
|
|
else
|
|
window_bits = 0;
|
|
# else
|
|
# define window_bits 0
|
|
# endif
|
|
#endif
|
|
|
|
/* Set this for safety, just in case the previous owner left pointers to
|
|
* memory allocations.
|
|
*/
|
|
png_ptr->zstream.next_in = NULL;
|
|
png_ptr->zstream.avail_in = 0;
|
|
png_ptr->zstream.next_out = NULL;
|
|
png_ptr->zstream.avail_out = 0;
|
|
|
|
if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
|
|
{
|
|
#if PNG_ZLIB_VERNUM < 0x1240
|
|
ret = inflateReset(&png_ptr->zstream);
|
|
#else
|
|
ret = inflateReset2(&png_ptr->zstream, window_bits);
|
|
#endif
|
|
}
|
|
|
|
else
|
|
{
|
|
#if PNG_ZLIB_VERNUM < 0x1240
|
|
ret = inflateInit(&png_ptr->zstream);
|
|
#else
|
|
ret = inflateInit2(&png_ptr->zstream, window_bits);
|
|
#endif
|
|
|
|
if (ret == Z_OK)
|
|
png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
|
|
}
|
|
|
|
if (ret == Z_OK)
|
|
png_ptr->zowner = owner;
|
|
|
|
else
|
|
png_zstream_error(png_ptr, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef window_bits
|
|
# undef window_bits
|
|
#endif
|
|
}
|
|
|
|
#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
|
|
/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to
|
|
* allow the caller to do multiple calls if required. If the 'finish' flag is
|
|
* set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must
|
|
* be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and
|
|
* Z_OK or Z_STREAM_END will be returned on success.
|
|
*
|
|
* The input and output sizes are updated to the actual amounts of data consumed
|
|
* or written, not the amount available (as in a z_stream). The data pointers
|
|
* are not changed, so the next input is (data+input_size) and the next
|
|
* available output is (output+output_size).
|
|
*/
|
|
static int
|
|
png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
|
|
/* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr,
|
|
/* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr)
|
|
{
|
|
if (png_ptr->zowner == owner) /* Else not claimed */
|
|
{
|
|
int ret;
|
|
png_alloc_size_t avail_out = *output_size_ptr;
|
|
png_uint_32 avail_in = *input_size_ptr;
|
|
|
|
/* zlib can't necessarily handle more than 65535 bytes at once (i.e. it
|
|
* can't even necessarily handle 65536 bytes) because the type uInt is
|
|
* "16 bits or more". Consequently it is necessary to chunk the input to
|
|
* zlib. This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the
|
|
* maximum value that can be stored in a uInt.) It is possible to set
|
|
* ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have
|
|
* a performance advantage, because it reduces the amount of data accessed
|
|
* at each step and that may give the OS more time to page it in.
|
|
*/
|
|
png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
|
|
/* avail_in and avail_out are set below from 'size' */
|
|
png_ptr->zstream.avail_in = 0;
|
|
png_ptr->zstream.avail_out = 0;
|
|
|
|
/* Read directly into the output if it is available (this is set to
|
|
* a local buffer below if output is NULL).
|
|
*/
|
|
if (output != NULL)
|
|
png_ptr->zstream.next_out = output;
|
|
|
|
do
|
|
{
|
|
uInt avail;
|
|
Byte local_buffer[PNG_INFLATE_BUF_SIZE];
|
|
|
|
/* zlib INPUT BUFFER */
|
|
/* The setting of 'avail_in' used to be outside the loop; by setting it
|
|
* inside it is possible to chunk the input to zlib and simply rely on
|
|
* zlib to advance the 'next_in' pointer. This allows arbitrary
|
|
* amounts of data to be passed through zlib at the unavoidable cost of
|
|
* requiring a window save (memcpy of up to 32768 output bytes)
|
|
* every ZLIB_IO_MAX input bytes.
|
|
*/
|
|
avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
|
|
|
|
avail = ZLIB_IO_MAX;
|
|
|
|
if (avail_in < avail)
|
|
avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
|
|
|
|
avail_in -= avail;
|
|
png_ptr->zstream.avail_in = avail;
|
|
|
|
/* zlib OUTPUT BUFFER */
|
|
avail_out += png_ptr->zstream.avail_out; /* not written last time */
|
|
|
|
avail = ZLIB_IO_MAX; /* maximum zlib can process */
|
|
|
|
if (output == NULL)
|
|
{
|
|
/* Reset the output buffer each time round if output is NULL and
|
|
* make available the full buffer, up to 'remaining_space'
|
|
*/
|
|
png_ptr->zstream.next_out = local_buffer;
|
|
if ((sizeof local_buffer) < avail)
|
|
avail = (sizeof local_buffer);
|
|
}
|
|
|
|
if (avail_out < avail)
|
|
avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
|
|
|
|
png_ptr->zstream.avail_out = avail;
|
|
avail_out -= avail;
|
|
|
|
/* zlib inflate call */
|
|
/* In fact 'avail_out' may be 0 at this point, that happens at the end
|
|
* of the read when the final LZ end code was not passed at the end of
|
|
* the previous chunk of input data. Tell zlib if we have reached the
|
|
* end of the output buffer.
|
|
*/
|
|
ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH :
|
|
(finish ? Z_FINISH : Z_SYNC_FLUSH));
|
|
} while (ret == Z_OK);
|
|
|
|
/* For safety kill the local buffer pointer now */
|
|
if (output == NULL)
|
|
png_ptr->zstream.next_out = NULL;
|
|
|
|
/* Claw back the 'size' and 'remaining_space' byte counts. */
|
|
avail_in += png_ptr->zstream.avail_in;
|
|
avail_out += png_ptr->zstream.avail_out;
|
|
|
|
/* Update the input and output sizes; the updated values are the amount
|
|
* consumed or written, effectively the inverse of what zlib uses.
|
|
*/
|
|
if (avail_out > 0)
|
|
*output_size_ptr -= avail_out;
|
|
|
|
if (avail_in > 0)
|
|
*input_size_ptr -= avail_in;
|
|
|
|
/* Ensure png_ptr->zstream.msg is set (even in the success case!) */
|
|
png_zstream_error(png_ptr, ret);
|
|
return ret;
|
|
}
|
|
|
|
else
|
|
{
|
|
/* This is a bad internal error. The recovery assigns to the zstream msg
|
|
* pointer, which is not owned by the caller, but this is safe; it's only
|
|
* used on errors!
|
|
*/
|
|
png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
|
|
return Z_STREAM_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decompress trailing data in a chunk. The assumption is that read_buffer
|
|
* points at an allocated area holding the contents of a chunk with a
|
|
* trailing compressed part. What we get back is an allocated area
|
|
* holding the original prefix part and an uncompressed version of the
|
|
* trailing part (the malloc area passed in is freed).
|
|
*/
|
|
static int
|
|
png_decompress_chunk(png_structrp png_ptr,
|
|
png_uint_32 chunklength, png_uint_32 prefix_size,
|
|
png_alloc_size_t *newlength /* must be initialized to the maximum! */,
|
|
int terminate /*add a '\0' to the end of the uncompressed data*/)
|
|
{
|
|
/* TODO: implement different limits for different types of chunk.
|
|
*
|
|
* The caller supplies *newlength set to the maximum length of the
|
|
* uncompressed data, but this routine allocates space for the prefix and
|
|
* maybe a '\0' terminator too. We have to assume that 'prefix_size' is
|
|
* limited only by the maximum chunk size.
|
|
*/
|
|
png_alloc_size_t limit = PNG_SIZE_MAX;
|
|
|
|
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_malloc_max > 0 &&
|
|
png_ptr->user_chunk_malloc_max < limit)
|
|
limit = png_ptr->user_chunk_malloc_max;
|
|
# elif PNG_USER_CHUNK_MALLOC_MAX > 0
|
|
if (PNG_USER_CHUNK_MALLOC_MAX < limit)
|
|
limit = PNG_USER_CHUNK_MALLOC_MAX;
|
|
# endif
|
|
|
|
if (limit >= prefix_size + (terminate != 0))
|
|
{
|
|
int ret;
|
|
|
|
limit -= prefix_size + (terminate != 0);
|
|
|
|
if (limit < *newlength)
|
|
*newlength = limit;
|
|
|
|
/* Now try to claim the stream. */
|
|
ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
|
|
|
|
if (ret == Z_OK)
|
|
{
|
|
png_uint_32 lzsize = chunklength - prefix_size;
|
|
|
|
ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
|
|
/* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
|
|
/* output: */ NULL, newlength);
|
|
|
|
if (ret == Z_STREAM_END)
|
|
{
|
|
/* Use 'inflateReset' here, not 'inflateReset2' because this
|
|
* preserves the previously decided window size (otherwise it would
|
|
* be necessary to store the previous window size.) In practice
|
|
* this doesn't matter anyway, because png_inflate will call inflate
|
|
* with Z_FINISH in almost all cases, so the window will not be
|
|
* maintained.
|
|
*/
|
|
if (inflateReset(&png_ptr->zstream) == Z_OK)
|
|
{
|
|
/* Because of the limit checks above we know that the new,
|
|
* expanded, size will fit in a size_t (let alone an
|
|
* png_alloc_size_t). Use png_malloc_base here to avoid an
|
|
* extra OOM message.
|
|
*/
|
|
png_alloc_size_t new_size = *newlength;
|
|
png_alloc_size_t buffer_size = prefix_size + new_size +
|
|
(terminate != 0);
|
|
png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr,
|
|
buffer_size));
|
|
|
|
if (text != NULL)
|
|
{
|
|
ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
|
|
png_ptr->read_buffer + prefix_size, &lzsize,
|
|
text + prefix_size, newlength);
|
|
|
|
if (ret == Z_STREAM_END)
|
|
{
|
|
if (new_size == *newlength)
|
|
{
|
|
if (terminate != 0)
|
|
text[prefix_size + *newlength] = 0;
|
|
|
|
if (prefix_size > 0)
|
|
memcpy(text, png_ptr->read_buffer, prefix_size);
|
|
|
|
{
|
|
png_bytep old_ptr = png_ptr->read_buffer;
|
|
|
|
png_ptr->read_buffer = text;
|
|
png_ptr->read_buffer_size = buffer_size;
|
|
text = old_ptr; /* freed below */
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
/* The size changed on the second read, there can be no
|
|
* guarantee that anything is correct at this point.
|
|
* The 'msg' pointer has been set to "unexpected end of
|
|
* LZ stream", which is fine, but return an error code
|
|
* that the caller won't accept.
|
|
*/
|
|
ret = PNG_UNEXPECTED_ZLIB_RETURN;
|
|
}
|
|
}
|
|
|
|
else if (ret == Z_OK)
|
|
ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */
|
|
|
|
/* Free the text pointer (this is the old read_buffer on
|
|
* success)
|
|
*/
|
|
png_free(png_ptr, text);
|
|
|
|
/* This really is very benign, but it's still an error because
|
|
* the extra space may otherwise be used as a Trojan Horse.
|
|
*/
|
|
if (ret == Z_STREAM_END &&
|
|
chunklength - prefix_size != lzsize)
|
|
png_chunk_benign_error(png_ptr, "extra compressed data");
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Out of memory allocating the buffer */
|
|
ret = Z_MEM_ERROR;
|
|
png_zstream_error(png_ptr, Z_MEM_ERROR);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
/* inflateReset failed, store the error message */
|
|
png_zstream_error(png_ptr, ret);
|
|
|
|
if (ret == Z_STREAM_END)
|
|
ret = PNG_UNEXPECTED_ZLIB_RETURN;
|
|
}
|
|
}
|
|
|
|
else if (ret == Z_OK)
|
|
ret = PNG_UNEXPECTED_ZLIB_RETURN;
|
|
|
|
/* Release the claimed stream */
|
|
png_ptr->zowner = 0;
|
|
}
|
|
|
|
else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */
|
|
ret = PNG_UNEXPECTED_ZLIB_RETURN;
|
|
|
|
return ret;
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Application/configuration limits exceeded */
|
|
png_zstream_error(png_ptr, Z_MEM_ERROR);
|
|
return Z_MEM_ERROR;
|
|
}
|
|
}
|
|
#endif /* READ_COMPRESSED_TEXT */
|
|
|
|
#ifdef PNG_READ_iCCP_SUPPORTED
|
|
/* Perform a partial read and decompress, producing 'avail_out' bytes and
|
|
* reading from the current chunk as required.
|
|
*/
|
|
static int
|
|
png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
|
|
png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size,
|
|
int finish)
|
|
{
|
|
if (png_ptr->zowner == png_ptr->chunk_name)
|
|
{
|
|
int ret;
|
|
|
|
/* next_in and avail_in must have been initialized by the caller. */
|
|
png_ptr->zstream.next_out = next_out;
|
|
png_ptr->zstream.avail_out = 0; /* set in the loop */
|
|
|
|
do
|
|
{
|
|
if (png_ptr->zstream.avail_in == 0)
|
|
{
|
|
if (read_size > *chunk_bytes)
|
|
read_size = (uInt)*chunk_bytes;
|
|
*chunk_bytes -= read_size;
|
|
|
|
if (read_size > 0)
|
|
png_crc_read(png_ptr, read_buffer, read_size);
|
|
|
|
png_ptr->zstream.next_in = read_buffer;
|
|
png_ptr->zstream.avail_in = read_size;
|
|
}
|
|
|
|
if (png_ptr->zstream.avail_out == 0)
|
|
{
|
|
uInt avail = ZLIB_IO_MAX;
|
|
if (avail > *out_size)
|
|
avail = (uInt)*out_size;
|
|
*out_size -= avail;
|
|
|
|
png_ptr->zstream.avail_out = avail;
|
|
}
|
|
|
|
/* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all
|
|
* the available output is produced; this allows reading of truncated
|
|
* streams.
|
|
*/
|
|
ret = inflate(&png_ptr->zstream,
|
|
*chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
|
|
}
|
|
while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0));
|
|
|
|
*out_size += png_ptr->zstream.avail_out;
|
|
png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */
|
|
|
|
/* Ensure the error message pointer is always set: */
|
|
png_zstream_error(png_ptr, ret);
|
|
return ret;
|
|
}
|
|
|
|
else
|
|
{
|
|
png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
|
|
return Z_STREAM_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Read and check the IDHR chunk */
|
|
|
|
void /* PRIVATE */
|
|
png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte buf[13];
|
|
png_uint_32 width, height;
|
|
int bit_depth, color_type, compression_type, filter_type;
|
|
int interlace_type;
|
|
|
|
png_debug(1, "in png_handle_IHDR");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) != 0)
|
|
png_chunk_error(png_ptr, "out of place");
|
|
|
|
/* Check the length */
|
|
if (length != 13)
|
|
png_chunk_error(png_ptr, "invalid");
|
|
|
|
png_ptr->mode |= PNG_HAVE_IHDR;
|
|
|
|
png_crc_read(png_ptr, buf, 13);
|
|
png_crc_finish(png_ptr, 0);
|
|
|
|
width = png_get_uint_31(png_ptr, buf);
|
|
height = png_get_uint_31(png_ptr, buf + 4);
|
|
bit_depth = buf[8];
|
|
color_type = buf[9];
|
|
compression_type = buf[10];
|
|
filter_type = buf[11];
|
|
interlace_type = buf[12];
|
|
|
|
/* Set internal variables */
|
|
png_ptr->width = width;
|
|
png_ptr->height = height;
|
|
png_ptr->bit_depth = (png_byte)bit_depth;
|
|
png_ptr->interlaced = (png_byte)interlace_type;
|
|
png_ptr->color_type = (png_byte)color_type;
|
|
#ifdef PNG_MNG_FEATURES_SUPPORTED
|
|
png_ptr->filter_type = (png_byte)filter_type;
|
|
#endif
|
|
png_ptr->compression_type = (png_byte)compression_type;
|
|
|
|
/* Find number of channels */
|
|
switch (png_ptr->color_type)
|
|
{
|
|
default: /* invalid, png_set_IHDR calls png_error */
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
case PNG_COLOR_TYPE_PALETTE:
|
|
png_ptr->channels = 1;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
|
png_ptr->channels = 3;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
png_ptr->channels = 2;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
png_ptr->channels = 4;
|
|
break;
|
|
}
|
|
|
|
/* Set up other useful info */
|
|
png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels);
|
|
png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);
|
|
png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
|
|
png_debug1(3, "channels = %d", png_ptr->channels);
|
|
png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
|
|
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
|
|
color_type, interlace_type, compression_type, filter_type);
|
|
}
|
|
|
|
/* Read and check the palette */
|
|
void /* PRIVATE */
|
|
png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_color palette[PNG_MAX_PALETTE_LENGTH];
|
|
int num, i;
|
|
#ifdef PNG_POINTER_INDEXING_SUPPORTED
|
|
png_colorp pal_ptr;
|
|
#endif
|
|
|
|
png_debug(1, "in png_handle_PLTE");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
/* Moved to before the 'after IDAT' check below because otherwise duplicate
|
|
* PLTE chunks are potentially ignored (the spec says there shall not be more
|
|
* than one PLTE, the error is not treated as benign, so this check trumps
|
|
* the requirement that PLTE appears before IDAT.)
|
|
*/
|
|
else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0)
|
|
png_chunk_error(png_ptr, "duplicate");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
/* This is benign because the non-benign error happened before, when an
|
|
* IDAT was encountered in a color-mapped image with no PLTE.
|
|
*/
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
png_ptr->mode |= PNG_HAVE_PLTE;
|
|
|
|
if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
|
|
return;
|
|
}
|
|
|
|
#ifndef PNG_READ_OPT_PLTE_SUPPORTED
|
|
if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
|
|
if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
|
|
else
|
|
png_chunk_error(png_ptr, "invalid");
|
|
|
|
return;
|
|
}
|
|
|
|
/* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */
|
|
num = (int)length / 3;
|
|
|
|
#ifdef PNG_POINTER_INDEXING_SUPPORTED
|
|
for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
|
|
{
|
|
png_byte buf[3];
|
|
|
|
png_crc_read(png_ptr, buf, 3);
|
|
pal_ptr->red = buf[0];
|
|
pal_ptr->green = buf[1];
|
|
pal_ptr->blue = buf[2];
|
|
}
|
|
#else
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
png_byte buf[3];
|
|
|
|
png_crc_read(png_ptr, buf, 3);
|
|
/* Don't depend upon png_color being any order */
|
|
palette[i].red = buf[0];
|
|
palette[i].green = buf[1];
|
|
palette[i].blue = buf[2];
|
|
}
|
|
#endif
|
|
|
|
/* If we actually need the PLTE chunk (ie for a paletted image), we do
|
|
* whatever the normal CRC configuration tells us. However, if we
|
|
* have an RGB image, the PLTE can be considered ancillary, so
|
|
* we will act as though it is.
|
|
*/
|
|
#ifndef PNG_READ_OPT_PLTE_SUPPORTED
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
#endif
|
|
{
|
|
png_crc_finish(png_ptr, 0);
|
|
}
|
|
|
|
#ifndef PNG_READ_OPT_PLTE_SUPPORTED
|
|
else if (png_crc_error(png_ptr) != 0) /* Only if we have a CRC error */
|
|
{
|
|
/* If we don't want to use the data from an ancillary chunk,
|
|
* we have two options: an error abort, or a warning and we
|
|
* ignore the data in this chunk (which should be OK, since
|
|
* it's considered ancillary for a RGB or RGBA image).
|
|
*
|
|
* IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the
|
|
* chunk type to determine whether to check the ancillary or the critical
|
|
* flags.
|
|
*/
|
|
if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0)
|
|
{
|
|
if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0)
|
|
return;
|
|
|
|
else
|
|
png_chunk_error(png_ptr, "CRC error");
|
|
}
|
|
|
|
/* Otherwise, we (optionally) emit a warning and use the chunk. */
|
|
else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0)
|
|
png_chunk_warning(png_ptr, "CRC error");
|
|
}
|
|
#endif
|
|
|
|
/* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its
|
|
* own copy of the palette. This has the side effect that when png_start_row
|
|
* is called (this happens after any call to png_read_update_info) the
|
|
* info_ptr palette gets changed. This is extremely unexpected and
|
|
* confusing.
|
|
*
|
|
* Fix this by not sharing the palette in this way.
|
|
*/
|
|
png_set_PLTE(png_ptr, info_ptr, palette, num);
|
|
|
|
/* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before
|
|
* IDAT. Prior to 1.6.0 this was not checked; instead the code merely
|
|
* checked the apparent validity of a tRNS chunk inserted before PLTE on a
|
|
* palette PNG. 1.6.0 attempts to rigorously follow the standard and
|
|
* therefore does a benign error if the erroneous condition is detected *and*
|
|
* cancels the tRNS if the benign error returns. The alternative is to
|
|
* amend the standard since it would be rather hypocritical of the standards
|
|
* maintainers to ignore it.
|
|
*/
|
|
#ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (png_ptr->num_trans > 0 ||
|
|
(info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
|
|
{
|
|
/* Cancel this because otherwise it would be used if the transforms
|
|
* require it. Don't cancel the 'valid' flag because this would prevent
|
|
* detection of duplicate chunks.
|
|
*/
|
|
png_ptr->num_trans = 0;
|
|
|
|
if (info_ptr != NULL)
|
|
info_ptr->num_trans = 0;
|
|
|
|
png_chunk_benign_error(png_ptr, "tRNS must be after");
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_hIST_SUPPORTED
|
|
if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
|
|
png_chunk_benign_error(png_ptr, "hIST must be after");
|
|
#endif
|
|
|
|
#ifdef PNG_READ_bKGD_SUPPORTED
|
|
if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
|
|
png_chunk_benign_error(png_ptr, "bKGD must be after");
|
|
#endif
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_debug(1, "in png_handle_IEND");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 ||
|
|
(png_ptr->mode & PNG_HAVE_IDAT) == 0)
|
|
png_chunk_error(png_ptr, "out of place");
|
|
|
|
png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
|
|
|
|
png_crc_finish(png_ptr, length);
|
|
|
|
if (length != 0)
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
|
|
PNG_UNUSED(info_ptr)
|
|
}
|
|
|
|
#ifdef PNG_READ_gAMA_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_fixed_point igamma;
|
|
png_byte buf[4];
|
|
|
|
png_debug(1, "in png_handle_gAMA");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
if (length != 4)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 4);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
igamma = png_get_fixed_point(NULL, buf);
|
|
|
|
png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma);
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_sBIT_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
unsigned int truelen, i;
|
|
png_byte sample_depth;
|
|
png_byte buf[4];
|
|
|
|
png_debug(1, "in png_handle_sBIT");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
truelen = 3;
|
|
sample_depth = 8;
|
|
}
|
|
|
|
else
|
|
{
|
|
truelen = png_ptr->channels;
|
|
sample_depth = png_ptr->bit_depth;
|
|
}
|
|
|
|
if (length != truelen || length > 4)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
buf[0] = buf[1] = buf[2] = buf[3] = sample_depth;
|
|
png_crc_read(png_ptr, buf, truelen);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
for (i=0; i<truelen; ++i)
|
|
{
|
|
if (buf[i] == 0 || buf[i] > sample_depth)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
|
|
{
|
|
png_ptr->sig_bit.red = buf[0];
|
|
png_ptr->sig_bit.green = buf[1];
|
|
png_ptr->sig_bit.blue = buf[2];
|
|
png_ptr->sig_bit.alpha = buf[3];
|
|
}
|
|
|
|
else
|
|
{
|
|
png_ptr->sig_bit.gray = buf[0];
|
|
png_ptr->sig_bit.red = buf[0];
|
|
png_ptr->sig_bit.green = buf[0];
|
|
png_ptr->sig_bit.blue = buf[0];
|
|
png_ptr->sig_bit.alpha = buf[1];
|
|
}
|
|
|
|
png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_cHRM_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte buf[32];
|
|
png_xy xy;
|
|
|
|
png_debug(1, "in png_handle_cHRM");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
if (length != 32)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 32);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
xy.whitex = png_get_fixed_point(NULL, buf);
|
|
xy.whitey = png_get_fixed_point(NULL, buf + 4);
|
|
xy.redx = png_get_fixed_point(NULL, buf + 8);
|
|
xy.redy = png_get_fixed_point(NULL, buf + 12);
|
|
xy.greenx = png_get_fixed_point(NULL, buf + 16);
|
|
xy.greeny = png_get_fixed_point(NULL, buf + 20);
|
|
xy.bluex = png_get_fixed_point(NULL, buf + 24);
|
|
xy.bluey = png_get_fixed_point(NULL, buf + 28);
|
|
|
|
if (xy.whitex == PNG_FIXED_ERROR ||
|
|
xy.whitey == PNG_FIXED_ERROR ||
|
|
xy.redx == PNG_FIXED_ERROR ||
|
|
xy.redy == PNG_FIXED_ERROR ||
|
|
xy.greenx == PNG_FIXED_ERROR ||
|
|
xy.greeny == PNG_FIXED_ERROR ||
|
|
xy.bluex == PNG_FIXED_ERROR ||
|
|
xy.bluey == PNG_FIXED_ERROR)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid values");
|
|
return;
|
|
}
|
|
|
|
/* If a colorspace error has already been output skip this chunk */
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
|
|
return;
|
|
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0)
|
|
{
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
|
|
(void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
|
|
1/*prefer cHRM values*/);
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_sRGB_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte intent;
|
|
|
|
png_debug(1, "in png_handle_sRGB");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
if (length != 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, &intent, 1);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
/* If a colorspace error has already been output skip this chunk */
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
|
|
return;
|
|
|
|
/* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
|
|
* this.
|
|
*/
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0)
|
|
{
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
png_chunk_benign_error(png_ptr, "too many profiles");
|
|
return;
|
|
}
|
|
|
|
(void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
}
|
|
#endif /* READ_sRGB */
|
|
|
|
#ifdef PNG_READ_iCCP_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
/* Note: this does not properly handle profiles that are > 64K under DOS */
|
|
{
|
|
png_const_charp errmsg = NULL; /* error message output, or no error */
|
|
int finished = 0; /* crc checked */
|
|
|
|
png_debug(1, "in png_handle_iCCP");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
/* Consistent with all the above colorspace handling an obviously *invalid*
|
|
* chunk is just ignored, so does not invalidate the color space. An
|
|
* alternative is to set the 'invalid' flags at the start of this routine
|
|
* and only clear them in they were not set before and all the tests pass.
|
|
* The minimum 'deflate' stream is assumed to be just the 2 byte header and
|
|
* 4 byte checksum. The keyword must be at least one character and there is
|
|
* a terminator (0) byte and the compression method.
|
|
*/
|
|
if (length < 9)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "too short");
|
|
return;
|
|
}
|
|
|
|
/* If a colorspace error has already been output skip this chunk */
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
/* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
|
|
* this.
|
|
*/
|
|
if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0)
|
|
{
|
|
uInt read_length, keyword_length;
|
|
char keyword[81];
|
|
|
|
/* Find the keyword; the keyword plus separator and compression method
|
|
* bytes can be at most 81 characters long.
|
|
*/
|
|
read_length = 81; /* maximum */
|
|
if (read_length > length)
|
|
read_length = (uInt)length;
|
|
|
|
png_crc_read(png_ptr, (png_bytep)keyword, read_length);
|
|
length -= read_length;
|
|
|
|
keyword_length = 0;
|
|
while (keyword_length < 80 && keyword_length < read_length &&
|
|
keyword[keyword_length] != 0)
|
|
++keyword_length;
|
|
|
|
/* TODO: make the keyword checking common */
|
|
if (keyword_length >= 1 && keyword_length <= 79)
|
|
{
|
|
/* We only understand '0' compression - deflate - so if we get a
|
|
* different value we can't safely decode the chunk.
|
|
*/
|
|
if (keyword_length+1 < read_length &&
|
|
keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE)
|
|
{
|
|
read_length -= keyword_length+2;
|
|
|
|
if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)
|
|
{
|
|
Byte profile_header[132];
|
|
Byte local_buffer[PNG_INFLATE_BUF_SIZE];
|
|
png_alloc_size_t size = (sizeof profile_header);
|
|
|
|
png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2);
|
|
png_ptr->zstream.avail_in = read_length;
|
|
(void)png_inflate_read(png_ptr, local_buffer,
|
|
(sizeof local_buffer), &length, profile_header, &size,
|
|
0/*finish: don't, because the output is too small*/);
|
|
|
|
if (size == 0)
|
|
{
|
|
/* We have the ICC profile header; do the basic header checks.
|
|
*/
|
|
const png_uint_32 profile_length =
|
|
png_get_uint_32(profile_header);
|
|
|
|
if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
|
|
keyword, profile_length) != 0)
|
|
{
|
|
/* The length is apparently ok, so we can check the 132
|
|
* byte header.
|
|
*/
|
|
if (png_icc_check_header(png_ptr, &png_ptr->colorspace,
|
|
keyword, profile_length, profile_header,
|
|
png_ptr->color_type) != 0)
|
|
{
|
|
/* Now read the tag table; a variable size buffer is
|
|
* needed at this point, allocate one for the whole
|
|
* profile. The header check has already validated
|
|
* that none of these stuff will overflow.
|
|
*/
|
|
const png_uint_32 tag_count = png_get_uint_32(
|
|
profile_header+128);
|
|
png_bytep profile = png_read_buffer(png_ptr,
|
|
profile_length, 2/*silent*/);
|
|
|
|
if (profile != NULL)
|
|
{
|
|
memcpy(profile, profile_header,
|
|
(sizeof profile_header));
|
|
|
|
size = 12 * tag_count;
|
|
|
|
(void)png_inflate_read(png_ptr, local_buffer,
|
|
(sizeof local_buffer), &length,
|
|
profile + (sizeof profile_header), &size, 0);
|
|
|
|
/* Still expect a buffer error because we expect
|
|
* there to be some tag data!
|
|
*/
|
|
if (size == 0)
|
|
{
|
|
if (png_icc_check_tag_table(png_ptr,
|
|
&png_ptr->colorspace, keyword, profile_length,
|
|
profile) != 0)
|
|
{
|
|
/* The profile has been validated for basic
|
|
* security issues, so read the whole thing in.
|
|
*/
|
|
size = profile_length - (sizeof profile_header)
|
|
- 12 * tag_count;
|
|
|
|
(void)png_inflate_read(png_ptr, local_buffer,
|
|
(sizeof local_buffer), &length,
|
|
profile + (sizeof profile_header) +
|
|
12 * tag_count, &size, 1/*finish*/);
|
|
|
|
if (length > 0 && !(png_ptr->flags &
|
|
PNG_FLAG_BENIGN_ERRORS_WARN))
|
|
errmsg = "extra compressed data";
|
|
|
|
/* But otherwise allow extra data: */
|
|
else if (size == 0)
|
|
{
|
|
if (length > 0)
|
|
{
|
|
/* This can be handled completely, so
|
|
* keep going.
|
|
*/
|
|
png_chunk_warning(png_ptr,
|
|
"extra compressed data");
|
|
}
|
|
|
|
png_crc_finish(png_ptr, length);
|
|
finished = 1;
|
|
|
|
# ifdef PNG_sRGB_SUPPORTED
|
|
/* Check for a match against sRGB */
|
|
png_icc_set_sRGB(png_ptr,
|
|
&png_ptr->colorspace, profile,
|
|
png_ptr->zstream.adler);
|
|
# endif
|
|
|
|
/* Steal the profile for info_ptr. */
|
|
if (info_ptr != NULL)
|
|
{
|
|
png_free_data(png_ptr, info_ptr,
|
|
PNG_FREE_ICCP, 0);
|
|
|
|
info_ptr->iccp_name = png_voidcast(char*,
|
|
png_malloc_base(png_ptr,
|
|
keyword_length+1));
|
|
if (info_ptr->iccp_name != NULL)
|
|
{
|
|
memcpy(info_ptr->iccp_name, keyword,
|
|
keyword_length+1);
|
|
info_ptr->iccp_proflen =
|
|
profile_length;
|
|
info_ptr->iccp_profile = profile;
|
|
png_ptr->read_buffer = NULL; /*steal*/
|
|
info_ptr->free_me |= PNG_FREE_ICCP;
|
|
info_ptr->valid |= PNG_INFO_iCCP;
|
|
}
|
|
|
|
else
|
|
{
|
|
png_ptr->colorspace.flags |=
|
|
PNG_COLORSPACE_INVALID;
|
|
errmsg = "out of memory";
|
|
}
|
|
}
|
|
|
|
/* else the profile remains in the read
|
|
* buffer which gets reused for subsequent
|
|
* chunks.
|
|
*/
|
|
|
|
if (info_ptr != NULL)
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
|
|
if (errmsg == NULL)
|
|
{
|
|
png_ptr->zowner = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
else if (size > 0)
|
|
errmsg = "truncated";
|
|
|
|
#ifndef __COVERITY__
|
|
else
|
|
errmsg = png_ptr->zstream.msg;
|
|
#endif
|
|
}
|
|
|
|
/* else png_icc_check_tag_table output an error */
|
|
}
|
|
|
|
else /* profile truncated */
|
|
errmsg = png_ptr->zstream.msg;
|
|
}
|
|
|
|
else
|
|
errmsg = "out of memory";
|
|
}
|
|
|
|
/* else png_icc_check_header output an error */
|
|
}
|
|
|
|
/* else png_icc_check_length output an error */
|
|
}
|
|
|
|
else /* profile truncated */
|
|
errmsg = png_ptr->zstream.msg;
|
|
|
|
/* Release the stream */
|
|
png_ptr->zowner = 0;
|
|
}
|
|
|
|
else /* png_inflate_claim failed */
|
|
errmsg = png_ptr->zstream.msg;
|
|
}
|
|
|
|
else
|
|
errmsg = "bad compression method"; /* or missing */
|
|
}
|
|
|
|
else
|
|
errmsg = "bad keyword";
|
|
}
|
|
|
|
else
|
|
errmsg = "too many profiles";
|
|
|
|
/* Failure: the reason is in 'errmsg' */
|
|
if (finished == 0)
|
|
png_crc_finish(png_ptr, length);
|
|
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
|
|
png_colorspace_sync(png_ptr, info_ptr);
|
|
if (errmsg != NULL) /* else already output */
|
|
png_chunk_benign_error(png_ptr, errmsg);
|
|
}
|
|
#endif /* READ_iCCP */
|
|
|
|
#ifdef PNG_READ_sPLT_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
/* Note: this does not properly handle chunks that are > 64K under DOS */
|
|
{
|
|
png_bytep entry_start, buffer;
|
|
png_sPLT_t new_palette;
|
|
png_sPLT_entryp pp;
|
|
png_uint_32 data_length;
|
|
int entry_size, i;
|
|
png_uint_32 skip = 0;
|
|
png_uint_32 dl;
|
|
png_size_t max_dl;
|
|
|
|
png_debug(1, "in png_handle_sPLT");
|
|
|
|
#ifdef PNG_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_cache_max != 0)
|
|
{
|
|
if (png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
if (--png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_warning(png_ptr, "No space in chunk cache for sPLT");
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
#ifdef PNG_MAX_MALLOC_64K
|
|
if (length > 65535U)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "too large to fit in memory");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
|
|
if (buffer == NULL)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
|
|
/* WARNING: this may break if size_t is less than 32 bits; it is assumed
|
|
* that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a
|
|
* potential breakage point if the types in pngconf.h aren't exactly right.
|
|
*/
|
|
png_crc_read(png_ptr, buffer, length);
|
|
|
|
if (png_crc_finish(png_ptr, skip) != 0)
|
|
return;
|
|
|
|
buffer[length] = 0;
|
|
|
|
for (entry_start = buffer; *entry_start; entry_start++)
|
|
/* Empty loop to find end of name */ ;
|
|
|
|
++entry_start;
|
|
|
|
/* A sample depth should follow the separator, and we should be on it */
|
|
if (entry_start > buffer + length - 2)
|
|
{
|
|
png_warning(png_ptr, "malformed sPLT chunk");
|
|
return;
|
|
}
|
|
|
|
new_palette.depth = *entry_start++;
|
|
entry_size = (new_palette.depth == 8 ? 6 : 10);
|
|
/* This must fit in a png_uint_32 because it is derived from the original
|
|
* chunk data length.
|
|
*/
|
|
data_length = length - (png_uint_32)(entry_start - buffer);
|
|
|
|
/* Integrity-check the data length */
|
|
if ((data_length % entry_size) != 0)
|
|
{
|
|
png_warning(png_ptr, "sPLT chunk has bad length");
|
|
return;
|
|
}
|
|
|
|
dl = (png_int_32)(data_length / entry_size);
|
|
max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));
|
|
|
|
if (dl > max_dl)
|
|
{
|
|
png_warning(png_ptr, "sPLT chunk too long");
|
|
return;
|
|
}
|
|
|
|
new_palette.nentries = (png_int_32)(data_length / entry_size);
|
|
|
|
new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
|
|
png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)));
|
|
|
|
if (new_palette.entries == NULL)
|
|
{
|
|
png_warning(png_ptr, "sPLT chunk requires too much memory");
|
|
return;
|
|
}
|
|
|
|
#ifdef PNG_POINTER_INDEXING_SUPPORTED
|
|
for (i = 0; i < new_palette.nentries; i++)
|
|
{
|
|
pp = new_palette.entries + i;
|
|
|
|
if (new_palette.depth == 8)
|
|
{
|
|
pp->red = *entry_start++;
|
|
pp->green = *entry_start++;
|
|
pp->blue = *entry_start++;
|
|
pp->alpha = *entry_start++;
|
|
}
|
|
|
|
else
|
|
{
|
|
pp->red = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp->green = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp->blue = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
|
|
}
|
|
|
|
pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
|
|
}
|
|
#else
|
|
pp = new_palette.entries;
|
|
|
|
for (i = 0; i < new_palette.nentries; i++)
|
|
{
|
|
|
|
if (new_palette.depth == 8)
|
|
{
|
|
pp[i].red = *entry_start++;
|
|
pp[i].green = *entry_start++;
|
|
pp[i].blue = *entry_start++;
|
|
pp[i].alpha = *entry_start++;
|
|
}
|
|
|
|
else
|
|
{
|
|
pp[i].red = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp[i].blue = png_get_uint_16(entry_start); entry_start += 2;
|
|
pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
|
|
}
|
|
|
|
pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;
|
|
}
|
|
#endif
|
|
|
|
/* Discard all chunk data except the name and stash that */
|
|
new_palette.name = (png_charp)buffer;
|
|
|
|
png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
|
|
|
|
png_free(png_ptr, new_palette.entries);
|
|
}
|
|
#endif /* READ_sPLT */
|
|
|
|
#ifdef PNG_READ_tRNS_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
|
|
|
|
png_debug(1, "in png_handle_tRNS");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
|
|
{
|
|
png_byte buf[2];
|
|
|
|
if (length != 2)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 2);
|
|
png_ptr->num_trans = 1;
|
|
png_ptr->trans_color.gray = png_get_uint_16(buf);
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
|
|
{
|
|
png_byte buf[6];
|
|
|
|
if (length != 6)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, length);
|
|
png_ptr->num_trans = 1;
|
|
png_ptr->trans_color.red = png_get_uint_16(buf);
|
|
png_ptr->trans_color.green = png_get_uint_16(buf + 2);
|
|
png_ptr->trans_color.blue = png_get_uint_16(buf + 4);
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
if ((png_ptr->mode & PNG_HAVE_PLTE) == 0)
|
|
{
|
|
/* TODO: is this actually an error in the ISO spec? */
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
if (length > (unsigned int) png_ptr->num_palette ||
|
|
length > (unsigned int) PNG_MAX_PALETTE_LENGTH ||
|
|
length == 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, readbuf, length);
|
|
png_ptr->num_trans = (png_uint_16)length;
|
|
}
|
|
|
|
else
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid with alpha channel");
|
|
return;
|
|
}
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
{
|
|
png_ptr->num_trans = 0;
|
|
return;
|
|
}
|
|
|
|
/* TODO: this is a horrible side effect in the palette case because the
|
|
* png_struct ends up with a pointer to the tRNS buffer owned by the
|
|
* png_info. Fix this.
|
|
*/
|
|
png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
|
|
&(png_ptr->trans_color));
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_bKGD_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
unsigned int truelen;
|
|
png_byte buf[6];
|
|
png_color_16 background;
|
|
|
|
png_debug(1, "in png_handle_bKGD");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
|
|
(png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
|
|
(png_ptr->mode & PNG_HAVE_PLTE) == 0))
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
truelen = 1;
|
|
|
|
else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
|
|
truelen = 6;
|
|
|
|
else
|
|
truelen = 2;
|
|
|
|
if (length != truelen)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, truelen);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
/* We convert the index value into RGB components so that we can allow
|
|
* arbitrary RGB values for background when we have transparency, and
|
|
* so it is easy to determine the RGB values of the background color
|
|
* from the info_ptr struct.
|
|
*/
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
background.index = buf[0];
|
|
|
|
if (info_ptr != NULL && info_ptr->num_palette != 0)
|
|
{
|
|
if (buf[0] >= info_ptr->num_palette)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid index");
|
|
return;
|
|
}
|
|
|
|
background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
|
|
background.green = (png_uint_16)png_ptr->palette[buf[0]].green;
|
|
background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;
|
|
}
|
|
|
|
else
|
|
background.red = background.green = background.blue = 0;
|
|
|
|
background.gray = 0;
|
|
}
|
|
|
|
else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */
|
|
{
|
|
background.index = 0;
|
|
background.red =
|
|
background.green =
|
|
background.blue =
|
|
background.gray = png_get_uint_16(buf);
|
|
}
|
|
|
|
else
|
|
{
|
|
background.index = 0;
|
|
background.red = png_get_uint_16(buf);
|
|
background.green = png_get_uint_16(buf + 2);
|
|
background.blue = png_get_uint_16(buf + 4);
|
|
background.gray = 0;
|
|
}
|
|
|
|
png_set_bKGD(png_ptr, info_ptr, &background);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_hIST_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
unsigned int num, i;
|
|
png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
|
|
|
|
png_debug(1, "in png_handle_hIST");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
|
|
(png_ptr->mode & PNG_HAVE_PLTE) == 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
num = length / 2 ;
|
|
|
|
if (num != (unsigned int) png_ptr->num_palette ||
|
|
num > (unsigned int) PNG_MAX_PALETTE_LENGTH)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
png_byte buf[2];
|
|
|
|
png_crc_read(png_ptr, buf, 2);
|
|
readbuf[i] = png_get_uint_16(buf);
|
|
}
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
png_set_hIST(png_ptr, info_ptr, readbuf);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_pHYs_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte buf[9];
|
|
png_uint_32 res_x, res_y;
|
|
int unit_type;
|
|
|
|
png_debug(1, "in png_handle_pHYs");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if (length != 9)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 9);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
res_x = png_get_uint_32(buf);
|
|
res_y = png_get_uint_32(buf + 4);
|
|
unit_type = buf[8];
|
|
png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_oFFs_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte buf[9];
|
|
png_int_32 offset_x, offset_y;
|
|
int unit_type;
|
|
|
|
png_debug(1, "in png_handle_oFFs");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if (length != 9)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 9);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
offset_x = png_get_int_32(buf);
|
|
offset_y = png_get_int_32(buf + 4);
|
|
unit_type = buf[8];
|
|
png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_pCAL_SUPPORTED
|
|
/* Read the pCAL chunk (described in the PNG Extensions document) */
|
|
void /* PRIVATE */
|
|
png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_int_32 X0, X1;
|
|
png_byte type, nparams;
|
|
png_bytep buffer, buf, units, endptr;
|
|
png_charpp params;
|
|
int i;
|
|
|
|
png_debug(1, "in png_handle_pCAL");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
|
|
length + 1);
|
|
|
|
buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buffer, length);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
buffer[length] = 0; /* Null terminate the last string */
|
|
|
|
png_debug(3, "Finding end of pCAL purpose string");
|
|
for (buf = buffer; *buf; buf++)
|
|
/* Empty loop */ ;
|
|
|
|
endptr = buffer + length;
|
|
|
|
/* We need to have at least 12 bytes after the purpose string
|
|
* in order to get the parameter information.
|
|
*/
|
|
if (endptr <= buf + 12)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");
|
|
X0 = png_get_int_32((png_bytep)buf+1);
|
|
X1 = png_get_int_32((png_bytep)buf+5);
|
|
type = buf[9];
|
|
nparams = buf[10];
|
|
units = buf + 11;
|
|
|
|
png_debug(3, "Checking pCAL equation type and number of parameters");
|
|
/* Check that we have the right number of parameters for known
|
|
* equation types.
|
|
*/
|
|
if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
|
|
(type == PNG_EQUATION_BASE_E && nparams != 3) ||
|
|
(type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
|
|
(type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid parameter count");
|
|
return;
|
|
}
|
|
|
|
else if (type >= PNG_EQUATION_LAST)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "unrecognized equation type");
|
|
}
|
|
|
|
for (buf = units; *buf; buf++)
|
|
/* Empty loop to move past the units string. */ ;
|
|
|
|
png_debug(3, "Allocating pCAL parameters array");
|
|
|
|
params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
|
|
nparams * (sizeof (png_charp))));
|
|
|
|
if (params == NULL)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
/* Get pointers to the start of each parameter string. */
|
|
for (i = 0; i < nparams; i++)
|
|
{
|
|
buf++; /* Skip the null string terminator from previous parameter. */
|
|
|
|
png_debug1(3, "Reading pCAL parameter %d", i);
|
|
|
|
for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++)
|
|
/* Empty loop to move past each parameter string */ ;
|
|
|
|
/* Make sure we haven't run out of data yet */
|
|
if (buf > endptr)
|
|
{
|
|
png_free(png_ptr, params);
|
|
png_chunk_benign_error(png_ptr, "invalid data");
|
|
return;
|
|
}
|
|
}
|
|
|
|
png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
|
|
(png_charp)units, params);
|
|
|
|
png_free(png_ptr, params);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_sCAL_SUPPORTED
|
|
/* Read the sCAL chunk */
|
|
void /* PRIVATE */
|
|
png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_bytep buffer;
|
|
png_size_t i;
|
|
int state;
|
|
|
|
png_debug(1, "in png_handle_sCAL");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of place");
|
|
return;
|
|
}
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
/* Need unit type, width, \0, height: minimum 4 bytes */
|
|
else if (length < 4)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
|
|
length + 1);
|
|
|
|
buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buffer, length);
|
|
buffer[length] = 0; /* Null terminate the last string */
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
/* Validate the unit. */
|
|
if (buffer[0] != 1 && buffer[0] != 2)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "invalid unit");
|
|
return;
|
|
}
|
|
|
|
/* Validate the ASCII numbers, need two ASCII numbers separated by
|
|
* a '\0' and they need to fit exactly in the chunk data.
|
|
*/
|
|
i = 1;
|
|
state = 0;
|
|
|
|
if (png_check_fp_number((png_const_charp)buffer, length, &state, &i) == 0 ||
|
|
i >= length || buffer[i++] != 0)
|
|
png_chunk_benign_error(png_ptr, "bad width format");
|
|
|
|
else if (PNG_FP_IS_POSITIVE(state) == 0)
|
|
png_chunk_benign_error(png_ptr, "non-positive width");
|
|
|
|
else
|
|
{
|
|
png_size_t heighti = i;
|
|
|
|
state = 0;
|
|
if (png_check_fp_number((png_const_charp)buffer, length,
|
|
&state, &i) == 0 || i != length)
|
|
png_chunk_benign_error(png_ptr, "bad height format");
|
|
|
|
else if (PNG_FP_IS_POSITIVE(state) == 0)
|
|
png_chunk_benign_error(png_ptr, "non-positive height");
|
|
|
|
else
|
|
/* This is the (only) success case. */
|
|
png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
|
|
(png_charp)buffer+1, (png_charp)buffer+heighti);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_tIME_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_byte buf[7];
|
|
png_time mod_time;
|
|
|
|
png_debug(1, "in png_handle_tIME");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "duplicate");
|
|
return;
|
|
}
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
|
|
if (length != 7)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "invalid");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buf, 7);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
mod_time.second = buf[6];
|
|
mod_time.minute = buf[5];
|
|
mod_time.hour = buf[4];
|
|
mod_time.day = buf[3];
|
|
mod_time.month = buf[2];
|
|
mod_time.year = png_get_uint_16(buf);
|
|
|
|
png_set_tIME(png_ptr, info_ptr, &mod_time);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_tEXt_SUPPORTED
|
|
/* Note: this does not properly handle chunks that are > 64K under DOS */
|
|
void /* PRIVATE */
|
|
png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_text text_info;
|
|
png_bytep buffer;
|
|
png_charp key;
|
|
png_charp text;
|
|
png_uint_32 skip = 0;
|
|
|
|
png_debug(1, "in png_handle_tEXt");
|
|
|
|
#ifdef PNG_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_cache_max != 0)
|
|
{
|
|
if (png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
if (--png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "no space in chunk cache");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
|
|
#ifdef PNG_MAX_MALLOC_64K
|
|
if (length > 65535U)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "too large to fit in memory");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buffer, length);
|
|
|
|
if (png_crc_finish(png_ptr, skip) != 0)
|
|
return;
|
|
|
|
key = (png_charp)buffer;
|
|
key[length] = 0;
|
|
|
|
for (text = key; *text; text++)
|
|
/* Empty loop to find end of key */ ;
|
|
|
|
if (text != key + length)
|
|
text++;
|
|
|
|
text_info.compression = PNG_TEXT_COMPRESSION_NONE;
|
|
text_info.key = key;
|
|
text_info.lang = NULL;
|
|
text_info.lang_key = NULL;
|
|
text_info.itxt_length = 0;
|
|
text_info.text = text;
|
|
text_info.text_length = strlen(text);
|
|
|
|
if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0)
|
|
png_warning(png_ptr, "Insufficient memory to process text chunk");
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_zTXt_SUPPORTED
|
|
/* Note: this does not correctly handle chunks that are > 64K under DOS */
|
|
void /* PRIVATE */
|
|
png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_const_charp errmsg = NULL;
|
|
png_bytep buffer;
|
|
png_uint_32 keyword_length;
|
|
|
|
png_debug(1, "in png_handle_zTXt");
|
|
|
|
#ifdef PNG_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_cache_max != 0)
|
|
{
|
|
if (png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
if (--png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "no space in chunk cache");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
|
|
buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buffer, length);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
/* TODO: also check that the keyword contents match the spec! */
|
|
for (keyword_length = 0;
|
|
keyword_length < length && buffer[keyword_length] != 0;
|
|
++keyword_length)
|
|
/* Empty loop to find end of name */ ;
|
|
|
|
if (keyword_length > 79 || keyword_length < 1)
|
|
errmsg = "bad keyword";
|
|
|
|
/* zTXt must have some LZ data after the keyword, although it may expand to
|
|
* zero bytes; we need a '\0' at the end of the keyword, the compression type
|
|
* then the LZ data:
|
|
*/
|
|
else if (keyword_length + 3 > length)
|
|
errmsg = "truncated";
|
|
|
|
else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
|
|
errmsg = "unknown compression type";
|
|
|
|
else
|
|
{
|
|
png_alloc_size_t uncompressed_length = PNG_SIZE_MAX;
|
|
|
|
/* TODO: at present png_decompress_chunk imposes a single application
|
|
* level memory limit, this should be split to different values for iCCP
|
|
* and text chunks.
|
|
*/
|
|
if (png_decompress_chunk(png_ptr, length, keyword_length+2,
|
|
&uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
|
|
{
|
|
png_text text;
|
|
|
|
/* It worked; png_ptr->read_buffer now looks like a tEXt chunk except
|
|
* for the extra compression type byte and the fact that it isn't
|
|
* necessarily '\0' terminated.
|
|
*/
|
|
buffer = png_ptr->read_buffer;
|
|
buffer[uncompressed_length+(keyword_length+2)] = 0;
|
|
|
|
text.compression = PNG_TEXT_COMPRESSION_zTXt;
|
|
text.key = (png_charp)buffer;
|
|
text.text = (png_charp)(buffer + keyword_length+2);
|
|
text.text_length = uncompressed_length;
|
|
text.itxt_length = 0;
|
|
text.lang = NULL;
|
|
text.lang_key = NULL;
|
|
|
|
if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
|
|
errmsg = "insufficient memory";
|
|
}
|
|
|
|
else
|
|
errmsg = png_ptr->zstream.msg;
|
|
}
|
|
|
|
if (errmsg != NULL)
|
|
png_chunk_benign_error(png_ptr, errmsg);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_iTXt_SUPPORTED
|
|
/* Note: this does not correctly handle chunks that are > 64K under DOS */
|
|
void /* PRIVATE */
|
|
png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
|
|
{
|
|
png_const_charp errmsg = NULL;
|
|
png_bytep buffer;
|
|
png_uint_32 prefix_length;
|
|
|
|
png_debug(1, "in png_handle_iTXt");
|
|
|
|
#ifdef PNG_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_cache_max != 0)
|
|
{
|
|
if (png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
return;
|
|
}
|
|
|
|
if (--png_ptr->user_chunk_cache_max == 1)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "no space in chunk cache");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
|
|
png_chunk_error(png_ptr, "missing IHDR");
|
|
|
|
if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
|
|
buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "out of memory");
|
|
return;
|
|
}
|
|
|
|
png_crc_read(png_ptr, buffer, length);
|
|
|
|
if (png_crc_finish(png_ptr, 0) != 0)
|
|
return;
|
|
|
|
/* First the keyword. */
|
|
for (prefix_length=0;
|
|
prefix_length < length && buffer[prefix_length] != 0;
|
|
++prefix_length)
|
|
/* Empty loop */ ;
|
|
|
|
/* Perform a basic check on the keyword length here. */
|
|
if (prefix_length > 79 || prefix_length < 1)
|
|
errmsg = "bad keyword";
|
|
|
|
/* Expect keyword, compression flag, compression type, language, translated
|
|
* keyword (both may be empty but are 0 terminated) then the text, which may
|
|
* be empty.
|
|
*/
|
|
else if (prefix_length + 5 > length)
|
|
errmsg = "truncated";
|
|
|
|
else if (buffer[prefix_length+1] == 0 ||
|
|
(buffer[prefix_length+1] == 1 &&
|
|
buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
|
|
{
|
|
int compressed = buffer[prefix_length+1] != 0;
|
|
png_uint_32 language_offset, translated_keyword_offset;
|
|
png_alloc_size_t uncompressed_length = 0;
|
|
|
|
/* Now the language tag */
|
|
prefix_length += 3;
|
|
language_offset = prefix_length;
|
|
|
|
for (; prefix_length < length && buffer[prefix_length] != 0;
|
|
++prefix_length)
|
|
/* Empty loop */ ;
|
|
|
|
/* WARNING: the length may be invalid here, this is checked below. */
|
|
translated_keyword_offset = ++prefix_length;
|
|
|
|
for (; prefix_length < length && buffer[prefix_length] != 0;
|
|
++prefix_length)
|
|
/* Empty loop */ ;
|
|
|
|
/* prefix_length should now be at the trailing '\0' of the translated
|
|
* keyword, but it may already be over the end. None of this arithmetic
|
|
* can overflow because chunks are at most 2^31 bytes long, but on 16-bit
|
|
* systems the available allocation may overflow.
|
|
*/
|
|
++prefix_length;
|
|
|
|
if (compressed == 0 && prefix_length <= length)
|
|
uncompressed_length = length - prefix_length;
|
|
|
|
else if (compressed != 0 && prefix_length < length)
|
|
{
|
|
uncompressed_length = PNG_SIZE_MAX;
|
|
|
|
/* TODO: at present png_decompress_chunk imposes a single application
|
|
* level memory limit, this should be split to different values for
|
|
* iCCP and text chunks.
|
|
*/
|
|
if (png_decompress_chunk(png_ptr, length, prefix_length,
|
|
&uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
|
|
buffer = png_ptr->read_buffer;
|
|
|
|
else
|
|
errmsg = png_ptr->zstream.msg;
|
|
}
|
|
|
|
else
|
|
errmsg = "truncated";
|
|
|
|
if (errmsg == NULL)
|
|
{
|
|
png_text text;
|
|
|
|
buffer[uncompressed_length+prefix_length] = 0;
|
|
|
|
if (compressed == 0)
|
|
text.compression = PNG_ITXT_COMPRESSION_NONE;
|
|
|
|
else
|
|
text.compression = PNG_ITXT_COMPRESSION_zTXt;
|
|
|
|
text.key = (png_charp)buffer;
|
|
text.lang = (png_charp)buffer + language_offset;
|
|
text.lang_key = (png_charp)buffer + translated_keyword_offset;
|
|
text.text = (png_charp)buffer + prefix_length;
|
|
text.text_length = 0;
|
|
text.itxt_length = uncompressed_length;
|
|
|
|
if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
|
|
errmsg = "insufficient memory";
|
|
}
|
|
}
|
|
|
|
else
|
|
errmsg = "bad compression info";
|
|
|
|
if (errmsg != NULL)
|
|
png_chunk_benign_error(png_ptr, errmsg);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
|
/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */
|
|
static int
|
|
png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
|
|
{
|
|
png_alloc_size_t limit = PNG_SIZE_MAX;
|
|
|
|
if (png_ptr->unknown_chunk.data != NULL)
|
|
{
|
|
png_free(png_ptr, png_ptr->unknown_chunk.data);
|
|
png_ptr->unknown_chunk.data = NULL;
|
|
}
|
|
|
|
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
|
|
if (png_ptr->user_chunk_malloc_max > 0 &&
|
|
png_ptr->user_chunk_malloc_max < limit)
|
|
limit = png_ptr->user_chunk_malloc_max;
|
|
|
|
# elif PNG_USER_CHUNK_MALLOC_MAX > 0
|
|
if (PNG_USER_CHUNK_MALLOC_MAX < limit)
|
|
limit = PNG_USER_CHUNK_MALLOC_MAX;
|
|
# endif
|
|
|
|
if (length <= limit)
|
|
{
|
|
PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
|
|
/* The following is safe because of the PNG_SIZE_MAX init above */
|
|
png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/;
|
|
/* 'mode' is a flag array, only the bottom four bits matter here */
|
|
png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/;
|
|
|
|
if (length == 0)
|
|
png_ptr->unknown_chunk.data = NULL;
|
|
|
|
else
|
|
{
|
|
/* Do a 'warn' here - it is handled below. */
|
|
png_ptr->unknown_chunk.data = png_voidcast(png_bytep,
|
|
png_malloc_warn(png_ptr, length));
|
|
}
|
|
}
|
|
|
|
if (png_ptr->unknown_chunk.data == NULL && length > 0)
|
|
{
|
|
/* This is benign because we clean up correctly */
|
|
png_crc_finish(png_ptr, length);
|
|
png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits");
|
|
return 0;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (length > 0)
|
|
png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length);
|
|
png_crc_finish(png_ptr, 0);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif /* READ_UNKNOWN_CHUNKS */
|
|
|
|
/* Handle an unknown, or known but disabled, chunk */
|
|
void /* PRIVATE */
|
|
png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
|
|
png_uint_32 length, int keep)
|
|
{
|
|
int handled = 0; /* the chunk was handled */
|
|
|
|
png_debug(1, "in png_handle_unknown");
|
|
|
|
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
|
/* NOTE: this code is based on the code in libpng-1.4.12 except for fixing
|
|
* the bug which meant that setting a non-default behavior for a specific
|
|
* chunk would be ignored (the default was always used unless a user
|
|
* callback was installed).
|
|
*
|
|
* 'keep' is the value from the png_chunk_unknown_handling, the setting for
|
|
* this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it
|
|
* will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here.
|
|
* This is just an optimization to avoid multiple calls to the lookup
|
|
* function.
|
|
*/
|
|
# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
|
# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
|
|
keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name);
|
|
# endif
|
|
# endif
|
|
|
|
/* One of the following methods will read the chunk or skip it (at least one
|
|
* of these is always defined because this is the only way to switch on
|
|
* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
|
|
*/
|
|
# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
|
|
/* The user callback takes precedence over the chunk keep value, but the
|
|
* keep value is still required to validate a save of a critical chunk.
|
|
*/
|
|
if (png_ptr->read_user_chunk_fn != NULL)
|
|
{
|
|
if (png_cache_unknown_chunk(png_ptr, length) != 0)
|
|
{
|
|
/* Callback to user unknown chunk handler */
|
|
int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr,
|
|
&png_ptr->unknown_chunk);
|
|
|
|
/* ret is:
|
|
* negative: An error occurred; png_chunk_error will be called.
|
|
* zero: The chunk was not handled, the chunk will be discarded
|
|
* unless png_set_keep_unknown_chunks has been used to set
|
|
* a 'keep' behavior for this particular chunk, in which
|
|
* case that will be used. A critical chunk will cause an
|
|
* error at this point unless it is to be saved.
|
|
* positive: The chunk was handled, libpng will ignore/discard it.
|
|
*/
|
|
if (ret < 0)
|
|
png_chunk_error(png_ptr, "error in user chunk");
|
|
|
|
else if (ret == 0)
|
|
{
|
|
/* If the keep value is 'default' or 'never' override it, but
|
|
* still error out on critical chunks unless the keep value is
|
|
* 'always' While this is weird it is the behavior in 1.4.12.
|
|
* A possible improvement would be to obey the value set for the
|
|
* chunk, but this would be an API change that would probably
|
|
* damage some applications.
|
|
*
|
|
* The png_app_warning below catches the case that matters, where
|
|
* the application has not set specific save or ignore for this
|
|
* chunk or global save or ignore.
|
|
*/
|
|
if (keep < PNG_HANDLE_CHUNK_IF_SAFE)
|
|
{
|
|
# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
|
|
if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE)
|
|
{
|
|
png_chunk_warning(png_ptr, "Saving unknown chunk:");
|
|
png_app_warning(png_ptr,
|
|
"forcing save of an unhandled chunk;"
|
|
" please call png_set_keep_unknown_chunks");
|
|
/* with keep = PNG_HANDLE_CHUNK_IF_SAFE */
|
|
}
|
|
# endif
|
|
keep = PNG_HANDLE_CHUNK_IF_SAFE;
|
|
}
|
|
}
|
|
|
|
else /* chunk was handled */
|
|
{
|
|
handled = 1;
|
|
/* Critical chunks can be safely discarded at this point. */
|
|
keep = PNG_HANDLE_CHUNK_NEVER;
|
|
}
|
|
}
|
|
|
|
else
|
|
keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */
|
|
}
|
|
|
|
else
|
|
/* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */
|
|
# endif /* READ_USER_CHUNKS */
|
|
|
|
# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
|
|
{
|
|
/* keep is currently just the per-chunk setting, if there was no
|
|
* setting change it to the global default now (not that this may
|
|
* still be AS_DEFAULT) then obtain the cache of the chunk if required,
|
|
* if not simply skip the chunk.
|
|
*/
|
|
if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
|
|
keep = png_ptr->unknown_default;
|
|
|
|
if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
|
|
(keep == PNG_HANDLE_CHUNK_IF_SAFE &&
|
|
PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
|
|
{
|
|
if (png_cache_unknown_chunk(png_ptr, length) == 0)
|
|
keep = PNG_HANDLE_CHUNK_NEVER;
|
|
}
|
|
|
|
else
|
|
png_crc_finish(png_ptr, length);
|
|
}
|
|
# else
|
|
# ifndef PNG_READ_USER_CHUNKS_SUPPORTED
|
|
# error no method to support READ_UNKNOWN_CHUNKS
|
|
# endif
|
|
|
|
{
|
|
/* If here there is no read callback pointer set and no support is
|
|
* compiled in to just save the unknown chunks, so simply skip this
|
|
* chunk. If 'keep' is something other than AS_DEFAULT or NEVER then
|
|
* the app has erroneously asked for unknown chunk saving when there
|
|
* is no support.
|
|
*/
|
|
if (keep > PNG_HANDLE_CHUNK_NEVER)
|
|
png_app_error(png_ptr, "no unknown chunk support available");
|
|
|
|
png_crc_finish(png_ptr, length);
|
|
}
|
|
# endif
|
|
|
|
# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
|
|
/* Now store the chunk in the chunk list if appropriate, and if the limits
|
|
* permit it.
|
|
*/
|
|
if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
|
|
(keep == PNG_HANDLE_CHUNK_IF_SAFE &&
|
|
PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
|
|
{
|
|
# ifdef PNG_USER_LIMITS_SUPPORTED
|
|
switch (png_ptr->user_chunk_cache_max)
|
|
{
|
|
case 2:
|
|
png_ptr->user_chunk_cache_max = 1;
|
|
png_chunk_benign_error(png_ptr, "no space in chunk cache");
|
|
/* FALL THROUGH */
|
|
case 1:
|
|
/* NOTE: prior to 1.6.0 this case resulted in an unknown critical
|
|
* chunk being skipped, now there will be a hard error below.
|
|
*/
|
|
break;
|
|
|
|
default: /* not at limit */
|
|
--(png_ptr->user_chunk_cache_max);
|
|
/* FALL THROUGH */
|
|
case 0: /* no limit */
|
|
# endif /* USER_LIMITS */
|
|
/* Here when the limit isn't reached or when limits are compiled
|
|
* out; store the chunk.
|
|
*/
|
|
png_set_unknown_chunks(png_ptr, info_ptr,
|
|
&png_ptr->unknown_chunk, 1);
|
|
handled = 1;
|
|
# ifdef PNG_USER_LIMITS_SUPPORTED
|
|
break;
|
|
}
|
|
# endif
|
|
}
|
|
# else /* no store support: the chunk must be handled by the user callback */
|
|
PNG_UNUSED(info_ptr)
|
|
# endif
|
|
|
|
/* Regardless of the error handling below the cached data (if any) can be
|
|
* freed now. Notice that the data is not freed if there is a png_error, but
|
|
* it will be freed by destroy_read_struct.
|
|
*/
|
|
if (png_ptr->unknown_chunk.data != NULL)
|
|
png_free(png_ptr, png_ptr->unknown_chunk.data);
|
|
png_ptr->unknown_chunk.data = NULL;
|
|
|
|
#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
|
|
/* There is no support to read an unknown chunk, so just skip it. */
|
|
png_crc_finish(png_ptr, length);
|
|
PNG_UNUSED(info_ptr)
|
|
PNG_UNUSED(keep)
|
|
#endif /* !READ_UNKNOWN_CHUNKS */
|
|
|
|
/* Check for unhandled critical chunks */
|
|
if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
|
|
png_chunk_error(png_ptr, "unhandled critical chunk");
|
|
}
|
|
|
|
/* This function is called to verify that a chunk name is valid.
|
|
* This function can't have the "critical chunk check" incorporated
|
|
* into it, since in the future we will need to be able to call user
|
|
* functions to handle unknown critical chunks after we check that
|
|
* the chunk name itself is valid.
|
|
*/
|
|
|
|
/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is:
|
|
*
|
|
* ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
|
|
*/
|
|
|
|
void /* PRIVATE */
|
|
png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)
|
|
{
|
|
int i;
|
|
|
|
png_debug(1, "in png_check_chunk_name");
|
|
|
|
for (i=1; i<=4; ++i)
|
|
{
|
|
int c = chunk_name & 0xff;
|
|
|
|
if (c < 65 || c > 122 || (c > 90 && c < 97))
|
|
png_chunk_error(png_ptr, "invalid chunk type");
|
|
|
|
chunk_name >>= 8;
|
|
}
|
|
}
|
|
|
|
/* Combines the row recently read in with the existing pixels in the row. This
|
|
* routine takes care of alpha and transparency if requested. This routine also
|
|
* handles the two methods of progressive display of interlaced images,
|
|
* depending on the 'display' value; if 'display' is true then the whole row
|
|
* (dp) is filled from the start by replicating the available pixels. If
|
|
* 'display' is false only those pixels present in the pass are filled in.
|
|
*/
|
|
void /* PRIVATE */
|
|
png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display)
|
|
{
|
|
unsigned int pixel_depth = png_ptr->transformed_pixel_depth;
|
|
png_const_bytep sp = png_ptr->row_buf + 1;
|
|
png_alloc_size_t row_width = png_ptr->width;
|
|
unsigned int pass = png_ptr->pass;
|
|
png_bytep end_ptr = 0;
|
|
png_byte end_byte = 0;
|
|
unsigned int end_mask;
|
|
|
|
png_debug(1, "in png_combine_row");
|
|
|
|
/* Added in 1.5.6: it should not be possible to enter this routine until at
|
|
* least one row has been read from the PNG data and transformed.
|
|
*/
|
|
if (pixel_depth == 0)
|
|
png_error(png_ptr, "internal row logic error");
|
|
|
|
/* Added in 1.5.4: the pixel depth should match the information returned by
|
|
* any call to png_read_update_info at this point. Do not continue if we got
|
|
* this wrong.
|
|
*/
|
|
if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes !=
|
|
PNG_ROWBYTES(pixel_depth, row_width))
|
|
png_error(png_ptr, "internal row size calculation error");
|
|
|
|
/* Don't expect this to ever happen: */
|
|
if (row_width == 0)
|
|
png_error(png_ptr, "internal row width error");
|
|
|
|
/* Preserve the last byte in cases where only part of it will be overwritten,
|
|
* the multiply below may overflow, we don't care because ANSI-C guarantees
|
|
* we get the low bits.
|
|
*/
|
|
end_mask = (pixel_depth * row_width) & 7;
|
|
if (end_mask != 0)
|
|
{
|
|
/* end_ptr == NULL is a flag to say do nothing */
|
|
end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1;
|
|
end_byte = *end_ptr;
|
|
# ifdef PNG_READ_PACKSWAP_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
|
|
/* little-endian byte */
|
|
end_mask = 0xff << end_mask;
|
|
|
|
else /* big-endian byte */
|
|
# endif
|
|
end_mask = 0xff >> end_mask;
|
|
/* end_mask is now the bits to *keep* from the destination row */
|
|
}
|
|
|
|
/* For non-interlaced images this reduces to a memcpy(). A memcpy()
|
|
* will also happen if interlacing isn't supported or if the application
|
|
* does not call png_set_interlace_handling(). In the latter cases the
|
|
* caller just gets a sequence of the unexpanded rows from each interlace
|
|
* pass.
|
|
*/
|
|
#ifdef PNG_READ_INTERLACING_SUPPORTED
|
|
if (png_ptr->interlaced != 0 &&
|
|
(png_ptr->transformations & PNG_INTERLACE) != 0 &&
|
|
pass < 6 && (display == 0 ||
|
|
/* The following copies everything for 'display' on passes 0, 2 and 4. */
|
|
(display == 1 && (pass & 1) != 0)))
|
|
{
|
|
/* Narrow images may have no bits in a pass; the caller should handle
|
|
* this, but this test is cheap:
|
|
*/
|
|
if (row_width <= PNG_PASS_START_COL(pass))
|
|
return;
|
|
|
|
if (pixel_depth < 8)
|
|
{
|
|
/* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit
|
|
* into 32 bits, then a single loop over the bytes using the four byte
|
|
* values in the 32-bit mask can be used. For the 'display' option the
|
|
* expanded mask may also not require any masking within a byte. To
|
|
* make this work the PACKSWAP option must be taken into account - it
|
|
* simply requires the pixels to be reversed in each byte.
|
|
*
|
|
* The 'regular' case requires a mask for each of the first 6 passes,
|
|
* the 'display' case does a copy for the even passes in the range
|
|
* 0..6. This has already been handled in the test above.
|
|
*
|
|
* The masks are arranged as four bytes with the first byte to use in
|
|
* the lowest bits (little-endian) regardless of the order (PACKSWAP or
|
|
* not) of the pixels in each byte.
|
|
*
|
|
* NOTE: the whole of this logic depends on the caller of this function
|
|
* only calling it on rows appropriate to the pass. This function only
|
|
* understands the 'x' logic; the 'y' logic is handled by the caller.
|
|
*
|
|
* The following defines allow generation of compile time constant bit
|
|
* masks for each pixel depth and each possibility of swapped or not
|
|
* swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index,
|
|
* is in the range 0..7; and the result is 1 if the pixel is to be
|
|
* copied in the pass, 0 if not. 'S' is for the sparkle method, 'B'
|
|
* for the block method.
|
|
*
|
|
* With some compilers a compile time expression of the general form:
|
|
*
|
|
* (shift >= 32) ? (a >> (shift-32)) : (b >> shift)
|
|
*
|
|
* Produces warnings with values of 'shift' in the range 33 to 63
|
|
* because the right hand side of the ?: expression is evaluated by
|
|
* the compiler even though it isn't used. Microsoft Visual C (various
|
|
* versions) and the Intel C compiler are known to do this. To avoid
|
|
* this the following macros are used in 1.5.6. This is a temporary
|
|
* solution to avoid destabilizing the code during the release process.
|
|
*/
|
|
# if PNG_USE_COMPILE_TIME_MASKS
|
|
# define PNG_LSR(x,s) ((x)>>((s) & 0x1f))
|
|
# define PNG_LSL(x,s) ((x)<<((s) & 0x1f))
|
|
# else
|
|
# define PNG_LSR(x,s) ((x)>>(s))
|
|
# define PNG_LSL(x,s) ((x)<<(s))
|
|
# endif
|
|
# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\
|
|
PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1)
|
|
# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\
|
|
PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1)
|
|
|
|
/* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is
|
|
* little endian - the first pixel is at bit 0 - however the extra
|
|
* parameter 's' can be set to cause the mask position to be swapped
|
|
* within each byte, to match the PNG format. This is done by XOR of
|
|
* the shift with 7, 6 or 4 for bit depths 1, 2 and 4.
|
|
*/
|
|
# define PIXEL_MASK(p,x,d,s) \
|
|
(PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0))))
|
|
|
|
/* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask.
|
|
*/
|
|
# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
|
|
# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
|
|
|
|
/* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp
|
|
* cases the result needs replicating, for the 4-bpp case the above
|
|
* generates a full 32 bits.
|
|
*/
|
|
# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1)))
|
|
|
|
# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\
|
|
S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\
|
|
S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d)
|
|
|
|
# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\
|
|
B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\
|
|
B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d)
|
|
|
|
#if PNG_USE_COMPILE_TIME_MASKS
|
|
/* Utility macros to construct all the masks for a depth/swap
|
|
* combination. The 's' parameter says whether the format is PNG
|
|
* (big endian bytes) or not. Only the three odd-numbered passes are
|
|
* required for the display/block algorithm.
|
|
*/
|
|
# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\
|
|
S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) }
|
|
|
|
# define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) }
|
|
|
|
# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2))
|
|
|
|
/* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and
|
|
* then pass:
|
|
*/
|
|
static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =
|
|
{
|
|
/* Little-endian byte masks for PACKSWAP */
|
|
{ S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) },
|
|
/* Normal (big-endian byte) masks - PNG format */
|
|
{ S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) }
|
|
};
|
|
|
|
/* display_mask has only three entries for the odd passes, so index by
|
|
* pass>>1.
|
|
*/
|
|
static PNG_CONST png_uint_32 display_mask[2][3][3] =
|
|
{
|
|
/* Little-endian byte masks for PACKSWAP */
|
|
{ B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) },
|
|
/* Normal (big-endian byte) masks - PNG format */
|
|
{ B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) }
|
|
};
|
|
|
|
# define MASK(pass,depth,display,png)\
|
|
((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\
|
|
row_mask[png][DEPTH_INDEX(depth)][pass])
|
|
|
|
#else /* !PNG_USE_COMPILE_TIME_MASKS */
|
|
/* This is the runtime alternative: it seems unlikely that this will
|
|
* ever be either smaller or faster than the compile time approach.
|
|
*/
|
|
# define MASK(pass,depth,display,png)\
|
|
((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png))
|
|
#endif /* !USE_COMPILE_TIME_MASKS */
|
|
|
|
/* Use the appropriate mask to copy the required bits. In some cases
|
|
* the byte mask will be 0 or 0xff; optimize these cases. row_width is
|
|
* the number of pixels, but the code copies bytes, so it is necessary
|
|
* to special case the end.
|
|
*/
|
|
png_uint_32 pixels_per_byte = 8 / pixel_depth;
|
|
png_uint_32 mask;
|
|
|
|
# ifdef PNG_READ_PACKSWAP_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
|
|
mask = MASK(pass, pixel_depth, display, 0);
|
|
|
|
else
|
|
# endif
|
|
mask = MASK(pass, pixel_depth, display, 1);
|
|
|
|
for (;;)
|
|
{
|
|
png_uint_32 m;
|
|
|
|
/* It doesn't matter in the following if png_uint_32 has more than
|
|
* 32 bits because the high bits always match those in m<<24; it is,
|
|
* however, essential to use OR here, not +, because of this.
|
|
*/
|
|
m = mask;
|
|
mask = (m >> 8) | (m << 24); /* rotate right to good compilers */
|
|
m &= 0xff;
|
|
|
|
if (m != 0) /* something to copy */
|
|
{
|
|
if (m != 0xff)
|
|
*dp = (png_byte)((*dp & ~m) | (*sp & m));
|
|
else
|
|
*dp = *sp;
|
|
}
|
|
|
|
/* NOTE: this may overwrite the last byte with garbage if the image
|
|
* is not an exact number of bytes wide; libpng has always done
|
|
* this.
|
|
*/
|
|
if (row_width <= pixels_per_byte)
|
|
break; /* May need to restore part of the last byte */
|
|
|
|
row_width -= pixels_per_byte;
|
|
++dp;
|
|
++sp;
|
|
}
|
|
}
|
|
|
|
else /* pixel_depth >= 8 */
|
|
{
|
|
unsigned int bytes_to_copy, bytes_to_jump;
|
|
|
|
/* Validate the depth - it must be a multiple of 8 */
|
|
if (pixel_depth & 7)
|
|
png_error(png_ptr, "invalid user transform pixel depth");
|
|
|
|
pixel_depth >>= 3; /* now in bytes */
|
|
row_width *= pixel_depth;
|
|
|
|
/* Regardless of pass number the Adam 7 interlace always results in a
|
|
* fixed number of pixels to copy then to skip. There may be a
|
|
* different number of pixels to skip at the start though.
|
|
*/
|
|
{
|
|
unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth;
|
|
|
|
row_width -= offset;
|
|
dp += offset;
|
|
sp += offset;
|
|
}
|
|
|
|
/* Work out the bytes to copy. */
|
|
if (display != 0)
|
|
{
|
|
/* When doing the 'block' algorithm the pixel in the pass gets
|
|
* replicated to adjacent pixels. This is why the even (0,2,4,6)
|
|
* passes are skipped above - the entire expanded row is copied.
|
|
*/
|
|
bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth;
|
|
|
|
/* But don't allow this number to exceed the actual row width. */
|
|
if (bytes_to_copy > row_width)
|
|
bytes_to_copy = (unsigned int)/*SAFE*/row_width;
|
|
}
|
|
|
|
else /* normal row; Adam7 only ever gives us one pixel to copy. */
|
|
bytes_to_copy = pixel_depth;
|
|
|
|
/* In Adam7 there is a constant offset between where the pixels go. */
|
|
bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth;
|
|
|
|
/* And simply copy these bytes. Some optimization is possible here,
|
|
* depending on the value of 'bytes_to_copy'. Special case the low
|
|
* byte counts, which we know to be frequent.
|
|
*
|
|
* Notice that these cases all 'return' rather than 'break' - this
|
|
* avoids an unnecessary test on whether to restore the last byte
|
|
* below.
|
|
*/
|
|
switch (bytes_to_copy)
|
|
{
|
|
case 1:
|
|
for (;;)
|
|
{
|
|
*dp = *sp;
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
dp += bytes_to_jump;
|
|
sp += bytes_to_jump;
|
|
row_width -= bytes_to_jump;
|
|
}
|
|
|
|
case 2:
|
|
/* There is a possibility of a partial copy at the end here; this
|
|
* slows the code down somewhat.
|
|
*/
|
|
do
|
|
{
|
|
dp[0] = sp[0], dp[1] = sp[1];
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
sp += bytes_to_jump;
|
|
dp += bytes_to_jump;
|
|
row_width -= bytes_to_jump;
|
|
}
|
|
while (row_width > 1);
|
|
|
|
/* And there can only be one byte left at this point: */
|
|
*dp = *sp;
|
|
return;
|
|
|
|
case 3:
|
|
/* This can only be the RGB case, so each copy is exactly one
|
|
* pixel and it is not necessary to check for a partial copy.
|
|
*/
|
|
for (;;)
|
|
{
|
|
dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2];
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
sp += bytes_to_jump;
|
|
dp += bytes_to_jump;
|
|
row_width -= bytes_to_jump;
|
|
}
|
|
|
|
default:
|
|
#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE
|
|
/* Check for double byte alignment and, if possible, use a
|
|
* 16-bit copy. Don't attempt this for narrow images - ones that
|
|
* are less than an interlace panel wide. Don't attempt it for
|
|
* wide bytes_to_copy either - use the memcpy there.
|
|
*/
|
|
if (bytes_to_copy < 16 /*else use memcpy*/ &&
|
|
png_isaligned(dp, png_uint_16) &&
|
|
png_isaligned(sp, png_uint_16) &&
|
|
bytes_to_copy % (sizeof (png_uint_16)) == 0 &&
|
|
bytes_to_jump % (sizeof (png_uint_16)) == 0)
|
|
{
|
|
/* Everything is aligned for png_uint_16 copies, but try for
|
|
* png_uint_32 first.
|
|
*/
|
|
if (png_isaligned(dp, png_uint_32) != 0 &&
|
|
png_isaligned(sp, png_uint_32) != 0 &&
|
|
bytes_to_copy % (sizeof (png_uint_32)) == 0 &&
|
|
bytes_to_jump % (sizeof (png_uint_32)) == 0)
|
|
{
|
|
png_uint_32p dp32 = png_aligncast(png_uint_32p,dp);
|
|
png_const_uint_32p sp32 = png_aligncastconst(
|
|
png_const_uint_32p, sp);
|
|
size_t skip = (bytes_to_jump-bytes_to_copy) /
|
|
(sizeof (png_uint_32));
|
|
|
|
do
|
|
{
|
|
size_t c = bytes_to_copy;
|
|
do
|
|
{
|
|
*dp32++ = *sp32++;
|
|
c -= (sizeof (png_uint_32));
|
|
}
|
|
while (c > 0);
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
dp32 += skip;
|
|
sp32 += skip;
|
|
row_width -= bytes_to_jump;
|
|
}
|
|
while (bytes_to_copy <= row_width);
|
|
|
|
/* Get to here when the row_width truncates the final copy.
|
|
* There will be 1-3 bytes left to copy, so don't try the
|
|
* 16-bit loop below.
|
|
*/
|
|
dp = (png_bytep)dp32;
|
|
sp = (png_const_bytep)sp32;
|
|
do
|
|
*dp++ = *sp++;
|
|
while (--row_width > 0);
|
|
return;
|
|
}
|
|
|
|
/* Else do it in 16-bit quantities, but only if the size is
|
|
* not too large.
|
|
*/
|
|
else
|
|
{
|
|
png_uint_16p dp16 = png_aligncast(png_uint_16p, dp);
|
|
png_const_uint_16p sp16 = png_aligncastconst(
|
|
png_const_uint_16p, sp);
|
|
size_t skip = (bytes_to_jump-bytes_to_copy) /
|
|
(sizeof (png_uint_16));
|
|
|
|
do
|
|
{
|
|
size_t c = bytes_to_copy;
|
|
do
|
|
{
|
|
*dp16++ = *sp16++;
|
|
c -= (sizeof (png_uint_16));
|
|
}
|
|
while (c > 0);
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
dp16 += skip;
|
|
sp16 += skip;
|
|
row_width -= bytes_to_jump;
|
|
}
|
|
while (bytes_to_copy <= row_width);
|
|
|
|
/* End of row - 1 byte left, bytes_to_copy > row_width: */
|
|
dp = (png_bytep)dp16;
|
|
sp = (png_const_bytep)sp16;
|
|
do
|
|
*dp++ = *sp++;
|
|
while (--row_width > 0);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* ALIGN_TYPE code */
|
|
|
|
/* The true default - use a memcpy: */
|
|
for (;;)
|
|
{
|
|
memcpy(dp, sp, bytes_to_copy);
|
|
|
|
if (row_width <= bytes_to_jump)
|
|
return;
|
|
|
|
sp += bytes_to_jump;
|
|
dp += bytes_to_jump;
|
|
row_width -= bytes_to_jump;
|
|
if (bytes_to_copy > row_width)
|
|
bytes_to_copy = (unsigned int)/*SAFE*/row_width;
|
|
}
|
|
}
|
|
|
|
/* NOT REACHED*/
|
|
} /* pixel_depth >= 8 */
|
|
|
|
/* Here if pixel_depth < 8 to check 'end_ptr' below. */
|
|
}
|
|
else
|
|
#endif /* READ_INTERLACING */
|
|
|
|
/* If here then the switch above wasn't used so just memcpy the whole row
|
|
* from the temporary row buffer (notice that this overwrites the end of the
|
|
* destination row if it is a partial byte.)
|
|
*/
|
|
memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));
|
|
|
|
/* Restore the overwritten bits from the last byte if necessary. */
|
|
if (end_ptr != NULL)
|
|
*end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask));
|
|
}
|
|
|
|
#ifdef PNG_READ_INTERLACING_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
|
|
png_uint_32 transformations /* Because these may affect the byte layout */)
|
|
{
|
|
/* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
|
|
/* Offset to next interlace block */
|
|
static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
|
|
|
|
png_debug(1, "in png_do_read_interlace");
|
|
if (row != NULL && row_info != NULL)
|
|
{
|
|
png_uint_32 final_width;
|
|
|
|
final_width = row_info->width * png_pass_inc[pass];
|
|
|
|
switch (row_info->pixel_depth)
|
|
{
|
|
case 1:
|
|
{
|
|
png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
|
|
png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
|
|
int sshift, dshift;
|
|
int s_start, s_end, s_inc;
|
|
int jstop = png_pass_inc[pass];
|
|
png_byte v;
|
|
png_uint_32 i;
|
|
int j;
|
|
|
|
#ifdef PNG_READ_PACKSWAP_SUPPORTED
|
|
if ((transformations & PNG_PACKSWAP) != 0)
|
|
{
|
|
sshift = (int)((row_info->width + 7) & 0x07);
|
|
dshift = (int)((final_width + 7) & 0x07);
|
|
s_start = 7;
|
|
s_end = 0;
|
|
s_inc = -1;
|
|
}
|
|
|
|
else
|
|
#endif
|
|
{
|
|
sshift = 7 - (int)((row_info->width + 7) & 0x07);
|
|
dshift = 7 - (int)((final_width + 7) & 0x07);
|
|
s_start = 0;
|
|
s_end = 7;
|
|
s_inc = 1;
|
|
}
|
|
|
|
for (i = 0; i < row_info->width; i++)
|
|
{
|
|
v = (png_byte)((*sp >> sshift) & 0x01);
|
|
for (j = 0; j < jstop; j++)
|
|
{
|
|
unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));
|
|
tmp |= v << dshift;
|
|
*dp = (png_byte)(tmp & 0xff);
|
|
|
|
if (dshift == s_end)
|
|
{
|
|
dshift = s_start;
|
|
dp--;
|
|
}
|
|
|
|
else
|
|
dshift += s_inc;
|
|
}
|
|
|
|
if (sshift == s_end)
|
|
{
|
|
sshift = s_start;
|
|
sp--;
|
|
}
|
|
|
|
else
|
|
sshift += s_inc;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
|
|
png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
|
|
int sshift, dshift;
|
|
int s_start, s_end, s_inc;
|
|
int jstop = png_pass_inc[pass];
|
|
png_uint_32 i;
|
|
|
|
#ifdef PNG_READ_PACKSWAP_SUPPORTED
|
|
if ((transformations & PNG_PACKSWAP) != 0)
|
|
{
|
|
sshift = (int)(((row_info->width + 3) & 0x03) << 1);
|
|
dshift = (int)(((final_width + 3) & 0x03) << 1);
|
|
s_start = 6;
|
|
s_end = 0;
|
|
s_inc = -2;
|
|
}
|
|
|
|
else
|
|
#endif
|
|
{
|
|
sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
|
|
dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
|
|
s_start = 0;
|
|
s_end = 6;
|
|
s_inc = 2;
|
|
}
|
|
|
|
for (i = 0; i < row_info->width; i++)
|
|
{
|
|
png_byte v;
|
|
int j;
|
|
|
|
v = (png_byte)((*sp >> sshift) & 0x03);
|
|
for (j = 0; j < jstop; j++)
|
|
{
|
|
unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));
|
|
tmp |= v << dshift;
|
|
*dp = (png_byte)(tmp & 0xff);
|
|
|
|
if (dshift == s_end)
|
|
{
|
|
dshift = s_start;
|
|
dp--;
|
|
}
|
|
|
|
else
|
|
dshift += s_inc;
|
|
}
|
|
|
|
if (sshift == s_end)
|
|
{
|
|
sshift = s_start;
|
|
sp--;
|
|
}
|
|
|
|
else
|
|
sshift += s_inc;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
|
|
png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
|
|
int sshift, dshift;
|
|
int s_start, s_end, s_inc;
|
|
png_uint_32 i;
|
|
int jstop = png_pass_inc[pass];
|
|
|
|
#ifdef PNG_READ_PACKSWAP_SUPPORTED
|
|
if ((transformations & PNG_PACKSWAP) != 0)
|
|
{
|
|
sshift = (int)(((row_info->width + 1) & 0x01) << 2);
|
|
dshift = (int)(((final_width + 1) & 0x01) << 2);
|
|
s_start = 4;
|
|
s_end = 0;
|
|
s_inc = -4;
|
|
}
|
|
|
|
else
|
|
#endif
|
|
{
|
|
sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
|
|
dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
|
|
s_start = 0;
|
|
s_end = 4;
|
|
s_inc = 4;
|
|
}
|
|
|
|
for (i = 0; i < row_info->width; i++)
|
|
{
|
|
png_byte v = (png_byte)((*sp >> sshift) & 0x0f);
|
|
int j;
|
|
|
|
for (j = 0; j < jstop; j++)
|
|
{
|
|
unsigned int tmp = *dp & (0xf0f >> (4 - dshift));
|
|
tmp |= v << dshift;
|
|
*dp = (png_byte)(tmp & 0xff);
|
|
|
|
if (dshift == s_end)
|
|
{
|
|
dshift = s_start;
|
|
dp--;
|
|
}
|
|
|
|
else
|
|
dshift += s_inc;
|
|
}
|
|
|
|
if (sshift == s_end)
|
|
{
|
|
sshift = s_start;
|
|
sp--;
|
|
}
|
|
|
|
else
|
|
sshift += s_inc;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
|
|
|
|
png_bytep sp = row + (png_size_t)(row_info->width - 1)
|
|
* pixel_bytes;
|
|
|
|
png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
|
|
|
|
int jstop = png_pass_inc[pass];
|
|
png_uint_32 i;
|
|
|
|
for (i = 0; i < row_info->width; i++)
|
|
{
|
|
png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */
|
|
int j;
|
|
|
|
memcpy(v, sp, pixel_bytes);
|
|
|
|
for (j = 0; j < jstop; j++)
|
|
{
|
|
memcpy(dp, v, pixel_bytes);
|
|
dp -= pixel_bytes;
|
|
}
|
|
|
|
sp -= pixel_bytes;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
row_info->width = final_width;
|
|
row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width);
|
|
}
|
|
#ifndef PNG_READ_PACKSWAP_SUPPORTED
|
|
PNG_UNUSED(transformations) /* Silence compiler warning */
|
|
#endif
|
|
}
|
|
#endif /* READ_INTERLACING */
|
|
|
|
static void
|
|
png_read_filter_row_sub(png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row)
|
|
{
|
|
png_size_t i;
|
|
png_size_t istop = row_info->rowbytes;
|
|
unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
|
|
png_bytep rp = row + bpp;
|
|
|
|
PNG_UNUSED(prev_row)
|
|
|
|
for (i = bpp; i < istop; i++)
|
|
{
|
|
*rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
|
|
rp++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_read_filter_row_up(png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row)
|
|
{
|
|
png_size_t i;
|
|
png_size_t istop = row_info->rowbytes;
|
|
png_bytep rp = row;
|
|
png_const_bytep pp = prev_row;
|
|
|
|
for (i = 0; i < istop; i++)
|
|
{
|
|
*rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
|
|
rp++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_read_filter_row_avg(png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row)
|
|
{
|
|
png_size_t i;
|
|
png_bytep rp = row;
|
|
png_const_bytep pp = prev_row;
|
|
unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
|
|
png_size_t istop = row_info->rowbytes - bpp;
|
|
|
|
for (i = 0; i < bpp; i++)
|
|
{
|
|
*rp = (png_byte)(((int)(*rp) +
|
|
((int)(*pp++) / 2 )) & 0xff);
|
|
|
|
rp++;
|
|
}
|
|
|
|
for (i = 0; i < istop; i++)
|
|
{
|
|
*rp = (png_byte)(((int)(*rp) +
|
|
(int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
|
|
|
|
rp++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row)
|
|
{
|
|
png_bytep rp_end = row + row_info->rowbytes;
|
|
int a, c;
|
|
|
|
/* First pixel/byte */
|
|
c = *prev_row++;
|
|
a = *row + c;
|
|
*row++ = (png_byte)a;
|
|
|
|
/* Remainder */
|
|
while (row < rp_end)
|
|
{
|
|
int b, pa, pb, pc, p;
|
|
|
|
a &= 0xff; /* From previous iteration or start */
|
|
b = *prev_row++;
|
|
|
|
p = b - c;
|
|
pc = a - c;
|
|
|
|
#ifdef PNG_USE_ABS
|
|
pa = abs(p);
|
|
pb = abs(pc);
|
|
pc = abs(p + pc);
|
|
#else
|
|
pa = p < 0 ? -p : p;
|
|
pb = pc < 0 ? -pc : pc;
|
|
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
|
|
#endif
|
|
|
|
/* Find the best predictor, the least of pa, pb, pc favoring the earlier
|
|
* ones in the case of a tie.
|
|
*/
|
|
if (pb < pa) pa = pb, a = b;
|
|
if (pc < pa) a = c;
|
|
|
|
/* Calculate the current pixel in a, and move the previous row pixel to c
|
|
* for the next time round the loop
|
|
*/
|
|
c = b;
|
|
a += *row;
|
|
*row++ = (png_byte)a;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row)
|
|
{
|
|
int bpp = (row_info->pixel_depth + 7) >> 3;
|
|
png_bytep rp_end = row + bpp;
|
|
|
|
/* Process the first pixel in the row completely (this is the same as 'up'
|
|
* because there is only one candidate predictor for the first row).
|
|
*/
|
|
while (row < rp_end)
|
|
{
|
|
int a = *row + *prev_row++;
|
|
*row++ = (png_byte)a;
|
|
}
|
|
|
|
/* Remainder */
|
|
rp_end += row_info->rowbytes - bpp;
|
|
|
|
while (row < rp_end)
|
|
{
|
|
int a, b, c, pa, pb, pc, p;
|
|
|
|
c = *(prev_row - bpp);
|
|
a = *(row - bpp);
|
|
b = *prev_row++;
|
|
|
|
p = b - c;
|
|
pc = a - c;
|
|
|
|
#ifdef PNG_USE_ABS
|
|
pa = abs(p);
|
|
pb = abs(pc);
|
|
pc = abs(p + pc);
|
|
#else
|
|
pa = p < 0 ? -p : p;
|
|
pb = pc < 0 ? -pc : pc;
|
|
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
|
|
#endif
|
|
|
|
if (pb < pa) pa = pb, a = b;
|
|
if (pc < pa) a = c;
|
|
|
|
a += *row;
|
|
*row++ = (png_byte)a;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_init_filter_functions(png_structrp pp)
|
|
/* This function is called once for every PNG image (except for PNG images
|
|
* that only use PNG_FILTER_VALUE_NONE for all rows) to set the
|
|
* implementations required to reverse the filtering of PNG rows. Reversing
|
|
* the filter is the first transformation performed on the row data. It is
|
|
* performed in place, therefore an implementation can be selected based on
|
|
* the image pixel format. If the implementation depends on image width then
|
|
* take care to ensure that it works correctly if the image is interlaced -
|
|
* interlacing causes the actual row width to vary.
|
|
*/
|
|
{
|
|
unsigned int bpp = (pp->pixel_depth + 7) >> 3;
|
|
|
|
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub;
|
|
pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up;
|
|
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg;
|
|
if (bpp == 1)
|
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
|
png_read_filter_row_paeth_1byte_pixel;
|
|
else
|
|
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
|
|
png_read_filter_row_paeth_multibyte_pixel;
|
|
|
|
#ifdef PNG_FILTER_OPTIMIZATIONS
|
|
/* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to
|
|
* call to install hardware optimizations for the above functions; simply
|
|
* replace whatever elements of the pp->read_filter[] array with a hardware
|
|
* specific (or, for that matter, generic) optimization.
|
|
*
|
|
* To see an example of this examine what configure.ac does when
|
|
* --enable-arm-neon is specified on the command line.
|
|
*/
|
|
PNG_FILTER_OPTIMIZATIONS(pp, bpp);
|
|
#endif
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row,
|
|
png_const_bytep prev_row, int filter)
|
|
{
|
|
/* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define
|
|
* PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic
|
|
* implementations. See png_init_filter_functions above.
|
|
*/
|
|
if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST)
|
|
{
|
|
if (pp->read_filter[0] == NULL)
|
|
png_init_filter_functions(pp);
|
|
|
|
pp->read_filter[filter-1](row_info, row, prev_row);
|
|
}
|
|
}
|
|
|
|
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
|
|
void /* PRIVATE */
|
|
png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
|
|
png_alloc_size_t avail_out)
|
|
{
|
|
/* Loop reading IDATs and decompressing the result into output[avail_out] */
|
|
png_ptr->zstream.next_out = output;
|
|
png_ptr->zstream.avail_out = 0; /* safety: set below */
|
|
|
|
if (output == NULL)
|
|
avail_out = 0;
|
|
|
|
do
|
|
{
|
|
int ret;
|
|
png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
|
|
|
|
if (png_ptr->zstream.avail_in == 0)
|
|
{
|
|
uInt avail_in;
|
|
png_bytep buffer;
|
|
|
|
while (png_ptr->idat_size == 0)
|
|
{
|
|
png_crc_finish(png_ptr, 0);
|
|
|
|
png_ptr->idat_size = png_read_chunk_header(png_ptr);
|
|
/* This is an error even in the 'check' case because the code just
|
|
* consumed a non-IDAT header.
|
|
*/
|
|
if (png_ptr->chunk_name != png_IDAT)
|
|
png_error(png_ptr, "Not enough image data");
|
|
}
|
|
|
|
avail_in = png_ptr->IDAT_read_size;
|
|
|
|
if (avail_in > png_ptr->idat_size)
|
|
avail_in = (uInt)png_ptr->idat_size;
|
|
|
|
/* A PNG with a gradually increasing IDAT size will defeat this attempt
|
|
* to minimize memory usage by causing lots of re-allocs, but
|
|
* realistically doing IDAT_read_size re-allocs is not likely to be a
|
|
* big problem.
|
|
*/
|
|
buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
|
|
|
|
png_crc_read(png_ptr, buffer, avail_in);
|
|
png_ptr->idat_size -= avail_in;
|
|
|
|
png_ptr->zstream.next_in = buffer;
|
|
png_ptr->zstream.avail_in = avail_in;
|
|
}
|
|
|
|
/* And set up the output side. */
|
|
if (output != NULL) /* standard read */
|
|
{
|
|
uInt out = ZLIB_IO_MAX;
|
|
|
|
if (out > avail_out)
|
|
out = (uInt)avail_out;
|
|
|
|
avail_out -= out;
|
|
png_ptr->zstream.avail_out = out;
|
|
}
|
|
|
|
else /* after last row, checking for end */
|
|
{
|
|
png_ptr->zstream.next_out = tmpbuf;
|
|
png_ptr->zstream.avail_out = (sizeof tmpbuf);
|
|
}
|
|
|
|
/* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the
|
|
* process. If the LZ stream is truncated the sequential reader will
|
|
* terminally damage the stream, above, by reading the chunk header of the
|
|
* following chunk (it then exits with png_error).
|
|
*
|
|
* TODO: deal more elegantly with truncated IDAT lists.
|
|
*/
|
|
ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
|
|
|
|
/* Take the unconsumed output back. */
|
|
if (output != NULL)
|
|
avail_out += png_ptr->zstream.avail_out;
|
|
|
|
else /* avail_out counts the extra bytes */
|
|
avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out;
|
|
|
|
png_ptr->zstream.avail_out = 0;
|
|
|
|
if (ret == Z_STREAM_END)
|
|
{
|
|
/* Do this for safety; we won't read any more into this row. */
|
|
png_ptr->zstream.next_out = NULL;
|
|
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
|
|
|
|
if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0)
|
|
png_chunk_benign_error(png_ptr, "Extra compressed data");
|
|
break;
|
|
}
|
|
|
|
if (ret != Z_OK)
|
|
{
|
|
png_zstream_error(png_ptr, ret);
|
|
|
|
if (output != NULL)
|
|
png_chunk_error(png_ptr, png_ptr->zstream.msg);
|
|
|
|
else /* checking */
|
|
{
|
|
png_chunk_benign_error(png_ptr, png_ptr->zstream.msg);
|
|
return;
|
|
}
|
|
}
|
|
} while (avail_out > 0);
|
|
|
|
if (avail_out > 0)
|
|
{
|
|
/* The stream ended before the image; this is the same as too few IDATs so
|
|
* should be handled the same way.
|
|
*/
|
|
if (output != NULL)
|
|
png_error(png_ptr, "Not enough image data");
|
|
|
|
else /* the deflate stream contained extra data */
|
|
png_chunk_benign_error(png_ptr, "Too much image data");
|
|
}
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_read_finish_IDAT(png_structrp png_ptr)
|
|
{
|
|
/* We don't need any more data and the stream should have ended, however the
|
|
* LZ end code may actually not have been processed. In this case we must
|
|
* read it otherwise stray unread IDAT data or, more likely, an IDAT chunk
|
|
* may still remain to be consumed.
|
|
*/
|
|
if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
|
|
{
|
|
/* The NULL causes png_read_IDAT_data to swallow any remaining bytes in
|
|
* the compressed stream, but the stream may be damaged too, so even after
|
|
* this call we may need to terminate the zstream ownership.
|
|
*/
|
|
png_read_IDAT_data(png_ptr, NULL, 0);
|
|
png_ptr->zstream.next_out = NULL; /* safety */
|
|
|
|
/* Now clear everything out for safety; the following may not have been
|
|
* done.
|
|
*/
|
|
if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
|
|
{
|
|
png_ptr->mode |= PNG_AFTER_IDAT;
|
|
png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
|
|
}
|
|
}
|
|
|
|
/* If the zstream has not been released do it now *and* terminate the reading
|
|
* of the final IDAT chunk.
|
|
*/
|
|
if (png_ptr->zowner == png_IDAT)
|
|
{
|
|
/* Always do this; the pointers otherwise point into the read buffer. */
|
|
png_ptr->zstream.next_in = NULL;
|
|
png_ptr->zstream.avail_in = 0;
|
|
|
|
/* Now we no longer own the zstream. */
|
|
png_ptr->zowner = 0;
|
|
|
|
/* The slightly weird semantics of the sequential IDAT reading is that we
|
|
* are always in or at the end of an IDAT chunk, so we always need to do a
|
|
* crc_finish here. If idat_size is non-zero we also need to read the
|
|
* spurious bytes at the end of the chunk now.
|
|
*/
|
|
(void)png_crc_finish(png_ptr, png_ptr->idat_size);
|
|
}
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_read_finish_row(png_structrp png_ptr)
|
|
{
|
|
/* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
|
|
|
|
/* Start of interlace block */
|
|
static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
|
|
|
|
/* Offset to next interlace block */
|
|
static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
|
|
|
|
/* Start of interlace block in the y direction */
|
|
static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
|
|
|
|
/* Offset to next interlace block in the y direction */
|
|
static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
|
|
|
|
png_debug(1, "in png_read_finish_row");
|
|
png_ptr->row_number++;
|
|
if (png_ptr->row_number < png_ptr->num_rows)
|
|
return;
|
|
|
|
if (png_ptr->interlaced != 0)
|
|
{
|
|
png_ptr->row_number = 0;
|
|
|
|
/* TO DO: don't do this if prev_row isn't needed (requires
|
|
* read-ahead of the next row's filter byte.
|
|
*/
|
|
memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
|
|
|
|
do
|
|
{
|
|
png_ptr->pass++;
|
|
|
|
if (png_ptr->pass >= 7)
|
|
break;
|
|
|
|
png_ptr->iwidth = (png_ptr->width +
|
|
png_pass_inc[png_ptr->pass] - 1 -
|
|
png_pass_start[png_ptr->pass]) /
|
|
png_pass_inc[png_ptr->pass];
|
|
|
|
if ((png_ptr->transformations & PNG_INTERLACE) == 0)
|
|
{
|
|
png_ptr->num_rows = (png_ptr->height +
|
|
png_pass_yinc[png_ptr->pass] - 1 -
|
|
png_pass_ystart[png_ptr->pass]) /
|
|
png_pass_yinc[png_ptr->pass];
|
|
}
|
|
|
|
else /* if (png_ptr->transformations & PNG_INTERLACE) */
|
|
break; /* libpng deinterlacing sees every row */
|
|
|
|
} while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
|
|
|
|
if (png_ptr->pass < 7)
|
|
return;
|
|
}
|
|
|
|
/* Here after at the end of the last row of the last pass. */
|
|
png_read_finish_IDAT(png_ptr);
|
|
}
|
|
#endif /* SEQUENTIAL_READ */
|
|
|
|
void /* PRIVATE */
|
|
png_read_start_row(png_structrp png_ptr)
|
|
{
|
|
/* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
|
|
|
|
/* Start of interlace block */
|
|
static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
|
|
|
|
/* Offset to next interlace block */
|
|
static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
|
|
|
|
/* Start of interlace block in the y direction */
|
|
static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
|
|
|
|
/* Offset to next interlace block in the y direction */
|
|
static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
|
|
|
|
int max_pixel_depth;
|
|
png_size_t row_bytes;
|
|
|
|
png_debug(1, "in png_read_start_row");
|
|
|
|
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
|
|
png_init_read_transformations(png_ptr);
|
|
#endif
|
|
if (png_ptr->interlaced != 0)
|
|
{
|
|
if ((png_ptr->transformations & PNG_INTERLACE) == 0)
|
|
png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
|
|
png_pass_ystart[0]) / png_pass_yinc[0];
|
|
|
|
else
|
|
png_ptr->num_rows = png_ptr->height;
|
|
|
|
png_ptr->iwidth = (png_ptr->width +
|
|
png_pass_inc[png_ptr->pass] - 1 -
|
|
png_pass_start[png_ptr->pass]) /
|
|
png_pass_inc[png_ptr->pass];
|
|
}
|
|
|
|
else
|
|
{
|
|
png_ptr->num_rows = png_ptr->height;
|
|
png_ptr->iwidth = png_ptr->width;
|
|
}
|
|
|
|
max_pixel_depth = png_ptr->pixel_depth;
|
|
|
|
/* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of
|
|
* calculations to calculate the final pixel depth, then
|
|
* png_do_read_transforms actually does the transforms. This means that the
|
|
* code which effectively calculates this value is actually repeated in three
|
|
* separate places. They must all match. Innocent changes to the order of
|
|
* transformations can and will break libpng in a way that causes memory
|
|
* overwrites.
|
|
*
|
|
* TODO: fix this.
|
|
*/
|
|
#ifdef PNG_READ_PACK_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_PACK) != 0 && png_ptr->bit_depth < 8)
|
|
max_pixel_depth = 8;
|
|
#endif
|
|
|
|
#ifdef PNG_READ_EXPAND_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_EXPAND) != 0)
|
|
{
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
if (png_ptr->num_trans != 0)
|
|
max_pixel_depth = 32;
|
|
|
|
else
|
|
max_pixel_depth = 24;
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
|
|
{
|
|
if (max_pixel_depth < 8)
|
|
max_pixel_depth = 8;
|
|
|
|
if (png_ptr->num_trans != 0)
|
|
max_pixel_depth *= 2;
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
|
|
{
|
|
if (png_ptr->num_trans != 0)
|
|
{
|
|
max_pixel_depth *= 4;
|
|
max_pixel_depth /= 3;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_EXPAND_16_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_EXPAND_16) != 0)
|
|
{
|
|
# ifdef PNG_READ_EXPAND_SUPPORTED
|
|
/* In fact it is an error if it isn't supported, but checking is
|
|
* the safe way.
|
|
*/
|
|
if ((png_ptr->transformations & PNG_EXPAND) != 0)
|
|
{
|
|
if (png_ptr->bit_depth < 16)
|
|
max_pixel_depth *= 2;
|
|
}
|
|
else
|
|
# endif
|
|
png_ptr->transformations &= ~PNG_EXPAND_16;
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_FILLER_SUPPORTED
|
|
if ((png_ptr->transformations & (PNG_FILLER)) != 0)
|
|
{
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
|
|
{
|
|
if (max_pixel_depth <= 8)
|
|
max_pixel_depth = 16;
|
|
|
|
else
|
|
max_pixel_depth = 32;
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB ||
|
|
png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
if (max_pixel_depth <= 32)
|
|
max_pixel_depth = 32;
|
|
|
|
else
|
|
max_pixel_depth = 64;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
|
|
if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
|
|
{
|
|
if (
|
|
#ifdef PNG_READ_EXPAND_SUPPORTED
|
|
(png_ptr->num_trans != 0 &&
|
|
(png_ptr->transformations & PNG_EXPAND) != 0) ||
|
|
#endif
|
|
#ifdef PNG_READ_FILLER_SUPPORTED
|
|
(png_ptr->transformations & (PNG_FILLER)) != 0 ||
|
|
#endif
|
|
png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
{
|
|
if (max_pixel_depth <= 16)
|
|
max_pixel_depth = 32;
|
|
|
|
else
|
|
max_pixel_depth = 64;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (max_pixel_depth <= 8)
|
|
{
|
|
if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
|
max_pixel_depth = 32;
|
|
|
|
else
|
|
max_pixel_depth = 24;
|
|
}
|
|
|
|
else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
|
max_pixel_depth = 64;
|
|
|
|
else
|
|
max_pixel_depth = 48;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
|
|
defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
|
|
if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
|
|
{
|
|
int user_pixel_depth = png_ptr->user_transform_depth *
|
|
png_ptr->user_transform_channels;
|
|
|
|
if (user_pixel_depth > max_pixel_depth)
|
|
max_pixel_depth = user_pixel_depth;
|
|
}
|
|
#endif
|
|
|
|
/* This value is stored in png_struct and double checked in the row read
|
|
* code.
|
|
*/
|
|
png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth;
|
|
png_ptr->transformed_pixel_depth = 0; /* calculated on demand */
|
|
|
|
/* Align the width on the next larger 8 pixels. Mainly used
|
|
* for interlacing
|
|
*/
|
|
row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
|
|
/* Calculate the maximum bytes needed, adding a byte and a pixel
|
|
* for safety's sake
|
|
*/
|
|
row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
|
|
1 + ((max_pixel_depth + 7) >> 3);
|
|
|
|
#ifdef PNG_MAX_MALLOC_64K
|
|
if (row_bytes > (png_uint_32)65536L)
|
|
png_error(png_ptr, "This image requires a row greater than 64KB");
|
|
#endif
|
|
|
|
if (row_bytes + 48 > png_ptr->old_big_row_buf_size)
|
|
{
|
|
png_free(png_ptr, png_ptr->big_row_buf);
|
|
png_free(png_ptr, png_ptr->big_prev_row);
|
|
|
|
if (png_ptr->interlaced != 0)
|
|
png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
|
|
row_bytes + 48);
|
|
|
|
else
|
|
png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
|
|
|
|
png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
|
|
|
|
#ifdef PNG_ALIGNED_MEMORY_SUPPORTED
|
|
/* Use 16-byte aligned memory for row_buf with at least 16 bytes
|
|
* of padding before and after row_buf; treat prev_row similarly.
|
|
* NOTE: the alignment is to the start of the pixels, one beyond the start
|
|
* of the buffer, because of the filter byte. Prior to libpng 1.5.6 this
|
|
* was incorrect; the filter byte was aligned, which had the exact
|
|
* opposite effect of that intended.
|
|
*/
|
|
{
|
|
png_bytep temp = png_ptr->big_row_buf + 32;
|
|
int extra = (int)((temp - (png_bytep)0) & 0x0f);
|
|
png_ptr->row_buf = temp - extra - 1/*filter byte*/;
|
|
|
|
temp = png_ptr->big_prev_row + 32;
|
|
extra = (int)((temp - (png_bytep)0) & 0x0f);
|
|
png_ptr->prev_row = temp - extra - 1/*filter byte*/;
|
|
}
|
|
|
|
#else
|
|
/* Use 31 bytes of padding before and 17 bytes after row_buf. */
|
|
png_ptr->row_buf = png_ptr->big_row_buf + 31;
|
|
png_ptr->prev_row = png_ptr->big_prev_row + 31;
|
|
#endif
|
|
png_ptr->old_big_row_buf_size = row_bytes + 48;
|
|
}
|
|
|
|
#ifdef PNG_MAX_MALLOC_64K
|
|
if (png_ptr->rowbytes > 65535)
|
|
png_error(png_ptr, "This image requires a row greater than 64KB");
|
|
|
|
#endif
|
|
if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))
|
|
png_error(png_ptr, "Row has too many bytes to allocate in memory");
|
|
|
|
memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
|
|
|
|
png_debug1(3, "width = %u,", png_ptr->width);
|
|
png_debug1(3, "height = %u,", png_ptr->height);
|
|
png_debug1(3, "iwidth = %u,", png_ptr->iwidth);
|
|
png_debug1(3, "num_rows = %u,", png_ptr->num_rows);
|
|
png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes);
|
|
png_debug1(3, "irowbytes = %lu",
|
|
(unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1);
|
|
|
|
/* The sequential reader needs a buffer for IDAT, but the progressive reader
|
|
* does not, so free the read buffer now regardless; the sequential reader
|
|
* reallocates it on demand.
|
|
*/
|
|
if (png_ptr->read_buffer != 0)
|
|
{
|
|
png_bytep buffer = png_ptr->read_buffer;
|
|
|
|
png_ptr->read_buffer_size = 0;
|
|
png_ptr->read_buffer = NULL;
|
|
png_free(png_ptr, buffer);
|
|
}
|
|
|
|
/* Finally claim the zstream for the inflate of the IDAT data, use the bits
|
|
* value from the stream (note that this will result in a fatal error if the
|
|
* IDAT stream has a bogus deflate header window_bits value, but this should
|
|
* not be happening any longer!)
|
|
*/
|
|
if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK)
|
|
png_error(png_ptr, png_ptr->zstream.msg);
|
|
|
|
png_ptr->flags |= PNG_FLAG_ROW_INIT;
|
|
}
|
|
#endif /* READ */
|