diff --git a/UWP/libzstd_UWP/libzstd_UWP.vcxproj b/UWP/libzstd_UWP/libzstd_UWP.vcxproj
index 46cc2b2b25..78101ed883 100644
--- a/UWP/libzstd_UWP/libzstd_UWP.vcxproj
+++ b/UWP/libzstd_UWP/libzstd_UWP.vcxproj
@@ -252,7 +252,7 @@
-
+
diff --git a/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters b/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters
index 4b04ed6865..1a22172909 100644
--- a/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters
+++ b/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters
@@ -42,7 +42,7 @@
-
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index ed2b06040f..d699a76b80 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -100,6 +100,8 @@ ARCH_FILES := \
$(SRC)/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp \
$(SRC)/Core/Util/DisArm64.cpp \
$(SRC)/GPU/Common/VertexDecoderArm64.cpp \
+ $(SRC)/ext/libpng17/arm/arm_init.c \
+ $(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \
Arm64EmitterTest.cpp
endif
@@ -596,7 +598,6 @@ LIBZSTD_FILES := \
$(SRC)/ext/zstd/lib/common/threading.c \
$(SRC)/ext/zstd/lib/common/xxhash.c \
$(SRC)/ext/zstd/lib/common/zstd_common.c \
- $(SRC)/ext/zstd/lib/common/zstd_trace.c \
$(SRC)/ext/zstd/lib/compress/fse_compress.c \
$(SRC)/ext/zstd/lib/compress/hist.c \
$(SRC)/ext/zstd/lib/compress/huf_compress.c \
diff --git a/ext/libpng17/arm/arm_init.c b/ext/libpng17/arm/arm_init.c
index a672eaebb5..79702c9077 100644
--- a/ext/libpng17/arm/arm_init.c
+++ b/ext/libpng17/arm/arm_init.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2014 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
- * Last changed in libpng 1.6.10 [(PENDING RELEASE)]
+ * Last changed in libpng 1.6.16 [December 22, 2014]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -15,8 +15,10 @@
#define _POSIX_SOURCE 1
#include "../pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_arm_arm_init
#ifdef PNG_READ_SUPPORTED
+
#if PNG_ARM_NEON_OPT > 0
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */
/* WARNING: it is strongly recommended that you do not build libpng with
@@ -130,4 +132,4 @@ png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
}
}
#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* PNG_READ_SUPPORTED */
+#endif /* READ */
diff --git a/ext/libpng17/arm/filter_neon.S b/ext/libpng17/arm/filter_neon.S
index 40d97775d6..b7c0a90b43 100644
--- a/ext/libpng17/arm/filter_neon.S
+++ b/ext/libpng17/arm/filter_neon.S
@@ -1,18 +1,17 @@
/* filter_neon.S - NEON optimised filter functions
*
- * Copyright (c) 2013 Glenn Randers-Pehrson
+ * Copyright (c) 2014 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
- * Last changed in libpng 1.6.8 [December 19, 2013]
+ * Last changed in libpng 1.6.16 [(PENDING RELEASE)]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
-/* This is required to get the symbol renames, which are #defines, and also
- * includes the definition (or not) of PNG_ARM_NEON_OPT and
- * PNG_ARM_NEON_IMPLEMENTATION.
+/* This is required to get the symbol renames, which are #defines, and the
+ * definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION.
*/
#define PNG_VERSION_INFO_ONLY
#include "../pngpriv.h"
@@ -21,6 +20,8 @@
.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
#endif
+#ifdef PNG_READ_SUPPORTED
+
/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for
* ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it
* only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h
@@ -28,7 +29,6 @@
*/
#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */
-#ifdef PNG_READ_SUPPORTED
#if PNG_ARM_NEON_OPT > 0
#ifdef __ELF__
@@ -47,6 +47,13 @@ ELF .size \name, . - \name
.purgem endfunc
.endm
.text
+
+ /* Explicitly specifying alignment here because some versions of
+ * GAS don't align code correctly. This is harmless in correctly
+ * written versions of GAS.
+ */
+ .align 2
+
.if \export
.global \name
.endif
@@ -242,5 +249,5 @@ func png_read_filter_row_paeth3_neon, export=1
pop {r4,pc}
endfunc
#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* PNG_READ_SUPPORTED */
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */
+#endif /* READ */
diff --git a/ext/libpng17/arm/filter_neon_intrinsics.c b/ext/libpng17/arm/filter_neon_intrinsics.c
index 09c2516a60..2788dd3391 100644
--- a/ext/libpng17/arm/filter_neon_intrinsics.c
+++ b/ext/libpng17/arm/filter_neon_intrinsics.c
@@ -1,11 +1,11 @@
/* filter_neon_intrinsics.c - NEON optimised filter functions
*
- * Copyright (c) 2013 Glenn Randers-Pehrson
+ * Copyright (c) 2014 Glenn Randers-Pehrson
* Written by James Yu , October 2013.
* Based on filter_neon.S, written by Mans Rullgard, 2011.
*
- * Last changed in libpng 1.6.8 [December 19, 2013]
+ * Last changed in libpng 1.6.16 [December 22, 2014]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -13,6 +13,9 @@
*/
#include "../pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_arm_filter_neon_intrinsics
+
+#ifdef PNG_READ_SUPPORTED
/* This code requires -mfpu=neon on the command line: */
#if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
@@ -39,15 +42,14 @@
#define png_ldr(type,pointer)\
(temp_pointer = png_ptr(type,pointer), *temp_pointer)
-#ifdef PNG_READ_SUPPORTED
#if PNG_ARM_NEON_OPT > 0
void
-png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_up_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
png_const_bytep pp = prev_row;
for (; rp < rp_stop; rp += 16, pp += 16)
@@ -59,14 +61,15 @@ png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row,
qrp = vaddq_u8(qrp, qpp);
vst1q_u8(rp, qrp);
}
+
+ PNG_UNUSED(bpp)
}
void
-png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_sub3_neon(png_alloc_size_t rowbytes, unsigned int bpp, png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
uint8x16_t vtmp = vld1q_u8(rp);
uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);
@@ -103,15 +106,16 @@ png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row,
rp += 3;
}
+ PNG_UNUSED(bpp)
PNG_UNUSED(prev_row)
}
void
-png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_sub4_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
@@ -130,16 +134,17 @@ png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row,
vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
}
+ PNG_UNUSED(bpp)
PNG_UNUSED(prev_row)
}
void
-png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_avg3_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
png_const_bytep pp = prev_row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
@@ -197,14 +202,16 @@ png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row,
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
+
+ PNG_UNUSED(bpp)
}
void
-png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_avg4_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
png_const_bytep pp = prev_row;
uint8x8x4_t vdest;
@@ -235,6 +242,8 @@ png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row,
vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
}
+
+ PNG_UNUSED(bpp)
}
static uint8x8_t
@@ -265,12 +274,12 @@ paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c)
}
void
-png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_paeth3_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
png_const_bytep pp = prev_row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
@@ -328,14 +337,16 @@ png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row,
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
+
+ PNG_UNUSED(bpp)
}
void
-png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_paeth4_neon(png_alloc_size_t rowbytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row)
{
png_bytep rp = row;
- png_bytep rp_stop = row + row_info->rowbytes;
+ png_bytep rp_stop = row + rowbytes;
png_const_bytep pp = prev_row;
uint8x8_t vlast = vdup_n_u8(0);
@@ -369,8 +380,9 @@ png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row,
vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0);
}
-}
+ PNG_UNUSED(bpp)
+}
#endif /* PNG_ARM_NEON_OPT > 0 */
-#endif /* PNG_READ_SUPPORTED */
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */
+#endif /* READ */
diff --git a/ext/libpng17/arm/neon.h b/ext/libpng17/arm/neon.h
new file mode 100644
index 0000000000..2485cd9bf1
--- /dev/null
+++ b/ext/libpng17/arm/neon.h
@@ -0,0 +1,124 @@
+/* Compile time options.
+ * =====================
+ * In a multi-arch build the compiler may compile the code several times for the
+ * same object module, producing different binaries for different architectures.
+ * When this happens configure-time setting of the target host options cannot be
+ * done. An example is iOS vs Android compilation of ARM NEON support.
+ *
+ * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because
+ * this is not possible with certain compilers (Oracle SUN OS CC), as a result
+ * it is necessary to ensure that all extern functions that *might* be used
+ * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON
+ * below is one example of this behavior because it is controlled by the
+ * presence or not of -mfpu=neon on the GCC command line, it is possible to do
+ * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely
+ * do this.
+ */
+#ifndef PNG_ARM_NEON_OPT
+ /* ARM NEON optimizations are being controlled by the compiler settings,
+ * typically the target FPU. If the FPU supports NEON instructions then the
+ * compiler will define __ARM_NEON and we can rely unconditionally on NEON
+ * instructions not crashing, otherwise we must disable use of NEON
+ * instructions.
+ *
+ * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they
+ * can only be turned on automatically if that is supported too. If
+ * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail
+ * to compile with an appropriate #error if ALIGNED_MEMORY has been turned
+ * off.
+ *
+ * Note that older versions of GCC defined __ARM_NEON__; this is no longer
+ * supported. Also 32-bit ARM versions of GCC required the NEON FPU mode to
+ * be turned on explicitly on the command line. If this is not done (on
+ * 32-bit ARM) NEON code will not be included.
+ *
+ * To disable ARM_NEON optimizations entirely, and skip compiling the
+ * associated assembler code, pass --enable-arm-neon=no to configure
+ * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS.
+ */
+# if defined(__ARM_NEON) && defined(PNG_ALIGNED_MEMORY_SUPPORTED)
+# define PNG_ARM_NEON_OPT 2
+# else
+# define PNG_ARM_NEON_OPT 0
+# endif
+#endif
+
+#if PNG_ARM_NEON_OPT > 0
+ /* NEON optimizations are to be at least considered by libpng, so enable the
+ * callbacks to do this.
+ */
+# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon
+
+ /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used
+ * if possible - if __ARM_NEON is set and the compiler version is not known
+ * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can
+ * be:
+ *
+ * 1 The intrinsics code (the default with __ARM_NEON)
+ * 2 The hand coded assembler (the default without __ARM_NEON)
+ *
+ * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however
+ * this is *NOT* supported and may cease to work even after a minor revision
+ * to libpng. It *is* valid to do this for testing purposes, e.g. speed
+ * testing or a new compiler, but the results should be communicated to the
+ * libpng implementation list for incorporation in the next minor release.
+ */
+# ifndef PNG_ARM_NEON_IMPLEMENTATION
+# ifdef __ARM_NEON
+# if defined(__clang__)
+ /* At present it is unknown by the libpng developers which versions
+ * of clang support the intrinsics, however some or perhaps all
+ * versions do not work with the assembler so this may be
+ * irrelevant, so just use the default (do nothing here.)
+ */
+# elif defined(__GNUC__)
+ /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to
+ * work, so if this *is* GCC, or G++, look for a version >4.5
+ */
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
+# define PNG_ARM_NEON_IMPLEMENTATION 2
+# endif /* no GNUC support */
+# endif /* __GNUC__ */
+# else /* !defined __ARM_NEON */
+ /* The 'intrinsics' code simply won't compile without compiler support
+ * and that support switches on __ARM_NEON, so use the assembler:
+ */
+# define PNG_ARM_NEON_IMPLEMENTATION 2
+# endif /* __ARM_NEON */
+# endif /* !defined PNG_ARM_NEON_IMPLEMENTATION */
+
+# ifndef PNG_ARM_NEON_IMPLEMENTATION
+ /* Use the intrinsics code by default. */
+# define PNG_ARM_NEON_IMPLEMENTATION 1
+# endif
+#endif /* PNG_ARM_NEON_OPT > 0 */
+
+#ifdef PNG_SET_OPTION_SUPPORTED
+#ifdef PNG_ARM_NEON_API_SUPPORTED
+# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */
+#endif
+#endif /* SET_OPTION */
+
+#if PNG_ARM_NEON_OPT > 0
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,
+ (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row,
+ png_const_bytep prev_row),PNG_EMPTY);
+#endif
diff --git a/ext/libpng17/png.c b/ext/libpng17/png.c
index 5ce74f8417..edf915a3fc 100644
--- a/ext/libpng17/png.c
+++ b/ext/libpng17/png.c
@@ -2,7 +2,7 @@
/* png.c - location for general purpose libpng functions
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2017 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.)
*
@@ -12,9 +12,10 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_png
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_7_0beta35 Your_png_h_is_not_version_1_7_0beta35;
+typedef png_libpng_version_1_7_0beta90 Your_png_h_is_not_version_1_7_0beta90;
/* Tells libpng that we have already handled the first "num_bytes" bytes
* of the PNG file signature. If the PNG data is embedded into another
@@ -34,7 +35,7 @@ png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
if (num_bytes > 8)
png_error(png_ptr, "Too many bytes for PNG signature");
- png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);
+ png_ptr->sig_bytes = png_check_byte(png_ptr, num_bytes < 0 ? 0 : num_bytes);
}
/* Checks whether the supplied bytes match the PNG signature. We allow
@@ -65,7 +66,7 @@ png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));
}
-#endif /* PNG_READ_SUPPORTED */
+#endif /* READ */
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
/* Function to allocate memory for zlib */
@@ -79,8 +80,8 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
if (items >= (~(png_alloc_size_t)0)/size)
{
- png_warning (png_voidcast(png_structrp, png_ptr),
- "Potential overflow in png_zalloc()");
+ png_warning(png_voidcast(png_structrp, png_ptr),
+ "Potential overflow in png_zalloc()");
return NULL;
}
@@ -99,10 +100,26 @@ png_zfree(voidpf png_ptr, voidpf ptr)
* in case CRC is > 32 bits to leave the top bits 0.
*/
void /* PRIVATE */
-png_reset_crc(png_structrp png_ptr)
+png_reset_crc(png_structrp png_ptr, png_const_bytep chunk_tag)
{
- /* The cast is safe because the crc is a 32 bit value. */
- png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0);
+# ifdef PNG_READ_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ /* Set png_struct::current_crc appropriately. */
+ if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))
+ png_ptr->current_crc = png_ptr->ancillary_crc;
+
+ else /* critical */
+ png_ptr->current_crc = png_ptr->critical_crc;
+ }
+
+ else
+ png_ptr->current_crc = crc_error_quit; /* for write */
+
+ /* Now do not calculate the CRC if it isn't required: */
+ if (png_ptr->current_crc != crc_quiet_use)
+# endif /* READ */
+ png_ptr->crc = 0xFFFFFFFFU & crc32(0, chunk_tag, 4);
}
/* Calculate the CRC over a section of data. We can only pass as
@@ -111,111 +128,100 @@ png_reset_crc(png_structrp png_ptr)
* trouble of calculating it.
*/
void /* PRIVATE */
-png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length)
+png_calculate_crc(png_structrp png_ptr, png_const_voidp ptr, png_size_t length)
{
- int need_crc = 1;
-
- if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))
- {
- 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)
- need_crc = 0;
- }
-
/* 'uLong' is defined in zlib.h as unsigned long; this means that on some
* systems it is a 64 bit value. crc32, however, returns 32 bits so the
* following cast is safe. 'uInt' may be no more than 16 bits, so it is
* necessary to perform a loop here.
*/
- if (need_crc && length > 0)
+# ifdef PNG_READ_SUPPORTED
+ if (png_ptr->current_crc != crc_quiet_use)
+# endif /* READ */
+ if (length > 0)
{
uLong crc = png_ptr->crc; /* Should never issue a warning */
+ const Bytef* rptr = png_voidcast(const Bytef*,ptr);
do
{
- uInt safe_length = (uInt)length;
- if (safe_length == 0)
- safe_length = (uInt)-1; /* evil, but safe */
+ uInt safe_length;
- crc = crc32(crc, ptr, safe_length);
+ /* TODO: this uses ZLIB_IO_MAX which may be #defined to less than the
+ * maximum of a uInt, is this the best thing to do?
+ */
+ if (length > ZLIB_IO_MAX)
+ safe_length = ZLIB_IO_MAX;
+
+ else
+ safe_length = (uInt)/*SAFE*/length;
+
+ crc = crc32(crc, PNGZ_INPUT_CAST(rptr), safe_length);
/* The following should never issue compiler warnings; if they do the
* target system has characteristics that will probably violate other
* assumptions within the libpng code.
*/
- ptr += safe_length;
+ rptr += safe_length;
length -= safe_length;
}
while (length > 0);
/* And the following is always safe because the crc is only 32 bits. */
- png_ptr->crc = (png_uint_32)crc;
+ png_ptr->crc = 0xFFFFFFFFU & crc;
}
}
/* Check a user supplied version number, called from both read and write
* functions that create a png_struct.
*/
-int
+static int
png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
{
+ /* Libpng versions 1.0.0 and later are binary compatible if the version
+ * string matches through the second '.'; we must recompile any applications
+ * that use any older library version.
+ */
if (user_png_ver != NULL)
{
- int i = 0;
+ int library_match = 1;
+ int i = -1;
+ unsigned int found_dots = 0;
do
{
- if (user_png_ver[i] != png_libpng_ver[i])
- png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
- } while (png_libpng_ver[i++]);
+ i++;
+ if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i])
+ library_match = 0;
+ if (user_png_ver[i] == '.')
+ found_dots++;
+ } while (library_match && found_dots < 2 && user_png_ver[i] != 0 &&
+ PNG_LIBPNG_VER_STRING[i] != 0);
+
+ if (library_match)
+ return 1; /* Library matches ok */
}
- else
- png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-
- if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
- {
- /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
- * we must recompile any applications that use any older library version.
- * For versions after libpng 1.0, we will be compatible, so we need
- * only check the first and third digits (note that when we reach version
- * 1.10 we will need to check the fourth symbol, namely user_png_ver[3]).
- */
- if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
- (user_png_ver[0] == '1' && (user_png_ver[2] != png_libpng_ver[2] ||
- user_png_ver[3] != png_libpng_ver[3])) ||
- (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
- {
+ /* Failure: mismatched library major version number */
#ifdef PNG_WARNINGS_SUPPORTED
- size_t pos = 0;
- char m[128];
+ {
+ size_t pos = 0;
+ char m[128];
- pos = png_safecat(m, (sizeof m), pos,
- "Application built with libpng-");
- pos = png_safecat(m, (sizeof m), pos, user_png_ver);
- pos = png_safecat(m, (sizeof m), pos, " but running with ");
- pos = png_safecat(m, (sizeof m), pos, png_libpng_ver);
- PNG_UNUSED(pos)
+ pos = png_safecat(m, (sizeof m), pos,
+ "Application built with libpng-");
+ /* This is ok if user_png_ver is NULL, it appends nothing: */
+ pos = png_safecat(m, (sizeof m), pos, user_png_ver);
+ pos = png_safecat(m, (sizeof m), pos, " but running with ");
+ pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING);
+ PNG_UNUSED(pos)
- png_warning(png_ptr, m);
-#endif
-
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- png_ptr->flags = 0;
-#endif
-
- return 0;
- }
+ png_app_warning(png_ptr, m);
}
+#endif
- /* Success return. */
- return 1;
+ return 0; /* Failure */
+ PNG_UNUSED(png_ptr) /* if no warning */
}
/* Generic function to create a png_struct for either read or write - this
@@ -231,6 +237,19 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
jmp_buf create_jmp_buf;
# endif
+ /* This is a compile-type only test to ensure that the build satisifies the
+ * contraints for the row buffer stack allocations. A 'duplicate case
+ * statements' style of error means that one of the tests below failed:
+ */
+ switch (0)
+ {
+ case 0:
+ case PNG_ROW_BUFFER_SIZE >= PNG_MIN_ROW_BUFFER_SIZE: /*1*/
+ case 2*(PNG_ROW_BUFFER_SIZE <= PNG_MAX_ROW_BUFFER_SIZE): /*2*/
+ default:
+ break;
+ }
+
/* This temporary stack-allocated structure is used to provide a place to
* build enough context to allow the user provided memory allocator (if any)
* to be called.
@@ -274,7 +293,9 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
# ifdef PNG_SETJMP_SUPPORTED
if (!setjmp(create_jmp_buf))
+# endif
{
+# ifdef PNG_SETJMP_SUPPORTED
/* Temporarily fake out the longjmp information until we have
* successfully completed this function. This only works if we have
* setjmp() support compiled in, but it is safe - this stuff should
@@ -283,25 +304,16 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
create_struct.jmp_buf_ptr = &create_jmp_buf;
create_struct.jmp_buf_size = 0; /*stack allocation*/
create_struct.longjmp_fn = longjmp;
-# else
- {
# endif
/* Call the general version checker (shared with read and write code):
*/
- if (png_user_version_check(&create_struct, user_png_ver))
+ if (png_user_version_check(&create_struct, user_png_ver) != 0)
{
png_structrp png_ptr = png_voidcast(png_structrp,
- png_malloc_warn(&create_struct, (sizeof *png_ptr)));
+ png_malloc_warn(&create_struct, (sizeof *png_ptr)));
if (png_ptr != NULL)
{
- /* png_ptr->zstream holds a back-pointer to the png_struct, so
- * this can only be done now:
- */
- create_struct.zstream.zalloc = png_zalloc;
- create_struct.zstream.zfree = png_zfree;
- create_struct.zstream.opaque = png_ptr;
-
# ifdef PNG_SETJMP_SUPPORTED
/* Eliminate the local error handling: */
create_struct.jmp_buf_ptr = NULL;
@@ -340,7 +352,7 @@ png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
* has always been done in 'example.c'.
*/
info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
- (sizeof *info_ptr)));
+ (sizeof *info_ptr)));
if (info_ptr != NULL)
memset(info_ptr, 0, (sizeof *info_ptr));
@@ -387,7 +399,7 @@ png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)
void PNGAPI
png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
- int num)
+ int num)
{
png_debug(1, "in png_free_data");
@@ -396,34 +408,35 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
#ifdef PNG_TEXT_SUPPORTED
/* Free text item num or (if num == -1) all text items */
- if ((mask & PNG_FREE_TEXT) & info_ptr->free_me)
+ if (info_ptr->text != 0 &&
+ ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0)
{
if (num != -1)
{
- if (info_ptr->text && info_ptr->text[num].key)
- {
- png_free(png_ptr, info_ptr->text[num].key);
- info_ptr->text[num].key = NULL;
- }
+ png_free(png_ptr, info_ptr->text[num].key);
+ info_ptr->text[num].key = NULL;
}
else
{
int i;
+
for (i = 0; i < info_ptr->num_text; i++)
- png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i);
+ png_free(png_ptr, info_ptr->text[i].key);
+
png_free(png_ptr, info_ptr->text);
info_ptr->text = NULL;
- info_ptr->num_text=0;
+ info_ptr->num_text = 0;
+ info_ptr->max_text = 0;
}
}
#endif
#ifdef PNG_tRNS_SUPPORTED
/* Free any tRNS entry */
- if ((mask & PNG_FREE_TRNS) & info_ptr->free_me)
+ if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0)
{
- info_ptr->valid &= ~PNG_INFO_tRNS;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
png_free(png_ptr, info_ptr->trans_alpha);
info_ptr->trans_alpha = NULL;
info_ptr->num_trans = 0;
@@ -432,156 +445,148 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
#ifdef PNG_sCAL_SUPPORTED
/* Free any sCAL entry */
- if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)
+ if (((mask & PNG_FREE_SCAL) & info_ptr->free_me) != 0)
{
png_free(png_ptr, info_ptr->scal_s_width);
png_free(png_ptr, info_ptr->scal_s_height);
info_ptr->scal_s_width = NULL;
info_ptr->scal_s_height = NULL;
- info_ptr->valid &= ~PNG_INFO_sCAL;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sCAL);
}
#endif
#ifdef PNG_pCAL_SUPPORTED
/* Free any pCAL entry */
- if ((mask & PNG_FREE_PCAL) & info_ptr->free_me)
+ if (((mask & PNG_FREE_PCAL) & info_ptr->free_me) != 0)
{
png_free(png_ptr, info_ptr->pcal_purpose);
png_free(png_ptr, info_ptr->pcal_units);
info_ptr->pcal_purpose = NULL;
info_ptr->pcal_units = NULL;
+
if (info_ptr->pcal_params != NULL)
{
- unsigned int i;
+ int i;
+
for (i = 0; i < info_ptr->pcal_nparams; i++)
- {
png_free(png_ptr, info_ptr->pcal_params[i]);
- info_ptr->pcal_params[i] = NULL;
- }
+
png_free(png_ptr, info_ptr->pcal_params);
info_ptr->pcal_params = NULL;
}
- info_ptr->valid &= ~PNG_INFO_pCAL;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_pCAL);
}
#endif
#ifdef PNG_iCCP_SUPPORTED
/* Free any profile entry */
- if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)
+ if (((mask & PNG_FREE_ICCP) & info_ptr->free_me) != 0)
{
png_free(png_ptr, info_ptr->iccp_name);
png_free(png_ptr, info_ptr->iccp_profile);
info_ptr->iccp_name = NULL;
info_ptr->iccp_profile = NULL;
- info_ptr->valid &= ~PNG_INFO_iCCP;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_iCCP);
}
#endif
#ifdef PNG_sPLT_SUPPORTED
/* Free a given sPLT entry, or (if num == -1) all sPLT entries */
- if ((mask & PNG_FREE_SPLT) & info_ptr->free_me)
+ if (info_ptr->splt_palettes != 0 &&
+ ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0)
{
if (num != -1)
{
- if (info_ptr->splt_palettes)
- {
- png_free(png_ptr, info_ptr->splt_palettes[num].name);
- png_free(png_ptr, info_ptr->splt_palettes[num].entries);
- info_ptr->splt_palettes[num].name = NULL;
- info_ptr->splt_palettes[num].entries = NULL;
- }
- }
-
- else
- {
- if (info_ptr->splt_palettes_num)
- {
- int i;
- for (i = 0; i < info_ptr->splt_palettes_num; i++)
- png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, (int)i);
-
- png_free(png_ptr, info_ptr->splt_palettes);
- info_ptr->splt_palettes = NULL;
- info_ptr->splt_palettes_num = 0;
- }
- info_ptr->valid &= ~PNG_INFO_sPLT;
- }
- }
-#endif
-
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
- if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)
- {
- if (num != -1)
- {
- if (info_ptr->unknown_chunks)
- {
- png_free(png_ptr, info_ptr->unknown_chunks[num].data);
- info_ptr->unknown_chunks[num].data = NULL;
- }
+ png_free(png_ptr, info_ptr->splt_palettes[num].name);
+ png_free(png_ptr, info_ptr->splt_palettes[num].entries);
+ info_ptr->splt_palettes[num].name = NULL;
+ info_ptr->splt_palettes[num].entries = NULL;
}
else
{
int i;
- if (info_ptr->unknown_chunks_num)
+ for (i = 0; i < info_ptr->splt_palettes_num; i++)
{
- for (i = 0; i < info_ptr->unknown_chunks_num; i++)
- png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, (int)i);
-
- png_free(png_ptr, info_ptr->unknown_chunks);
- info_ptr->unknown_chunks = NULL;
- info_ptr->unknown_chunks_num = 0;
+ png_free(png_ptr, info_ptr->splt_palettes[i].name);
+ png_free(png_ptr, info_ptr->splt_palettes[i].entries);
}
+
+ png_free(png_ptr, info_ptr->splt_palettes);
+ info_ptr->splt_palettes = NULL;
+ info_ptr->splt_palettes_num = 0;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sPLT);
+ }
+ }
+#endif
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+ if (info_ptr->unknown_chunks != 0 &&
+ ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0)
+ {
+ if (num != -1)
+ {
+ png_free(png_ptr, info_ptr->unknown_chunks[num].data);
+ info_ptr->unknown_chunks[num].data = NULL;
+ }
+
+ else
+ {
+ int i;
+
+ for (i = 0; i < info_ptr->unknown_chunks_num; i++)
+ png_free(png_ptr, info_ptr->unknown_chunks[i].data);
+
+ png_free(png_ptr, info_ptr->unknown_chunks);
+ info_ptr->unknown_chunks = NULL;
+ info_ptr->unknown_chunks_num = 0;
}
}
#endif
#ifdef PNG_hIST_SUPPORTED
/* Free any hIST entry */
- if ((mask & PNG_FREE_HIST) & info_ptr->free_me)
+ if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0)
{
png_free(png_ptr, info_ptr->hist);
info_ptr->hist = NULL;
- info_ptr->valid &= ~PNG_INFO_hIST;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_hIST);
}
#endif
/* Free any PLTE entry that was internally allocated */
- if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)
+ if (((mask & PNG_FREE_PLTE) & info_ptr->free_me) != 0)
{
png_free(png_ptr, info_ptr->palette);
info_ptr->palette = NULL;
- info_ptr->valid &= ~PNG_INFO_PLTE;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE);
info_ptr->num_palette = 0;
}
#ifdef PNG_INFO_IMAGE_SUPPORTED
/* Free any image bits attached to the info structure */
- if ((mask & PNG_FREE_ROWS) & info_ptr->free_me)
+ if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0)
{
- if (info_ptr->row_pointers)
+ if (info_ptr->row_pointers != 0)
{
png_uint_32 row;
for (row = 0; row < info_ptr->height; row++)
- {
png_free(png_ptr, info_ptr->row_pointers[row]);
- info_ptr->row_pointers[row] = NULL;
- }
+
png_free(png_ptr, info_ptr->row_pointers);
info_ptr->row_pointers = NULL;
}
- info_ptr->valid &= ~PNG_INFO_IDAT;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_IDAT);
}
#endif
if (num != -1)
- mask &= ~PNG_FREE_MUL;
+ mask &= PNG_BIC_MASK(PNG_FREE_MUL);
info_ptr->free_me &= ~mask;
}
-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+#endif /* READ || WRITE */
/* This function returns a pointer to the io_ptr associated with the user
* functions. The application should free any memory associated with this
@@ -612,26 +617,30 @@ png_init_io(png_structrp png_ptr, png_FILE_p fp)
if (png_ptr == NULL)
return;
- png_ptr->io_ptr = (png_voidp)fp;
-}
-# endif
+ if (png_ptr->rw_data_fn == NULL)
+ {
+# ifdef PNG_READ_SUPPORTED
+ if (png_ptr->read_struct)
+ png_set_read_fn(png_ptr, fp, png_default_read_data);
+# ifdef PNG_WRITE_SUPPORTED
+ else
+# endif /* WRITE */
+# endif /* READ */
+# ifdef PNG_WRITE_SUPPORTED
+ if (!png_ptr->read_struct)
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_set_write_fn(png_ptr, fp, png_default_write_data,
+ png_default_flush);
+# else
+ png_set_write_fn(png_ptr, fp, png_default_write_data, NULL);
+# endif
+# endif /* WRITE */
+ }
-#ifdef PNG_SAVE_INT_32_SUPPORTED
-/* The png_save_int_32 function assumes integers are stored in two's
- * complement format. If this isn't the case, then this routine needs to
- * be modified to write data in two's complement format. Note that,
- * the following works correctly even if png_int_32 has more than 32 bits
- * (compare the more complex code required on read for sign extension.)
- */
-void PNGAPI
-png_save_int_32(png_bytep buf, png_int_32 i)
-{
- buf[0] = (png_byte)((i >> 24) & 0xff);
- buf[1] = (png_byte)((i >> 16) & 0xff);
- buf[2] = (png_byte)((i >> 8) & 0xff);
- buf[3] = (png_byte)(i & 0xff);
+ else
+ png_ptr->io_ptr = fp;
}
-#endif
+# endif /* STDIO */
# ifdef PNG_TIME_RFC1123_SUPPORTED
/* Convert the supplied time into an RFC 1123 string suitable for use in
@@ -683,9 +692,9 @@ png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
return 1;
}
-# endif /* PNG_TIME_RFC1123_SUPPORTED */
+# endif /* TIME_RFC1123 */
-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+#endif /* READ || WRITE */
png_const_charp PNGAPI
png_get_copyright(png_const_structrp png_ptr)
@@ -696,14 +705,15 @@ png_get_copyright(png_const_structrp png_ptr)
#else
# ifdef __STDC__
return PNG_STRING_NEWLINE \
- "libpng version 1.7.0beta35 - March 22, 2014" PNG_STRING_NEWLINE \
- "Copyright (c) 1998-2014 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
+ "libpng version 1.7.0beta90 - August 28, 2017" PNG_STRING_NEWLINE \
+ "Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson" \
+ PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
"Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
PNG_STRING_NEWLINE;
# else
- return "libpng version 1.7.0beta35 - March 22, 2014\
- Copyright (c) 1998-2014 Glenn Randers-Pehrson\
+ return "libpng version 1.7.0beta90 - August 28, 2017\
+ Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson\
Copyright (c) 1996-1997 Andreas Dilger\
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
# endif
@@ -739,11 +749,11 @@ png_get_header_version(png_const_structrp png_ptr)
/* Returns longer string containing both version and date */
PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */
#ifdef __STDC__
- return PNG_HEADER_VERSION_STRING
# ifndef PNG_READ_SUPPORTED
- " (NO READ SUPPORT)"
+ return PNG_HEADER_VERSION_STRING " (NO READ SUPPORT)" PNG_STRING_NEWLINE;
+# else
+ return PNG_HEADER_VERSION_STRING PNG_STRING_NEWLINE;
# endif
- PNG_STRING_NEWLINE;
#else
return PNG_HEADER_VERSION_STRING;
#endif
@@ -760,9 +770,9 @@ void PNGAPI
png_build_grayscale_palette(int bit_depth, png_colorp palette)
{
int num_palette;
- int color_inc;
+ png_byte color_inc;
int i;
- int v;
+ png_byte v;
png_debug(1, "in png_do_build_grayscale_palette");
@@ -797,12 +807,8 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette)
break;
}
- for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
- {
- palette[i].red = (png_byte)v;
- palette[i].green = (png_byte)v;
- palette[i].blue = (png_byte)v;
- }
+ for (i = 0, v = 0; i < num_palette; ++i, v = PNG_BYTE(v+color_inc))
+ palette[i].red = palette[i].green = palette[i].blue = v;
}
#endif
@@ -827,7 +833,7 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)
{
p -= 5;
- if (!memcmp(chunk_name, p, 4))
+ if (memcmp(chunk_name, p, 4) == 0)
return p[4];
}
while (p > p_end);
@@ -839,18 +845,6 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)
*/
return PNG_HANDLE_CHUNK_AS_DEFAULT;
}
-
-#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
- defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
-int /* PRIVATE */
-png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name)
-{
- png_byte chunk_string[5];
-
- PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name);
- return png_handle_as_unknown(png_ptr, chunk_string);
-}
-#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
#endif /* SET_UNKNOWN_CHUNKS */
/* This function was added to libpng-1.0.7 */
@@ -867,58 +861,58 @@ png_access_version_number(void)
* like Z_OK or Z_STREAM_END where the error code is apparently a success code.
*/
void /* PRIVATE */
-png_zstream_error(png_structrp png_ptr, int ret)
+png_zstream_error(z_stream *zstream, int ret)
{
/* Translate 'ret' into an appropriate error string, priority is given to the
* one in zstream if set. This always returns a string, even in cases like
* Z_OK or Z_STREAM_END where the error code is a success code.
*/
- if (png_ptr->zstream.msg == NULL) switch (ret)
+ if (zstream->msg == NULL) switch (ret)
{
default:
case Z_OK:
- png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
+ zstream->msg = PNGZ_MSG_CAST("unexpected zlib return code");
break;
case Z_STREAM_END:
/* Normal exit */
- png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
+ zstream->msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
break;
case Z_NEED_DICT:
/* This means the deflate stream did not have a dictionary; this
* indicates a bogus PNG.
*/
- png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
+ zstream->msg = PNGZ_MSG_CAST("missing LZ dictionary");
break;
case Z_ERRNO:
/* gz APIs only: should not happen */
- png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
+ zstream->msg = PNGZ_MSG_CAST("zlib IO error");
break;
case Z_STREAM_ERROR:
/* internal libpng error */
- png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
+ zstream->msg = PNGZ_MSG_CAST("bad parameters to zlib");
break;
case Z_DATA_ERROR:
- png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
+ zstream->msg = PNGZ_MSG_CAST("damaged LZ stream");
break;
case Z_MEM_ERROR:
- png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
+ zstream->msg = PNGZ_MSG_CAST("insufficient memory");
break;
case Z_BUF_ERROR:
/* End of input or output; not a problem if the caller is doing
* incremental read or write.
*/
- png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
+ zstream->msg = PNGZ_MSG_CAST("truncated");
break;
case Z_VERSION_ERROR:
- png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
+ zstream->msg = PNGZ_MSG_CAST("unsupported zlib version");
break;
case PNG_UNEXPECTED_ZLIB_RETURN:
@@ -927,7 +921,7 @@ png_zstream_error(png_structrp png_ptr, int ret)
* and change pngpriv.h. Note that this message is "... return",
* whereas the default/Z_OK one is "... return code".
*/
- png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
+ zstream->msg = PNGZ_MSG_CAST("unexpected zlib return");
break;
}
}
@@ -940,7 +934,7 @@ png_zstream_error(png_structrp png_ptr, int ret)
#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */
static int
png_colorspace_check_gamma(png_const_structrp png_ptr,
- png_colorspacerp colorspace, png_fixed_point gAMA, int from)
+ png_colorspacerp colorspace, png_fixed_point gAMA, int from)
/* This is called to check a new gamma value against an existing one. The
* routine returns false if the new gamma value should not be written.
*
@@ -949,13 +943,19 @@ png_colorspace_check_gamma(png_const_structrp png_ptr,
* 0: the new gamma value is the libpng estimate for an ICC profile
* 1: the new gamma value comes from a gAMA chunk
* 2: the new gamma value comes from an sRGB chunk
+ *
+ * API CHANGE: libpng 1.7.0: prior to 1.7 the check below used the build-time
+ * constant PNG_GAMMA_THRESHOLD_FIXED and the results would therefore depend
+ * on a parameter that was intended for tuning the READ_GAMMA support. In
+ * 1.7 a fixed value of +/-1% is used instead; this reflects the fact that
+ * gamma values are rarely quoted to more than 2 decimal digits of precision.
*/
{
png_fixed_point gtest;
if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
- (!png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) ||
- png_gamma_significant(gtest)))
+ (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 ||
+ gtest < PNG_FP_1 - 1000 || gtest > PNG_FP_1 + 1000))
{
/* Either this is an sRGB image, in which case the calculated gamma
* approximation should match, or this is an image with a profile and the
@@ -966,7 +966,7 @@ png_colorspace_check_gamma(png_const_structrp png_ptr,
if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
{
png_chunk_report(png_ptr, "gamma value does not match sRGB",
- PNG_CHUNK_ERROR);
+ PNG_CHUNK_ERROR);
/* Do not overwrite an sRGB value */
return from == 2;
}
@@ -974,7 +974,7 @@ png_colorspace_check_gamma(png_const_structrp png_ptr,
else /* sRGB tag not involved */
{
png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
- PNG_CHUNK_WARNING);
+ PNG_CHUNK_WARNING);
return from == 1;
}
}
@@ -984,10 +984,10 @@ png_colorspace_check_gamma(png_const_structrp png_ptr,
void /* PRIVATE */
png_colorspace_set_gamma(png_const_structrp png_ptr,
- png_colorspacerp colorspace, png_fixed_point gAMA)
+ png_colorspacerp colorspace, png_fixed_point gAMA)
{
/* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
- * occur. Since the fixed point representation is assymetrical it is
+ * occur. Since the fixed point representation is asymmetrical it is
* possible for 1/gamma to overflow the limit of 21474 and this means the
* gamma value must be at least 5/100000 and hence at most 20000.0. For
* safety the limits here are a little narrower. The values are 0.00016 to
@@ -997,25 +997,43 @@ png_colorspace_set_gamma(png_const_structrp png_ptr,
* In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk
* handling code, which only required the value to be >0.
*/
- png_const_charp errmsg;
+# if (defined PNG_TRANSFORM_MECH_SUPPORTED) &&\
+ (defined PNG_ERROR_TEXT_SUPPORTED)
+# define ERRMSG 1
+# else
+# define ERRMSG 0
+# endif
+
+# if ERRMSG
+ png_const_charp errmsg;
+# endif
if (gAMA < 16 || gAMA > 625000000)
- errmsg = "gamma value out of range";
+ {
+# if ERRMSG
+ errmsg = "gamma value out of range";
+# endif
+ }
# ifdef PNG_READ_gAMA_SUPPORTED
/* Allow the application to set the gamma value more than once */
- else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
+ else if (png_ptr->read_struct &&
(colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)
+ {
+# if ERRMSG
errmsg = "duplicate";
+# endif
+ }
# endif
/* Do nothing if the colorspace is already invalid */
- else if (colorspace->flags & PNG_COLORSPACE_INVALID)
+ else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
return;
else
{
- if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, 1/*from gAMA*/))
+ if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA,
+ 1/*from gAMA*/) != 0)
{
/* Store this gamma value. */
colorspace->gamma = gAMA;
@@ -1032,6 +1050,7 @@ png_colorspace_set_gamma(png_const_structrp png_ptr,
}
/* Error exit - errmsg has been set. */
+# undef ERRMSG
colorspace->flags |= PNG_COLORSPACE_INVALID;
png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);
}
@@ -1039,10 +1058,10 @@ png_colorspace_set_gamma(png_const_structrp png_ptr,
void /* PRIVATE */
png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)
{
- if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
{
/* Everything is invalid */
- info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|
PNG_INFO_iCCP);
# ifdef PNG_COLORSPACE_SUPPORTED
@@ -1060,24 +1079,24 @@ png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)
* it; this allows a PNG to contain a profile which matches sRGB and
* yet still have that profile retrievable by the application.
*/
- if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB)
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0)
info_ptr->valid |= PNG_INFO_sRGB;
else
- info_ptr->valid &= ~PNG_INFO_sRGB;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sRGB);
- if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
info_ptr->valid |= PNG_INFO_cHRM;
else
- info_ptr->valid &= ~PNG_INFO_cHRM;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_cHRM);
# endif
- if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA)
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0)
info_ptr->valid |= PNG_INFO_gAMA;
else
- info_ptr->valid &= ~PNG_INFO_gAMA;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA);
}
}
@@ -1095,6 +1114,25 @@ png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr)
#endif
#ifdef PNG_COLORSPACE_SUPPORTED
+/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
+static png_fixed_point
+png_reciprocal(png_fixed_point a)
+{
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = floor(1E10/a+.5);
+
+ if (r <= 2147483647. && r >= -2147483648.)
+ return (png_fixed_point)r;
+#else
+ png_fixed_point res;
+
+ if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0)
+ return res;
+#endif
+
+ return 0; /* error/overflow */
+}
+
/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for
* cHRM, as opposed to using chromaticities. These internal APIs return
* non-zero on a parameter error. The X, Y and Z values are required to be
@@ -1106,22 +1144,28 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
png_int_32 d, dwhite, whiteX, whiteY;
d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z;
- if (!png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d)) return 1;
- if (!png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d)) return 1;
+ if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0)
+ return 1;
+ if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0)
+ return 1;
dwhite = d;
whiteX = XYZ->red_X;
whiteY = XYZ->red_Y;
d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z;
- if (!png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d)) return 1;
- if (!png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d)) return 1;
+ if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0)
+ return 1;
+ if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0)
+ return 1;
dwhite += d;
whiteX += XYZ->green_X;
whiteY += XYZ->green_Y;
d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z;
- if (!png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d)) return 1;
- if (!png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d)) return 1;
+ if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0)
+ return 1;
+ if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0)
+ return 1;
dwhite += d;
whiteX += XYZ->blue_X;
whiteY += XYZ->blue_Y;
@@ -1129,8 +1173,10 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
/* The reference white is simply the sum of the end-point (X,Y,Z) vectors,
* thus:
*/
- if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1;
- if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1;
+ if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0)
+ return 1;
+ if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0)
+ return 1;
return 0;
}
@@ -1143,16 +1189,17 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
/* Check xy and, implicitly, z. Note that wide gamut color spaces typically
* have end points with 0 tristimulus values (these are impossible end
- * points, but they are used to cover the possible colors.)
+ * points, but they are used to cover the possible colors). We check
+ * xy->whitey against 5, not 0, to avoid a possible integer overflow.
*/
- if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1;
- if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1;
+ if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1;
+ if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1;
if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1;
if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1;
- if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1;
- if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1;
+ if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1;
+ if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1;
if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1;
- if (xy->whitey < 0 || xy->whitey > PNG_FP_1-xy->whitex) return 1;
+ if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1;
/* The reverse calculation is more difficult because the original tristimulus
* value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8
@@ -1227,7 +1274,7 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
* (1/white-y), so we can immediately see that as white-y approaches 0 the
* accuracy inherent in the cHRM chunk drops off substantially.
*
- * libpng arithmetic: a simple invertion of the above equations
+ * libpng arithmetic: a simple inversion of the above equations
* ------------------------------------------------------------
*
* white_scale = 1/white-y
@@ -1333,16 +1380,16 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
/* By the argument, above overflow should be impossible here. The return
* value of 2 indicates an internal error to the caller.
*/
- if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7))
+ if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0)
return 2;
- if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7))
+ if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0)
return 2;
denominator = left - right;
/* Now find the red numerator. */
- if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7))
+ if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
return 2;
- if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7))
+ if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
return 2;
/* Overflow is possible here and it indicates an extreme set of PNG cHRM
@@ -1350,16 +1397,16 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
* scale value because this allows us to delay the multiplication of white-y
* into the denominator, which tends to produce a small number.
*/
- if (!png_muldiv(&red_inverse, xy->whitey, denominator, left-right) ||
+ if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 ||
red_inverse <= xy->whitey /* r+g+b scales = white scale */)
return 1;
/* Similarly for green_inverse: */
- if (!png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7))
+ if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
return 2;
- if (!png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7))
+ if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
return 2;
- if (!png_muldiv(&green_inverse, xy->whitey, denominator, left-right) ||
+ if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 ||
green_inverse <= xy->whitey)
return 1;
@@ -1367,29 +1414,34 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
* can still produce 0 for extreme cHRM values.
*/
blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) -
- png_reciprocal(green_inverse);
- if (blue_scale <= 0) return 1;
+ png_reciprocal(green_inverse);
+ if (blue_scale <= 0)
+ return 1;
/* And fill in the png_XYZ: */
- if (!png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse)) return 1;
- if (!png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse)) return 1;
- if (!png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,
- red_inverse))
+ if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,
+ red_inverse) == 0)
return 1;
- if (!png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse))
+ if (png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse) == 0)
return 1;
- if (!png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse))
+ if (png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse) == 0)
return 1;
- if (!png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1,
- green_inverse))
+ if (png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1,
+ green_inverse) == 0)
return 1;
- if (!png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1)) return 1;
- if (!png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1)) return 1;
- if (!png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,
- PNG_FP_1))
+ if (png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,
+ PNG_FP_1) == 0)
return 1;
return 0; /*success*/
@@ -1411,24 +1463,35 @@ png_XYZ_normalize(png_XYZ *XYZ)
* safe.
*/
Y = XYZ->red_Y;
- if (0x7fffffff - Y < XYZ->green_X) return 1;
+ if (0x7fffffff - Y < XYZ->green_X)
+ return 1;
Y += XYZ->green_Y;
- if (0x7fffffff - Y < XYZ->blue_X) return 1;
+ if (0x7fffffff - Y < XYZ->blue_X)
+ return 1;
Y += XYZ->blue_Y;
if (Y != PNG_FP_1)
{
- if (!png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y)) return 1;
+ if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0)
+ return 1;
- if (!png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y)) return 1;
+ if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0)
+ return 1;
- if (!png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y)) return 1;
- if (!png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y)) return 1;
+ if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0)
+ return 1;
+ if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0)
+ return 1;
}
return 0;
@@ -1438,14 +1501,16 @@ static int
png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta)
{
/* Allow an error of +/-0.01 (absolute value) on each chromaticity */
- return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||
- PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||
- PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) ||
- PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) ||
- PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||
- PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||
- PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) ||
- PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta));
+ if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||
+ PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||
+ PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) ||
+ PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) ||
+ PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||
+ PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||
+ PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) ||
+ PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta))
+ return 0;
+ return 1;
}
/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM
@@ -1466,13 +1531,15 @@ png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy)
/* As a side-effect this routine also returns the XYZ endpoints. */
result = png_XYZ_from_xy(XYZ, xy);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
result = png_xy_from_XYZ(&xy_test, XYZ);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
if (png_colorspace_endpoints_match(xy, &xy_test,
- 5/*actually, the math is pretty accurate*/))
+ 5/*actually, the math is pretty accurate*/) != 0)
return 0;
/* Too much slip */
@@ -1489,10 +1556,12 @@ png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ)
png_XYZ XYZtemp;
result = png_XYZ_normalize(XYZ);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
result = png_xy_from_XYZ(xy, XYZ);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
XYZtemp = *XYZ;
return png_colorspace_check_xy(&XYZtemp, xy);
@@ -1510,22 +1579,24 @@ static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */
static int
png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
- png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
- int preferred)
+ png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
+ int preferred)
{
- if (colorspace->flags & PNG_COLORSPACE_INVALID)
+ if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
return 0;
/* The consistency check is performed on the chromaticities; this factors out
* variations because of the normalization (or not) of the end point Y
* values.
*/
- if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS))
+ if (preferred < 2 &&
+ (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
{
/* The end points must be reasonably close to any we already have. The
* following allows an error of up to +/-.001
*/
- if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 100))
+ if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy,
+ 100) == 0)
{
colorspace->flags |= PNG_COLORSPACE_INVALID;
png_benign_error(png_ptr, "inconsistent chromaticities");
@@ -1544,7 +1615,7 @@ png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
/* The end points are normally quoted to two decimal digits, so allow +/-0.01
* on this test.
*/
- if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000))
+ if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0)
colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;
else
@@ -1556,7 +1627,7 @@ png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
int /* PRIVATE */
png_colorspace_set_chromaticities(png_const_structrp png_ptr,
- png_colorspacerp colorspace, const png_xy *xy, int preferred)
+ png_colorspacerp colorspace, const png_xy *xy, int preferred)
{
/* We must check the end points to ensure they are reasonable - in the past
* color management systems have crashed as a result of getting bogus
@@ -1570,7 +1641,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr,
{
case 0: /* success */
return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
- preferred);
+ preferred);
case 1:
/* We can't invert the chromaticities so we can't produce value XYZ
@@ -1585,7 +1656,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr,
* want error reports so for the moment it is an error.
*/
colorspace->flags |= PNG_COLORSPACE_INVALID;
- png_error(png_ptr, "internal error checking chromaticities");
+ impossible("error checking chromaticities");
break;
}
@@ -1594,7 +1665,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr,
int /* PRIVATE */
png_colorspace_set_endpoints(png_const_structrp png_ptr,
- png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
+ png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
{
png_XYZ XYZ = *XYZ_in;
png_xy xy;
@@ -1603,7 +1674,7 @@ png_colorspace_set_endpoints(png_const_structrp png_ptr,
{
case 0:
return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,
- preferred);
+ preferred);
case 1:
/* End points are invalid. */
@@ -1613,7 +1684,7 @@ png_colorspace_set_endpoints(png_const_structrp png_ptr,
default:
colorspace->flags |= PNG_COLORSPACE_INVALID;
- png_error(png_ptr, "internal error checking chromaticities");
+ impossible("error checking chromaticities");
break;
}
@@ -1627,7 +1698,7 @@ png_icc_tag_char(png_uint_32 byte)
{
byte &= 0xff;
if (byte >= 32 && byte <= 126)
- return (char)byte;
+ return (char)/*SAFE*/byte;
else
return '?';
}
@@ -1650,7 +1721,8 @@ is_ICC_signature_char(png_alloc_size_t it)
(it >= 97 && it <= 122);
}
-static int is_ICC_signature(png_alloc_size_t it)
+static int
+is_ICC_signature(png_alloc_size_t it)
{
return is_ICC_signature_char(it >> 24) /* checks all the top bits */ &&
is_ICC_signature_char((it >> 16) & 0xff) &&
@@ -1660,7 +1732,7 @@ static int is_ICC_signature(png_alloc_size_t it)
static int
png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
- png_const_charp name, png_alloc_size_t value, png_const_charp reason)
+ png_const_charp name, png_alloc_size_t value, png_const_charp reason)
{
size_t pos;
char message[196]; /* see below for calculation */
@@ -1671,7 +1743,7 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */
pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */
pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */
- if (is_ICC_signature(value))
+ if (is_ICC_signature(value) != 0)
{
/* So 'value' is at most 4 bytes and the following cast is safe */
png_icc_tag_name(message+pos, (png_uint_32)value);
@@ -1685,8 +1757,8 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/
pos = png_safecat(message, (sizeof message), pos,
- png_format_number(number, number+(sizeof number),
- PNG_NUMBER_FORMAT_x, value));
+ png_format_number(number, number+(sizeof number),
+ PNG_NUMBER_FORMAT_x, value));
pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/
}
# endif
@@ -1695,12 +1767,12 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
PNG_UNUSED(pos)
/* This is recoverable, but make it unconditionally an app_error on write to
- * avoid writing invalid ICC profiles into PNG files. (I.e. we handle them
+ * avoid writing invalid ICC profiles into PNG files (i.e., we handle them
* on read, with a warning, but on write unless the app turns off
* application errors the PNG won't be written.)
*/
png_chunk_report(png_ptr, message,
- (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
+ (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
return 0;
}
@@ -1709,12 +1781,12 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
#ifdef PNG_sRGB_SUPPORTED
int /* PRIVATE */
png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
- int intent)
+ int intent)
{
/* sRGB sets known gamma, end points and (from the chunk) intent. */
/* IMPORTANT: these are not necessarily the values found in an ICC profile
* because ICC profiles store values adapted to a D50 environment; it is
- * expected that the ICC profile mediaWhitePointTag will be D50, see the
+ * expected that the ICC profile mediaWhitePointTag will be D50; see the
* checks and code elsewhere to understand this better.
*
* These XYZ values, which are accurate to 5dp, produce rgb to gray
@@ -1732,7 +1804,7 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
};
/* Do nothing if the colorspace is already invalidated. */
- if (colorspace->flags & PNG_COLORSPACE_INVALID)
+ if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
return 0;
/* Check the intent, then check for existing settings. It is valid for the
@@ -1746,10 +1818,10 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
*/
if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
return png_icc_profile_error(png_ptr, colorspace, "sRGB",
- (unsigned)intent, "invalid sRGB rendering intent");
+ (unsigned)intent, "invalid sRGB rendering intent");
if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
- colorspace->rendering_intent != intent)
+ colorspace->rendering_intent != intent)
return png_icc_profile_error(png_ptr, colorspace, "sRGB",
(unsigned)intent, "inconsistent rendering intents");
@@ -1763,8 +1835,8 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
* warn but overwrite the value with the correct one.
*/
if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
- !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
- 100))
+ !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
+ 100))
png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
PNG_CHUNK_ERROR);
@@ -1772,10 +1844,10 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
* returns true when the 'from' argument corresponds to sRGB (2).
*/
(void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
- 2/*from sRGB*/);
+ 2/*from sRGB*/);
/* intent: bugs in GCC force 'int' to be used as the parameter type. */
- colorspace->rendering_intent = (png_uint_16)intent;
+ colorspace->rendering_intent = png_check_u16(png_ptr, intent);
colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT;
/* endpoints */
@@ -1807,23 +1879,23 @@ static const png_byte D50_nCIEXYZ[12] =
int /* PRIVATE */
png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
- png_const_charp name, png_uint_32 profile_length)
+ png_const_charp name, png_uint_32 profile_length)
{
if (profile_length < 132)
return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
- "too short");
+ "too short");
if (profile_length & 3)
return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
- "invalid length");
+ "invalid length");
return 1;
}
int /* PRIVATE */
png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
- png_const_charp name, png_uint_32 profile_length,
- png_const_bytep profile/* first 132 bytes only */, int color_type)
+ png_const_charp name, png_uint_32 profile_length,
+ png_const_bytep profile/* first 132 bytes only */, int is_color)
{
png_uint_32 temp;
@@ -1835,13 +1907,13 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
temp = png_get_uint_32(profile);
if (temp != profile_length)
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "length does not match profile");
+ "length does not match profile");
temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */
profile_length < 132+12*temp) /* truncated tag table */
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "tag count too large");
+ "tag count too large");
/* The 'intent' must be valid or we can't store it, ICC limits the intent to
* 16 bits.
@@ -1849,14 +1921,14 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
temp = png_get_uint_32(profile+64);
if (temp >= 0xffff) /* The ICC limit */
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "invalid rendering intent");
+ "invalid rendering intent");
/* This is just a warning because the profile may be valid in future
* versions.
*/
if (temp >= PNG_sRGB_INTENT_LAST)
(void)png_icc_profile_error(png_ptr, NULL, name, temp,
- "intent outside defined range");
+ "intent outside defined range");
/* At this point the tag table can't be checked because it hasn't necessarily
* been loaded; however, various header fields can be checked. These checks
@@ -1873,7 +1945,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
temp = png_get_uint_32(profile+36); /* signature 'ascp' */
if (temp != 0x61637370)
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "invalid signature");
+ "invalid signature");
/* Currently the PCS illuminant/adopted white point (the computational
* white point) are required to be D50,
@@ -1884,7 +1956,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
*/
if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)
(void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,
- "PCS illuminant is not D50");
+ "PCS illuminant is not D50");
/* The PNG spec requires this:
* "If the iCCP chunk is present, the image samples conform to the colour
@@ -1910,20 +1982,20 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
switch (temp)
{
case 0x52474220: /* 'RGB ' */
- if (!(color_type & PNG_COLOR_MASK_COLOR))
+ if (!is_color)
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "RGB color space not permitted on grayscale PNG");
+ "RGB color space not permitted on grayscale PNG");
break;
case 0x47524159: /* 'GRAY' */
- if (color_type & PNG_COLOR_MASK_COLOR)
+ if (is_color)
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "Gray color space not permitted on RGB PNG");
+ "Gray color space not permitted on RGB PNG");
break;
default:
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "invalid ICC profile color space");
+ "invalid ICC profile color space");
}
/* It is up to the application to check that the profile class matches the
@@ -1948,25 +2020,25 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
case 0x61627374: /* 'abst' */
/* May not be embedded in an image */
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "invalid embedded Abstract ICC profile");
+ "invalid embedded Abstract ICC profile");
case 0x6C696E6B: /* 'link' */
- /* DeviceLink profiles cannnot be interpreted in a non-device specific
+ /* DeviceLink profiles cannot be interpreted in a non-device specific
* fashion, if an app uses the AToB0Tag in the profile the results are
* undefined unless the result is sent to the intended device,
* therefore a DeviceLink profile should not be found embedded in a
* PNG.
*/
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "unexpected DeviceLink ICC profile class");
+ "unexpected DeviceLink ICC profile class");
case 0x6E6D636C: /* 'nmcl' */
/* A NamedColor profile is also device specific, however it doesn't
- * contain an AToB0 tag that is open to misintrepretation. Almost
+ * contain an AToB0 tag that is open to misinterpretation. Almost
* certainly it will fail the tests below.
*/
(void)png_icc_profile_error(png_ptr, NULL, name, temp,
- "unexpected NamedColor ICC profile class");
+ "unexpected NamedColor ICC profile class");
break;
default:
@@ -1976,7 +2048,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
* understood profiles.
*/
(void)png_icc_profile_error(png_ptr, NULL, name, temp,
- "unrecognized ICC profile class");
+ "unrecognized ICC profile class");
break;
}
@@ -1992,7 +2064,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
default:
return png_icc_profile_error(png_ptr, colorspace, name, temp,
- "unexpected ICC PCS encoding");
+ "unexpected ICC PCS encoding");
}
return 1;
@@ -2000,8 +2072,8 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
int /* PRIVATE */
png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
- png_const_charp name, png_uint_32 profile_length,
- png_const_bytep profile /* header plus whole tag table */)
+ png_const_charp name, png_uint_32 profile_length,
+ png_const_bytep profile /* header plus whole tag table */)
{
png_uint_32 tag_count = png_get_uint_32(profile+128);
png_uint_32 itag;
@@ -2029,7 +2101,7 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
* alignment.
*/
(void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
- "ICC profile tag start not a multiple of 4");
+ "ICC profile tag start not a multiple of 4");
}
/* This is a hard error; potentially it can cause read outside the
@@ -2037,13 +2109,14 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
*/
if (tag_start > profile_length || tag_length > profile_length - tag_start)
return png_icc_profile_error(png_ptr, colorspace, name, tag_id,
- "ICC profile tag outside profile");
+ "ICC profile tag outside profile");
}
return 1; /* success, maybe with warnings */
}
#ifdef PNG_sRGB_SUPPORTED
+#if PNG_sRGB_PROFILE_CHECKS >= 0
/* Information about the known ICC sRGB profiles */
static const struct
{
@@ -2064,22 +2137,22 @@ static const struct
*/
/* adler32, crc32, MD5[4], intent, date, length, file-name */
PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
- PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
- "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
+ PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
+ "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
/* ICC sRGB v2 perceptual no black-compensation: */
PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
- PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
- "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
+ PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
+ "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
- PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
- "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
+ PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
+ "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
/* ICC sRGB v4 perceptual */
PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
- PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
- "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
+ PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
+ "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
/* The following profiles have no known MD5 checksum. If there is a match
* on the (empty) MD5 the other fields are used to attempt a match and
@@ -2087,8 +2160,8 @@ static const struct
* which suggests that they were also made by Hewlett Packard.
*/
PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
- PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
- "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
+ PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
+ "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
/* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
* match the D50 PCS illuminant in the header (it is in fact the D65 values,
@@ -2098,17 +2171,17 @@ static const struct
* chromaticAdaptationTag.
*/
PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
- PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
- "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
+ PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
+ "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
- PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
- "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
+ PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
+ "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
};
static int
png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
- png_const_bytep profile, uLong adler)
+ png_const_bytep profile, uLong adler)
{
/* The quick check is to verify just the MD5 signature and trust the
* rest of the data. Because the profile has already been verified for
@@ -2124,6 +2197,12 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
#endif
unsigned int i;
+# ifdef PNG_SET_OPTION_SUPPORTED
+# ifdef PNG_SKIP_sRGB_CHECK_PROFILE
+ /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */
+ if (!png_ptr->skip_sRGB_profile_check)
+# endif /* SKIP_sRGB_CHECK_PROFILE */
+# endif /* SET_OPTION */
for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)
{
if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&
@@ -2136,7 +2215,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
* are not used by default if there is an MD5!)
*/
# if PNG_sRGB_PROFILE_CHECKS == 0
- if (png_sRGB_checks[i].have_md5)
+ if (png_sRGB_checks[i].have_md5 != 0)
return 1+png_sRGB_checks[i].is_broken;
# endif
@@ -2149,7 +2228,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
/* Length *and* intent must match */
if (length == png_sRGB_checks[i].length &&
- intent == png_sRGB_checks[i].intent)
+ intent == (png_uint_32) png_sRGB_checks[i].intent)
{
/* Now calculate the adler32 if not done already. */
if (adler == 0)
@@ -2176,7 +2255,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
if (crc == png_sRGB_checks[i].crc)
# endif
{
- if (png_sRGB_checks[i].is_broken)
+ if (png_sRGB_checks[i].is_broken != 0)
{
/* These profiles are known to have bad data that may cause
* problems if they are used, therefore attempt to
@@ -2184,24 +2263,23 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
* which is made irrelevant by this error.
*/
png_chunk_report(png_ptr, "known incorrect sRGB profile",
- PNG_CHUNK_ERROR);
+ PNG_CHUNK_ERROR);
}
/* Warn that this being done; this isn't even an error since
* the profile is perfectly valid, but it would be nice if
* people used the up-to-date ones.
*/
- else if (!png_sRGB_checks[i].have_md5)
+ else if (png_sRGB_checks[i].have_md5 == 0)
{
png_chunk_report(png_ptr,
- "out-of-date sRGB profile with no signature",
- PNG_CHUNK_WARNING);
+ "out-of-date sRGB profile with no signature",
+ PNG_CHUNK_WARNING);
}
return 1+png_sRGB_checks[i].is_broken;
}
}
- }
# if PNG_sRGB_PROFILE_CHECKS > 0
/* The signature matched, but the profile had been changed in some
@@ -2209,45 +2287,46 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
* Fall through to "no match".
*/
png_chunk_report(png_ptr,
- "Not recognizing known sRGB profile that has been edited",
+ "Not recognizing known sRGB profile that has been edited",
PNG_CHUNK_WARNING);
+ break;
# endif
+ }
}
}
return 0; /* no match */
}
-#endif
-#ifdef PNG_sRGB_SUPPORTED
void /* PRIVATE */
png_icc_set_sRGB(png_const_structrp png_ptr,
- png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
+ png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
{
/* Is this profile one of the known ICC sRGB profiles? If it is, just set
* the sRGB information.
*/
- if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler))
+ if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0)
(void)png_colorspace_set_sRGB(png_ptr, colorspace,
(int)/*already checked*/png_get_uint_32(profile+64));
}
-#endif /* PNG_READ_sRGB_SUPPORTED */
+#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
+#endif /* sRGB */
int /* PRIVATE */
png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
- png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
- int color_type)
+ png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
+ int is_color)
{
- if (colorspace->flags & PNG_COLORSPACE_INVALID)
+ if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
return 0;
- if (png_icc_check_length(png_ptr, colorspace, name, profile_length) &&
- png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
- color_type) &&
- png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
- profile))
+ if (png_icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
+ png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
+ is_color) != 0 &&
+ png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
+ profile) != 0)
{
-# ifdef PNG_sRGB_SUPPORTED
+# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
/* If no sRGB support, don't try storing sRGB information */
png_icc_set_sRGB(png_ptr, colorspace, profile, 0);
# endif
@@ -2258,80 +2337,149 @@ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
return 0;
}
#endif /* iCCP */
+#endif /* COLORSPACE */
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-void /* PRIVATE */
-png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
+png_alloc_size_t /* PRIVATE */
+png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth,
+ png_uint_32 row_width)
{
- /* Set the rgb_to_gray coefficients from the colorspace. */
- if ((png_ptr->colorspace.flags & PNG_COLORSPACE_RGB_TO_GRAY_SET) == 0 &&
- (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+ png_alloc_size_t rowbytes = row_width;
+
+ /* Carefully calculate the row buffer size. */
+ if (pixel_depth > 8)
{
- /* png_set_background has not been called, get the coefficients from the Y
- * values of the colorspace colorants.
+ if ((pixel_depth & 7) != 0)
+ png_error(png_ptr, "unsupported pixel byte size");
+
+ pixel_depth >>= 3; /* Now in bytes */
+
+ if (rowbytes > PNG_SIZE_MAX/pixel_depth)
+ png_error(png_ptr, "image row exceeds system limits");
+
+ rowbytes *= pixel_depth;
+ }
+
+ else /* Less than 1 byte per pixel */ switch (pixel_depth)
+ {
+ case 1: rowbytes += 7; rowbytes >>= 3; break;
+ case 2: rowbytes += 3; rowbytes >>= 2; break;
+ case 4: rowbytes += 1; rowbytes >>= 1; break;
+ case 8: break;
+ default:
+ png_error(png_ptr, "unsupported pixel bit size");
+ }
+
+ return rowbytes;
+}
+
+unsigned int /*PRIVATE*/
+png_max_pixel_block(png_const_structrp png_ptr)
+{
+ /* Need the *smallest* pixel size that must occur in alignment units. On
+ * read this is the PNG pixel depth because the read transforms cannot reduce
+ * the pixel size below the input size or 8-bits, whichever is smaller.
+ *
+ * On write the 'pack' transform can pack 8-bit pixels back to a lower bit
+ * depth, but the lowest bit depth is still given by the PNG pixel size.
+ */
+ const unsigned int pixel_depth = PNG_PIXEL_DEPTH(*png_ptr);
+ const unsigned int pixel_block = /* count of pixels in a block */
+ pixel_depth < 8U ?
+ PNG_ROW_BUFFER_BYTE_ALIGN * (8U/pixel_depth) :
+ PNG_ROW_BUFFER_BYTE_ALIGN; /* pixels may be any whole byte size */
+ /* The maximum block size in bits is MAX_PIXEL_DEPTH*pixel_block so work out
+ * the minimum number of pixel blocks that can fit in PNG_ROW_BUFFER_SIZE
+ * bytes and use this to calculate the maximum number of pixels:
+ */
+ return pixel_block *
+ ((8U*PNG_ROW_BUFFER_SIZE) / (png_ptr->row_max_pixel_depth*pixel_block));
+}
+
+void /* PRIVATE */
+png_copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
+ png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/,
+ unsigned int pixel_depth, int clear/*clear the final byte*/, int x_in_dest)
+ /* Copy the row in row_buffer; this is the non-interlaced copy used in both
+ * the read and write code. 'x_in_dest' specifies whether the 'x' applies to
+ * the destination (sp->dp[x], x_in_dest tru) or the source (sp[x]->dp,
+ * x_in_dest false).
+ */
+{
+ png_alloc_size_t cb, offset;
+ unsigned int remaining; /* remaining bits in a partial byte */
+
+ /* Copy 'cb' pixels, but take care with the last byte because it may
+ * be partially written. 'x' must correspond to the start of a byte, check
+ * that too:
+ */
+ switch (pixel_depth)
+ {
+ case 1U: remaining = width & 7U;
+ debug((x & 7U) == 0U);
+ cb = width >> 3;
+ offset = x >> 3;
+ break;
+ case 2U: remaining = (width << 1) & 6U;
+ debug((x & 3U) == 0U);
+ cb = width >> 2;
+ offset = x >> 2;
+ break;
+ case 4U: remaining = (width << 2) & 4U;
+ debug((x & 1U) == 0U);
+ cb = width >> 1;
+ offset = x >> 1;
+ break;
+ case 8U: remaining = 0U;
+ cb = width;
+ offset = x;
+ break;
+ default: remaining = 0U;
+ cb = png_calc_rowbytes(png_ptr, pixel_depth, width);
+ offset = png_calc_rowbytes(png_ptr, pixel_depth, x);
+ break;
+ }
+
+ if (x_in_dest)
+ dp += offset;
+
+ else
+ sp += offset;
+
+ memcpy(dp, sp, cb);
+
+ if (remaining > 0U)
+ {
+ /* 'remaining' is the number of bits still to be copied. Format may be
+ * little endian; bits to copy in the bottom of 's'. Make 'remaining'
+ * into a mask of the bits to *preserve* in dp.
*/
- png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y;
- png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y;
- png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y;
- png_fixed_point total = r+g+b;
-
- if (total > 0 &&
- r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&
- g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&
- b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 &&
- r+g+b <= 32769)
- {
- /* We allow 0 coefficients here. r+g+b may be 32769 if two or
- * all of the coefficients were rounded up. Handle this by
- * reducing the *largest* coefficient by 1; this matches the
- * approach used for the default coefficients in pngrtran.c
- */
- int add = 0;
-
- if (r+g+b > 32768)
- add = -1;
- else if (r+g+b < 32768)
- add = 1;
-
- if (add != 0)
- {
- if (g >= r && g >= b)
- g += add;
- else if (r >= g && r >= b)
- r += add;
- else
- b += add;
- }
-
- /* Check for an internal error. */
- if (r+g+b != 32768)
- png_error(png_ptr,
- "internal error handling cHRM coefficients");
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) == 0U)
+ remaining = 0xffU >> remaining;
else
- {
- png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r;
- png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g;
- }
- }
+# endif /* TRANSFORM_MECH */
+ remaining = 0xffU << remaining;
- /* This is a png_error at present even though it could be ignored -
- * it should never happen, but it is important that if it does, the
- * bug is fixed.
+ /* remaining is now the bits to *keep* from the destination byte, if
+ * 'clear' is true the source bytes aren't copied - this is for security
+ * reasons to avoid copying undefined bits at the end of a row. If
+ * 'clear' is set the destination bits are not preserved, they are just
+ * set to 0.
*/
+ if (clear)
+ dp[cb] = PNG_BYTE(sp[cb] & ~remaining);
+
else
- png_error(png_ptr, "internal error handling cHRM->XYZ");
+ dp[cb] = PNG_BYTE((sp[cb] & ~remaining) | (dp[cb] & remaining));
}
}
-#endif
-
-#endif /* COLORSPACE */
void /* PRIVATE */
png_check_IHDR(png_const_structrp png_ptr,
- png_uint_32 width, png_uint_32 height, int bit_depth,
- int color_type, int interlace_type, int compression_type,
- int filter_type)
+ png_uint_32 width, png_uint_32 height, int bit_depth,
+ int color_type, int interlace_type, int compression_type,
+ int filter_type)
{
int error = 0;
@@ -2341,46 +2489,52 @@ png_check_IHDR(png_const_structrp png_ptr,
png_warning(png_ptr, "Image width is zero in IHDR");
error = 1;
}
- else if (width > PNG_UINT_31_MAX)
+
+ if (width > PNG_UINT_31_MAX)
{
png_warning(png_ptr, "Invalid image width in IHDR");
error = 1;
}
- else
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ if (width > png_ptr->user_width_max)
{
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
- if (width > png_ptr->user_width_max)
-# else
- if (width > PNG_USER_WIDTH_MAX)
-# endif
- {
- png_warning(png_ptr, "Image width exceeds user limit in IHDR");
- error = 1;
- }
+ png_warning(png_ptr, "Image width exceeds user limit in IHDR");
+ error = 1;
}
+#else
+ if (width > PNG_USER_WIDTH_MAX)
+ {
+ png_warning(png_ptr, "Image width exceeds WIDTH_MAX in IHDR");
+ error = 1;
+ }
+#endif
if (height == 0)
{
png_warning(png_ptr, "Image height is zero in IHDR");
error = 1;
}
- else if (height > PNG_UINT_31_MAX)
+
+ if (height > PNG_UINT_31_MAX)
{
png_warning(png_ptr, "Invalid image height in IHDR");
error = 1;
}
- else
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ if (height > png_ptr->user_height_max)
{
-# ifdef PNG_SET_USER_LIMITS_SUPPORTED
- if (height > png_ptr->user_height_max)
-# else
- if (height > PNG_USER_HEIGHT_MAX)
-# endif
- {
- png_warning(png_ptr, "Image height exceeds user limit in IHDR");
- error = 1;
- }
+ png_warning(png_ptr, "Image height exceeds user limit in IHDR");
+ error = 1;
}
+#else
+ if (height > PNG_USER_HEIGHT_MAX)
+ {
+ png_warning(png_ptr, "Image height exceeds HEIGHT_MAX in IHDR");
+ error = 1;
+ }
+#endif
/* Check other values */
if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
@@ -2418,7 +2572,7 @@ png_check_IHDR(png_const_structrp png_ptr,
error = 1;
}
-# ifdef PNG_MNG_FEATURES_SUPPORTED
+#ifdef PNG_MNG_FEATURES_SUPPORTED
/* Accept filter_method 64 (intrapixel differencing) only if
* 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
* 2. Libpng did not read a PNG signature (this filter_method is only
@@ -2428,39 +2582,46 @@ png_check_IHDR(png_const_structrp png_ptr,
* 4. The filter_method is 64 and
* 5. The color_type is RGB or RGBA
*/
- if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) &&
- png_ptr->mng_features_permitted)
+ if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 &&
+ png_ptr->mng_features_permitted != 0)
png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
if (filter_type != PNG_FILTER_TYPE_BASE)
{
- if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
(filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
(color_type == PNG_COLOR_TYPE_RGB ||
- color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
{
- png_warning(png_ptr, "Unknown filter method in IHDR");
+ png_warning(png_ptr, "Invalid filter method in IHDR");
error = 1;
}
- if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE)
+ if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0)
{
png_warning(png_ptr, "Invalid filter method in IHDR");
error = 1;
}
}
-# else
+#else /* !MNG_FEATURES */
if (filter_type != PNG_FILTER_TYPE_BASE)
{
png_warning(png_ptr, "Unknown filter method in IHDR");
error = 1;
}
-# endif
+#endif /* !MNG_FEATURES */
if (error == 1)
png_error(png_ptr, "Invalid IHDR data");
+
+ /* Finally, if the IHDR data is correct, check it against the system
+ * limits (NOTE: this need not be done; the IDAT handling code repeats the
+ * check in both read and write.)
+ */
+ (void)png_calc_rowbytes(png_ptr,
+ PNG_COLOR_TYPE_CHANNELS(color_type) * bit_depth, width);
}
#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
@@ -2474,7 +2635,7 @@ png_check_IHDR(png_const_structrp png_ptr,
int /* PRIVATE */
png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
- png_size_tp whereami)
+ png_size_tp whereami)
{
int state = *statep;
png_size_t i = *whereami;
@@ -2504,7 +2665,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY))
{
case PNG_FP_INTEGER + PNG_FP_SAW_SIGN:
- if (state & PNG_FP_SAW_ANY)
+ if ((state & PNG_FP_SAW_ANY) != 0)
goto PNG_FP_End; /* not a part of the number */
png_fp_add(state, type);
@@ -2512,10 +2673,10 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
case PNG_FP_INTEGER + PNG_FP_SAW_DOT:
/* Ok as trailer, ok as lead of fraction. */
- if (state & PNG_FP_SAW_DOT) /* two dots */
+ if ((state & PNG_FP_SAW_DOT) != 0) /* two dots */
goto PNG_FP_End;
- else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */
+ else if ((state & PNG_FP_SAW_DIGIT) != 0) /* trailing dot? */
png_fp_add(state, type);
else
@@ -2524,7 +2685,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
break;
case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT:
- if (state & PNG_FP_SAW_DOT) /* delayed fraction */
+ if ((state & PNG_FP_SAW_DOT) != 0) /* delayed fraction */
png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
png_fp_add(state, type | PNG_FP_WAS_VALID);
@@ -2562,7 +2723,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
break;
case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN:
- if (state & PNG_FP_SAW_ANY)
+ if ((state & PNG_FP_SAW_ANY) != 0)
goto PNG_FP_End; /* not a part of the number */
png_fp_add(state, PNG_FP_SAW_SIGN);
@@ -2605,13 +2766,13 @@ png_check_fp_string(png_const_charp string, png_size_t size)
int state=0;
png_size_t char_index=0;
- if (png_check_fp_number(string, size, &state, &char_index) &&
+ if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
(char_index == size || string[char_index] == 0))
return state /* must be non-zero - see above */;
return 0; /* i.e. fail */
}
-#endif /* pCAL or sCAL */
+#endif /* pCAL || sCAL */
#ifdef PNG_sCAL_SUPPORTED
# ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -2630,7 +2791,7 @@ png_pow10(int power)
if (power < 0)
{
if (power < DBL_MIN_10_EXP) return 0;
- recip = 1, power = -power;
+ recip = 1; power = -power;
}
if (power > 0)
@@ -2683,7 +2844,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
if (fp >= DBL_MIN && fp <= DBL_MAX)
{
- int exp_b10; /* A base 10 exponent */
+ int exp_b10; /* A base 10 exponent */
double base; /* 10^exp_b10 */
/* First extract a base 10 exponent of the number,
@@ -2708,7 +2869,9 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
double test = png_pow10(exp_b10+1);
if (test <= DBL_MAX)
- ++exp_b10, base = test;
+ {
+ ++exp_b10; base = test;
+ }
else
break;
@@ -2722,7 +2885,10 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
* test on DBL_MAX above.
*/
fp /= base;
- while (fp >= 1) fp /= 10, ++exp_b10;
+ while (fp >= 1)
+ {
+ fp /= 10; ++exp_b10;
+ }
/* Because of the code above fp may, at this point, be
* less than .1, this is ok because the code below can
@@ -2731,7 +2897,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
*/
{
- int czero, clead, cdigits;
+ unsigned int czero, clead, cdigits;
char exponent[10];
/* Allow up to two leading zeros - this will not lengthen
@@ -2761,7 +2927,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
* of the loop don't break the number into parts so
* that the final digit is rounded.
*/
- if (cdigits+czero-clead+1 < (int)precision)
+ if (cdigits+czero+1 < precision+clead)
fp = modf(fp, &d);
else
@@ -2773,7 +2939,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
/* Rounding up to 10, handle that here. */
if (czero > 0)
{
- --czero, d = 1;
+ --czero; d = 1;
if (cdigits == 0) --clead;
}
else
@@ -2787,7 +2953,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
else if (ch == 46)
{
- ch = *--ascii, ++size;
+ ch = *--ascii; ++size;
/* Advance exp_b10 to '1', so that the
* decimal point happens after the
* previous digit.
@@ -2814,7 +2980,9 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
int ch = *--ascii;
if (ch == 46)
- ++size, exp_b10 = 1;
+ {
+ ++size; exp_b10 = 1;
+ }
/* Else lost a leading zero, so 'exp_b10' is
* still ok at (-1)
@@ -2859,21 +3027,22 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
if (exp_b10 != (-1))
{
- if (exp_b10 == 0) *ascii++ = 46, --size; /* counted
- above */
+ if (exp_b10 == 0)
+ *ascii++ = 46, --size; /* counted above */
+
--exp_b10;
}
- *ascii++ = (char)(48 + (int)d), ++cdigits;
+ *ascii++ = png_check_char(png_ptr, 48 + (int)d), ++cdigits;
}
}
- while (cdigits+czero-clead < (int)precision && fp > DBL_MIN);
+ while (cdigits+czero < precision+clead && fp > DBL_MIN);
/* The total output count (max) is now 4+precision */
/* Check for an exponent, if we don't need one we are
* done and just need to terminate the string. At
* this point exp_b10==(-1) is effectively if flag - it got
- * to '-1' because of the decrement after outputing
+ * to '-1' because of the decrement after outputting
* the decimal point above (the exponent required is
* *not* -1!)
*/
@@ -2881,7 +3050,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
{
/* The following only happens if we didn't output the
* leading zeros above for negative exponent, so this
- * doest add to the digit requirement. Note that the
+ * doesn't add to the digit requirement. Note that the
* two zeros here can only be output if the two leading
* zeros were *not* output, so this doesn't increase
* the output count.
@@ -2904,7 +3073,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
*/
size -= cdigits;
- *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision */
+ *ascii++ = 69; --size; /* 'E': PLUS 1 TOTAL 2+precision */
/* The following use of an unsigned temporary avoids ambiguities in
* the signed arithmetic on exp_b10 and permits GCC at least to do
@@ -2926,7 +3095,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
while (uexp_b10 > 0)
{
- exponent[cdigits++] = (char)(48 + uexp_b10 % 10);
+ exponent[cdigits++] = (char)/*SAFE*/(48 + uexp_b10 % 10);
uexp_b10 /= 10;
}
}
@@ -2934,7 +3103,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
/* Need another size check here for the exponent digits, so
* this need not be considered above.
*/
- if ((int)size > cdigits)
+ if (size > cdigits)
{
while (cdigits > 0) *ascii++ = exponent[--cdigits];
@@ -2996,7 +3165,7 @@ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
/* Split the low digit off num: */
unsigned int tmp = num/10;
num -= tmp*10;
- digits[ndigits++] = (char)(48 + num);
+ digits[ndigits++] = png_check_char(png_ptr, 48 + num);
/* Record the first non-zero digit, note that this is a number
* starting at 1, it's not actually the array index.
*/
@@ -3020,7 +3189,10 @@ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
* then ndigits digits to first:
*/
i = 5;
- while (ndigits < i) *ascii++ = 48, --i;
+ while (ndigits < i)
+ {
+ *ascii++ = 48; --i;
+ }
while (ndigits >= first) *ascii++ = digits[--ndigits];
/* Don't output the trailing zeros! */
}
@@ -3180,7 +3352,8 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
result = -result;
/* Check for overflow. */
- if ((negative && result <= 0) || (!negative && result >= 0))
+ if ((negative != 0 && result <= 0) ||
+ (negative == 0 && result >= 0))
{
*res = result;
return 1;
@@ -3192,969 +3365,140 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
return 0;
}
-#endif /* READ_GAMMA || INCH_CONVERSIONS */
+#endif /* GAMMA || INCH_CONVERSIONS */
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* The following is for when the caller doesn't much care about the
- * result.
- */
-png_fixed_point
-png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,
- png_int_32 divisor)
+/* SOFTWARE SETTING SUPPORT */
+png_int_32 PNGAPI
+png_setting(png_structrp png_ptr, png_uint_32 setting, png_uint_32 parameter,
+ png_int_32 value)
{
- png_fixed_point result;
+ png_int_32 result;
- if (png_muldiv(&result, a, times, divisor))
- return result;
-
- png_warning(png_ptr, "fixed point overflow ignored");
- return 0;
-}
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */
-/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
-png_fixed_point
-png_reciprocal(png_fixed_point a)
-{
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
- double r = floor(1E10/a+.5);
-
- if (r <= 2147483647. && r >= -2147483648.)
- return (png_fixed_point)r;
-#else
- png_fixed_point res;
-
- if (png_muldiv(&res, 100000, 100000, a))
- return res;
-#endif
-
- return 0; /* error/overflow */
-}
-
-/* This is the shared test on whether a gamma value is 'significant' - whether
- * it is worth doing gamma correction.
- */
-int /* PRIVATE */
-png_gamma_significant(png_fixed_point gamma_val)
-{
- return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||
- gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
-}
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* A local convenience routine. */
-#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
-static png_fixed_point
-png_product2(png_fixed_point a, png_fixed_point b)
-{
- /* The required result is 1/a * 1/b; the following preserves accuracy. */
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
- double r = a * 1E-5;
- r *= b;
- r = floor(r+.5);
-
- if (r <= 2147483647. && r >= -2147483648.)
- return (png_fixed_point)r;
-#else
- png_fixed_point res;
-
- if (png_muldiv(&res, a, b, 100000))
- return res;
-#endif
-
- return 0; /* overflow */
-}
-#endif
-
-/* The inverse of the above. */
-png_fixed_point
-png_reciprocal2(png_fixed_point a, png_fixed_point b)
-{
- /* The required result is 1/a * 1/b; the following preserves accuracy. */
-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
- double r = 1E15/a;
- r /= b;
- r = floor(r+.5);
-
- if (r <= 2147483647. && r >= -2147483648.)
- return (png_fixed_point)r;
-#else
- /* This may overflow because the range of png_fixed_point isn't symmetric,
- * but this API is only used for the product of file and screen gamma so it
- * doesn't matter that the smallest number it can produce is 1/21474, not
- * 1/100000
- */
- png_fixed_point res = png_product2(a, b);
-
- if (res != 0)
- return png_reciprocal(res);
-#endif
-
- return 0; /* overflow */
-}
-#endif /* READ_GAMMA */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */
-#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
-/* Fixed point gamma.
- *
- * The code to calculate the tables used below can be found in the shell script
- * contrib/tools/intgamma.sh
- *
- * To calculate gamma this code implements fast log() and exp() calls using only
- * fixed point arithmetic. This code has sufficient precision for either 8-bit
- * or 16-bit sample values.
- *
- * The tables used here were calculated using simple 'bc' programs, but C double
- * precision floating point arithmetic would work fine.
- *
- * 8-bit log table
- * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
- * 255, so it's the base 2 logarithm of a normalized 8-bit floating point
- * mantissa. The numbers are 32-bit fractions.
- */
-static const png_uint_32
-png_8bit_l2[128] =
-{
- 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
- 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
- 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
- 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
- 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
- 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
- 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
- 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
- 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
- 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
- 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
- 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
- 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
- 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
- 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
- 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
- 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
- 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
- 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
- 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
- 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
- 24347096U, 0U
-
-#if 0
- /* The following are the values for 16-bit tables - these work fine for the
- * 8-bit conversions but produce very slightly larger errors in the 16-bit
- * log (about 1.2 as opposed to 0.7 absolute error in the final value). To
- * use these all the shifts below must be adjusted appropriately.
- */
- 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
- 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
- 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
- 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
- 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
- 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
- 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
- 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
- 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
- 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
- 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
- 1119, 744, 372
-#endif
-};
-
-static png_int_32
-png_log8bit(unsigned int x)
-{
- unsigned int lg2 = 0;
- /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
- * because the log is actually negate that means adding 1. The final
- * returned value thus has the range 0 (for 255 input) to 7.994 (for 1
- * input), return -1 for the overflow (log 0) case, - so the result is
- * always at most 19 bits.
- */
- if ((x &= 0xff) == 0)
- return -1;
-
- if ((x & 0xf0) == 0)
- lg2 = 4, x <<= 4;
-
- if ((x & 0xc0) == 0)
- lg2 += 2, x <<= 2;
-
- if ((x & 0x80) == 0)
- lg2 += 1, x <<= 1;
-
- /* result is at most 19 bits, so this cast is safe: */
- return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));
-}
-
-/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
- * for 16-bit images we use the most significant 8 bits of the 16-bit value to
- * get an approximation then multiply the approximation by a correction factor
- * determined by the remaining up to 8 bits. This requires an additional step
- * in the 16-bit case.
- *
- * We want log2(value/65535), we have log2(v'/255), where:
- *
- * value = v' * 256 + v''
- * = v' * f
- *
- * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
- * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
- * than 258. The final factor also needs to correct for the fact that our 8-bit
- * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
- *
- * This gives a final formula using a calculated value 'x' which is value/v' and
- * scaling by 65536 to match the above table:
- *
- * log2(x/257) * 65536
- *
- * Since these numbers are so close to '1' we can use simple linear
- * interpolation between the two end values 256/257 (result -368.61) and 258/257
- * (result 367.179). The values used below are scaled by a further 64 to give
- * 16-bit precision in the interpolation:
- *
- * Start (256): -23591
- * Zero (257): 0
- * End (258): 23499
- */
-static png_int_32
-png_log16bit(png_uint_32 x)
-{
- unsigned int lg2 = 0;
-
- /* As above, but now the input has 16 bits. */
- if ((x &= 0xffff) == 0)
- return -1;
-
- if ((x & 0xff00) == 0)
- lg2 = 8, x <<= 8;
-
- if ((x & 0xf000) == 0)
- lg2 += 4, x <<= 4;
-
- if ((x & 0xc000) == 0)
- lg2 += 2, x <<= 2;
-
- if ((x & 0x8000) == 0)
- lg2 += 1, x <<= 1;
-
- /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
- * value.
- */
- lg2 <<= 28;
- lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4;
-
- /* Now we need to interpolate the factor, this requires a division by the top
- * 8 bits. Do this with maximum precision.
- */
- x = ((x << 16) + (x >> 9)) / (x >> 8);
-
- /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
- * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly
- * 16 bits to interpolate to get the low bits of the result. Round the
- * answer. Note that the end point values are scaled by 64 to retain overall
- * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust
- * the overall scaling by 6-12. Round at every step.
- */
- x -= 1U << 24;
-
- if (x <= 65536U) /* <= '257' */
- lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12);
-
- else
- lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
-
- /* Safe, because the result can't have more than 20 bits: */
- return (png_int_32)((lg2 + 2048) >> 12);
-}
-
-/* The 'exp()' case must invert the above, taking a 20-bit fixed point
- * logarithmic value and returning a 16 or 8-bit number as appropriate. In
- * each case only the low 16 bits are relevant - the fraction - since the
- * integer bits (the top 4) simply determine a shift.
- *
- * The worst case is the 16-bit distinction between 65535 and 65534, this
- * requires perhaps spurious accuracty in the decoding of the logarithm to
- * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance
- * of getting this accuracy in practice.
- *
- * To deal with this the following exp() function works out the exponent of the
- * frational part of the logarithm by using an accurate 32-bit value from the
- * top four fractional bits then multiplying in the remaining bits.
- */
-static const png_uint_32
-png_32bit_exp[16] =
-{
- /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
- 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
- 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
- 2553802834U, 2445529972U, 2341847524U, 2242560872U
-};
-
-/* Adjustment table; provided to explain the numbers in the code below. */
-#if 0
-for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
- 11 44937.64284865548751208448
- 10 45180.98734845585101160448
- 9 45303.31936980687359311872
- 8 45364.65110595323018870784
- 7 45395.35850361789624614912
- 6 45410.72259715102037508096
- 5 45418.40724413220722311168
- 4 45422.25021786898173001728
- 3 45424.17186732298419044352
- 2 45425.13273269940811464704
- 1 45425.61317555035558641664
- 0 45425.85339951654943850496
-#endif
-
-static png_uint_32
-png_exp(png_fixed_point x)
-{
- if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
+ if (png_ptr != NULL)
{
- /* Obtain a 4-bit approximation */
- png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];
+ int handle_error = (setting & PNG_SF_ERROR) != 0U;
- /* Incorporate the low 12 bits - these decrease the returned value by
- * multiplying by a number less than 1 if the bit is set. The multiplier
- * is determined by the above table and the shift. Notice that the values
- * converge on 45426 and this is used to allow linear interpolation of the
- * low bits.
+ setting &= ~PNG_SF_ERROR; /* because it is handled below. */
+
+ switch (setting & (PNG_SF_READ|PNG_SF_WRITE))
+ {
+# ifdef PNG_READ_SUPPORTED
+ case PNG_SF_READ:
+ if (png_ptr->read_struct)
+ result = png_read_setting(png_ptr, setting, parameter, value);
+
+ else
+ result = PNG_EINVAL; /* read setting on write struct */
+ break;
+# endif /* READ */
+
+# ifdef PNG_WRITE_SUPPORTED
+ case PNG_SF_WRITE:
+ if (!png_ptr->read_struct)
+ result = png_write_setting(png_ptr, setting, parameter,
+ value);
+
+ else
+ result = PNG_EINVAL; /* write setting on read struct */
+ break;
+# endif /* WRITE */
+
+ default:
+ /* Handle everything else here. This includes the error of not
+ * having either read or write set; that error will cause a
+ * PNG_ENOSYS return code.
+ */
+ switch (setting)
+ {
+# ifdef PNG_SET_OPTION_SUPPORTED
+ case PNG_SRW_OPTION:
+ if (parameter >= PNG_OPTION_NEXT)
+ return PNG_OPTION_INVALID;
+
+ if (parameter == PNG_SKIP_sRGB_CHECK_PROFILE)
+ {
+ if (png_ptr->skip_sRGB_profile_check)
+ {
+ if (!value)
+ png_ptr->skip_sRGB_profile_check = 0U;
+ result = PNG_OPTION_ON;
+ }
+
+ else
+ {
+ if (value)
+ png_ptr->skip_sRGB_profile_check = 1U;
+ result = PNG_OPTION_OFF;
+ }
+
+ break;
+ }
+
+# ifdef PNG_READ_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ result = png_read_setting(png_ptr, setting,
+ parameter, value);
+ break;
+ }
+# endif /* READ */
+
+ /* No write options at present */
+ result = PNG_OPTION_UNSET; /* i.e. ignore it */
+ break;
+# endif /* SET_OPTION */
+
+ default:
+ /* Any other option; handle in the appropriate setting: */
+# ifdef PNG_READ_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ result = png_read_setting(png_ptr, setting,
+ parameter, value);
+ break;
+ }
+# endif /* READ */
+
+# ifdef PNG_WRITE_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ result = png_write_setting(png_ptr, setting,
+ parameter, value);
+ break;
+ }
+# endif /* WRITE */
+
+ NOT_REACHED;
+ result= PNG_ENOSYS;
+ break;
+ }
+ break;
+ } /* switch */
+
+ /* Handle error returns here.
+ * TODO: this is crude, should use a formatted warning style message and
+ * output result/setting/parameter/value.
*/
- if (x & 0x800)
- e -= (((e >> 16) * 44938U) + 16U) >> 5;
-
- if (x & 0x400)
- e -= (((e >> 16) * 45181U) + 32U) >> 6;
-
- if (x & 0x200)
- e -= (((e >> 16) * 45303U) + 64U) >> 7;
-
- if (x & 0x100)
- e -= (((e >> 16) * 45365U) + 128U) >> 8;
-
- if (x & 0x080)
- e -= (((e >> 16) * 45395U) + 256U) >> 9;
-
- if (x & 0x040)
- e -= (((e >> 16) * 45410U) + 512U) >> 10;
-
- /* And handle the low 6 bits in a single block. */
- e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
-
- /* Handle the upper bits of x. */
- e >>= x >> 16;
- return e;
+ if (handle_error && PNG_FAILED(result))
+ png_error(png_ptr, "png_setting");
}
- /* Check for overflow */
- if (x <= 0)
- return png_32bit_exp[0];
+ else /* png_ptr is NULL */
+ result = PNG_EINVAL;
- /* Else underflow */
- return 0;
+ return result;
+ PNG_UNUSED(parameter)
+ PNG_UNUSED(value)
}
-static png_byte
-png_exp8bit(png_fixed_point lg2)
-{
- /* Get a 32-bit value: */
- png_uint_32 x = png_exp(lg2);
-
- /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the
- * second, rounding, step can't overflow because of the first, subtraction,
- * step.
- */
- x -= x >> 8;
- return (png_byte)((x + 0x7fffffU) >> 24);
-}
-
-static png_uint_16
-png_exp16bit(png_fixed_point lg2)
-{
- /* Get a 32-bit value: */
- png_uint_32 x = png_exp(lg2);
-
- /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
- x -= x >> 16;
- return (png_uint_16)((x + 32767U) >> 16);
-}
-#endif /* FLOATING_ARITHMETIC */
-
-png_byte
-png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
-{
- if (value > 0 && value < 255)
- {
-# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
- double r = floor(255*pow(value/255.,gamma_val*.00001)+.5);
- return (png_byte)r;
-# else
- png_int_32 lg2 = png_log8bit(value);
- png_fixed_point res;
-
- if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
- return png_exp8bit(res);
-
- /* Overflow. */
- value = 0;
-# endif
- }
-
- return (png_byte)value;
-}
-
-png_uint_16
-png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
-{
- if (value > 0 && value < 65535)
- {
-# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
- double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5);
- return (png_uint_16)r;
-# else
- png_int_32 lg2 = png_log16bit(value);
- png_fixed_point res;
-
- if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
- return png_exp16bit(res);
-
- /* Overflow. */
- value = 0;
-# endif
- }
-
- return (png_uint_16)value;
-}
-
-/* This does the right thing based on the bit_depth field of the
- * png_struct, interpreting values as 8-bit or 16-bit. While the result
- * is nominally a 16-bit value if bit depth is 8 then the result is
- * 8-bit (as are the arguments.)
- */
-png_uint_16 /* PRIVATE */
-png_gamma_correct(png_structrp png_ptr, unsigned int value,
- png_fixed_point gamma_val)
-{
- if (png_ptr->bit_depth == 8)
- return png_gamma_8bit_correct(value, gamma_val);
-
- else
- return png_gamma_16bit_correct(value, gamma_val);
-}
-
-#define PNG_GAMMA_TABLE_8 0 /* 8-bit entries in png_byte */
-#define PNG_GAMMA_TABLE_8_IN_16 1 /* 8-bit entries * 257 in png_uint_16 */
-#define PNG_GAMMA_TABLE_16 2 /* 16-bit entries in png_uint_16 */
-
-typedef struct
-{
- png_fixed_point gamma;
- png_uint_32 mult;
- unsigned int add;
- unsigned int shift; /* input value is (i * mult + add) >> shift */
- int output; /* One of the above values */
- int adjust; /* Divide or multiple output by 257 */
- png_voidp table; /* Lookup table */
-} gamma_table_data;
-
-static unsigned int
-write_gamma_table_entry(const gamma_table_data *data, png_uint_32 i)
- /* Calculate and write a single entry into table[i], the value of the entry
- * written is returned.
- */
-{
- png_uint_32 in = (i * data->mult + data->add) >> data->shift;
- unsigned int out;
-
- /* If the output is TABLE_8 with no adjust, or the output is not with an
- * adjust, use 8-bit correction.
- */
- if ((data->output == PNG_GAMMA_TABLE_8) != (data->adjust != 0))
- {
- out = png_gamma_8bit_correct((unsigned int)in, data->gamma);
-
- if (data->adjust != 0)
- out *= 257U;
- }
-
- else /* 16-bit correction */
- {
- out = png_gamma_16bit_correct((unsigned int)in, data->gamma);
-
- if (data->adjust != 0)
- out = PNG_DIV257(out);
- }
-
- if (data->output == PNG_GAMMA_TABLE_8)
- ((png_bytep)data->table)[i] = (png_byte)out;
-
- else
- ((png_uint_16p)data->table)[i] = (png_uint_16)out;
-
- return out;
-}
-
-static void
-write_gamma_table(const gamma_table_data *data, png_uint_32 lo,
- unsigned int loval, png_uint_32 hi, unsigned int hival)
- /* Fill in gamma table entries between lo and hi, exclusive. The entries at
- * table[lo] and table[hi] have already been written, the intervening entries
- * are written.
- */
-{
- if (hi > lo+1) /* Else nothing to fill in */
- {
- if (hival == loval)
- {
- /* All intervening entries must be the same. */
- if (data->output == PNG_GAMMA_TABLE_8)
- {
- png_bytep table8 = ((png_bytep)data->table);
-
- while (++lo < hi)
- table8[lo] = (png_byte)loval;
- }
-
- else
- {
- png_uint_16p table16 = ((png_uint_16p)data->table);
-
- while (++lo < hi)
- table16[lo] = (png_uint_16)loval;
- }
- }
-
- else
- {
- png_uint_32 mid = (lo+hi) >> 1;
- unsigned int midval = write_gamma_table_entry(data, mid);
-
- /* The algorithm used is to divide the entries to be written in half
- * and fill in the middle. For all practical tables with significant
- * gamma this will result in a performance gain because the expensive
- * gamma correction arithmetic is avoided for some entries.
- */
- write_gamma_table(data, lo, loval, mid, midval);
- write_gamma_table(data, mid, midval, hi, hival);
- }
- }
-}
-
-static void *
-png_build_gamma_table(png_structrp png_ptr, png_fixed_point gamma_val,
- int output/*as above*/, int input_depth, int use_shift)
- /* Build a gamma lookup table to encode input_depth bit input values.
- * The table will have 2^input_depth entries plus an extra one if use_shift
- * is specified. With shift the table is accessed:
- *
- * table[(original-value + rounding) >> shift]
- *
- * And an extra entry exists to accomodate overflow of original-value on
- * rounding. If use_shift is not specified the table is accessed with an
- * input_depth bit value and the original values must have been correctly
- * scaled to this range (not using a shift!)
- *
- * Each table entry contains input-value^gamma_val rounded to the output
- * precision. This is 8 bit precision unless output is specified as
- * PNG_GAMMA_TABLE_16, in which case it is 16-bit precision. For
- * PNG_GAMMA_TABLE_8_IN_16 the 8-bit value is scaled to 16-bits by
- * multiplying by 257.
- */
-{
- png_uint_32 size;
- unsigned int hival;
- gamma_table_data data;
-
- /* If use_shift is true or if the input or output is not 8-bit the gamma
- * correction will use the 16-bit correction code. This requires a value in
- * the range 0..65535. For use_shift the value is simply:
- *
- * input << shift
- *
- * For the scaling case the value is:
- *
- * round(input * 65535 / ((1<> shift;
- *
- * With 'mult' and 'add' chosen to minimize the error for all input values
- * in the range 0..((1< PNG_GAMMA_TABLE_8;
-
- if (use_shift != 0)
- {
- /* The multiplier does the shift: */
- data.mult = 1U << (8-input_depth);
- data.add = 0;
- data.shift = 0;
- if (input_depth < 8) ++size;
- }
-
- else
- {
- data.mult = multadd255[input_depth-1].mult;
- data.add = multadd255[input_depth-1].add;
- data.shift = multadd255[input_depth-1].shift;
- }
- }
-
- else
- {
- /* 16-bit correction is used for cases where input or output require more
- * than 8 bits.
- */
- data.adjust = output == PNG_GAMMA_TABLE_8;
-
- if (use_shift != 0)
- {
- data.mult = 1U << (16-input_depth);
- data.add = 0;
- data.shift = 0;
- if (input_depth < 16) ++size;
- }
-
- else
- {
- data.mult = multadd65535[input_depth-1].mult;
- data.add = multadd65535[input_depth-1].add;
- data.shift = multadd65535[input_depth-1].shift;
- }
- }
-
- if (output == PNG_GAMMA_TABLE_8)
- {
- data.table = png_malloc(png_ptr, size * sizeof (png_byte));
- ((png_bytep)data.table)[0] = 0;
- hival = ((png_bytep)data.table)[size-1] = 255;
- }
-
- else
- {
- /* Output is 16 bits, although it may only have 8 bits of precision */
- data.table = png_malloc(png_ptr, size * sizeof (png_uint_16));
- ((png_uint_16p)data.table)[0] = 0;
- hival = ((png_uint_16p)data.table)[size-1] = 65535;
- }
-
- if (png_gamma_significant(gamma_val))
- write_gamma_table(&data, 0, 0, size-1, hival);
-
- else /* gamma_val not significant */
- {
- if (output == PNG_GAMMA_TABLE_8)
- {
- png_uint_32 i;
- png_bytep table8 = ((png_bytep)data.table);
-
- if (data.adjust)
- for (i=1; i>
- data.shift);
-
- else
- for (i=1; i> data.shift);
- }
-
- else
- {
- png_uint_32 i;
- png_uint_16p table16 = ((png_uint_16p)data.table);
-
- if (data.adjust)
- for (i=1; i>
- data.shift) * 257U);
-
- else
- for (i=1; i>
- data.shift);
- }
- }
-
- return data.table;
-}
-
-/* Used from png_read_destroy and below to release the memory used by the gamma
- * tables.
- */
-void /* PRIVATE */
-png_destroy_gamma_table(png_structrp png_ptr)
-{
- png_free(png_ptr, png_ptr->gamma_table);
- png_ptr->gamma_table = NULL;
-
- png_free(png_ptr, png_ptr->gamma_16_table);
- png_ptr->gamma_16_table = NULL;
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
- defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- png_free(png_ptr, png_ptr->gamma_from_1);
- png_ptr->gamma_from_1 = NULL;
- png_free(png_ptr, png_ptr->gamma_to_1);
- png_ptr->gamma_to_1 = NULL;
-
- png_free(png_ptr, png_ptr->gamma_16_from_1);
- png_ptr->gamma_16_from_1 = NULL;
- png_free(png_ptr, png_ptr->gamma_16_to_1);
- png_ptr->gamma_16_to_1 = NULL;
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-}
-
-/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit
- * tables, we don't make a full table if we are reducing to 8-bit in
- * the future. Note also how the gamma_16 tables are segmented so that
- * we don't need to allocate > 64K chunks for a full 16-bit table.
- */
-void /* PRIVATE */
-png_build_gamma_tables(png_structrp png_ptr, int bit_depth)
-{
- png_debug(1, "in png_build_gamma_table");
-
- /* Remove any existing table; this copes with multiple calls to
- * png_read_update_info. The warning is because building the gamma tables
- * multiple times is a performance hit - it's harmless but the ability to call
- * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible
- * to warn if the app introduces such a hit.
- */
- if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)
- {
- png_warning(png_ptr, "gamma table being rebuilt");
- png_destroy_gamma_table(png_ptr);
- }
-
- if (bit_depth <= 8)
- {
- png_ptr->gamma_table = png_voidcast(png_bytep, png_build_gamma_table(
- png_ptr, png_ptr->screen_gamma > 0 ?
- png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) :
- PNG_FP_1, PNG_GAMMA_TABLE_8, 8/*input depth*/, 0/*scale*/));
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
- defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))
- {
- /* This sets the accuracy of 8-bit composition and the 8-bit RGB to gray
- * conversion - PNG_MAX_GAMMA_8 (the number of bits in the sixteen bit
- * value that are considered significant.)
- */
- png_ptr->gamma_to_1 = png_voidcast(png_uint_16p, png_build_gamma_table(
- png_ptr, png_reciprocal(png_ptr->colorspace.gamma),
- PNG_GAMMA_TABLE_16, 8/*input depth*/, 0/*scale*/));
-
- png_ptr->gamma_from_1 = png_voidcast(png_bytep, png_build_gamma_table(
- png_ptr, png_ptr->screen_gamma > 0 ?
- png_reciprocal(png_ptr->screen_gamma) :
- png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */,
- PNG_GAMMA_TABLE_8, PNG_MAX_GAMMA_8/*input depth*/, 1/*shift*/));
-
- png_ptr->gamma_shift = 16-PNG_MAX_GAMMA_8;
- }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
- }
- else
- {
- png_byte shift, sig_bit;
- int table_type;
-
-# ifdef PNG_16BIT_SUPPORTED
- table_type = PNG_GAMMA_TABLE_16;
-# else
- table_type = PNG_GAMMA_TABLE_8_IN_16;
-# endif
-
- if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
- {
- sig_bit = png_ptr->sig_bit.red;
-
- if (png_ptr->sig_bit.green > sig_bit)
- sig_bit = png_ptr->sig_bit.green;
-
- if (png_ptr->sig_bit.blue > sig_bit)
- sig_bit = png_ptr->sig_bit.blue;
- }
- else
- sig_bit = png_ptr->sig_bit.gray;
-
- if (sig_bit > 0 && sig_bit < 16U)
- shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */
-
- else
- shift = 0; /* keep all 16 bits */
-
- if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8))
- {
- /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
- * the significant bits in the *input* when the output will
- * eventually be 8 bits.
- */
- if (shift < (16U - PNG_MAX_GAMMA_8))
- shift = (16U - PNG_MAX_GAMMA_8);
-
- table_type = PNG_GAMMA_TABLE_8_IN_16;
- }
-
- png_ptr->gamma_shift = shift;
-
- png_ptr->gamma_16_table = png_voidcast(png_uint_16p, png_build_gamma_table(
- png_ptr, png_ptr->screen_gamma > 0 ? png_reciprocal2(
- png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1,
- table_type, (16-shift)/*input depth*/, 1/*shift*/));
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
- defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))
- {
- png_ptr->gamma_16_to_1 = png_voidcast(png_uint_16p,
- png_build_gamma_table(png_ptr,
- png_reciprocal(png_ptr->colorspace.gamma), PNG_GAMMA_TABLE_16,
- (16-shift)/*input depth*/, 1/*shift*/));
-
- /* Notice that the '16 from 1' table should be full precision, however
- * the lookup on this table still uses gamma_shift, so it can't be.
- * TODO: fix this.
- */
- png_ptr->gamma_16_from_1 = png_voidcast(png_uint_16p,
- png_build_gamma_table(png_ptr, png_ptr->screen_gamma > 0 ?
- png_reciprocal(png_ptr->screen_gamma) :
- png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */,
- PNG_GAMMA_TABLE_16, (16-shift)/*input depth*/, 1/*shift*/));
- }
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
- }
-}
-#endif /* READ_GAMMA */
-
-/* HARDWARE OPTION SUPPORT */
-#ifdef PNG_SET_OPTION_SUPPORTED
-int PNGAPI
-png_set_option(png_structrp png_ptr, int option, int onoff)
-{
- if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT &&
- (option & 1) == 0)
- {
- int mask = 3 << option;
- int setting = (2 + (onoff != 0)) << option;
- int current = png_ptr->options;
-
- png_ptr->options = (png_byte)((current & ~mask) | setting);
-
- return (current & mask) >> option;
- }
-
- return PNG_OPTION_INVALID;
-}
-#endif
-
/* sRGB support */
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
/* sRGB conversion tables; these are machine generated with the code in
* contrib/tools/makesRGB.c. The actual sRGB transfer curve defined in the
- * specification (see the article at http://en.wikipedia.org/wiki/SRGB)
+ * specification (see the article at https://en.wikipedia.org/wiki/SRGB)
* is used, not the gamma=1/2.2 approximation use elsewhere in libpng.
- * The sRGB to linear table is exact (to the nearest 16 bit linear fraction).
+ * The sRGB to linear table is exact (to the nearest 16-bit linear fraction).
* The inverse (linear to sRGB) table has accuracies as follows:
*
* For all possible (255*65535+1) input values:
@@ -4165,7 +3509,7 @@ png_set_option(png_structrp png_ptr, int option, int onoff)
*
* error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact
*
- * In all cases the inexact readings are off by one.
+ * In all cases the inexact readings are only off by one.
*/
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
@@ -4206,7 +3550,7 @@ const png_uint_16 png_sRGB_table[256] =
61517,62082,62650,63221,63795,64372,64952,65535
};
-#endif /* simplified read only */
+#endif /* SIMPLIFIED_READ */
/* The base/delta tables are required for both read and write (but currently
* only the simplified versions.)
@@ -4334,15 +3678,15 @@ png_image_free_function(png_voidp argument)
/* First free any data held in the control structure. */
# ifdef PNG_STDIO_SUPPORTED
- if (cp->owned_file)
+ if (cp->owned_file != 0)
{
- FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr);
+ FILE *fp = png_voidcast(FILE*, png_get_io_ptr(cp->png_ptr));
cp->owned_file = 0;
/* Ignore errors here. */
if (fp != NULL)
{
- cp->png_ptr->io_ptr = NULL;
+ png_init_io(cp->png_ptr, NULL);
(void)fclose(fp);
}
}
@@ -4358,7 +3702,7 @@ png_image_free_function(png_voidp argument)
png_free(c.png_ptr, cp);
/* Then the structures, calling the correct API. */
- if (c.for_write)
+ if (c.for_write != 0)
{
# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
png_destroy_write_struct(&c.png_ptr, &c.info_ptr);
@@ -4405,5 +3749,34 @@ png_image_error(png_imagep image, png_const_charp error_message)
return 0;
}
+#ifdef PNG_STDIO_SUPPORTED
+typedef struct
+{
+ png_structrp png_ptr;
+ png_FILE_p fp;
+}
+png_image_init_io_struct;
+
+static int
+image_init_io(png_voidp display)
+{
+ png_image_init_io_struct *p =
+ png_voidcast(png_image_init_io_struct*, display);
+
+ png_init_io(p->png_ptr, p->fp);
+ return 1;
+}
+
+int /* PRIVATE */
+png_image_init_io(png_imagep image, png_FILE_p fp)
+{
+ png_image_init_io_struct s;
+
+ s.png_ptr = image->opaque->png_ptr;
+ s.fp = fp;
+
+ return png_safe_execute(image, image_init_io, &s);
+}
+#endif /* STDIO */
#endif /* SIMPLIFIED READ/WRITE */
-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+#endif /* READ || WRITE */
diff --git a/ext/libpng17/png.h b/ext/libpng17/png.h
index 775fe83a62..3feecd4c41 100644
--- a/ext/libpng17/png.h
+++ b/ext/libpng17/png.h
@@ -1,8 +1,9 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.7.0beta35 - March 17, 2014
- * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * libpng version 1.7.0beta90, August 28, 2017
+ *
+ * Copyright (c) 1998-2002,2004,2006-2017 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.)
*
@@ -10,17 +11,161 @@
*
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
- * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- * libpng versions 0.97, January 1998, through 1.7.0beta35 - March 17, 2014: Glenn
+ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
+ * libpng versions 0.97, January 1998, through 1.7.0beta90, August 28, 2017:
+ * Glenn Randers-Pehrson.
* See also "Contributing Authors", below.
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
*
- * Note about libpng version numbers:
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
*
- * Due to various miscommunications, unforeseen code incompatibilities
- * and occasional factors outside the authors' control, version numbering
- * on the library has not always been consistent and straightforward.
- * The following table summarizes matters since version 0.89c, which was
- * the first widely used release:
+ * This code is released under the libpng license.
+ *
+ * libpng versions 1.0.7, July 1, 2000 through 1.7.0beta90, August 28, 2017 are
+ * Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are
+ * derived from libpng-1.0.6, and are distributed according to the same
+ * disclaimer and license as libpng-1.0.6 with the following individuals
+ * added to the list of Contributing Authors:
+ *
+ * Simon-Pierre Cadieux
+ * Eric S. Raymond
+ * Mans Rullgard
+ * Cosmin Truta
+ * Gilles Vollant
+ * James Yu
+ *
+ * and with the following additions to the disclaimer:
+ *
+ * There is no warranty against interference with your enjoyment of the
+ * library or against infringement. There is no warranty that our
+ * efforts or the library will fulfill any of your particular purposes
+ * or needs. This library is provided with all faults, and the entire
+ * risk of satisfactory quality, performance, accuracy, and effort is with
+ * the user.
+ *
+ * Some files in the "contrib" directory and some configure-generated
+ * files that are distributed with libpng have other copyright owners and
+ * are released under other open source licenses.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
+ * libpng-0.96, and are distributed according to the same disclaimer and
+ * license as libpng-0.96, with the following individuals added to the list
+ * of Contributing Authors:
+ *
+ * Tom Lane
+ * Glenn Randers-Pehrson
+ * Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
+ * and are distributed according to the same disclaimer and license as
+ * libpng-0.88, with the following individuals added to the list of
+ * Contributing Authors:
+ *
+ * John Bowler
+ * Kevin Bracey
+ * Sam Bushell
+ * Magnus Holmgren
+ * Greg Roelofs
+ * Tom Tanner
+ *
+ * Some files in the "scripts" directory have other copyright owners
+ * but are released under this license.
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ * Andreas Dilger
+ * Dave Martindale
+ * Guy Eric Schalnat
+ * Paul Schmidt
+ * Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS". The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose. The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ * 1. The origin of this source code must not be misrepresented.
+ *
+ * 2. Altered versions must be plainly marked as such and must not
+ * be misrepresented as being the original source.
+ *
+ * 3. This Copyright notice may not be removed or altered from any
+ * source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products. If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ *
+ * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE.
+ *
+ * TRADEMARK:
+ *
+ * The name "libpng" has not been registered by the Copyright owner
+ * as a trademark in any jurisdiction. However, because libpng has
+ * been distributed and maintained world-wide, continually since 1995,
+ * the Copyright owner claims "common-law trademark protection" in any
+ * jurisdiction where common-law trademark is recognized.
+ *
+ * OSI CERTIFICATION:
+ *
+ * Libpng is OSI Certified Open Source Software. OSI Certified Open Source is
+ * a certification mark of the Open Source Initiative. OSI has not addressed
+ * the additional disclaimers inserted at version 1.0.7.
+ *
+ * EXPORT CONTROL:
+ *
+ * The Copyright owner believes that the Export Control Classification
+ * Number (ECCN) for libpng is EAR99, which means not subject to export
+ * controls or International Traffic in Arms Regulations (ITAR) because
+ * it is open source, publicly available software, that does not contain
+ * any encryption software. See the EAR, paragraphs 734.3(b)(3) and
+ * 734.7(b).
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ * printf("%s", png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience. This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/* Note about libpng version numbers:
+ *
+ * Due to various miscommunications, unforeseen code incompatibilities
+ * and occasional factors outside the authors' control, version numbering
+ * on the library has not always been consistent and straightforward.
+ * The following table summarizes matters since version 0.89c, which was
+ * the first widely used release:
*
* source png.h png.h shared-lib
* version string int version
@@ -58,267 +203,51 @@
* 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible)
* 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible)
* 1.0.7 1 10007 (still compatible)
- * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4
- * 1.0.8rc1 1 10008 2.1.0.8rc1
- * 1.0.8 1 10008 2.1.0.8
- * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6
- * 1.0.9rc1 1 10009 2.1.0.9rc1
- * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10
- * 1.0.9rc2 1 10009 2.1.0.9rc2
- * 1.0.9 1 10009 2.1.0.9
- * 1.0.10beta1 1 10010 2.1.0.10beta1
- * 1.0.10rc1 1 10010 2.1.0.10rc1
- * 1.0.10 1 10010 2.1.0.10
- * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3
- * 1.0.11rc1 1 10011 2.1.0.11rc1
- * 1.0.11 1 10011 2.1.0.11
- * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2
- * 1.0.12rc1 2 10012 2.1.0.12rc1
- * 1.0.12 2 10012 2.1.0.12
- * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned)
- * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2
- * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5
- * 1.2.0rc1 3 10200 3.1.2.0rc1
- * 1.2.0 3 10200 3.1.2.0
- * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4
- * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2
- * 1.2.1 3 10201 3.1.2.1
- * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6
- * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1
- * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1
- * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1
- * 1.0.13 10 10013 10.so.0.1.0.13
- * 1.2.2 12 10202 12.so.0.1.2.2
- * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6
- * 1.2.3 12 10203 12.so.0.1.2.3
- * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3
- * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1
- * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1
- * 1.0.14 10 10014 10.so.0.1.0.14
- * 1.2.4 13 10204 12.so.0.1.2.4
- * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2
- * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3
- * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3
- * 1.0.15 10 10015 10.so.0.1.0.15
- * 1.2.5 13 10205 12.so.0.1.2.5
- * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4
- * 1.0.16 10 10016 10.so.0.1.0.16
- * 1.2.6 13 10206 12.so.0.1.2.6
- * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2
- * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1
- * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1
- * 1.0.17 10 10017 12.so.0.1.0.17
- * 1.2.7 13 10207 12.so.0.1.2.7
- * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5
- * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5
- * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5
- * 1.0.18 10 10018 12.so.0.1.0.18
- * 1.2.8 13 10208 12.so.0.1.2.8
- * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3
- * 1.2.9beta4-11 13 10209 12.so.0.9[.0]
- * 1.2.9rc1 13 10209 12.so.0.9[.0]
- * 1.2.9 13 10209 12.so.0.9[.0]
- * 1.2.10beta1-7 13 10210 12.so.0.10[.0]
- * 1.2.10rc1-2 13 10210 12.so.0.10[.0]
- * 1.2.10 13 10210 12.so.0.10[.0]
- * 1.4.0beta1-5 14 10400 14.so.0.0[.0]
- * 1.2.11beta1-4 13 10211 12.so.0.11[.0]
- * 1.4.0beta7-8 14 10400 14.so.0.0[.0]
- * 1.2.11 13 10211 12.so.0.11[.0]
- * 1.2.12 13 10212 12.so.0.12[.0]
- * 1.4.0beta9-14 14 10400 14.so.0.0[.0]
- * 1.2.13 13 10213 12.so.0.13[.0]
- * 1.4.0beta15-36 14 10400 14.so.0.0[.0]
- * 1.4.0beta37-87 14 10400 14.so.14.0[.0]
- * 1.4.0rc01 14 10400 14.so.14.0[.0]
- * 1.4.0beta88-109 14 10400 14.so.14.0[.0]
- * 1.4.0rc02-08 14 10400 14.so.14.0[.0]
- * 1.4.0 14 10400 14.so.14.0[.0]
- * 1.4.1beta01-03 14 10401 14.so.14.1[.0]
- * 1.4.1rc01 14 10401 14.so.14.1[.0]
- * 1.4.1beta04-12 14 10401 14.so.14.1[.0]
- * 1.4.1 14 10401 14.so.14.1[.0]
- * 1.4.2 14 10402 14.so.14.2[.0]
- * 1.4.3 14 10403 14.so.14.3[.0]
- * 1.4.4 14 10404 14.so.14.4[.0]
- * 1.5.0beta01-58 15 10500 15.so.15.0[.0]
- * 1.5.0rc01-07 15 10500 15.so.15.0[.0]
- * 1.5.0 15 10500 15.so.15.0[.0]
- * 1.5.1beta01-11 15 10501 15.so.15.1[.0]
- * 1.5.1rc01-02 15 10501 15.so.15.1[.0]
- * 1.5.1 15 10501 15.so.15.1[.0]
- * 1.5.2beta01-03 15 10502 15.so.15.2[.0]
- * 1.5.2rc01-03 15 10502 15.so.15.2[.0]
- * 1.5.2 15 10502 15.so.15.2[.0]
- * 1.5.3beta01-10 15 10503 15.so.15.3[.0]
- * 1.5.3rc01-02 15 10503 15.so.15.3[.0]
- * 1.5.3beta11 15 10503 15.so.15.3[.0]
- * 1.5.3 [omitted]
- * 1.5.4beta01-08 15 10504 15.so.15.4[.0]
- * 1.5.4rc01 15 10504 15.so.15.4[.0]
- * 1.5.4 15 10504 15.so.15.4[.0]
- * 1.5.5beta01-08 15 10505 15.so.15.5[.0]
- * 1.5.5rc01 15 10505 15.so.15.5[.0]
- * 1.5.5 15 10505 15.so.15.5[.0]
- * 1.5.6beta01-07 15 10506 15.so.15.6[.0]
- * 1.5.6rc01-03 15 10506 15.so.15.6[.0]
- * 1.5.6 15 10506 15.so.15.6[.0]
- * 1.5.7beta01-05 15 10507 15.so.15.7[.0]
- * 1.5.7rc01-03 15 10507 15.so.15.7[.0]
- * 1.5.7 15 10507 15.so.15.7[.0]
- * 1.6.0beta01-37 16 10600 16.so.16.0[.0]
+ * ...
+ * 1.0.19 10 10019 10.so.0.19[.0]
+ * ...
+ * 1.2.56 13 10253 12.so.0.53[.0]
+ * ...
+ * 1.5.27 15 10523 15.so.15.23[.0]
+ * ...
+ * 1.6.22 16 10622 16.so.16.22[.0]
+ * ...
* 1.7.0alpha01-10 17 10700 17.so.17.0[.0]
- * 1.7.0beta01-35 17 10700 17.so.17.0[.0]
+ * 1.7.0beta01-84 17 10700 17.so.17.0[.0]
*
- * Henceforth the source version will match the shared-library major
- * and minor numbers; the shared-library major version number will be
- * used for changes in backward compatibility, as it is intended. The
- * PNG_LIBPNG_VER macro, which is not used within libpng but is available
- * for applications, is an unsigned integer of the form xyyzz corresponding
- * to the source version x.y.z (leading zeros in y and z). Beta versions
- * were given the previous public release number plus a letter, until
- * version 1.0.6j; from then on they were given the upcoming public
- * release number plus "betaNN" or "rcNN".
+ * Henceforth the source version will match the shared-library major
+ * and minor numbers; the shared-library major version number will be
+ * used for changes in backward compatibility, as it is intended. The
+ * PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ * for applications, is an unsigned integer of the form xyyzz corresponding
+ * to the source version x.y.z (leading zeros in y and z). Beta versions
+ * were given the previous public release number plus a letter, until
+ * version 1.0.6j; from then on they were given the upcoming public
+ * release number plus "betaNN" or "rcNN".
*
- * Binary incompatibility exists only when applications make direct access
- * to the info_ptr or png_ptr members through png.h, and the compiled
- * application is loaded with a different version of the library.
+ * Binary incompatibility exists only when applications make direct access
+ * to the info_ptr or png_ptr members through png.h, and the compiled
+ * application is loaded with a different version of the library.
*
- * DLLNUM will change each time there are forward or backward changes
- * in binary compatibility (e.g., when a new feature is added).
+ * DLLNUM will change each time there are forward or backward changes
+ * in binary compatibility (e.g., when a new feature is added).
*
- * See libpng-manual.txt or libpng.3 for more information. The PNG
- * specification is available as a W3C Recommendation and as an ISO
- * Specification,
*
* If you just need to read a PNG file and don't want to read the documentation
* skip to the end of this file and read the section entitled 'simplified API'.
*/
/* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.7.0beta35"
-#define PNG_HEADER_VERSION_STRING \
- " libpng version 1.7.0beta35 - March 17, 2014\n"
+#define PNG_LIBPNG_VER_STRING "1.7.0beta90"
+#define PNG_HEADER_VERSION_STRING " libpng version 1.7.0beta90 - August 28, 2017\n"
#define PNG_LIBPNG_VER_SONUM 17
#define PNG_LIBPNG_VER_DLLNUM 17
@@ -394,7 +324,7 @@
* PNG_LIBPNG_VER_STRING, omitting any leading zero:
*/
-#define PNG_LIBPNG_VER_BUILD 35
+#define PNG_LIBPNG_VER_BUILD 90
/* Release Status */
#define PNG_LIBPNG_BUILD_ALPHA 1
@@ -425,14 +355,14 @@
* the library has been built.
*/
#ifndef PNGLCONF_H
- /* If pnglibconf.h is missing, you can
- * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
- */
+/* If pnglibconf.h is missing, you can
+ * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
+ */
# include "pnglibconf.h"
#endif
#ifndef PNG_VERSION_INFO_ONLY
- /* Machine specific configuration. */
+/* Machine specific configuration. */
# include "pngconf.h"
#endif
@@ -479,6 +409,7 @@
* definitions.
* 4. Exported library functions.
* 5. Simplified API.
+ * 6. Implementation options
*
* The library source code has additional files (principally pngpriv.h) that
* allow configuration of the library.
@@ -507,18 +438,24 @@
*/
#define PNG_u2(b1, b2) (((unsigned int)(b1) << 8) + (b2))
-#define PNG_U16(b1, b2) ((png_uint_16)PNG_u2(b1, b2))
+#define PNG_U16(b1, b2) ((png_uint_16)/*SAFE*/PNG_u2(b1, b2))
#define PNG_U32(b1, b2, b3, b4)\
- (((png_uint_32)PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4))
+ (((png_uint_32)/*SAFE*/PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4))
/* ISO-PNG states that signed 32-bit values are stored in two's complement
* format. There is no guarantee that (png_int_32) is exactly 32 bits, so the
* following macro tests for a negative number and generates the machine format
* directly by portable arithmetic operations. The cost is that the argument
* 'b1' is evaluated twice.
+ *
+ * NOTE: the 0x7fffffffU BIC is there to ensure that potential overflow in the
+ * cast does not occur. This fixes the case where 1's complement machines could
+ * be forced into an overflow by an invalid value in the stream and, therefore,
+ * potentially raise an arithmetic exception; the invalid value is converted to
+ * 0 and any resultant problems will be caught later in the libpng checking.
*/
#define PNG_S32(b1, b2, b3, b4) ((b1) & 0x80\
- ? -(png_int_32)((PNG_U32(b1, b2, b3, b4) ^ 0xffffffff) + 1)\
+ ? -(png_int_32)(((PNG_U32(b1, b2, b3, b4)^0xffffffffU)+1U)&0x7fffffffU)\
: (png_int_32)PNG_U32(b1, b2, b3, b4))
/* Constants for known chunk types.
@@ -588,8 +525,10 @@
* systems where (char) is more than 8 bits.
*/
#define PNG_STRING_FROM_CHUNK(s,c)\
- (void)(((char*)(s))[0]=(char)((c)>>24), ((char*)(s))[1]=(char)((c)>>16),\
- ((char*)(s))[2]=(char)((c)>>8), ((char*)(s))[3]=(char)((c)))
+ (void)(((char*)(s))[0]=(char)(((c)>>24) & 0xff), \
+ ((char*)(s))[1]=(char)(((c)>>16) & 0xff),\
+ ((char*)(s))[2]=(char)(((c)>>8) & 0xff), \
+ ((char*)(s))[3]=(char)((c & 0xff)))
/* Do the same but terminate with a null character. */
#define PNG_CSTRING_FROM_CHUNK(s,c)\
@@ -645,7 +584,7 @@ extern "C" {
/* This triggers a compiler error in png.c, if png.c and png.h
* do not agree upon the version number.
*/
-typedef char* png_libpng_version_1_7_0beta35;
+typedef char* png_libpng_version_1_7_0beta90;
/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info.
*
@@ -775,6 +714,16 @@ typedef png_sPLT_t * * png_sPLT_tpp;
* PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the
* same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag"
* which is always 0 or 1, or its "compression method" which is always 0.
+ *
+ * The location field (added in libpng 1.7.0) records where the text chunk was
+ * found when png_get_text is used. When png_set_text is used the field in the
+ * structure passed in is ignored and, instead, the field is set to the current
+ * write position.
+ *
+ * Prior to 1.7.0 the write behavior was the same; the text fields were written
+ * (once) at the next write_info call, however the read mechanism did not record
+ * the chunk location so if an info_struct from read was passed to the write
+ * APIs the text chunks would all be written at the start (before PLTE).
*/
typedef struct png_text_struct
{
@@ -783,6 +732,9 @@ typedef struct png_text_struct
0: zTXt, deflate
1: iTXt, none
2: iTXt, deflate */
+ png_byte location; /* 1: PNG_HAVE_IHDR
+ 2: PNG_HAVE_PLTE
+ 8: PNG_AFTER_IDAT */
png_charp key; /* keyword, 1-79 character description of "text" */
png_charp text; /* comment, may be an empty string (ie "")
or a NULL pointer */
@@ -800,8 +752,14 @@ typedef png_text * * png_textpp;
/* Supported compression types for text in PNG files (tEXt, and zTXt).
* The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#ifdef PNG_OLD_COMPRESSION_CODES_SUPPORTED
+ /* These values were used to prevent double write of text chunks in versions
+ * prior to 1.7.0. They are never set now; if you need them #define the
+ * _SUPPORTED macro.
+ */
#define PNG_TEXT_COMPRESSION_NONE_WR -3
#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#endif /* OLD_COMPRESSION_CODES */
#define PNG_TEXT_COMPRESSION_NONE -1
#define PNG_TEXT_COMPRESSION_zTXt 0
#define PNG_ITXT_COMPRESSION_NONE 1
@@ -838,9 +796,11 @@ typedef png_time * * png_timepp;
*/
typedef struct png_unknown_chunk_t
{
- png_byte name[5]; /* Textual chunk name with '\0' terminator */
- png_byte *data; /* Data, should not be modified on read! */
- size_t size;
+ png_byte *data; /* Data, should not be modified on read! */
+ png_uint_32 size; /* Size of data, must not exceed 0x7fffffff.
+ * API CHANGE 1.7.0: changed from 'size_t'
+ */
+ png_byte name[5]; /* Textual chunk name with '\0' terminator */
/* On write 'location' must be set using the flag values listed below.
* Notice that on read it is set by libpng however the values stored have
@@ -848,7 +808,7 @@ typedef struct png_unknown_chunk_t
* bitmask. On write set only one bit - setting multiple bits may cause the
* chunk to be written in multiple places.
*/
- png_byte location; /* mode of operation at read time */
+ png_byte location; /* mode of operation at read time */
}
png_unknown_chunk;
@@ -857,10 +817,10 @@ typedef const png_unknown_chunk * png_const_unknown_chunkp;
typedef png_unknown_chunk * * png_unknown_chunkpp;
#endif
-/* Flag values for the unknown chunk location byte. */
-#define PNG_HAVE_IHDR 0x01
-#define PNG_HAVE_PLTE 0x02
-#define PNG_AFTER_IDAT 0x08
+/* Flag values for the chunk location byte. */
+#define PNG_HAVE_IHDR 0x01U
+#define PNG_HAVE_PLTE 0x02U
+#define PNG_AFTER_IDAT 0x08U
/* Maximum positive integer used in PNG is (2^31)-1 */
#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
@@ -877,12 +837,12 @@ typedef png_unknown_chunk * * png_unknown_chunkpp;
/* These describe the color_type field in png_info. */
/* color type masks */
-#define PNG_COLOR_MASK_PALETTE 1
-#define PNG_COLOR_MASK_COLOR 2
-#define PNG_COLOR_MASK_ALPHA 4
+#define PNG_COLOR_MASK_PALETTE 1U
+#define PNG_COLOR_MASK_COLOR 2U
+#define PNG_COLOR_MASK_ALPHA 4U
/* color types. Note that not all combinations are legal */
-#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_GRAY 0U
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
@@ -895,11 +855,26 @@ typedef png_unknown_chunk * * png_unknown_chunkpp;
#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
-/* This is for filter type. PNG 1.0-1.2 only define the single type. */
+/* This is for filter method. PNG 1.0-1.2 only defines a single method.
+ *
+ * NOTE: CONFUSING NAME. The specification refers to a 'method', one of the
+ * defines below, and a 'type', one of the FILTER_VALUE defines.
+ * Historically libpng uses TYPE for 'method' and VALUE for 'type'.
+ */
#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */
#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE
+/* Filter values defined for method '0' (PNG_FILTER_TYPE_BASE) in the PNG
+ * specification.
+ */
+#define PNG_FILTER_VALUE_NONE 0
+#define PNG_FILTER_VALUE_SUB 1
+#define PNG_FILTER_VALUE_UP 2
+#define PNG_FILTER_VALUE_AVG 3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST 5 /* Not a valid value */
+
/* These are for the interlacing type. These values should NOT be changed. */
#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */
#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */
@@ -946,22 +921,22 @@ typedef png_unknown_chunk * * png_unknown_chunkpp;
* data in the info_struct to be written into the output file. The values
* of the PNG_INFO_ defines should NOT be changed.
*/
-#define PNG_INFO_gAMA 0x0001
-#define PNG_INFO_sBIT 0x0002
-#define PNG_INFO_cHRM 0x0004
-#define PNG_INFO_PLTE 0x0008
-#define PNG_INFO_tRNS 0x0010
-#define PNG_INFO_bKGD 0x0020
-#define PNG_INFO_hIST 0x0040
-#define PNG_INFO_pHYs 0x0080
-#define PNG_INFO_oFFs 0x0100
-#define PNG_INFO_tIME 0x0200
-#define PNG_INFO_pCAL 0x0400
-#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */
-#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */
-#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */
-#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */
-#define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */
+#define PNG_INFO_gAMA 0x0001U
+#define PNG_INFO_sBIT 0x0002U
+#define PNG_INFO_cHRM 0x0004U
+#define PNG_INFO_PLTE 0x0008U
+#define PNG_INFO_tRNS 0x0010U
+#define PNG_INFO_bKGD 0x0020U
+#define PNG_INFO_hIST 0x0040U
+#define PNG_INFO_pHYs 0x0080U
+#define PNG_INFO_oFFs 0x0100U
+#define PNG_INFO_tIME 0x0200U
+#define PNG_INFO_pCAL 0x0400U
+#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */
+#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */
+#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */
+#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */
+#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */
/* This is used for the transformation routines, as some of them
* change these values for the row. It also should enable using
@@ -1000,22 +975,53 @@ typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
-/* The following callback receives png_uint_32 row_number, int pass for the
- * png_bytep data of the row. When transforming an interlaced image the
- * row number is the row number within the sub-image of the interlace pass, so
- * the value will increase to the height of the sub-image (not the full image)
- * then reset to 0 for the next pass.
+/* WARNING: the API for this callback is poorly documented and produces
+ * unexpected results when dealing with interlaced images. For non-interlaced
+ * images the parameters are straightforward:
*
- * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
- * find the output pixel (x,y) given an interlaced sub-image pixel
- * (row,col,pass). (See below for these macros.)
+ * next_row: a pointer to the transformed row read from the PNG input
+ * stream, it has png_get_image_width() pixels.
+ * row_y: the y ordinate of the image; 0..png_get_image_height()-1
+ * pass: 0
+ *
+ * For interlaced images if png_set_interlace_handling has been called (libpng
+ * does *not* call this itself) the parameters are the same except that the
+ * pass will be the pass in the range 0..6 (NOTE: one less than the PNG spec)
+ * and 'next_row' will be NULL if (and only if) the row does not contribute
+ * to the output in 'blocky' display mode.
+ *
+ * pass: 0..6
+ *
+ * If 'next_row' is not NULL it is necessary for the application to combine the
+ * pixels with the output. This can most easily be done by calling
+ * png_progressive_combine_row(). Note that the 'next_row' data cannot be
+ * changed; even though the value is passed to png_progressive_combine_row the
+ * pointer is not used, it is just a flag , if it is NULL nothing will happen.
+ *
+ * If png_set_interlace_handling has not been called the callback only gets
+ * called for original PNG interlaced row:
+ *
+ * row_y: the y ordinate in the pass; 0..PNG_PASS_ROWS()-1
+ *
+ * What is more if PNG_PASS_COLS() is 0 the entire pass will be skipped. The
+ * row data is not full width and there is no guarantee that the buffer passed
+ * in 'next_row' is able to accomodate the full width of output pixels, however
+ * 'next_row' will never be NULL.
+ *
+ * Use PNG_ROW_FROM_PASS_ROW(row_y, pass) and PNG_COL_FROM_PASS_COL(col, pass)
+ * to find the output pixel (x,y) given an interlaced sub-image pixel
+ * (row_y,col,pass). (See below for these macros.)
+ *
+ * Note that in this latter case if you want to do the 'blocky' display update
+ * method you have to work out all the details yourself with regard to which
+ * pixels to set for each row and whether to replicate it to the following
+ * rows of the image.
*/
-typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
- png_uint_32, int));
+typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp,
+ png_bytep next_row, png_uint_32 row_y, int pass));
#endif
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
- defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
png_bytep));
#endif
@@ -1144,11 +1150,35 @@ PNG_EXPORTA(5, png_structp, png_create_write_struct,
png_error_ptr warn_fn),
PNG_ALLOCATED);
-PNG_EXPORT(6, size_t, png_get_compression_buffer_size,
- (png_const_structrp png_ptr));
-
-PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
- size_t size));
+/* These APIs control the size of the buffer used for reading IDAT chunks in the
+ * sequential read code and the size of the IDAT chunks produced when writing.
+ * They have no effect on the progressive read code. In both read and write
+ * cases it will be necessary to allocate at least this amount of buffer space.
+ * The default value is PNG_IDAT_READ_SIZE on read and PNG_ZBUF_SIZE on write.
+ *
+ * The valid range is 1..0x7FFFFFFF on write and 1..max(uInt) on read, where
+ * uInt is the type declared by zlib.h. On write setting the largest value will
+ * typically cause the PNG image data to be written in one chunk; this gives the
+ * smallest PNG and has little or no effect on applications that read the PNG.
+ *
+ * DEPRECATED: use png_set_IDAT_size on write and png_set_read_buffer_size on
+ * read.
+ */
+PNG_EXPORTA(6, png_alloc_size_t, png_get_compression_buffer_size,
+ (png_const_structrp png_ptr), PNG_DEPRECATED);
+PNG_EXPORTA(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
+ png_alloc_size_t size), PNG_DEPRECATED);
+#define png_set_read_buffer_size(p,size) (png_setting((p),\
+ PNG_SR_COMPRESS_buffer_size, (size), 0))
+ /* The size of the buffer used while processing compressed data, both single
+ * chunk data (zTXt, iTXt, iCCP) and IDAT data. With IDAT data in libpng 1.7
+ * IDATs are read until the end or until the buffer is full; this means that
+ * you can optimize the buffer size for the particular memory behavior of
+ * your system and, possibly, your application.
+ *
+ * NOTE: the result (on success) is 0, which is actually an invalid value.
+ * Retrieving the current value is not possible.
+ */
/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
* match up.
@@ -1200,8 +1230,8 @@ PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr));
/* Write a PNG chunk - size, type, (optional) data, CRC. */
-PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
- chunk_name, png_const_bytep data, size_t length));
+PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr,
+ png_const_bytep chunk_name, png_const_voidp data, size_t length));
/* Write the start of a PNG chunk - length and chunk name. */
PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
@@ -1209,7 +1239,7 @@ PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
/* Write the data of a PNG chunk started with png_write_chunk_start(). */
PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
- png_const_bytep data, size_t length));
+ png_const_voidp data, size_t length));
/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
@@ -1247,12 +1277,13 @@ PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29],
#ifdef PNG_CONVERT_tIME_SUPPORTED
/* Convert from a struct tm to png_time */
-PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
- const struct tm * ttime));
+PNG_EXPORT(24, PNG_DEPRECATED void, png_convert_from_struct_tm,
+ (png_timep ptime, const struct tm * ttime));
/* Convert from time_t to png_time. Uses gmtime() */
-PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
-#endif /* PNG_CONVERT_tIME_SUPPORTED */
+PNG_EXPORT(25, PNG_DEPRECATED void, png_convert_from_time_t, (png_timep ptime,
+ time_t ttime));
+#endif /* CONVERT_tIME */
#ifdef PNG_READ_EXPAND_SUPPORTED
/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
@@ -1281,15 +1312,45 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
/* Reduce RGB to grayscale. */
-#define PNG_ERROR_ACTION_NONE 1
-#define PNG_ERROR_ACTION_WARN 2
-#define PNG_ERROR_ACTION_ERROR 3
+#define PNG_ERROR_ACTION_NO_CHECK 0
+#define PNG_ERROR_ACTION_NONE 1
+#define PNG_ERROR_ACTION_WARN 2
+#define PNG_ERROR_ACTION_ERROR 3
#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/
PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr,
int error_action, double red, double green))
PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr,
int error_action, png_fixed_point red, png_fixed_point green))
+ /* Convert RGB pixels to gray (CIE Y) values, the red and green value must be
+ * less than or equal to 1, if either is negative a set of defaults
+ * corresponding to the sRGB standard are used.
+ *
+ * The error action specifies whether to check for r==g==b in each pixel, if
+ * it is 0 (PNG_ERROR_ACTION_NO_CHECK; added in libpng 1.7.0) no check will
+ * be performed, otherwise a check is performed and the result can be
+ * retrieved using png_get_rgb_to_gray_status (which just returns a
+ * true if a non-gray pixel was encountered).
+ *
+ * Pass PNG_ERROR_ACTION_ERROR if you are confident that the image only
+ * contains gray pixels (you have already checked); the check is still
+ * performed but a very optimized code path is used for RGB to gray
+ * convertion.
+ *
+ * If you want to extract a single channel pass PNG_FP_1 for the coefficient
+ * for that channel and 0 for the rest (0 for both red and green to extract
+ * blue).
+ *
+ * NOTE: the default coefficients used if negative values are passed for red
+ * or green are based on the cHRM chunk if available, otherwise sRGB. The
+ * calculation returns the Y (luminance value) corresponding to the white
+ * point of the PNG. UNLESS THE WHITE POINT IS D50 THIS IS NOT A CIEXYZ Y
+ * VALUE. It is the luminance of the pixel perceived by a viewer completely
+ * adapted to the white point of the PNG, this may not be what you want
+ * because to interpret it you have to also record the white point of the
+ * PNG. To obtain CIEXYZ Y values read the cHRM chunk XYZ values and
+ * chromatically adapt them to D50
+ */
PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp
png_ptr));
@@ -1458,7 +1519,7 @@ PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
png_uint_32 filler, int flags));
-#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+#endif /* READ_FILLER || WRITE_FILLER */
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
/* Swap bytes in 16-bit depth files. */
@@ -1524,7 +1585,7 @@ PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr));
#endif
#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */
+#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */
/* Strip the second byte of information from a 16-bit depth file. */
PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr));
#endif
@@ -1562,11 +1623,16 @@ PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr,
#endif
#ifdef PNG_WRITE_FLUSH_SUPPORTED
-/* Set how many lines between output flushes - 0 for no flushing */
-PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows));
+PNG_REMOVED(51, void, png_set_flush, (png_structrp png_ptr, int nrows),
+ PNG_EMPTY)
+#define png_set_flush(p,v) (png_setting((p), PNG_SW_FLUSH, 0, (v)))
+ /* Set how many lines between output flushes - 0 for no flushing. The result
+ * on success is always 0.
+ */
+
/* Flush the current PNG output buffer */
PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr));
-#endif
+#endif /* WRITE_FLUSH */
/* Optional update palette with requested transformations */
PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr));
@@ -1587,7 +1653,7 @@ PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row,
png_bytep display_row));
#endif
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_READ_IMAGE_SUPPORTED
/* Read the whole image into memory at once. */
PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image));
#endif
@@ -1628,9 +1694,11 @@ PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
png_infopp info_ptr_ptr));
-/* Set the libpng method of handling chunk CRC errors */
-PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
- int ancil_action));
+/* Set the libpng method of handling chunk CRC errors on read */
+PNG_REMOVED(66, void, png_set_crc_action, (png_structrp png_ptr,
+ int crit_action, int ancil_action), PNG_EMPTY)
+#define png_set_crc_action(png_ptr, crit, ancil)\
+ (png_setting((png_ptr), PNG_SR_CRC_ACTION, (crit), (ancil)))
/* Values for png_set_crc_action() say how to handle CRC errors in
* ancillary and critical chunks, and whether to use the data contained
@@ -1648,6 +1716,169 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */
#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */
+/* Write image filtering and compression options.
+ *
+ * These settings just change the very low level encoding of a PNG. The changes
+ * make no difference to the image or the meta-data stored in the PNG. The API
+ * used to make these changes can be disabled in a very minimal configuration,
+ * if it is your compiler will report undefined values when the APIs below are
+ * used.
+ *
+ * Write settings defined here, in order of ease of use:
+ *
+ * 1) Write compression settings: whether to optimize the write and the PNG
+ * that results for read speed, final PNG size, write speed or memory
+ * usage.
+ * 2) IDAT size: What size to make the IDAT chunks in the PNG.
+ * 3) PNG row filters to consider when writing the PNG.
+ * 4) Very low level control over the deflate compression (useful mainly for
+ * programs that want to try every option to find which gives the smallest
+ * PNG.)
+ */
+#ifdef PNG_WRITE_SUPPORTED
+/* (1) Write compression settings: */
+#define png_set_compression(p, v) (png_setting((p), PNG_SW_COMPRESS_png_level,\
+ 0, (v)))
+ /* Control the write compression of all chunks. This affects five basic
+ * pieces of behavior:
+ *
+ * 1) The size of the PNG produced.
+ * 2) The amount of memory the write code takes to produce the PNG.
+ * 3) The amount of time the write code takes to produce the PNG.
+ * 4) The amount of memory required to read the resultant PNG.
+ * 5) The amount of time required to read the resultant PNG.
+ *
+ * There is considerable interdependence between these variables. As a
+ * result there are a limited number of options:
+ */
+# define PNG_COMPRESSION_LOW_MEMORY (1)
+ /* Minimize the memory required both when reading (4) and writing (2) the
+ * PNG. This results in a significantly larger PNG (which may itself have
+ * the opposite effect of slowing down either read or write) however the
+ * memory overhead is reduced and, apart from the extra time to read the
+ * data, the read time is likely to be reduced too.
+ *
+ * Use this when both read and write will happen on a memory starved
+ * (really, very low memory) system. Note that this sets a high deflate
+ * compression setting because that does not affect zlib memory usage.
+ */
+# define PNG_COMPRESSION_HIGH_SPEED (2)
+ /* Minimize the time to both read (5) and write (3) the PNG. This uses
+ * slightly more memory on read and potentially significantly more on
+ * write but is optimized for maximum speed in both cases.
+ *
+ * Use this when both read and write need to be fast and PNG size is not
+ * likely to be an issue. An example would be if you are using PNG to
+ * pass intermediate data between applications on the same machine.
+ */
+# define PNG_COMPRESSION_HIGH_READ_SPEED (3)
+ /* Minimize the time to read (5) the PNG. This also reduces the amount
+ * of memory on read, however some options which require more memory but
+ * are likely to decrease PNG size, therefore improve read spead, are
+ * used.
+ *
+ * This is one of the 'normal' options; options that are used when a
+ * reasonably capable write machine is producing PNG files that will be
+ * read many times. In this case the option is optimizing for speed on
+ * read even if it increases the size of the PNG.
+ */
+# define PNG_COMPRESSION_LOW (4)
+ /* This switches on options which do affect speed of both compression and
+ * decompression, but biases the choice towards higher performance in both
+ * cases. (So it is something of a compromise between all-out speed and
+ * PNG compression).
+ *
+ * This is a good default to use in typical usages where PNG file size is
+ * less of an issue than the overheads on reading a PNG file.
+ *
+ * Use this option when producing PNG files that are not expected to be
+ * distributed widely or where read speed is more important than size.
+ * This is also a good default for small images where the slight increase
+ * in size of the compressed data doesn't change the file size much.
+ */
+# define PNG_COMPRESSION_MEDIUM (5)
+ /* This is a compromise which switches on the options found most helpful
+ * across a wide range of files without switching on the full range of
+ * options which would decrease file size only a little while taking a lot
+ * more time. PNG read memory (4) or time (5) is not a factor in the
+ * choice of options; only write time (3).
+ *
+ * This is closest to the default used in prior versions of libpng. There
+ * seems no logic to using it if the actual requirements are known and,
+ * even if they aren't, it is probably better to guess 'LOW' or 'HIGH'.
+ *
+ * This is the normal libpng default.
+ */
+# define PNG_COMPRESSION_HIGH (6)
+ /* This turns on everything which reduces file size on aggregate across a
+ * large test set of files. It optimizes solely for the size of the
+ * resultant PNG (1).
+ *
+ * This is a good default to use if file size is all important; it was the
+ * stated original default in the PNG design, but the implementation of
+ * libpng never used it.
+ *
+ * Use this setting in image authoring applications when writing the
+ * finished image in PNG format.
+ *
+ * NOTE: several PNG file size optimizers exist (see the web-site
+ * libpng.org). libpng does not perform the same functions as these
+ * optimizers; libpng does not search for the best compression settings.
+ * For this reason if you really want to minimize the size of the PNG files
+ * produced use PNG_COMPRESSION_HIGH_SPEED then post-process the result
+ * with one of the many PNG optimization programs.
+ */
+# define PNG_COMPRESSION_COMPAT (0)
+ /* DEPRECATED: this is provided as a setting to aid transition of test
+ * suites between major library versions (1.5 or 1.6 moving to 1.7). The
+ * default settings change in 1.7 so, while the PNG files produced do not
+ * change, their encoding does. Test systems that rely on constant
+ * encoding can use this to verify that this is all that has changed.
+ *
+ * NOTE: the option will be removed at some point. It is difficult to
+ * maintain and adds to libpng code size.
+ *
+ * NOTE: there are other changes in major and minor releases, such as
+ * better ancillary chunk error handling, that also cause binary changes
+ * to the PNG files libpng generates. Furthermore versions of libpng
+ * prior to 1.7 included random data from uninitialized memory in the
+ * image data under certain circumstances; this meant that earlier
+ * versions were often not even consistent across two writes of the same
+ * PNG file!
+ */
+
+/* png_set_compression sets the default for all libpng compression operations.
+ * While the setting is the same for all chunks it results in different
+ * compression options for different chunks. The setting can be applied
+ * separately to each class of chunks as follows:
+ */
+#define png_set_image_compression(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_png_level, png_IDAT, (v)))
+ /* Control the compression of the image data (IDAT) chunks. */
+
+#define png_set_ICC_profile_compression(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_png_level, png_iCCP, (v)))
+ /* Control the compression of ICC profiles (iCCP chunks.) */
+
+#define png_set_text_compression(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_png_level, png_zTXt, (v)))
+ /* Control the compression of text (png_zTXt and png_iTXt) chunks. */
+
+/* (2) IDAT size: */
+#define png_set_IDAT_size(p, v) (png_setting((p), PNG_SW_IDAT_size, (v), 0))
+ /* Set the maximum size of the IDAT chunks libpng writes. Valid values are
+ * in the range 1U..0x7fffffffU, the default is 'PNG_ZBUF_SIZE' (a
+ * historically confusing name) and this default *also* controls the size of
+ * the buffer the read code uses when reading IDAT chunks.
+ *
+ * libpng has to buffer the data in the IDAT chunk before it writes any of
+ * it, therefore this number directly controls that part of the memory
+ * overhead while writing a PNG. There is a 12 byte per chunk overhead, so
+ * the number also directly affects the size of the PNG. The number has no
+ * significant effect (beyond the latter size effect) on the read code.
+ */
+#endif /* WRITE */
+
/* These functions give the user control over the scan-line filtering in
* libpng and the compression methods used by zlib. These functions are
* mainly useful for testing, as the defaults should work with most users.
@@ -1656,135 +1887,221 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
* header file (zlib.h) for an explination of the compression functions.
*/
-/* Set the filtering method(s) used by libpng. Currently, the only valid
- * value for "method" is 0.
- */
-PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
- int filters));
-
-/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
- * These defines match the values in the PNG specification.
- */
-#define PNG_FILTER_VALUE_NONE 0
-#define PNG_FILTER_VALUE_SUB 1
-#define PNG_FILTER_VALUE_UP 2
-#define PNG_FILTER_VALUE_AVG 3
-#define PNG_FILTER_VALUE_PAETH 4
-#define PNG_FILTER_VALUE_LAST 5
-
-/* The above values are valid arguments to png_set_filter() if only a single
- * filter is to be used. If multiple filters are to be allowed (the default is
- * to allow any of them) then a combination of the following masks must be used
- * and the low three bits of the argument to png_set_filter must be 0.
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+/* (3) PNG row filters to consider when writing the PNG.
*
- * The resultant argument fits in a single byte.
+ * Control the filtering method(s) used by libpng for the write of subsequent
+ * rows of the image. The argument is either a single filter value (one of the
+ * PNG_FILTER_VALUE_ defines above), in which case that filter will be used on
+ * following rows, or a mask of filter values (logical or of the PNG_FILTER_
+ * bit masks that follow PNG_FILTER_VALUE_*). Support for selection of a filter
+ * from a mask with more than one bit set is dependent on
+ * PNG_SELECT_FILTER_SUPPORTED, however support is the default configuration of
+ * libpng. If support is not available the lowest bit set in the mask (the
+ * lowest numbered filter) is used.
+ *
+ * The set of filters may be changed at any time, the new values will affect the
+ * next row written.
+ *
+ * The 'method' must match that passed to png_set_IHDR; it cannot be changed and
+ * is ignored in 1.7 and later.
+ *
+ * If multiple filters are enabled libpng will select one according to the
+ * following rules:
+ *
+ * 1) On the first row of a pass UP is ignored if NONE is set and PAETH is
+ * ignored if SUB is set; this is because these filter pairs are equivalent
+ * when there is no previous row.
+ *
+ * 2) PNG_SELECT_FILTER_SUPPORTED:
+ * If the PNG rows are long enough (have enough bytes) libpng will process a
+ * row at a time; it will buffer the row if necessary. It uses a heuristic
+ * based on the closeness of the filtered values to 0 to determine which
+ * filter to use.
+ *
+ * 3) !PNG_SELECT_FILTER_SUPPORTED:
+ * libpng selects the first filter in the list (there is no warning that this
+ * will happen - check the #defines if you need to know.)
+ *
+ * The 'up', 'avg' and 'Paeth' filters require the previous image row to work.
+ * If it is not available they are removed from the set of filters to try. The
+ * first time the filter mask includes one of these filters libpng turns on
+ * saving of the row. The filters do work on the first row of a pass, where
+ * there is no previous row from the image. The PNG standard defines the
+ * previous row as consisting of all 0 bytes in this case. That definition
+ * causes the filters to have the following properties on the first row of a
+ * pass:
+ *
+ * UP: The same as NONE (i.e. no filtering).
+ * AVG: Uses the arithmetic (not modular arithmetc!) half of the preceding
+ * pixel as the predictor. This is unique and not typically very
+ * useful.
+ * PAETH: The same as SUB.
+ *
+ * As a result with all versions of libpng if you want to use any of these
+ * filters anywhere in the image you need only turn on one of them on the first
+ * row of the image, or of a pass for interlaced images. For example if you
+ * want to use 'sub' on the first row simply set 'sub'+'Paeth' in the mask;
+ * libpng will automatically eliminate the Paeth algorithm from consideration
+ * because it knows that 'sub' will rank equal or (if the filter byte is taken
+ * into account) better.
+ *
+ * This approach is portable to earlier versions of libpng, however it may be
+ * difficult to program. 1.7 allows you to directly specify whether or not to
+ * retain the previous row. This is simpler and allows you to turn off previous
+ * row retention if you want to.
*/
-#define PNG_FILTER_NONE (0x08 << PNG_FILTER_VALUE_NONE)
-#define PNG_FILTER_SUB (0x08 << PNG_FILTER_VALUE_SUB)
-#define PNG_FILTER_UP (0x08 << PNG_FILTER_VALUE_UP)
-#define PNG_FILTER_AVG (0x08 << PNG_FILTER_VALUE_AVG)
-#define PNG_FILTER_PAETH (0x08 << PNG_FILTER_VALUE_PAETH)
+PNG_REMOVED(67, void, png_set_filter, (png_structrp png_ptr, int method,
+ int filters), PNG_EMPTY)
-/* Then two convenience values. PNG_NO_FILTERS is the same as
+#define png_set_filter(p, m, f) (png_setting((p), PNG_SW_COMPRESS_filters,\
+ (m), (f)))
+ /* 'm' is the method and must be 0 (PNG_FILTER_TYPE_BASE) unless MNG
+ * processing is supported (very unusual). 'f' is either a single value,
+ * PNG_FILTER_VALUE_* below, or a combination of one or more PNG_FILTER_MASK
+ * values.
+ *
+ * This sets the filter mask (or value) for the *next* row that is written.
+ * It may be called at any time but does not have any effect until the next
+ * row starts to be written.
+ *
+ * The return value is the mask that is set (or, with PNG_SF_GET, the
+ * currently set mask). When PNG_SELECT_FILTER_SUPPORTED is not defined this
+ * mask will have only one bit.
+ *
+ * NOTE: with PNG_SF_GET the result will be PNG_UNSET if png_set_filter has
+ * not been called before and row writing has not started.
+ */
+
+#define png_set_row_buffers(p, onoff) (png_setting((p),\
+ PNG_SW_COMPRESS_row_buffers, (onoff), 0))
+ /* If you intend to change the filter list after the first row using the
+ * previous API call png_set_row_buffers(png_ptr, 1) if you intend to use UP,
+ * AVG or Paeth filters.
+ *
+ * You can turn the buffering on and off dynamically, just as with
+ * png_set_filter.
+ *
+ * The second argument should be 0 (off) or 1 (on). In the future it may be
+ * used to control the maximum number of rows buffered.
+ */
+#endif /* WRITE_FILTER */
+
+/* The PNG_FILTER_VALUE_ definitions (the filter values from the base PNG spec)
+ * are valid arguments to png_set_filter() if only a single filter is to be
+ * used. If multiple filters are to be allowed (the default is to allow any of
+ * them) then a combination of the following masks must be used and the low
+ * three bits of the argument to png_set_filter must be 0.
+ *
+ * The resultant argument fits in a single byte in either case.
+ */
+#define PNG_FILTER_MASK(value) (0x08 << (value))
+#define PNG_FILTER_NONE PNG_FILTER_MASK(PNG_FILTER_VALUE_NONE)
+#define PNG_FILTER_SUB PNG_FILTER_MASK(PNG_FILTER_VALUE_SUB)
+#define PNG_FILTER_UP PNG_FILTER_MASK(PNG_FILTER_VALUE_UP)
+#define PNG_FILTER_AVG PNG_FILTER_MASK(PNG_FILTER_VALUE_AVG)
+#define PNG_FILTER_PAETH PNG_FILTER_MASK(PNG_FILTER_VALUE_PAETH)
+
+/* Then three convenience values. PNG_NO_FILTERS is the same as
* PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing.
*/
-#define PNG_NO_FILTERS 0x00
-#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
- PNG_FILTER_AVG | PNG_FILTER_PAETH)
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */
-/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
- * defines, either the default (minimum-sum-of-absolute-differences), or
- * the experimental method (weighted-minimum-sum-of-absolute-differences).
- *
- * Weights are factors >= 1.0, indicating how important it is to keep the
- * filter type consistent between rows. Larger numbers mean the current
- * filter is that many times as likely to be the same as the "num_weights"
- * previous filters. This is cumulative for each previous row with a weight.
- * There needs to be "num_weights" values in "filter_weights", or it can be
- * NULL if the weights aren't being specified. Weights have no influence on
- * the selection of the first row filter. Well chosen weights can (in theory)
- * improve the compression for a given image.
- *
- * Costs are factors >= 1.0 indicating the relative decoding costs of a
- * filter type. Higher costs indicate more decoding expense, and are
- * therefore less likely to be selected over a filter with lower computational
- * costs. There needs to be a value in "filter_costs" for each valid filter
- * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
- * setting the costs. Costs try to improve the speed of decompression without
- * unduly increasing the compressed image size.
- *
- * A negative weight or cost indicates the default value is to be used, and
- * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
- * The default values for both weights and costs are currently 1.0, but may
- * change if good general weighting/cost heuristics can be found. If both
- * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
- * to the UNWEIGHTED method, but with added encoding time/computation.
- */
-PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
- int heuristic_method, int num_weights, png_const_doublep filter_weights,
- png_const_doublep filter_costs))
-PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
- (png_structrp png_ptr, int heuristic_method, int num_weights,
- png_const_fixed_point_p filter_weights,
- png_const_fixed_point_p filter_costs))
-#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
-
-/* Heuristic used for row filter selection. These defines should NOT be
- * changed.
- */
-#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */
-#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */
-#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */
-#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */
+#define PNG_NO_FILTERS 0x00
+#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP)
+#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH)
#ifdef PNG_WRITE_SUPPORTED
-/* Set the library compression level. Currently, valid values range from
- * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
- * (0 - no compression, 9 - "maximal" compression). Note that tests have
- * shown that zlib compression levels 3-6 usually perform as well as level 9
- * for PNG images, and do considerably fewer caclulations. In the future,
- * these values may not correspond directly to the zlib compression levels.
- */
-PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
- int level));
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
+PNG_REMOVED(68, void, png_set_filter_heuristics,
+ (png_structrp png_ptr, int heuristic_method, int num_weights,
+ png_const_doublep filter_weights, png_const_doublep filter_costs),
+ PNG_DEPRECATED)
+PNG_REMOVED(209, void, png_set_filter_heuristics_fixed,
+ (png_structrp png_ptr, int heuristic_method, int num_weights,
+ png_const_fixed_point_p filter_weights,
+ png_const_fixed_point_p filter_costs),
+ PNG_DEPRECATED)
+ /* Neither of these API calls did anything in libpng 1.6, however they were
+ * not marked PNG_DEPRECATED, so they are converted to no-op function-like
+ * macros here. (NOTE: the macro arguments are evaluated once each, this
+ * will probably cause warnings with some compiler options: simply remove the
+ * function call after ensuring that the arguments had no side effects.)
+ */
+#define png_set_filter_heuristics(p,m,w,fw,fc) ((void)(p,m,w,fw,fc))
+#define png_set_filter_heuristics_fixed(p,m,w,fw,fc) ((void)(p,m,w,fw,fc))
+#endif /* WRITE_WEIGHTED_FILTER */
-PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
- int mem_level));
+#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+PNG_REMOVED(69, void, png_set_compression_level, (png_structrp png_ptr,
+ int level), PNG_EMPTY)
+#define png_set_compression_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_zlib_level, png_IDAT, (v)))
-PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr,
- int strategy));
+PNG_REMOVED(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
+ int mem_level), PNG_EMPTY)
+#define png_set_compression_mem_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_memLevel, png_IDAT, (v)))
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
- int window_bits));
+PNG_REMOVED(71, void, png_set_compression_strategy, (png_structrp png_ptr,
+ int strategy), PNG_EMPTY)
+#define png_set_compression_strategy(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_strategy, png_IDAT, (v)))
-PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
- int method));
-#endif
+PNG_REMOVED(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
+ int window_bits), PNG_EMPTY)
+#define png_set_compression_window_bits(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_windowBits, png_IDAT, (v)))
+
+PNG_REMOVED(73, void, png_set_compression_method, (png_structrp png_ptr,
+ int method), PNG_EMPTY)
+#define png_set_compression_method(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_method, png_IDAT, (v)))
+#endif /* WRITE_CUSTOMIZE_COMPRESSION */
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
/* Also set zlib parameters for compressing non-IDAT chunks */
-PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr,
- int level));
+PNG_REMOVED(222, void, png_set_text_compression_level, (png_structrp png_ptr,
+ int level), PNG_EMPTY)
+#define png_set_text_compression_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_zlib_level, png_zTXt, (v)))
-PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr,
- int mem_level));
+PNG_REMOVED(223, void, png_set_text_compression_mem_level,
+ (png_structrp png_ptr, int mem_level), PNG_EMPTY)
+#define png_set_text_compression_mem_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_memLevel, png_zTXt, (v)))
-PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
- int strategy));
+PNG_REMOVED(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
+ int strategy), PNG_EMPTY)
+#define png_set_text_compression_strategy(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_strategy, png_zTXt, (v)))
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
+PNG_REMOVED(225, void, png_set_text_compression_window_bits,
+ (png_structrp png_ptr, int window_bits), PNG_EMPTY)
+#define png_set_text_compression_window_bits(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_windowBits, png_zTXt, (v)))
+
+PNG_REMOVED(226, void, png_set_text_compression_method, (png_structrp png_ptr,
+ int method), PNG_EMPTY)
+#define png_set_text_compression_method(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_method, png_zTXt, (v)))
+
+/* NOTE: in versions of libpng prior to 1.7 iCCP compression was controlled by
+ * the text settings, hence the controls were only available if
+ * PNG_WRITE_CUSTOMIZIZE_ZTXT_COMPRESSION_SUPPORTED. In 1.7 the text settings
+ * no longer affect iCCP compression, the following macros must be used (if
+ * necessary):
*/
-PNG_EXPORT(225, void, png_set_text_compression_window_bits,
- (png_structrp png_ptr, int window_bits));
-
-PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
- int method));
-#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
+#define png_set_ICC_profile_compression_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_zlib_level, png_iCCP, (v)))
+#define png_set_ICC_profile_compression_mem_level(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_memLevel, png_iCCP, (v)))
+#define png_set_ICC_profile_compression_strategy(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_strategy, png_iCCP, (v)))
+#define png_set_ICC_profile_compression_window_bits(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_windowBits, png_iCCP, (v)))
+#define png_set_ICC_profile_compression_method(p, v) (png_setting((p),\
+ PNG_SW_COMPRESS_method, png_iCCP, (v)))
+#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+#endif /* WRITE */
/* These next functions are called for input/output, memory, and error
* handling. They are in the file pngrio.c, pngwio.c, and pngerror.c,
@@ -1887,15 +2204,15 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
/* This callback is called only for *unknown* chunks. If
* PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known
* chunks to be treated as unknown, however in this case the callback must do
- * any processing required by the chunk (e.g. by calling the appropriate
- * png_set_ APIs.)
+ * any processing required by the chunk and this is not possible for any chunk
+ * that affects the image reading (e.g. PLTE, tRNS).
*
* There is no write support - on write, by default, all the chunks in the
* 'unknown' list are written in the specified position.
*
* The integer return from the callback function is interpreted thus:
*
- * negative: An error occured, png_chunk_error will be called.
+ * negative: An error occurred; png_chunk_error will be called.
* zero: The chunk was not handled, the default unknown handling is used
* (even if this was a chunk that would otherwise be known.)
* NOTE: prior to libpng 1.7 handling values of
@@ -1904,6 +2221,11 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
* happens) so it was not possible to discard unknown chunk data if a
* user callback was installed.
* positive: The chunk was handled, libpng will ignore/discard it.
+ *
+ * WARNING: if this callback is set every chunk in the stream is temporarily
+ * read into a memory buffer. This has potential performance implications,
+ * particularly for small PNG images with large amounts of ancilliary
+ * information.
*/
PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
@@ -1954,8 +2276,8 @@ PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
*/
PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
png_bytep old_row, png_const_bytep new_row));
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+#endif /* READ_INTERLACING */
+#endif /* PROGRESSIVE_READ */
PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
png_alloc_size_t size), PNG_ALLOCATED);
@@ -1986,21 +2308,21 @@ PNG_REMOVED(99, void, png_data_freer, (png_const_structrp png_ptr,
png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED)
/* Flags for png_free_data */
-#define PNG_FREE_HIST 0x0008
-#define PNG_FREE_ICCP 0x0010
-#define PNG_FREE_SPLT 0x0020
-#define PNG_FREE_ROWS 0x0040
-#define PNG_FREE_PCAL 0x0080
-#define PNG_FREE_SCAL 0x0100
+#define PNG_FREE_HIST 0x0008U
+#define PNG_FREE_ICCP 0x0010U
+#define PNG_FREE_SPLT 0x0020U
+#define PNG_FREE_ROWS 0x0040U
+#define PNG_FREE_PCAL 0x0080U
+#define PNG_FREE_SCAL 0x0100U
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-# define PNG_FREE_UNKN 0x0200
+# define PNG_FREE_UNKN 0x0200U
#endif
-/* PNG_FREE_LIST 0x0400 removed in 1.6.0 because it is ignored */
-#define PNG_FREE_PLTE 0x1000
-#define PNG_FREE_TRNS 0x2000
-#define PNG_FREE_TEXT 0x4000
-#define PNG_FREE_ALL 0x7fff
-#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
+/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */
+#define PNG_FREE_PLTE 0x1000U
+#define PNG_FREE_TRNS 0x2000U
+#define PNG_FREE_TEXT 0x4000U
+#define PNG_FREE_ALL 0x7fffU
+#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
#ifdef PNG_USER_MEM_SUPPORTED
/* These were deprecated in libpng 1.6.0 and have been removed from libpng
@@ -2017,16 +2339,18 @@ PNG_REMOVED(101, void, png_free_default, (png_const_structrp png_ptr,
/* Fatal error in PNG image of libpng - can't continue */
PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr,
png_const_charp error_message), PNG_NORETURN);
-
-/* The same, but the chunk name is prepended to the error string. */
-PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
- png_const_charp error_message), PNG_NORETURN);
-
#else
/* Fatal error in PNG image of libpng - can't continue */
PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
# define png_error(s1,s2) png_err(s1)
-# define png_chunk_error(s1,s2) png_err(s1)
+#endif
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
+/* The same, but the chunk name is prepended to the error string. */
+PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
+ png_const_charp error_message), PNG_NORETURN);
+#else
+# define png_chunk_error(s1,s2) png_error(s1,s2)
#endif
#ifdef PNG_WARNINGS_SUPPORTED
@@ -2054,15 +2378,38 @@ PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr,
png_const_charp warning_message));
#endif
-PNG_EXPORT(109, void, png_set_benign_errors,
- (png_structrp png_ptr, int allowed));
+#define png_set_error_action(png_ptr, what, action)\
+ (png_setting((png_ptr), PNG_SRW_ERROR_HANDLING, (what), (action)))
+ /* Control the handling of 'benign' errors; errors that can be handled in
+ * some way. The action is one of the following values:
+ */
+#define PNG_IGNORE 0 /* ignore the error; no warning or error message */
+#define PNG_WARN 1 /* call png_warning with an appropriate error message */
+#define PNG_ERROR 2 /* call png_error with the error message */
+ /* 'what' is a list (bit mask) of the errors to set: */
+#define PNG_BENIGN_ERRORS (1U)
+#define PNG_APP_WARNINGS (2U)
+#define PNG_APP_ERRORS (4U)
+#define PNG_IDAT_ERRORS (8U)
+#define PNG_SAFE_ERRORS (PNG_BENIGN_ERRORS+PNG_APP_WARNINGS+PNG_APP_ERRORS)
+#define PNG_ALL_ERRORS (PNG_SAFE_ERRORS+PNG_IDAT_ERRORS)
+
+PNG_REMOVED(109, void, png_set_benign_errors,
+ (png_structrp png_ptr, int allowed), PNG_EMPTY)
+#define png_set_benign_errors(png_ptr, allowed) (png_setting((png_ptr),\
+ PNG_SRW_ERROR_HANDLING, PNG_SAFE_ERRORS,\
+ (allowed) ? PNG_WARN : PNG_ERROR))
+ /* Turn all errors that can be handled into warnings, or turn them back into
+ * errors if 'allowed' is false.
+ */
+
#else
# ifdef PNG_ALLOW_BENIGN_ERRORS
-# define png_benign_error png_warning
-# define png_chunk_benign_error png_chunk_warning
+# define png_benign_error(pp,e) png_warning(pp,e)
+# define png_chunk_benign_error(pp,e) png_chunk_warning(pp,e)
# else
-# define png_benign_error png_error
-# define png_chunk_benign_error png_chunk_error
+# define png_benign_error(pp,e) png_error(pp,e)
+# define png_chunk_benign_error(pp,e) png_chunk_error(pp,e)
# endif
#endif
@@ -2083,16 +2430,18 @@ PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr,
png_const_inforp info_ptr, png_uint_32 flag));
/* Returns number of bytes needed to hold a transformed row. */
-PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr,
+PNG_EXPORT(111, png_alloc_size_t, png_get_rowbytes, (png_const_structrp png_ptr,
png_const_inforp info_ptr));
-#ifdef PNG_INFO_IMAGE_SUPPORTED
+#ifdef PNG_READ_PNG_SUPPORTED
/* Returns row_pointers, which is an array of pointers to scanlines that was
* returned from png_read_png().
*/
PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr,
png_const_inforp info_ptr));
+#endif
+#ifdef PNG_WRITE_PNG_SUPPORTED
/* Set row_pointers, which is an array of pointers to scanlines for use
* by png_write_png().
*/
@@ -2157,7 +2506,7 @@ PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
(png_const_structrp png_ptr, png_const_inforp info_ptr));
-#endif /* PNG_EASY_ACCESS_SUPPORTED */
+#endif /* EASY_ACCESS */
#ifdef PNG_READ_SUPPORTED
/* Returns pointer to signature string read from PNG header */
@@ -2413,7 +2762,7 @@ PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr,
PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
png_inforp info_ptr, int unit,
png_const_charp swidth, png_const_charp sheight));
-#endif /* PNG_sCAL_SUPPORTED */
+#endif /* sCAL */
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
/* Provide the default handling for all unknown chunks or, optionally, for
@@ -2510,8 +2859,10 @@ PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
* except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to
* be processed by libpng.
*/
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
int keep, png_const_bytep chunk_list, int num_chunks));
+#endif /* HANDLE_AS_UNKNOWN */
/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned;
* the result is therefore true (non-zero) if special handling is required,
@@ -2519,7 +2870,7 @@ PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
*/
PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
png_const_bytep chunk_name));
-#endif
+#endif /* SET_UNKNOWN_CHUNKS */
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr,
@@ -2532,6 +2883,12 @@ PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr,
* code won't be compiled on earlier versions you can rely on
* png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing
* the correct thing.
+ *
+ * API CHANGE: in 1.7.0 this API will not work on read unless
+ * PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED is set; it may be necessary to change
+ * code to check the latter SUPPORTED flag. png_set_keep_unknown_chunks
+ * will issue a warning if it is asked to save a chunk and there is no read
+ * support.
*/
PNG_EXPORT(175, void, png_set_unknown_chunk_location,
@@ -2548,17 +2905,15 @@ PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr,
PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr,
png_inforp info_ptr, int mask));
-#ifdef PNG_INFO_IMAGE_SUPPORTED
/* The "params" pointer is currently not used and is for future expansion. */
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_READ_PNG_SUPPORTED
PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
int transforms, png_voidp params));
#endif
-#ifdef PNG_WRITE_SUPPORTED
+#ifdef PNG_WRITE_PNG_SUPPORTED
PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
int transforms, png_voidp params));
#endif
-#endif
PNG_EXPORT(180, png_const_charp, png_get_copyright,
(png_const_structrp png_ptr));
@@ -2581,12 +2936,10 @@ PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
#define PNG_HANDLE_CHUNK_ALWAYS 3
#define PNG_HANDLE_CHUNK_LAST 4
-/* Strip the prepended error numbers ("#nnn ") from error and warning
- * messages before passing them to the error or warning handler.
- */
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
- png_uint_32 strip_mode));
+/* This was never implemented: */
+PNG_REMOVED(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
+ png_uint_32 strip_mode), PNG_EMPTY)
#endif
/* Added in libpng-1.2.6 */
@@ -2637,8 +2990,8 @@ PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
int *unit_type));
-# endif /* PNG_pHYs_SUPPORTED */
-#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */
+# endif /* pHYs */
+#endif /* INCH_CONVERSIONS */
/* Added in libpng-1.4.0 */
#ifdef PNG_IO_STATE_SUPPORTED
@@ -2652,16 +3005,16 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
(png_const_structrp png_ptr));
/* The flags returned by png_get_io_state() are the following: */
-# define PNG_IO_NONE 0x0000 /* no I/O at this moment */
-# define PNG_IO_READING 0x0001 /* currently reading */
-# define PNG_IO_WRITING 0x0002 /* currently writing */
-# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */
-# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */
-# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */
-# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */
-# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */
-# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */
-#endif /* ?PNG_IO_STATE_SUPPORTED */
+# define PNG_IO_NONE 0x0000U /* no I/O at this moment */
+# define PNG_IO_READING 0x0001U /* currently reading */
+# define PNG_IO_WRITING 0x0002U /* currently writing */
+# define PNG_IO_SIGNATURE 0x0010U /* currently at the file signature */
+# define PNG_IO_CHUNK_HDR 0x0020U /* currently at the chunk header */
+# define PNG_IO_CHUNK_DATA 0x0040U /* currently at the chunk data */
+# define PNG_IO_CHUNK_CRC 0x0080U /* currently at the chunk crc */
+# define PNG_IO_MASK_OP 0x000fU /* current operation: reading/writing */
+# define PNG_IO_MASK_LOC 0x00f0U /* current location: sig/hdr/data/crc */
+#endif /* IO_STATE */
/* Interlace support. The following macros are always defined so that if
* libpng interlace handling is turned off the macros may be used to handle
@@ -2685,7 +3038,7 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1))
/* Two macros to help evaluate the number of rows or columns in each
- * pass. This is expressed as a shift - effectively log2 of the number or
+ * pass. This is expressed as a shift - effectively 3-log2 of the number or
* rows or columns in each 8x8 tile of the original image.
*/
#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
@@ -2725,6 +3078,25 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+/* Whether the pass is in the image at all, taking into account the full image
+ * width and height, evaluates 'pass' lots of times, but width and height at
+ * most once each.
+ */
+#define PNG_PASS_IN_IMAGE(width, height, pass)\
+ ((height) > PNG_PASS_START_ROW(pass) && (width) > PNG_PASS_START_COL(pass))
+
+/* A macro to find the last pass (in the range 0 to 6) given an image width and
+ * height. Then two macros two find whether a given image row or column which
+ * is present in the pass is the last row or column in the pass. Note that
+ * these macros return 'true' for earlier rows or columns of the image that are
+ * *not* in the pass.
+ */
+#define PNG_LAST_PASS(width, height) ((height) > 1 ? 6 : ((width) > 1 ? 5 : 0))
+#define PNG_LAST_PASS_ROW(y, pass, height)\
+ ((y) + PNG_PASS_ROW_OFFSET(pass) >= (height))
+#define PNG_LAST_PASS_COL(x, pass, width)\
+ ((x) + PNG_PASS_COL_OFFSET(pass) >= (width))
+
#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
/* With these routines we avoid an integer divide, which will be slower on
* most machines. However, it does take more operations than the corresponding
@@ -2765,7 +3137,7 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
(composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
(png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \
32767) / 65535)
-#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+#endif /* READ_COMPOSITE_NODIV */
#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
@@ -2780,10 +3152,17 @@ PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr,
/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
-#endif
-#ifdef PNG_SAVE_INT_32_SUPPORTED
-PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
-#endif
+
+/* This becomes a macro in 1.7 because the old implementation was wrong; it
+ * failed to do the cast. ANSI C requires the cast to convert a negative number
+ * to the 2's complement form, so this just works:
+ */
+#define png_save_int_32(b, i) png_save_uint_32(b, i);
+#endif /* WRITE_INT_FUNCTIONS */
+
+/* Apps that used this will use the macro in 1.7. */
+PNG_REMOVED(206, void, png_save_int_32, (png_bytep buf, png_int_32 i),
+ PNG_DEPRECATED)
/* Place a 16-bit number into a buffer in PNG byte order.
* The parameter is declared unsigned int, not png_uint_16,
@@ -2827,59 +3206,92 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
#endif
#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-PNG_EXPORT(242, void, png_set_check_for_invalid_index,
- (png_structrp png_ptr, int allowed));
-# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+PNG_REMOVED(242, void, png_set_check_for_invalid_index,
+ (png_structrp png_ptr, int enabled_if_greater_than_0), PNG_EMPTY)
+#define png_set_check_for_invalid_index(png_ptr, value)\
+ (png_setting((png_ptr), PNG_SRW_CHECK_FOR_INVALID_INDEX, 0, (value)))
+ /* By default the check is enabled on both read and write when the number of
+ * entries in the palette is less than the maximum required by the bit depth
+ * of a palette image.
+ *
+ * Passing 1 to 'enabled' turns the check on in all cases.
+ * Passing -1 turns it off and the PNG may have invalid palette index values.
+ * Passing 0 restores the default.
+ *
+ * On read chunk (benign) error messages are only produced with the default
+ * setting; it is assumed that when the check is turned on explicitly the
+ * caller will call png_get_palette_max to check the result.
+ *
+ * The png_setting call returns 0.
+ */
+#endif /* CHECK_FOR_INVALID_INDEX */
+#ifdef PNG_GET_PALETTE_MAX_SUPPORTED
PNG_EXPORT(243, int, png_get_palette_max, (png_const_structrp png_ptr,
png_const_inforp info_ptr));
-# endif
-#endif /* CHECK_FOR_INVALID_INDEX */
+ /* The info_ptr is not used, it may be NULL in 1.7.0 (not in earlier
+ * versions). If the information is not available because
+ * png_set_check_for_invalid_index was not used to turn it on -1 is returned.
+ * Valid results can only be obtained after the complete image has been read,
+ * though it may be called at any time to get the result so far.
+ */
+#endif /* GET_PALETTE_MAX */
-/*******************************************************************************
- * IMPLEMENTATION OPTIONS
- *******************************************************************************
+/* Memory format options; these return information about the layout of the
+ * transformed row using the Simplified API PNG_FORMAT_ values (see below for
+ * the #defines).
*
- * Support for arbitrary implementation-specific optimizations. The API allows
- * particular options to be turned on or off. 'Option' is the number of the
- * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given
- * by the PNG_OPTION_ defines below.
+ * These are only relevant if read or write transforms are supported; these
+ * may cause the memory format of pixel data to differ from that used in the
+ * PNG file itself. Nevertheless the APIs are supported regardless of whether
+ * transforms are applied; use these to consistently and safely determine the
+ * layout of the image in memory.
*
- * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions,
- * are detected at run time, however sometimes it may be impossible
- * to do this in user mode, in which case it is necessary to discover
- * the capabilities in an OS specific way. Such capabilities are
- * listed here when libpng has support for them and must be turned
- * ON by the application if present.
- *
- * SOFTWARE: sometimes software optimizations actually result in performance
- * decrease on some architectures or systems, or with some sets of
- * PNG images. 'Software' options allow such optimizations to be
- * selected at run time.
+ * Some of the same information can be obtained from png_info, however this
+ * does not record whether the byte or bit formats have been changed.
*/
-#ifdef PNG_SET_OPTION_SUPPORTED
-#ifdef PNG_ARM_NEON_API_SUPPORTED
-# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */
-#endif
-#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
-#define PNG_OPTION_NEXT 4 /* Next option - numbers must be even */
+PNG_EXPORT(246, unsigned int, png_memory_format, (png_structrp png_ptr));
+ /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the
+ * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the
+ * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR:
+ *
+ * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call
+ * png_memory_gamma to find the correct value.
+ *
+ * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call
+ * png_memory_channel_depth to find the correct value.
+ *
+ * It is only valid to call these APIS *after* either png_read_update_info
+ * or png_start_read_image on read or after the first row of an image has
+ * been written on write.
+ *
+ * To find the number of channels in each pixel from the returned value,
+ * 'fmt' use:
+ *
+ * PNG_FORMAT_CHANNELS(fmt)
+ */
-/* Return values: NOTE: there are four values and 'off' is *not* zero */
-#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */
-#define PNG_OPTION_INVALID 1 /* Option number out of range */
-#define PNG_OPTION_OFF 2
-#define PNG_OPTION_ON 3
+PNG_EXPORT(247, unsigned int, png_memory_channel_depth, (png_structrp png_ptr));
+ /* The actual depth of each channel in the image, to determine the full pixel
+ * depth (in bits) use:
+ *
+ * png_memory_channel_depth(pp) * PNG_FORMAT_CHANNELS(fmt)
+ */
-PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
- int onoff));
-#endif
-
-/*******************************************************************************
- * END OF HARDWARE OPTIONS
- ******************************************************************************/
-
-/* Maintainer: Put new public prototypes here ^, in libpng.3, and project
- * defs
- */
+#ifdef PNG_GAMMA_SUPPORTED
+PNG_EXPORT(248, png_fixed_point, png_memory_gamma, (png_structrp png_ptr));
+ /* The actual gamma of the image data, scaled by 100,000. This is the
+ * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will
+ * return 0.
+ *
+ * On write this invariably returns 0; libpng does not change the gamma of
+ * the data on write.
+ *
+ * Note that this is not always the exact inverse of the 'screen gamma'
+ * passed to png_set_gamma; internal optimizations remove attempts to make
+ * small changes to the gamma value. This function returns the actual
+ * output value.
+ */
+#endif /* GAMMA */
/*******************************************************************************
* Section 5: SIMPLIFIED API
@@ -2952,7 +3364,7 @@ typedef struct
# define PNG_IMAGE_WARNING 1
# define PNG_IMAGE_ERROR 2
/*
- * The result is a two bit code such that a value more than 1 indicates
+ * The result is a two-bit code such that a value more than 1 indicates
* a failure in the API just called:
*
* 0 - no warning or error
@@ -2993,7 +3405,7 @@ typedef struct
*
* When the simplified API needs to convert between sRGB and linear colorspaces,
* the actual sRGB transfer curve defined in the sRGB specification (see the
- * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2
+ * article at https://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2
* approximation used elsewhere in libpng.
*
* When an alpha channel is present it is expected to denote pixel coverage
@@ -3039,20 +3451,79 @@ typedef struct
#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */
#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2 byte channels else 1 byte */
#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */
+#define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */
+#define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */
+ /* other bits RESERVED */
-#ifdef PNG_FORMAT_BGR_SUPPORTED
-# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */
-#endif
+/* The PNG color type value can be derived from a format which repesents a valid
+ * PNG format using the following macro. Note that if any of the non-PNG
+ * format elements are use, such as BGR or AFIRST, the color type value that
+ * results does represent the number of channels in the format but may not
+ * represent their order or encoding.
+ *
+ * NOTE: the format can encode illegal PNG formats, such as a colormap with
+ * alpha or without color; these are legal simplified API formats which produce
+ * data that cannot be represented as PNG regardless of channel order or
+ * encoding.
+ *
+ * The macro below is the bit shift version, a multiplicative version which only
+ * evaluates 'f' once is:
+ *
+ * ((((((((f) * 0x111) & 0x128) * 0x3) & 0x130) * 0x5) >> 6) & 0x7)
+ */
+#define PNG_COLOR_TYPE_FROM_FORMAT(f)\
+ ((((f) & (!((f) & PNG_FORMAT_FLAG_AFILLER))) << 2) |\
+ (((f) & PNG_FORMAT_FLAG_COLOR) ) |\
+ (((f) & PNG_FORMAT_FLAG_COLORMAP) >> 3))
-#ifdef PNG_FORMAT_AFIRST_SUPPORTED
-# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */
-#endif
+/* The inverse: note that PNG_FORMAT_FLAG_LINEAR is not set by this macro and
+ * that there is no handling for a 'filler' channel, consequently the macro must
+ * only be used on genuine PNG color types, not the result of png_get_color_type
+ * after transforms have been applied to the original PNG data.
+ */
+#define PNG_FORMAT_FROM_COLOR_TYPE(c)\
+ ((((c) & PNG_COLOR_MASK_ALPHA) >> 2) |\
+ (((c) & PNG_COLOR_MASK_COLOR) ) |\
+ (((c) & PNG_COLOR_MASK_PALETTE) << 3))
+
+/* The following flags are not used by the simplified API but may be returned
+ * by png_memory_format. Presence of any of these flags means that the values
+ * in the image (in memory) cannot be handled 'normally'.
+ */
+#define PNG_FORMAT_FLAG_AFILLER 0x40U /* The 'alpha' channel is a filler:
+ * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel
+ * is not an alpha value and (therefore) cannot be used for alpha
+ * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT
+ * will return a color type *without* PNG_COLOR_MASK_ALPHA, however
+ * PNG_FORMAT_CHANNELS will return the correct number, including the
+ * filler channel.
+ */
+#define PNG_FORMAT_FLAG_SWAPPED 0x80U /* bytes or bits swapped:
+ * When the bit depth is 16 this means that the bytes within the
+ * components have been swapped, when the bit depth is less than 8
+ * it means the pixels within the bytes have been swapped. It should
+ * not be set for 8-bit compononents (it is meaningless). */
+#define PNG_FORMAT_FLAG_RANGE 0x100U /* component range not 0..bit-depth:
+ * Low-bit-depth grayscale components have been unpacked into bytes
+ * without scaling, or RGB[A] pixels have been shifted back to the
+ * significant-bit range from the sBIT chunk or channels (currently
+ * alpha or gray) have been inverted. */
+#define PNG_FORMAT_FLAG_INVALID 0x8000U /* Invalid simplified API channel depth:
+ * For single channel grayscale and palette indexed image data PNG
+ * supports bit depths of 1, 2 or 4 bits per pixel (and per channel)
+ * packed into bytes. The simplified API macros will not work with
+ * these formats (the simplified API always uses 8 or 16-bit channels).
+ * In the simplified API 'linear' images always have 16-bit channels
+ * and non-linear images are always sRGB encoded. If the INVALID flag
+ * is set then this may not be true; it is necessary to check the
+ * memory format bit-depth and gamma separately.
+ */
/* Commonly used formats have predefined macros.
*
* First the single byte (sRGB) formats:
*/
-#define PNG_FORMAT_GRAY 0
+#define PNG_FORMAT_GRAY 0U
#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA
#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST)
#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR
@@ -3143,6 +3614,12 @@ typedef struct
* color-mapped image.
*/
+#define PNG_FORMAT_CHANNELS(fmt) PNG_IMAGE_PIXEL_CHANNELS(fmt)
+ /* Synonym for the above for use with the result of png_get_memory_format.
+ * This exists to avoid confusion with the PNG_IMAGE_ macros which do not
+ * work on all possible results of png_get_memory_format.
+ */
+
#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\
PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt)
/* The size, in bytes, of each component in a pixel; 1 for a color-mapped
@@ -3154,17 +3631,36 @@ typedef struct
/* Information about the whole row, or whole image */
#define PNG_IMAGE_ROW_STRIDE(image)\
- (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width)
+ (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (png_alloc_size_t)(image).width)
/* Return the total number of components in a single row of the image; this
* is the minimum 'row stride', the minimum count of components between each
* row. For a color-mapped image this is the minimum number of bytes in a
* row.
+ *
+ * WARNING: libpng 1.7: this macro now returns a png_alloc_size_t, previous
+ * versions returned a png_uint_32 and could overflow for images that fit in
+ * memory. This macro can still overflow, but if it does the row will not
+ * fit in memory. The simplified API functions detect this and refuse to
+ * handle the image.
*/
#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\
(PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride))
/* Return the size, in bytes, of an image buffer given a png_image and a row
* stride - the number of components to leave space for in each row.
+ *
+ * WARNING: This is the total size of the image, for large images it will
+ * overflow on a 32-bit system. In libpng 1.7 (but not before) it returns a
+ * png_alloc_size_t which means that the result only overflows for
+ * ridiculously large PNG files. libpng checks and will refuse to handle
+ * such data (the PNG is probably invalid.)
+ *
+ * Take great care over the type of 'row_stride'; libpng assumes that the
+ * type is png_alloc_size_t, as returned by PNG_IMAGE_ROW_STRIDE. You can
+ * use any type you like but libpng only checks for overflow when the type is
+ * png_alloc_size_t. In particular for png_uint_32 on a 64-bit system you
+ * must do your own overflow checking. Cast row_stride as (png_alloc_size_t)
+ * to avoid this (check for overflow before the cast of course!)
*/
#define PNG_IMAGE_SIZE(image)\
@@ -3237,14 +3733,14 @@ PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image,
PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
FILE* file));
/* The PNG header is read from the stdio FILE object. */
-#endif /* PNG_STDIO_SUPPORTED */
+#endif /* STDIO */
PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
png_const_voidp memory, size_t size));
/* The PNG header is read from the given memory buffer. */
PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
- png_const_colorp background, void *buffer, png_int_32 row_stride,
+ png_const_colorp background, void *buffer, ptrdiff_t row_stride,
void *colormap));
/* Finish reading the image into the supplied buffer and clean up the
* png_image structure.
@@ -3282,10 +3778,9 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image));
/* Free any data allocated by libpng in image->opaque, setting the pointer to
* NULL. May be called at any time after the structure is initialized.
*/
-#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
+#endif /* SIMPLIFIED_READ */
#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
-#ifdef PNG_STDIO_SUPPORTED
/* WRITE APIS
* ----------
* For write you must initialize a png_image structure to describe the image to
@@ -3302,17 +3797,19 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image));
* values do not correspond to the colors in sRGB.
* colormap_entries: set to the number of entries in the color-map (0 to 256)
*/
+#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
const char *file, int convert_to_8bit, const void *buffer,
- png_int_32 row_stride, const void *colormap));
+ ptrdiff_t row_stride, const void *colormap));
/* Write the image to the named file. */
PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
- int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
+ int convert_to_8_bit, const void *buffer, ptrdiff_t row_stride,
const void *colormap));
/* Write the image to the given (FILE*). */
+#endif /* SIMPLIFIED_WRITE_STDIO */
-/* With both write APIs if image is in one of the linear formats with 16-bit
+/* With all write APIs if image is in one of the linear formats with 16-bit
* data then setting convert_to_8_bit will cause the output to be an 8-bit PNG
* gamma encoded according to the sRGB specification, otherwise a 16-bit linear
* encoded PNG file is written.
@@ -3324,22 +3821,426 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
*
* With all APIs row_stride is handled as in the read APIs - it is the spacing
* from one row to the next in component sized units (1 or 2 bytes) and if
- * negative indicates a bottom-up row layout in the buffer.
+ * negative indicates a bottom-up row layout in the buffer. If row_stride is
+ * zero, libpng will calculate it for you from the image width and number of
+ * channels.
*
- * Note that the write API does not support interlacing or sub-8-bit pixels.
+ * Note that the write API does not support interlacing, sub-8-bit pixels or
+ * most ancillary chunks. If you need to write text chunks (e.g. for copyright
+ * notices) you need to use one of the other APIs.
*/
-#endif /* PNG_STDIO_SUPPORTED */
-#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */
+
+PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
+ png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit,
+ const void *buffer, ptrdiff_t row_stride, const void *colormap));
+ /* Write the image to the given memory buffer. The function both writes the
+ * whole PNG data stream to *memory and updates *memory_bytes with the count
+ * of bytes written.
+ *
+ * 'memory' may be NULL. In this case *memory_bytes is not read however on
+ * success the number of bytes which would have been written will still be
+ * stored in *memory_bytes. On failure *memory_bytes will contain 0.
+ *
+ * If 'memory' is not NULL it must point to memory[*memory_bytes] of
+ * writeable memory.
+ *
+ * If the function returns success memory[*memory_bytes] (if 'memory' is not
+ * NULL) contains the written PNG data. *memory_bytes will always be less
+ * than or equal to the original value.
+ *
+ * If the function returns false and *memory_bytes was not changed an error
+ * occured during write. If *memory_bytes was changed, or is not 0 if
+ * 'memory' was NULL, the write would have succeeded but for the memory
+ * buffer being too small. *memory_bytes contains the required number of
+ * bytes and will be bigger that the original value.
+ */
+
+#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\
+ row_stride, colormap)\
+ png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\
+ row_stride, colormap)
+ /* Return the amount of memory in 'size' required to compress this image.
+ * The png_image structure 'image' must be filled in as in the above
+ * function and must not be changed before the actual write call, the buffer
+ * and all other parameters must also be identical to that in the final
+ * write call. The 'size' variable need not be initialized.
+ *
+ * NOTE: the macro returns true/false, if false is returned 'size' will be
+ * set to zero and the write failed and probably will fail if tried again.
+ */
+
+/* You can pre-allocate the buffer by making sure it is of sufficient size
+ * regardless of the amount of compression achieved. The buffer size will
+ * always be bigger than the original image and it will never be filled. The
+ * following macros are provided to assist in allocating the buffer.
+ */
+#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height)
+ /* The number of uncompressed bytes in the PNG byte encoding of the image;
+ * uncompressing the PNG IDAT data will give this number of bytes.
+ */
+#ifndef PNG_ZLIB_MAX_SIZE
+# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U)
+ /* An upper bound on the number of compressed bytes given 'b' uncompressed
+ * bytes. This is based on deflateBounds() in zlib; different
+ * implementations of zlib compression may conceivably produce more data so
+ * if your zlib implementation is not zlib itself redefine this macro
+ * appropriately.
+ */
+#endif
+
+#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\
+ PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image))
+ /* An upper bound on the size of the data in the PNG IDAT chunks. */
+
+#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\
+ ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\
+ (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\
+ 12U+3U*(image).colormap_entries/*PLTE data*/+\
+ (((image).format&PNG_FORMAT_FLAG_ALPHA)?\
+ 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\
+ 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size))
+ /* A helper for the following macro; if your compiler cannot handle the
+ * following macro use this one with the result of
+ * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most
+ * compilers should handle this just fine.)
+ */
+
+#define PNG_IMAGE_PNG_SIZE_MAX(image)\
+ PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image))
+ /* An upper bound on the total length of the PNG data stream for 'image'.
+ * The result is of type png_alloc_size_t, on 32-bit systems this may
+ * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will
+ * run out of buffer space but return a corrected size which should work.
+ *
+ * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this
+ * macro can because of the extra bytes used in the PNG byte encoding. You
+ * need to avoid this macro if your image size approaches the limit of your
+ * system memory; typically the maximum value of size_t. Use the above
+ * function call instead.
+ */
+#endif /* SIMPLIFIED_WRITE */
/*******************************************************************************
* END OF SIMPLIFIED API
******************************************************************************/
+/*******************************************************************************
+ * Section 6: IMPLEMENTATION OPTIONS
+ *******************************************************************************
+ *
+ * Change of options used during read and/or write.
+ *
+ * A number of internal options can (but do not need to be) changed to
+ * fine tune the implementation. These options control such things as the
+ * precise settings for compression, the accuracy of arithmetic used internally
+ * for image processing operations (gamma transformations) and, in some cases,
+ * the specific implementations (hardware or software optimizations.)
+ *
+ * To avoid API proliferation there is a single general API (new in 1.7) to do
+ * this. When a particular option is not supported by the build in libpng an
+ * attempt to set it will return a failure code but will be totally ignored
+ * unless the PNG_SF_ERROR flag is set (see below).
+ */
+PNG_EXPORT(249, png_int_32, png_setting, (png_structrp png_ptr,
+ png_uint_32 setting, png_uint_32 parameter, png_int_32 value));
+ /* Alter setting 'setting' using the values of 'parameter' and 'value'. The
+ * result is either one of the following failure codes or a setting/parameter
+ * specific result code.
+ *
+ * The failure codes match the POSIX 1003.1 values (section 2.5,
+ * error numbers) with a preceding PNG_. (png_uint_32)result gives a number
+ * in the range 0x80000001U to 0x8000000fU.
+ */
+# define PNG_EBADF (-0x7fffffff) /* read/write error */
+ /* An attempt was made to apply a read setting to a write structure or
+ * vice versa.
+ */
+# define PNG_EINVAL (-0x7ffffffe) /* invalid argument */
+ /* 'png_ptr' was NULL or 'parameter' or 'value' is invalid for the given
+ * setting.
+ */
+# define PNG_EDOM (-0x7ffffffd) /* out of range */
+ /* Either 'parameter' or 'value' is out of range for the given setting
+ * (only returned when paramter or value are used and are numeric; for
+ * flag values PNG_EINVAL will be returned.)
+ */
+# define PNG_ENOSYS (-0x7ffffff1) /* unsupported setting/param */
+ /* The setting was not recognized; typically this means that libpng was
+ * built without the appropriate support.
+ */
+# define PNG_UNSUPPORTED_SETTING PNG_ENOSYS
+ /* For backware compatibility with earlier libpng versions and
+ * 'png_set_option' return codes.
+ */
+# define PNG_UNSET (-0x7ffffff0) /* NOT an erro code: no previous setting */
+ /* The setting was not (previously) set. Returned when there is no built
+ * in default for a setting. Normally this means that the default will
+ * depend on other settings or the PNG itself.
+ */
+ /* Results larger (more positive) than PNG_ENOSYS are success codes (even if
+ * negative). The value is interpreted as follows (as defined by the
+ * setting):
+ *
+ * 1) A signed 31-bit number in the range -0x7fffffef to +0x7fffffff
+ * 2) An unsigned 31 bit number in the range 0U to 0x7fffffffU
+ * 3) An unsigned 32 bit bit set/flag value in the range 0U to 0xfffffffU
+ * but excluding values in the range 0x80000000U to 0x80000000FU
+ * encoded as follows:
+ *
+ * if (v <= 0x7fffffffU)
+ * v
+ * else if (v > 0x8000000FU)
+ * -(png_int_32)-v
+ *
+ * The result can be converted by to the original (png_uint_32) simply
+ * by casting it as such.
+ */
+# define PNG_FAILED(result) ((result) <= PNG_ENOSYS)
+ /* The setting did not take; this includes both errors making the setting
+ * (e.g. parameter or value errors) and unsupported settings. Check the
+ * result code itself for more information.
+ */
+# define PNG_OK(result) ((result) > PNG_ENOSYS)
+ /* The setting succeeded; the result is a return code which depends on the
+ * particular setting. (E.g. it might be a return code or it might be the
+ * previous value.)
+ */
+
+/* SETTING VALUES (generic)
+ *
+ * These are flag values that are added to the setting definitions below to
+ * simplify processing inside libpng and self-document the setting behavior.
+ * All these values have the prefix PNG_SF_
+ */
+#define PNG_SF_ERROR (0x80000000U)
+ /* If this is set on the 'setting' argument to png_setting and a failure code
+ * would otherwise be returned call png_error instead. This is a convenience
+ * for applications that do not want to check the result code. It is never
+ * set by default. The error string is cryptic.
+ */
+#define PNG_SF_GET (0x40000000U)
+ /* Do not set the setting. With most settings this just allows for the
+ * presence of support for the setting to be checked at run time; if the
+ * setting is not support PNG_ENOSYS will be returned.
+ *
+ * With some settings checking of the parameter or value may be done, but
+ * there is no guarantee, so always supply valid parameter and value.
+ *
+ * With some settings the current setting is returned. This is typically
+ * only done when the default setting is configurable and not even always
+ * then. If the setting does this it will document the behavior.
+ */
+#define PNG_SF_READ (0x20000000U)
+ /* The setting may be applied to a read png_struct. If this is not set and
+ * an attempt is made to apply the setting to a read struct
+ * PNG_EBADF will be returned.
+ */
+#define PNG_SF_WRITE (0x10000000U)
+ /* The setting may be applied to a write png_struct. If this is not set
+ * and an attempt is made to apply the setting to a write struct
+ * PNG_EBADF will be returned.
+ */
+
+/*********************************** WRITE ************************************/
+/* WRITE COMPRESSION SUPPORT
+ *
+ * These settings are normally accessed using the macros that are defined above;
+ * the function-like macros replace the API calls present in previous versions
+ * of libpng.
+ *
+ * 'setting' is as follows, 'parameter' is a chunk name; png_IDAT for IDAT
+ * compression, png_iCCP for iCCP chunk compression png_zTXt for zTZt *and* iTXt
+ * text chunk compression. Other values must not be used; they will result in
+ * PNG_ENOSYS at present but may alter compression of new chunks in the future.
+ *
+ * The value is the new compression setting. The result is is the old
+ * compression setting or an error code. Compression settings are documented
+ * in text above describing the function-like macros. PNG_UNSET is returned
+ * when the setting was not previously set; in this case the default may vary
+ * according to the actual data (e.g. length, PNG format).
+ *
+ * 0 is valid as a parameter if PNG_SF_GET is set, in that case the current or
+ * last setting is returned.
+ */
+#define PNG_SW_COMPRESS_zlib_level (PNG_SF_WRITE + 0U)
+#define PNG_SW_COMPRESS_windowBits (PNG_SF_WRITE + 1U)
+#define PNG_SW_COMPRESS_memLevel (PNG_SF_WRITE + 2U)
+#define PNG_SW_COMPRESS_strategy (PNG_SF_WRITE + 3U)
+#define PNG_SW_COMPRESS_png_level (PNG_SF_WRITE + 4U)
+#define PNG_SW_COMPRESS_method (PNG_SF_WRITE + 5U)
+
+/* WRITE IDAT size.
+ *
+ * The size of the IDAT chunks that are written (the last may be smaller).
+ */
+#define PNG_SW_IDAT_size (PNG_SF_WRITE + 6U)
+
+/* WRITE FILTER CONTROL
+ *
+ * These settings are used by png_set_filter and png_set_row_buffers to control
+ * the filters used during compression. The 'filters' setting has two arguments
+ * however the first is the filter method (or type) and must be 0 for PNG.
+ * Standards based on PNG may define additional values, as with other base file
+ * characteristics such as the compression type, however the result would not be
+ * a PNG.
+ */
+#define PNG_SW_COMPRESS_filters (PNG_SF_WRITE + 7U)
+#define PNG_SW_COMPRESS_row_buffers (PNG_SF_WRITE + 8U)
+
+/* WRITE ROW FLUSH CONTROL
+ *
+ * This sets the number of rows between flush calls. '0' was used to indicate
+ * no flushing (before the end). The maximum number of rows in a PNG is
+ * actually greater than the maximum of a 31-bit integer for interlaced images,
+ * however this doesn't matter much; the number of rows was always declared as
+ * 'int', so it is still passed in the 'value' argument.
+ */
+#define PNG_SW_FLUSH (PNG_SF_WRITE + 9U)
+
+/*********************************** READ *************************************/
+/* The size of the buffer used while reading IDAT chunks and, potentially, other
+ * compressed chunks.
+ */
+#define PNG_SR_COMPRESS_buffer_size (PNG_SF_READ + 1U)
+ /* Read compressed data buffer size, in 'parameter'. The result is 0. */
+
+#define PNG_SR_GAMMA_threshold (PNG_SF_READ + 2U)
+#define png_set_gamma_threshold(png_ptr, threshold)\
+ (png_setting((png_ptr), PNG_SR_GAMMA_threshold, (threshold), 0))
+ /* SETTING: threshold below which gamma correction is not done, the default
+ * (set when the library is built) is PNG_GAMMA_THRESHOLD_FIXED, the
+ * 'parameter' is a png_fixed_point number, the difference from PNG_FP_1
+ * above which gamma correction will be performed.
+ *
+ * The value '153' is sufficient to maintain 1% accuracy in 16-bit linear
+ * calculations over a 655:1 range; over the maximum range possible with the
+ * 16-bit linear representation. Reasonable values are:
+ *
+ * 0: always do gamma correction, even if the gamma values are
+ * identical. The only point to this is to avoid a bug in the
+ * optimized (no gamma correction) code path, or for testing.
+ * 2: always do gamma correction if there is any significant
+ * difference. Notice that '1' will result in gamma correction in
+ * many cases when the screen gamma is the inverse of the encoding
+ * gamma because of inaccuracies in the representation of gamma.
+ * 153: do gamma correction if it is needed to maintain the accuracy of
+ * 16-bit linear calculations at 1% or below.
+ * 216: maintain 1% accuracy over a 100:1 dynamic range in 16-bit linear
+ * calculations. This matches the widely accepted numbers for human
+ * perception of differences within an image, however that doesn't
+ * mean that such high accuracy is required to avoid artefacts; such
+ * accuracy (such a low number) is only required if versions of the
+ * same image with and without gamma correction are to be compared
+ * visually.
+ * 5000: this is the default from libpng 1.6 and earlier. Using this
+ * produces changes in image contrast that are visible when suitable
+ * images are compared side-by-side however they are not obvious and
+ * it is inconceivable that a user would notice the change unless
+ * the user was very familiar with the image and the viewing
+ * environment.
+ *
+ * Values between 216 and 5000 produce varying very small changes in image
+ * contrast. Values above 10,000 (10%) produce noticeable increase or
+ * decrease in contrast which will probably change how the image is
+ * perceived. There is an internal limit on the maximum value which is
+ * currently 65%; PNG_EDOM will be returned for higher values.
+ *
+ * The result is the value that was set.
+ */
+#if 0 /*NYI*/
+#define PNG_SR_GAMMA_accuracy /*NYI*/
+ /* SETTING: controls the accuracy of the gamma calculations when the results
+ * are cached. The default is PNG_DEFAULT_GAMMA_ACCURACY. The number is 100
+ * times the number of bits, 'b', used in the internal tables when the input
+ * is linear, permitted values are 0..1600 however '0' causes the caching to
+ * be skipped entirely (so gives maximum accuracy with no caching!)
+ *
+ * The accuracy in the linear domain for a value 'a' is:
+ *
+ * +/-(.5/2^a)
+ *
+ * so for the default-default of 665 this means the accuracy is +/-0.5% and
+ * this ensures that almost-equal input values do not differ by more than 1%
+ * in the output, meeting the accepted requirement for human vision.
+ *
+ * The default value has no effect on input narrower than 16 bits. For n-bit
+ * input the total table size is ((n-v)+1)*(2^v), where 'v' is a/gamma and
+ * 'gamma' is the gamma encoding of the input:
+ *
+ * n a gamma 'v' table size
+ * 8 6.65 .45455 14.6 256
+ * 16 6.65 .45455 14.6 65536
+ * 16 6.65 1.0 6.65 1280
+ * 16 6 1.0 6 704
+ * 16 5 1.0 5 384
+ */
+#endif /*NYI*/
+
+#define PNG_SR_CRC_ACTION (PNG_SF_READ + 4U)
+ /* 'parameter' is what to do with critical chunks, 'value' is what to do with
+ * ancillary ones when the CRC does not match on read. 0 is returned. See
+ * png_set_crc_action for more information.
+ */
+
+/*********************************** OPTIONS **********************************/
+/* png_set_option is implemented via png_setting to provide API compatibility
+ * with releases prior to 1.7.0
+ */
+/* HARDWARE OPTIMIZATIONS
+ *
+ * Normally hardware capabilites, such as the Intel SSE instructions, are
+ * detected at run time, however sometimes it may be impossible to do this in
+ * user mode, in which case it is necessary to discover the capabilities in an
+ * OS specific way. Such capabilities are listed here when libpng has support
+ * for them and must be turned ON by the application if present. Check
+ * pnglibconf.h for options appropriate to your hardware.
+ *
+ * In general 'PNG_EXTENSIONS' controls hardware optimizations; these are not
+ * supported parts of libpng and, if there are problems with them, bugs should
+ * be ported to the implementers. Depending on the configuration it may not be
+ * possible to disable extensions at run time.
+ */
+#define PNG_SRW_OPTION (PNG_SF_READ+PNG_SF_WRITE + 0U)
+#ifdef PNG_SET_OPTION_SUPPORTED
+PNG_REMOVED(244, int, png_set_option, (png_structrp png_ptr, int option,
+ int onoff), PNG_EMPTY)
+
+#define png_set_option(p, opt, onoff)\
+ (png_setting((p), PNG_SRW_OPTION, (opt), (onoff)))
+ /* Pre 1.7 API; in 1.7 the result values have changed numerically but not by
+ * name. For backward API compatibility this setting only returns one error
+ * code, PNG_ENOSYS and that only for option numbers out of range, otherwise
+ * if the option isn't supported PNG_OPTION_UNSET (PNG_UNSET) is returned.
+ */
+#endif /* SET_OPTION */
+#define PNG_OPTION_UNSET PNG_UNSET /* Unset - defaults to off */
+#define PNG_OPTION_INVALID PNG_ENOSYS /* Option number out of range */
+#define PNG_OPTION_OFF 0
+#define PNG_OPTION_ON 1
+
+/* Specific options: */
+#define PNG_EXTENSIONS 0 /* HARDWARE: switch extensions on or off */
+#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
+#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */
+#define PNG_OPTION_NEXT 6 /* Next option - numbers are even */
+
+#define PNG_SRW_CHECK_FOR_INVALID_INDEX (PNG_SF_READ+PNG_SF_WRITE + 1U)
+ /* Turn the palette index check on or off; see
+ * png_set_check_for_invalid_index above.
+ */
+
+#define PNG_SRW_ERROR_HANDLING (PNG_SF_READ+PNG_SF_WRITE + 2U)
+ /* Change the action on issues that can be handled. */
+
+/*******************************************************************************
+ * END OF HARDWARE OPTIONS
+ ******************************************************************************/
+
/* The last ordinal number (this is the *last* one already used; the next
* one to use is one more than this.) Maintainer, remember to add an entry to
* scripts/symbols.def as well.
*/
#ifdef PNG_EXPORT_LAST_ORDINAL
- PNG_EXPORT_LAST_ORDINAL(244);
+ PNG_EXPORT_LAST_ORDINAL(249);
#endif
#ifdef __cplusplus
diff --git a/ext/libpng17/pngchunk.h b/ext/libpng17/pngchunk.h
new file mode 100644
index 0000000000..319eb5ec17
--- /dev/null
+++ b/ext/libpng17/pngchunk.h
@@ -0,0 +1,50 @@
+#ifdef _MSC_VER
+#pragma warning (disable:4028)
+#endif
+
+/* pngchunk.h - PNG chunk descriptions
+ *
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 2016 Glenn Randers-Pehrson
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * If this list is changed in any way scripts/chunkhash.c must be rebuilt and
+ * run to regenerate the lookup functions for the tables described from this
+ * list.
+ *
+ * IDAT MUST be first in the list; it must have index '0'. The order of the
+ * remaining chunks comes from section 5.6 "Chunk ordering" in the ISO spec
+ * plus the ordering rules in the PNG extensions documnet.
+ *
+ * Keep PNG_CHUNK_BEGIN and PNG_CHUNK_END at the beginning and end.
+ */
+PNG_CHUNK_BEGIN(IDAT, 73, 68, 65, 84, within_IDAT, after_start)
+PNG_CHUNK( IHDR, 73, 72, 68, 82, before_start, at_start)
+PNG_CHUNK( cHRM, 99, 72, 82, 77, before_PLTE, after_start)
+PNG_CHUNK( gAMA, 103, 65, 77, 65, before_PLTE, after_start)
+PNG_CHUNK( iCCP, 105, 67, 67, 80, before_PLTE, after_start)
+PNG_CHUNK( sBIT, 115, 66, 73, 84, before_PLTE, after_start)
+PNG_CHUNK( sRGB, 115, 82, 71, 66, before_PLTE, after_start)
+PNG_CHUNK( PLTE, 80, 76, 84, 69, before_PLTE, after_start)
+PNG_CHUNK( bKGD, 98, 75, 71, 68, before_IDAT, after_PLTE)
+PNG_CHUNK( hIST, 104, 73, 83, 84, before_IDAT, after_PLTE)
+PNG_CHUNK( tRNS, 116, 82, 78, 83, before_IDAT, after_PLTE)
+PNG_CHUNK( oFFs, 111, 70, 70, 115, before_IDAT, after_start)
+PNG_CHUNK( pCAL, 112, 67, 65, 76, before_IDAT, after_start)
+PNG_CHUNK( sCAL, 115, 67, 65, 76, before_IDAT, after_start)
+PNG_CHUNK( sTER, 115, 84, 69, 82, before_IDAT, after_start)
+PNG_CHUNK( pHYs, 112, 72, 89, 115, before_IDAT, after_start)
+PNG_CHUNK( sPLT, 115, 80, 76, 84, before_IDAT, after_start)
+PNG_CHUNK( tIME, 116, 73, 77, 69, before_end, after_start)
+PNG_CHUNK( iTXt, 105, 84, 88, 116, before_end, after_start)
+PNG_CHUNK( tEXt, 116, 69, 88, 116, before_end, after_start)
+PNG_CHUNK( zTXt, 122, 84, 88, 116, before_end, after_start)
+PNG_CHUNK( fRAc, 102, 82, 65, 99, before_end, after_start)
+PNG_CHUNK( gIFg, 103, 73, 70, 103, before_end, after_start)
+PNG_CHUNK( gIFt, 103, 73, 70, 116, before_end, after_start)
+PNG_CHUNK( gIFx, 103, 73, 70, 120, before_end, after_start)
+PNG_CHUNK( dSIG, 100, 83, 73, 71, before_end, after_start)
+PNG_CHUNK_END( IEND, 73, 69, 78, 68, before_end, after_IDAT)
diff --git a/ext/libpng17/pngconf.h b/ext/libpng17/pngconf.h
index de3ce894f6..89adb39afb 100644
--- a/ext/libpng17/pngconf.h
+++ b/ext/libpng17/pngconf.h
@@ -1,9 +1,9 @@
/* pngconf.h - machine configurable file for libpng
*
- * libpng version 1.7.0beta35 - March 17, 2014
+ * libpng version 1.7.0beta90, August 28, 2017
*
- * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -11,9 +11,7 @@
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
- */
-
-/* Any machine specific code is near the front of this file, so if you
+ * Any machine specific code is near the front of this file, so if you
* are configuring libpng for a machine, you may want to read the section
* starting here down to where it starts to typedef png_color, png_text,
* and png_info.
@@ -22,26 +20,6 @@
#ifndef PNGCONF_H
#define PNGCONF_H
-/* To do: Do all of this in scripts/pnglibconf.dfa */
-#ifdef PNG_SAFE_LIMITS_SUPPORTED
-# ifdef PNG_USER_WIDTH_MAX
-# undef PNG_USER_WIDTH_MAX
-# define PNG_USER_WIDTH_MAX 1000000L
-# endif
-# ifdef PNG_USER_HEIGHT_MAX
-# undef PNG_USER_HEIGHT_MAX
-# define PNG_USER_HEIGHT_MAX 1000000L
-# endif
-# ifdef PNG_USER_CHUNK_MALLOC_MAX
-# undef PNG_USER_CHUNK_MALLOC_MAX
-# define PNG_USER_CHUNK_MALLOC_MAX 4000000L
-# endif
-# ifdef PNG_USER_CHUNK_CACHE_MAX
-# undef PNG_USER_CHUNK_CACHE_MAX
-# define PNG_USER_CHUNK_CACHE_MAX 128
-# endif
-#endif
-
#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
@@ -210,27 +188,27 @@
* compatible with GCC or Visual C because of different calling conventions.
*/
# if PNG_API_RULE == 2
- /* If this line results in an error, either because __watcall is not
- * understood or because of a redefine just below you cannot use *this*
- * build of the library with the compiler you are using. *This* build was
- * build using Watcom and applications must also be built using Watcom!
- */
+ /* If this line results in an error, either because __watcall is not
+ * understood or because of a redefine just below you cannot use *this*
+ * build of the library with the compiler you are using. *This* build was
+ * build using Watcom and applications must also be built using Watcom!
+ */
# define PNGCAPI __watcall
# endif
# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
# define PNGCAPI __cdecl
# if PNG_API_RULE == 1
- /* If this line results in an error __stdcall is not understood and
- * PNG_API_RULE should not have been set to '1'.
- */
+ /* If this line results in an error __stdcall is not understood and
+ * PNG_API_RULE should not have been set to '1'.
+ */
# define PNGAPI __stdcall
# endif
# else
- /* An older compiler, or one not detected (erroneously) above,
- * if necessary override on the command line to get the correct
- * variants for the compiler.
- */
+ /* An older compiler, or one not detected (erroneously) above,
+ * if necessary override on the command line to get the correct
+ * variants for the compiler.
+ */
# ifndef PNGCAPI
# define PNGCAPI _cdecl
# endif
@@ -238,6 +216,7 @@
# define PNGAPI _stdcall
# endif
# endif /* compiler/api */
+
/* NOTE: PNGCBAPI always defaults to PNGCAPI. */
# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
@@ -246,10 +225,10 @@
# if (defined(_MSC_VER) && _MSC_VER < 800) ||\
(defined(__BORLANDC__) && __BORLANDC__ < 0x500)
- /* older Borland and MSC
- * compilers used '__export' and required this to be after
- * the type.
- */
+ /* older Borland and MSC
+ * compilers used '__export' and required this to be after
+ * the type.
+ */
# ifndef PNG_EXPORT_TYPE
# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
# endif
@@ -265,9 +244,9 @@
# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
# define PNGAPI _System
# else /* !Windows/x86 && !OS/2 */
- /* Use the defaults, or define PNG*API on the command line (but
- * this will have to be done for every compile!)
- */
+ /* Use the defaults, or define PNG*API on the command line (but
+ * this will have to be done for every compile!)
+ */
# endif /* other system, !OS/2 */
#endif /* !Windows/x86 */
@@ -288,7 +267,7 @@
*/
#ifndef PNG_IMPEXP
# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
- /* This forces use of a DLL, disallowing static linking */
+ /* This forces use of a DLL, disallowing static linking */
# define PNG_IMPEXP PNG_DLL_IMPORT
# endif
@@ -360,8 +339,8 @@
* version 1.2.41. Disabling these removes the warnings but may also produce
* less efficient code.
*/
-# if defined(__clang__)
- /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
+# if defined(__clang__) && defined(__has_attribute)
+ /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
# endif
@@ -385,6 +364,7 @@
# ifndef PNG_RESTRICT
# define PNG_RESTRICT __restrict
# endif
+
# elif defined(__GNUC__)
# ifndef PNG_USE_RESULT
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
diff --git a/ext/libpng17/pngdebug.h b/ext/libpng17/pngdebug.h
index b43c59cde5..4bfff4de34 100644
--- a/ext/libpng17/pngdebug.h
+++ b/ext/libpng17/pngdebug.h
@@ -1,12 +1,11 @@
/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
*
- * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.8 [December 19, 2013]
+ * Copyright (c) 1998-2002,2004,2006-2013 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.)
*
- * Last changed in libpng 1.6.8 [December 19, 2013]
- *
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
@@ -36,14 +35,11 @@
#define PNGDEBUG_H
/* These settings control the formatting of messages in png.c and pngerror.c */
/* Moved to pngdebug.h at 1.5.0 */
-# ifndef PNG_LITERAL_SHARP
-# define PNG_LITERAL_SHARP 0x23
-# endif
# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
-# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
+# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5bU
# endif
# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
-# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
+# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5dU
# endif
# ifndef PNG_STRING_NEWLINE
# define PNG_STRING_NEWLINE "\n"
diff --git a/ext/libpng17/pngerror.c b/ext/libpng17/pngerror.c
index 828cac4b3d..0b39ae4f1d 100644
--- a/ext/libpng17/pngerror.c
+++ b/ext/libpng17/pngerror.c
@@ -2,7 +2,7 @@
/* pngerror.c - stub functions for i/o and memory allocation
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -17,6 +17,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngerror
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
@@ -26,8 +27,8 @@ static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,
#ifdef PNG_WARNINGS_SUPPORTED
static void /* PRIVATE */
png_default_warning PNGARG((png_const_structrp png_ptr,
- png_const_charp warning_message));
-#endif /* PNG_WARNINGS_SUPPORTED */
+ png_const_charp warning_message));
+#endif /* WARNINGS */
/* This function is called whenever there is a fatal error. This function
* should not be changed. If there is a need to handle errors differently,
@@ -37,48 +38,8 @@ png_default_warning PNGARG((png_const_structrp png_ptr,
#ifdef PNG_ERROR_TEXT_SUPPORTED
PNG_FUNCTION(void,PNGAPI
png_error,(png_const_structrp png_ptr, png_const_charp error_message),
- PNG_NORETURN)
+ PNG_NORETURN)
{
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- char msg[16];
- if (png_ptr != NULL)
- {
- if (png_ptr->flags&
- (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
- {
- if (*error_message == PNG_LITERAL_SHARP)
- {
- /* Strip "#nnnn " from beginning of error message. */
- int offset;
- for (offset = 1; offset<15; offset++)
- if (error_message[offset] == ' ')
- break;
-
- if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
- {
- int i;
- for (i = 0; i < offset - 1; i++)
- msg[i] = error_message[i + 1];
- msg[i - 1] = '\0';
- error_message = msg;
- }
-
- else
- error_message += offset;
- }
-
- else
- {
- if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
- {
- msg[0] = '0';
- msg[1] = '\0';
- error_message = msg;
- }
- }
- }
- }
-#endif
if (png_ptr != NULL && png_ptr->error_fn != NULL)
(*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),
error_message);
@@ -103,14 +64,14 @@ png_err,(png_const_structrp png_ptr),PNG_NORETURN)
use the default handler, which will not return. */
png_default_error(png_ptr, "");
}
-#endif /* PNG_ERROR_TEXT_SUPPORTED */
+#endif /* ERROR_TEXT */
/* Utility to safely appends strings to a buffer. This never errors out so
* error checking is not required in the caller.
*/
size_t
png_safecat(png_charp buffer, size_t bufsize, size_t pos,
- png_const_charp string)
+ png_const_charp string)
{
if (buffer != NULL && pos < bufsize)
{
@@ -129,9 +90,10 @@ png_safecat(png_charp buffer, size_t bufsize, size_t pos,
* and end pointer (which should point just *beyond* the end of the buffer!)
* Returns the pointer to the start of the formatted string.
*/
+#define PNG_HAVE_FORMAT_NUMBER /* for the code below */
png_charp
png_format_number(png_const_charp start, png_charp end, int format,
- png_alloc_size_t number)
+ png_alloc_size_t number)
{
int count = 0; /* number of digits output */
int mincount = 1; /* minimum number required */
@@ -152,7 +114,7 @@ png_format_number(png_const_charp start, png_charp end, int format,
case PNG_NUMBER_FORMAT_fixed:
/* Needs five digits (the fraction) */
mincount = 5;
- if (output || number % 10 != 0)
+ if (output != 0 || number % 10 != 0)
{
*--end = digits[number % 10];
output = 1;
@@ -189,7 +151,7 @@ png_format_number(png_const_charp start, png_charp end, int format,
++count;
/* Float a fixed number here: */
- if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start)
+ if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start))
{
/* End of the fraction, but maybe nothing was output? In that case
* drop the decimal point. If the number is a true zero handle that
@@ -216,24 +178,9 @@ void PNGAPI
png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
int offset = 0;
- if (png_ptr != NULL)
- {
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- if (png_ptr->flags&
- (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
-#endif
- {
- if (*warning_message == PNG_LITERAL_SHARP)
- {
- for (offset = 1; offset < 15; offset++)
- if (warning_message[offset] == ' ')
- break;
- }
- }
- }
if (png_ptr != NULL && png_ptr->warning_fn != NULL)
(*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
- warning_message + offset);
+ warning_message + offset);
else
png_default_warning(png_ptr, warning_message + offset);
}
@@ -245,7 +192,7 @@ png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
*/
void
png_warning_parameter(png_warning_parameters p, int number,
- png_const_charp string)
+ png_const_charp string)
{
if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
(void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
@@ -253,7 +200,7 @@ png_warning_parameter(png_warning_parameters p, int number,
void
png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
- png_alloc_size_t value)
+ png_alloc_size_t value)
{
char buffer[PNG_NUMBER_BUFFER_SIZE];
png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
@@ -261,7 +208,7 @@ png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
void
png_warning_parameter_signed(png_warning_parameters p, int number, int format,
- png_int_32 value)
+ png_int_32 value)
{
png_alloc_size_t u;
png_charp str;
@@ -282,7 +229,7 @@ png_warning_parameter_signed(png_warning_parameters p, int number, int format,
void
png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
- png_const_charp message)
+ png_const_charp message)
{
/* The internal buffer is just 192 bytes - enough for all our messages,
* overflow doesn't happen because this code checks! If someone figures
@@ -355,32 +302,47 @@ png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
*/
png_warning(png_ptr, msg);
}
-#endif /* PNG_WARNINGS_SUPPORTED */
+#endif /* WARNINGS */
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
void PNGAPI
png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
{
- if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)
+ switch (png_ptr->benign_error_action)
{
-# ifdef PNG_READ_SUPPORTED
- if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
- png_ptr->chunk_name != 0)
- png_chunk_warning(png_ptr, error_message);
- else
-# endif
- png_warning(png_ptr, error_message);
+ case PNG_ERROR:
+ png_chunk_error(png_ptr, error_message);
+ break;
+
+ case PNG_WARN:
+ png_chunk_warning(png_ptr, error_message);
+ break;
+
+ default: /* PNG_IGNORE */
+ break;
}
- else
+# ifndef PNG_ERROR_TEXT_SUPPORTED
+ PNG_UNUSED(error_message)
+# endif
+}
+
+static void
+app_error(png_const_structrp png_ptr, png_const_charp error_message,
+ unsigned int error_action)
+{
+ switch (error_action)
{
-# ifdef PNG_READ_SUPPORTED
- if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
- png_ptr->chunk_name != 0)
- png_chunk_error(png_ptr, error_message);
- else
-# endif
- png_error(png_ptr, error_message);
+ case PNG_ERROR:
+ png_error(png_ptr, error_message);
+ break;
+
+ case PNG_WARN:
+ png_warning(png_ptr, error_message);
+ break;
+
+ default: /* PNG_IGNORE */
+ break;
}
# ifndef PNG_ERROR_TEXT_SUPPORTED
@@ -391,35 +353,28 @@ png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
void /* PRIVATE */
png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
{
- if (png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN)
- png_warning(png_ptr, error_message);
- else
- png_error(png_ptr, error_message);
-
-# ifndef PNG_ERROR_TEXT_SUPPORTED
- PNG_UNUSED(error_message)
-# endif
+ app_error(png_ptr, error_message, png_ptr->app_warning_action);
}
void /* PRIVATE */
png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
{
- if (png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN)
- png_warning(png_ptr, error_message);
- else
- png_error(png_ptr, error_message);
-
-# ifndef PNG_ERROR_TEXT_SUPPORTED
- PNG_UNUSED(error_message)
-# endif
+ app_error(png_ptr, error_message, png_ptr->app_error_action);
}
#endif /* BENIGN_ERRORS */
+#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */
+#if defined(PNG_WARNINGS_SUPPORTED) || \
+ (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
/* These utilities are used internally to build an error message that relates
- * to the current chunk. The chunk name comes from png_ptr->chunk_name,
- * this is used to prefix the message. The message is limited in length
- * to 63 bytes, the name characters are output as hex digits wrapped in []
- * if the character is invalid.
+ * to the current chunk. The chunk name comes from png_ptr->chunk_name unless
+ * png_ptr->zowner is set in which case that is used in preference. This is
+ * used to prefix the message. The message is limited in length to 63 bytes.
+ * The name characters are output as hex digits wrapped in [] if the character
+ * is invalid.
+ *
+ * Using 'zowner' means that IDAT errors at the end of the IDAT stream are still
+ * reported as from the IDAT chunks.
*/
#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
static PNG_CONST char png_digit[16] = {
@@ -427,22 +382,22 @@ static PNG_CONST char png_digit[16] = {
'A', 'B', 'C', 'D', 'E', 'F'
};
-#define PNG_MAX_ERROR_TEXT 196 /* Currently limited be profile_error in png.c */
-#if defined(PNG_WARNINGS_SUPPORTED) || \
- (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
static void /* PRIVATE */
png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
error_message)
{
- png_uint_32 chunk_name = png_ptr->chunk_name;
+ png_uint_32 chunk_name = png_ptr->zowner;
int iout = 0, ishift = 24;
+ if (chunk_name == 0)
+ chunk_name = png_ptr->chunk_name;
+
while (ishift >= 0)
{
int c = (int)(chunk_name >> ishift) & 0xff;
ishift -= 8;
- if (isnonalpha(c))
+ if (isnonalpha(c) != 0)
{
buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
buffer[iout++] = png_digit[(c & 0xf0) >> 4];
@@ -452,7 +407,7 @@ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
else
{
- buffer[iout++] = (char)c;
+ buffer[iout++] = png_check_char(png_ptr, c);
}
}
@@ -473,15 +428,15 @@ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
buffer[iout] = '\0';
}
}
-#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */
+#endif /* WARNINGS || ERROR_TEXT */
#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
PNG_FUNCTION(void,PNGAPI
png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
- PNG_NORETURN)
+ PNG_NORETURN)
{
char msg[18+PNG_MAX_ERROR_TEXT];
- if (png_ptr == NULL)
+ if (png_ptr == NULL || png_ptr->chunk_name == 0U)
png_error(png_ptr, error_message);
else
@@ -490,14 +445,14 @@ png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
png_error(png_ptr, msg);
}
}
-#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */
+#endif /* READ && ERROR_TEXT */
#ifdef PNG_WARNINGS_SUPPORTED
void PNGAPI
png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
char msg[18+PNG_MAX_ERROR_TEXT];
- if (png_ptr == NULL)
+ if (png_ptr == NULL || png_ptr->chunk_name == 0U)
png_warning(png_ptr, warning_message);
else
@@ -506,7 +461,7 @@ png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
png_warning(png_ptr, msg);
}
}
-#endif /* PNG_WARNINGS_SUPPORTED */
+#endif /* WARNINGS */
#ifdef PNG_READ_SUPPORTED
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
@@ -514,31 +469,36 @@ void PNGAPI
png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
error_message)
{
- if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)
- png_chunk_warning(png_ptr, error_message);
+ switch (png_ptr->benign_error_action)
+ {
+ case PNG_ERROR:
+ png_chunk_error(png_ptr, error_message);
+ break;
- else
- png_chunk_error(png_ptr, error_message);
+ case PNG_WARN:
+ png_chunk_warning(png_ptr, error_message);
+ break;
+
+ default: /* PNG_IGNORE */
+ break;
+ }
# ifndef PNG_ERROR_TEXT_SUPPORTED
PNG_UNUSED(error_message)
# endif
}
-#endif
-#endif /* PNG_READ_SUPPORTED */
+#endif /* BENIGN_ERRORS */
+#endif /* READ */
void /* PRIVATE */
-png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
+(png_chunk_report)(png_const_structrp png_ptr, png_const_charp message,
+ int error)
{
-# ifndef PNG_WARNINGS_SUPPORTED
- PNG_UNUSED(message)
-# endif
-
/* This is always supported, but for just read or just write it
* unconditionally does the right thing.
*/
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
- if (png_ptr->mode & PNG_IS_READ_STRUCT)
+ if (png_ptr->read_struct)
# endif
# ifdef PNG_READ_SUPPORTED
@@ -546,13 +506,16 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
if (error < PNG_CHUNK_ERROR)
png_chunk_warning(png_ptr, message);
- else
+ else if (error < PNG_CHUNK_FATAL)
png_chunk_benign_error(png_ptr, message);
+
+ else
+ png_chunk_error(png_ptr, message);
}
# endif
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
- else if (!(png_ptr->mode & PNG_IS_READ_STRUCT))
+ else if (!png_ptr->read_struct)
# endif
# ifdef PNG_WRITE_SUPPORTED
@@ -560,10 +523,17 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
if (error < PNG_CHUNK_WRITE_ERROR)
png_app_warning(png_ptr, message);
- else
+ else if (error < PNG_CHUNK_FATAL)
png_app_error(png_ptr, message);
+
+ else
+ png_error(png_ptr, message);
}
# endif
+
+# ifndef PNG_ERROR_TEXT_SUPPORTED
+ PNG_UNUSED(message)
+# endif
}
#ifdef PNG_ERROR_TEXT_SUPPORTED
@@ -583,11 +553,12 @@ png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
memcpy(msg, fixed_message, fixed_message_ln);
iin = 0;
- if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
- {
- msg[fixed_message_ln + iin] = name[iin];
- ++iin;
- }
+ if (name != NULL)
+ while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
+ {
+ msg[fixed_message_ln + iin] = name[iin];
+ ++iin;
+ }
msg[fixed_message_ln + iin] = 0;
png_error(png_ptr, msg);
}
@@ -625,7 +596,7 @@ png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
else
{
png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
- png_malloc_warn(png_ptr, jmp_buf_size));
+ png_malloc_warn(png_ptr, jmp_buf_size));
if (png_ptr->jmp_buf_ptr == NULL)
return NULL; /* new NULL return on OOM */
@@ -646,9 +617,9 @@ png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
/* This is an internal error in libpng: somehow we have been left
* with a stack allocated jmp_buf when the application regained
* control. It's always possible to fix this up, but for the moment
- * this is a png_error because that makes it easy to detect.
+ * this is an affirm because that makes it easy to detect.
*/
- png_error(png_ptr, "Libpng jmp_buf still allocated");
+ impossible("Libpng jmp_buf still allocated");
/* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */
}
}
@@ -714,40 +685,9 @@ png_free_jmpbuf(png_structrp png_ptr)
*/
static PNG_FUNCTION(void /* PRIVATE */,
png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
- PNG_NORETURN)
+ PNG_NORETURN)
{
#ifdef PNG_CONSOLE_IO_SUPPORTED
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- /* Check on NULL only added in 1.5.4 */
- if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
- {
- /* Strip "#nnnn " from beginning of error message. */
- int offset;
- char error_number[16];
- for (offset = 0; offset<15; offset++)
- {
- error_number[offset] = error_message[offset + 1];
- if (error_message[offset] == ' ')
- break;
- }
-
- if ((offset > 1) && (offset < 15))
- {
- error_number[offset - 1] = '\0';
- fprintf(stderr, "libpng error no. %s: %s",
- error_number, error_message + offset + 1);
- fprintf(stderr, PNG_STRING_NEWLINE);
- }
-
- else
- {
- fprintf(stderr, "libpng error: %s, offset=%d",
- error_message, offset);
- fprintf(stderr, PNG_STRING_NEWLINE);
- }
- }
- else
-#endif
{
fprintf(stderr, "libpng error: %s", error_message ? error_message :
"undefined");
@@ -763,7 +703,8 @@ PNG_FUNCTION(void,PNGAPI
png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
{
#ifdef PNG_SETJMP_SUPPORTED
- if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr)
+ if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
+ png_ptr->jmp_buf_ptr != NULL)
png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);
#else
PNG_UNUSED(png_ptr)
@@ -772,7 +713,7 @@ png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
/* If control reaches this point, png_longjmp() must not return. The only
* choice is to terminate the whole process (or maybe the thread); to do
- * this the ANSI-C abort() function is used unless a different method is
+ * this the ANSI-C abort() function is used unless a different method is
* implemented by overriding the default configuration setting for
* PNG_ABORT (see scripts/pnglibconf.dfa).
*
@@ -793,36 +734,6 @@ static void /* PRIVATE */
png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
#ifdef PNG_CONSOLE_IO_SUPPORTED
-# ifdef PNG_ERROR_NUMBERS_SUPPORTED
- if (*warning_message == PNG_LITERAL_SHARP)
- {
- int offset;
- char warning_number[16];
- for (offset = 0; offset < 15; offset++)
- {
- warning_number[offset] = warning_message[offset + 1];
- if (warning_message[offset] == ' ')
- break;
- }
-
- if ((offset > 1) && (offset < 15))
- {
- warning_number[offset + 1] = '\0';
- fprintf(stderr, "libpng warning no. %s: %s",
- warning_number, warning_message + offset);
- fprintf(stderr, PNG_STRING_NEWLINE);
- }
-
- else
- {
- fprintf(stderr, "libpng warning: %s",
- warning_message);
- fprintf(stderr, PNG_STRING_NEWLINE);
- }
- }
- else
-# endif
-
{
fprintf(stderr, "libpng warning: %s", warning_message);
fprintf(stderr, PNG_STRING_NEWLINE);
@@ -832,7 +743,7 @@ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
#endif
PNG_UNUSED(png_ptr) /* Make compiler happy */
}
-#endif /* PNG_WARNINGS_SUPPORTED */
+#endif /* WARNINGS */
/* This function is called when the application wants to use another method
* of handling errors and warnings. Note that the error function MUST NOT
@@ -870,19 +781,6 @@ png_get_error_ptr(png_const_structrp png_ptr)
}
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-void PNGAPI
-png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
-{
- if (png_ptr != NULL)
- {
- png_ptr->flags &=
- ((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
- PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
- }
-}
-#endif
-
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
/* Currently the above both depend on SETJMP_SUPPORTED, however it would be
@@ -891,7 +789,7 @@ png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
*/
PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
- PNG_NORETURN)
+ PNG_NORETURN)
{
const png_const_structrp png_ptr = png_nonconst_ptr;
png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
@@ -914,7 +812,7 @@ png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
/* Missing longjmp buffer, the following is to help debugging: */
{
size_t pos = png_safecat(image->message, (sizeof image->message), 0,
- "bad longjmp: ");
+ "bad longjmp: ");
png_safecat(image->message, (sizeof image->message), pos,
error_message);
}
@@ -967,5 +865,259 @@ png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
return result;
}
-#endif /* SIMPLIFIED READ/WRITE */
-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
+#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
+
+/* Affirms: minimal code in 'STABLE' builds to return control to the
+ * application via png_error(), more verbose code followed by PNG_ABORT for
+ * all other builds to ensure that internal errors are detected.
+ *
+ * The code always produces a message if it is possible, regardless of the
+ * setting of PNG_ERROR_TEXT_SUPPORTED, except that in stable builds
+ * PNG_ERROR_TEXT_SUPPORTED is honored. See pngpriv.h for the calculation of
+ * the two control macros PNG_RELEASE_BUILD (don't abort; stable build or rc)
+ * and PNG_AFFIRM_TEXT (output text.)
+ */
+#if PNG_AFFIRM_TEXT
+# ifdef PNG_HAVE_FORMAT_NUMBER
+static size_t
+png_affirm_number(png_charp buffer, size_t bufsize, size_t pos,
+ unsigned int number, int format)
+{
+ char numbuf[PNG_NUMBER_BUFFER_SIZE];
+ return png_safecat(buffer, bufsize, pos,
+ png_format_number(numbuf, numbuf + sizeof numbuf, format, number));
+}
+# define affirm_number(a,b,c,d,e) png_affirm_number(a,b,c,d,e)
+# else /* !HAVE_FORMAT_NUMBER */
+static size_t
+png_affirm_number(png_charp buffer, size_t bufsize, size_t pos,
+ unsigned int number)
+{
+ /* binhex it; highly non-portable, assumes the ASCII character set, but
+ * if warnings are turned off then it is unlikely the text will get read
+ * anyway. This binhex variant is (48+val), where 'val' is the next 6
+ * bits of the number, so it starts as '0' (for 0) and ends at 'I' for
+ * 63. The number is wrapped in {}, so 0 comes out as '{}' and 9 comes
+ * out as '{9}' and so on.
+ */
+ char numbuf[32];
+ int i = sizeof numbuf;
+
+ numbuf[--i] = 0;
+ numbuf[--i] = '}';
+
+ do
+ {
+ if (number > 0)
+ numbuf[--i] = (char)/*SAFE*/((number & 63) + 48), number >>= 6;
+ else
+ {
+ numbuf[--i] = '{';
+ break;
+ }
+ }
+ while (i > 0);
+
+ return png_safecat(buffer, bufsize, pos, numbuf+i);
+}
+# define affirm_number(a,b,c,d,e) png_affirm_number(a,b,c,d)
+#endif /* !HAVE_FORMAT_NUMBER */
+
+static void
+affirm_text(png_charp buffer, size_t bufsize,
+ param_deb(png_const_charp condition) unsigned int position)
+{
+ /* Format the 'position' number and output:
+ *
+ * " : affirm 'condition' failed\n"
+ * " libpng version - \n"
+ * " translated __DATE__ __TIME__"
+ *
+ * In the STABLE versions the output is the same for the last two lines
+ * but the first line becomes:
+ *
+ * ": affirm failed"
+ *
+ * If there is no number formatting the numbers just get replaced by
+ * some binhex (see the utility above).
+ */
+ size_t pos = 0;
+
+# if PNG_RELEASE_BUILD /* no 'condition' parameter: minimal text */
+ pos = affirm_number(buffer, bufsize, pos, position, PNG_NUMBER_FORMAT_x);
+ pos = png_safecat(buffer, bufsize, pos, ": affirm failed");
+# else /* !STABLE */
+ /* Break down 'position' into a file name and a line number: */
+ {
+# define PNG_apply(f) { #f "\0", PNG_SRC_FILE_ ## f },
+# define PNG_end { "", PNG_SRC_FILE_LAST }
+ static struct {
+ char filename[28]; /* GCC checks this size */
+ unsigned int base;
+ } fileinfo[] = { PNG_FILES };
+# undef PNG_apply
+# undef PNG_end
+
+ unsigned int i;
+ png_const_charp filename;
+
+ /* Do 'nfiles' this way to avoid problems with g++ where it whines
+ * about (size_t) being larger than (int), even though this is a
+ * compile time constant:
+ */
+# define nfiles ((sizeof fileinfo)/(sizeof (fileinfo[0])))
+ for (i=0; i < nfiles && position > fileinfo[i].base; ++i) {}
+
+ if (i == 0 || i > nfiles)
+ filename = "UNKNOWN";
+ else
+ {
+ filename = fileinfo[i-1].filename;
+ position -= fileinfo[i-1].base;
+ }
+# undef nfiles
+
+ pos = png_safecat(buffer, bufsize, pos, filename);
+ pos = png_safecat(buffer, bufsize, pos, ".c ");
+ pos = affirm_number(buffer, bufsize, pos, position,
+ PNG_NUMBER_FORMAT_u);
+ }
+
+ pos = png_safecat(buffer, bufsize, pos, ": affirm '");
+ pos = png_safecat(buffer, bufsize, pos, condition);
+ pos = png_safecat(buffer, bufsize, pos, "' failed\n");
+# endif /* !STABLE */
+
+ pos = png_safecat(buffer, bufsize, pos, PNG_HEADER_VERSION_STRING);
+ pos = png_safecat(buffer, bufsize, pos,
+ " translated " __DATE__ " " __TIME__);
+}
+
+#define affirm_text(b, c, p)\
+ do {\
+ (affirm_text)(b, sizeof b, param_deb(c) (p));\
+ } while (0)
+
+#endif /* AFFIRM_TEXT */
+
+PNG_FUNCTION(void,png_affirm,(png_const_structrp png_ptr,
+ param_deb(png_const_charp condition) unsigned int position),PNG_NORETURN)
+{
+# if PNG_AFFIRM_TEXT
+ char buffer[512];
+
+ affirm_text(buffer, condition, position);
+# else /* !AFFIRM_TEXT */
+ PNG_UNUSED(position)
+# if !PNG_RELEASE_BUILD
+ PNG_UNUSED(condition)
+# endif
+# endif /* AFFIRM_TEXT */
+
+ /* Now in STABLE do a png_error, but in other builds output the message
+ * (if possible) then abort (PNG_ABORT).
+ */
+# if PNG_RELEASE_BUILD
+ png_error(png_ptr, buffer/*macro parameter used only if ERROR_TEXT*/);
+# else /* !AFFIRM_ERROR */
+ /* Use console IO if possible; this is because there is no guarantee that
+ * the app 'warning' will output anything. For certain the simplified
+ * API implementation just copies the message (truncated) to the image
+ * message buffer, which makes debugging much more difficult.
+ *
+ * Note that it is possible that neither WARNINGS nor CONSOLE_IO are
+ * supported; in that case no text will be output (and PNG_AFFIRM_TEXT
+ * will be false.)
+ */
+# ifdef PNG_CONSOLE_IO_SUPPORTED
+ fprintf(stderr, "%s\n", buffer);
+ PNG_UNUSED(png_ptr)
+# elif defined PNG_WARNINGS_SUPPORTED
+ if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+ png_ptr->warning_fn(png_constcast(png_structrp, png_ptr), buffer);
+ /* else no way to output the text */
+# else
+ PNG_UNUSED(png_ptr)
+# endif
+
+ PNG_ABORT
+# endif /* AFFIRM_ERROR */
+}
+
+#if !PNG_RELEASE_BUILD
+void /* PRIVATE */
+png_handled_affirm(png_const_structrp png_ptr, png_const_charp message,
+ unsigned int position)
+{
+# if PNG_RELEASE_BUILD
+ /* testing in RC: we want to return control to the caller, so do not
+ * use png_affirm.
+ */
+ char buffer[512];
+
+ affirm_text(buffer, message, position);
+
+# ifdef PNG_CONSOLE_IO_SUPPORTED
+ fprintf(stderr, "%s\n", buffer);
+# elif defined PNG_WARNINGS_SUPPORTED
+ if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+ png_ptr->warning_fn(png_constcast(png_structrp, png_ptr), buffer);
+ /* else no way to output the text */
+# else
+ PNG_UNUSED(png_ptr)
+# endif
+# else
+ png_affirm(png_ptr, message, position);
+# endif
+}
+#endif /* !RELEASE_BUILD */
+
+#ifdef PNG_RANGE_CHECK_SUPPORTED
+/* The character/byte checking APIs. These do their own calls to png_affirm
+ * because the caller provides the position.
+ */
+unsigned int /* PRIVATE */
+png_bit_affirm(png_const_structrp png_ptr, unsigned int position,
+ unsigned int u, unsigned int bits)
+{
+ /* The following avoids overflow errors even if 'bits' is 16 or 32: */
+ if (u <= (1U << bits)-1U)
+ return u;
+
+ png_affirm(png_ptr, param_deb("(bit field) range") position);
+}
+
+char /* PRIVATE */
+png_char_affirm(png_const_structrp png_ptr, unsigned int position, int c)
+{
+ if (c >= CHAR_MIN && c <= CHAR_MAX)
+ return (char)/*SAFE*/c;
+
+ png_affirm(png_ptr, param_deb("(char) range") position);
+}
+
+png_byte /* PRIVATE */
+png_byte_affirm(png_const_structrp png_ptr, unsigned int position, int b)
+{
+ /* For the type png_byte the limits.h values are ignored and we check
+ * against the values PNG expects to store in a byte:
+ */
+ if (b >= 0 && b <= 255)
+ return (png_byte)/*SAFE*/b;
+
+ png_affirm(png_ptr, param_deb("PNG byte range") position);
+}
+
+#if INT_MAX >= 65535
+png_uint_16 /* PRIVATE */
+png_u16_affirm(png_const_structrp png_ptr, unsigned int position, int b)
+{
+ /* Check against the PNG 16-bit limit, as with png_byte. */
+ if (b >= 0 && b <= 65535)
+ return (png_uint_16)/*SAFE*/b;
+
+ png_affirm(png_ptr, param_deb("PNG 16-bit range") position);
+}
+#endif /* INT_MAX >= 65535 */
+#endif /* RANGE_CHECK */
+#endif /* READ || WRITE */
diff --git a/ext/libpng17/pngget.c b/ext/libpng17/pngget.c
index 1db8041995..52f6fbf6c7 100644
--- a/ext/libpng17/pngget.c
+++ b/ext/libpng17/pngget.c
@@ -2,7 +2,7 @@
/* pngget.c - retrieval of values from info struct
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -13,6 +13,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngget
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
@@ -26,16 +27,18 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,
return(0);
}
-png_size_t PNGAPI
+png_alloc_size_t PNGAPI
png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
if (png_ptr != NULL && info_ptr != NULL)
- return(info_ptr->rowbytes);
+ return png_calc_rowbytes(png_ptr,
+ PNG_FORMAT_CHANNELS(info_ptr->format) * info_ptr->bit_depth,
+ info_ptr->width);
- return(0);
+ return 0;
}
-#ifdef PNG_INFO_IMAGE_SUPPORTED
+#ifdef PNG_READ_PNG_SUPPORTED
png_bytepp PNGAPI
png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
@@ -79,7 +82,8 @@ png_byte PNGAPI
png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
if (png_ptr != NULL && info_ptr != NULL)
- return info_ptr->color_type;
+ return png_check_byte(png_ptr,
+ PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format));
return (0);
}
@@ -116,7 +120,8 @@ png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
info_ptr)
{
#ifdef PNG_pHYs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
png_debug1(1, "in %s retrieval function",
"png_get_x_pixels_per_meter");
@@ -137,7 +142,8 @@ png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
info_ptr)
{
#ifdef PNG_pHYs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
png_debug1(1, "in %s retrieval function",
"png_get_y_pixels_per_meter");
@@ -157,7 +163,8 @@ png_uint_32 PNGAPI
png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
#ifdef PNG_pHYs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");
@@ -179,7 +186,8 @@ png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
info_ptr)
{
#ifdef PNG_READ_pHYs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");
@@ -202,10 +210,11 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
png_const_inforp info_ptr)
{
#ifdef PNG_READ_pHYs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)
- && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0
- && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX
- && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0 &&
+ info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 &&
+ info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX &&
+ info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)
{
png_fixed_point res;
@@ -215,7 +224,7 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
* range of 0..2^31-1; otherwise the cast might overflow.
*/
if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1,
- (png_int_32)info_ptr->x_pixels_per_unit))
+ (png_int_32)info_ptr->x_pixels_per_unit) != 0)
return res;
}
#else
@@ -231,7 +240,8 @@ png_int_32 PNGAPI
png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
#ifdef PNG_oFFs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_oFFs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");
@@ -250,7 +260,8 @@ png_int_32 PNGAPI
png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
#ifdef PNG_oFFs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_oFFs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");
@@ -269,7 +280,8 @@ png_int_32 PNGAPI
png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
#ifdef PNG_oFFs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_oFFs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");
@@ -288,7 +300,8 @@ png_int_32 PNGAPI
png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
#ifdef PNG_oFFs_SUPPORTED
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_oFFs) != 0)
{
png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");
@@ -307,7 +320,7 @@ png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
static png_uint_32
ppi_from_ppm(png_uint_32 ppm)
{
-#if 0
+#if 0 /*NOT USED*/
/* The conversion is *(2.54/100), in binary (32 digits):
* .00000110100000001001110101001001
*/
@@ -328,7 +341,7 @@ ppi_from_ppm(png_uint_32 ppm)
*/
png_fixed_point result;
if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,
- 5000))
+ 5000) != 0)
return result;
/* Overflow. */
@@ -356,14 +369,25 @@ png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
#ifdef PNG_FIXED_POINT_SUPPORTED
static png_fixed_point
-png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)
+png_fixed_inches_from_microns(png_const_structrp png_ptr,
+ png_int_32 microns)
{
/* Convert from metres * 1,000,000 to inches * 100,000, meters to
* inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.
* Notice that this can overflow - a warning is output and 0 is
* returned.
*/
- return png_muldiv_warn(png_ptr, microns, 500, 127);
+ png_fixed_point result;
+
+ if (png_muldiv(&result, microns, 500, 127) != 0)
+ return result;
+
+ /* TODO: review this, png_error might be better. */
+ png_warning(png_ptr, "inch to microns overflow");
+ return 0;
+# ifndef PNG_WARNINGS_SUPPORTED
+ PNG_UNUSED(png_ptr)
+# endif
}
png_fixed_point PNGAPI
@@ -373,9 +397,7 @@ png_get_x_offset_inches_fixed(png_const_structrp png_ptr,
return png_fixed_inches_from_microns(png_ptr,
png_get_x_offset_microns(png_ptr, info_ptr));
}
-#endif
-#ifdef PNG_FIXED_POINT_SUPPORTED
png_fixed_point PNGAPI
png_get_y_offset_inches_fixed(png_const_structrp png_ptr,
png_const_inforp info_ptr)
@@ -383,7 +405,7 @@ png_get_y_offset_inches_fixed(png_const_structrp png_ptr,
return png_fixed_inches_from_microns(png_ptr,
png_get_y_offset_microns(png_ptr, info_ptr));
}
-#endif
+#endif /* FIXED_POINT */
#ifdef PNG_FLOATING_POINT_SUPPORTED
float PNGAPI
@@ -414,7 +436,8 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
{
png_uint_32 retval = 0;
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
png_debug1(1, "in %s retrieval function", "pHYs");
@@ -445,19 +468,19 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
return (retval);
}
-#endif /* PNG_pHYs_SUPPORTED */
-#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */
+#endif /* pHYs */
+#endif /* INCH_CONVERSIONS */
/* png_get_channels really belongs in here, too, but it's been around longer */
-#endif /* PNG_EASY_ACCESS_SUPPORTED */
+#endif /* EASY_ACCESS */
png_byte PNGAPI
png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
if (png_ptr != NULL && info_ptr != NULL)
- return(info_ptr->channels);
+ return png_check_byte(png_ptr, PNG_FORMAT_CHANNELS(info_ptr->format));
return (0);
}
@@ -476,10 +499,11 @@ png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr)
#ifdef PNG_bKGD_SUPPORTED
png_uint_32 PNGAPI
png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
- png_color_16p *background)
+ png_color_16p *background)
{
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)
- && background != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_bKGD) != 0 &&
+ background != NULL)
{
png_debug1(1, "in %s retrieval function", "bKGD");
@@ -509,34 +533,34 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
* consistent.
*/
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
{
png_debug1(1, "in %s retrieval function", "cHRM");
if (white_x != NULL)
*white_x = png_float(png_ptr,
- info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
+ info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
if (white_y != NULL)
*white_y = png_float(png_ptr,
- info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
+ info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
if (red_x != NULL)
*red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,
- "cHRM red X");
+ "cHRM red X");
if (red_y != NULL)
*red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,
- "cHRM red Y");
+ "cHRM red Y");
if (green_x != NULL)
*green_x = png_float(png_ptr,
- info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
+ info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
if (green_y != NULL)
*green_y = png_float(png_ptr,
- info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
+ info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
if (blue_x != NULL)
*blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,
- "cHRM blue X");
+ "cHRM blue X");
if (blue_y != NULL)
*blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
- "cHRM blue Y");
+ "cHRM blue Y");
return (PNG_INFO_cHRM);
}
@@ -545,42 +569,42 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_uint_32 PNGAPI
png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
- double *red_X, double *red_Y, double *red_Z, double *green_X,
- double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
- double *blue_Z)
+ double *red_X, double *red_Y, double *red_Z, double *green_X,
+ double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
+ double *blue_Z)
{
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
{
png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
if (red_X != NULL)
*red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
- "cHRM red X");
+ "cHRM red X");
if (red_Y != NULL)
*red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,
- "cHRM red Y");
+ "cHRM red Y");
if (red_Z != NULL)
*red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,
- "cHRM red Z");
+ "cHRM red Z");
if (green_X != NULL)
*green_X = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
+ info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
if (green_Y != NULL)
*green_Y = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
+ info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
if (green_Z != NULL)
*green_Z = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
+ info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
if (blue_X != NULL)
*blue_X = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
+ info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
if (blue_Y != NULL)
*blue_Y = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
+ info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
if (blue_Z != NULL)
*blue_Z = png_float(png_ptr,
- info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
+ info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
return (PNG_INFO_cHRM);
}
@@ -598,7 +622,7 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_fixed_point *int_blue_Z)
{
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
{
png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
@@ -635,7 +659,7 @@ png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_debug1(1, "in %s retrieval function", "cHRM");
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
{
if (white_x != NULL)
*white_x = info_ptr->colorspace.end_points_xy.whitex;
@@ -670,8 +694,8 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_debug1(1, "in %s retrieval function", "gAMA");
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) &&
- file_gamma != NULL)
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
+ file_gamma != NULL)
{
*file_gamma = info_ptr->colorspace.gamma;
return (PNG_INFO_gAMA);
@@ -689,11 +713,11 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_debug1(1, "in %s retrieval function", "gAMA(float)");
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) &&
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
file_gamma != NULL)
{
*file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
- "png_get_gAMA");
+ "png_get_gAMA");
return (PNG_INFO_gAMA);
}
@@ -709,8 +733,8 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "sRGB");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)
- && file_srgb_intent != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL)
{
*file_srgb_intent = info_ptr->colorspace.rendering_intent;
return (PNG_INFO_sRGB);
@@ -728,9 +752,10 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "iCCP");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)
- && name != NULL && compression_type != NULL && profile != NULL &&
- proflen != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_iCCP) != 0 &&
+ name != NULL && compression_type != NULL && profile != NULL &&
+ proflen != NULL)
{
*name = info_ptr->iccp_name;
*profile = info_ptr->iccp_profile;
@@ -768,8 +793,8 @@ png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "hIST");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)
- && hist != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL)
{
*hist = info_ptr->hist;
return (PNG_INFO_hIST);
@@ -787,14 +812,20 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "IHDR");
- if (png_ptr == NULL || info_ptr == NULL || width == NULL ||
- height == NULL || bit_depth == NULL || color_type == NULL)
+ if (png_ptr == NULL || info_ptr == NULL)
return (0);
- *width = info_ptr->width;
- *height = info_ptr->height;
- *bit_depth = info_ptr->bit_depth;
- *color_type = info_ptr->color_type;
+ if (width != NULL)
+ *width = info_ptr->width;
+
+ if (height != NULL)
+ *height = info_ptr->height;
+
+ if (bit_depth != NULL)
+ *bit_depth = info_ptr->bit_depth;
+
+ if (color_type != NULL)
+ *color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format);
if (compression_type != NULL)
*compression_type = info_ptr->compression_type;
@@ -805,16 +836,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
if (interlace_type != NULL)
*interlace_type = info_ptr->interlace_type;
- /* This is redundant if we can be sure that the info_ptr values were all
- * assigned in png_set_IHDR(). We do the check anyhow in case an
- * application has ignored our advice not to mess with the members
- * of info_ptr directly.
- */
- png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height,
- info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
- info_ptr->compression_type, info_ptr->filter_type);
-
- return (1);
+ return 1;
}
#ifdef PNG_oFFs_SUPPORTED
@@ -824,8 +846,9 @@ png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "oFFs");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)
- && offset_x != NULL && offset_y != NULL && unit_type != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_oFFs) != 0 &&
+ offset_x != NULL && offset_y != NULL && unit_type != NULL)
{
*offset_x = info_ptr->x_offset;
*offset_y = info_ptr->y_offset;
@@ -845,8 +868,9 @@ png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "pCAL");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)
- && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pCAL) != 0 &&
+ purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
nparams != NULL && units != NULL && params != NULL)
{
*purpose = info_ptr->pcal_purpose;
@@ -871,7 +895,7 @@ png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
int *unit, png_fixed_point *width, png_fixed_point *height)
{
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->valid & PNG_INFO_sCAL))
+ (info_ptr->valid & PNG_INFO_sCAL) != 0)
{
*unit = info_ptr->scal_unit;
/*TODO: make this work without FP support; the API is currently eliminated
@@ -880,7 +904,7 @@ png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
*/
*width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
*height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
- "sCAL height");
+ "sCAL height");
return (PNG_INFO_sCAL);
}
@@ -894,7 +918,7 @@ png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr,
int *unit, double *width, double *height)
{
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->valid & PNG_INFO_sCAL))
+ (info_ptr->valid & PNG_INFO_sCAL) != 0)
{
*unit = info_ptr->scal_unit;
*width = atof(info_ptr->scal_s_width);
@@ -912,7 +936,7 @@ png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr,
int *unit, png_charpp width, png_charpp height)
{
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->valid & PNG_INFO_sCAL))
+ (info_ptr->valid & PNG_INFO_sCAL) != 0)
{
*unit = info_ptr->scal_unit;
*width = info_ptr->scal_s_width;
@@ -934,7 +958,7 @@ png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_debug1(1, "in %s retrieval function", "pHYs");
if (png_ptr != NULL && info_ptr != NULL &&
- (info_ptr->valid & PNG_INFO_pHYs))
+ (info_ptr->valid & PNG_INFO_pHYs) != 0)
{
if (res_x != NULL)
{
@@ -965,8 +989,8 @@ png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "PLTE");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE)
- && palette != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_PLTE) != 0 && palette != NULL)
{
*palette = info_ptr->palette;
*num_palette = info_ptr->num_palette;
@@ -984,8 +1008,8 @@ png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "sBIT");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)
- && sig_bit != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL)
{
*sig_bit = &(info_ptr->sig_bit);
return (PNG_INFO_sBIT);
@@ -1028,8 +1052,8 @@ png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s retrieval function", "tIME");
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)
- && mod_time != NULL)
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL)
{
*mod_time = &(info_ptr->mod_time);
return (PNG_INFO_tIME);
@@ -1045,11 +1069,12 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)
{
png_uint_32 retval = 0;
- if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_tRNS) != 0)
{
png_debug1(1, "in %s retrieval function", "tRNS");
- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
{
if (trans_alpha != NULL)
{
@@ -1061,7 +1086,7 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
*trans_color = &(info_ptr->trans_color);
}
- else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+ else /* if (info_ptr->format not colormapped */
{
if (trans_color != NULL)
{
@@ -1095,15 +1120,17 @@ png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr,
return info_ptr->unknown_chunks_num;
}
- return (0);
+ return 0;
}
#endif
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
png_byte PNGAPI
-png_get_rgb_to_gray_status (png_const_structrp png_ptr)
+png_get_rgb_to_gray_status(png_const_structrp png_ptr)
{
- return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0);
+ if (png_ptr)
+ return png_ptr->rgb_to_gray_status;
+ return 0;
}
#endif
@@ -1111,30 +1138,24 @@ png_get_rgb_to_gray_status (png_const_structrp png_ptr)
png_voidp PNGAPI
png_get_user_chunk_ptr(png_const_structrp png_ptr)
{
- return (png_ptr ? png_ptr->user_chunk_ptr : NULL);
+ if (png_ptr)
+ return png_ptr->user_chunk_ptr;
+
+ return NULL;
}
#endif
-png_size_t PNGAPI
+png_alloc_size_t PNGAPI
png_get_compression_buffer_size(png_const_structrp png_ptr)
{
if (png_ptr == NULL)
return 0;
-# ifdef PNG_WRITE_SUPPORTED
- if (png_ptr->mode & PNG_IS_READ_STRUCT)
-# endif
- {
-# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
- return png_ptr->IDAT_read_size;
-# else
- return PNG_IDAT_READ_SIZE;
-# endif
- }
+# if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+ return png_ptr->IDAT_size;
-# ifdef PNG_WRITE_SUPPORTED
- else
- return png_ptr->zbuffer_size;
+# else
+ return PNG_IDAT_READ_SIZE; /* progressive reader */
# endif
}
@@ -1166,7 +1187,7 @@ png_get_chunk_malloc_max (png_const_structrp png_ptr)
{
return (png_ptr ? png_ptr->user_chunk_malloc_max : 0);
}
-#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+#endif /* SET_USER_LIMITS */
/* These functions were added to libpng 1.4.0 */
#ifdef PNG_IO_STATE_SUPPORTED
@@ -1181,19 +1202,5 @@ png_get_io_chunk_type (png_const_structrp png_ptr)
{
return png_ptr->chunk_name;
}
-#endif /* ?PNG_IO_STATE_SUPPORTED */
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
-int PNGAPI
-png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr)
-{
- if (png_ptr != NULL && info_ptr != NULL)
- return png_ptr->num_palette_max;
-
- return (-1);
-}
-# endif
-#endif
-
-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
+#endif /* IO_STATE */
+#endif /* READ || WRITE */
diff --git a/ext/libpng17/pnginfo.h b/ext/libpng17/pnginfo.h
index 26bf26502c..a0b5d5c761 100644
--- a/ext/libpng17/pnginfo.h
+++ b/ext/libpng17/pnginfo.h
@@ -1,18 +1,17 @@
/* pnginfo.h - header file for PNG reference library
*
- * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.1 [March 28, 2013]
+ * Copyright (c) 1998-2002,2004,2006-2013 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.)
*
- * Last changed in libpng 1.6.1 [March 28, 2013]
- *
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
- /* png_info is a structure that holds the information in a PNG file so
+/* png_info is a structure that holds the information in a PNG file so
* that the application can find out the characteristics of the image.
* If you are reading the file, this structure will tell you what is
* in the PNG file. If you are writing the file, fill in the information
@@ -58,25 +57,17 @@ struct png_info_def
/* The following are necessary for every PNG file */
png_uint_32 width; /* width of image in pixels (from IHDR) */
png_uint_32 height; /* height of image in pixels (from IHDR) */
- png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */
- png_size_t rowbytes; /* bytes needed to hold an untransformed row */
+ unsigned int valid; /* valid chunk data (see PNG_INFO_ in png.h) */
png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */
- png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
- png_uint_16 num_trans; /* number of transparent palette color (tRNS) */
- png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
- png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */
+ unsigned int num_palette:9; /* number of color entries in "palette" (PLTE) */
+ unsigned int num_trans:9; /* number of transparent palette color (tRNS) */
+ unsigned int bit_depth:6; /* 1, 2, 4, 8, 16 or 32 bits/channel */
+ unsigned int format:PNG_RF_BITS; /* row format; see png_struct.h */
/* The following three should have been named *_method not *_type */
png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
- /* The following are set by png_set_IHDR, called from the application on
- * write, but the are never actually used by the write code.
- */
- png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */
- png_byte pixel_depth; /* number of bits per pixel */
- png_byte spare_byte; /* to align the data, and for future use */
-
#ifdef PNG_READ_SUPPORTED
/* This is never set during write */
png_byte signature[8]; /* magic bytes read by libpng from start of file */
@@ -106,7 +97,6 @@ struct png_info_def
/* iCCP chunk data. */
png_charp iccp_name; /* profile name */
png_bytep iccp_profile; /* International Color Consortium profile data */
- png_uint_32 iccp_proflen; /* ICC profile data length */
#endif
#ifdef PNG_TEXT_SUPPORTED
@@ -121,13 +111,14 @@ struct png_info_def
int num_text; /* number of comments read or comments to write */
int max_text; /* current size of text array */
png_textp text; /* array of comments read or comments to write */
-#endif /* PNG_TEXT_SUPPORTED */
+#endif /* TEXT */
#ifdef PNG_tIME_SUPPORTED
/* The tIME chunk holds the last time the displayed image data was
* modified. See the png_time struct for the contents of this struct.
*/
png_time mod_time;
+ png_byte time_location;
#endif
#ifdef PNG_sBIT_SUPPORTED
@@ -224,7 +215,7 @@ defined(PNG_READ_BACKGROUND_SUPPORTED)
/* Storage for unknown chunks that the library doesn't recognize. */
png_unknown_chunkp unknown_chunks;
- /* The type of this field is limited by the type of
+ /* The type of this field is limited by the type of
* png_struct::user_chunk_cache_max, else overflow can occur.
*/
int unknown_chunks_num;
diff --git a/ext/libpng17/pnglibconf.h b/ext/libpng17/pnglibconf.h
index b93120c791..f75c7c3a35 100644
--- a/ext/libpng17/pnglibconf.h
+++ b/ext/libpng17/pnglibconf.h
@@ -1,10 +1,10 @@
-/* libpng 1.7.0beta35 STANDARD API DEFINITION */
+/* libpng 1.7.0beta90 STANDARD API DEFINITION */
/* pnglibconf.h - library build configuration */
-/* Libpng version 1.7.0beta35 - March 22, 2014 */
+/* libpng version 1.7.0beta90, August 28, 2017 */
-/* Copyright (c) 1998-2013 Glenn Randers-Pehrson */
+/* Copyright (c) 1998-2016 Glenn Randers-Pehrson */
/* This code is released under the libpng license. */
/* For conditions of distribution and use, see the disclaimer */
@@ -18,8 +18,6 @@
/* options */
#define PNG_16BIT_SUPPORTED
#define PNG_ALIGNED_MEMORY_SUPPORTED
-/*#undef PNG_ARM_NEON_API_SUPPORTED*/
-/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
#define PNG_BENIGN_ERRORS_SUPPORTED
#define PNG_BENIGN_READ_ERRORS_SUPPORTED
/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
@@ -29,7 +27,6 @@
#define PNG_CONSOLE_IO_SUPPORTED
#define PNG_CONVERT_tIME_SUPPORTED
#define PNG_EASY_ACCESS_SUPPORTED
-/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
#define PNG_ERROR_TEXT_SUPPORTED
#define PNG_FIXED_POINT_SUPPORTED
#define PNG_FLOATING_ARITHMETIC_SUPPORTED
@@ -43,7 +40,9 @@
#define PNG_INFO_IMAGE_SUPPORTED
#define PNG_IO_STATE_SUPPORTED
#define PNG_MNG_FEATURES_SUPPORTED
-#define PNG_POINTER_INDEXING_SUPPORTED
+#define PNG_MNG_READ_FEATURES_SUPPORTED
+#define PNG_MNG_WRITE_FEATURES_SUPPORTED
+#define PNG_PALETTE_MAX_SUPPORTED
#define PNG_PROGRESSIVE_READ_SUPPORTED
#define PNG_READ_16BIT_SUPPORTED
#define PNG_READ_ALPHA_MODE_SUPPORTED
@@ -59,6 +58,7 @@
#define PNG_READ_GAMMA_SUPPORTED
#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_IMAGE_SUPPORTED
#define PNG_READ_INTERLACING_SUPPORTED
#define PNG_READ_INT_FUNCTIONS_SUPPORTED
#define PNG_READ_INVERT_ALPHA_SUPPORTED
@@ -66,6 +66,7 @@
#define PNG_READ_OPT_PLTE_SUPPORTED
#define PNG_READ_PACKSWAP_SUPPORTED
#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_PNG_SUPPORTED
#define PNG_READ_QUANTIZE_SUPPORTED
#define PNG_READ_RGB_TO_GRAY_SUPPORTED
#define PNG_READ_SCALE_16_TO_8_SUPPORTED
@@ -97,9 +98,8 @@
#define PNG_READ_tIME_SUPPORTED
#define PNG_READ_tRNS_SUPPORTED
#define PNG_READ_zTXt_SUPPORTED
-/*#undef PNG_SAFE_LIMITS_SUPPORTED*/
-#define PNG_SAVE_INT_32_SUPPORTED
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SELECT_FILTER_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
#define PNG_SET_OPTION_SUPPORTED
@@ -110,11 +110,13 @@
#define PNG_SIMPLIFIED_READ_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_SUPPORTED
#define PNG_STDIO_SUPPORTED
#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_TEXT_SUPPORTED
#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_TRANSFORM_MECH_SUPPORTED
#define PNG_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_USER_CHUNKS_SUPPORTED
#define PNG_USER_LIMITS_SUPPORTED
@@ -127,6 +129,7 @@
#define PNG_WRITE_BGR_SUPPORTED
#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
#define PNG_WRITE_FILLER_SUPPORTED
#define PNG_WRITE_FILTER_SUPPORTED
@@ -139,6 +142,7 @@
#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
#define PNG_WRITE_PACKSWAP_SUPPORTED
#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_PNG_SUPPORTED
#define PNG_WRITE_SHIFT_SUPPORTED
#define PNG_WRITE_SUPPORTED
#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
@@ -186,24 +190,23 @@
/* settings */
#define PNG_ABORT { (abort()); }
#define PNG_API_RULE 0
-#define PNG_COST_SHIFT 3
+#define PNG_DEFAULT_COMPRESSION_LEVEL PNG_COMPRESSION_MEDIUM
+#define PNG_DEFAULT_GAMMA_ACCURACY 665
#define PNG_DEFAULT_READ_MACROS 1
-#define PNG_GAMMA_THRESHOLD_FIXED 5000
-#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_GAMMA_THRESHOLD_FIXED 153
+#define PNG_IDAT_READ_SIZE 4096
#define PNG_INFLATE_BUF_SIZE 1024
#define PNG_MAX_GAMMA_8 11
#define PNG_QUANTIZE_BLUE_BITS 5
#define PNG_QUANTIZE_GREEN_BITS 5
#define PNG_QUANTIZE_RED_BITS 5
-#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
-#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
-#define PNG_WEIGHT_SHIFT 8
-#define PNG_ZLIB_VERNUM 0 /* unknown */
-#define PNG_ZBUF_SIZE 8192
+#define PNG_USER_CHUNK_CACHE_MAX 1000
+#define PNG_USER_CHUNK_MALLOC_MAX 8000000
+#define PNG_USER_HEIGHT_MAX 1000000
+#define PNG_USER_WIDTH_MAX 1000000
+#define PNG_ZBUF_SIZE 4096
#define PNG_ZLIB_HEADER
-#define PNG_Z_DEFAULT_COMPRESSION (-1)
-#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
-#define PNG_Z_DEFAULT_STRATEGY 1
+#define PNG_ZLIB_VERNUM 0
#define PNG_sCAL_PRECISION 5
#define PNG_sRGB_PROFILE_CHECKS 2
/* end of settings */
diff --git a/ext/libpng17/pngmem.c b/ext/libpng17/pngmem.c
index db336c8698..0593bb35f2 100644
--- a/ext/libpng17/pngmem.c
+++ b/ext/libpng17/pngmem.c
@@ -2,7 +2,7 @@
/* pngmem.c - stub functions for memory allocation
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -18,6 +18,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngmem
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
/* Free a png_struct */
@@ -41,7 +42,7 @@ png_destroy_png_struct(png_structrp png_ptr)
}
/* Allocate memory. For reasonable files, size should never exceed
- * 64K. However, zlib may allocate more then 64K if you don't tell
+ * 64K. However, zlib may allocate more than 64K if you don't tell
* it not to. See zconf.h and png.h for more information. zlib does
* need to allocate exactly 64K, so whatever you call here must
* have the ability to do that.
@@ -66,7 +67,7 @@ png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
*/
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
- PNG_ALLOCATED)
+ PNG_ALLOCATED)
{
/* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
* allocators have also been removed in 1.6.0, so any 16-bit system now has
@@ -77,19 +78,26 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
PNG_UNUSED(png_ptr)
#endif
- if (size > 0 && size <= PNG_SIZE_MAX
-# ifdef PNG_MAX_MALLOC_64K
- && size <= 65536U
-# endif
- )
+ /* Some compilers complain that this is always true. However, it
+ * can be false when integer overflow happens.
+ */
+#ifdef PNG_MAX_MALLOC_64K
+ if (size > 0 && size <= PNG_SIZE_MAX && size <= 65536U)
+#else
+ if (size > 0 && size <= PNG_SIZE_MAX)
+#endif
{
+ png_voidp result;
+
#ifdef PNG_USER_MEM_SUPPORTED
if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
- return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
+ result = png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
else
#endif
- return malloc((size_t)size); /* checked for truncation above */
+ result = malloc((size_t)size); /* checked for truncation above */
+
+ return result;
}
else
@@ -104,7 +112,7 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
*/
static png_voidp
png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
- size_t element_size)
+ size_t element_size)
{
png_alloc_size_t req = nelements; /* known to be > 0 */
@@ -117,22 +125,19 @@ png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_malloc_array,(png_const_structrp png_ptr, int nelements,
- size_t element_size),PNG_ALLOCATED)
+ size_t element_size),PNG_ALLOCATED)
{
- if (nelements <= 0 || element_size == 0)
- png_error(png_ptr, "internal error: array alloc");
-
+ affirm(nelements > 0 && element_size > 0);
return png_malloc_array_checked(png_ptr, nelements, element_size);
}
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array,
- int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
+ int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
{
/* These are internal errors: */
- if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
- (old_array == NULL && old_elements > 0))
- png_error(png_ptr, "internal error: array realloc");
+ affirm(add_elements > 0 && element_size > 0 && old_elements >= 0 &&
+ (old_array != NULL || old_elements == 0));
/* Check for overflow on the elements count (so the caller does not have to
* check.)
@@ -140,7 +145,7 @@ png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array,
if (add_elements <= INT_MAX - old_elements)
{
png_voidp new_array = png_malloc_array_checked(png_ptr,
- old_elements+add_elements, element_size);
+ old_elements+add_elements, element_size);
if (new_array != NULL)
{
@@ -151,7 +156,7 @@ png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array,
memcpy(new_array, old_array, element_size*(unsigned)old_elements);
memset((char*)new_array + element_size*(unsigned)old_elements, 0,
- element_size*(unsigned)add_elements);
+ element_size*(unsigned)add_elements);
return new_array;
}
@@ -196,7 +201,7 @@ png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
*/
PNG_FUNCTION(png_voidp,PNGAPI
png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
- PNG_ALLOCATED)
+ PNG_ALLOCATED)
{
if (png_ptr != NULL)
{
@@ -225,8 +230,11 @@ png_free(png_const_structrp png_ptr, png_voidp ptr)
png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);
else
-#endif /* PNG_USER_MEM_SUPPORTED */
free(ptr);
+#else
+ free(ptr);
+#endif /* USER_MEM */
+
}
#ifdef PNG_USER_MEM_SUPPORTED
@@ -257,5 +265,5 @@ png_get_mem_ptr(png_const_structrp png_ptr)
return png_ptr->mem_ptr;
}
-#endif /* PNG_USER_MEM_SUPPORTED */
-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
+#endif /* USER_MEM */
+#endif /* READ || WRITE */
diff --git a/ext/libpng17/pngpread.c b/ext/libpng17/pngpread.c
index 0b251f7363..c7e5425aaf 100644
--- a/ext/libpng17/pngpread.c
+++ b/ext/libpng17/pngpread.c
@@ -2,7 +2,7 @@
/* pngpread.c - read a png file in push mode
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -12,608 +12,12 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngpread
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-/* Push model modes */
-#define PNG_READ_SIG_MODE 0
-#define PNG_READ_CHUNK_MODE 1
-#define PNG_READ_IDAT_MODE 2
-#define PNG_SKIP_MODE 3
-#define PNG_READ_tEXt_MODE 4
-#define PNG_READ_zTXt_MODE 5
-#define PNG_READ_DONE_MODE 6
-#define PNG_READ_iTXt_MODE 7
-#define PNG_ERROR_MODE 8
-
-void PNGAPI
-png_process_data(png_structrp png_ptr, png_inforp info_ptr,
- png_bytep buffer, png_size_t buffer_size)
-{
- if (png_ptr == NULL || info_ptr == NULL)
- return;
-
- png_push_restore_buffer(png_ptr, buffer, buffer_size);
-
- while (png_ptr->buffer_size)
- {
- png_process_some_data(png_ptr, info_ptr);
- }
-}
-
-png_size_t PNGAPI
-png_process_data_pause(png_structrp png_ptr, int save)
-{
- if (png_ptr != NULL)
- {
- /* It's easiest for the caller if we do the save, then the caller doesn't
- * have to supply the same data again:
- */
- if (save != 0)
- png_push_save_buffer(png_ptr);
- else
- {
- /* This includes any pending saved bytes: */
- png_size_t remaining = png_ptr->buffer_size;
- png_ptr->buffer_size = 0;
-
- /* So subtract the saved buffer size, unless all the data
- * is actually 'saved', in which case we just return 0
- */
- if (png_ptr->save_buffer_size < remaining)
- return remaining - png_ptr->save_buffer_size;
- }
- }
-
- return 0;
-}
-
-png_uint_32 PNGAPI
-png_process_data_skip(png_structrp png_ptr)
-{
- png_uint_32 remaining = 0;
-
- if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE &&
- png_ptr->skip_length > 0)
- {
- /* At the end of png_process_data the buffer size must be 0 (see the loop
- * above) so we can detect a broken call here:
- */
- if (png_ptr->buffer_size != 0)
- png_error(png_ptr,
- "png_process_data_skip called inside png_process_data");
-
- /* If is impossible for there to be a saved buffer at this point -
- * otherwise we could not be in SKIP mode. This will also happen if
- * png_process_skip is called inside png_process_data (but only very
- * rarely.)
- */
- if (png_ptr->save_buffer_size != 0)
- png_error(png_ptr, "png_process_data_skip called with saved data");
-
- remaining = png_ptr->skip_length;
- png_ptr->skip_length = 0;
- png_ptr->process_mode = PNG_READ_CHUNK_MODE;
- }
-
- return remaining;
-}
-
-/* What we do with the incoming data depends on what we were previously
- * doing before we ran out of data...
- */
-void /* PRIVATE */
-png_process_some_data(png_structrp png_ptr, png_inforp info_ptr)
-{
- if (png_ptr == NULL)
- return;
-
- switch (png_ptr->process_mode)
- {
- case PNG_READ_SIG_MODE:
- {
- png_push_read_sig(png_ptr, info_ptr);
- break;
- }
-
- case PNG_READ_CHUNK_MODE:
- {
- png_push_read_chunk(png_ptr, info_ptr);
- break;
- }
-
- case PNG_READ_IDAT_MODE:
- {
- png_push_read_IDAT(png_ptr);
- break;
- }
-
- case PNG_SKIP_MODE:
- {
- png_push_crc_finish(png_ptr);
- break;
- }
-
- default:
- {
- png_ptr->buffer_size = 0;
- break;
- }
- }
-}
-
-/* Read any remaining signature bytes from the stream and compare them with
- * the correct PNG signature. It is possible that this routine is called
- * with bytes already read from the signature, either because they have been
- * checked by the calling application, or because of multiple calls to this
- * routine.
- */
-void /* PRIVATE */
-png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)
-{
- png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */
- num_to_check = 8 - num_checked;
-
- if (png_ptr->buffer_size < num_to_check)
- {
- num_to_check = png_ptr->buffer_size;
- }
-
- png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
- num_to_check);
- png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);
-
- if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
- {
- 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");
- }
- else
- {
- if (png_ptr->sig_bytes >= 8)
- {
- png_ptr->process_mode = PNG_READ_CHUNK_MODE;
- }
- }
-}
-
-void /* PRIVATE */
-png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
-{
- png_uint_32 chunk_name;
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
- int keep; /* unknown handling method */
-#endif
-
- /* First we make sure we have enough data for the 4 byte chunk name
- * and the 4 byte chunk length before proceeding with decoding the
- * chunk data. To fully decode each of these chunks, we also make
- * sure we have enough data in the buffer for the 4 byte CRC at the
- * end of every chunk (except IDAT, which is handled separately).
- */
- if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
- {
- png_byte chunk_length[4];
- png_byte chunk_tag[4];
-
- if (png_ptr->buffer_size < 8)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_push_fill_buffer(png_ptr, chunk_length, 4);
- png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
- png_reset_crc(png_ptr);
- png_crc_read(png_ptr, chunk_tag, 4);
- png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
- png_check_chunk_name(png_ptr, png_ptr->chunk_name);
- png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
- }
-
- chunk_name = png_ptr->chunk_name;
-
- if (chunk_name == png_IDAT)
- {
- if (png_ptr->mode & PNG_AFTER_IDAT)
- png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
-
- /* If we reach an IDAT chunk, this means we have read all of the
- * header chunks, and we can start reading the image (or if this
- * is called after the image has been read - we have an error).
- */
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_error(png_ptr, "Missing IHDR before IDAT");
-
- else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
- !(png_ptr->mode & PNG_HAVE_PLTE))
- png_error(png_ptr, "Missing PLTE before IDAT");
-
- png_ptr->mode |= PNG_HAVE_IDAT;
- png_ptr->process_mode = PNG_READ_IDAT_MODE;
-
- if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
- if (png_ptr->push_length == 0)
- return;
-
- if (png_ptr->mode & PNG_AFTER_IDAT)
- png_benign_error(png_ptr, "Too many IDATs found[p]");
- }
-
- if (chunk_name == png_IHDR)
- {
- if (png_ptr->push_length != 13)
- png_error(png_ptr, "Invalid IHDR length");
-
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
- }
-
- else if (chunk_name == png_IEND)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
-
- png_ptr->process_mode = PNG_READ_DONE_MODE;
- png_push_have_end(png_ptr, info_ptr);
- }
-
-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
- else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep);
-
- if (chunk_name == png_PLTE)
- png_ptr->mode |= PNG_HAVE_PLTE;
- }
-#endif
-
- else if (chunk_name == png_PLTE)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
- png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
- }
-
- else if (chunk_name == png_IDAT)
- {
- png_ptr->idat_size = png_ptr->push_length;
- png_ptr->process_mode = PNG_READ_IDAT_MODE;
- png_push_have_info(png_ptr, info_ptr);
- png_ptr->zstream.avail_out =
- (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,
- png_ptr->iwidth) + 1;
- png_ptr->zstream.next_out = png_ptr->row_buf;
- return;
- }
-
-#ifdef PNG_READ_gAMA_SUPPORTED
- else if (png_ptr->chunk_name == png_gAMA)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_sBIT_SUPPORTED
- else if (png_ptr->chunk_name == png_sBIT)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_cHRM_SUPPORTED
- else if (png_ptr->chunk_name == png_cHRM)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_sRGB_SUPPORTED
- else if (chunk_name == png_sRGB)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_iCCP_SUPPORTED
- else if (png_ptr->chunk_name == png_iCCP)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_sPLT_SUPPORTED
- else if (chunk_name == png_sPLT)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_tRNS_SUPPORTED
- else if (chunk_name == png_tRNS)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_bKGD_SUPPORTED
- else if (chunk_name == png_bKGD)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_hIST_SUPPORTED
- else if (chunk_name == png_hIST)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_pHYs_SUPPORTED
- else if (chunk_name == png_pHYs)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_oFFs_SUPPORTED
- else if (chunk_name == png_oFFs)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
- }
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
- else if (chunk_name == png_pCAL)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_sCAL_SUPPORTED
- else if (chunk_name == png_sCAL)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_tIME_SUPPORTED
- else if (chunk_name == png_tIME)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_tEXt_SUPPORTED
- else if (chunk_name == png_tEXt)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_zTXt_SUPPORTED
- else if (chunk_name == png_zTXt)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
- }
-
-#endif
-#ifdef PNG_READ_iTXt_SUPPORTED
- else if (chunk_name == png_iTXt)
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
- }
-#endif
-
- else
- {
- if (png_ptr->push_length + 4 > png_ptr->buffer_size)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
- png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
- PNG_HANDLE_CHUNK_AS_DEFAULT);
- }
-
- png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
-}
-
-void /* PRIVATE */
-png_push_crc_skip(png_structrp png_ptr, png_uint_32 skip)
-{
- png_ptr->process_mode = PNG_SKIP_MODE;
- png_ptr->skip_length = skip;
-}
-
-void /* PRIVATE */
-png_push_crc_finish(png_structrp png_ptr)
-{
- if (png_ptr->skip_length && png_ptr->save_buffer_size)
- {
- png_size_t save_size = png_ptr->save_buffer_size;
- png_uint_32 skip_length = png_ptr->skip_length;
-
- /* We want the smaller of 'skip_length' and 'save_buffer_size', but
- * they are of different types and we don't know which variable has the
- * fewest bits. Carefully select the smaller and cast it to the type of
- * the larger - this cannot overflow. Do not cast in the following test
- * - it will break on either 16 or 64 bit platforms.
- */
- if (skip_length < save_size)
- save_size = (png_size_t)skip_length;
-
- else
- skip_length = (png_uint_32)save_size;
-
- png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
-
- png_ptr->skip_length -= skip_length;
- png_ptr->buffer_size -= save_size;
- png_ptr->save_buffer_size -= save_size;
- png_ptr->save_buffer_ptr += save_size;
- }
- if (png_ptr->skip_length && png_ptr->current_buffer_size)
- {
- png_size_t save_size = png_ptr->current_buffer_size;
- png_uint_32 skip_length = png_ptr->skip_length;
-
- /* We want the smaller of 'skip_length' and 'current_buffer_size', here,
- * the same problem exists as above and the same solution.
- */
- if (skip_length < save_size)
- save_size = (png_size_t)skip_length;
-
- else
- skip_length = (png_uint_32)save_size;
-
- png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
-
- png_ptr->skip_length -= skip_length;
- png_ptr->buffer_size -= save_size;
- png_ptr->current_buffer_size -= save_size;
- png_ptr->current_buffer_ptr += save_size;
- }
- if (!png_ptr->skip_length)
- {
- if (png_ptr->buffer_size < 4)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
-
- png_crc_finish(png_ptr, 0);
- png_ptr->process_mode = PNG_READ_CHUNK_MODE;
- }
-}
-
-void PNGCBAPI
+/* Standard callbacks */
+static void PNGCBAPI
png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
{
png_bytep ptr;
@@ -622,7 +26,9 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
return;
ptr = buffer;
- if (png_ptr->save_buffer_size)
+ debug(length > 0);
+
+ if (length > 0 && png_ptr->save_buffer_size > 0)
{
png_size_t save_size;
@@ -639,7 +45,8 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
png_ptr->save_buffer_size -= save_size;
png_ptr->save_buffer_ptr += save_size;
}
- if (length && png_ptr->current_buffer_size)
+
+ if (length > 0 && png_ptr->current_buffer_size > 0)
{
png_size_t save_size;
@@ -656,615 +63,782 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
}
}
-void /* PRIVATE */
+/* Push model modes: png_chunk_op plus extras */
+typedef enum
+{
+ /* all the png_chunk_op codes, plus: */
+ png_read_signature = 0, /* starting value */
+ png_read_chunk_header,
+ png_read_end_IDAT,
+ png_read_done,
+ png_read_chunk, /* Not a state, use these derived from png_chunk_op: */
+ png_read_chunk_skip = png_read_chunk+png_chunk_skip,
+ png_read_chunk_unknown = png_read_chunk+png_chunk_unknown,
+ png_read_chunk_process_all = png_read_chunk+png_chunk_process_all,
+ png_read_chunk_process_part = png_read_chunk+png_chunk_process_part
+} png_read_mode;
+
+static void
+png_push_save_buffer_partial(png_structrp png_ptr, size_t amount)
+{
+ /* Copy 'amount' bytes to the end of the save buffer from the current
+ * buffer.
+ */
+ png_bytep buffer;
+ size_t save_size = png_ptr->save_buffer_size;
+
+ if (save_size > PNG_SIZE_MAX - amount)
+ png_error(png_ptr, "save buffer overflow");
+
+ if (png_ptr->save_buffer_max < save_size + amount)
+ {
+ /* Reallocate the save buffer. */
+ buffer = png_voidcast(png_bytep, png_malloc(png_ptr, save_size + amount));
+ memcpy(buffer, png_ptr->save_buffer_ptr, save_size);
+ png_free(png_ptr, png_ptr->save_buffer);
+ png_ptr->save_buffer_ptr = png_ptr->save_buffer = buffer;
+ }
+
+ else if (png_ptr->save_buffer_max -
+ (png_ptr->save_buffer_ptr - png_ptr->save_buffer) < save_size + amount)
+ {
+ /* Move the existing saved data */
+ buffer = png_ptr->save_buffer;
+ memmove(buffer, png_ptr->save_buffer_ptr, save_size);
+ png_ptr->save_buffer_ptr = buffer;
+ }
+
+ else /* Just copy the data */
+ buffer = png_ptr->save_buffer_ptr;
+
+ memcpy(buffer+save_size, png_ptr->current_buffer_ptr, amount);
+ png_ptr->save_buffer_size = save_size + amount;
+ png_ptr->current_buffer_ptr += amount;
+ png_ptr->current_buffer_size -= amount;
+ png_ptr->buffer_size -= amount;
+}
+
+static void
png_push_save_buffer(png_structrp png_ptr)
{
- if (png_ptr->save_buffer_size)
- {
- if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
- {
- png_size_t i, istop;
- png_bytep sp;
- png_bytep dp;
-
- istop = png_ptr->save_buffer_size;
- for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
- i < istop; i++, sp++, dp++)
- {
- *dp = *sp;
- }
- }
- }
- if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
- png_ptr->save_buffer_max)
- {
- png_size_t new_max;
- png_bytep old_buffer;
-
- if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
- (png_ptr->current_buffer_size + 256))
- {
- png_error(png_ptr, "Potential overflow of save_buffer");
- }
-
- new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
- old_buffer = png_ptr->save_buffer;
- png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,
- (png_size_t)new_max);
-
- if (png_ptr->save_buffer == NULL)
- {
- png_free(png_ptr, old_buffer);
- png_error(png_ptr, "Insufficient memory for save_buffer");
- }
-
- memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
- png_free(png_ptr, old_buffer);
- png_ptr->save_buffer_max = new_max;
- }
- if (png_ptr->current_buffer_size)
- {
- memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
- png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
- png_ptr->save_buffer_size += png_ptr->current_buffer_size;
- png_ptr->current_buffer_size = 0;
- }
- png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+ png_push_save_buffer_partial(png_ptr, png_ptr->current_buffer_size);
+ /* This terminates the process loop. */
png_ptr->buffer_size = 0;
}
-void /* PRIVATE */
-png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,
- png_size_t buffer_length)
-{
- png_ptr->current_buffer = buffer;
- png_ptr->current_buffer_size = buffer_length;
- png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
- png_ptr->current_buffer_ptr = png_ptr->current_buffer;
-}
+#define PNG_PUSH_SAVE_BUFFER_IF_FULL \
+if (png_ptr->chunk_length + 4 > png_ptr->buffer_size) \
+ { png_push_save_buffer(png_ptr); return; }
+#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \
+if (png_ptr->buffer_size < N) \
+ { png_push_save_buffer(png_ptr); return; }
-void /* PRIVATE */
-png_push_read_IDAT(png_structrp png_ptr)
+png_size_t PNGAPI
+png_process_data_pause(png_structrp png_ptr, int save)
{
- if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+ if (png_ptr != NULL)
{
- png_byte chunk_length[4];
- png_byte chunk_tag[4];
-
- /* TODO: this code can be commoned up with the same code in push_read */
- if (png_ptr->buffer_size < 8)
- {
+ /* It's easiest for the caller if we do the save; then the caller doesn't
+ * have to supply the same data again:
+ */
+ if (save != 0)
png_push_save_buffer(png_ptr);
- return;
- }
-
- png_push_fill_buffer(png_ptr, chunk_length, 4);
- png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
- png_reset_crc(png_ptr);
- png_crc_read(png_ptr, chunk_tag, 4);
- png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
- png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
-
- if (png_ptr->chunk_name != png_IDAT)
+ else
{
- png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+ /* This includes any pending saved bytes: */
+ png_size_t remaining = png_ptr->buffer_size;
+ png_ptr->buffer_size = 0; /* Terminate the process loop */
- if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
- png_error(png_ptr, "Not enough compressed data");
-
- return;
+ /* So subtract the saved buffer size, unless all the data
+ * is actually 'saved', in which case we just return 0
+ */
+ if (png_ptr->save_buffer_size < remaining)
+ return remaining - png_ptr->save_buffer_size;
}
-
- png_ptr->idat_size = png_ptr->push_length;
}
- if (png_ptr->idat_size && png_ptr->save_buffer_size)
- {
- png_size_t save_size = png_ptr->save_buffer_size;
- png_uint_32 idat_size = png_ptr->idat_size;
+ return 0;
+}
- /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
- * are of different types and we don't know which variable has the fewest
- * bits. Carefully select the smaller and cast it to the type of the
- * larger - this cannot overflow. Do not cast in the following test - it
- * will break on either 16 or 64 bit platforms.
+png_uint_32 PNGAPI
+png_process_data_skip(png_structrp png_ptr)
+{
+ if (png_ptr != NULL && png_ptr->process_mode == png_read_chunk_skip)
+ {
+ /* At the end of png_process_data the buffer size must be 0 (see the loop
+ * above) so we can detect a broken call here:
*/
- if (idat_size < save_size)
- save_size = (png_size_t)idat_size;
+ if (png_ptr->buffer_size != 0)
+ png_app_error(png_ptr,
+ "png_process_data_skip called inside png_process_data");
+
+ /* If is impossible for there to be a saved buffer at this point -
+ * otherwise we could not be in SKIP mode. This will also happen if
+ * png_process_skip is called inside png_process_data (but only very
+ * rarely.)
+ */
+ else if (png_ptr->save_buffer_size != 0)
+ png_app_error(png_ptr, "png_process_data_skip called with saved data");
else
- idat_size = (png_uint_32)save_size;
+ {
+ /* Skipping png_ptr->chunk_length of data then checking the CRC, after
+ * that a new chunk header will be read.
+ */
+ png_ptr->process_mode = png_read_chunk_header;
+ return png_ptr->chunk_length + 4;
+ }
+ }
+
+ return 0;
+}
+
+static void
+png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,
+ png_size_t buffer_length)
+{
+ png_ptr->current_buffer_size = buffer_length;
+ png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+ png_ptr->current_buffer_ptr = buffer;
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature. It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+static void
+png_push_read_signature(png_structrp png_ptr, png_inforp info_ptr)
+{
+ unsigned int num_checked = png_ptr->sig_bytes;
+ unsigned int num_to_check = 8 - num_checked;
+
+ if (png_ptr->buffer_size < num_to_check)
+ num_to_check = (int)/*SAFE*/png_ptr->buffer_size;
+
+ png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+ num_to_check);
+ png_ptr->sig_bytes = png_check_byte(png_ptr,
+ png_ptr->sig_bytes + num_to_check);
+
+ if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+ {
+ 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");
+ }
+
+ else if (png_ptr->sig_bytes >= 8)
+ png_ptr->process_mode = png_read_chunk_header;
+}
+
+static void
+png_push_crc_finish(png_structrp png_ptr)
+ /* CRC the remainder of the chunk data; png_struct::chunk_length must be the
+ * amount of data left to read excluding the CRC.
+ */
+{
+ if (png_ptr->chunk_length != 0 && png_ptr->save_buffer_size != 0)
+ {
+ png_size_t save_size = png_ptr->save_buffer_size;
+ png_uint_32 skip_length = png_ptr->chunk_length;
+
+ /* We want the smaller of 'skip_length' and 'save_buffer_size', but
+ * they are of different types and we don't know which variable has the
+ * fewest bits. Carefully select the smaller and cast it to the type of
+ * the larger - this cannot overflow. Do not cast in the following test
+ * - it will break on either 16 or 64 bit platforms.
+ */
+ if (skip_length < save_size)
+ save_size = (png_size_t)/*SAFE*/skip_length;
+
+ else
+ skip_length = (png_uint_32)/*SAFE*/save_size;
png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
- png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
-
- png_ptr->idat_size -= idat_size;
+ png_ptr->chunk_length -= skip_length;
png_ptr->buffer_size -= save_size;
png_ptr->save_buffer_size -= save_size;
png_ptr->save_buffer_ptr += save_size;
}
- if (png_ptr->idat_size && png_ptr->current_buffer_size)
+ if (png_ptr->chunk_length != 0 && png_ptr->current_buffer_size != 0)
{
png_size_t save_size = png_ptr->current_buffer_size;
- png_uint_32 idat_size = png_ptr->idat_size;
+ png_uint_32 skip_length = png_ptr->chunk_length;
- /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
- * are of different types and we don't know which variable has the fewest
- * bits. Carefully select the smaller and cast it to the type of the
- * larger - this cannot overflow.
+ /* We want the smaller of 'skip_length' and 'current_buffer_size', here,
+ * the same problem exists as above and the same solution.
*/
- if (idat_size < save_size)
- save_size = (png_size_t)idat_size;
+ if (skip_length < save_size)
+ save_size = (png_size_t)/*SAFE*/skip_length;
else
- idat_size = (png_uint_32)save_size;
+ skip_length = (png_uint_32)/*SAFE*/save_size;
png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
- png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
-
- png_ptr->idat_size -= idat_size;
+ png_ptr->chunk_length -= skip_length;
png_ptr->buffer_size -= save_size;
png_ptr->current_buffer_size -= save_size;
png_ptr->current_buffer_ptr += save_size;
}
- if (!png_ptr->idat_size)
- {
- if (png_ptr->buffer_size < 4)
- {
- png_push_save_buffer(png_ptr);
- return;
- }
+ if (png_ptr->chunk_length == 0)
+ {
+ PNG_PUSH_SAVE_BUFFER_IF_LT(4)
png_crc_finish(png_ptr, 0);
- png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
- png_ptr->mode |= PNG_AFTER_IDAT;
- png_ptr->zowner = 0;
+ png_ptr->process_mode = png_read_chunk_header;
}
}
-void /* PRIVATE */
-png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer,
- png_size_t buffer_length)
+static void
+png_push_read_unknown(png_structrp png_ptr, png_inforp info_ptr)
{
- /* The caller checks for a non-zero buffer length. */
- if (!(buffer_length > 0) || buffer == NULL)
- png_error(png_ptr, "No IDAT data (internal error)");
-
- /* This routine must process all the data it has been given
- * before returning, calling the row callback as required to
- * handle the uncompressed results.
+ /* Handle an unknown chunk. All the data is available but it may not
+ * all be in the same buffer. png_handle_unknown needs the chunk data in
+ * just one buffer.
*/
- png_ptr->zstream.next_in = buffer;
- /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
- png_ptr->zstream.avail_in = (uInt)buffer_length;
+ png_bytep buffer;
+ png_uint_32 chunk_length = png_ptr->chunk_length;
- /* Keep going until the decompressed data is all processed
- * or the stream marked as finished.
- */
- while (png_ptr->zstream.avail_in > 0 &&
- !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+ if (png_ptr->save_buffer_size > 0)
{
- int ret;
+ png_size_t save_size = png_ptr->save_buffer_size;
- /* We have data for zlib, but we must check that zlib
- * has someplace to put the results. It doesn't matter
- * if we don't expect any results -- it may be the input
- * data is just the LZ end code.
- */
- if (!(png_ptr->zstream.avail_out > 0))
+ if (save_size < chunk_length)
{
- /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
- png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,
- png_ptr->iwidth) + 1);
-
- png_ptr->zstream.next_out = png_ptr->row_buf;
+ /* Copy the current_buffer_ptr data into the save buffer. */
+ png_push_save_buffer_partial(png_ptr, chunk_length - save_size);
+ save_size = chunk_length;
}
- /* Using Z_SYNC_FLUSH here means that an unterminated
- * LZ stream (a stream with a missing end code) can still
- * be handled, otherwise (Z_NO_FLUSH) a future zlib
- * implementation might defer output and therefore
- * change the current behavior (see comments in inflate.c
- * for why this doesn't happen at present with zlib 1.2.5).
- */
- ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+ buffer = png_ptr->save_buffer_ptr;
+ png_ptr->save_buffer_ptr = buffer+chunk_length;
+ png_ptr->save_buffer_size = save_size-chunk_length;
+ png_ptr->buffer_size -= chunk_length;
+ affirm(png_ptr->buffer_size >= 4);
+ }
- /* Check for any failure before proceeding. */
- if (ret != Z_OK && ret != Z_STREAM_END)
+ else
+ {
+ affirm(png_ptr->current_buffer_size >= chunk_length+4);
+ buffer = png_ptr->current_buffer_ptr;
+ png_ptr->current_buffer_ptr = buffer+chunk_length;
+ png_ptr->current_buffer_size -= chunk_length;
+ png_ptr->buffer_size -= chunk_length;
+ }
+
+ /* Now check the CRC, before attempting the unknown handling. */
+ png_calculate_crc(png_ptr, buffer, chunk_length);
+ png_crc_finish(png_ptr, 0);
+# ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ png_handle_unknown(png_ptr, info_ptr, buffer);
+# else /* !READ_UNKNOWN_CHUNKS */
+ PNG_UNUSED(info_ptr)
+# endif /* !READ_UNKNOWN_CHUNKS */
+ png_ptr->process_mode = png_read_chunk_header;
+}
+
+static void
+png_push_have_row(png_structrp png_ptr, png_bytep row)
+{
+ if (png_ptr->row_fn != NULL)
+ {
+ png_uint_32 row_number = png_ptr->row_number;
+ png_byte pass = png_ptr->pass;
+
+ if (png_ptr->interlaced)
{
- /* Terminate the decompression. */
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- png_ptr->zowner = 0;
-
- /* This may be a truncated stream (missing or
- * damaged end code). Treat that as a warning.
+ /* If the row de-interlace is not being done by PNG this wacky API
+ * delivers the row number in the pass to the caller. We know that
+ * if we get here the row exists, so the number is just one less than
+ * the height of an interlaced image with just the rows up to this
+ * one:
*/
- if (png_ptr->row_number >= png_ptr->num_rows ||
- png_ptr->pass > 6)
- png_warning(png_ptr, "Truncated compressed data in IDAT");
-
- else
- png_error(png_ptr, "Decompression error in IDAT");
-
- /* Skip the check on unprocessed input */
- return;
- }
-
- /* Did inflate output any data? */
- if (png_ptr->zstream.next_out != png_ptr->row_buf)
- {
- /* Is this unexpected data after the last row?
- * If it is, artificially terminate the LZ output
- * here.
- */
- if (png_ptr->row_number >= png_ptr->num_rows ||
- png_ptr->pass > 6)
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (!png_ptr->do_interlace)
+# endif /* READ_INTERLACING */
{
- /* Extra data. */
- png_warning(png_ptr, "Extra compressed data in IDAT");
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- png_ptr->zowner = 0;
+ affirm(PNG_ROW_IN_INTERLACE_PASS(row_number, pass) && row != NULL);
+ row_number = PNG_PASS_ROWS(row_number+1, pass);
+ affirm(row_number > 0);
+ --row_number;
+ }
+ }
- /* Do no more processing; skip the unprocessed
- * input check below.
+ (*(png_ptr->row_fn))(png_ptr, row, row_number, pass);
+ }
+}
+
+static void
+png_push_read_sync_zstream(png_structp png_ptr, png_bytep *bufferp,
+ size_t *buffer_lengthp)
+ /* Synchronize the png_struct progressive read buffer
+ * {*bufferp,*buffer_lengthp} with png_struct::zstream.next_in, on the
+ * assumption that the zstream had previously been set up with *bufferp.
+ */
+{
+ png_bytep original_start = *bufferp;
+ png_alloc_size_t bytes_consumed = png_ptr->zstream.next_in - original_start;
+
+ affirm(buffer_lengthp != NULL);
+
+ /* Calculate the CRC for the consumed data: */
+ png_calculate_crc(png_ptr, original_start, bytes_consumed);
+
+ /* Update the buffer pointers and the various lengths: */
+ *bufferp = original_start + bytes_consumed; /* == png_ptr->zstream.next_in */
+
+ affirm(bytes_consumed <= *buffer_lengthp);
+ *buffer_lengthp -= (size_t)/*SAFE*/bytes_consumed;
+
+ affirm(bytes_consumed <= png_ptr->chunk_length);
+ png_ptr->chunk_length -= (png_uint_32)/*SAFE*/bytes_consumed;
+
+ affirm(bytes_consumed <= png_ptr->buffer_size);
+ png_ptr->buffer_size -= (size_t)/*SAFE*/bytes_consumed;
+}
+
+static void
+png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp,
+ size_t *buffer_lengthp)
+ /* If the the *buffer_lengthp parameter is NULL there is no more input,
+ * png_struct::mode & PNG_AFTER_IDAT must be set at this point.
+ */
+{
+ png_alloc_size_t buffer_length;
+
+ if (buffer_lengthp != NULL)
+ buffer_length = *buffer_lengthp;
+
+ else /* end of IDAT */
+ {
+ /* SECURITY: if this affirm fails the code would go into an infinite loop;
+ * see the handling of avail_in == 0 in png_inflate_IDAT.
+ */
+ affirm(png_ptr->mode & PNG_AFTER_IDAT);
+ buffer_length = 0;
+ }
+
+ /* This routine attempts to process all the data it has been given before
+ * returning, calling the row callback as required to handle the
+ * uncompressed results.
+ *
+ * If a pause happens during processing (png_ptr->buffer_size is set to 0)
+ * or the end of the chunk is encountered the routine may return without
+ * handling all the input data.
+ */
+ if (buffer_length > png_ptr->chunk_length)
+ {
+ buffer_length = png_ptr->chunk_length;
+
+ /* This works because the last part of a 'skip' is to read and check the
+ * CRC, then the process mode is set to png_read_chunk_header.
+ */
+ if (buffer_length == 0)
+ png_ptr->process_mode = png_read_chunk_skip;
+ }
+
+ /* It is possble for buffer_length to be zero at this point if the stream
+ * caontains a zero length IDAT. This is handled below.
+ */
+ png_ptr->zstream.next_in = *bufferp;
+
+ while (buffer_length > 0 || buffer_lengthp == NULL)
+ {
+ if (buffer_length >= ZLIB_IO_MAX)
+ {
+ png_ptr->zstream.avail_in = ZLIB_IO_MAX;
+ buffer_length -= ZLIB_IO_MAX;
+ }
+
+ else
+ {
+ png_ptr->zstream.avail_in = (uInt)/*SAFE*/buffer_length;
+ buffer_length = 0;
+ }
+
+ /* The last row may already have been processed.
+ *
+ * row_number is the *current* row number in the range 0..height-1. It is
+ * updated only by the call to png_read_process_IDAT that follows the call
+ * which returns something other than png_row_incomplete.
+ *
+ * At the end of the image that call must *NOT* be made; png_process_IDAT
+ * must not be called after the last row. png_struct::zstream_eod is set
+ * below to allow this condition to be detected.
+ *
+ * Note that png_read_process_IDAT handles errors in the LZ compressed
+ * data (i.e. the cases where png_struct::zstream_error is set) by filling
+ * the rows in with 0, which is a safe value, so keep calling it until we
+ * reach the end of the image.
+ */
+ if (!png_ptr->zstream_eod)
+ {
+ png_bytep row_buffer = NULL;
+ png_row_op row_op =
+ png_read_process_IDAT(png_ptr, NULL, NULL, 1/*save row*/);
+
+ if (row_op != png_row_incomplete)
+ {
+ /* Have a complete row, so check for end-of-image; do this here
+ * because interlaced images can end on earlier rows or passes but
+ * we keep calling png_read_process_IDAT until it updates row_number
+ * to the last row of the actual image:
*/
+ if (png_ptr->row_number+1 >= png_ptr->height &&
+ (!png_ptr->interlaced || png_ptr->pass == 6))
+ png_ptr->zstream_eod = 1; /* end of image */
+ }
+
+ switch (row_op)
+ {
+ case png_row_incomplete:
+ /* more IDAT data needed for row */
+ debug(png_ptr->zstream.avail_in == 0);
+
+ /* png_inflate_IDAT is supposed to handle this and recognize a
+ * call with 0 avail_in as end of stream:
+ */
+ affirm(buffer_lengthp != NULL);
+ continue;
+
+ case png_row_process:
+ /* If a row was obtained after the end of the IDAT this was done
+ * by fabricating data, ensure this is reported, else there is a
+ * security issue; normally libpng does a png_error in this
+ * case, even if the error is ignored png_struct::zstream_error
+ * should be set so somehow the error wasn't noticed!
+ */
+ affirm(buffer_lengthp != NULL || png_ptr->zstream_error);
+
+ /* png_struct::transformed_row contains a complete, transformed,
+ * row; this is processed in both 'sparkle' and 'block' mode.
+ */
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ row_buffer = png_ptr->transformed_row;
+ if (row_buffer == NULL)
+# endif /* TRANSFORM_MECH */
+ row_buffer = png_ptr->row_buffer;
+ break;
+
+ case png_row_repeat:
+ /* row not in this pass, but the existing row in
+ * png_struct::transformed_row may be used, this is only required
+ * if the 'block' or 'rectangle' mode of display is done and
+ * libpng is handling the de-interlace; when the app does it it
+ * only see the real rows.
+ */
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->do_interlace)
+ {
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ row_buffer = png_ptr->transformed_row;
+ if (row_buffer == NULL)
+# endif /* TRANSFORM_MECH */
+ row_buffer = png_ptr->row_buffer;
+ break;
+ }
+# endif /* READ_INTERLACING */
+ continue;
+
+ case png_row_skip:
+ /* row not in pass and no appropriate data; skip this row,
+ * nothing more need be done, except the read_row_fn. The use
+ * of 'NULL' to mean this row doesn't contribute to the output
+ * is historical and not documented;
+ */
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->do_interlace)
+ break;
+# endif /* READ_INTERLACING */
+ continue;
+
+ default:
+ impossible("not reached");
+ }
+
+ /* Here if there is a row to process. */
+
+ /* Now adjust the buffer pointers before calling png_push_have_row
+ * because the callback might call png_process_data_pause and that
+ * calls png_push_save_row. (Yes, this is insane; it was forced on
+ * libpng by writers of an external app that ignored the instructions
+ * not to fiddle with the insides of png_struct in version 1.4. It
+ * will probably be fixed here before 1.7.0 is released by removing
+ * the need for the save buffer entirely.)
+ */
+ if (buffer_lengthp != NULL)
+ png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp);
+
+ /* Process one row: */
+ png_push_have_row(png_ptr, row_buffer);
+
+ /* The buffer pointer and size may have changed at this point,
+ * so everything needs to be reloaded if we can continue reading.
+ */
+ if (buffer_lengthp != NULL) /* not at end of IDATs */
+ {
+ if (png_ptr->chunk_length == 0)
+ png_ptr->process_mode = png_read_chunk_skip;
+
+ /* If the buffer_size has been set to zero more input is required,
+ * this may be a 'pause', and if the specific input buffer being
+ * processed has been exhaused then more input is also required.
+ * Otherwise we can keep going, however the input buffer may have
+ * been changed by the app callback, so do a complete reload:
+ */
+ else if (png_ptr->buffer_size > 0 && *buffer_lengthp > 0)
+ png_push_read_process_IDAT(png_ptr, bufferp, buffer_lengthp);
+
return;
}
- /* Do we have a complete row? */
- if (png_ptr->zstream.avail_out == 0)
- png_push_process_row(png_ptr);
+ /* If we can't continue reading because there is no more IDAT data this
+ * may still be a pause.
+ */
+ if (png_ptr->buffer_size == 0)
+ return;
+
+ /* Else continue, with zero data: */
+ continue;
}
- /* And check for the end of the stream. */
- if (ret == Z_STREAM_END)
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
- }
+ affirm(png_ptr->zstream_eod);
- /* All the data should have been processed, if anything
- * is left at this point we have bytes of IDAT data
- * after the zlib end code.
- */
- if (png_ptr->zstream.avail_in > 0)
- png_warning(png_ptr, "Extra compression data in IDAT");
-}
-
-void /* PRIVATE */
-png_push_process_row(png_structrp png_ptr)
-{
- /* 1.5.6: row_info moved out of png_struct to a local here. */
- png_row_info row_info;
-
- row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
- row_info.color_type = png_ptr->color_type;
- row_info.bit_depth = png_ptr->bit_depth;
- row_info.channels = png_ptr->channels;
- row_info.pixel_depth = png_ptr->pixel_depth;
- row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
- if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
- {
- if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
- png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
- png_ptr->prev_row + 1, png_ptr->row_buf[0]);
- else
- png_error(png_ptr, "bad adaptive filter value");
- }
-
- /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
- * 1.5.6, while the buffer really is this big in current versions of libpng
- * it may not be in the future, so this was changed just to copy the
- * interlaced row count:
- */
- memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
- if (png_ptr->transformations)
- png_do_read_transformations(png_ptr, &row_info);
-#endif
-
- /* The transformed pixel depth should match the depth now in row_info. */
- if (png_ptr->transformed_pixel_depth == 0)
- {
- png_ptr->transformed_pixel_depth = row_info.pixel_depth;
- if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
- png_error(png_ptr, "progressive row overflow");
- }
-
- else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
- png_error(png_ptr, "internal progressive row size calculation error");
-
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* Blow up interlaced rows to full size */
- if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
- {
- if (png_ptr->pass < 6)
- png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
- png_ptr->transformations);
-
- switch (png_ptr->pass)
- {
- case 0:
- {
- int i;
- for (i = 0; i < 8 && png_ptr->pass == 0; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */
- }
-
- if (png_ptr->pass == 2) /* Pass 1 might be empty */
- {
- for (i = 0; i < 4 && png_ptr->pass == 2; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
-
- if (png_ptr->pass == 4 && png_ptr->height <= 4)
- {
- for (i = 0; i < 2 && png_ptr->pass == 4; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
-
- if (png_ptr->pass == 6 && png_ptr->height <= 4)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
-
- break;
- }
-
- case 1:
- {
- int i;
- for (i = 0; i < 8 && png_ptr->pass == 1; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-
- if (png_ptr->pass == 2) /* Skip top 4 generated rows */
- {
- for (i = 0; i < 4 && png_ptr->pass == 2; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
-
- break;
- }
-
- case 2:
- {
- int i;
-
- for (i = 0; i < 4 && png_ptr->pass == 2; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-
- for (i = 0; i < 4 && png_ptr->pass == 2; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
-
- if (png_ptr->pass == 4) /* Pass 3 might be empty */
- {
- for (i = 0; i < 2 && png_ptr->pass == 4; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
-
- break;
- }
-
- case 3:
- {
- int i;
-
- for (i = 0; i < 4 && png_ptr->pass == 3; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-
- if (png_ptr->pass == 4) /* Skip top two generated rows */
- {
- for (i = 0; i < 2 && png_ptr->pass == 4; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
-
- break;
- }
-
- case 4:
- {
- int i;
-
- for (i = 0; i < 2 && png_ptr->pass == 4; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-
- for (i = 0; i < 2 && png_ptr->pass == 4; i++)
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
-
- if (png_ptr->pass == 6) /* Pass 5 might be empty */
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
-
- break;
- }
-
- case 5:
- {
- int i;
-
- for (i = 0; i < 2 && png_ptr->pass == 5; i++)
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-
- if (png_ptr->pass == 6) /* Skip top generated row */
- {
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
-
- break;
- }
-
- default:
- case 6:
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
-
- if (png_ptr->pass != 6)
- break;
-
- png_push_have_row(png_ptr, NULL);
- png_read_push_finish_row(png_ptr);
- }
- }
- }
- else
-#endif
- {
- png_push_have_row(png_ptr, png_ptr->row_buf + 1);
- png_read_push_finish_row(png_ptr);
- }
-}
-
-void /* PRIVATE */
-png_read_push_finish_row(png_structrp png_ptr)
-{
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
-
- /* Start of interlace block */
- static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
-
- /* Offset to next interlace block */
- static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
-
- /* Start of interlace block in the y direction */
- static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
-
- /* Offset to next interlace block in the y direction */
- static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
-
- /* Height of interlace block. This is not currently used - if you need
- * it, uncomment it here and in png.h
- static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
- */
-#endif
-
- png_ptr->row_number++;
- if (png_ptr->row_number < png_ptr->num_rows)
- return;
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- if (png_ptr->interlaced)
- {
- png_ptr->row_number = 0;
- memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
-
- do
+ if (png_ptr->zowner == 0 || png_read_finish_IDAT(png_ptr))
{
- png_ptr->pass++;
- if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
- (png_ptr->pass == 3 && png_ptr->width < 3) ||
- (png_ptr->pass == 5 && png_ptr->width < 2))
- png_ptr->pass++;
+ /* The zlib stream has ended, there may still be input data in
+ * png_ptr->zstream.next_in, restore this.
+ */
+ debug(png_ptr->zowner == 0 && png_ptr->zstream_ended);
- if (png_ptr->pass > 7)
- png_ptr->pass--;
+ if (buffer_lengthp != NULL)
+ {
+ png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp);
- if (png_ptr->pass >= 7)
- break;
+ /* If the chunk_length is greater than 0 then there is extra data,
+ * report this once. Notice that for IDAT after the end of the
+ * stream we keep coming to this point and doing the skip.
+ */
+ if (png_ptr->chunk_length > 0)
+ {
+ if (!png_ptr->zstream_error)
+ {
+ png_chunk_benign_error(png_ptr,
+ "too much IDAT data (progressive read)");
+ png_ptr->zstream_error = 1;
+ }
+ }
- 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];
+ /* In any case CRC the chunk, skipping any unneeded data: */
+ png_ptr->process_mode = png_read_chunk_skip;
+ }
+ return;
+ }
- if (png_ptr->transformations & PNG_INTERLACE)
- break;
-
- 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];
-
- } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+ /* else more input is required */
+ /* NOTE: this test only fires on a small (less than 5 byte) IDAT chunk
+ * which just contains the LZ EOF and the Adler32 CRC.
+ */
+ affirm(png_ptr->zowner == png_IDAT && !png_ptr->zstream_ended);
}
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+ /* At this point all the input has been consumed, however the CRC has not
+ * been done and the three length fields in png_struct, *buffer_lengthp,
+ * buffer_size and chunk_length, all need updating.
+ */
+ png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp);
}
-void /* PRIVATE */
+static void
+png_push_read_IDAT(png_structrp png_ptr)
+{
+ if (png_ptr->save_buffer_size > 0)
+ {
+ png_push_read_process_IDAT(png_ptr, &png_ptr->save_buffer_ptr,
+ &png_ptr->save_buffer_size);
+
+ /* This is a slight optimization; normally when the process mode changes
+ * there will still be something in the buffer:
+ */
+ if (png_ptr->save_buffer_size > 0)
+ return;
+
+ /* Check for a change in process mode or an application pause before
+ * checking the current input buffer. This is only rarely reached.
+ */
+ if (png_ptr->process_mode != png_read_chunk_process_part ||
+ png_ptr->buffer_size == 0)
+ return;
+ }
+
+ if (png_ptr->current_buffer_size > 0)
+ png_push_read_process_IDAT(png_ptr, &png_ptr->current_buffer_ptr,
+ &png_ptr->current_buffer_size);
+}
+
+static void
+png_push_finish_IDAT(png_structrp png_ptr)
+ /* Called once when the first chunk after IDAT is seen. */
+{
+ /* All of the IDAT data has been processed, however the stream may have
+ * been truncated and the image rows may not all have been processed.
+ * Clean up here (this doesn't read anything.)
+ *
+ * 1.7.0: this attempts some measure of compatibility with the sequential
+ * API, if the IDAT is truncated and the resultant error reporting doesn't
+ * abort the read the image is filled in using zeros of pixel data. This
+ * actually happens inside png_inflate_IDAT (pngrutil.c) when called with
+ * z_stream::avail_in == 0.
+ */
+ while (png_ptr->zowner == png_IDAT)
+ {
+ png_byte b = 0, *pb = &b;
+
+ png_push_read_process_IDAT(png_ptr, &pb, NULL/*end of IDAT*/);
+
+ if (png_ptr->zowner == 0)
+ break;
+
+ if (png_ptr->buffer_size == 0) /* pause */
+ return;
+ }
+
+ png_ptr->process_mode = png_check_bits(png_ptr, png_ptr->process_mode >> 4,
+ 4);
+}
+
+static void
png_push_have_info(png_structrp png_ptr, png_inforp info_ptr)
{
if (png_ptr->info_fn != NULL)
(*(png_ptr->info_fn))(png_ptr, info_ptr);
}
-void /* PRIVATE */
+static void
png_push_have_end(png_structrp png_ptr, png_inforp info_ptr)
{
if (png_ptr->end_fn != NULL)
(*(png_ptr->end_fn))(png_ptr, info_ptr);
}
-void /* PRIVATE */
-png_push_have_row(png_structrp png_ptr, png_bytep row)
+static void
+png_push_read_chunk_header(png_structrp png_ptr, png_infop info_ptr)
{
- if (png_ptr->row_fn != NULL)
- (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
- (int)png_ptr->pass);
+ /* Called to read a new chunk header and work out how to handle the remainder
+ * of the data.
+ */
+ unsigned int mode; /* mode prior to the header */
+ png_byte chunk_header[8];
+
+ PNG_PUSH_SAVE_BUFFER_IF_LT(8)
+ png_push_fill_buffer(png_ptr, chunk_header, 8);
+ png_ptr->chunk_length = png_get_uint_31(png_ptr, chunk_header);
+ png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_header+4);
+ png_reset_crc(png_ptr, chunk_header+4);
+ png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+ png_check_chunk_length(png_ptr, png_ptr->chunk_length);
+ mode = png_ptr->mode;
+ png_ptr->process_mode = png_check_bits(png_ptr,
+ png_read_chunk+png_find_chunk_op(png_ptr), 4);
+
+ /* Is this the first IDAT chunk? */
+ if ((mode ^ png_ptr->mode) & PNG_HAVE_IDAT)
+ png_push_have_info(png_ptr, info_ptr);
+
+ /* Is it the chunk after the last IDAT chunk? */
+ else if (((mode ^ png_ptr->mode) & PNG_AFTER_IDAT) != 0)
+ png_ptr->process_mode = png_check_bits(png_ptr,
+ (png_ptr->process_mode << 4) + png_read_end_IDAT, 8);
}
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-void PNGAPI
-png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row,
- png_const_bytep new_row)
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+static void
+png_process_some_data(png_structrp png_ptr, png_inforp info_ptr)
{
if (png_ptr == NULL)
return;
- /* new_row is a flag here - if it is NULL then the app callback was called
- * from an empty row (see the calls to png_struct::row_fn below), otherwise
- * it must be png_ptr->row_buf+1
- */
- if (new_row != NULL)
- png_combine_row(png_ptr, old_row, 1/*display*/);
+ switch (png_ptr->process_mode & 0xf)
+ {
+ case png_read_signature:
+ png_push_read_signature(png_ptr, info_ptr);
+ return;
+
+ case png_read_chunk_header:
+ png_push_read_chunk_header(png_ptr, info_ptr);
+ return;
+
+ case png_read_chunk_skip:
+ png_push_crc_finish(png_ptr);
+ return;
+
+ case png_read_chunk_unknown:
+ PNG_PUSH_SAVE_BUFFER_IF_FULL
+ png_push_read_unknown(png_ptr, info_ptr);
+ return;
+
+ case png_read_chunk_process_all:
+ PNG_PUSH_SAVE_BUFFER_IF_FULL
+ png_handle_chunk(png_ptr, info_ptr);
+
+ if (png_ptr->mode & PNG_HAVE_IEND)
+ {
+ png_ptr->process_mode = png_read_done;
+ png_push_have_end(png_ptr, info_ptr);
+ png_ptr->buffer_size = 0;
+ }
+
+ else
+ png_ptr->process_mode = png_read_chunk_header;
+ return;
+
+ case png_read_chunk_process_part:
+ debug(png_ptr->chunk_name == png_IDAT &&
+ (png_ptr->mode & PNG_HAVE_IDAT) &&
+ !(png_ptr->mode & PNG_AFTER_IDAT));
+
+ if (png_ptr->zowner == 0 && !png_ptr->zstream_ended) /* first time */
+ png_read_start_IDAT(png_ptr);
+
+ png_push_read_IDAT(png_ptr);
+ return;
+
+ case png_read_end_IDAT:
+ png_push_finish_IDAT(png_ptr);
+ return;
+
+ case png_read_done:
+ png_app_error(png_ptr, "read beyond end of stream");
+ png_ptr->buffer_size = 0;
+ return;
+
+ default:
+ impossible("invalid process mode");
+ }
+}
+
+void PNGAPI
+png_process_data(png_structrp png_ptr, png_inforp info_ptr,
+ png_bytep buffer, png_size_t buffer_size)
+{
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+ while (png_ptr->buffer_size)
+ png_process_some_data(png_ptr, info_ptr);
}
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
void PNGAPI
png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr,
@@ -1289,4 +863,4 @@ png_get_progressive_ptr(png_const_structrp png_ptr)
return png_ptr->io_ptr;
}
-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+#endif /* PROGRESSIVE_READ */
diff --git a/ext/libpng17/pngpriv.h b/ext/libpng17/pngpriv.h
index 1156092683..15a3226d3d 100644
--- a/ext/libpng17/pngpriv.h
+++ b/ext/libpng17/pngpriv.h
@@ -1,13 +1,11 @@
/* pngpriv.h - private declarations for use inside libpng
*
- * For conditions of distribution and use, see copyright notice in png.h
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
- * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- *
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
@@ -125,94 +123,6 @@
#endif /* VERSION_INFO_ONLY */
-/* Compile time options.
- * =====================
- * In a multi-arch build the compiler may compile the code several times for the
- * same object module, producing different binaries for different architectures.
- * When this happens configure-time setting of the target host options cannot be
- * done and this interferes with the handling of the ARM NEON optimizations, and
- * possibly other similar optimizations. Put additional tests here; in general
- * this is needed when the same option can be changed at both compile time and
- * run time depending on the target OS (i.e. iOS vs Android.)
- *
- * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because
- * this is not possible with certain compilers (Oracle SUN OS CC), as a result
- * it is necessary to ensure that all extern functions that *might* be used
- * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON__
- * below is one example of this behavior because it is controlled by the
- * presence or not of -mfpu=neon on the GCC command line, it is possible to do
- * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely
- * do this.
- */
-#ifndef PNG_ARM_NEON_OPT
- /* ARM NEON optimizations are being controlled by the compiler settings,
- * typically the target FPU. If the FPU has been set to NEON (-mfpu=neon
- * with GCC) then the compiler will define __ARM_NEON__ and we can rely
- * unconditionally on NEON instructions not crashing, otherwise we must
- * disable use of NEON instructions.
- *
- * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they
- * can only be turned on automatically if that is supported too. If
- * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail
- * to compile with an appropriate #error if ALIGNED_MEMORY has been turned
- * off.
- */
-# if defined(__ARM_NEON__) && defined(PNG_ALIGNED_MEMORY_SUPPORTED)
-# define PNG_ARM_NEON_OPT 2
-# else
-# define PNG_ARM_NEON_OPT 0
-# endif
-#endif
-
-#if PNG_ARM_NEON_OPT > 0
- /* NEON optimizations are to be at least considered by libpng, so enable the
- * callbacks to do this.
- */
-# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon
-
- /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used
- * if possible - if __ARM_NEON__ is set and the compiler version is not known
- * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can
- * be:
- *
- * 1 The intrinsics code (the default with __ARM_NEON__)
- * 2 The hand coded assembler (the default without __ARM_NEON__)
- *
- * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however
- * this is *NOT* supported and may cease to work even after a minor revision
- * to libpng. It *is* valid to do this for testing purposes, e.g. speed
- * testing or a new compiler, but the results should be communicated to the
- * libpng implementation list for incorporation in the next minor release.
- */
-# ifndef PNG_ARM_NEON_IMPLEMENTATION
-# ifdef __ARM_NEON__
-# if defined(__clang__)
- /* At present it is unknown by the libpng developers which versions
- * of clang support the intrinsics, however some or perhaps all
- * versions do not work with the assembler so this may be
- * irrelevant, so just use the default (do nothing here.)
- */
-# elif defined(__GNUC__)
- /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to
- * work, so if this *is* GCC, or G++, look for a version >4.5
- */
-# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
-# define PNG_ARM_NEON_IMPLEMENTATION 2
-# endif /* no GNUC support */
-# endif /* __GNUC__ */
-# else /* !defined __ARM_NEON__ */
- /* The 'intrinsics' code simply won't compile without this -mfpu=neon:
- */
-# define PNG_ARM_NEON_IMPLEMENTATION 2
-# endif /* __ARM_NEON__ */
-# endif /* !defined PNG_ARM_NEON_IMPLEMENTATION */
-
-# ifndef PNG_ARM_NEON_IMPLEMENTATION
- /* Use the intrinsics code by default. */
-# define PNG_ARM_NEON_IMPLEMENTATION 1
-# endif
-#endif /* PNG_ARM_NEON_OPT > 0 */
-
/* Is this a build of a DLL where compilation of the object modules requires
* different preprocessor settings to those required for a simple library? If
* so PNG_BUILD_DLL must be set.
@@ -334,59 +244,191 @@
# define PNG_DLL_EXPORT
#endif
-/* asserts are turned off in release code, but are in even in release candidates
- * because often system builders only check future libpng releases when a
- * release candidate is available.
+/* This is a global switch to set the compilation for an installed system
+ * (a release build). It can be set for testing debug builds to ensure that
+ * they will compile when the build type is switched to RC or STABLE, the
+ * default is just to use PNG_LIBPNG_BUILD_BASE_TYPE. Set this in CPPFLAGS
+ * with either:
+ *
+ * -DPNG_RELEASE_BUILD Turns on the release compile path
+ * -DPNG_RELEASE_BUILD=0 Turns it off
+ * or in your pngusr.h with
+ * #define PNG_RELEASE_BUILD=1 Turns on the release compile path
+ * #define PNG_RELEASE_BUILD=0 Turns it off
*/
-#if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE
-# define NDEBUG
+#ifndef PNG_RELEASE_BUILD
+# define PNG_RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC)
#endif
-#ifndef PNG_VERSION_INFO_ONLY
-# include
+
+/* General purpose macros avoid the need to put #if PNG_RELEASE_BUILD
+ * macro blocks around function declarations and definitions when the
+ * parameter number varies. Using these results in slightly cleaner code.
+ */
+#if PNG_RELEASE_BUILD
+# define only_rel(text) text
+# define only_deb(text)
+# define param_rel(param) param,
+# define param_deb(param)
+#else
+# define only_rel(text)
+# define only_deb(text) text
+# define param_rel(param)
+# define param_deb(param) param,
#endif
+/* The affirm mechanism results in a minimal png_error() in released versions
+ * ('STABLE' versions) and a more descriptive PNG_ABORT in all other cases,
+ * when the "condition" is false (zero). If "condition" is true (nonzero),
+ * then the affirm mechanism does nothing.
+ *
+ * The PNG_RELEASE_BUILD macro, defined above, controls the behavior of
+ * 'affirm': if set to 1 affirm will call png_error (or png_err) rather than
+ * abort. The png_error text is the minimal (file location) text in this case,
+ * if it is produced. This flag indicates a STABLE (or RC) build.
+ *
+ * The macros rely on the naming convention throughout this code - png_ptr
+ * exists and is of type png_const_structrp or a compatible type - and the
+ * presence in each file of a uniquely defined macro PNG_SRC_FILE; a number
+ * indicating which file this is (this is to save space in released versions).
+ *
+ * 'affirm' is intended to look like the ANSI-C macro; note that
+ * this macro can coexist with the assert macro if is
+ * included.
+ *
+ * PNG_SRC_LINE is the position of the affirm macro. There are currently 15
+ * main source files (4 bits) and the biggest (pngrtran.c) has more than 4095
+ * lines (12 bits). However, to ensure the number will fit into 16-bits in the
+ * future and to allow hardware files to use affirm, the encoding is a bit-wise
+ * encoding based on the current number of lines.
+ *
+ * 'debug' is a version of 'affirm' that is completely removed from RELEASE
+ * builds. This is used when either an unexpected condition is completely
+ * handled or when it can't be handled even by png_error, for example after a
+ * memory overwrite.
+ *
+ * UNTESTED is used to mark code that has not been tested; it causes an assert
+ * if the code is executed and (therefore) tested. UNTESTED should not remain
+ * in release candidate code.
+ *
+ * PNG_AFFIRM_TEXT is set to 1 if affirm text should be produced, either
+ * the minimal text or, if PNG_RELEASE_BUILD is 0, the more verbose text
+ * including the 'condition' string. This value depends on whether the
+ * build supports an appropriate way of outputting the message.
+ *
+ * Note that PNG_AFFIRM_TEXT is not configurable but is worked out here: this
+ * is just the affirm code; there's no reason to allow configuration of this
+ * option.
+ */
+#if PNG_RELEASE_BUILD ?\
+ (defined PNG_ERROR_TEXT_SUPPORTED) :\
+ (defined PNG_WARNINGS_SUPPORTED) || (defined PNG_CONSOLE_IO_SUPPORTED)
+# define PNG_AFFIRM_TEXT 1
+#else
+# define PNG_AFFIRM_TEXT 0
+#endif /* PNG_AFFIRM_TEXT definition */
+
+#define PNG_SRC_LINE (PNG_SRC_FILE + __LINE__)
+
+/* png_affirmpp and png_impossiblepp are macros to make the correct call to the
+ * png_affirm function; these macros do not assume that the png_structp is
+ * called png_ptr.
+ */
+#if PNG_RELEASE_BUILD
+# define png_affirmpp(pp, cond)\
+ do\
+ if (!(cond)) png_affirm(pp, PNG_SRC_LINE);\
+ while (0)
+# define png_affirmexp(pp, cond)\
+ ((cond) ? (void)0 : png_affirm(pp, PNG_SRC_LINE))
+# define png_handled(pp, m) ((void)0)
+# define png_impossiblepp(pp, reason) png_affirm(pp, PNG_SRC_LINE)
+
+# define debug(cond) do {} while (0)
+# define debug_handled(cond) do {} while (0)
+# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
+ /* Make sure there are no 'UNTESTED' macros in released code: */
+# define UNTESTED libpng untested code
+# endif
+# define NOT_REACHED do {} while (0)
+#else
+# define png_affirmpp(pp, cond)\
+ do\
+ if (!(cond)) png_affirm(pp, #cond, PNG_SRC_LINE);\
+ while (0)
+# define png_affirmexp(pp, cond)\
+ ((cond) ? (void)0 : png_affirm(pp, #cond, PNG_SRC_LINE))
+# define png_handled(pp, m) (png_handled_affirm((pp), (m), PNG_SRC_LINE))
+# define png_impossiblepp(pp, reason) png_affirm(pp, reason, PNG_SRC_LINE)
+
+# define debug(cond) png_affirmpp(png_ptr, cond)
+# define debug_handled(cond)\
+ do\
+ if (!(cond)) png_handled(png_ptr, #cond);\
+ while (0)
+# define UNTESTED png_affirm(png_ptr, "untested code", PNG_SRC_LINE);
+# define NOT_REACHED png_affirm(png_ptr, "NOT REACHED", PNG_SRC_LINE)
+#endif
+
+#define affirm(cond) png_affirmpp(png_ptr, cond)
+#define affirmexp(cond) png_affirmexp(png_ptr, cond)
+#define handled(m) png_handled(png_ptr, (m))
+#define impossible(cond) png_impossiblepp(png_ptr, cond)
+#define implies(a, b) debug(!(a) || (b))
+
+/* The defines for PNG_SRC_FILE: */
+#define PNG_SRC_FILE_(f,lines) PNG_SRC_FILE_ ## f + lines
+
+#define PNG_SRC_FILE_png 0
+#define PNG_SRC_FILE_pngerror (PNG_SRC_FILE_png +8192)
+#define PNG_SRC_FILE_pngget (PNG_SRC_FILE_pngerror +2048)
+#define PNG_SRC_FILE_pngmem (PNG_SRC_FILE_pngget +2048)
+#define PNG_SRC_FILE_pngpread (PNG_SRC_FILE_pngmem +1024)
+#define PNG_SRC_FILE_pngread (PNG_SRC_FILE_pngpread +2048)
+#define PNG_SRC_FILE_pngrio (PNG_SRC_FILE_pngread +8192)
+#define PNG_SRC_FILE_pngrtran (PNG_SRC_FILE_pngrio +1024)
+#define PNG_SRC_FILE_pngrutil (PNG_SRC_FILE_pngrtran +8192)
+#define PNG_SRC_FILE_pngset (PNG_SRC_FILE_pngrutil +8192)
+#define PNG_SRC_FILE_pngtrans (PNG_SRC_FILE_pngset +2048)
+#define PNG_SRC_FILE_pngwio (PNG_SRC_FILE_pngtrans +4096)
+#define PNG_SRC_FILE_pngwrite (PNG_SRC_FILE_pngwio +1024)
+#define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096)
+#define PNG_SRC_FILE_pngwutil (PNG_SRC_FILE_pngwtran +1024)
+
+#define PNG_SRC_FILE_arm_arm_init (PNG_SRC_FILE_pngwutil +8192)
+#define PNG_SRC_FILE_arm_filter_neon_intrinsics\
+ (PNG_SRC_FILE_arm_arm_init +1024)
+
+/* Add new files by changing the following line: */
+#define PNG_SRC_FILE_LAST (PNG_SRC_FILE_arm_filter_neon_intrinsics +1024)
+
+/* The following #define must list the files in exactly the same order as
+ * the above.
+ */
+#define PNG_FILES\
+ PNG_apply(png)\
+ PNG_apply(pngerror)\
+ PNG_apply(pngget)\
+ PNG_apply(pngmem)\
+ PNG_apply(pngpread)\
+ PNG_apply(pngread)\
+ PNG_apply(pngrio)\
+ PNG_apply(pngrtran)\
+ PNG_apply(pngrutil)\
+ PNG_apply(pngset)\
+ PNG_apply(pngtrans)\
+ PNG_apply(pngwio)\
+ PNG_apply(pngwrite)\
+ PNG_apply(pngwtran)\
+ PNG_apply(pngwutil)\
+ PNG_apply(arm_arm_init)\
+ PNG_apply(arm_filter_neon_intrinsics)\
+ PNG_end
+
/* SECURITY and SAFETY:
*
- * By default libpng is built without any internal limits on image size,
- * individual heap (png_malloc) allocations or the total amount of memory used.
- * If PNG_SAFE_LIMITS_SUPPORTED is defined, however, the limits below are used
- * (unless individually overridden). These limits are believed to be fairly
- * safe, but builders of secure systems should verify the values against the
- * real system capabilities.
- */
-#ifdef PNG_SAFE_LIMITS_SUPPORTED
- /* 'safe' limits */
-# ifndef PNG_USER_WIDTH_MAX
-# define PNG_USER_WIDTH_MAX 1000000
-# endif
-# ifndef PNG_USER_HEIGHT_MAX
-# define PNG_USER_HEIGHT_MAX 1000000
-# endif
-# ifndef PNG_USER_CHUNK_CACHE_MAX
-# define PNG_USER_CHUNK_CACHE_MAX 128
-# endif
-# ifndef PNG_USER_CHUNK_MALLOC_MAX
-# define PNG_USER_CHUNK_MALLOC_MAX 8000000
-# endif
-#else
- /* values for no limits */
-# ifndef PNG_USER_WIDTH_MAX
-# define PNG_USER_WIDTH_MAX 0x7fffffff
-# endif
-# ifndef PNG_USER_HEIGHT_MAX
-# define PNG_USER_HEIGHT_MAX 0x7fffffff
-# endif
-# ifndef PNG_USER_CHUNK_CACHE_MAX
-# define PNG_USER_CHUNK_CACHE_MAX 0
-# endif
-# ifndef PNG_USER_CHUNK_MALLOC_MAX
-# define PNG_USER_CHUNK_MALLOC_MAX 0
-# endif
-#endif
-
-/* Moved to pngpriv.h at libpng-1.5.0 */
-/* NOTE: some of these may have been used in external applications as
- * these definitions were exposed in pngconf.h prior to 1.5.
+ * libpng is built with support for internal limits on image dimensions and
+ * memory usage. These are documented in scripts/pnglibconf.dfa of the
+ * source and recorded in the machine generated header file pnglibconf.h.
*/
/* If you are running on a machine where you cannot allocate more
@@ -414,12 +456,17 @@
# define PNG_UNUSED(param) (void)param;
#endif
+/* This is a convenience for parameters which are not used in release
+ * builds.
+ */
+#define PNG_UNUSEDRC(param) only_rel(PNG_UNUSED(param))
+
/* Just a little check that someone hasn't tried to define something
* contradictory.
*/
-#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
+#if (PNG_ZBUF_SIZE > 32768) && defined(PNG_MAX_MALLOC_64K)
# undef PNG_ZBUF_SIZE
-# define PNG_ZBUF_SIZE 65536L
+# define PNG_ZBUF_SIZE 32768
#endif
/* If warnings or errors are turned off the code is disabled or redirected here.
@@ -446,6 +493,7 @@
*/
#ifdef __cplusplus
# define png_voidcast(type, value) static_cast(value)
+# define png_upcast(type, value) static_cast(value)
# define png_constcast(type, value) const_cast(value)
# define png_aligncast(type, value) \
static_cast(static_cast(value))
@@ -453,6 +501,7 @@
static_cast(static_cast(value))
#else
# define png_voidcast(type, value) (value)
+# define png_upcast(type, value) ((type)(value))
# define png_constcast(type, value) ((type)(value))
# define png_aligncast(type, value) ((void*)(value))
# define png_aligncastconst(type, value) ((const void*)(value))
@@ -513,6 +562,47 @@
# define png_isaligned(ptr, type) 0
#endif
+/* Buffer alignment control. These #defines control how the buffers used during
+ * read are aligned and how big they are.
+ */
+#ifndef PNG_ROW_BUFFER_ALIGN_TYPE
+ /* The absolute minimum alignment for a row buffer is that required for
+ * png_uint_32 direct access. The #define is of a legal C type that can be
+ * used as the type in the definition of the first member of a C union; give
+ * a typedef name if in doubt.
+ */
+# define PNG_ROW_BUFFER_ALIGN_TYPE png_uint_32
+#endif /* !ROW_BUFFER_ALIGN_TYPE */
+#ifndef PNG_ROW_BUFFER_BYTE_ALIGN
+ /* This is the minimum size in bytes of the buffer used while processing
+ * parts of row. Except at the end of the row pixels will always be
+ * processed in blocks such that the block size is a multiple of this number
+ */
+# define PNG_ROW_BUFFER_BYTE_ALIGN\
+ ((unsigned int)/*SAFE*/(sizeof (PNG_ROW_BUFFER_ALIGN_TYPE)))
+#endif /* !ROW_BUFFER_BYTE_ALIGN */
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+# define PNG_MAX_PIXEL_BYTES 16U /* 4x32-bit channels */
+#else /* !READ_USER_TRANSFORM */
+# define PNG_MAX_PIXEL_BYTES 8U /* 4x16-bit channels */
+#endif /* !READ_USER_TRANSFORM */
+/* PNG_ROW_BUFFER_SIZE is a compile time constant for the size of the row
+ * buffer. The minimum size of 2048 bytes is intended to allow the buffer to
+ * hold a complete 256 entry color map of 64-bit (8-byte) pixels. This is a
+ * requirement at some points of the colormap handling code.
+ *
+ * The maximum size is intended to allow (unsigned int) indexing of the buffer,
+ * it only affects systems with a 16-bit unsigned value where it limits the
+ * maximum to 4096 bytes.
+ */
+#define PNG_MIN_ROW_BUFFER_SIZE\
+ (PNG_MAX_PIXEL_BYTES * PNG_ROW_BUFFER_BYTE_ALIGN * 8U)
+#define PNG_MAX_ROW_BUFFER_SIZE ((UINT_MAX / 16U) + 1U)
+#ifndef PNG_ROW_BUFFER_SIZE
+# define PNG_ROW_BUFFER_SIZE\
+ (PNG_MIN_ROW_BUFFER_SIZE < 2048U ? 2048U : PNG_MIN_ROW_BUFFER_SIZE)
+#endif /* ROW_BUFFER_SIZE */
+
/* End of memory model/platform independent support */
/* End of 1.5.0beta36 move from pngconf.h */
@@ -525,117 +615,19 @@
* are defined in png.h because they need to be visible to applications
* that call png_set_unknown_chunk().
*/
-/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */
-/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */
-#define PNG_HAVE_IDAT 0x04
-/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */
-#define PNG_HAVE_IEND 0x10
- /* 0x20 (unused) */
- /* 0x40 (unused) */
- /* 0x80 (unused) */
-#define PNG_HAVE_CHUNK_HEADER 0x100
-#define PNG_WROTE_tIME 0x200
-#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
- /* 0x800 (unused) */
-#define PNG_HAVE_PNG_SIGNATURE 0x1000
-#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
- /* 0x4000 (unused) */
-#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */
-
-/* Flags for the transformations the PNG library does on the image data */
-#define PNG_BGR 0x0001
-#define PNG_INTERLACE 0x0002
-#define PNG_PACK 0x0004
-#define PNG_SHIFT 0x0008
-#define PNG_SWAP_BYTES 0x0010
-#define PNG_INVERT_MONO 0x0020
-#define PNG_QUANTIZE 0x0040
-#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */
- /* 0x0100 unused */
-#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */
-#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */
-#define PNG_RGBA 0x0800
-#define PNG_EXPAND 0x1000
-#define PNG_GAMMA 0x2000
-#define PNG_GRAY_TO_RGB 0x4000
-#define PNG_FILLER 0x8000
-#define PNG_PACKSWAP 0x10000
-#define PNG_SWAP_ALPHA 0x20000
-#define PNG_STRIP_ALPHA 0x40000
-#define PNG_INVERT_ALPHA 0x80000
-#define PNG_USER_TRANSFORM 0x100000
-#define PNG_RGB_TO_GRAY_ERR 0x200000
-#define PNG_RGB_TO_GRAY_WARN 0x400000
-#define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */
-#define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */
-#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */
-#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */
-#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */
- /* 0x8000000 unused */
- /* 0x10000000 unused */
- /* 0x20000000 unused */
- /* 0x40000000 unused */
-/* Flags for png_create_struct */
-#define PNG_STRUCT_PNG 0x0001
-#define PNG_STRUCT_INFO 0x0002
-
-/* Scaling factor for filter heuristic weighting calculations */
-#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))
-#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))
-
-/* Flags for the png_ptr->flags rather than declaring a byte for each one */
-#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001
-#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */
- /* 0x0004 unused */
-#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */
- /* 0x0010 unused */
- /* 0x0020 unused */
-#define PNG_FLAG_ROW_INIT 0x0040
-#define PNG_FLAG_FILLER_AFTER 0x0080
-#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100
-#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200
-#define PNG_FLAG_CRC_CRITICAL_USE 0x0400
-#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800
-#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */
-#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */
-#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */
-/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 */
-/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 */
-#define PNG_FLAG_LIBRARY_MISMATCH 0x20000
-#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000
-#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000
-#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */
-#define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_BACKGROUND_IS_GRAY 0x800000
-#define PNG_FLAG_BACKGROUND_EXPAND 0x1000000
- /* 0x2000000 unused */
- /* 0x4000000 unused */
- /* 0x8000000 unused */
- /* 0x10000000 unused */
- /* 0x20000000 unused */
- /* 0x40000000 unused */
-
-#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
- PNG_FLAG_CRC_ANCILLARY_NOWARN)
-
-#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \
- PNG_FLAG_CRC_CRITICAL_IGNORE)
-
-#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \
- PNG_FLAG_CRC_CRITICAL_MASK)
-
-/* Save typing and make code easier to understand */
-
-#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
- abs((int)((c1).green) - (int)((c2).green)) + \
- abs((int)((c1).blue) - (int)((c2).blue)))
+/* #define PNG_HAVE_IHDR 0x01 (defined as (int) in png.h) */
+/* #define PNG_HAVE_PLTE 0x02 (defined as (int) in png.h) */
+#define PNG_HAVE_IDAT 0x04U
+/* #define PNG_AFTER_IDAT 0x08 (defined as (int) in png.h) */
+#define PNG_HAVE_IEND 0x10U
+#define PNG_HAVE_PNG_SIGNATURE 0x20U
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
/* See below for the definitions of the tables used in these macros */
-#define PNG_sRGB_FROM_LINEAR(linear) ((png_byte)((png_sRGB_base[(linear)>>15] +\
- ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8))
+#define PNG_sRGB_FROM_LINEAR(pp, linear) png_check_byte(pp,\
+ (png_sRGB_base[(linear)>>15] +\
+ ((((linear)&0x7fffU)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)
/* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB
* encoded value with maximum error 0.646365. Note that the input is not a
* 16-bit value; it has been multiplied by 255! */
@@ -649,14 +641,43 @@
* 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it
* only gives off-by-one errors and only for 0.5% (1 in 200) of the values.
*/
-#define PNG_DIV65535(v24) (((v24) + 32895) >> 16)
-#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255)
+#define PNG_DIV65535(v24) (((v24) + 32895U) >> 16)
+#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255U)
-/* Added to libpng-1.2.6 JB */
+/* Added to libpng-1.2.6 JB
+ * Modified in libpng-1.7.0 to avoid the intermediate calculation overflow
+ * when:
+ *
+ * pixel_bits == 4: any width over 0x3FFFFFFEU overflows
+ * pixel_bits == 2: any width over 0x7FFFFFFCU overflows
+ *
+ * In both these cases any width results in a rowbytes that fits in 32 bits.
+ * The problem arose in previous versions because the calculation used was
+ * simply ((width x pixel-bit-depth)+7)/8. At the cost of more calculations
+ * on pixel_depth this avoids the problem.
+ */
+#define PNG_SHIFTOF(pixel_bits/*<8*/) \
+ ( (pixel_bits) == 1 ? 3 : \
+ ( (pixel_bits) == 2 ? 2 : \
+ ( (pixel_bits) == 4 ? 1 : \
+ 0/*force bytes*/ ) ) )
+#define PNG_ADDOF(pixel_bits/*<8*/) ((1U<= 8 ? \
- ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \
- (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )
+ ((pixel_bits) >= 8 ? \
+ ((png_alloc_size_t)(width) * ((pixel_bits) >> 3)) : \
+ (((png_alloc_size_t)(width) + PNG_ADDOF(pixel_bits)) >> \
+ PNG_SHIFTOF(pixel_bits)) )
+
+/* This macros, added in 1.7.0, makes it easy to deduce the number of channels
+ * and therefore the pixel depth from the color type. The PNG specification
+ * numbers are used in preference to the png.h constants to make it more clear
+ * why the macro works.
+ */
+#define PNG_COLOR_TYPE_CHANNELS(ct)\
+ (((ct) & PNG_COLOR_MASK_PALETTE) ?\
+ 1U : 1U+((ct) & 2U/*COLOR*/)+(((ct)>>2)&1U/*ALPHA*/))
+#define PNG_CHANNELS(ps) PNG_COLOR_TYPE_CHANNELS((ps).color_type)
+#define PNG_PIXEL_DEPTH(ps) (PNG_CHANNELS(ps) * (ps).bit_depth)
/* PNG_OUT_OF_RANGE returns true if value is outside the range
* ideal-delta..ideal+delta. Each argument is evaluated twice.
@@ -666,6 +687,18 @@
#define PNG_OUT_OF_RANGE(value, ideal, delta) \
( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
+/* Handling of bit-field masks. Because the expression:
+ *
+ * bit_field & ~mask
+ *
+ * has implementation defined behavior in ANSI C-90 for many (int) values of
+ * 'mask' and because some of these are defined in png.h and passed in (int)
+ * parameters use of '~' has been expunged in libpng 1.7 and replaced by this
+ * macro, which is well defined in ANSI C-90 (there is a similar, 16-bit,
+ * version in pngstruct.h for the colorspace flags.)
+ */
+#define PNG_BIC_MASK(flags) (0xFFFFFFFFU - (flags))
+
/* Conversions between fixed and floating point, only defined if
* required (to make sure the code doesn't accidentally use float
* when it is supposedly disabled.)
@@ -730,7 +763,7 @@
* zlib version number and because this affects handling of certain broken
* PNG files the -I directives must match.
*
- * The most likely explanation is that you passed a -I in CFLAGS, this will
+ * The most likely explanation is that you passed a -I in CFLAGS. This will
* not work; all the preprocessor directories and in particular all the -I
* directives must be in CPPFLAGS.
*/
@@ -766,31 +799,152 @@ extern "C" {
*
* All of these functions must be declared with PNG_INTERNAL_FUNCTION.
*/
+/* Affirm handling */
+PNG_INTERNAL_FUNCTION(void, png_affirm,(png_const_structrp png_ptr,
+ param_deb(png_const_charp condition) unsigned int position), PNG_NORETURN);
+
+#if !PNG_RELEASE_BUILD
+PNG_INTERNAL_FUNCTION(void, png_handled_affirm,(png_const_structrp png_ptr,
+ png_const_charp message, unsigned int position), PNG_EMPTY);
+ /* This is not marked PNG_NORETURN because in PNG_RELEASE_BUILD it will
+ * disappear and control will pass through it.
+ */
+#endif /* !RELEASE_BUILD */
+
+/* Character/byte range checking. */
+/* GCC complains about assignments of an (int) expression to a (char) even when
+ * it can readily determine that the value is in range. This makes arithmetic
+ * on (char) or (png_byte) values tedious. The warning is not issued by
+ * default, but libpng coding rules require no warnings leading to excessive,
+ * ridiculous and dangerous expressions of the form:
+ *
+ * = (char)(expression & 0xff)
+ *
+ * They are dangerous because they hide the warning, which might actually be
+ * valid, and therefore merely enable introduction of undetected overflows when
+ * code is modified.
+ *
+ * The following macros exist to reliably detect any overflow in non-release
+ * builds. The theory here is that we really want to know about overflows, not
+ * merely hide a basically flawed compiler warning by throwing unnecessary casts
+ * into the code. The warnings disappear in RC builds so that the released
+ * (STABLE) version just assigns the value (with, possibly, a warning if someone
+ * turns on the -Wconversion GCC warning.)
+ *
+ * Doing it this way ensures that the code meets two very important aims:
+ *
+ * 1) Overflows are detected in pre-release tests; previously versions of libpng
+ * have been released that really did have overflows in the RGB calculations.
+ * 2) In release builds GCC specific operations, which may reduce the ability
+ * of other compilers and even GCC to optimize the code, are avoided.
+ *
+ * There is one important extra consequence for pre-release code; it is
+ * performing a lot of checks in pixel arithmetic that the release code won't
+ * perform. As a consequence a build time option, RANGE_CHECK, is provided
+ * to allow the checks to be turned off in pre-release when building for
+ * performance testing. This is a standard "_SUPPORTED" option except that it
+ * cannot be set in the system configuration (pnglibconf.h, pnglibconf.dfa).
+ *
+ * A separate macro PNG_BYTE() is provided to safely convert an unsigned value
+ * to the PNG byte range 0..255. This handles the fact that, technically,
+ * an ANSI-C (unsigned char), hence a (png_byte), may be able to store values
+ * outside this range. Note that if you are building on a system where this is
+ * true libpng is almost certainly going to produce errors; it has never been
+ * tested on such a system. For the moment pngconf.h ensures that this will
+ * not happen.
+ *
+ * PNG_UINT_16 does the same thing for a 16-bit value passed in an (int) or
+ * (png_uint_32) (where checking is not expected.)
+ */
+#if !PNG_RELEASE_BUILD
+# ifndef PNG_NO_RANGE_CHECK /* Turn off even in pre-release */
+# define PNG_RANGE_CHECK_SUPPORTED
+# endif
+#endif
+
+#ifdef PNG_RANGE_CHECK_SUPPORTED
+PNG_INTERNAL_FUNCTION(unsigned int, png_bit_affirm,(png_const_structrp png_ptr,
+ unsigned int position, unsigned int u, unsigned int bits), PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(char, png_char_affirm,(png_const_structrp png_ptr,
+ unsigned int position, int c), PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(png_byte, png_byte_affirm,(png_const_structrp png_ptr,
+ unsigned int position, int b), PNG_EMPTY);
+
+#if INT_MAX >= 65535
+PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr,
+ unsigned int position, int u), PNG_EMPTY);
+# define png_check_u16(pp, u) (png_u16_affirm((pp), PNG_SRC_LINE, (u)))
+#else
+ /* (int) cannot hold a (png_uint_16) so the above function just won't
+ * compile correctly, for the moment just do this:
+ */
+# define png_check_u16(pp, u) (u)
+#endif
+
+# define png_check_bits(pp, u, bits)\
+ (((1U<<(bits))-1) & png_bit_affirm((pp), PNG_SRC_LINE, (u), (bits)))
+# define png_check_char(pp, c) (png_char_affirm((pp), PNG_SRC_LINE, (c)))
+# define png_check_byte(pp, b) (png_byte_affirm((pp), PNG_SRC_LINE, (b)))
+# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU))
+# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU))
+#elif !(defined PNG_REMOVE_CASTS) /* && !RANGE_CHECK */
+# define png_check_bits(pp, u, bits) (((1U<<(bits))-1U) & (u))
+# define png_check_char(pp, c) ((char)(c))
+# define png_check_byte(pp, b) ((png_byte)(b))
+# define png_check_u16(pp, u) ((png_uint_16)(u))
+# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU))
+# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU))
+#else /* !RANGE_CHECK */
+ /* This is somewhat trust-me-it-works: if PNG_REMOVE_CASTS is defined then
+ * the casts, which might otherwise change the values, are completely
+ * removed. Use this to test your compiler to see if it makes *any*
+ * difference (code size or speed.) Currently NOT SUPPORTED.
+ *
+ * It also makes the PNG_BYTE and PNG_UINT_16 macros do nothing either
+ * NOTE: this seems safe at present but might lead to unexpected results
+ * if someone writes code to depend on the truncation.
+ */
+# define png_check_bits(pp, u, bits) (u)
+# define png_check_char(pp, c) (c)
+# define png_check_byte(pp, b) (b)
+# define png_check_u16(pp, u) (u)
+# define PNG_BYTE(b) (b)
+# define PNG_UINT_16(u) (u)
+#endif /* RANGE_CHECK */
+
+/* Safe calculation of a rowbytes value; does a png_error if the system limits
+ * are exceeded.
+ */
+PNG_INTERNAL_FUNCTION(png_alloc_size_t,png_calc_rowbytes,
+ (png_const_structrp png_ptr, unsigned int pixel_depth,
+ png_uint_32 row_width),PNG_EMPTY);
+
+/* Common code to calculate the maximum number of pixels to transform or filter
+ * at one time; controlled by PNG_ROW_BUFFER_SIZE above:
+ */
+PNG_INTERNAL_FUNCTION(unsigned int,png_max_pixel_block,
+ (png_const_structrp png_ptr),PNG_EMPTY);
+
+/* Copy the row in row_buffer; this is the non-interlaced copy used in both the
+ * read and write code. 'x_in_dest' specifies whether the 'x' applies to
+ * the destination (sp->dp[x], x_in_dest tru) or the source (sp[x]->dp,
+ * x_in_dest false).
+ */
+PNG_INTERNAL_FUNCTION(void, png_copy_row,(png_const_structrp png_ptr,
+ png_bytep dp, png_const_bytep sp, png_uint_32 x/*in INPUT*/,
+ png_uint_32 width/*of INPUT*/, unsigned int pixel_depth,
+ int clear/*clear the final byte*/, int x_in_dest),PNG_EMPTY);
/* Zlib support */
#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
-PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
+PNG_INTERNAL_FUNCTION(void, png_zstream_error,(z_stream *zstream, int ret),
PNG_EMPTY);
/* Used by the zlib handling functions to ensure that z_stream::msg is always
* set before they return.
*/
-#ifdef PNG_WRITE_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
- png_compression_bufferp *list),PNG_EMPTY);
- /* Free the buffer list used by the compressed write code. */
-#endif
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_alloc_filter_row_buffers,
- (png_structrp png_ptr, int filters),PNG_EMPTY);
- /* Allocate pixel row buffers to cache filtered rows while testing candidate
- * filters.
- * TODO: avoid this, only one spare row buffer (at most) is required, this
- * wastes a lot of memory for large images.
- */
-#endif
-
#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
!defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
(defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
@@ -802,12 +956,6 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,
double fp, png_const_charp text),PNG_EMPTY);
#endif
-/* Check the user version string for compatibility, returns false if the version
- * numbers aren't compatible.
- */
-PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
- png_const_charp user_png_ver),PNG_EMPTY);
-
/* Internal base allocator - no messages, NULL on failure to allocate. This
* does, however, call the application provided allocator and that could call
* png_error (although that would be a bug in the application implementation.)
@@ -857,69 +1005,60 @@ PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),
/* Function to free memory for zlib. PNGAPI is disallowed. */
PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);
-/* Next four functions are used internally as callbacks. PNGCBAPI is required
- * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to
- * PNGCBAPI at 1.5.0
+/* The next three functions are used by png_init_io to set the default
+ * implementations for reading or writing to a stdio (png_FILE_p) stream.
+ * They can't be static because in 1.7 png_init_io needs to reference them.
*/
-
+#ifdef PNG_STDIO_SUPPORTED
+# ifdef PNG_READ_SUPPORTED
PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
png_bytep data, png_size_t length),PNG_EMPTY);
+# endif /* READ */
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
- png_bytep buffer, png_size_t length),PNG_EMPTY);
-#endif
-
+# ifdef PNG_WRITE_SUPPORTED
PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
png_bytep data, png_size_t length),PNG_EMPTY);
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-# ifdef PNG_STDIO_SUPPORTED
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),
PNG_EMPTY);
-# endif
-#endif
+# endif /* WRITE_FLUSH */
+# endif /* WRITE */
+#endif /* STDIO */
-/* Reset the CRC variable */
-PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);
+/* Reset the CRC variable. The CRC is initialized with the chunk tag (4 bytes).
+ * NOTE: at present png_struct::chunk_name MUST be set before this as well so
+ * that png_struct::current_crc is initialized correctly!
+ */
+PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr,
+ png_const_bytep chunk_tag), PNG_EMPTY);
/* Write the "data" buffer to whatever output you are using */
PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
- png_const_bytep data, png_size_t length),PNG_EMPTY);
+ png_const_voidp data, png_size_t length),PNG_EMPTY);
/* Read and check the PNG file signature */
PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
png_inforp info_ptr),PNG_EMPTY);
-/* Read the chunk header (length + type name) */
-PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),
- PNG_EMPTY);
-
/* Read data from whatever input you are using into the "data" buffer */
-PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
+PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_voidp data,
png_size_t length),PNG_EMPTY);
/* Read bytes into buf, and update png_ptr->crc */
-PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
+PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_voidp buf,
png_uint_32 length),PNG_EMPTY);
/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
png_uint_32 skip),PNG_EMPTY);
-/* Read the CRC from the file and compare it to the libpng calculated CRC */
-PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY);
-
/* Calculate the CRC over a section of data. Note that we are only
* passing a maximum of 64K on systems that have this as a memory limit,
* since this is the maximum buffer size we can specify.
*/
PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
- png_const_bytep ptr, png_size_t length),PNG_EMPTY);
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
-#endif
+ png_const_voidp ptr, png_size_t length),PNG_EMPTY);
/* Write various chunks */
@@ -931,11 +1070,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
- png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
- png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
- PNG_EMPTY);
+ png_const_colorp palette, unsigned int num_pal),PNG_EMPTY);
PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
@@ -952,7 +1087,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,
#ifdef PNG_WRITE_cHRM_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
const png_xy *xy), PNG_EMPTY);
- /* The xy value must have been previously validated */
+ /* The xy value must have been previously validated */
#endif
#ifdef PNG_WRITE_sRGB_SUPPORTED
@@ -962,7 +1097,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,
#ifdef PNG_WRITE_iCCP_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
- png_const_charp name, png_const_bytep profile), PNG_EMPTY);
+ png_const_charp name, png_const_voidp profile), PNG_EMPTY);
/* The profile must have been previously validated for correctness, the
* length comes from the first four bytes. Only the base, deflate,
* compression is supported.
@@ -993,12 +1128,12 @@ PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
/* Chunks that have keywords */
#ifdef PNG_WRITE_tEXt_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
- png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
+ png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
#endif
#ifdef PNG_WRITE_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
- key, png_const_charp text, png_size_t text_len, int compression),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr,
+ png_const_charp key, png_const_charp text, int compression),PNG_EMPTY);
#endif
#ifdef PNG_WRITE_iTXt_SUPPORTED
@@ -1039,330 +1174,599 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);
#endif
-/* Called when finished processing a row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),
- PNG_EMPTY);
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_start_IDAT,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Do any required initialization before IDAT or row processing starts. */
-/* Internal use only. Called before first row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
- PNG_EMPTY);
-
-/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an
- * array of png_ptr->width pixels. If the image is not interlaced or this
- * is the final pass this just does a memcpy, otherwise the "display" flag
- * is used to determine whether to copy pixels that are not in the current pass.
+/* Choose the best filter to use and filter the row data then write it out. If
+ * WRITE_FILTERING is not supported this just writes the data out with a zero
+ * (NONE) filter byte.
*
- * Because 'png_do_read_interlace' (below) replicates pixels this allows this
- * function to achieve the documented 'blocky' appearance during interlaced read
- * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row'
- * are not changed if they are not in the current pass, when display is 0.
- *
- * 'display' must be 0 or 1, otherwise the memcpy will be done regardless.
- *
- * The API always reads from the png_struct row buffer and always assumes that
- * it is full width (png_do_read_interlace has already been called.)
- *
- * This function is only ever used to write to row buffers provided by the
- * caller of the relevant libpng API and the row must have already been
- * transformed by the read transformations.
- *
- * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed
- * bitmasks for use within the code, otherwise runtime generated masks are used.
- * The default is compile time masks.
+ * This may be called multiple times per row, but calls must be in 'x' order;
+ * first a call with x 0 to mark the start of the row and, at the end, one with
+ * PNG_ROW_END set (this can be done in the same function call if the whole row
+ * is passed.) The following flags are used internally to control pass
+ * filtering and deflate:
*/
-#ifndef PNG_USE_COMPILE_TIME_MASKS
-# define PNG_USE_COMPILE_TIME_MASKS 1
-#endif
-PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
- png_bytep row, int display),PNG_EMPTY);
+enum
+{
+ png_pass_last =0x1U, /* This is the last pass in the image */
+ png_pass_last_row =0x2U, /* This is the last row in a pass */
+ png_pass_first_row =0x4U, /* This is the first row in a pass */
+ png_row_end =0x8U, /* This is the last block in the row */
+ png_no_row_info =0x0U /* Placeholder */
-#ifdef PNG_READ_INTERLACING_SUPPORTED
-/* Expand an interlaced row: the 'row_info' describes the pass data that has
- * been read in and must correspond to the pixels in 'row', the pixels are
- * expanded (moved apart) in 'row' to match the final layout, when doing this
- * the pixels are *replicated* to the intervening space. This is essential for
- * the correct operation of png_combine_row, above.
- */
-PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,
- png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);
-#endif
-
-/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-/* Grab pixels out of a row for an interlaced pass */
-PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,
- png_bytep row, int pass),PNG_EMPTY);
-#endif
-
-/* Unfilter a row: check the filter value before calling this, there is no point
- * calling it for PNG_FILTER_VALUE_NONE.
- */
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
- png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
- row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-
-/* Choose the best filter to use and filter the row data */
-PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
- png_row_infop row_info),PNG_EMPTY);
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
- png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
- /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer
- * is NULL the function checks, instead, for the end of the stream. In this
- * case a benign error will be issued if the stream end is not found or if
- * extra data has to be consumed.
+ /* A useful macro; return true if this is the last block of the last row in
+ * the image.
*/
-PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
+# define PNG_IDAT_END(f) (((f) & ~png_pass_first_row) == \
+ (png_row_end+png_pass_last_row+png_pass_last))
+};
+PNG_INTERNAL_FUNCTION(void,png_write_png_data,(png_structrp png_ptr,
+ png_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x,
+ unsigned int width/*pixels*/, unsigned int row_info_flags),
PNG_EMPTY);
- /* This cleans up when the IDAT LZ stream does not end when the last image
- * byte is read; there is still some pending input.
+
+PNG_INTERNAL_FUNCTION(void,png_write_png_rows,(png_structrp png_ptr,
+ png_const_bytep *rows, png_uint_32 num_rows), PNG_EMPTY);
+ /* As above but rows[num_rows] of correctly (PNG) formated but unfiltered
+ * data are passed in. For an interlaced image the rows will be interlaced
+ * rows and therefore may be narrower than the image width.
+ *
+ * This function advances png_structp::pass and png_structp::row_number as
+ * required.
*/
-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
+/* Release memory used by the deflate mechanism */
+PNG_INTERNAL_FUNCTION(void, png_deflate_destroy, (png_structp png_ptr),
PNG_EMPTY);
- /* Finish a row while reading, dealing with interlacing passes, etc. */
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* WRITE */
-/* Initialize the row buffers, etc. */
-PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_transform_free,(png_const_structrp png_ptr,
+ png_transformp *list),PNG_EMPTY);
+ /* Free the entire transform list, from the given point on. the argument is
+ * set to NULL.
+ */
+
+PNG_INTERNAL_FUNCTION(void,png_init_transform_control,(
+ png_transform_controlp out, png_structp png_ptr),PNG_EMPTY);
+ /* Initialize a transform control for running the transform list forward (the
+ * read case, and write initialization, but the write case is called within
+ * pngtrans.c by the above function.)
+ */
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+PNG_INTERNAL_FUNCTION(unsigned int,png_run_this_transform_list_forwards,
+ (png_transform_controlp tc, png_transformp *start, png_transformp end),
+ PNG_EMPTY);
+ /* Used by the transform cache code to run a sub-list, from *start to the
+ * transform end.
+ */
+#endif /* READ_TRANSFORMS */
+
+#ifdef PNG_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(unsigned int,png_run_transform_list_forwards,
+ (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY);
+ /* Run the transform list in the forwards direction (from PNG format to
+ * memory format). The transform control must be initialized completely by
+ * the caller. This function takes account of transforms which delete
+ * themselves during the run; it must be used.
+ */
+#endif /* READ */
+
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_run_transform_list_backwards,
+ (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY);
+ /* Run the transform list in the backwards direction (from memory format to
+ * PNG format). The transform control must be initialized completely by
+ * the caller. This function takes account of transforms which delete
+ * themselves during the run; it must be used.
+ */
+#endif /* WRITE */
+
+PNG_INTERNAL_FUNCTION(png_transformp,png_add_transform,(png_structrp png_ptr,
+ size_t size, png_transform_fn fn, unsigned int order),PNG_EMPTY);
+ /* Add a transform, using the information in 'order' to control the position
+ * of the transform in the list, returning a pointer to the transform. The
+ * top 8 bits of 'order' control the position in the list. If a transform
+ * does not already exist in the list with the given value a new transform
+ * will be created and 'fn' and 'order' set. If there is a transform with
+ * that value 'fn' must match and 'order' will be updated by combining the
+ * new value in with a bitwise or (|). It is up to the function (fn) or the
+ * caller of png_add_transform to determine whether the combination is valid.
+ *
+ * 'size' is used when creating a new transform, it may be larger than
+ * (sizeof png_transform) if required to accomodate extra data.
+ *
+ * Prior to 1.7.0 transforms were executed in an order hard-wired into the
+ * code that executed the transform functions. This was summarized in the
+ * read case by the following comment from pngrtran.c
+ * (png_init_read_transformations), note that this has been marked up to
+ * indicate which PNG formats the transforms in the list apply to:
+ *
+ * *: applies to most formats
+ * A: only formats with alpha
+ * L: only low-bit-depth (less than 8 bits per component/pixel)
+ * H: only high-bit-depth (16-bits per component)
+ *
+ > From the code of png_do_read_transformations the order is:
+ *
+ * GGRR For example column: . no action
+ * AGG r acts on read
+ * BB w acts on write
+ * A B acts on both read and write
+ >
+ > r.r. 1) PNG_EXPAND (including PNG_EXPAND_tRNS)
+ > .r.r 2) PNG_STRIP_ALPHA (if no compose)
+ > ..rr 3) PNG_RGB_TO_GRAY
+ > rr.. 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY
+ > rrrr 5) PNG_COMPOSE
+ > rrrr 6) PNG_GAMMA
+ > .r.r 7) PNG_STRIP_ALPHA (if compose)
+ > .r.r 8) PNG_ENCODE_ALPHA
+ > rrrr 9) PNG_SCALE_16_TO_8
+ > rrrr 10) PNG_16_TO_8
+ > ..rr 11) PNG_QUANTIZE (converts to palette)
+ > rrrr 12) PNG_EXPAND_16
+ > rr.. 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY
+ > BB.. 14) PNG_INVERT_MONO
+ > .B.B 15) PNG_INVERT_ALPHA
+ > BBBB 16) PNG_SHIFT
+ *
+ * Note that transforms from this point on are used in 1.7.0 on palette
+ * indices as well; a png_set_pack request (for example) packs the palette
+ * index values if the output will be palettized and the grayscale values
+ * if it will not be (if the output is low-bit-grayscale, not palette.)
+ *
+ > B... 17) PNG_PACK
+ > ..BB 18) PNG_BGR
+ > B... 19) PNG_PACKSWAP
+ > rwrw 20) PNG_FILLER (includes PNG_ADD_ALPHA)
+ > .B.B 21) PNG_SWAP_ALPHA
+ > BBBB 22) PNG_SWAP_BYTES
+ > BBBB 23) PNG_USER_TRANSFORM [must be last]
+ *
+ * Finally, outside the set of transforms prior to 1.7.0, the libpng
+ * interlace handling required the pixels to be replicated to match the pixel
+ * spacing in the image row; the first part the pre-1.7.0 interlace support,
+ * this is still the case when reading, but for writing the interlace is now
+ * a transform:
+ *
+ > BBBB 24) png_do_{read,write}_interlace (interlaced images only).
+ *
+ * First transforms are grouped according to basic function using the top 3
+ * bits of the order code:
+ */
+# define PNG_TR_START 0x0000U /* initial ops on the PNG data */
+# define PNG_TR_ARITHMETIC 0x2000U /* arithmetic linear operations */
+# define PNG_TR_CHANNEL 0x4000U /* PNG conformant format changes */
+# define PNG_TR_QUANTIZE 0x6000U /* quantize and following operations */
+# define PNG_TR_ENCODING 0x8000U /* Row encoding transforms */
+# define PNG_TR_INTERLACE 0xA000U /* write interlace transform */
+ /*
+ * In libpng 1.7.0 the check on palette index values is moved to the start
+ * (of read, end of write, which is where it was before) immediately after
+ * the MNG filter handling
+ */
+# define PNG_TR_MNG_INTRAPIXEL (PNG_TR_START + 0x0100U)
+ /* Perform intra-pixel differencing (write) or un-differencing on read. */
+# define PNG_TR_CHECK_PALETTE (PNG_TR_START + 0x0200U)
+ /* Done before at the start on read, at the end on write to give a
+ * consistent postion:
+ *
+ * PNG_RWTR_CHECK_PALETTE PI W11: happens in pngwrite.c last
+ */
+# define PNG_TR_START_CACHE (PNG_TR_START + 0x0300U)
+ /* Not used on a transform; this is just a marker for the point at which
+ * palette or low-bit-depth caching can start on read. (The previous
+ * operations cannot be cached).
+ */
+# define PNG_TR_INIT_ALPHA (PNG_TR_START + 0x0400U)
+ /* This just handles alpha/tRNS initialization issues to resolve the
+ * inter-dependencies with tRNS expansion and background composition; it
+ * doesn't do anything itself, just sets flags and pushes transforms.
+ */
+ /*
+ * Prior to 1.7 the arithmetic operations interleaved with the RGB-to-gray
+ * and alpha strip byte level ops. This was done to reduce the amount of
+ * data processed, i.e. it was an optimization not a requirement. These
+ * operations were preceded by the 'expand' operations, which is the
+ * opposite; it was done to simplify the code and actually slows things down
+ * in the low bit depth gray case. The full list of operations after expand,
+ * in the 1.6 order, is:
+ *
+ * PNG_TR_STRIP_ALPHA png_do_strip_channel (sometimes)
+ * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray
+ * PNG_TR_GRAY_TO_RGB png_do_gray_to_rgb (sometimes)
+ * PNG_TR_COMPOSE png_do_compose
+ * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY)
+ * PNG_TR_STRIP_ALPHA png_do_strip_channel (other times)
+ * PNG_TR_ENCODE_ALPHA png_do_encode_alpha
+ *
+ * In 1.7 the operations are moved round somewhat, including moving alpha and
+ * 16-to-8 bit reduction later. This leaves the following operations:
+ *
+ * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray
+ * PNG_TR_COMPOSE png_do_compose
+ * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY)
+ * PNG_TR_ENCODE_ALPHA png_do_encode_alpha
+ *
+ * Prior to 1.7 some combinations of transforms would do gamma correction
+ * twice, the actual implementation in 1.7 is to use the following order and
+ * rely on the cache code to optimize gray 1,2,4,8 and (of course) palette.
+ */
+# define PNG_TR_COMPOSE (PNG_TR_ARITHMETIC + 0x0100U)
+ /* Handle background composition. This may need to push a gray-to-rgb
+ * transform if the background is RGB for gray input. This precedes RGB
+ * to gray convertion so that it can handle tRNS appropriately when the
+ * background is in the PNG encoding however, typically, the processing
+ * happens at PNG_TR_COMPOSE_ALPHA below.
+ *
+ * NOTE: this must be the first arithmetic transform because the code in
+ * png_init_background relies on png_transform_control::gamma being the
+ * original PNG gamma.
+ */
+# define PNG_TR_RGB_TO_GRAY (PNG_TR_ARITHMETIC + 0x0200U) /* to gray */
+ /* Convert any RGB input (8/16 bit depth, RGB, RGBA) to linear gray
+ * 16-bit. This happens first because it cannot be cached; the input data
+ * has 24 or 48 bits of uncorrelated data so the transform has to happen
+ * pixel-by-pixel. Internally the transform may maintain an 8 or 16-bit
+ * gamma correction table (to 16-bit linear) to speed things up.
+ *
+ * NOTE: this transform must follow PNG_TR_COMPOSE with no intervening
+ * transforms; see the code in png_init_background (pngrtran.c) which
+ * relies on this during PNG_TC_INIT_FORMAT.
+ */
+# define PNG_TR_COMPOSE_ALPHA (PNG_TR_ARITHMETIC + 0x0300U)
+ /* Compose alpha composition and tRNS handling when the background is a
+ * screen color. Pushed by PNG_TR_COMPOSE as required.
+ */
+# define PNG_TR_GAMMA_ENCODE (PNG_TR_ARITHMETIC + 0x1F00U) /* last */
+ /* Gamma encode the input. This encodes the gray or RGB channels to the
+ * required bit depth and either scales the alpha channel or encodes it as
+ * well, depending on the requested alpha encoding.
+ */
+ /*
+ * The 'expand' operations come after the arithmetic ones in libpng 1.7, this
+ * forces the arithmetic stuff to do the expand, but since arithmetic is (in
+ * 1.7) normally done in 16-bit linear this avoids spurious expands.
+ */
+# define PNG_TR_EXPAND (PNG_TR_CHANNEL + 0x0100U)
+ /* Includes:
+ *
+ * PNG_TR_EXPAND_PALETTE palette images only, includes tRNS
+ * PNG_TR_EXPAND_LBP_GRAY grayscale low-bit depth only
+ * PNG_TR_EXPAND_tRNS non-palette images only
+ */
+# define PNG_TR_SCALE_16_TO_8 (PNG_TR_CHANNEL + 0x0200U)
+ /* Comes after the expand and before the chop version; note that it works on
+ * the pixel values directly, so it is a linear transform on a non-linear
+ * value.
+ */
+ /*
+ * To handle transforms that affect the palette entries, not the palette
+ * indices in the row data, libpng 1.7 reorders some of the post-quantize
+ * transformations to put all the "PC" transforms ahead of all the "PI"
+ * transforms. The "PC" transforms that came after png_do_quantize in libpng
+ * 1.6 cannot be ordered to be before so they are included in the
+ * PNG_TR_QUANTIZE section. The PI transforms are all in PNG_TR_ENCODING,
+ * PNG_GRAY_TO_RGB is moved before PNG_TR_QUANTIZE to avoid the unpredictable
+ * behavior of png_set_quantize that otherwise arises.
+ *
+ * The transforms in the PNG_TR_QUANTIZE section are:
+ *
+ * PNG_TR_EXPAND_16 !P !W
+ * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel
+ * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel
+ * PNG_RWTR_SHIFT PC W6: read: down, write: scale up
+ * PNG_RWTR_BGR !P W9
+ * PNG_RWTR_FILLER !P W2: add on R, remove on W
+ * PNG_RWTR_SWAP_ALPHA !P W7
+ * PNG_RWTR_SWAP_16 !P W5
+ *
+ * The ones in PNG_TR_ENCODING are:
+ *
+ * PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack
+ * PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte
+ * PNG_RWTR_USER PI W1
+ */
+
+# define PNG_TR_CHANNEL_PREQ (PNG_TR_CHANNEL + 0x1F00U)
+ /* The channel swap transforms that must happen before PNG_TR_QUANTIZE:
+ *
+ * PNG_TR_STRIP_ALPHA
+ * PNG_TR_CHOP_16_TO_8
+ * PNG_TR_GRAY_TO_RGB
+ */
+# define PNG_TR_CHANNEL_POSTQ (PNG_TR_QUANTIZE + 0x0100U)
+ /* The post-quantize channel swap transforms:
+ *
+ * PNG_TR_EXPAND_16 !P !W
+ * PNG_RWTR_BGR !P W9
+ * PNG_RWTR_FILLER !P W2: (filler) add on R, remove on W
+ * PNG_RWTR_SWAP_ALPHA !P W7
+ * PNG_RWTR_SWAP_16 !P W5
+ *
+ * The 'CHANNEL' operation sets the transform_control channel_add flag for
+ * use below.
+ */
+# define PNG_TR_INVERT (PNG_TR_QUANTIZE + 0x0200U)
+ /* Invert MONO and ALPHA. If the channel_add flag is set in the transform
+ * control INVERT_ALPHA will not be done; the png_add_alpha/filler APIs
+ * happened after png_set_invert_alpha in earlier versions so the filler
+ * value had to include the invert.
+ *
+ * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel
+ * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel
+ */
+# define PNG_TR_SHIFT (PNG_TR_QUANTIZE + 0x0300U)
+ /* The channel shift, except that if the channel_add flag has been set the
+ * alpha channel is not shifted.
+ *
+ * PNG_RWTR_SHIFT PC W6: read: down, write: scale up
+ */
+# define PNG_TR_PACK (PNG_TR_ENCODING + 0x0200U)
+ /* PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack */
+# define PNG_TR_PIXEL_SWAP (PNG_TR_ENCODING + 0x0300U)
+ /* PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte */
+# define PNG_TR_USER (PNG_TR_ENCODING + 0x1F00U)
+ /* The user transform; must be last before the interlace handling because it
+ * does unpredictable things to the format.
+ *
+ * PNG_RWTR_USER PI W1
+ */
+
+PNG_INTERNAL_FUNCTION(png_transformp,png_push_transform,(png_structrp png_ptr,
+ size_t size, png_transform_fn fn, png_transformp *transform,
+ png_transform_controlp tc),PNG_EMPTY);
+ /* As png_add_transform except that the new transform is inserted ahead of
+ * the given transform (*transform). The new transform is returned, but it
+ * will also invariably be in *transform. If 'tc' is not NULL the transform
+ * callback will also be called; it needs to be called if this function is
+ * called while transforms are being run.
+ *
+ * 'fn' must not be NULL.
+ *
+ * The transform is inserted with the same 'order' as the passed in
+ * *transform, that transform and following transforms are moved up ('order'
+ * is incremented) as required to make space. Consequently, unlike with
+ * png_add_transform, the transform will always be new. To detect loops
+ * (*transform)->fn must not be the same as the passed in 'fn'.
+ */
+
+PNG_INTERNAL_FUNCTION(png_voidp,png_transform_cast_check,
+ (png_const_structp png_ptr, unsigned int src_line, png_transformp tr,
+ size_t size),PNG_EMPTY);
+ /* Given a pointer to a transform, 'tr' validate that the underlying derived
+ * class has size 'size' using the tr->size field and return the same
+ * pointer. If there is a size mismatch the function does an affirm using
+ * the given line number.
+ */
+#define png_transform_cast(type, pointer) png_voidcast(type*,\
+ png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer), sizeof (type)))
+ /* This takes a pointer to a transform and safely returns a pointer to a
+ * derived transform class (type); type must not have the pointer. It
+ * validates the 'size' field. Derived classes start with a png_transform
+ * as the first member called 'tr'.
+ */
+#endif /* TRANSFORM_MECH_SUPPORTED */
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Remove a transform from a list, moving the next transform down into
+ * *transform.
+ */
+PNG_INTERNAL_FUNCTION(void,png_remove_transform,(png_const_structp png_ptr,
+ png_transformp *transform),PNG_EMPTY);
+
+/* Initializer for read transforms that handles caching, palette update and
+ * palette expansion.
+ */
+PNG_INTERNAL_FUNCTION(unsigned int,png_read_init_transform_mech,
+ (png_structp png_ptr, png_transform_control *tc),PNG_EMPTY);
+
/* Optional call to update the users info structure */
PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
+ png_inforp info_ptr),PNG_EMPTY);
#endif
+/* APIs which do a tranform on both read and write but where the implementation
+ * is separate for each; the read and write init functions are in pngrtran.c or
+ * pngwtran.c, the API is in pngtrans.c
+ */
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_init_read_pack,(png_transformp *transform,
+ png_transform_controlp tc),PNG_EMPTY);
+#endif /* READ_PACK || READ_EXPAND */
+#ifdef PNG_WRITE_PACK_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_init_write_pack,(png_transformp *transform,
+ png_transform_controlp tc),PNG_EMPTY);
+#endif /* WRITE_PACK */
+
/* Shared transform functions, defined in pngtran.c */
#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,
- png_bytep row, int at_start),PNG_EMPTY);
-#endif
-
-#ifdef PNG_16BIT_SUPPORTED
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,
- png_bytep row),PNG_EMPTY);
-#endif
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
- defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,
- png_bytep row),PNG_EMPTY);
-#endif
+PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(
+ png_transform_controlp row_info, png_bytep row, int at_start),PNG_EMPTY);
+#endif /* FILLER */
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,
+PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_transform_controlp row_info,
png_bytep row),PNG_EMPTY);
-#endif
+#endif /* INVERT */
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
+ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_invert_alpha,(png_transform_controlp row_info,
png_bytep row),PNG_EMPTY);
-#endif
+#endif /* INVERT_ALPHA */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_add_rgb_to_gray_byte_ops,(png_structrp png_ptr,
+ png_transform_controlp tc, unsigned int index, unsigned int order),
+ PNG_EMPTY);
+ /* This is an init-time utility to add appropriate byte ops to select a given
+ * channel from R/G/B.
+ */
+#endif /* READ_RGB_TO_GRAY */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\
+ defined(PNG_READ_BACKGROUND_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_push_gray_to_rgb_byte_ops,(png_transformp *tr,
+ png_transform_controlp tc), PNG_EMPTY);
+ /* This is an init-time utility to push appropriate byte ops to expand a
+ * grayscale PNG data set to RGB. It calls the function callback so 'tc'
+ * must be non-NULL.
+ */
+#endif /* GRAY_TO_RGB && READ_BACKGROUND */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_add_strip_alpha_byte_ops,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Called from pngrtran.c to add the relevant byte op. */
+#endif /* READ_STRIP_ALPHA */
/* The following decodes the appropriate chunks, and does error correction,
* then calls the appropriate callback for the chunk if it is valid.
*/
-/* Decode the IHDR chunk */
-PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#ifdef PNG_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(png_bytep,png_read_buffer,(png_structrp png_ptr,
+ png_alloc_size_t new_size, int warn),PNG_EMPTY);
+ /* Manage the dynamically allocated read buffer */
-#ifdef PNG_READ_bKGD_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+/* Shared READ IDAT handling: */
+PNG_INTERNAL_FUNCTION(void,png_read_start_IDAT,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Initialize the row buffers, etc. */
+
+typedef enum
+{
+ png_row_incomplete,
+ /* more IDAT data needed for row */
+ png_row_process,
+ /* png_struct::row_buffer contains a complete row */
+ png_row_repeat,
+ /* row not in this pass, but the existing row may be used */
+ png_row_skip
+ /* row not in pass and no appropriate data; skip this row */
+} png_row_op;
+PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr,
+ png_bytep transformed_row, png_bytep display_row, int save_row),
+ PNG_EMPTY);
+ /* Process a block of IDAT data; the routine returns early if it has
+ * obtained a row. It is valid to call this routine with no input data;
+ * it will return png_row_incomplete if it needs input.
+ *
+ * transformed_row: The transformed pixels of the input are written here.
+ * For interlaced images only the pixels in the pass will
+ * be written, the other pixels will not be touched.
+ *
+ * display_row: The transformed pixels but replicated to that the entire
+ * buffer will have been initialized. For passes after the
+ * first the pixels written are determined by the 'block'
+ * algorithm; only those *following* pixels which are
+ * written by *later* passes are written (with a copy of the
+ * pixel from the pass.)
+ *
+ * save_row: A boolean which indicates that the row (unexpanded)
+ * should be saved in png_struct::transformed_row. This can
+ * be used in a later call to png_combine_row.
+ *
+ * During reading the row is built up until png_row_process is returned. At
+ * this point png_struct::row_buffer contains the original PNG row from the
+ * file and, if save_row was set, png_struct::transformed_row contains the
+ * row after the selected row transforms have been performed. For interlaced
+ * images both are the width of the interlace pass.
+ *
+ * When png_row_repeat is returned the same is true, except that the buffers
+ * still contain the contents of the preceding row (the one where this
+ * funciton returned png_row_pricess).
+ *
+ * The row buffers should not be accessed if png_row_skip is returned; this
+ * row is not modified in the current pass.
+ */
+
+PNG_INTERNAL_FUNCTION(void,png_read_free_row_buffers,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Free allocated row buffers; done as soon as possible to avoid carrying
+ * around all the memory for longer than necessary.
+ */
+
+PNG_INTERNAL_FUNCTION(int,png_read_finish_IDAT,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Complete reading of the IDAT chunks. This returns 0 if more data is to
+ * be read, 1 if the zlib stream has terminated. Call this routine with
+ * zstream.avail_in greater than zero unless there is no more input data.
+ * When zstream_avail_in is 0 on entry and the stream does not terminate
+ * an "IDAT truncated" error will be output.
+ *
+ * ENTRY: png_ptr->zstream.{next,avail}_in points to more IDAT data, if
+ * available, otherwise avail_in should be 0.
+ * RET 0: the LZ stream is still active, more IDAT date is required, if
+ * available, the routine *must* be called again.
+ * RET 1: the LZ stream has been closed and an error may have been output;
+ * png_ptr->zstream_error says whether it has. If not and there
+ * is more IDAT data available the caller should output an
+ * appropriate (too much IDAT) error message.
+ */
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_cache_known_unknown,(png_structrp png_ptr,
+ png_const_bytep add, int keep),PNG_EMPTY);
+ /* Update the png_struct::known_unknown bit cache which stores whether each
+ * known chunk should be treated as unknown.
+ */
#endif
-#ifdef PNG_READ_cHRM_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
+typedef enum
+{
+ png_chunk_skip = 0, /* Skip this chunk */
+ png_chunk_unknown, /* Pass the chunk to png_handle_unknown */
+ png_chunk_process_all, /* Process the chunk all at once */
+ png_chunk_process_part /* Process the chunk in parts (for IDAT) */
+} png_chunk_op;
-#ifdef PNG_READ_gAMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
+PNG_INTERNAL_FUNCTION(png_chunk_op,png_find_chunk_op,(png_structrp png_ptr),
+ PNG_EMPTY);
+ /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name
+ * and work out how it should be handled. This function checks the chunk
+ * location using png_struct::mode and will set the mode appropriately for
+ * the known critical chunks but otherwise makes no changes to the stream
+ * read state.
+ */
-#ifdef PNG_READ_hIST_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
+PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr,
+ const png_uint_32 chunk_name),PNG_EMPTY);
-#ifdef PNG_READ_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* PNG_READ_iCCP_SUPPORTED */
-
-#ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif /* PNG_READ_sPLT_SUPPORTED */
-
-#ifdef PNG_READ_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-#endif
-
-PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr,
- png_uint_32 chunk_name),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr,
+ const png_uint_32 chunk_length),PNG_EMPTY);
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);
- /* This is the function that gets called for unknown chunks. The 'keep'
- * argument is either non-zero for a known chunk that has been set to be
- * handled as unknown or zero for an unknown chunk. By default the function
- * just skips the chunk or errors out if it is critical.
+ png_inforp info_ptr, png_bytep chunk_data),PNG_EMPTY);
+ /* Handle an unknown chunk that needs to be processed. It is only valid
+ * to call this after png_find_chunk_op returns png_chunk_unknown. The
+ * data argument points to the png_struct::chunk_length bytes of the chunk
+ * data.
*/
+#endif /* READ_UNKNOWN_CHUNKS */
-#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
- defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
-PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
- (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);
- /* Exactly as the API png_handle_as_unknown() except that the argument is a
- * 32-bit chunk name, not a string.
+PNG_INTERNAL_FUNCTION(void,png_handle_chunk,(png_structrp png_ptr,
+ png_inforp info_ptr),PNG_EMPTY);
+ /* The chunk to handle is in png_struct::chunk_name,chunk_length.
+ *
+ * NOTE: at present it is only valid to call this after png_find_chunk_op
+ * has returned png_chunk_process_all and all the data is available for
+ * png_handle_chunk (via the libpng read callback.)
*/
-#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
+#endif /* READ */
-/* Handle the transformations for reading and writing */
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,
- png_row_infop row_info),PNG_EMPTY);
-#endif
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,
- png_row_infop row_info),PNG_EMPTY);
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),
- PNG_EMPTY);
-#endif
-
-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_crc_skip,(png_structrp png_ptr,
- png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_crc_finish,(png_structrp png_ptr),
- PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
- PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
- png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
- png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
- PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
- png_bytep row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
- PNG_EMPTY);
-# ifdef PNG_READ_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-# endif
-# ifdef PNG_READ_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-# endif
-# ifdef PNG_READ_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr,
- png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr,
- png_inforp info_ptr),PNG_EMPTY);
-# endif
-
-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+PNG_INTERNAL_FUNCTION(void,png_init_row_info,(png_structrp png_ptr),PNG_EMPTY);
+ /* Set the png_struct::row_ members from the PNG file information, running
+ * transforms if required.
+ */
/* Added at libpng version 1.6.0 */
#ifdef PNG_GAMMA_SUPPORTED
@@ -1375,13 +1779,13 @@ PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,
PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,
png_inforp info_ptr), PNG_EMPTY);
- /* Synchronize the info 'valid' flags with the colorspace */
+ /* Synchronize the info 'valid' flags with the colorspace */
PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,
png_inforp info_ptr), PNG_EMPTY);
- /* Copy the png_struct colorspace to the info_struct and call the above to
- * synchronize the flags. Checks for NULL info_ptr and does nothing.
- */
+ /* Copy the png_struct colorspace to the info_struct and call the above to
+ * synchronize the flags. Checks for NULL info_ptr and does nothing.
+ */
#endif
/* Added at libpng version 1.4.0 */
@@ -1410,7 +1814,7 @@ PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr,
#ifdef PNG_iCCP_SUPPORTED
PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,
png_colorspacerp colorspace, png_const_charp name,
- png_uint_32 profile_length, png_const_bytep profile, int color_type),
+ png_uint_32 profile_length, png_const_bytep profile, int is_color),
PNG_EMPTY);
/* The 'name' is used for information only */
@@ -1421,7 +1825,7 @@ PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
png_colorspacerp colorspace, png_const_charp name,
png_uint_32 profile_length,
- png_const_bytep profile /* first 132 bytes only */, int color_type),
+ png_const_bytep profile /* first 132 bytes only */, int is_color),
PNG_EMPTY);
PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
png_colorspacerp colorspace, png_const_charp name,
@@ -1435,14 +1839,8 @@ PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,(
* be zero to indicate that it is not available. It is used, if provided,
* as a fast check on the profile when checking to see if it is sRGB.
*/
-#endif
+#endif /* sRGB */
#endif /* iCCP */
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients,
- (png_structrp png_ptr), PNG_EMPTY);
- /* Set the rgb_to_gray coefficients from the colorspace Y values */
-#endif /* READ_RGB_TO_GRAY */
#endif /* COLORSPACE */
/* Added at libpng version 1.4.0 */
@@ -1451,13 +1849,6 @@ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
int color_type, int interlace_type, int compression_type,
int filter_type),PNG_EMPTY);
-/* Added at libpng version 1.5.10 */
-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
- defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
- (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
-#endif
-
#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,
png_const_charp name),PNG_NORETURN);
@@ -1575,11 +1966,19 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
* data set via an application call to a png_set_ API and png_app_error or
* png_app_warning is used as appropriate.
*
+ * With PNG_CHUNK_FATAL an error can be marked as unrecoverable, and the
+ * function will not return.
+ *
* The 'error' parameter must have one of the following values:
*/
#define PNG_CHUNK_WARNING 0 /* never an error */
#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */
#define PNG_CHUNK_ERROR 2 /* always an error */
+#define PNG_CHUNK_FATAL 3 /* an unrecoverable error */
+
+#ifndef PNG_ERROR_TEXT_SUPPORTED
+# define png_chunk_report(pp,e,v) png_chunk_report(pp,NULL,v)
+#endif
/* ASCII to FP interfaces, currently only implemented if sCAL
* support is required.
@@ -1715,56 +2114,51 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
*/
PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
-#endif
-
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
-/* Same deal, but issue a warning on overflow and return 0. */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn,
- (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by,
- png_int_32 divided_by),PNG_EMPTY);
-#endif
-
-#ifdef PNG_GAMMA_SUPPORTED
-/* Calculate a reciprocal - used for gamma values. This returns
- * 0 if the argument is 0 in order to maintain an undefined value;
- * there are no warnings.
- */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
- PNG_EMPTY);
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* The same but gives a reciprocal of the product of two fixed point
- * values. Accuracy is suitable for gamma calculations but this is
- * not exact - use png_muldiv for that. Only required at present on read.
- */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
- png_fixed_point b),PNG_EMPTY);
-#endif
-
-/* Return true if the gamma value is significantly different from 1.0 */
-PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
- PNG_EMPTY);
-#endif
+#endif /* GAMMA || INCH_CONVERSIONS || READ_pHYs */
#ifdef PNG_READ_GAMMA_SUPPORTED
/* Internal fixed point gamma correction. These APIs are called as
* required to convert single values - they don't need to be fast,
* they are not used when processing image pixel values.
- *
- * While the input is an 'unsigned' value it must actually be the
- * correct bit value - 0..255 or 0..65535 as required.
*/
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,
- unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,
- png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,
- png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
- PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_build_gamma_tables,(png_structrp png_ptr,
- int bit_depth),PNG_EMPTY);
-#endif
+PNG_INTERNAL_FUNCTION(unsigned int,png_gamma_nxmbit_correct,
+ (unsigned int value, png_fixed_point gamma_val, unsigned int n/*input bits*/,
+ unsigned int m/*output bits */),PNG_EMPTY);
+ /* In this case the value must have 'n' bits and the output will have 'm'
+ * bits.
+ */
+
+#if !PNG_RELEASE_BUILD
+PNG_INTERNAL_FUNCTION(int,png_gamma_check,(png_const_structrp png_ptr,
+ png_const_transform_controlp tc),PNG_EMPTY);
+ /* Debugging only routine to repeat the test used above to determine if the
+ * gamma was insignificant.
+ */
+#endif /* !RELEASE_BUILD */
+#endif /* READ_GAMMA */
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+/* Internal check function to saw if the gamma of the PNG data is far enough
+ * from the given screen gamma to require gamma correction (only needed for a
+ * bug work-round in the simplified API).
+ * TODO: it should be possible to remove the bug work-round in 1.7
+ */
+PNG_INTERNAL_FUNCTION(int,png_need_gamma_correction,(png_const_structrp png_ptr,
+ png_fixed_point gamma, int sRGB_output),PNG_EMPTY);
+#endif /* SIMPLIFIED_READ */
+
+/* This is a utility macro to say whether a gamma value is close enough to sRGB.
+ * The test is now hardwired:
+ *
+ * API CHANGE: prior to 1.7 this would depend on the build-time
+ * PNG_GAMMA_THRESHOLD_FIXED setting, which would cause inconsistent results
+ * when the setting was changed. Since this setting can now be changed at
+ * run-time it seems more sensible to have a single fixed definition of 'sRGB'.
+ *
+ * The test is approximately +/- 1%, it allows any decimal value from 0.45 (the
+ * two digit rounded version of 1/2.2) to just under 0.46).
+ */
+#define PNG_GAMMA_IS_sRGB(g) ((g) >= 45000 && (g) < 46000)
/* SIMPLIFIED READ/WRITE SUPPORT */
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
@@ -1815,6 +2209,14 @@ PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,
PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,
png_const_charp error_message),PNG_EMPTY);
+/* Safely initialize a stdio pointer - used by both the read and the write
+ * code.
+ */
+#ifdef PNG_STDIO_SUPPORTED
+PNG_INTERNAL_FUNCTION(int,png_image_init_io,(png_imagep image, png_FILE_p file),
+ PNG_EMPTY);
+#endif /* STDIO */
+
#ifndef PNG_SIMPLIFIED_READ_SUPPORTED
/* png_image_free is used by the write code but not exported */
PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
@@ -1822,6 +2224,20 @@ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
#endif /* SIMPLIFIED READ/WRITE */
+#ifdef PNG_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(png_int_32, png_read_setting, (png_structrp png_ptr,
+ png_uint_32 setting, png_uint_32 parameter, png_int_32 value), PNG_EMPTY);
+#endif /* READ */
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(png_int_32, png_write_setting, (png_structrp png_ptr,
+ png_uint_32 setting, png_uint_32 parameter, png_int_32 value), PNG_EMPTY);
+ /* Implementations of read and write settings, in pngrutil.c and pngwutil.c
+ * respectively.
+ */
+#endif /* WRITE */
+
+/* Maintainer: Put new private prototypes here ^ */
+
/* These are initialization functions for hardware specific PNG filter
* optimizations; list these here then select the appropriate one at compile
* time using the macro PNG_FILTER_OPTIMIZATIONS. If the macro is not defined
@@ -1840,10 +2256,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
(png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
#endif
-/* Maintainer: Put new private prototypes here ^ */
-
#include "pngdebug.h"
+/* EXTENSION SPECIFIC FUNCTIONS */
+#include "arm/neon.h"
+
#ifdef __cplusplus
}
#endif
diff --git a/ext/libpng17/pngread.c b/ext/libpng17/pngread.c
index cec1144451..96a5a1b4b4 100644
--- a/ext/libpng17/pngread.c
+++ b/ext/libpng17/pngread.c
@@ -2,7 +2,7 @@
/* pngread.c - read a PNG file
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -14,18 +14,13 @@
* read a PNG file or stream.
*/
-#ifdef _MSC_VER
-#pragma warning (disable:4996)
-#pragma warning (disable:4244)
-#endif
-
#include "pngpriv.h"
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
# include
#endif
+#define PNG_SRC_FILE PNG_SRC_FILE_pngread
#ifdef PNG_READ_SUPPORTED
-
/* Create a PNG structure for reading, and allocate any memory needed. */
PNG_FUNCTION(png_structp,PNGAPI
png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
@@ -33,10 +28,10 @@ png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
{
#ifndef PNG_USER_MEM_SUPPORTED
png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
- error_fn, warn_fn, NULL, NULL, NULL);
+ error_fn, warn_fn, NULL, NULL, NULL);
#else
return png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
- warn_fn, NULL, NULL, NULL);
+ warn_fn, NULL, NULL, NULL);
}
/* Alternate create PNG structure for reading, and allocate any memory
@@ -48,36 +43,51 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
{
png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
- error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
-#endif /* PNG_USER_MEM_SUPPORTED */
+ error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
+#endif /* USER_MEM */
if (png_ptr != NULL)
{
- png_ptr->mode = PNG_IS_READ_STRUCT;
+ png_ptr->read_struct = 1;
+ png_ptr->critical_crc = crc_error_quit;
+ png_ptr->ancillary_crc = crc_warn_discard;
- /* Added in libpng-1.6.0; this can be used to detect a read structure if
- * required (it will be zero in a write structure.)
- */
-# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
- png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;
-# endif
+# ifdef PNG_BENIGN_ERRORS_SUPPORTED
+# if !PNG_RELEASE_BUILD
+ /* Always quit on error prior to release */
+ png_ptr->benign_error_action = PNG_ERROR;
+ png_ptr->app_warning_action = PNG_WARN;
+ png_ptr->app_error_action = PNG_ERROR;
+# else /* RELEASE_BUILD */
+ /* Allow benign errors on read, subject to app control. */
+ png_ptr->benign_error_action = PNG_WARN;
+# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
+ png_ptr->app_error_action = PNG_WARN;
+ png_ptr->app_warning_action = PNG_WARN;
+# else /* !BENIGN_READ_ERRORS */
+ /* libpng build without benign error support; the application
+ * author has to be assumed to be correct, so:
+ */
+ png_ptr->app_warning_action = PNG_WARN;
+ png_ptr->app_error_action = PNG_ERROR;
+# endif /* !BENIGN_READ_ERRORS */
+# endif /* RELEASE_BUILD */
-# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
- png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
+ /* This is always png_error unless explicitly changed: */
+ png_ptr->IDAT_error_action = PNG_ERROR;
+# endif /* BENIGN_ERRORS */
- /* In stable builds only warn if an application error can be completely
- * handled.
- */
-# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
- png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
-# endif
-# endif
+# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ png_ptr->IDAT_size = PNG_IDAT_READ_SIZE;
+# endif /* SEQUENTIAL_READ */
- /* TODO: delay this, it can be done in png_init_io (if the app doesn't
- * do it itself) avoiding setting the default function if it is not
- * required.
- */
- png_set_read_fn(png_ptr, NULL, NULL);
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ /* Default gamma correction values: */
+# if 0 /*NYI*/
+ png_ptr->gamma_accuracy = PNG_DEFAULT_GAMMA_ACCURACY;
+# endif /*NYI*/
+ png_ptr->gamma_threshold = PNG_GAMMA_THRESHOLD_FIXED;
+# endif /* READ_GAMMA */
}
return png_ptr;
@@ -85,6 +95,64 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the chunk header (length + type name).
+ * Put the type name into png_ptr->chunk_name, and return the length.
+ */
+static void
+png_read_chunk_header(png_structrp png_ptr)
+{
+ png_byte buf[8];
+
+#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);
+
+ /* Put the chunk name into png_ptr->chunk_name. */
+ png_ptr->chunk_length = png_get_uint_31(png_ptr, buf);
+ 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)png_ptr->chunk_length);
+
+ /* Reset the crc and run it over the chunk name. */
+ png_reset_crc(png_ptr, buf + 4);
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
+#endif
+}
+
+static void
+png_read_sequential_unknown(png_structrp png_ptr, png_inforp info_ptr)
+{
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ /* Read the data for an unknown chunk. The read buffer is used: */
+ png_bytep buffer = png_read_buffer(png_ptr, png_ptr->chunk_length,
+ PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)); /* error if critical */
+
+ if (buffer != NULL)
+ {
+ if (png_ptr->chunk_length > 0U)
+ png_crc_read(png_ptr, buffer, png_ptr->chunk_length);
+
+ png_crc_finish(png_ptr, 0);
+ png_handle_unknown(png_ptr, info_ptr, buffer);
+ }
+
+ else /* out of memory on an ancillary chunk; skip the chunk */
+#else /* !READ_UNKNOWN_CHUNKS */
+ /* or, no support for reading unknown chunks, so just skip it. */
+ PNG_UNUSED(info_ptr)
+#endif /* !READ_UNKNOWN_CHUNKS */
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+}
+
/* Read the information before the actual image data. This has been
* changed in v0.90 to allow reading a file that already has the magic
* bytes read from the stream. You can tell libpng how many bytes have
@@ -96,196 +164,65 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
void PNGAPI
png_read_info(png_structrp png_ptr, png_inforp info_ptr)
{
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- int keep;
-#endif
-
png_debug(1, "in png_read_info");
if (png_ptr == NULL || info_ptr == NULL)
return;
- /* Read and check the PNG file signature. */
+ /* Read and check the PNG file signature (this may do nothing if it has
+ * already been read.)
+ */
png_read_sig(png_ptr, info_ptr);
- for (;;)
+ /* Loop reading chunks until an IDAT is encountered or we reach the end of
+ * the stream (IEND).
+ *
+ * Prior to 1.7.0 this function behaved very weirdly if called after the
+ * IDATs had been read; it would keep on reading chunks util it found
+ * another IDAT. This could cause it to read beyond IEND, damaging the
+ * state in the host stream. This is now caught by the check below.
+ */
+ while ((png_ptr->mode & (PNG_HAVE_IEND|PNG_HAVE_IDAT)) == 0)
{
- png_uint_32 length = png_read_chunk_header(png_ptr);
- png_uint_32 chunk_name = png_ptr->chunk_name;
-
- /* IDAT logic needs to happen here to simplify getting the two flags
- * right.
- */
- if (chunk_name == png_IDAT)
+ png_read_chunk_header(png_ptr);
+ switch (png_find_chunk_op(png_ptr))
{
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "Missing IHDR before IDAT");
-
- else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
- !(png_ptr->mode & PNG_HAVE_PLTE))
- png_chunk_error(png_ptr, "Missing PLTE before IDAT");
-
- else if (png_ptr->mode & PNG_AFTER_IDAT)
- png_chunk_benign_error(png_ptr, "Too many IDATs found[s]");
-
- png_ptr->mode |= PNG_HAVE_IDAT;
- }
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
- png_ptr->mode |= PNG_AFTER_IDAT;
-
- /* This should be a binary subdivision search or a hash for
- * matching the chunk name rather than a linear search.
- */
- if (chunk_name == png_IHDR)
- png_handle_IHDR(png_ptr, info_ptr, length);
-
- else if (chunk_name == png_IEND)
- png_handle_IEND(png_ptr, info_ptr, length);
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
- {
- png_handle_unknown(png_ptr, info_ptr, length, keep);
-
- if (chunk_name == png_PLTE)
- png_ptr->mode |= PNG_HAVE_PLTE;
-
- else if (chunk_name == png_IDAT)
- {
- png_ptr->idat_size = 0; /* It has been consumed */
+ default:
+ impossible("invalid chunk op");
+ /* FALL THROUGH */
+ case png_chunk_skip:
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
break;
- }
+
+ case png_chunk_unknown:
+ png_read_sequential_unknown(png_ptr, info_ptr);
+ break;
+
+ case png_chunk_process_all:
+ png_handle_chunk(png_ptr, info_ptr);
+ break;
+
+ case png_chunk_process_part:
+ debug(png_ptr->mode & PNG_HAVE_IDAT);
+ return;
}
-#endif
- else if (chunk_name == png_PLTE)
- png_handle_PLTE(png_ptr, info_ptr, length);
-
- else if (chunk_name == png_IDAT)
- {
- png_ptr->idat_size = length;
- break;
- }
-
-#ifdef PNG_READ_bKGD_SUPPORTED
- else if (chunk_name == png_bKGD)
- png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_cHRM_SUPPORTED
- else if (chunk_name == png_cHRM)
- png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_gAMA_SUPPORTED
- else if (chunk_name == png_gAMA)
- png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
- else if (chunk_name == png_hIST)
- png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
- else if (chunk_name == png_oFFs)
- png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
- else if (chunk_name == png_pCAL)
- png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
- else if (chunk_name == png_sCAL)
- png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
- else if (chunk_name == png_pHYs)
- png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
- else if (chunk_name == png_sBIT)
- png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
- else if (chunk_name == png_sRGB)
- png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
- else if (chunk_name == png_iCCP)
- png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
- else if (chunk_name == png_sPLT)
- png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
- else if (chunk_name == png_tEXt)
- png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
- else if (chunk_name == png_tIME)
- png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
- else if (chunk_name == png_tRNS)
- png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
- else if (chunk_name == png_zTXt)
- png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
- else if (chunk_name == png_iTXt)
- png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
-
- else
- png_handle_unknown(png_ptr, info_ptr, length,
- PNG_HANDLE_CHUNK_AS_DEFAULT);
}
-}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
-/* Optional call to update the users info_ptr structure */
-void PNGAPI
-png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)
-{
- png_debug(1, "in png_read_update_info");
+ /* The loop was ended by IDAT or IEND, but if an IEND was seen the read code
+ * (png_handle_position in pngrutil.c) should have errored out, therefore:
+ */
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ affirm(png_ptr->chunk_name == png_IDAT && ((png_ptr->known_unknown)&1U));
+#else
+ debug(png_ptr->chunk_name == png_IDAT);
+ impossible("unknown IDAT");
+#endif
- if (png_ptr != NULL)
- {
- if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
- {
- png_read_start_row(png_ptr);
-
-# ifdef PNG_READ_TRANSFORMS_SUPPORTED
- png_read_transform_info(png_ptr, info_ptr);
-# else
- PNG_UNUSED(info_ptr)
-# endif
- }
-
- /* New in 1.6.0 this avoids the bug of doing the initializations twice */
- else
- png_app_error(png_ptr,
- "png_read_update_info/png_start_read_image: duplicate call");
- }
+ /* And the code cannot have left it unread; it must have called one of the
+ * handlers, so we are skipping IDAT.
+ */
}
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Initialize palette, background, etc, after transformations
* are set, but before any reading takes place. This allows
* the user to obtain a gamma-corrected palette, for example.
@@ -298,318 +235,175 @@ png_start_read_image(png_structrp png_ptr)
if (png_ptr != NULL)
{
- if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
- png_read_start_row(png_ptr);
+ if (png_ptr->zowner != png_IDAT)
+ png_read_start_IDAT(png_ptr);
- /* New in 1.6.0 this avoids the bug of doing the initializations twice */
+ /* New in 1.6.0 this avoids the bug of doing the initializations twice,
+ * it could be a warning but in practice it indicates that the app may
+ * have made png_get_ calls on png_ptr assuming that it hadn't been
+ * 'started'.
+ */
else
png_app_error(png_ptr,
- "png_start_read_image/png_read_update_info: duplicate call");
+ "png_start_read_image/png_read_update_info: duplicate call");
}
}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-/* Undoes intrapixel differencing,
- * NOTE: this is apparently only supported in the 'sequential' reader.
- */
static void
-png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
+png_read_IDAT(png_structrp png_ptr)
{
- png_debug(1, "in png_do_read_intrapixel");
+ /* Read more input data, up to png_struct::IDAT_size, stop at the end of the
+ * IDAT stream. pngset.c checks png_struct::IDAT_size to ensure that it will
+ * fit in a uInt.
+ */
+ const uInt buffer_size = (uInt)/*SAFE*/png_ptr->IDAT_size;
+ uInt IDAT_size = 0;
+ png_bytep buffer =
+ png_read_buffer(png_ptr, buffer_size, 0/*error*/);
- if (
- (row_info->color_type & PNG_COLOR_MASK_COLOR))
+ png_ptr->zstream.next_in = buffer;
+
+ while (png_ptr->chunk_name == png_IDAT && IDAT_size < buffer_size)
{
- int bytes_per_pixel;
- png_uint_32 row_width = row_info->width;
+ png_uint_32 l = png_ptr->chunk_length;
- if (row_info->bit_depth == 8)
+ while (l == 0) /* end of this IDAT */
{
- png_bytep rp;
- png_uint_32 i;
+ png_crc_finish(png_ptr, 0);
+ png_read_chunk_header(png_ptr);
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
- bytes_per_pixel = 3;
-
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- bytes_per_pixel = 4;
-
- else
- return;
-
- for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+ if (png_ptr->chunk_name != png_IDAT) /* end of all IDAT */
{
- *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff);
- *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff);
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ goto done;
}
+
+ l = png_ptr->chunk_length;
}
- else if (row_info->bit_depth == 16)
- {
- png_bytep rp;
- png_uint_32 i;
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
- bytes_per_pixel = 6;
+ /* Read from the IDAT chunk into the buffer, up to png_struct::IDAT_size:
+ */
+ if (l > buffer_size - IDAT_size) /* SAFE: while check */
+ l = buffer_size - IDAT_size;
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- bytes_per_pixel = 8;
-
- else
- return;
-
- for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
- {
- png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1);
- png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3);
- png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5);
- png_uint_32 red = (s0 + s1 + 65536) & 0xffff;
- png_uint_32 blue = (s2 + s1 + 65536) & 0xffff;
- *(rp ) = (png_byte)((red >> 8) & 0xff);
- *(rp + 1) = (png_byte)(red & 0xff);
- *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
- *(rp + 5) = (png_byte)(blue & 0xff);
- }
- }
+ png_crc_read(png_ptr, buffer+IDAT_size, l);
+ IDAT_size += (uInt)/*SAFE*/l;
+ png_ptr->chunk_length -= l;
}
+done:
+
+ /* IDAT_size may be zero if the compressed image stream is truncated;
+ * this is likely given a broken PNG.
+ */
+ png_ptr->zstream.next_in = buffer;
+ png_ptr->zstream.avail_in = IDAT_size;
}
-#endif /* PNG_MNG_FEATURES_SUPPORTED */
void PNGAPI
png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
+ /* It is valid to call this API with both 'row' and 'dsp_row' NULL, all
+ * the processing gets done. This is only useful for, either, performance
+ * testing (but it skips png_combine_row) or if there is a user transform
+ * or user row callback which actually uses the row data.
+ */
{
- png_row_info row_info;
-
if (png_ptr == NULL)
return;
png_debug2(1, "in png_read_row (row %lu, pass %d)",
(unsigned long)png_ptr->row_number, png_ptr->pass);
- /* png_read_start_row sets the information (in particular iwidth) for this
- * interlace pass.
+ /* Check the row number; if png_read_process_IDAT is called too many times
+ * if issues an affirm, but, while this is appropriate for the progressive
+ * reader, it is an app error if it happens here.
+ *
+ * Note that when the app does the interlace handling the last row will
+ * typically be before the last row in the image.
*/
- if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
- png_read_start_row(png_ptr);
-
- /* 1.5.6: row_info moved out of png_struct to a local here. */
- row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
- row_info.color_type = png_ptr->color_type;
- row_info.bit_depth = png_ptr->bit_depth;
- row_info.channels = png_ptr->channels;
- row_info.pixel_depth = png_ptr->pixel_depth;
- row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
- if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+ if (png_ptr->read_started &&
+ (png_ptr->interlaced == PNG_INTERLACE_NONE ?
+ png_ptr->row_number == png_ptr->height-1U : (
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ png_ptr->do_interlace ?
+ png_ptr->pass == 6U && png_ptr->row_number == png_ptr->height-1U :
+# endif /* READ_INTERLACING */
+ png_ptr->pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) &&
+ PNG_LAST_PASS_ROW(png_ptr->row_number, png_ptr->pass,
+ png_ptr->height)
+ )
+ ))
{
- /* Check for transforms that have been set but were defined out */
-#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
- if (png_ptr->transformations & PNG_INVERT_MONO)
- png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
- if (png_ptr->transformations & PNG_FILLER)
- png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
- !defined(PNG_READ_PACKSWAP_SUPPORTED)
- if (png_ptr->transformations & PNG_PACKSWAP)
- png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
- if (png_ptr->transformations & PNG_PACK)
- png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
- if (png_ptr->transformations & PNG_SHIFT)
- png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
- if (png_ptr->transformations & PNG_BGR)
- png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined");
-#endif
-
-#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
- if (png_ptr->transformations & PNG_SWAP_BYTES)
- png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined");
-#endif
+ png_app_error(png_ptr, "Too many calls to png_read_row");
+ return;
}
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* If interlaced and we do not need a new row, combine row and return.
- * Notice that the pixels we have from previous rows have been transformed
- * already; we can only combine like with like (transformed or
- * untransformed) and, because of the libpng API for interlaced images, this
- * means we must transform before de-interlacing.
+ /* Check this right at the start; functions like png_read_process_IDAT
+ * regard this condition as an internal error:
*/
- if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+ if (png_ptr->zowner != png_IDAT)
+ png_read_start_IDAT(png_ptr);
+
+ /* So reading has started: */
+ png_ptr->read_started = 1;
+
+ for (;;)
{
- switch (png_ptr->pass)
+ if (png_ptr->zstream.avail_in == 0)
+ png_read_IDAT(png_ptr);
+
+ /* So... zstream.next_in may still be 0, but this may be enough for the
+ * next row if zlib is storing enough output state (it only need be enough
+ * for one byte, because png_read_process_IDAT keeps the next filter byte,
+ * so on the last row of the image only one byte might be required.)
+ *
+ * png_read_process_IDAT handles the case where the input has ended; mode
+ * has PNG_AFTER_IDAT set, by either doing png_error or using 0 bytes for
+ * the data (after issuing a warning.)
+ */
+ switch (png_read_process_IDAT(png_ptr, row, dsp_row, 0/*no save*/))
{
- case 0:
- if (png_ptr->row_number & 0x07)
- {
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
- png_read_finish_row(png_ptr);
- return;
- }
- break;
+ case png_row_incomplete:
+ /* more IDAT data needed for row */
+ debug(png_ptr->zstream.avail_in == 0);
+ continue;
- case 1:
- if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
- {
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- png_read_finish_row(png_ptr);
- return;
- }
- break;
-
- case 2:
- if ((png_ptr->row_number & 0x07) != 4)
- {
- if (dsp_row != NULL && (png_ptr->row_number & 4))
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- png_read_finish_row(png_ptr);
- return;
- }
- break;
-
- case 3:
- if ((png_ptr->row_number & 3) || png_ptr->width < 3)
- {
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- png_read_finish_row(png_ptr);
- return;
- }
- break;
-
- case 4:
- if ((png_ptr->row_number & 3) != 2)
- {
- if (dsp_row != NULL && (png_ptr->row_number & 2))
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- png_read_finish_row(png_ptr);
- return;
- }
- break;
-
- case 5:
- if ((png_ptr->row_number & 1) || png_ptr->width < 2)
- {
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- png_read_finish_row(png_ptr);
- return;
- }
- break;
+ case png_row_repeat:
+ /* row not in this pass, but the existing row in row_buffer or (if
+ * transforms are happening) png_struct::transformed_row is
+ * available from a previous row.
+ */
+ /* FALL THROUGH */
+ case png_row_skip:
+ /* row not in pass and no appropriate data; skip this row, nothing
+ * more need be done, except the read_row_fn and then only if libpng
+ * is doing the interlace handling (this is the historical
+ * behavior!)
+ */
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (!png_ptr->do_interlace) continue;
+# else /* !do_interlace */
+ continue;
+# endif /* !do_interlace */
+ /* FALL THROUGH */
+ case png_row_process:
+ /* png_read_process_IDAT has done everything we need, the only extra
+ * required is to call the application row callback.
+ */
+ if (png_ptr->read_row_fn != NULL)
+ png_ptr->read_row_fn(png_ptr, png_ptr->row_number,
+ png_ptr->pass);
+ /* And return now because the next row has been processed; so there
+ * is exactly one read_row_fn callback for each call to
+ * png_read_process_IDAT.
+ */
+ return;
default:
- case 6:
- if (!(png_ptr->row_number & 1))
- {
- png_read_finish_row(png_ptr);
- return;
- }
- break;
+ impossible("not reached");
}
}
-#endif
-
- if (!(png_ptr->mode & PNG_HAVE_IDAT))
- png_error(png_ptr, "Invalid attempt to read row data");
-
- /* Fill the row with IDAT data: */
- png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
-
- if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
- {
- if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
- png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
- png_ptr->prev_row + 1, png_ptr->row_buf[0]);
- else
- png_error(png_ptr, "bad adaptive filter value");
- }
-
- /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
- * 1.5.6, while the buffer really is this big in current versions of libpng
- * it may not be in the future, so this was changed just to copy the
- * interlaced count:
- */
- memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
- (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
- {
- /* Intrapixel differencing */
- png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1);
- }
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
- if (png_ptr->transformations)
- png_do_read_transformations(png_ptr, &row_info);
-#endif
-
- /* The transformed pixel depth should match the depth now in row_info. */
- if (png_ptr->transformed_pixel_depth == 0)
- {
- png_ptr->transformed_pixel_depth = row_info.pixel_depth;
- if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
- png_error(png_ptr, "sequential row overflow");
- }
-
- else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
- png_error(png_ptr, "internal sequential row size calculation error");
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* Blow up interlaced rows to full size */
- if (png_ptr->interlaced &&
- (png_ptr->transformations & PNG_INTERLACE))
- {
- if (png_ptr->pass < 6)
- png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
- png_ptr->transformations);
-
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, 1/*display*/);
-
- if (row != NULL)
- png_combine_row(png_ptr, row, 0/*row*/);
- }
-
- else
-#endif
- {
- if (row != NULL)
- png_combine_row(png_ptr, row, -1/*ignored*/);
-
- if (dsp_row != NULL)
- png_combine_row(png_ptr, dsp_row, -1/*ignored*/);
- }
- png_read_finish_row(png_ptr);
-
- if (png_ptr->read_row_fn != NULL)
- (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
-
}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read one or more rows of image data. If the image is interlaced,
* and png_set_interlace_handling() has been called, the rows need to
* contain the contents of the rows from the previous pass. If the
@@ -674,14 +468,14 @@ png_read_rows(png_structrp png_ptr, png_bytepp row,
dp++;
}
}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* SEQUENTIAL_READ */
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_READ_IMAGE_SUPPORTED
/* Read the entire image. If the image has an alpha channel or a tRNS
* chunk, and you have called png_handle_alpha()[*], you will need to
* initialize the image to the current image that PNG will be overlaying.
* We set the num_rows again here, in case it was incorrectly set in
- * png_read_start_row() by a call to png_read_update_info() or
+ * png_read_start_IDAT() by a call to png_read_update_info() or
* png_start_read_image() if png_set_interlace_handling() wasn't called
* prior to either of these functions like it should have been. You can
* only call this function once. If you desire to have an image for
@@ -692,54 +486,31 @@ png_read_rows(png_structrp png_ptr, png_bytepp row,
void PNGAPI
png_read_image(png_structrp png_ptr, png_bytepp image)
{
- png_uint_32 i, image_height;
+ png_uint_32 image_height;
int pass, j;
- png_bytepp rp;
png_debug(1, "in png_read_image");
if (png_ptr == NULL)
return;
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
- {
+ if (png_ptr->zowner != png_IDAT)
pass = png_set_interlace_handling(png_ptr);
- /* And make sure transforms are initialized. */
- png_start_read_image(png_ptr);
- }
+
else
{
- if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE))
- {
- /* Caller called png_start_read_image or png_read_update_info without
- * first turning on the PNG_INTERLACE transform. We can fix this here,
- * but the caller should do it!
- */
- png_warning(png_ptr, "Interlace handling should be turned on when "
- "using png_read_image");
- /* Make sure this is set correctly */
- png_ptr->num_rows = png_ptr->height;
- }
+ if (png_ptr->interlaced == 0)
+ pass = 1;
- /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
- * the above error case.
- */
- pass = png_set_interlace_handling(png_ptr);
+ else
+ pass = PNG_INTERLACE_ADAM7_PASSES;
}
-#else
- if (png_ptr->interlaced)
- png_error(png_ptr,
- "Cannot read interlaced image -- interlace handler disabled");
- pass = 1;
-#endif
-
- image_height=png_ptr->height;
-
- for (j = 0; j < pass; j++)
+ for (j = 0, image_height = png_ptr->height; j < pass; ++j)
{
- rp = image;
+ png_bytepp rp = image;
+ png_uint_32 i;
+
for (i = 0; i < image_height; i++)
{
png_read_row(png_ptr, *rp, NULL);
@@ -747,7 +518,7 @@ png_read_image(png_structrp png_ptr, png_bytepp image)
}
}
}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* READ_IMAGE */
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read the end of the PNG file. Will not read past the end of the
@@ -757,162 +528,147 @@ png_read_image(png_structrp png_ptr, png_bytepp image)
void PNGAPI
png_read_end(png_structrp png_ptr, png_inforp info_ptr)
{
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- int keep;
-#endif
-
png_debug(1, "in png_read_end");
if (png_ptr == NULL)
return;
- /* If png_read_end is called in the middle of reading the rows there may
- * still be pending IDAT data and an owned zstream. Deal with this here.
+ /* When this routine is entered it is possible that an IDAT chunk still
+ * remains to be read. There are three conditions:
+ *
+ * 1) The app decided to handle IDAT as unknown, libpng will have consumed
+ * the first IDAT in png_read_info, the rest will be consumed as normal
+ * chunks by calls to png_handle_chunk below.
+ *
+ * 2) The app did not start to read an image, so png_read_start_IDAT was
+ * not called and png_struct::zowner is not png_IDAT. The first IDAT
+ * must still be skipped then the code below will skip the remainder.
+ *
+ * 3) The app did start to read the image. png_struct::zowner is png_IDAT
+ * and we need to close down the IDAT reading code. There may also be
+ * pending IDAT chunks, these are passed to png_read_finish_IDAT here so
+ * that error detection happens. If the app didn't read all the rows
+ * libpng will issue an 'extra compressed data' error, we could supress
+ * that by warning that not all the rows have been read and setting
+ * png_struct::zstream_error if necessary.
*/
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- if (!png_chunk_unknown_handling(png_ptr, png_IDAT))
-#endif
- png_read_finish_IDAT(png_ptr);
-
-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
- /* Report invalid palette index; added at libng-1.5.10 */
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
- png_ptr->num_palette_max > png_ptr->num_palette)
- png_benign_error(png_ptr, "Read palette index exceeding num_palette");
-#endif
-
- do
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ if (!(png_ptr->known_unknown & 1U))
+# endif
{
- png_uint_32 length = png_read_chunk_header(png_ptr);
- png_uint_32 chunk_name = png_ptr->chunk_name;
-
- if (chunk_name == png_IEND)
- png_handle_IEND(png_ptr, info_ptr, length);
-
- else if (chunk_name == png_IHDR)
- png_handle_IHDR(png_ptr, info_ptr, length);
-
- else if (info_ptr == NULL)
- png_crc_finish(png_ptr, length);
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
+ if (png_ptr->zowner == png_IDAT)
{
- if (chunk_name == png_IDAT)
- {
- if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
- png_benign_error(png_ptr, "Too many IDATs found(a1)");
- }
- png_handle_unknown(png_ptr, info_ptr, length, keep);
- if (chunk_name == png_PLTE)
- png_ptr->mode |= PNG_HAVE_PLTE;
- }
-#endif
-
- else if (chunk_name == png_IDAT)
- {
- /* Zero length IDATs are legal after the last IDAT has been
- * read, but not after other chunks have been read.
+ /* Normal case: read to the end of the IDAT chunks. In about
+ * 5/PNG_IDAT_READ_SIZE cases (typically that's 1:820) zlib will have
+ * returned all the image data but not read up to the end of the
+ * Adler32 because the end of the stream had not been read. Make sure
+ * it gets read here:
*/
- if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
- png_benign_error(png_ptr, "Too many IDATs found(a2)");
+ if (png_ptr->zstream.avail_in == 0)
+ png_read_IDAT(png_ptr);
- png_crc_finish(png_ptr, length);
+ while (!png_read_finish_IDAT(png_ptr)) {
+ /* This will adjust zstream.next/avail_in appropriately and if
+ * necessary read the next chunk. After this avail_in may still
+ * be zero, but if it is then PNG_AFTER_IDAT should be set.
+ */
+ debug(png_ptr->zstream.avail_in == 0);
+ png_read_IDAT(png_ptr);
+ debug(png_ptr->zstream.avail_in > 0 ||
+ (png_ptr->mode & PNG_AFTER_IDAT) != 0);
+ }
+
+ debug(png_ptr->zstream.avail_in == 0 && png_ptr->zowner == 0);
+
+ /* If this is still an IDAT then it hasn't been finished; at least
+ * the CRC has not been read. If there is data left in it then
+ * an error may need to be output. Note that the code below handles
+ * any additional chunks.
+ */
+ if (png_ptr->chunk_name == png_IDAT)
+ {
+ if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error)
+ {
+ png_chunk_benign_error(png_ptr, "too much IDAT data (read)");
+ png_ptr->zstream_error = 1;
+ }
+
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+ png_read_chunk_header(png_ptr);
+ }
}
- else if (chunk_name == png_PLTE)
- png_handle_PLTE(png_ptr, info_ptr, length);
-#ifdef PNG_READ_bKGD_SUPPORTED
- else if (chunk_name == png_bKGD)
- png_handle_bKGD(png_ptr, info_ptr, length);
-#endif
+ else if (png_ptr->chunk_name == png_IDAT)
+ {
+ /* This IDAT has not been processed, the remainder will be finished
+ * in the loop. This is the case where IDAT is being skipped because
+ * the rows weren't read, this is OK, but warn anyway.
+ */
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+ png_app_warning(png_ptr, "image reading skipped");
+ png_ptr->zstream_error = 1; /* Prevent 'too much IDAT' errors */
+ png_read_chunk_header(png_ptr);
+ }
-#ifdef PNG_READ_cHRM_SUPPORTED
- else if (chunk_name == png_cHRM)
- png_handle_cHRM(png_ptr, info_ptr, length);
-#endif
+ else /* This might work, if the signature was read, but just in case: */
+ png_app_error(png_ptr, "Missing call to png_read_info");
+ }
-#ifdef PNG_READ_gAMA_SUPPORTED
- else if (chunk_name == png_gAMA)
- png_handle_gAMA(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_hIST_SUPPORTED
- else if (chunk_name == png_hIST)
- png_handle_hIST(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_oFFs_SUPPORTED
- else if (chunk_name == png_oFFs)
- png_handle_oFFs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pCAL_SUPPORTED
- else if (chunk_name == png_pCAL)
- png_handle_pCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sCAL_SUPPORTED
- else if (chunk_name == png_sCAL)
- png_handle_sCAL(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_pHYs_SUPPORTED
- else if (chunk_name == png_pHYs)
- png_handle_pHYs(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sBIT_SUPPORTED
- else if (chunk_name == png_sBIT)
- png_handle_sBIT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sRGB_SUPPORTED
- else if (chunk_name == png_sRGB)
- png_handle_sRGB(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iCCP_SUPPORTED
- else if (chunk_name == png_iCCP)
- png_handle_iCCP(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_sPLT_SUPPORTED
- else if (chunk_name == png_sPLT)
- png_handle_sPLT(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tEXt_SUPPORTED
- else if (chunk_name == png_tEXt)
- png_handle_tEXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tIME_SUPPORTED
- else if (chunk_name == png_tIME)
- png_handle_tIME(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_tRNS_SUPPORTED
- else if (chunk_name == png_tRNS)
- png_handle_tRNS(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_zTXt_SUPPORTED
- else if (chunk_name == png_zTXt)
- png_handle_zTXt(png_ptr, info_ptr, length);
-#endif
-
-#ifdef PNG_READ_iTXt_SUPPORTED
- else if (chunk_name == png_iTXt)
- png_handle_iTXt(png_ptr, info_ptr, length);
-#endif
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ else
+ {
+ /* IDAT is unknown, the chunk that terminated the loop must be an IDAT
+ * and it has been processed. Get a new chunk header.
+ */
+ if (png_ptr->chunk_name == png_IDAT)
+ png_read_chunk_header(png_ptr);
else
- png_handle_unknown(png_ptr, info_ptr, length,
- PNG_HANDLE_CHUNK_AS_DEFAULT);
- } while (!(png_ptr->mode & PNG_HAVE_IEND));
+ png_app_error(png_ptr,
+ "Missing call to png_read_info with unknown IDAT");
+ }
+# endif
+
+ if ((png_ptr->mode & PNG_HAVE_IEND) == 0) for (;;)
+ {
+ switch (png_find_chunk_op(png_ptr))
+ {
+ default:
+ impossible("invalid chunk op");
+ /* FALL THROUGH */
+ case png_chunk_skip:
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+ break;
+
+ case png_chunk_unknown:
+ png_read_sequential_unknown(png_ptr, info_ptr);
+ break;
+
+ case png_chunk_process_all:
+ png_handle_chunk(png_ptr, info_ptr);
+ break;
+
+ case png_chunk_process_part:
+ debug(png_ptr->chunk_name == png_IDAT);
+ debug(!(png_ptr->mode & PNG_AFTER_IDAT));
+ if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error)
+ {
+ png_chunk_benign_error(png_ptr, "too many IDAT chunks");
+ png_ptr->zstream_error = 1;
+ }
+
+ /* Skip it: */
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+ return;
+ }
+
+ if ((png_ptr->mode & PNG_HAVE_IEND) != 0)
+ break;
+
+ png_read_chunk_header(png_ptr);
+ }
}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* SEQUENTIAL_READ */
/* Free all memory used in the read struct */
static void
@@ -920,43 +676,50 @@ png_read_destroy(png_structrp png_ptr)
{
png_debug(1, "in png_read_destroy");
-#ifdef PNG_READ_GAMMA_SUPPORTED
- png_destroy_gamma_table(png_ptr);
-#endif
+ png_read_free_row_buffers(png_ptr);
- png_free(png_ptr, png_ptr->big_row_buf);
- png_free(png_ptr, png_ptr->big_prev_row);
png_free(png_ptr, png_ptr->read_buffer);
+ png_ptr->read_buffer = NULL;
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
- png_free(png_ptr, png_ptr->palette_lookup);
- png_free(png_ptr, png_ptr->quantize_index);
-#endif
+ if (png_ptr->palette != NULL)
+ {
+ png_free(png_ptr, png_ptr->palette);
+ png_ptr->num_palette = 0;
+ png_ptr->palette = NULL;
+ }
- if (png_ptr->free_me & PNG_FREE_PLTE)
- png_zfree(png_ptr, png_ptr->palette);
- png_ptr->free_me &= ~PNG_FREE_PLTE;
-
-#if defined(PNG_tRNS_SUPPORTED) || \
- defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
- if (png_ptr->free_me & PNG_FREE_TRNS)
+#ifdef PNG_READ_tRNS_SUPPORTED
+ if (png_ptr->trans_alpha != NULL)
+ {
png_free(png_ptr, png_ptr->trans_alpha);
- png_ptr->free_me &= ~PNG_FREE_TRNS;
+ png_ptr->num_trans = 0;
+ png_ptr->trans_alpha = NULL;
+ }
#endif
- inflateEnd(&png_ptr->zstream);
+ if (png_ptr->zstream.state != NULL)
+ {
+ int ret = inflateEnd(&png_ptr->zstream);
+
+ if (ret != Z_OK)
+ {
+ png_zstream_error(&png_ptr->zstream, ret);
+ png_warning(png_ptr, png_ptr->zstream.msg);
+ }
+ }
+
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ png_transform_free(png_ptr, &png_ptr->transform_list);
+#endif
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
png_free(png_ptr, png_ptr->save_buffer);
-#endif
-
-#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \
- defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
- png_free(png_ptr, png_ptr->unknown_chunk.data);
+ png_ptr->save_buffer = NULL;
#endif
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
png_free(png_ptr, png_ptr->chunk_list);
+ png_ptr->chunk_list = NULL;
#endif
/* NOTE: the 'setjmp' buffer may still be allocated and the memory and error
@@ -1002,12 +765,21 @@ png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)
}
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-#ifdef PNG_INFO_IMAGE_SUPPORTED
+#ifdef PNG_READ_PNG_SUPPORTED
+#ifdef __GNUC__
+/* This exists solely to work round a warning from GNU C. */
+static int /* PRIVATE */
+png_gt(size_t a, size_t b)
+{
+ return a > b;
+}
+#else
+# define png_gt(a,b) ((a) > (b))
+#endif
+
void PNGAPI
-png_read_png(png_structrp png_ptr, png_inforp info_ptr,
- int transforms,
- voidp params)
+png_read_png(png_structrp png_ptr, png_inforp info_ptr, int transforms,
+ voidp params)
{
if (png_ptr == NULL || info_ptr == NULL)
return;
@@ -1016,7 +788,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
* PNG file before the first IDAT (image data chunk).
*/
png_read_info(png_ptr, info_ptr);
- if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep)))
+ if (png_gt(info_ptr->height, PNG_SIZE_MAX/(sizeof (png_bytep))))
png_error(png_ptr, "Image is too high to process with png_read_png()");
/* -------------- image transformations start here ------------------- */
@@ -1028,7 +800,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Tell libpng to strip 16-bit/color files down to 8 bits per color.
*/
- if (transforms & PNG_TRANSFORM_SCALE_16)
+ if ((transforms & PNG_TRANSFORM_SCALE_16) != 0)
/* Added at libpng-1.5.4. "strip_16" produces the same result that it
* did in earlier versions, while "scale_16" is now more accurate.
*/
@@ -1042,7 +814,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
* latter by doing SCALE first. This is ok and allows apps not to check for
* which is supported to get the right answer.
*/
- if (transforms & PNG_TRANSFORM_STRIP_16)
+ if ((transforms & PNG_TRANSFORM_STRIP_16) != 0)
#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
png_set_strip_16(png_ptr);
#else
@@ -1052,7 +824,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Strip alpha bytes from the input data without combining with
* the background (not recommended).
*/
- if (transforms & PNG_TRANSFORM_STRIP_ALPHA)
+ if ((transforms & PNG_TRANSFORM_STRIP_ALPHA) != 0)
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
png_set_strip_alpha(png_ptr);
#else
@@ -1062,7 +834,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
* byte into separate bytes (useful for paletted and grayscale images).
*/
- if (transforms & PNG_TRANSFORM_PACKING)
+ if ((transforms & PNG_TRANSFORM_PACKING) != 0)
#ifdef PNG_READ_PACK_SUPPORTED
png_set_packing(png_ptr);
#else
@@ -1072,7 +844,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Change the order of packed pixels to least significant bit first
* (not useful if you are using png_set_packing).
*/
- if (transforms & PNG_TRANSFORM_PACKSWAP)
+ if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
#ifdef PNG_READ_PACKSWAP_SUPPORTED
png_set_packswap(png_ptr);
#else
@@ -1084,19 +856,17 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
* Expand paletted or RGB images with transparency to full alpha
* channels so the data will be available as RGBA quartets.
*/
- if (transforms & PNG_TRANSFORM_EXPAND)
+ if ((transforms & PNG_TRANSFORM_EXPAND) != 0)
#ifdef PNG_READ_EXPAND_SUPPORTED
png_set_expand(png_ptr);
#else
png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND not supported");
#endif
- /* We don't handle background color or gamma transformation or quantizing.
- */
+ /* We don't handle background color, gamma transformation, or quantizing. */
- /* Invert monochrome files to have 0 as white and 1 as black
- */
- if (transforms & PNG_TRANSFORM_INVERT_MONO)
+ /* Invert monochrome files to have 0 as white and 1 as black */
+ if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
#ifdef PNG_READ_INVERT_SUPPORTED
png_set_invert_mono(png_ptr);
#else
@@ -1107,16 +877,16 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
* [0,65535] to the original [0,7] or [0,31], or whatever range the
* colors were originally in:
*/
- if (transforms & PNG_TRANSFORM_SHIFT)
+ if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
#ifdef PNG_READ_SHIFT_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sBIT)
+ if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
png_set_shift(png_ptr, &info_ptr->sig_bit);
#else
png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
#endif
/* Flip the RGB pixels to BGR (or RGBA to BGRA) */
- if (transforms & PNG_TRANSFORM_BGR)
+ if ((transforms & PNG_TRANSFORM_BGR) != 0)
#ifdef PNG_READ_BGR_SUPPORTED
png_set_bgr(png_ptr);
#else
@@ -1124,7 +894,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
- if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+ if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
png_set_swap_alpha(png_ptr);
#else
@@ -1132,7 +902,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Swap bytes of 16-bit files to least significant byte first */
- if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+ if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
#ifdef PNG_READ_SWAP_SUPPORTED
png_set_swap(png_ptr);
#else
@@ -1141,7 +911,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Added at libpng-1.2.41 */
/* Invert the alpha channel from opacity to transparency */
- if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+ if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
png_set_invert_alpha(png_ptr);
#else
@@ -1150,7 +920,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
/* Added at libpng-1.2.41 */
/* Expand grayscale image to RGB */
- if (transforms & PNG_TRANSFORM_GRAY_TO_RGB)
+ if ((transforms & PNG_TRANSFORM_GRAY_TO_RGB) != 0)
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
png_set_gray_to_rgb(png_ptr);
#else
@@ -1158,7 +928,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Added at libpng-1.5.4 */
- if (transforms & PNG_TRANSFORM_EXPAND_16)
+ if ((transforms & PNG_TRANSFORM_EXPAND_16) != 0)
#ifdef PNG_READ_EXPAND_16_SUPPORTED
png_set_expand_16(png_ptr);
#else
@@ -1184,6 +954,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->row_pointers == NULL)
{
png_uint_32 iptr;
+ png_alloc_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr,
info_ptr->height * (sizeof (png_bytep))));
@@ -1195,7 +966,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
for (iptr = 0; iptr < info_ptr->height; iptr++)
info_ptr->row_pointers[iptr] = png_voidcast(png_bytep,
- png_malloc(png_ptr, info_ptr->rowbytes));
+ png_malloc(png_ptr, rowbytes));
}
png_read_image(png_ptr, info_ptr->row_pointers);
@@ -1206,8 +977,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
PNG_UNUSED(params)
}
-#endif /* PNG_INFO_IMAGE_SUPPORTED */
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* READ_PNG */
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
/* SIMPLIFIED READ
@@ -1223,9 +993,10 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
# define P_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */
# define P_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */
# define P_LINEAR8 4 /* 8-bit linear: only from a file value */
+# define P_FILE8 5 /* 8-bit encoded to file gamma but not significant bits */
/* Color-map processing: after libpng has run on the PNG image further
- * processing may be needed to conver the data to color-map indicies.
+ * processing may be needed to convert the data to color-map indices.
*/
#define PNG_CMAP_NONE 0
#define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */
@@ -1245,7 +1016,7 @@ typedef struct
/* Arguments: */
png_imagep image;
png_voidp buffer;
- png_int_32 row_stride;
+ ptrdiff_t row_stride;
png_voidp colormap;
png_const_colorp background;
/* Local variables: */
@@ -1253,8 +1024,9 @@ typedef struct
png_voidp first_row;
ptrdiff_t row_bytes; /* step between rows */
int file_encoding; /* E_ values above */
- png_fixed_point gamma_to_linear; /* For P_FILE, reciprocal of gamma */
+ png_fixed_point file_to_sRGB; /* Cached correction factor */
int colormap_processing; /* PNG_CMAP_ values above */
+ png_byte sBIT[4]; /* Significant bits for channels */
} png_image_read_control;
/* Do all the *safe* initialization - 'safe' means that png_error won't be
@@ -1283,7 +1055,7 @@ png_image_read_init(png_imagep image)
if (info_ptr != NULL)
{
png_controlp control = png_voidcast(png_controlp,
- png_malloc_warn(png_ptr, (sizeof *control)));
+ png_malloc_warn(png_ptr, (sizeof *control)));
if (control != NULL)
{
@@ -1316,10 +1088,10 @@ png_image_format(png_structrp png_ptr)
{
png_uint_32 format = 0;
- if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
format |= PNG_FORMAT_FLAG_COLOR;
- if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
format |= PNG_FORMAT_FLAG_ALPHA;
/* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS
@@ -1333,30 +1105,19 @@ png_image_format(png_structrp png_ptr)
if (png_ptr->bit_depth == 16)
format |= PNG_FORMAT_FLAG_LINEAR;
- if (png_ptr->color_type & PNG_COLOR_MASK_PALETTE)
+ if ((png_ptr->color_type & PNG_COLOR_MASK_PALETTE) != 0)
format |= PNG_FORMAT_FLAG_COLORMAP;
return format;
}
-/* Is the given gamma significantly different from sRGB? The test is the same
- * one used in pngrtran.c when deciding whether to do gamma correction. The
- * arithmetic optimizes the division by using the fact that the inverse of the
- * file sRGB gamma is 2.2
+/* Is the given gamma significantly different from sRGB?
*/
static int
png_gamma_not_sRGB(png_fixed_point g)
{
- if (g < PNG_FP_1)
- {
- /* An uninitialized gamma is assumed to be sRGB for the simplified API. */
- if (g == 0)
- return 0;
-
- return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);
- }
-
- return 1;
+ /* An uninitialized gamma is assumed to be sRGB for the simplified API. */
+ return g != 0 && !PNG_GAMMA_IS_sRGB(g);
}
/* Do the main body of a 'png_image_begin_read' function; read the PNG file
@@ -1370,7 +1131,9 @@ png_image_read_header(png_voidp argument)
png_structrp png_ptr = image->opaque->png_ptr;
png_inforp info_ptr = image->opaque->info_ptr;
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
png_set_benign_errors(png_ptr, 1/*warn*/);
+#endif
png_read_info(png_ptr, info_ptr);
/* Do this the fast way; just read directly out of png_struct. */
@@ -1425,6 +1188,47 @@ png_image_read_header(png_voidp argument)
return 1;
}
+static void
+png_image_get_sBIT(png_image_read_control *display)
+ /* Utility to cache the sBIT values. This uses the information from the
+ * png_struct not png_info because it may be needed after the sBIT
+ * information in png_info has been invalidated.
+ */
+{
+ if (display->sBIT[0] == 0)
+ {
+ const png_const_structrp png_ptr = display->image->opaque->png_ptr;
+ const unsigned int color_type = png_ptr->color_type;
+ const png_byte bit_depth =
+ (color_type & PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth;
+
+ memset(display->sBIT, bit_depth, sizeof display->sBIT);
+
+ if (color_type & PNG_COLOR_MASK_COLOR)
+ {
+ if (png_ptr->sig_bit.red > 0 && png_ptr->sig_bit.red < bit_depth)
+ display->sBIT[0] = png_ptr->sig_bit.red;
+ if (png_ptr->sig_bit.green > 0 && png_ptr->sig_bit.green < bit_depth)
+ display->sBIT[1] = png_ptr->sig_bit.green;
+ if (png_ptr->sig_bit.blue > 0 && png_ptr->sig_bit.blue < bit_depth)
+ display->sBIT[2] = png_ptr->sig_bit.blue;
+ }
+
+ else
+ {
+ if (png_ptr->sig_bit.gray > 0 && png_ptr->sig_bit.gray < bit_depth)
+ display->sBIT[2] = display->sBIT[1] = display->sBIT[0] =
+ png_ptr->sig_bit.gray;
+ }
+
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ if (png_ptr->sig_bit.alpha > 0 && png_ptr->sig_bit.alpha < bit_depth)
+ display->sBIT[3] = png_ptr->sig_bit.alpha;
+ }
+ }
+}
+
#ifdef PNG_STDIO_SUPPORTED
int PNGAPI
png_image_begin_read_from_stdio(png_imagep image, FILE* file)
@@ -1433,25 +1237,19 @@ png_image_begin_read_from_stdio(png_imagep image, FILE* file)
{
if (file != NULL)
{
- if (png_image_read_init(image))
- {
- /* This is slightly evil, but png_init_io doesn't do anything other
- * than this and we haven't changed the standard IO functions so
- * this saves a 'safe' function.
- */
- image->opaque->png_ptr->io_ptr = file;
+ if (png_image_read_init(image) != 0 &&
+ png_image_init_io(image, file) != 0)
return png_safe_execute(image, png_image_read_header, image);
- }
}
else
return png_image_error(image,
- "png_image_begin_read_from_stdio: invalid argument");
+ "png_image_begin_read_from_stdio: invalid argument");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
+ "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
return 0;
}
@@ -1467,9 +1265,9 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name)
if (fp != NULL)
{
- if (png_image_read_init(image))
+ if (png_image_read_init(image) != 0 &&
+ png_image_init_io(image, fp) != 0)
{
- image->opaque->png_ptr->io_ptr = fp;
image->opaque->owned_file = 1;
return png_safe_execute(image, png_image_read_header, image);
}
@@ -1484,16 +1282,16 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name)
else
return png_image_error(image,
- "png_image_begin_read_from_file: invalid argument");
+ "png_image_begin_read_from_file: invalid argument");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
+ "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
return 0;
}
-#endif /* PNG_STDIO_SUPPORTED */
+#endif /* STDIO */
static void PNGCBAPI
png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need)
@@ -1525,14 +1323,26 @@ png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need)
}
}
-int PNGAPI png_image_begin_read_from_memory(png_imagep image,
- png_const_voidp memory, png_size_t size)
+static int
+image_init_memory_io(png_voidp param)
+ /* Set the read function and pointer for a memory read, the io pointer is
+ * just the imagep so it is passed in directly.
+ */
+{
+ png_imagep image = png_voidcast(png_imagep, param);
+ png_set_read_fn(image->opaque->png_ptr, image, png_image_memory_read);
+ return 1;
+}
+
+int PNGAPI
+png_image_begin_read_from_memory(png_imagep image, png_const_voidp memory,
+ png_size_t size)
{
if (image != NULL && image->version == PNG_IMAGE_VERSION)
{
if (memory != NULL && size > 0)
{
- if (png_image_read_init(image))
+ if (png_image_read_init(image) != 0)
{
/* Now set the IO functions to read from the memory buffer and
* store it into io_ptr. Again do this in-place to avoid calling a
@@ -1540,21 +1350,20 @@ int PNGAPI png_image_begin_read_from_memory(png_imagep image,
*/
image->opaque->memory = png_voidcast(png_const_bytep, memory);
image->opaque->size = size;
- image->opaque->png_ptr->io_ptr = image;
- image->opaque->png_ptr->read_data_fn = png_image_memory_read;
- return png_safe_execute(image, png_image_read_header, image);
+ return png_safe_execute(image, image_init_memory_io, image) &&
+ png_safe_execute(image, png_image_read_header, image);
}
}
else
return png_image_error(image,
- "png_image_begin_read_from_memory: invalid argument");
+ "png_image_begin_read_from_memory: invalid argument");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
+ "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
return 0;
}
@@ -1600,18 +1409,18 @@ png_image_skip_unused_chunks(png_structrp png_ptr)
* IHDR, PLTE, tRNS, IDAT, and IEND chunks.
*/
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER,
- NULL, -1);
+ NULL, -1);
/* But do not ignore image data handling chunks */
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT,
- chunks_to_process, (sizeof chunks_to_process)/5);
- }
+ chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5);
+ }
}
# define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)
#else
# define PNG_SKIP_CHUNKS(p) ((void)0)
-#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */
+#endif /* HANDLE_AS_UNKNOWN */
/* The following macro gives the exact rounded answer for all values in the
* range 0..255 (it actually divides by 51.2, but the rounding still generates
@@ -1623,14 +1432,15 @@ png_image_skip_unused_chunks(png_structrp png_ptr)
static void
set_file_encoding(png_image_read_control *display)
{
- png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;
- if (png_gamma_significant(g))
+ /* First test for an encoding close to linear: */
+ if (png_need_gamma_correction(display->image->opaque->png_ptr,
+ 0/*PNG gamma*/, 0/*not sRGB*/))
{
+ png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;
+
+ /* Now look for one close to sRGB: */
if (png_gamma_not_sRGB(g))
- {
display->file_encoding = P_FILE;
- display->gamma_to_linear = png_reciprocal(g);
- }
else
display->file_encoding = P_sRGB;
@@ -1640,11 +1450,138 @@ set_file_encoding(png_image_read_control *display)
display->file_encoding = P_LINEAR8;
}
-static unsigned int
-decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)
+/* For colormap entries we end up doing the gamma correction here and the
+ * following routines are provided to separate out the code. In all cases the
+ * input value is in the range 0..255 and is encoded P_FILE with the gamma value
+ * stored in the png_struct colorspace.
+ */
+static void
+init_correct(png_const_structrp png_ptr, png_fixed_point *correct)
{
+ /* Record the convertion necessary to get from the encoding values to
+ * sRGB. If this overflows just store FP_1.
+ *
+ * NOTE: this code used to store, and use, a convertion factor to
+ * linear then use the sRGB encoding tables to get back to sRGB, but
+ * this smashes the low values; the ones which fall in the linear part
+ * of the sRGB transfer function.
+ *
+ * The new version of this code assumes an encoding which is neither
+ * linear nor sRGB is a power law transform of the sRGB curve, not
+ * linear values. This is somewhat at odds with a precise reading of
+ * the PNG spec, but given that we are trying to produce sRGB values
+ * here it is most likely to be correct.
+ */
+ affirm(png_ptr->colorspace.gamma > 0);
+
+ if (!png_muldiv(correct, PNG_GAMMA_sRGB_INVERSE, PNG_FP_1,
+ png_ptr->colorspace.gamma))
+ *correct = PNG_FP_1;
+}
+
+static png_uint_32
+update_for_sBIT(png_uint_32 value, unsigned int significant_bits,
+ unsigned int bit_depth)
+ /* Return a bit_depth value adjusted for the number of significant bits in
+ * the value.
+ */
+{
+ if (significant_bits < bit_depth)
+ {
+ value >>= bit_depth - significant_bits;
+ /* Now scale back to bit_depth, taking care not to overflow when 'value'
+ * is (1<image->opaque->png_ptr;
+
+ debug(value <= 255U && significant_bits <= 8U && significant_bits > 0U);
+
+ if (display->file_to_sRGB == 0)
+ init_correct(png_ptr, &display->file_to_sRGB);
+
+ /* Now simply apply this correction factor and scale back to 8 bits. */
+ if (display->file_to_sRGB != PNG_FP_1)
+ value = png_gamma_nxmbit_correct(value >> (8U-significant_bits),
+ display->file_to_sRGB, significant_bits, 8U);
+
+ else if (significant_bits < 8U)
+ value = update_for_sBIT(value, significant_bits, 8U);
+
+ return value;
+}
+
+static png_uint_32
+convert_to_linear(png_image_read_control *display, png_uint_32 value,
+ unsigned int significant_bits)
+{
+ /* Converts an 8-bit value from P_FILE to 16-bit P_LINEAR */
+ png_const_structrp png_ptr = display->image->opaque->png_ptr;
+
+ debug(value <= 255U && significant_bits <= 8U && significant_bits > 0U);
+
+ if (display->file_to_sRGB == 0)
+ init_correct(png_ptr, &display->file_to_sRGB);
+
+ /* Use this correction to get a 16-bit sRGB value: */
+ if (display->file_to_sRGB != PNG_FP_1)
+ value = png_gamma_nxmbit_correct(value >> (8U-significant_bits),
+ display->file_to_sRGB, significant_bits, 16U);
+
+ else
+ {
+ value *= 257U;
+
+ if (significant_bits < 8U)
+ value = update_for_sBIT(value, significant_bits, 16U);
+ }
+
+ /* Now convert this back to linear, using the correct transfer function. */
+ if (value <= 2650U /* 65535 * 0.04045 */)
+ {
+ /* We want to divide a 12-bit number by 12.92, do this by scaling to 32
+ * bits then dividing by 2^24, with rounding:
+ */
+ value = (value * 1298546U + 649273U) >> 24;
+ }
+
+ else
+ {
+ /* Calculate for v in the range 0.04045..1.0 calculate:
+ *
+ * ((v + 0.055)/1.055)^2.4
+ *
+ * the gamma correction function needs a 16-bit value:
+ */
+ value *= 62119U;
+ value += 223904831U+32768U; /* cannot overflow; test with 65535 */
+ value = png_gamma_nxmbit_correct(value >> 16, 240000, 16U, 16U);
+ }
+
+ return value;
+}
+
+static unsigned int
+decode_gamma(png_image_read_control *display, png_uint_32 value,
+ unsigned int significant_bits, int encoding)
+{
+ int do_sBIT = 0;
+
if (encoding == P_FILE) /* double check */
- encoding = display->file_encoding;
+ encoding = display->file_encoding, do_sBIT = 1;
if (encoding == P_NOTSET) /* must be the file encoding */
{
@@ -1655,23 +1592,31 @@ decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)
switch (encoding)
{
case P_FILE:
- value = png_gamma_16bit_correct(value*257, display->gamma_to_linear);
+ /* This is a file value, so the sBIT, if any, needs to be used. */
+ value = convert_to_linear(display, value, significant_bits);
break;
case P_sRGB:
+ if (do_sBIT)
+ value = update_for_sBIT(value, significant_bits, 8U);
+
value = png_sRGB_table[value];
break;
case P_LINEAR:
+ if (do_sBIT)
+ value = update_for_sBIT(value, significant_bits, 16U);
break;
case P_LINEAR8:
value *= 257;
+ if (do_sBIT)
+ value = update_for_sBIT(value, significant_bits, 16U);
break;
default:
- png_error(display->image->opaque->png_ptr,
- "unexpected encoding (internal error)");
+ png_impossiblepp(display->image->opaque->png_ptr,
+ "unexpected encoding");
break;
}
@@ -1680,16 +1625,18 @@ decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)
static png_uint_32
png_colormap_compose(png_image_read_control *display,
- png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,
- png_uint_32 background, int encoding)
+ png_uint_32 foreground, unsigned int foreground_significant_bits,
+ int foreground_encoding, png_uint_32 alpha,
+ png_uint_32 background, int encoding)
{
/* The file value is composed on the background, the background has the given
* encoding and so does the result, the file is encoded with P_FILE and the
* file and alpha are 8-bit values. The (output) encoding will always be
* P_LINEAR or P_sRGB.
*/
- png_uint_32 f = decode_gamma(display, foreground, foreground_encoding);
- png_uint_32 b = decode_gamma(display, background, encoding);
+ png_uint_32 f = decode_gamma(display, foreground,
+ foreground_significant_bits, foreground_encoding);
+ png_uint_32 b = decode_gamma(display, background, 0U/*UNUSED*/, encoding);
/* The alpha is always an 8-bit value (it comes from the palette), the value
* scaled by 255 is what PNG_sRGB_FROM_LINEAR requires.
@@ -1707,7 +1654,7 @@ png_colormap_compose(png_image_read_control *display,
}
else /* P_sRGB */
- f = PNG_sRGB_FROM_LINEAR(f);
+ f = PNG_sRGB_FROM_LINEAR(display->image->opaque->png_ptr, f);
return f;
}
@@ -1717,17 +1664,25 @@ png_colormap_compose(png_image_read_control *display,
*/
static void
png_create_colormap_entry(png_image_read_control *display,
- png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
- png_uint_32 alpha, int encoding)
+ png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
+ png_uint_32 alpha, int encoding)
{
png_imagep image = display->image;
- const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) ?
+# define png_ptr image->opaque->png_ptr /* for error messages */
+ const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
P_LINEAR : P_sRGB;
const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&
- (red != green || green != blue);
+ (red != green || green != blue);
+ int use_sBIT = encoding == P_FILE;
- if (ip > 255)
- png_error(image->opaque->png_ptr, "color-map index out of range");
+ affirm(ip <= 255);
+ implies(encoding != P_LINEAR, red <= 255U && green <= 255U && blue <= 255U
+ && display->sBIT[0] <= 8U && display->sBIT[1] <= 8U
+ && display->sBIT[2] <= 8U && display->sBIT[3] <= 8U);
+
+ /* This is a hack for the grayscale colormap below. */
+ if (encoding == P_FILE8)
+ encoding = P_FILE;
/* Update the cache with whether the file gamma is significantly different
* from sRGB.
@@ -1737,67 +1692,112 @@ png_create_colormap_entry(png_image_read_control *display,
if (display->file_encoding == P_NOTSET)
set_file_encoding(display);
- /* Note that the cached value may be P_FILE too, but if it is then the
- * gamma_to_linear member has been set.
- */
+ /* Note that the cached value may be P_FILE too. */
encoding = display->file_encoding;
+ if (use_sBIT)
+ png_image_get_sBIT(display);
}
if (encoding == P_FILE)
{
- png_fixed_point g = display->gamma_to_linear;
-
- red = png_gamma_16bit_correct(red*257, g);
- green = png_gamma_16bit_correct(green*257, g);
- blue = png_gamma_16bit_correct(blue*257, g);
-
- if (convert_to_Y || output_encoding == P_LINEAR)
+ if (convert_to_Y != 0 || output_encoding == P_LINEAR)
{
- alpha *= 257;
+ red = convert_to_linear(display, red,
+ use_sBIT ? display->sBIT[0] : 8U);
+ green = convert_to_linear(display, green,
+ use_sBIT ? display->sBIT[1] : 8U);
+ blue = convert_to_linear(display, blue,
+ use_sBIT ? display->sBIT[2] : 8U);
+ alpha *= 257U;
+ if (use_sBIT)
+ alpha = update_for_sBIT(alpha, display->sBIT[3], 16U);
+
encoding = P_LINEAR;
+ use_sBIT = 0;
}
else
{
- red = PNG_sRGB_FROM_LINEAR(red * 255);
- green = PNG_sRGB_FROM_LINEAR(green * 255);
- blue = PNG_sRGB_FROM_LINEAR(blue * 255);
+ red = convert_to_sRGB(display, red,
+ use_sBIT ? display->sBIT[0] : 8U);
+ green = convert_to_sRGB(display, green,
+ use_sBIT ? display->sBIT[1] : 8U);
+ blue = convert_to_sRGB(display, blue,
+ use_sBIT ? display->sBIT[2] : 8U);
+ if (use_sBIT)
+ alpha = update_for_sBIT(alpha, display->sBIT[3], 8U);
encoding = P_sRGB;
+ use_sBIT = 0;
}
}
else if (encoding == P_LINEAR8)
{
- /* This encoding occurs quite frequently in test cases because PngSuite
- * includes a gAMA 1.0 chunk with most images.
+ /* This encoding corresponds to a colormap with linear RGB entries, this
+ * is not a very sensible encoding but it does happen with the PNGSuite
+ * test images.
*/
red *= 257;
green *= 257;
blue *= 257;
alpha *= 257;
+ if (use_sBIT)
+ {
+ red = update_for_sBIT(red, display->sBIT[0], 16U);
+ green = update_for_sBIT(green, display->sBIT[1], 16U);
+ blue = update_for_sBIT(blue, display->sBIT[2], 16U);
+ alpha = update_for_sBIT(alpha, display->sBIT[3], 16U);
+ use_sBIT = 0;
+ }
encoding = P_LINEAR;
}
- else if (encoding == P_sRGB && (convert_to_Y || output_encoding == P_LINEAR))
+ else if (encoding == P_sRGB &&
+ (convert_to_Y != 0 || output_encoding == P_LINEAR))
{
/* The values are 8-bit sRGB values, but must be converted to 16-bit
* linear.
*/
- red = png_sRGB_table[red];
- green = png_sRGB_table[green];
- blue = png_sRGB_table[blue];
- alpha *= 257;
+ if (use_sBIT)
+ {
+
+ red = convert_to_linear(display, red, display->sBIT[0]);
+ green = convert_to_linear(display, green, display->sBIT[1]);
+ blue = convert_to_linear(display, blue, display->sBIT[2]);
+ alpha = update_for_sBIT(alpha * 257U, display->sBIT[3], 16U);
+ use_sBIT = 0;
+ }
+
+ else
+ {
+ red = png_sRGB_table[red];
+ green = png_sRGB_table[green];
+ blue = png_sRGB_table[blue];
+ alpha *= 257;
+ }
+
encoding = P_LINEAR;
}
+ else if (encoding == P_sRGB && use_sBIT)
+ {
+ debug(output_encoding == P_sRGB); /* P_LINEAR handled above */
+ red = update_for_sBIT(red, display->sBIT[0], 8U);
+ green = update_for_sBIT(green, display->sBIT[1], 8U);
+ blue = update_for_sBIT(blue, display->sBIT[2], 8U);
+ alpha = update_for_sBIT(alpha, display->sBIT[3], 8U);
+ use_sBIT = 0;
+ }
+
+ debug(!use_sBIT); /* it should have been handled above */
+
/* This is set if the color isn't gray but the output is. */
if (encoding == P_LINEAR)
{
if (convert_to_Y != 0)
{
/* NOTE: these values are copied from png_do_rgb_to_gray */
- png_uint_32 y = (png_uint_32)6968 * red + (png_uint_32)23434 * green +
- (png_uint_32)2366 * blue;
+ png_uint_32 y = 6968 * red + 23434 * green + 2366 * blue;
if (output_encoding == P_LINEAR)
y = (y + 16384) >> 15;
@@ -1807,7 +1807,8 @@ png_create_colormap_entry(png_image_read_control *display,
/* y is scaled by 32768, we need it scaled by 255: */
y = (y + 128) >> 8;
y *= 255;
- y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7);
+ y = PNG_sRGB_FROM_LINEAR(png_ptr, (y + 64) >> 7);
+ alpha = PNG_DIV257(alpha);
encoding = P_sRGB;
}
@@ -1816,30 +1817,30 @@ png_create_colormap_entry(png_image_read_control *display,
else if (output_encoding == P_sRGB)
{
- red = PNG_sRGB_FROM_LINEAR(red * 255);
- green = PNG_sRGB_FROM_LINEAR(green * 255);
- blue = PNG_sRGB_FROM_LINEAR(blue * 255);
+ red = PNG_sRGB_FROM_LINEAR(png_ptr, red * 255);
+ green = PNG_sRGB_FROM_LINEAR(png_ptr, green * 255);
+ blue = PNG_sRGB_FROM_LINEAR(png_ptr, blue * 255);
alpha = PNG_DIV257(alpha);
encoding = P_sRGB;
}
}
if (encoding != output_encoding)
- png_error(image->opaque->png_ptr, "bad encoding (internal error)");
+ png_impossiblepp(png_ptr, "bad encoding");
/* Store the value. */
{
-# ifdef PNG_FORMAT_AFIRST_SUPPORTED
- const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
- (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
-# else
-# define afirst 0
-# endif
-# ifdef PNG_FORMAT_BGR_SUPPORTED
- const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;
-# else
-# define bgr 0
-# endif
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
+ (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
+# else
+# define afirst 0
+# endif
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
+# else
+# define bgr 0
+# endif
if (output_encoding == P_LINEAR)
{
@@ -1854,7 +1855,7 @@ png_create_colormap_entry(png_image_read_control *display,
switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
{
case 4:
- entry[afirst ? 0 : 3] = (png_uint_16)alpha;
+ entry[afirst ? 0 : 3] = png_check_u16(png_ptr, alpha);
/* FALL THROUGH */
case 3:
@@ -1870,13 +1871,13 @@ png_create_colormap_entry(png_image_read_control *display,
else
red = green = blue = 0;
}
- entry[afirst + (2 ^ bgr)] = (png_uint_16)blue;
- entry[afirst + 1] = (png_uint_16)green;
- entry[afirst + bgr] = (png_uint_16)red;
+ entry[afirst + (2 ^ bgr)] = png_check_u16(png_ptr, blue);
+ entry[afirst + 1] = png_check_u16(png_ptr, green);
+ entry[afirst + bgr] = png_check_u16(png_ptr, red);
break;
case 2:
- entry[1 ^ afirst] = (png_uint_16)alpha;
+ entry[1 ^ afirst] = png_check_u16(png_ptr, alpha);
/* FALL THROUGH */
case 1:
@@ -1888,7 +1889,7 @@ png_create_colormap_entry(png_image_read_control *display,
else
green = 0;
}
- entry[afirst] = (png_uint_16)green;
+ entry[afirst] = png_check_u16(png_ptr, green);
break;
default:
@@ -1902,20 +1903,24 @@ png_create_colormap_entry(png_image_read_control *display,
entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);
+ png_affirmpp(png_ptr, output_encoding == P_sRGB);
+
switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
{
case 4:
- entry[afirst ? 0 : 3] = (png_byte)alpha;
+ entry[afirst ? 0 : 3] = png_check_byte(png_ptr, alpha);
+
case 3:
- entry[afirst + (2 ^ bgr)] = (png_byte)blue;
- entry[afirst + 1] = (png_byte)green;
- entry[afirst + bgr] = (png_byte)red;
+ entry[afirst + (2 ^ bgr)] = png_check_byte(png_ptr, blue);
+ entry[afirst + 1] = png_check_byte(png_ptr, green);
+ entry[afirst + bgr] = png_check_byte(png_ptr, red);
break;
case 2:
- entry[1 ^ afirst] = (png_byte)alpha;
+ entry[1 ^ afirst] = png_check_byte(png_ptr, alpha);
+
case 1:
- entry[afirst] = (png_byte)green;
+ entry[afirst] = png_check_byte(png_ptr, green);
break;
default:
@@ -1923,13 +1928,15 @@ png_create_colormap_entry(png_image_read_control *display,
}
}
-# ifdef afirst
-# undef afirst
-# endif
-# ifdef bgr
-# undef bgr
-# endif
+# ifdef afirst
+# undef afirst
+# endif
+# ifdef bgr
+# undef bgr
+# endif
}
+
+# undef png_ptr
}
static int
@@ -1938,7 +1945,7 @@ make_gray_file_colormap(png_image_read_control *display)
unsigned int i;
for (i=0; i<256; ++i)
- png_create_colormap_entry(display, i, i, i, i, 255, P_FILE);
+ png_create_colormap_entry(display, i, i, i, i, 255, P_FILE8);
return i;
}
@@ -2002,7 +2009,7 @@ make_ga_colormap(png_image_read_control *display)
for (g=0; g<6; ++g)
png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51,
- P_sRGB);
+ P_sRGB);
}
return i;
@@ -2026,7 +2033,7 @@ make_rgb_colormap(png_image_read_control *display)
for (b=0; b<6; ++b)
png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255,
- P_sRGB);
+ P_sRGB);
}
}
@@ -2037,7 +2044,8 @@ make_rgb_colormap(png_image_read_control *display)
/* Return a palette index to the above palette given three 8-bit sRGB values. */
#define PNG_RGB_INDEX(r,g,b) \
- ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b)))
+ (png_check_byte(image->opaque->png_ptr,\
+ 6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b)))
static int
png_image_read_colormap(png_voidp argument)
@@ -2048,7 +2056,7 @@ png_image_read_colormap(png_voidp argument)
const png_structrp png_ptr = image->opaque->png_ptr;
const png_uint_32 output_format = image->format;
- const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) ?
+ const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
P_LINEAR : P_sRGB;
unsigned int cmap_entries;
@@ -2078,7 +2086,7 @@ png_image_read_colormap(png_voidp argument)
else if (display->background == NULL /* no way to remove it */)
png_error(png_ptr,
- "a background color must be supplied to remove alpha/transparency");
+ "background color must be supplied to remove alpha/transparency");
/* Get a copy of the background color (this avoids repeating the checks
* below.) The encoding is 8-bit sRGB or 16-bit linear, depending on the
@@ -2087,7 +2095,7 @@ png_image_read_colormap(png_voidp argument)
else
{
back_g = display->background->green;
- if (output_format & PNG_FORMAT_FLAG_COLOR)
+ if ((output_format & PNG_FORMAT_FLAG_COLOR) != 0)
{
back_r = display->background->red;
back_b = display->background->blue;
@@ -2122,7 +2130,12 @@ png_image_read_colormap(png_voidp argument)
else
png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE;
- png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+ /* Make sure libpng doesn't ignore the setting: */
+ if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
+ png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA;
+
+ else
+ png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
}
/* Decide what to do based on the PNG color type of the input data. The
@@ -2173,7 +2186,7 @@ png_image_read_colormap(png_voidp argument)
*/
if (i != trans)
png_create_colormap_entry(display, i, val, val, val, 255,
- P_FILE/*8-bit with file gamma*/);
+ P_FILE/*8-bit with file gamma*/);
/* Else this entry is transparent. The colors don't matter if
* there is an alpha channel (back_alpha == 0), but it does no
@@ -2184,15 +2197,22 @@ png_image_read_colormap(png_voidp argument)
* supplied background color when it is used.
*/
else
+ {
+#ifdef __COVERITY__
+ /* Coverity says back_r|g|b might be 16-bit values */
+ png_affirmpp(png_ptr, back_r < 256 && back_g < 256 &&
+ back_b < 256);
+#endif
png_create_colormap_entry(display, i, back_r, back_g, back_b,
- back_alpha, output_encoding);
+ back_alpha, output_encoding);
+ }
}
/* We need libpng to preserve the original encoding. */
data_encoding = P_FILE;
/* The rows from libpng, while technically gray values, are now also
- * color-map indicies; however, they may need to be expanded to 1
+ * color-map indices; however, they may need to be expanded to 1
* byte per pixel. This is what png_set_packing does (i.e., it
* unpacks the bit values into bytes.)
*/
@@ -2229,7 +2249,7 @@ png_image_read_colormap(png_voidp argument)
{
unsigned int back_alpha;
- if (output_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
back_alpha = 0;
else
@@ -2244,42 +2264,46 @@ png_image_read_colormap(png_voidp argument)
if (output_encoding == P_LINEAR)
{
- gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+ gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255);
/* And make sure the corresponding palette entry
* matches.
*/
png_create_colormap_entry(display, gray, back_g, back_g,
- back_g, 65535, P_LINEAR);
+ back_g, 65535, P_LINEAR);
}
/* The background passed to libpng, however, must be the
* sRGB value.
*/
c.index = 0; /*unused*/
- c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+ c.gray = c.red = c.green = c.blue =
+ png_check_u16(png_ptr, gray);
/* NOTE: does this work without expanding tRNS to alpha?
* It should be the color->gray case below apparently
* doesn't.
*/
png_set_background_fixed(png_ptr, &c,
- PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
- 0/*gamma: not used*/);
+ PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+ 0/*gamma: not used*/);
output_processing = PNG_CMAP_NONE;
break;
}
-
- back_alpha = output_encoding == P_LINEAR ? 65535 : 255;
+ /* Coverity claims that output_encoding cannot be 2 (P_LINEAR)
+ * here.
+ */
+ affirm(output_encoding != P_LINEAR);
+ back_alpha = 255U;
}
/* output_processing means that the libpng-processed row will be
* 8-bit GA and it has to be processing to single byte color-map
* values. Entry 254 is replaced by either a completely
* transparent entry or by the background color at full
- * precision (and the background color is not a simple gray leve
- * in this case.)
+ * precision (and the background color is not a simple gray
+ * level in this case.)
*/
expand_tRNS = 1;
output_processing = PNG_CMAP_TRANS;
@@ -2288,8 +2312,13 @@ png_image_read_colormap(png_voidp argument)
/* And set (overwrite) color-map entry 254 to the actual
* background color at full precision.
*/
+#ifdef __COVERITY__
+ /* Coverity says back_r|g|b might be 16-bit values */
+ png_affirmpp(png_ptr, back_r < 256 && back_g < 256 &&
+ back_b < 256);
+#endif
png_create_colormap_entry(display, 254, back_r, back_g, back_b,
- back_alpha, output_encoding);
+ back_alpha, output_encoding);
}
else
@@ -2310,7 +2339,7 @@ png_image_read_colormap(png_voidp argument)
*/
data_encoding = P_sRGB;
- if (output_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
png_error(png_ptr, "gray+alpha color-map: too few entries");
@@ -2353,22 +2382,22 @@ png_image_read_colormap(png_voidp argument)
if (output_encoding == P_LINEAR)
{
- gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+ gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255);
/* And make sure the corresponding palette entry matches. */
png_create_colormap_entry(display, gray, back_g, back_g,
- back_g, 65535, P_LINEAR);
+ back_g, 65535, P_LINEAR);
}
/* The background passed to libpng, however, must be the sRGB
* value.
*/
c.index = 0; /*unused*/
- c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+ c.gray = c.red = c.green = c.blue = png_check_u16(png_ptr, gray);
png_set_background_fixed(png_ptr, &c,
- PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
- 0/*gamma: not used*/);
+ PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+ 0/*gamma: not used*/);
output_processing = PNG_CMAP_NONE;
}
@@ -2388,15 +2417,18 @@ png_image_read_colormap(png_voidp argument)
{
png_uint_32 gray = (i * 256 + 115) / 231;
png_create_colormap_entry(display, i++, gray, gray, gray,
- 255, P_sRGB);
+ 255, P_sRGB);
}
/* NOTE: this preserves the full precision of the application
* background color.
+ *
+ * Coverity claims that output_encoding cannot be 2 (P_LINEAR)
*/
+ affirm(output_encoding != P_LINEAR);
background_index = i;
png_create_colormap_entry(display, i++, back_r, back_g, back_b,
- output_encoding == P_LINEAR ? 65535U : 255U, output_encoding);
+ 255U, output_encoding);
/* For non-opaque input composite on the sRGB background - this
* requires inverting the encoding for each component. The input
@@ -2434,9 +2466,10 @@ png_image_read_colormap(png_voidp argument)
png_uint_32 gray = png_sRGB_table[g*51] * alpha;
png_create_colormap_entry(display, i++,
- PNG_sRGB_FROM_LINEAR(gray + back_rx),
- PNG_sRGB_FROM_LINEAR(gray + back_gx),
- PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB);
+ PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_rx),
+ PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_gx),
+ PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_bx),
+ 255, P_sRGB);
}
}
@@ -2462,7 +2495,7 @@ png_image_read_colormap(png_voidp argument)
* png_set_tRNS_to_alpha before png_set_background_fixed.
*/
png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1,
- -1);
+ -1);
data_encoding = P_sRGB;
/* The output will now be one or two 8-bit gray or gray+alpha
@@ -2505,7 +2538,7 @@ png_image_read_colormap(png_voidp argument)
*/
if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
png_ptr->num_trans > 0) &&
- png_gamma_not_sRGB(png_ptr->colorspace.gamma))
+ png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0)
{
cmap_entries = make_gray_file_colormap(display);
data_encoding = P_FILE;
@@ -2537,8 +2570,8 @@ png_image_read_colormap(png_voidp argument)
if (output_encoding == P_sRGB)
gray = png_sRGB_table[gray]; /* now P_LINEAR */
- gray = PNG_DIV257(png_gamma_16bit_correct(gray,
- png_ptr->colorspace.gamma)); /* now P_FILE */
+ gray = png_gamma_nxmbit_correct(gray,
+ png_ptr->colorspace.gamma, 16U, 8U);
/* And make sure the corresponding palette entry contains
* exactly the required sRGB value.
@@ -2549,7 +2582,7 @@ png_image_read_colormap(png_voidp argument)
else if (output_encoding == P_LINEAR)
{
- gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+ gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255);
/* And make sure the corresponding palette entry matches.
*/
@@ -2561,7 +2594,8 @@ png_image_read_colormap(png_voidp argument)
* output (normally sRGB) value.
*/
c.index = 0; /*unused*/
- c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+ c.gray = c.red = c.green = c.blue =
+ png_check_u16(png_ptr, gray);
/* NOTE: the following is apparently a bug in libpng. Without
* it the transparent color recognition in
@@ -2569,8 +2603,8 @@ png_image_read_colormap(png_voidp argument)
*/
expand_tRNS = 1;
png_set_background_fixed(png_ptr, &c,
- PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
- 0/*gamma: not used*/);
+ PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+ 0/*gamma: not used*/);
}
output_processing = PNG_CMAP_NONE;
@@ -2593,7 +2627,7 @@ png_image_read_colormap(png_voidp argument)
/* Is there alpha in the output too? If so all four channels are
* processed into a special RGB cube with alpha support.
*/
- if (output_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
png_uint_32 r;
@@ -2604,7 +2638,7 @@ png_image_read_colormap(png_voidp argument)
/* Add a transparent entry. */
png_create_colormap_entry(display, cmap_entries, 255, 255,
- 255, 0, P_sRGB);
+ 255, 0, P_sRGB);
/* This is stored as the background index for the processing
* algorithm.
@@ -2625,7 +2659,7 @@ png_image_read_colormap(png_voidp argument)
*/
for (b=0; b<256; b = (b << 1) | 0x7f)
png_create_colormap_entry(display, cmap_entries++,
- r, g, b, 128, P_sRGB);
+ r, g, b, 128, P_sRGB);
}
}
@@ -2652,13 +2686,13 @@ png_image_read_colormap(png_voidp argument)
cmap_entries = make_rgb_colormap(display);
png_create_colormap_entry(display, cmap_entries, back_r,
- back_g, back_b, 0/*unused*/, output_encoding);
+ back_g, back_b, 0/*unused*/, output_encoding);
if (output_encoding == P_LINEAR)
{
- r = PNG_sRGB_FROM_LINEAR(back_r * 255);
- g = PNG_sRGB_FROM_LINEAR(back_g * 255);
- b = PNG_sRGB_FROM_LINEAR(back_b * 255);
+ r = PNG_sRGB_FROM_LINEAR(png_ptr, back_r * 255);
+ g = PNG_sRGB_FROM_LINEAR(png_ptr, back_g * 255);
+ b = PNG_sRGB_FROM_LINEAR(png_ptr, back_b * 255);
}
else
@@ -2674,9 +2708,9 @@ png_image_read_colormap(png_voidp argument)
* index.
*/
if (memcmp((png_const_bytep)display->colormap +
- sample_size * cmap_entries,
- (png_const_bytep)display->colormap +
- sample_size * PNG_RGB_INDEX(r,g,b),
+ sample_size * cmap_entries,
+ (png_const_bytep)display->colormap +
+ sample_size * PNG_RGB_INDEX(r,g,b),
sample_size) != 0)
{
/* The background color must be added. */
@@ -2694,12 +2728,12 @@ png_image_read_colormap(png_voidp argument)
*/
for (b=0; b<256; b = (b << 1) | 0x7f)
png_create_colormap_entry(display, cmap_entries++,
- png_colormap_compose(display, r, P_sRGB, 128,
- back_r, output_encoding),
- png_colormap_compose(display, g, P_sRGB, 128,
- back_g, output_encoding),
- png_colormap_compose(display, b, P_sRGB, 128,
- back_b, output_encoding),
+ png_colormap_compose(display, r, 8U, P_sRGB,
+ 128U, back_r, output_encoding),
+ png_colormap_compose(display, g, 8U, P_sRGB,
+ 128U, back_g, output_encoding),
+ png_colormap_compose(display, b, 8U, P_sRGB,
+ 128U, back_b, output_encoding),
0/*unused*/, output_encoding);
}
}
@@ -2713,13 +2747,13 @@ png_image_read_colormap(png_voidp argument)
png_color_16 c;
c.index = 0; /*unused*/
- c.red = (png_uint_16)back_r;
- c.gray = c.green = (png_uint_16)back_g;
- c.blue = (png_uint_16)back_b;
+ c.red = png_check_u16(png_ptr, back_r);
+ c.gray = c.green = png_check_u16(png_ptr, back_g);
+ c.blue = png_check_u16(png_ptr, back_b);
png_set_background_fixed(png_ptr, &c,
- PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
- 0/*gamma: not used*/);
+ PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+ 0/*gamma: not used*/);
output_processing = PNG_CMAP_RGB;
}
@@ -2757,7 +2791,7 @@ png_image_read_colormap(png_voidp argument)
num_trans = 0;
output_processing = PNG_CMAP_NONE;
- data_encoding = P_FILE; /* Don't change from color-map indicies */
+ data_encoding = P_FILE; /* Don't change from color-map indices */
cmap_entries = png_ptr->num_palette;
if (cmap_entries > 256)
cmap_entries = 256;
@@ -2767,25 +2801,39 @@ png_image_read_colormap(png_voidp argument)
for (i=0; i < cmap_entries; ++i)
{
- if (do_background && i < num_trans && trans[i] < 255)
+ if (do_background != 0 && i < num_trans && trans[i] < 255)
{
if (trans[i] == 0)
png_create_colormap_entry(display, i, back_r, back_g,
- back_b, 0, output_encoding);
+ back_b, 0, output_encoding);
else
{
+ unsigned int alpha;
+
/* Must compose the PNG file color in the color-map entry
* on the sRGB color in 'back'.
*/
+ png_image_get_sBIT(display);
+ alpha = update_for_sBIT(trans[i], display->sBIT[3], 8U);
+
+ /* Do the sBIT handling here because it only applies to the
+ * values from the colormap, not the background. Passing
+ * output_encoding to png_create_colormap_entry prevents
+ * this being duplicated.
+ */
png_create_colormap_entry(display, i,
- png_colormap_compose(display, colormap[i].red, P_FILE,
- trans[i], back_r, output_encoding),
- png_colormap_compose(display, colormap[i].green, P_FILE,
- trans[i], back_g, output_encoding),
- png_colormap_compose(display, colormap[i].blue, P_FILE,
- trans[i], back_b, output_encoding),
- output_encoding == P_LINEAR ? trans[i] * 257U :
+ png_colormap_compose(display, colormap[i].red,
+ display->sBIT[0], P_FILE, alpha, back_r,
+ output_encoding),
+ png_colormap_compose(display, colormap[i].green,
+ display->sBIT[1], P_FILE, alpha, back_g,
+ output_encoding),
+ png_colormap_compose(display, colormap[i].blue,
+ display->sBIT[2], P_FILE, alpha, back_b,
+ output_encoding),
+ output_encoding == P_LINEAR ?
+ update_for_sBIT(alpha*257U, display->sBIT[3], 16U) :
trans[i],
output_encoding);
}
@@ -2797,7 +2845,7 @@ png_image_read_colormap(png_voidp argument)
i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/);
}
- /* The PNG data may have indicies packed in fewer than 8 bits, it
+ /* The PNG data may have indices packed in fewer than 8 bits, it
* must be expanded if so.
*/
if (png_ptr->bit_depth < 8)
@@ -2808,18 +2856,17 @@ png_image_read_colormap(png_voidp argument)
default:
png_error(png_ptr, "invalid PNG color type");
/*NOT REACHED*/
- break;
}
/* Now deal with the output processing */
- if (expand_tRNS && png_ptr->num_trans > 0 &&
- (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0)
+ if (expand_tRNS != 0 && png_ptr->num_trans > 0 &&
+ (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0)
png_set_tRNS_to_alpha(png_ptr);
switch (data_encoding)
{
default:
- png_error(png_ptr, "bad data option (internal error)");
+ impossible("bad data option");
break;
case P_sRGB:
@@ -2833,8 +2880,7 @@ png_image_read_colormap(png_voidp argument)
break;
}
- if (cmap_entries > 256 || cmap_entries > image->colormap_entries)
- png_error(png_ptr, "color map overflow (BAD internal error)");
+ affirm(cmap_entries <= 256 && cmap_entries <= image->colormap_entries);
image->colormap_entries = cmap_entries;
@@ -2868,10 +2914,10 @@ png_image_read_colormap(png_voidp argument)
break;
default:
- png_error(png_ptr, "bad processing option (internal error)");
+ impossible("bad processing option");
bad_background:
- png_error(png_ptr, "bad background index (internal error)");
+ impossible("bad background index");
}
display->colormap_processing = output_processing;
@@ -2884,7 +2930,7 @@ static int
png_image_read_and_map(png_voidp argument)
{
png_image_read_control *display = png_voidcast(png_image_read_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
int passes;
@@ -2980,7 +3026,7 @@ png_image_read_and_map(png_voidp argument)
entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray);
}
- *outrow = (png_byte)entry;
+ *outrow = png_check_byte(png_ptr, entry);
}
break;
@@ -2997,7 +3043,7 @@ png_image_read_and_map(png_voidp argument)
*outrow = gray;
else
- *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1);
+ *outrow = PNG_CMAP_TRANS_BACKGROUND+1;
}
break;
@@ -3021,7 +3067,7 @@ png_image_read_and_map(png_voidp argument)
if (alpha >= 196)
*outrow = PNG_RGB_INDEX(inrow[0], inrow[1],
- inrow[2]);
+ inrow[2]);
else if (alpha < 64)
*outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND;
@@ -3052,7 +3098,7 @@ png_image_read_and_map(png_voidp argument)
if (inrow[0] & 0x80) back_i += 1; /* blue */
if (inrow[0] & 0x40) back_i += 1;
- *outrow = (png_byte)back_i;
+ *outrow = png_check_byte(png_ptr, back_i);
}
inrow += 4;
@@ -3073,12 +3119,12 @@ static int
png_image_read_colormapped(png_voidp argument)
{
png_image_read_control *display = png_voidcast(png_image_read_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_controlp control = image->opaque;
png_structrp png_ptr = control->png_ptr;
png_inforp info_ptr = control->info_ptr;
-
+ int color_type, bit_depth;
int passes = 0; /* As a flag */
PNG_SKIP_CHUNKS(png_ptr);
@@ -3092,6 +3138,12 @@ png_image_read_colormapped(png_voidp argument)
png_read_update_info(png_ptr, info_ptr);
+ /* Avoid the 'easy access' functions below because this allows them to be
+ * disabled; there are not useful with the simplified API.
+ */
+ color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format);
+ bit_depth = info_ptr->bit_depth;
+
/* The expected output can be deduced from the colormap_processing option. */
switch (display->colormap_processing)
{
@@ -3099,9 +3151,8 @@ png_image_read_colormapped(png_voidp argument)
/* Output must be one channel and one byte per pixel, the output
* encoding can be anything.
*/
- if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
- info_ptr->color_type == PNG_COLOR_TYPE_GRAY) &&
- info_ptr->bit_depth == 8)
+ if ((color_type == PNG_COLOR_TYPE_PALETTE ||
+ color_type == PNG_COLOR_TYPE_GRAY) && bit_depth == 8)
break;
goto bad_output;
@@ -3112,30 +3163,30 @@ png_image_read_colormapped(png_voidp argument)
* can be checked with an exact number because it should have been set
* to this number above!
*/
- if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
- info_ptr->bit_depth == 8 &&
- png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
- image->colormap_entries == 256)
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA && bit_depth == 8 &&
+ !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr),
+ 1/*sRGB*/) &&
+ image->colormap_entries == 256)
break;
goto bad_output;
case PNG_CMAP_RGB:
/* Output must be 8-bit sRGB encoded RGB */
- if (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
- info_ptr->bit_depth == 8 &&
- png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
- image->colormap_entries == 216)
+ if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8 &&
+ !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr),
+ 1/*sRGB*/) &&
+ image->colormap_entries == 216)
break;
goto bad_output;
case PNG_CMAP_RGB_ALPHA:
/* Output must be 8-bit sRGB encoded RGBA */
- if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
- info_ptr->bit_depth == 8 &&
- png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
- image->colormap_entries == 244 /* 216 + 1 + 27 */)
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA && bit_depth == 8 &&
+ !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr),
+ 1/*sRGB*/) &&
+ image->colormap_entries == 244 /* 216 + 1 + 27 */)
break;
/* goto bad_output; */
@@ -3143,7 +3194,7 @@ png_image_read_colormapped(png_voidp argument)
default:
bad_output:
- png_error(png_ptr, "bad color-map processing (internal error)");
+ impossible("bad color-map processing");
}
/* Now read the rows. Do this here if it is possible to read directly into
@@ -3206,7 +3257,7 @@ static int
png_image_read_composite(png_voidp argument)
{
png_image_read_control *display = png_voidcast(png_image_read_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
int passes;
@@ -3229,7 +3280,8 @@ png_image_read_composite(png_voidp argument)
png_uint_32 height = image->height;
png_uint_32 width = image->width;
ptrdiff_t step_row = display->row_bytes;
- unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
+ unsigned int channels =
+ (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
int pass;
for (pass = 0; pass < passes; ++pass)
@@ -3299,10 +3351,11 @@ png_image_read_composite(png_voidp argument)
* therefore appropriate for the sRGB to linear
* conversion table.
*/
- component = PNG_sRGB_FROM_LINEAR(component);
+ component =
+ PNG_sRGB_FROM_LINEAR(png_ptr, component);
}
- outrow[c] = (png_byte)component;
+ outrow[c] = png_check_byte(png_ptr, component);
}
}
@@ -3318,21 +3371,21 @@ png_image_read_composite(png_voidp argument)
/* The do_local_background case; called when all the following transforms are to
* be done:
*
- * PNG_RGB_TO_GRAY
- * PNG_COMPOSITE
- * PNG_GAMMA
+ * PNG_READ_RGB_TO_GRAY
+ * PNG_READ_COMPOSITE
+ * PNG_READ_GAMMA
*
- * This is a work-round for the fact that both the PNG_RGB_TO_GRAY and
+ * This is a work-around for the fact that both the PNG_READ_RGB_TO_GRAY and
* PNG_COMPOSITE code performs gamma correction, so we get double gamma
- * correction. The fix-up is to prevent the PNG_COMPOSITE operation happening
- * inside libpng, so this routine sees an 8 or 16-bit gray+alpha row and handles
- * the removal or pre-multiplication of the alpha channel.
+ * correction. The fix-up is to prevent the PNG_COMPOSITE operation from
+ * happening inside libpng, so this routine sees an 8 or 16-bit gray+alpha
+ * row and handles the removal or pre-multiplication of the alpha channel.
*/
static int
png_image_read_background(png_voidp argument)
{
png_image_read_control *display = png_voidcast(png_image_read_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
png_inforp info_ptr = image->opaque->info_ptr;
@@ -3345,14 +3398,9 @@ png_image_read_background(png_voidp argument)
* left to the png_image_read_background function. The rows libpng produce
* might be 8 or 16-bit but should always have two channels; gray plus alpha.
*/
- if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
- png_error(png_ptr, "lost rgb to gray");
-
- if ((png_ptr->transformations & PNG_COMPOSE) != 0)
- png_error(png_ptr, "unexpected compose");
-
- if (png_get_channels(png_ptr, info_ptr) != 2)
- png_error(png_ptr, "lost/gained channels");
+ affirm(PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format) ==
+ PNG_COLOR_TYPE_GRAY_ALPHA);
+ debug(png_get_channels(png_ptr, info_ptr) == 2);
/* Expect the 8-bit case to always remove the alpha channel */
if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 &&
@@ -3396,8 +3444,7 @@ png_image_read_background(png_voidp argument)
for (pass = 0; pass < passes; ++pass)
{
- png_bytep row = png_voidcast(png_bytep,
- display->first_row);
+ png_bytep row = png_voidcast(png_bytep, display->first_row);
unsigned int startx, stepx, stepy;
png_uint_32 y;
@@ -3425,7 +3472,7 @@ png_image_read_background(png_voidp argument)
for (; ylocal_row);
+ display->local_row);
png_bytep outrow = first_row + y * step_row;
png_const_bytep end_row = outrow + width;
@@ -3451,10 +3498,11 @@ png_image_read_background(png_voidp argument)
component = png_sRGB_table[component] * alpha;
component += png_sRGB_table[outrow[0]] *
(255-alpha);
- component = PNG_sRGB_FROM_LINEAR(component);
+ component =
+ PNG_sRGB_FROM_LINEAR(png_ptr, component);
}
- outrow[0] = (png_byte)component;
+ outrow[0] = png_check_byte(png_ptr, component);
}
inrow += 2; /* gray and alpha channel */
@@ -3470,7 +3518,7 @@ png_image_read_background(png_voidp argument)
for (; ylocal_row);
+ display->local_row);
png_bytep outrow = first_row + y * step_row;
png_const_bytep end_row = outrow + width;
@@ -3491,10 +3539,11 @@ png_image_read_background(png_voidp argument)
{
component = png_sRGB_table[component] * alpha;
component += background * (255-alpha);
- component = PNG_sRGB_FROM_LINEAR(component);
+ component =
+ PNG_sRGB_FROM_LINEAR(png_ptr, component);
}
- outrow[0] = (png_byte)component;
+ outrow[0] = png_check_byte(png_ptr, component);
}
else
@@ -3517,7 +3566,7 @@ png_image_read_background(png_voidp argument)
*/
{
png_uint_16p first_row = png_voidcast(png_uint_16p,
- display->first_row);
+ display->first_row);
/* The division by two is safe because the caller passed in a
* stride which was multiplied by 2 (below) to get row_bytes.
*/
@@ -3526,10 +3575,11 @@ png_image_read_background(png_voidp argument)
unsigned int outchannels = 1+preserve_alpha;
int swap_alpha = 0;
-# ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
- if (preserve_alpha && (image->format & PNG_FORMAT_FLAG_AFIRST))
- swap_alpha = 1;
-# endif
+#ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
+ if (preserve_alpha != 0 &&
+ (image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
+ swap_alpha = 1;
+#endif
for (pass = 0; pass < passes; ++pass)
{
@@ -3566,7 +3616,7 @@ png_image_read_background(png_voidp argument)
/* Read the row, which is packed: */
png_read_row(png_ptr, png_voidcast(png_bytep,
- display->local_row), NULL);
+ display->local_row), NULL);
inrow = png_voidcast(png_const_uint_16p, display->local_row);
/* Now do the pre-multiplication on each pixel in this row.
@@ -3590,7 +3640,8 @@ png_image_read_background(png_voidp argument)
else
component = 0;
- outrow[swap_alpha] = (png_uint_16)component;
+ outrow[swap_alpha] =
+ png_check_u16(png_ptr, component);
if (preserve_alpha != 0)
outrow[1 ^ swap_alpha] = alpha;
@@ -3610,7 +3661,7 @@ static int
png_image_read_direct(png_voidp argument)
{
png_image_read_control *display = png_voidcast(png_image_read_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
png_inforp info_ptr = image->opaque->info_ptr;
@@ -3630,16 +3681,16 @@ png_image_read_direct(png_voidp argument)
/* Now check the format to see if it was modified. */
{
png_uint_32 base_format = png_image_format(png_ptr) &
- ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */;
+ PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP) /* removed by png_set_expand */;
png_uint_32 change = format ^ base_format;
png_fixed_point output_gamma;
int mode; /* alpha mode */
/* Do this first so that we have a record if rgb to gray is happening. */
- if (change & PNG_FORMAT_FLAG_COLOR)
+ if ((change & PNG_FORMAT_FLAG_COLOR) != 0)
{
/* gray<->color transformation required. */
- if (format & PNG_FORMAT_FLAG_COLOR)
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
png_set_gray_to_rgb(png_ptr);
else
@@ -3657,14 +3708,14 @@ png_image_read_direct(png_voidp argument)
* enormous change) 'do_local_background' is used to indicate that
* the problem exists.
*/
- if (base_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
do_local_background = 1/*maybe*/;
png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE,
- PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
+ PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
}
- change &= ~PNG_FORMAT_FLAG_COLOR;
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
}
/* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.
@@ -3672,8 +3723,8 @@ png_image_read_direct(png_voidp argument)
{
png_fixed_point input_gamma_default;
- if ((base_format & PNG_FORMAT_FLAG_LINEAR) &&
- (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
+ if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
+ (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
input_gamma_default = PNG_GAMMA_LINEAR;
else
input_gamma_default = PNG_DEFAULT_sRGB;
@@ -3689,7 +3740,7 @@ png_image_read_direct(png_voidp argument)
/* If there *is* an alpha channel in the input it must be multiplied
* out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG.
*/
- if (base_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
mode = PNG_ALPHA_STANDARD; /* associated alpha */
else
@@ -3712,15 +3763,12 @@ png_image_read_direct(png_voidp argument)
*/
if (do_local_background != 0)
{
- png_fixed_point gtest;
-
- /* This is 'png_gamma_threshold' from pngrtran.c; the test used for
- * gamma correction, the screen gamma hasn't been set on png_struct
- * yet; it's set below. png_struct::gamma, however, is set to the
- * final value.
+ /* This is intended to be a safe check to see if libpng will perform
+ * gamma work in pngrtran.c; if it will *not* be performed the
+ * do_local_background flag is cancelled.
*/
- if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma,
- PNG_FP_1) && !png_gamma_significant(gtest))
+ if (!png_need_gamma_correction(png_ptr, 0/*PNG gamma*/,
+ output_gamma != PNG_GAMMA_LINEAR))
do_local_background = 0;
else if (mode == PNG_ALPHA_STANDARD)
@@ -3733,25 +3781,25 @@ png_image_read_direct(png_voidp argument)
}
/* If the bit-depth changes then handle that here. */
- if (change & PNG_FORMAT_FLAG_LINEAR)
+ if ((change & PNG_FORMAT_FLAG_LINEAR) != 0)
{
- if (linear /*16-bit output*/)
+ if (linear != 0 /*16-bit output*/)
png_set_expand_16(png_ptr);
else /* 8-bit output */
png_set_scale_16(png_ptr);
- change &= ~PNG_FORMAT_FLAG_LINEAR;
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_LINEAR);
}
/* Now the background/alpha channel changes. */
- if (change & PNG_FORMAT_FLAG_ALPHA)
+ if ((change & PNG_FORMAT_FLAG_ALPHA) != 0)
{
/* Removing an alpha channel requires composition for the 8-bit
* formats; for the 16-bit it is already done, above, by the
* pre-multiplication and the channel just needs to be stripped.
*/
- if (base_format & PNG_FORMAT_FLAG_ALPHA)
+ if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
/* If RGB->gray is happening the alpha channel must be left and the
* operation completed locally.
@@ -3783,8 +3831,8 @@ png_image_read_direct(png_voidp argument)
* pixels.
*/
png_set_background_fixed(png_ptr, &c,
- PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
- 0/*gamma: not used*/);
+ PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+ 0/*gamma: not used*/);
}
else /* compose on row: implemented below. */
@@ -3815,22 +3863,22 @@ png_image_read_direct(png_voidp argument)
else
filler = 255;
-# ifdef PNG_FORMAT_AFIRST_SUPPORTED
- if (format & PNG_FORMAT_FLAG_AFIRST)
- {
- where = PNG_FILLER_BEFORE;
- change &= ~PNG_FORMAT_FLAG_AFIRST;
- }
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
+ {
+ where = PNG_FILLER_BEFORE;
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST);
+ }
- else
-# endif
+ else
+#endif
where = PNG_FILLER_AFTER;
png_set_add_alpha(png_ptr, filler, where);
}
/* This stops the (irrelevant) call to swap_alpha below. */
- change &= ~PNG_FORMAT_FLAG_ALPHA;
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
}
/* Now set the alpha mode correctly; this is always done, even if there is
@@ -3839,45 +3887,45 @@ png_image_read_direct(png_voidp argument)
*/
png_set_alpha_mode_fixed(png_ptr, mode, output_gamma);
-# ifdef PNG_FORMAT_BGR_SUPPORTED
- if (change & PNG_FORMAT_FLAG_BGR)
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ if ((change & PNG_FORMAT_FLAG_BGR) != 0)
+ {
+ /* Check only the output format; PNG is never BGR; don't do this if
+ * the output is gray, but fix up the 'format' value in that case.
+ */
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
+ png_set_bgr(png_ptr);
+
+ else
+ format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR);
+
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR);
+ }
+# endif
+
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if ((change & PNG_FORMAT_FLAG_AFIRST) != 0)
+ {
+ /* Only relevant if there is an alpha channel - it's particularly
+ * important to handle this correctly because do_local_compose may
+ * be set above and then libpng will keep the alpha channel for this
+ * code to remove.
+ */
+ if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
- /* Check only the output format; PNG is never BGR; don't do this if
- * the output is gray, but fix up the 'format' value in that case.
+ /* Disable this if doing a local background,
+ * TODO: remove this when local background is no longer required.
*/
- if (format & PNG_FORMAT_FLAG_COLOR)
- png_set_bgr(png_ptr);
-
- else
- format &= ~PNG_FORMAT_FLAG_BGR;
-
- change &= ~PNG_FORMAT_FLAG_BGR;
+ if (do_local_background != 2)
+ png_set_swap_alpha(png_ptr);
}
-# endif
-# ifdef PNG_FORMAT_AFIRST_SUPPORTED
- if (change & PNG_FORMAT_FLAG_AFIRST)
- {
- /* Only relevant if there is an alpha channel - it's particularly
- * important to handle this correctly because do_local_compose may
- * be set above and then libpng will keep the alpha channel for this
- * code to remove.
- */
- if (format & PNG_FORMAT_FLAG_ALPHA)
- {
- /* Disable this if doing a local background,
- * TODO: remove this when local background is no longer required.
- */
- if (do_local_background != 2)
- png_set_swap_alpha(png_ptr);
- }
+ else
+ format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST);
- else
- format &= ~PNG_FORMAT_FLAG_AFIRST;
-
- change &= ~PNG_FORMAT_FLAG_AFIRST;
- }
-# endif
+ change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST);
+ }
+# endif
/* If the *output* is 16-bit then we need to check for a byte-swap on this
* architecture.
@@ -3886,7 +3934,7 @@ png_image_read_direct(png_voidp argument)
{
PNG_CONST png_uint_16 le = 0x0001;
- if (*(png_const_bytep)&le)
+ if ((*(png_const_bytep) & le) != 0)
png_set_swap(png_ptr);
}
@@ -3909,55 +3957,47 @@ png_image_read_direct(png_voidp argument)
png_read_update_info(png_ptr, info_ptr);
{
- png_uint_32 info_format = 0;
+ png_uint_32 out_format = png_memory_format(png_ptr);
- if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
- info_format |= PNG_FORMAT_FLAG_COLOR;
+ /* Swapping is expected for the 16-bit format: */
+ out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_SWAPPED);
- if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ /* The remaining upper bits should never be set: */
+ affirm(!(out_format & ~0x3FU));
+
+ if ((out_format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
/* do_local_compose removes this channel below. */
- if (do_local_compose == 0)
- {
- /* do_local_background does the same if required. */
- if (do_local_background != 2 ||
- (format & PNG_FORMAT_FLAG_ALPHA) != 0)
- info_format |= PNG_FORMAT_FLAG_ALPHA;
- }
+ if (do_local_compose != 0 ||
+ /* do_local_background does the same if required. */
+ (do_local_background == 2 &&
+ (format & PNG_FORMAT_FLAG_ALPHA) == 0))
+ out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
}
- else if (do_local_compose != 0) /* internal error */
- png_error(png_ptr, "png_image_read: alpha channel lost");
+ else
+ affirm(do_local_compose == 0 /* else alpha channel lost */);
- if (info_ptr->bit_depth == 16)
- info_format |= PNG_FORMAT_FLAG_LINEAR;
+ switch (png_memory_channel_depth(png_ptr))
+ {
+ case 16: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) != 0); break;
+ case 8: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) == 0); break;
+ default: impossible("unexpected bit depth"); break;
+ }
-# ifdef PNG_FORMAT_BGR_SUPPORTED
- if (png_ptr->transformations & PNG_BGR)
- info_format |= PNG_FORMAT_FLAG_BGR;
-# endif
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (do_local_background == 2)
+ {
+ /* do_local_background should be handling the swap: */
+ affirm(!(out_format & PNG_FORMAT_FLAG_AFIRST));
-# ifdef PNG_FORMAT_AFIRST_SUPPORTED
- if (do_local_background == 2)
- {
- if (format & PNG_FORMAT_FLAG_AFIRST)
- info_format |= PNG_FORMAT_FLAG_AFIRST;
- }
-
- if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 ||
- ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 &&
- (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0))
- {
- if (do_local_background == 2)
- png_error(png_ptr, "unexpected alpha swap transformation");
-
- info_format |= PNG_FORMAT_FLAG_AFIRST;
- }
-# endif
+ if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
+ out_format |= PNG_FORMAT_FLAG_AFIRST;
+ }
+# endif
/* This is actually an internal error. */
- if (info_format != format)
- png_error(png_ptr, "png_read_image: invalid transformations");
+ affirm(out_format == format /* else unimplemented transformations */);
}
/* Now read the rows. If do_local_compose is set then it is necessary to use
@@ -4034,70 +4074,112 @@ png_image_read_direct(png_voidp argument)
int PNGAPI
png_image_finish_read(png_imagep image, png_const_colorp background,
- void *buffer, png_int_32 row_stride, void *colormap)
+ void *buffer, ptrdiff_t row_stride, void *colormap)
{
if (image != NULL && image->version == PNG_IMAGE_VERSION)
{
- png_uint_32 check;
+ /* Check for row_stride overflow. This check is not performed on the
+ * original PNG format because it may not occur in the output PNG format
+ * and libpng deals with the issues of reading the original.
+ */
+ const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
- if (row_stride == 0)
- row_stride = PNG_IMAGE_ROW_STRIDE(*image);
-
- if (row_stride < 0)
- check = -row_stride;
-
- else
- check = row_stride;
-
- if (image->opaque != NULL && buffer != NULL &&
- check >= PNG_IMAGE_ROW_STRIDE(*image))
+ /* The test is slightly evil: it assumes that a signed pointer difference
+ * (ptrdiff_t) can hold a maximum value of half, rounded down, of the
+ * maximum of a (size_t). This is almost certain to be true.
+ */
+ if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */
{
- if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
- (image->colormap_entries > 0 && colormap != NULL))
+ png_alloc_size_t check;
+ const png_alloc_size_t png_row_stride =
+ (png_alloc_size_t)/*SAFE*/image->width * channels;
+
+ if (row_stride == 0)
+ row_stride = (ptrdiff_t)png_row_stride;
+
+ if (row_stride < 0)
+ check = -row_stride;
+
+ else
+ check = row_stride;
+
+ if (image->opaque != NULL && buffer != NULL && check >= png_row_stride)
{
- int result;
- png_image_read_control display;
-
- memset(&display, 0, (sizeof display));
- display.image = image;
- display.buffer = buffer;
- display.row_stride = row_stride;
- display.colormap = colormap;
- display.background = background;
- display.local_row = NULL;
-
- /* Choose the correct 'end' routine; for the color-map case all the
- * setup has already been done.
+ /* Now check for overflow of the image buffer calculation; this
+ * limits the whole image size to PNG_SIZE_MAX bytes.
+ *
+ * The PNG_IMAGE_BUFFER_SIZE macro is:
+ *
+ * (PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)*height*(row_stride))
+ *
+ * We have no way of guaranteeing that the application used the
+ * correct type for 'row_stride' if it used the macro, so this is
+ * technically not completely safe, but this is the case throughout
+ * libpng; the app is responsible for making sure the calcualtion of
+ * buffer sizes does not overflow.
*/
- if (image->format & PNG_FORMAT_FLAG_COLORMAP)
- result =
- png_safe_execute(image, png_image_read_colormap, &display) &&
- png_safe_execute(image, png_image_read_colormapped, &display);
+ if (image->height <= PNG_SIZE_MAX /
+ PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format) / check)
+ {
+ if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
+ (image->colormap_entries > 0 && colormap != NULL))
+ {
+ int result;
+ png_image_read_control display;
+
+ memset(&display, 0, (sizeof display));
+ display.image = image;
+ display.buffer = buffer;
+ display.row_stride = row_stride;
+ display.colormap = colormap;
+ display.background = background;
+ display.local_row = NULL;
+
+ /* Choose the correct 'end' routine; for the color-map case
+ * all the setup has already been done.
+ */
+ if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ result =
+ png_safe_execute(image,
+ png_image_read_colormap, &display) &&
+ png_safe_execute(image,
+ png_image_read_colormapped, &display);
+
+ else
+ result =
+ png_safe_execute(image,
+ png_image_read_direct, &display);
+
+ png_image_free(image);
+ return result;
+ }
+
+ else
+ return png_image_error(image,
+ "png_image_finish_read[color-map]: no color-map");
+ }
else
- result =
- png_safe_execute(image, png_image_read_direct, &display);
-
- png_image_free(image);
- return result;
+ return png_image_error(image,
+ "png_image_finish_read: image too large");
}
else
return png_image_error(image,
- "png_image_finish_read[color-map]: no color-map");
+ "png_image_finish_read: invalid argument");
}
else
return png_image_error(image,
- "png_image_finish_read: invalid argument");
+ "png_image_finish_read: row_stride too large");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_finish_read: damaged PNG_IMAGE_VERSION");
+ "png_image_finish_read: damaged PNG_IMAGE_VERSION");
return 0;
}
-#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
-#endif /* PNG_READ_SUPPORTED */
+#endif /* SIMPLIFIED_READ */
+#endif /* READ */
diff --git a/ext/libpng17/pngrio.c b/ext/libpng17/pngrio.c
index d75ac5b4dd..b77f827405 100644
--- a/ext/libpng17/pngrio.c
+++ b/ext/libpng17/pngrio.c
@@ -1,8 +1,8 @@
/* pngrio.c - functions for data input
*
- * Last changed in libpng 1.6.9 [February 6, 2014]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -19,6 +19,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngrio
#ifdef PNG_READ_SUPPORTED
@@ -26,18 +27,23 @@
* reads from a file pointer. Note that this routine sometimes gets called
* with very small lengths, so you should implement some kind of simple
* buffering if you are using unbuffered reads. This should never be asked
- * to read more then 64K on a 16 bit machine.
+ * to read more than 64K on a 16 bit machine.
*/
void /* PRIVATE */
-png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length)
+png_read_data(png_structrp png_ptr, png_voidp data, png_size_t length)
{
png_debug1(4, "reading %d bytes", (int)length);
- if (png_ptr->read_data_fn != NULL)
- (*(png_ptr->read_data_fn))(png_ptr, data, length);
+ /* This was guaranteed by prior versions of libpng, so app callbacks may
+ * assume it even though it isn't documented to be the case.
+ */
+ debug(length > 0U);
+
+ if (png_ptr->rw_data_fn != NULL)
+ png_ptr->rw_data_fn(png_ptr, png_voidcast(png_bytep,data), length);
else
- png_error(png_ptr, "Call to NULL read function");
+ png_app_error(png_ptr, "No read function");
}
#ifdef PNG_STDIO_SUPPORTED
@@ -62,7 +68,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
if (check != length)
png_error(png_ptr, "Read Error");
}
-#endif
+#endif /* STDIO */
/* This function allows the application to supply a new input function
* for libpng if standard C streams aren't being used.
@@ -85,36 +91,24 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
*/
void PNGAPI
png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
- png_rw_ptr read_data_fn)
+ png_rw_ptr read_data_fn)
{
if (png_ptr == NULL)
return;
- png_ptr->io_ptr = io_ptr;
-
-#ifdef PNG_STDIO_SUPPORTED
- if (read_data_fn != NULL)
- png_ptr->read_data_fn = read_data_fn;
-
- else
- png_ptr->read_data_fn = png_default_read_data;
-#else
- png_ptr->read_data_fn = read_data_fn;
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
- /* It is an error to write to a read device */
- if (png_ptr->write_data_fn != NULL)
+ if (!png_ptr->read_struct)
{
- png_ptr->write_data_fn = NULL;
- png_warning(png_ptr,
- "Can't set both read_data_fn and write_data_fn in the"
- " same structure");
+ png_app_error(png_ptr, "cannot set a read function on a write struct");
+ return;
}
-#endif
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
- png_ptr->output_flush_fn = NULL;
-#endif
+ if (read_data_fn == NULL)
+ {
+ png_app_error(png_ptr, "API change: png_set_read_fn requires a function");
+ return;
+ }
+
+ png_ptr->io_ptr = io_ptr;
+ png_ptr->rw_data_fn = read_data_fn;
}
-#endif /* PNG_READ_SUPPORTED */
+#endif /* READ */
diff --git a/ext/libpng17/pngrtran.c b/ext/libpng17/pngrtran.c
index 331b59ee62..c5fb837bee 100644
--- a/ext/libpng17/pngrtran.c
+++ b/ext/libpng17/pngrtran.c
@@ -1,8 +1,11 @@
+#ifdef _MSC_VER
+#pragma warning (disable:4146)
+#endif
/* pngrtran.c - transforms the data in a row for PNG readers
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -17,238 +20,3555 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngrtran
-#ifdef PNG_READ_SUPPORTED
-
-/* Set the action on getting a CRC error for an ancillary or critical chunk. */
-void PNGAPI
-png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action)
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+typedef struct
{
- png_debug(1, "in png_set_crc_action");
+ png_transform tr;
+ png_byte map[256U]; /* Map of palette values */
+ png_byte lut[1U << /* LUT for RGB values */
+ (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+PNG_QUANTIZE_BLUE_BITS)];
+} png_transform_quantize;
- if (png_ptr == NULL)
- return;
+#define PNG_QUANTIZE_MAP 1U /* map is present and not a 1:1 mapping */
+#define PNG_QUANTIZE_LUT 2U /* lut has been built */
- /* Tell libpng how we react to CRC errors in critical chunks */
- switch (crit_action)
+static void
+do_quantize_rgb(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
+ *transform);
+ unsigned int channels = PNG_TC_CHANNELS(*tc);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - channels/*safety*/;
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ affirm(tc->bit_depth == 8 && (channels == 3 || channels == 4) &&
+ !(tc->format & PNG_FORMAT_FLAG_SWAPPED) &&
+ (tr->tr.args & PNG_QUANTIZE_LUT) != 0);
+
+ tc->sp = dp;
+ tc->format |= PNG_FORMAT_FLAG_COLORMAP;
+
+ while (sp <= ep)
{
- case PNG_CRC_NO_CHANGE: /* Leave setting as is */
- break;
+ unsigned int r = sp[0];
+ unsigned int g = sp[1];
+ unsigned int b = sp[2];
- case PNG_CRC_WARN_USE: /* Warn/use data */
- png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
- png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
- break;
+ /* This looks real messy, but the compiler will reduce
+ * it down to a reasonable formula. For example, with
+ * 5 bits per color, we get:
+ * p = (((r >> 3) & 0x1f) << 10) |
+ * (((g >> 3) & 0x1f) << 5) |
+ * ((b >> 3) & 0x1f);
+ */
+ *dp++ = tr->lut[(((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
+ ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
+ (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
+ (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
+ ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
+ (PNG_QUANTIZE_BLUE_BITS)) |
+ ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
+ ((1 << PNG_QUANTIZE_BLUE_BITS) - 1))];
- case PNG_CRC_QUIET_USE: /* Quiet/use data */
- png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
- png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
- PNG_FLAG_CRC_CRITICAL_IGNORE;
- break;
-
- case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */
- png_warning(png_ptr,
- "Can't discard critical data on CRC error");
- case PNG_CRC_ERROR_QUIT: /* Error/quit */
-
- case PNG_CRC_DEFAULT:
- default:
- png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
- break;
+ sp += channels;
}
- /* Tell libpng how we react to CRC errors in ancillary chunks */
- switch (ancil_action)
- {
- case PNG_CRC_NO_CHANGE: /* Leave setting as is */
- break;
-
- case PNG_CRC_WARN_USE: /* Warn/use data */
- png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
- png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
- break;
-
- case PNG_CRC_QUIET_USE: /* Quiet/use data */
- png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
- png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
- PNG_FLAG_CRC_ANCILLARY_NOWARN;
- break;
-
- case PNG_CRC_ERROR_QUIT: /* Error/quit */
- png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
- png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
- break;
-
- case PNG_CRC_WARN_DISCARD: /* Warn/discard data */
-
- case PNG_CRC_DEFAULT:
- default:
- png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
- break;
- }
+ affirm(sp == ep+channels);
+ UNTESTED
+# undef png_ptr
}
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-/* Is it OK to set a transformation now? Only if png_start_read_image or
- * png_read_update_info have not been called. It is not necessary for the IHDR
- * to have been read in all cases, the parameter allows for this check too.
- */
-static int
-png_rtran_ok(png_structrp png_ptr, int need_IHDR)
+static void
+do_quantize_pal(png_transformp *transform, png_transform_controlp tc)
{
- if (png_ptr != NULL)
+# define png_ptr (tc->png_ptr)
+ png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
+ *transform);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ affirm(tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ !(tc->format & PNG_FORMAT_FLAG_SWAPPED) &&
+ (tr->tr.args & PNG_QUANTIZE_MAP) != 0);
+
+ tc->sp = dp;
+
+ while (sp < ep)
+ *dp++ = tr->map[*sp++];
+
+ UNTESTED
+# undef png_ptr
+}
+
+static void
+png_init_quantize(png_transformp *transform, png_transform_controlp tc)
+{
+ if (tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
{
- if (png_ptr->flags & PNG_FLAG_ROW_INIT)
- png_app_error(png_ptr,
- "invalid after png_start_read_image or png_read_update_info");
-
- else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)
- png_app_error(png_ptr, "invalid before the PNG header has been read");
-
- else
+ /* Either colormapped input, RGB or RGBA: */
+ if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) /* RGB, RGBA */
{
- /* Turn on failure to initialize correctly for all transforms. */
- png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
+ /* This must be a 'palette' lookup */
+ if (((*transform)->args & PNG_QUANTIZE_LUT) != 0)
+ {
+ /* This changes the format and invalidates pretty much everything in
+ * the info struct:
+ */
+ tc->format |= PNG_FORMAT_FLAG_COLORMAP;
- return 1; /* Ok */
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ (*transform)->fn = do_quantize_rgb;
+ tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL+
+ PNG_INFO_sBIT+PNG_INFO_bKGD;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(tc->png_ptr, tc->bit_depth);
+ }
+
+ return;
+ }
+ }
+
+ else /* colormapped */
+ {
+ /* This must be a 'quantize' lookup */
+ if (((*transform)->args & PNG_QUANTIZE_MAP) != 0)
+ {
+ /* This doesn't change the format, just the values: */
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ (*transform)->fn = do_quantize_pal;
+ tc->invalid_info |= PNG_INFO_sBIT+PNG_INFO_pCAL;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(tc->png_ptr, tc->bit_depth);
+ }
+
+ return;
+ }
}
}
- return 0; /* no png_error possible! */
+ /* Else not applicable */
+ (*transform)->fn = NULL;
}
-#endif
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-/* Handle alpha and tRNS via a background color */
-void PNGFAPI
-png_set_background_fixed(png_structrp png_ptr,
- png_const_color_16p background_color, int background_gamma_code,
- int need_expand, png_fixed_point background_gamma)
+/* Dither file to 8-bit. Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible. If the current number
+ * of colors is greater then the maximum number, the palette will be
+ * modified to fit in the maximum number. "full_quantize" indicates
+ * whether we need a quantizing cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+typedef struct png_dsort_struct
{
- png_debug(1, "in png_set_background_fixed");
+ struct png_dsort_struct * next;
+ png_byte left;
+ png_byte right;
+} png_dsort;
+typedef png_dsort * png_dsortp;
+typedef png_dsort * * png_dsortpp;
- if (!png_rtran_ok(png_ptr, 0) || background_color == NULL)
- return;
+static void
+init_map(png_bytep map)
+ /* Initialize a mapping table to be 1:1 */
+{
+ png_byte b = 0U;
- if (background_gamma_code != PNG_BACKGROUND_GAMMA_SCREEN &&
- background_gamma_code != PNG_BACKGROUND_GAMMA_FILE &&
- background_gamma_code != PNG_BACKGROUND_GAMMA_UNIQUE)
+ do
+ map[b] = b;
+ while (b++ != 255U);
+}
+
+/* Save typing and make code easier to understand */
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+ abs((int)((c1).green) - (int)((c2).green)) + \
+ abs((int)((c1).blue) - (int)((c2).blue)))
+
+void PNGAPI
+png_set_quantize(png_structrp png_ptr, png_colorp palette,
+ int num_palette, int maximum_colors, png_const_uint_16p histogram,
+ int full_quantize)
+{
+ png_debug(1, "in png_set_quantize");
+
+ if (png_ptr != NULL)
{
- png_app_error(png_ptr, "invalid gamma type");
- return;
+ png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
+ png_add_transform(png_ptr, sizeof (png_transform_quantize),
+ png_init_quantize, PNG_TR_QUANTIZE));
+
+ /* This is weird (consider what happens to png_set_background on a palette
+ * image with a tRNS chunk).
+ */
+ if (palette == png_ptr->palette)
+ png_app_warning(png_ptr, "png_set_quantize: PLTE will be damaged");
+
+ if (maximum_colors <= 0 || num_palette > 256)
+ {
+ /* The spuriously allocated transform will be removed by the init
+ * code.
+ */
+ png_app_error(png_ptr, "png_set_quantize: invalid color count");
+ return;
+ }
+
+ /* The app passed in a palette with too many colors, it's not clear why
+ * libpng is providing this functionality, it's nothing to do with PNG and
+ * can be done by the application without any PNG specific knowledge.
+ */
+ if (num_palette > maximum_colors)
+ {
+ int map_changed = 0;
+
+ /* The map table must be preset to do no mapping initially: */
+ init_map(tr->map);
+
+ if (histogram != NULL)
+ {
+ /* This is easy enough, just throw out the least used colors.
+ * Perhaps not the best solution, but good enough.
+ */
+ int i;
+ png_byte quantize_sort[256U];
+
+ /* Initialize an array to sort colors */
+ init_map(quantize_sort);
+
+ /* Find the least used palette entries by starting a
+ * bubble sort, and running it until we have sorted
+ * out enough colors. Note that we don't care about
+ * sorting all the colors, just finding which are
+ * least used.
+ */
+ for (i = num_palette - 1; i >= maximum_colors; i--)
+ {
+ int done; /* To stop early if the list is pre-sorted */
+ int j;
+
+ done = 1;
+ for (j = 0; j < i; j++)
+ {
+ if (histogram[quantize_sort[j]] <
+ histogram[quantize_sort[j+1]])
+ {
+ png_byte t = quantize_sort[j];
+ quantize_sort[j] = quantize_sort[j+1];
+ quantize_sort[j+1] = t;
+ done = 0;
+ }
+ }
+
+ if (done != 0)
+ break;
+ }
+
+ /* Swap the palette around, and set up a table, if necessary */
+ if (full_quantize)
+ {
+ int j = num_palette;
+
+ /* Put all the useful colors within the max, but don't
+ * move the others.
+ *
+ * NOTE: if the app passes in the result of png_get_PLTE it will
+ * be overwritten at this point, what is the API?
+ */
+ for (i = 0; i < maximum_colors; i++)
+ {
+ if (quantize_sort[i] >= maximum_colors)
+ {
+ do
+ j--;
+ while (quantize_sort[j] >= maximum_colors);
+
+ /* NOTE: NOT swapped, so the original palette[i] has been
+ * lost.
+ */
+ palette[i] = palette[j];
+ }
+ }
+ }
+
+ else /* !full_quantize */
+ {
+ int j = num_palette;
+
+ /* Move all the used colors inside the max limit, and
+ * develop a translation table.
+ */
+ for (i = 0; i < maximum_colors; i++)
+ {
+ /* Only move the colors we need to */
+ if (quantize_sort[i] >= maximum_colors)
+ {
+ png_color tmp_color;
+
+ do
+ j--;
+ while (quantize_sort[j] >= maximum_colors);
+
+ tmp_color = palette[j];
+ palette[j] = palette[i];
+ palette[i] = tmp_color;
+ /* Indicate where the color went */
+ tr->map[j] = png_check_byte(png_ptr, i);
+ tr->map[i] = png_check_byte(png_ptr, j);
+ map_changed = 1;
+ }
+ }
+
+ /* Find closest color for those colors we are not using */
+ for (i = 0; i < num_palette; i++)
+ {
+ if (tr->map[i] >= maximum_colors)
+ {
+ int min_d, k, min_k, d_index;
+
+ /* Find the closest color to one we threw out */
+ d_index = tr->map[i];
+ min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+ for (k = 1, min_k = 0; k < maximum_colors; k++)
+ {
+ int d;
+
+ d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+ if (d < min_d)
+ {
+ min_d = d;
+ min_k = k;
+ }
+ }
+
+ /* Point to closest color */
+ tr->map[i] = png_check_byte(png_ptr, min_k);
+ map_changed = 1;
+ }
+ }
+ } /* !full_quantize */
+ } /* have a histogram */
+
+ else /* no histogram */
+ {
+ /* This is much harder to do simply (and quickly). Perhaps
+ * we need to go through a median cut routine, but those
+ * don't always behave themselves with only a few colors
+ * as input. So we will just find the closest two colors,
+ * and throw out one of them (chosen somewhat randomly).
+ * [We don't understand this at all, so if someone wants to
+ * work on improving it, be our guest - AED, GRP]
+ */
+ int max_d;
+ int num_new_palette;
+ png_byte index_to_palette[256U];
+ png_byte palette_to_index[256U];
+ png_dsortp hash[769];
+
+ /* Initialize palette index sort arrays */
+ init_map(index_to_palette);
+ init_map(palette_to_index);
+ memset(hash, 0, sizeof hash);
+ num_new_palette = num_palette;
+
+ /* Initial wild guess at how far apart the farthest pixel
+ * pair we will be eliminating will be. Larger
+ * numbers mean more areas will be allocated, Smaller
+ * numbers run the risk of not saving enough data, and
+ * having to do this all over again.
+ *
+ * I have not done extensive checking on this number.
+ */
+ max_d = 96;
+
+ while (num_new_palette > maximum_colors)
+ {
+ int i;
+ png_dsortp t = NULL;
+
+ for (i = 0; i < num_new_palette - 1; i++)
+ {
+ int j;
+
+ for (j = i + 1; j < num_new_palette; j++)
+ {
+ int d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+ if (d <= max_d)
+ {
+
+ t = png_voidcast(png_dsortp, png_malloc_warn(png_ptr,
+ sizeof (*t)));
+
+ if (t == NULL)
+ break;
+
+ t->next = hash[d];
+ t->left = png_check_byte(png_ptr, i);
+ t->right = png_check_byte(png_ptr, j);
+ hash[d] = t;
+ }
+ }
+ if (t == NULL)
+ break;
+ }
+
+ if (t != NULL) for (i = 0; i <= max_d; i++)
+ {
+ if (hash[i] != NULL)
+ {
+ png_dsortp p;
+
+ for (p = hash[i]; p != NULL; p = p->next)
+ {
+ if (index_to_palette[p->left] < num_new_palette &&
+ index_to_palette[p->right] < num_new_palette)
+ {
+ int j, next_j;
+
+ if (num_new_palette & 0x01)
+ {
+ j = p->left;
+ next_j = p->right;
+ }
+ else
+ {
+ j = p->right;
+ next_j = p->left;
+ }
+
+ num_new_palette--;
+ /* NOTE: overwrites palette */
+ palette[index_to_palette[j]] =
+ palette[num_new_palette];
+
+ if (full_quantize == 0)
+ {
+ int k;
+
+ for (k = 0; k < num_palette; k++)
+ {
+ if (tr->map[k] == index_to_palette[j])
+ {
+ tr->map[k] = index_to_palette[next_j];
+ map_changed = 1;
+ }
+
+ if (tr->map[k] == num_new_palette)
+ {
+ tr->map[k] = index_to_palette[j];
+ map_changed = 1;
+ }
+ }
+ }
+
+ index_to_palette[palette_to_index[num_new_palette]] =
+ index_to_palette[j];
+
+ palette_to_index[index_to_palette[j]] =
+ palette_to_index[num_new_palette];
+
+ index_to_palette[j] =
+ png_check_byte(png_ptr, num_new_palette);
+
+ palette_to_index[num_new_palette] =
+ png_check_byte(png_ptr, j);
+ }
+
+ if (num_new_palette <= maximum_colors)
+ break;
+ }
+
+ if (num_new_palette <= maximum_colors)
+ break;
+ }
+ }
+
+ for (i = 0; i < 769; i++)
+ {
+ if (hash[i] != NULL)
+ {
+ png_dsortp p = hash[i];
+
+ while (p)
+ {
+ t = p->next;
+ png_free(png_ptr, p);
+ p = t;
+ }
+
+ hash[i] = NULL;
+ }
+ }
+
+ max_d += 96;
+ } /* while num_new_colors > maximum_colors */
+ } /* no histogram */
+
+ num_palette = maximum_colors;
+
+ if (map_changed) /* else the map is 1:1 */
+ tr->tr.args |= PNG_QUANTIZE_MAP;
+ } /* num_palette > maximum_colors */
+
+ /* The palette has been reduced to the requested number of colors if it
+ * was over maximum colors before.
+ */
+
+ /* TODO: what is this? Apparently the png_struct::palette member gets
+ * updated if it didn't originally have a palette, but the update relies
+ * on the app not freeing the passed in palette.
+ */
+ if (png_ptr->palette == NULL)
+ png_ptr->palette = palette;
+
+ png_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9);
+
+ if (full_quantize)
+ {
+ int i;
+ png_byte distance[1U << (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+
+ PNG_QUANTIZE_BLUE_BITS)];
+
+ memset(distance, 0xff, sizeof distance);
+
+ for (i = 0; i < num_palette; i++)
+ {
+ int ir;
+ int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
+ int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
+ int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
+
+ for (ir = 0; ir < (1< r) ? ir - r : r - ir);
+ int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
+ PNG_QUANTIZE_GREEN_BITS));
+
+ for (ig = 0; ig < (1< g) ? ig - g : g - ig);
+ int dt = dr + dg;
+ int dm = ((dr > dg) ? dr : dg);
+ int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
+
+ for (ib = 0; ib < (1< b) ? ib - b : b - ib);
+ int dmax = ((dm > db) ? dm : db);
+ int d = dmax + dt + db;
+
+ if (d < distance[d_index])
+ {
+ distance[d_index] = png_check_byte(png_ptr, d);
+ tr->lut[d_index] = png_check_byte(png_ptr, i);
+ }
+ } /* for blue */
+ } /* for green */
+ } /* for red */
+ } /* num_palette */
+ } /* full_quantize */
+ } /* png_ptr != NULL */
+}
+#endif /* READ_QUANTIZE */
+
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values. Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1. If you would rather they contain 0 and 255, use
+ * png_set_expand_gray_1_2_4_to_8 instead.
+ */
+static void
+png_do_read_unpack(png_transformp *transform, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = png_voidcast(png_const_bytep, tc->dp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ sp += PNG_TC_ROWBYTES(*tc) - 1; /* Start from end */
+ dp += tc->width; /* output bit depth is 8 */
+
+# define png_ptr (tc->png_ptr)
+ png_debug(1, "in png_do_unpack");
+
+ switch (tc->bit_depth)
+ {
+ case 1:
+ {
+ /* Because we copy from the last pixel down the shift required
+ * at the start is 8-pixels_in_last_byte, which is just:
+ */
+ unsigned int shift = 7U & -tc->width;
+
+ while (dp > ep)
+ {
+ *--dp = (*sp >> shift) & 1U;
+ shift = 7U & (shift+1U);
+ if (shift == 0U)
+ --sp;
+ }
+
+ debug(shift == 0U);
+ break;
+ }
+
+ case 2:
+ {
+ unsigned int shift = 7U & -(tc->width << 1);
+
+ while (dp > ep)
+ {
+ *--dp = (*sp >> shift) & 3U;
+ shift = 7U & (shift+2U);
+ if (shift == 0U)
+ --sp;
+ }
+
+ debug(shift == 0U);
+ break;
+ }
+
+ case 4:
+ {
+ unsigned int shift = 7U & -(tc->width << 2);
+
+ while (dp > ep)
+ {
+ *--dp = (*sp >> shift) & 15U;
+ shift = 7U & (shift+4U);
+ if (shift == 0U)
+ --sp;
+ }
+
+ debug(shift == 0U);
+ break;
+ }
+
+ default:
+ impossible("bit depth");
}
- png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+ debug(dp == ep && sp == png_upcast(png_const_bytep, tc->sp)-1U);
+ tc->sp = dp;
- png_ptr->background = *background_color;
- png_ptr->background_gamma = background_gamma;
- png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U)
+ {
+ tc->range++;
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+ }
- if (need_expand != 0)
- png_ptr->flags |= PNG_FLAG_BACKGROUND_EXPAND;
-
- else
- png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND;
+ tc->bit_depth = 8U;
+ PNG_UNUSED(transform)
+# undef png_ptr
}
-# ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_background(png_structrp png_ptr,
- png_const_color_16p background_color, int background_gamma_code,
- int need_expand, double background_gamma)
-{
- png_set_background_fixed(png_ptr, background_color, background_gamma_code,
- need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
-}
-# endif /* FLOATING_POINT */
-#endif /* READ_BACKGROUND */
-
-/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the
- * one that pngrtran does first (scale) happens. This is necessary to allow the
- * TRANSFORM and API behavior to be somewhat consistent, and it's simpler.
+/* Called from the curiously named png_set_packing API in pngtrans.c; the read
+ * and write code is separated because read 'unpacks' (from PNG format) and
+ * write 'packs' (to PNG format.)
*/
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-void PNGAPI
-png_set_scale_16(png_structrp png_ptr)
+void /* PRIVATE */
+png_init_read_pack(png_transformp *transform, png_transform_controlp tc)
{
- png_debug(1, "in png_set_scale_16");
+# define png_ptr tc->png_ptr
+ debug(tc->init);
- if (!png_rtran_ok(png_ptr, 0))
- return;
+ if (tc->bit_depth < 8) /* else no packing/unpacking */
+ {
+ /* For indexed images the pack operation does not invalidate the range; in
+ * fact the corresponding shift operation would!
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U)
+ {
+ tc->range++;
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+ }
- png_ptr->transformations |= PNG_SCALE_16_TO_8;
+ tc->bit_depth = 8U;
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ (*transform)->fn = png_do_read_unpack/* sic: it unpacks */;
+ }
+
+ else /* the transform is not applicable */
+ (*transform)->fn = NULL;
+
+# undef png_ptr
}
-#endif
+#endif /* READ_PACK */
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-/* Chop 16-bit depth files to 8-bit depth */
-void PNGAPI
-png_set_strip_16(png_structrp png_ptr)
+#if defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+# ifdef PNG_READ_tRNS_SUPPORTED
+static unsigned int
+fill_transparent_pixel(png_const_structrp png_ptr, png_byte *trans)
+ /* Fill a byte array according to the transparent pixel value and return a
+ * count of the number of bytes. Low bit depth gray values are replicated in
+ * the first byte. Writes from 1 to 6 bytes.
+ */
{
- png_debug(1, "in png_set_strip_16");
+ /* There must be a tRNS chunk and this must not be a palette image: */
+ debug(png_ptr->num_trans == 1 &&
+ !(png_ptr->color_type & (PNG_COLOR_MASK_ALPHA+PNG_COLOR_MASK_PALETTE)));
- if (!png_rtran_ok(png_ptr, 0))
- return;
+ if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* gray */
+ {
+ unsigned int t = png_ptr->trans_color.gray;
+ unsigned int depth = png_ptr->bit_depth;
- png_ptr->transformations |= PNG_16_TO_8;
+ if (depth < 16U)
+ {
+ /* ISO PNG 11.3.2.1 "tRNS Transparency": "If the image bit depth is
+ * less than 16, the least significant bits are used and the others are
+ * 0." So mask out the upper bits.
+ */
+ t &= (1U<> 8);
+ trans[1] = PNG_BYTE(t);
+ return 2U;
+ }
+
+ else /* color */ switch (png_ptr->bit_depth)
+ {
+ case 8: /* 8-bit RGB */
+ trans[0] = PNG_BYTE(png_ptr->trans_color.red);
+ trans[1] = PNG_BYTE(png_ptr->trans_color.green);
+ trans[2] = PNG_BYTE(png_ptr->trans_color.blue);
+ return 3U;
+
+ case 16: /* 16-bit RGB */
+ trans[0] = PNG_BYTE(png_ptr->trans_color.red >> 8);
+ trans[1] = PNG_BYTE(png_ptr->trans_color.red);
+ trans[2] = PNG_BYTE(png_ptr->trans_color.green >> 8);
+ trans[3] = PNG_BYTE(png_ptr->trans_color.green);
+ trans[4] = PNG_BYTE(png_ptr->trans_color.blue >> 8);
+ trans[5] = PNG_BYTE(png_ptr->trans_color.blue);
+ return 6U;
+
+ default:
+ NOT_REACHED;
+ return 0U; /* safe */
+ }
}
-#endif
+# endif /* READ_tRNS */
+#endif /* READ_EXPAND || READ_BACKGROUND */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Flags for png_init_expand */
+#define PNG_EXPAND_PALETTE 1U /* palette images only, includes tRNS */
+#define PNG_EXPAND_LBD_GRAY 2U /* grayscale low-bit depth only */
+#define PNG_EXPAND_tRNS 4U /* non-palette images only */
+
+/* This struct is only required for tRNS matching, but it is convenient to
+ * allocated it anyway even if READ_tRNS is not supported.
+ */
+typedef struct
+{
+ png_transform tr;
+ unsigned int ntrans; /* number of bytes below */
+ png_byte transparent_pixel[6]; /* the transparent pixel value */
+} png_expand;
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+/* Look for colors matching the trans_color in png_ptr, low bit depth gray is
+ * covered below so this only need handle 8 abd 16-bit channels.
+ */
+static void
+png_do_expand_tRNS(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_expand *tr = png_transform_cast(png_expand, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp;
+ const unsigned int spixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
+ unsigned int alpha_size;
+
+ /* We expect opaque and transparent pixels to be interleaved but with long
+ * sequences of each. Because we are adding an alpha channel we must copy
+ * down.
+ */
+ debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA));
+ debug(spixel_size == tr->ntrans);
+ sp += PNG_TC_ROWBYTES(*tc);
+ tc->sp = dp;
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ tc->invalid_info |= PNG_INFO_tRNS;
+ tc->transparent_alpha = 1U;
+ alpha_size = (PNG_TC_PIXEL_DEPTH(*tc)>>3) - spixel_size;
+ debug(alpha_size == 1 || alpha_size == 2);
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ do
+ {
+ unsigned int i = spixel_size;
+ png_byte alpha = 0U;
+
+ dp -= alpha_size;
+ alpha = 0U;
+
+ /* Copy and check one source pixel (backwards, to avoid any
+ * overwrite):
+ */
+ do if ((*--dp = *--sp) != tr->transparent_pixel[--i]) /* pixel != tRNS */
+ alpha = 0xFFU;
+ while (i != 0U);
+
+ /* i == 0 */
+ do
+ dp[spixel_size + i] = alpha;
+ while (++i < alpha_size);
+ } while (sp > ep);
+
+ debug(sp == ep && dp == tc->dp); /* else overwrite */
+# undef png_ptr
+}
+#endif /* READ_tRNS */
+
+/* Expand grayscale images of less than 8-bit depth to 8 bits.
+ * libpng 1.7.0: this no longer expands everything, it just expands the low bit
+ * depth gray row. It does *NOT* expand the tRNS into an alpha channel unless
+ * it is told to do so.
+ *
+ * API CHANGE: the function now does what it was always meant to do.
+ *
+ * This is like do_unpack except that the packed data is expanded to the full
+ * 8-bit range; scaled up. This is not a good thing to do on an indexed image;
+ * the indices will be invalid.
+ *
+ * The tRNS handling is included here too; speed is not important because the
+ * result will always be cached unless the PNG is very small.
+ */
+static void
+png_do_expand_lbd_gray(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ const png_const_bytep ep = dp;
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const unsigned int bit_depth = tc->bit_depth;
+# ifdef PNG_READ_sBIT_SUPPORTED
+ unsigned int insignificant_bits = 0U;
+# endif /* READ_sBIT */
+# ifdef PNG_READ_tRNS_SUPPORTED
+ unsigned int gray = 0xffffU; /* doesn't match anything */
+ unsigned int do_alpha = 0U;
+# endif /* READ_tRNS */
+
+ sp += PNG_TC_ROWBYTES(*tc); /* last byte +1 */
+ tc->bit_depth = 8U;
+ tc->invalid_info |= PNG_INFO_tRNS;
+# ifdef PNG_READ_sBIT_SUPPORTED
+ if (bit_depth > 1U /* irrelevant for bit depth 1 */ &&
+ !(tc->invalid_info & PNG_INFO_sBIT) &&
+ tc->sBIT_G > 0U/*SAFETY*/ && tc->sBIT_G < bit_depth)
+ insignificant_bits = bit_depth - tc->sBIT_G;
+# endif /* READ_sBIT */
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (((*transform)->args & PNG_EXPAND_tRNS) != 0)
+ {
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ tc->transparent_alpha = 1U;
+ gray = png_ptr->trans_color.gray & ((1U << bit_depth)-1U);
+ do_alpha = 1U;
+ }
+
+ /* This helps avoid cluttering the code up with #ifdefs: */
+# define check_tRNS if (do_alpha) *--dp = (pixel != gray) * 255U;
+# else /* !READ_tRNS */
+# define check_tRNS
+# endif /* READ_tRNS */
+
+ dp += PNG_TC_ROWBYTES(*tc); /* pre-decremented below */
+
+ switch (bit_depth)
+ {
+ case 1:
+ {
+ unsigned int shift = 7U & -tc->width;
+ unsigned int s = *--sp;
+
+ for(;;)
+ {
+ if (shift == 8U) s = *--sp, shift = 0;
+
+ {
+ const unsigned int pixel = (s >> shift) & 1U;
+
+ check_tRNS
+ *--dp = PNG_BYTE(pixel * 255U);
+ if (dp <= ep) break;
+ }
+ ++shift;
+ }
+
+ debug(dp == ep && shift == 7U && sp == tc->sp);
+ break;
+ }
+
+ case 2:
+ {
+ unsigned int shift = 7U & -(tc->width << 1)/*overflow ok*/;
+ unsigned int s = *--sp;
+
+ for (;;)
+ {
+ if (shift == 8U) s = *--sp, shift = 0;
+ {
+ const unsigned int pixel = (s >> shift) & 3U;
+
+ check_tRNS
+
+# ifdef PNG_READ_sBIT_SUPPORTED
+ /* 'sig_bits' must be 1 or 2 leaving insignificant_bits 0 or
+ * 1. This may look silly but it allows a compact representation
+ * of 1 bit gray + 1 bit alpha (transparency):
+ */
+ if (insignificant_bits /* only 1 bit significant */)
+ *--dp = PNG_BYTE((pixel >> 1) * 255U);
+
+ else
+# endif
+ *--dp = PNG_BYTE(pixel * 85U);
+
+ if (dp <= ep) break;
+ }
+ shift += 2U;
+ }
+
+ debug(dp == ep && shift == 6U && sp == tc->sp);
+ break;
+ }
+
+ case 4:
+ {
+ unsigned int shift = 7U & -(tc->width << 2)/*overflow ok*/;
+ unsigned int s = *--sp;
+# ifdef PNG_READ_sBIT_SUPPORTED
+ const unsigned int div = (1U << (4U-insignificant_bits)) - 1U;
+# endif
+
+ for (;;)
+ {
+ if (shift == 8U) s = *--sp, shift = 0;
+ {
+ unsigned int pixel = (s >> shift) & 15U;
+
+ check_tRNS
+
+# ifdef PNG_READ_sBIT_SUPPORTED
+ /* insignificant_bits may be 0, 1, 2 or 3, requiring a multiply
+ * by 17, 255/7, 85 or 255. Since this operation is always
+ * cached we don't much care about the time to do the divide
+ * below.
+ */
+ if (insignificant_bits)
+ pixel = ((pixel>>insignificant_bits) * 255U + (div>>1)) / div;
+
+ else
+# endif
+ pixel *= 17U;
+
+ *--dp = PNG_BYTE(pixel);
+ if (dp <= ep) break;
+ }
+
+ shift += 4U;
+ }
+
+ debug(dp == ep && shift == 4U && sp == tc->sp);
+ break;
+ }
+
+ default:
+ impossible("bit depth");
+ }
+
+ tc->sp = ep;
+
+# undef check_tRNS
+# undef png_ptr
+}
+
+static void
+png_init_expand(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* The possible combinations are:
+ *
+ * 1) PALETTE: the 'palette' flag should be set on the transform control and
+ * all that need be done is cancel this to cause the cache code to do the
+ * expansion.
+ *
+ * 2) LBP_GRAY, LBP_GRAY+tRNS: use png_do_expand_lbd_gray to do the required
+ * expand. Can be cached.
+ *
+ * 3) tRNS: scan the row for the relevant tRNS value.
+ *
+ * Note that expanding 8 to 16 bits is a byte op in pngtrans.c (it just
+ * replicates bytes).
+ */
+ if (tc->palette)
+ {
+ debug(tc->caching && !(tc->format & PNG_FORMAT_FLAG_COLORMAP));
+
+ if (((*transform)->args & PNG_EXPAND_PALETTE) != 0U)
+ {
+ tc->palette = 0U;
+ tc->invalid_info |= PNG_INFO_PLTE + PNG_INFO_tRNS;
+ tc->cost = PNG_CACHE_COST_LIMIT; /* the cache is required! */
+ }
+
+ /* Note that this needs to happen when the row is processed (!tc->init) as
+ * well.
+ */
+ }
+
+ else if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP))
+ {
+ png_uint_32 args = (*transform)->args & PNG_BIC_MASK(PNG_EXPAND_PALETTE);
+ unsigned int bit_depth = tc->bit_depth;
+
+ debug(tc->init);
+
+ if (bit_depth >= 8U)
+ args &= PNG_BIC_MASK(PNG_EXPAND_LBD_GRAY);
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (png_ptr->num_trans == 0U ||
+ (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0U ||
+ (tc->invalid_info & PNG_INFO_tRNS) != 0U)
+# endif
+ args &= PNG_BIC_MASK(PNG_EXPAND_tRNS);
+
+ (*transform)->args = args;
+
+ switch (args)
+ {
+ case PNG_EXPAND_LBD_GRAY:
+ tc->bit_depth = 8U;
+ tc->invalid_info |= PNG_INFO_tRNS;
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ (*transform)->fn = png_do_expand_lbd_gray;
+ break;
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ case PNG_EXPAND_LBD_GRAY + PNG_EXPAND_tRNS:
+ tc->bit_depth = 8U;
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ tc->invalid_info |= PNG_INFO_tRNS;
+ tc->transparent_alpha = 1U;
+
+ /* In this case tRNS must be left unmodified for the expansion code
+ * to handle.
+ */
+ if (tc->init == PNG_TC_INIT_FINAL)
+ (*transform)->fn = png_do_expand_lbd_gray;
+ break;
+
+ case PNG_EXPAND_tRNS:
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ png_expand *tr = png_transform_cast(png_expand, *transform);
+
+ affirm((tc->bit_depth == 8U || tc->bit_depth == 16U) &&
+ (tc->format &
+ (PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA)) == 0U);
+
+ tr->ntrans = fill_transparent_pixel(png_ptr,
+ tr->transparent_pixel);
+ tr->tr.fn = png_do_expand_tRNS;
+ } /* TC_INIT_FINAL */
+
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ tc->invalid_info |= PNG_INFO_tRNS;
+ tc->transparent_alpha = 1U;
+ break;
+# endif /* READ_tRNS */
+
+ default: /* transform not applicable */
+ (*transform)->fn = NULL;
+ break;
+ }
+
+ implies(tc->init == PNG_TC_INIT_FINAL,
+ (*transform)->fn != png_init_expand);
+ }
+
+ else /* not applicable */
+ {
+ debug(tc->init);
+ (*transform)->fn = NULL;
+ NOT_REACHED;
+ }
+# undef png_ptr
+}
+
+void PNGAPI
+png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
+ PNG_TR_EXPAND)->args |= PNG_EXPAND_LBD_GRAY;
+}
+
+/* Expand paletted images to 8-bit RGB or, if there is a tRNS chunk, RGBA.
+ * Note that this is effectively handled by the read code palette optimizations.
+ *
+ * API CHANGE: this used to have the completely unexpected side effect of
+ * turning on the above two optimizations.
+ */
+void PNGAPI
+png_set_palette_to_rgb(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
+ PNG_TR_EXPAND)->args |= PNG_EXPAND_PALETTE;
+}
+
+/* Expand paletted images to RGB, expand grayscale images of less than 8-bit
+ * depth to 8-bit depth, and expand tRNS chunks to alpha channels. I.e. all the
+ * above.
+ */
+void PNGAPI
+png_set_expand(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+ png_set_palette_to_rgb(png_ptr);
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+}
+#endif /* READ_EXPAND */
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) ||\
+ defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+
+#define PNG_INIT_STRIP_ALPHA 1U
+#define PNG_INIT_EXPAND_tRNS 2U
+static void
+png_init_alpha(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ int required = 0;
+
+# if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_tRNS_SUPPORTED)
+ if ((*transform)->args & PNG_INIT_EXPAND_tRNS)
+ {
+ /* Prior to 1.7 the alpha channel was stripped after expanding the tRNS
+ * chunk, so this effectively cancelled out the expand.
+ */
+ if (png_ptr->num_trans > 0 && !tc->palette &&
+ !((*transform)->args & PNG_INIT_STRIP_ALPHA))
+ {
+ debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0);
+
+ required = 1;
+ tc->expand_tRNS = 1U;
+
+ /* This happens as a result of an explicit API call to
+ * png_set_tRNS_to_alpha, so expand low-bit-depth gray too:
+ */
+ if (tc->init == PNG_TC_INIT_FORMAT)
+ png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
+ PNG_TR_EXPAND)->args |= PNG_EXPAND_tRNS + PNG_EXPAND_LBD_GRAY;
+ }
+
+ else
+ (*transform)->args &= PNG_BIC_MASK(PNG_INIT_EXPAND_tRNS);
+ }
+# endif /* READ_EXPAND && READ_tRNS */
+
+# ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ if ((*transform)->args & PNG_INIT_STRIP_ALPHA)
+ {
+ /* When compose is being done tRNS will be expanded regardless of the
+ * above test. Rather that trying to work out if this will happen the
+ * code just inserts a strip operation; it will be removed later if it
+ * is not needed.
+ */
+ required = 1;
+ tc->strip_alpha = 1U;
+
+ if (tc->init == PNG_TC_INIT_FORMAT)
+ png_add_strip_alpha_byte_ops(png_ptr);
+ }
+# endif /* READ_STRIP_ALPHA */
+
+ if (!required)
+ (*transform)->fn = NULL;
+# undef png_ptr
+}
+#endif /* READ_EXPAND || READ_STRIP_ALPHA */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expand tRNS chunks to alpha channels. This only expands the tRNS chunk on
+ * non-palette formats; call png_set_palette_to_rgb to get the corresponding
+ * effect for a palette.
+ *
+ * Note that this will expand low bit depth gray if there is a tRNS chunk, but
+ * if not nothing will happen.
+ *
+ * API CHANGE: this used to do all the expansions, it was rather pointless
+ * calling it.
+ */
+void PNGAPI
+png_set_tRNS_to_alpha(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)->
+ args |= PNG_INIT_EXPAND_tRNS;
+}
+#endif /* READ_EXPAND */
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
void PNGAPI
png_set_strip_alpha(png_structrp png_ptr)
{
- png_debug(1, "in png_set_strip_alpha");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= PNG_STRIP_ALPHA;
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)->
+ args |= PNG_INIT_STRIP_ALPHA;
}
+#endif /* READ_STRIP_ALPHA */
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+static void
+png_do_chop_16_to_8(png_transformp *transform, png_transform_controlp tc)
+ /* This is actually a repeat of the byte transform, unnecessary code
+ * replication.
+ *
+ * TODO: remove this
+ */
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */
+ png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */
+
+ debug(tc->bit_depth == 16U);
+ tc->sp = dp;
+ tc->bit_depth = 8U;
+
+ while (sp < ep)
+ *dp++ = *sp, sp += 2;
+
+ debug(sp == ep);
+# undef png_ptr
+
+ PNG_UNUSED(transform)
+}
+
+/* A transform containing some useful scaling values... */
+typedef struct
+{
+ png_transform tr;
+ png_uint_32 shifts; /* 4 4-bit values preceeded by a shibboleth (1) */
+ png_uint_32 channel_scale[4];
+} png_transform_scale_16_to_8;
+
+/* Scale rows of bit depth 16 down to 8 accurately */
+static void
+png_do_scale_16_to_8(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */
+ png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */
+ png_transform_scale_16_to_8 *tr =
+ png_transform_cast(png_transform_scale_16_to_8, *transform);
+ png_uint_32p scale = 0;
+ png_uint_32 shift = 1U; /* set the shibboleth at the start */
+
+ debug(tc->bit_depth == 16U);
+ tc->sp = dp;
+ tc->bit_depth = 8U;
+
+ while (sp < ep)
+ {
+ /* The input is an array of 16 bit components, these must be scaled to
+ * 8 bits each taking into account the sBIT setting. The calculation
+ * requires that the insignificant bits be stripped from the input value
+ * via a shift then scaled back to 8 bits:
+ *
+ * output = ((input >> shift) * scale + round) >> 24
+ *
+ * The shifts are packed into tr->shifts, with the end of the list marked
+ * by a shibboleth, 1, which is preset above.
+ */
+ png_uint_32 v = png_get_uint_16(sp);
+
+ sp += 2;
+
+ if (shift == 1U)
+ {
+ shift = tr->shifts;
+ scale = tr->channel_scale;
+ }
+
+ *dp++ = PNG_BYTE(((v >> (shift & 0xFU)) * *scale++ + 0x800000U) >> 24);
+ shift >>= 4;
+ }
+
+ affirm(sp == ep);
+# undef png_ptr
+}
+
+static int
+add_scale(png_transform_scale_16_to_8 *tr, unsigned int sBIT, unsigned int ch)
+{
+ /* This is the output max (255) scaled by 2^24 divided by the input max'
+ * (which is variable) and rounded. It gives the exact 8-bit answer for all
+ * input sBIT depths when used in the calculation:
+ *
+ * output = ((input >> shift) * scale + 0x800000U) >> 24
+ */
+ tr->channel_scale[ch] = (0xFF000000U + ((1U<>1)) / ((1U<shifts |= ((16U-sBIT) & 0xFU) << (4U*ch);
+
+ /* The result says whether there are 8 or fewer significant bits in the
+ * input value; if so we can just drop the low byte.
+ */
+ return sBIT <= 8U;
+}
+
+static void
+png_init_scale_16_to_8(png_transformp *transform, png_transform_controlp tc)
+{
+ if (tc->bit_depth == 16U)
+ {
+# define png_ptr (tc->png_ptr)
+ tc->bit_depth = 8U;
+ /* But this invalidates tRNS (a 16-bit tRNS cannot be updated to match
+ * 8-bit data correctly).
+ */
+ tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
+ /* TODO: These need further processing: PNG_INFO_bKGD */
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ png_transform_scale_16_to_8 *tr =
+ png_transform_cast(png_transform_scale_16_to_8, *transform);
+
+ /* Set the scale factors for each channel (up to 4), the factors are
+ * made so that:
+ *
+ * ((channel >> shift) * factor + 0x800000U) >> 24
+ *
+ * Gives the required 8-bit value. The 'shift' is stored in a single
+ * png_uint_32 with a shibboleth at the end.
+ */
+ unsigned int channels = 0U;
+ int chop_ok = 1;
+
+ tr->shifts = 0U;
+
+ /* This adds up to four scale factors, the remainder are left as 0
+ * which is safe and leads to obvious errors in the output images in
+ * the event of an (internal) error.
+ */
+ if (tc->format & PNG_FORMAT_FLAG_COLOR)
+ chop_ok &= add_scale(tr, tc->sBIT_R, channels++);
+
+ chop_ok &= add_scale(tr, tc->sBIT_G, channels++);
+
+ if (tc->format & PNG_FORMAT_FLAG_COLOR)
+ chop_ok &= add_scale(tr, tc->sBIT_B, channels++);
+
+ if (tc->format & PNG_FORMAT_FLAG_ALPHA)
+ chop_ok &= add_scale(tr, tc->sBIT_A, channels++);
+
+ if (chop_ok)
+ tr->tr.fn = png_do_chop_16_to_8;
+
+ else
+ {
+ int handled = 1;
+
+ /* Add the shibboleth at the end */
+ tr->shifts |= 1U << (4U*channels);
+ tr->tr.fn = png_do_scale_16_to_8;
+
+ /* sBIT is a little tricky; it has to be processed in the scaling
+ * operation. The result will have the same number of bits unless
+ * there were more than 8 before. The sBIT flags in the transform
+ * control are left unchanged here because the data is still valid,
+ * unless all the values end up as 8 in which case there is no
+ * remaining sBIT info.
+ *
+ * Note that fields, such as alpha, which are not set for this row
+ * format will always have max values, so won't reset 'handled':
+ */
+ if (tc->sBIT_R >= 8U) tc->sBIT_R = 8U; else handled = 0;
+ if (tc->sBIT_G >= 8U) tc->sBIT_G = 8U; else handled = 0;
+ if (tc->sBIT_B >= 8U) tc->sBIT_B = 8U; else handled = 0;
+ if (tc->sBIT_A >= 8U) tc->sBIT_A = 8U; else handled = 0;
+
+ /* If all the sBIT values were >= 8U all the bits are now
+ * significant:
+ */
+ if (handled)
+ tc->invalid_info |= PNG_INFO_sBIT;
+ }
+ }
+
+# undef png_ptr
+ }
+
+ else /* not applicable */
+ (*transform)->fn = NULL;
+}
+
+void PNGAPI
+png_set_scale_16(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, sizeof (png_transform_scale_16_to_8),
+ png_init_scale_16_to_8, PNG_TR_SCALE_16_TO_8);
+}
+#endif /* READ_SCALE_16_TO_8 */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ /* Code that depends on READ_GAMMA support; RGB to gray convertion and
+ * background composition (including the various alpha-mode handling
+ * operations which produce pre-multiplied alpha by composing on 0).
+ */
+/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
+static png_fixed_point
+png_reciprocal(png_fixed_point a)
+{
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = floor(1E10/a+.5);
+
+ if (r <= 2147483647. && r >= -2147483648.)
+ return (png_fixed_point)r;
+#else
+ png_fixed_point res;
+
+ if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0)
+ return res;
#endif
-#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
+ return 0; /* error/overflow */
+}
+
+/* This is the shared test on whether a gamma value is 'significant' - whether
+ * it is worth doing gamma correction. 'significant_bits' is the number of bits
+ * in the values to be corrected which are significant.
+ */
+static int
+png_gamma_significant(png_const_structrp png_ptr, png_fixed_point gamma_val,
+ unsigned int sbits)
+{
+#if 0
+ /* This seems to be wrong. The issue is that when the app asks for a higher
+ * bit depth output than the input has significant bits it causes gamma
+ * correction to be skipped (this was the intent) however there's no
+ * particular guarantee that the app won't go on to do further gamma
+ * processing - pngstest does this - and this messes up the results
+ * completely.
+ *
+ * TODO: work out how to optimize this correctly.
+ */
+ /* The following table lists the threshold as a difference from PNG_FP_1 at
+ * which the gamma correction will make a change to at least an 'sbits'
+ * value. There is no entry for 1 bit values; gamma correction is never
+ * significant.
+ */
+ static const png_uint_16 gamma_threshold_by_sbit[15][2] =
+ {
+ { 36907, 63092 }, /* 2 bits */
+ { 17812, 21518 }, /* 3 bits */
+ { 8675, 9496 }, /* 4 bits */
+ { 4290, 4484 }, /* 5 bits */
+ { 2134, 2181 }, /* 6 bits */
+ { 1064, 1075 }, /* 7 bits */
+ { 531, 534 }, /* 8 bits */
+ { 265, 266 }, /* 9 bits */
+ { 132, 132 }, /* 10 bits */
+ { 66, 66 }, /* 11 bits */
+ { 33, 33 }, /* 12 bits */
+ { 16, 16 }, /* 13 bits */
+ { 8, 8 }, /* 14 bits */
+ { 4, 4 }, /* 15 bits */
+ { 2, 2 }, /* 16 bits */
+ };
+
+ /* Handle out of range values in release by doing the gamma correction: */
+ debug_handled(sbits > 0U && sbits <= 16U);
+ if (sbits == 0U || sbits > 16U)
+ return 1;
+
+ /* 1 bit input or zero gamma, no correction possible/required: */
+ if (gamma_val == 0 || sbits < 2U)
+ return 0;
+
+ if (gamma_val < PNG_FP_1 - gamma_threshold_by_sbit[sbits-2U][0U])
+ return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold;
+
+ else if (gamma_val > PNG_FP_1 + gamma_threshold_by_sbit[sbits-2U][1U])
+ return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold;
+#else /* FIXUP */
+ if (gamma_val < PNG_FP_1)
+ return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold;
+
+ else if (gamma_val > PNG_FP_1)
+ return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold;
+
+ PNG_UNUSED(sbits)
+#endif /* FIXUP */
+
+ return 0; /* not significant */
+}
+
+static int
+png_gamma_equal(png_const_structrp png_ptr, png_fixed_point g1,
+ png_fixed_point g2, png_fixed_point *c, unsigned int sbits)
+ /* Gamma values are equal, or at least one is unknown; c is the correction
+ * factor from g1 to g2, i.e. g2/g1.
+ */
+{
+ return sbits == 1U || g1 == 0 || g2 == 0 || g1 == g2 ||
+ (png_muldiv(c, g2, PNG_FP_1, g1) &&
+ !png_gamma_significant(png_ptr, *c, sbits));
+}
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+int
+png_need_gamma_correction(png_const_structrp png_ptr, png_fixed_point gamma,
+ int sRGB_output)
+ /* This is a hook for the simplified code; it just decides whether or not the
+ * given gamma (which defaults to that of the PNG data) is close enough to
+ * linear or sRGB not to require gamma correction.
+ */
+{
+ if (gamma == 0)
+ gamma = png_ptr->colorspace.gamma;
+
+ if (gamma != 0 &&
+ (png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
+ PNG_COLORSPACE_HAVE_GAMMA)
+ {
+
+ if (sRGB_output && !png_muldiv(&gamma, gamma, PNG_GAMMA_sRGB, PNG_FP_1))
+ return 0; /* overflow, so no correction */
+
+ return png_gamma_significant(png_ptr, gamma, (png_ptr->color_type &
+ PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth);
+ }
+
+ return 0; /* no info, no correction */
+}
+#endif /* SIMPLIFIED_READ */
+
+#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
+/* Fixed point gamma.
+ *
+ * The code to calculate the tables used below can be found in the shell script
+ * contrib/tools/intgamma.sh
+ *
+ * To calculate gamma this code implements fast log() and exp() calls using only
+ * fixed point arithmetic. This code has sufficient precision for either 8-bit
+ * or 16-bit sample values.
+ *
+ * The tables used here were calculated using simple 'bc' programs, but C double
+ * precision floating point arithmetic would work fine.
+ *
+ * 8-bit log table
+ * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
+ * 255, so it's the base 2 logarithm of a normalized 8-bit floating point
+ * mantissa. The numbers are 32-bit fractions.
+ */
+static const png_uint_32
+png_8bit_l2[128] =
+{
+ 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
+ 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
+ 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
+ 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
+ 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
+ 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
+ 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
+ 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
+ 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
+ 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
+ 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
+ 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
+ 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
+ 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
+ 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
+ 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
+ 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
+ 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
+ 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
+ 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
+ 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
+ 24347096U, 0U
+
+#if 0 /* NOT USED */
+ /* The following are the values for 16-bit tables - these work fine for the
+ * 8-bit conversions but produce very slightly larger errors in the 16-bit
+ * log (about 1.2 as opposed to 0.7 absolute error in the final value). To
+ * use these all the shifts below must be adjusted appropriately.
+ */
+ 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
+ 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
+ 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
+ 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
+ 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
+ 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
+ 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
+ 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
+ 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
+ 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
+ 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
+ 1119, 744, 372
+#endif
+};
+
+#if 0 /* UNUSED */
+static png_int_32
+png_log8bit(unsigned int x)
+{
+ png_uint_32 lg2 = 0U;
+
+ /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
+ * because the log is actually negate that means adding 1. The final
+ * returned value thus has the range 0 (for 255 input) to 7.994 (for 1
+ * input), return -1 for the overflow (log 0) case, - so the result is
+ * always at most 19 bits.
+ */
+ if ((x &= 0xffU) == 0U) /* 0 input, -inf output */
+ return -0xfffff;
+
+ if ((x & 0xf0U) == 0U)
+ lg2 = 4U, x <<= 4;
+
+ if ((x & 0xc0U) == 0U)
+ lg2 += 2U, x <<= 2;
+
+ if ((x & 0x80U) == 0U)
+ lg2 += 1U, x <<= 1;
+
+ /* result is at most 19 bits, so this cast is safe: */
+ return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128U]+32768U)>>16));
+}
+#endif /* UNUSED */
+
+/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
+ * for 16-bit images we use the most significant 8 bits of the 16-bit value to
+ * get an approximation then multiply the approximation by a correction factor
+ * determined by the remaining up to 8 bits. This requires an additional step
+ * in the 16-bit case.
+ *
+ * We want log2(value/65535), we have log2(v'/255), where:
+ *
+ * value = v' * 256 + v''
+ * = v' * f
+ *
+ * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
+ * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
+ * than 258. The final factor also needs to correct for the fact that our 8-bit
+ * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
+ *
+ * This gives a final formula using a calculated value 'x' which is value/v' and
+ * scaling by 65536 to match the above table:
+ *
+ * log2(x/257) * 65536
+ *
+ * Since these numbers are so close to '1' we can use simple linear
+ * interpolation between the two end values 256/257 (result -368.61) and 258/257
+ * (result 367.179). The values used below are scaled by a further 64 to give
+ * 16-bit precision in the interpolation:
+ *
+ * Start (256): -23591
+ * Zero (257): 0
+ * End (258): 23499
+ *
+ * In libpng 1.7.0 this is further generalized to return -log2(value/maxval) for
+ * any maxval up to 65535. This is done by evaluating -log2(value/65535) first
+ * then adjusting for the required maxval:
+ *
+ * ( value) (value 65535) (value) ( 65535)
+ * -log2(------) = -log2(----- x ------) = -log2(-----)-log2(------)
+ * (maxval) (65535 maxval) (65535) (maxval)
+ *
+ * The extra argument, 'factor', is (2^(16+12))*log2(65535/maxval) (a positive
+ * value less than 2^32) and this is *subtracted* from the intermediate
+ * calculation below.
+ */
+static png_int_32
+png_log(unsigned int x, png_uint_32 factor)
+ /* x: a value of up to 16 bits,
+ * factor: a 4.28 number which is subtracted from the log below
+ */
+{
+ png_uint_32 lg2 = 0U;
+
+ /* As above, but now the input has 16 bits. */
+ if ((x &= 0xffffU) == 0U)
+ return -0xfffff;
+
+ if ((x & 0xff00U) == 0U)
+ lg2 = 8U, x <<= 8;
+
+ if ((x & 0xf000U) == 0U)
+ lg2 += 4U, x <<= 4;
+
+ if ((x & 0xc000U) == 0U)
+ lg2 += 2U, x <<= 2;
+
+ if ((x & 0x8000U) == 0U)
+ lg2 += 1U, x <<= 1;
+
+ /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
+ * value.
+ */
+ lg2 <<= 28;
+ lg2 += (png_8bit_l2[(x>>8)-128U]+8U) >> 4;
+
+ /* Now we need to interpolate the factor, this requires a division by the top
+ * 8 bits. Do this with maximum precision.
+ */
+ {
+ png_uint_32 i = x;
+
+ i = ((i << 16) + (i >> 9)) / (x>> 8);
+
+ /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
+ * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us
+ * exactly 16 bits to interpolate to get the low bits of the result.
+ * Round the answer. Note that the end point values are scaled by 64 to
+ * retain overall precision and that 'lg2' is current scaled by an extra
+ * 12 bits, so adjust the overall scaling by 6-12. Round at every step.
+ */
+ i -= 1U << 24;
+
+ if (i <= 65536U) /* <= '257' */
+ lg2 += ((23591U * (65536U-i)) + (1U << (16+6-12-1))) >> (16+6-12);
+
+ else
+ lg2 -= ((23499U * (i-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
+ }
+
+ if (lg2 >= factor)
+ return (png_int_32)/*SAFE*/((lg2 - factor + 2048U) >> 12);
+
+ else /* the result will be greater than 1.0, so negative: */
+ return -(png_int_32)/*SAFE*/((factor - lg2 + 2048U) >> 12);
+}
+
+#if 0 /* UNUSED */
+static png_int_32
+png_log16bit(unsigned int x)
+{
+ return png_log(x, 0U);
+}
+#endif /* UNUSED */
+
+/* libpng 1.7.0: generalization of png_log{8,16}bit to accept an n-bit input
+ * value. We want to maintain 1% accuracy in linear light space. This
+ * corresponds to, approximately, (1*g)% in a gamma encoded space where the
+ * gamma encoding is 'g' (in the PNG sense, e.g. 0.45455 for sRGB). Apparently
+ * this requires unbounded accuracy as the gamma encoding value goes down and
+ * this is a problem for modern HDR data because it may require a high gamma to
+ * accurately encode image data over a wide dynamic range; the dynamic range of
+ * 16-bit linear data is only 655:1 if 1% accuracy is needed!
+ *
+ * However 16-bit gamma encoded data is still limited because PNG can only
+ * express gamma encoding. (A log-to-base-1.01 encoding is unlimited; a 12-bit
+ * value, with 4094 steps, has a dynamic range of more than 1:10^17, which
+ * exceeds the human eye's range of 1:10^14.)
+ *
+ * Notice that sRGB uses a 1/2.4 encoding and CIELab uses a 1/3 encoding. It is
+ * obvious that, if we assume a maximum D difference in the luminance of
+ * adjacent pixel values the dynamic range is given by the lowest pixel value
+ * which is D or less greater than its predecessor, so:
+ *
+ * ( P ) (1)
+ * (---)^(-) = D
+ * (P-1) (g)
+ *
+ * and the maximum dynamic range that can be achieved using M+1 separate values,
+ * where M+1 is 2^N-1 for an N bit value, reserving the first value for 0, is:
+ *
+ * (M) (1)
+ * range(R) = (-)^(-)
+ * (P) (g)
+ *
+ * So we can eliminate 'P' from the two equations:
+ *
+ * P = (P-1) x (D^g)
+ *
+ * D^g
+ * P = -----
+ * D^g-1
+ *
+ * (M x (D^g-1)) (1)
+ * R = (-----------)^(-)
+ * ( D^g ) (g)
+ *
+ * (M x (D^g-1)) ^ (1/g)
+ * = ---------------------
+ * D
+ *
+ * Which is a function in two variables (R and g) for a given D (maximum delta
+ * between two adjacent pixel values) and M (number of pixel values, controlled
+ * by the channel bit depth).
+ *
+ * See contrib/tools/dynamic-range.c for code exploring this function. This
+ * program will output the optimal gamma for a given number of bits and
+ * precision.
+ *
+ * The range of sensitivity of human vision is roughly as follows (this comes
+ * from the wikipedia article on scotopic vision):
+ *
+ * scotopic: 10^-6 to 10^-3.5 cd/m^2
+ * mesopic: 10^-3 to 10^0.5 cd/m^2
+ * photopic: 10 to 10^8 cd/m^2
+ *
+ * Giving a total range of about 1:10^14. The maximum precision at which this
+ * range can be achieved using 16-bit channels is about .15% using a gamma of
+ * 36, higher ranges are possible using higher gammas but precision is reduced.
+ * The range with 1% precision and 16-bit channels is 1:10^104, using a gamma of
+ * 240.
+ *
+ * In general the optimal gamma for n-bit channels (where 'n' is at least 7 and
+ * precision is .01 or less) is:
+ *
+ * 2^n * precision
+ * gamma = ---------------
+ * 2.736
+ *
+ * Or: (24000 * precision) for 16-bit data.
+ *
+ * The net effect is that we can't rely on the encoding gamma being limited to
+ * values around 1/2.5!
+ */
+static png_int_32
+png_log_nbit(unsigned int x, unsigned int nbits)
+{
+ static const png_uint_32 factors[16] =
+ {
+ 4294961387U, /* 1 bit */
+ 3869501255U, /* 2 bit */
+ 3541367788U, /* 3 bit */
+ 3246213428U, /* 4 bit */
+ 2965079441U, /* 5 bit */
+ 2690447525U, /* 6 bit */
+ 2418950626U, /* 7 bit */
+ 2148993476U, /* 8 bit */
+ 1879799410U, /* 9 bit */
+ 1610985205U, /* 10 bit */
+ 1342360514U, /* 11 bit */
+ 1073830475U, /* 12 bit */
+ 805347736U, /* 13 bit */
+ 536888641U, /* 14 bit */
+ 268441365U, /* 15 bit */
+ 0U /* 16 bit */
+ };
+
+ return png_log(x, factors[nbits-1]);
+}
+
+
+/* The 'exp()' case must invert the above, taking a 20-bit fixed point
+ * logarithmic value and returning a 16 or 8-bit number as appropriate. In
+ * each case only the low 16 bits are relevant - the fraction - since the
+ * integer bits (the top 4) simply determine a shift.
+ *
+ * The worst case is the 16-bit distinction between 65535 and 65534. This
+ * requires perhaps spurious accuracy in the decoding of the logarithm to
+ * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance
+ * of needing this accuracy in practice.
+ *
+ * To deal with this the following exp() function works out the exponent of the
+ * frational part of the logarithm by using an accurate 32-bit value from the
+ * top four fractional bits then multiplying in the remaining bits.
+ */
+static const png_uint_32
+png_32bit_exp[16] =
+{
+ /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
+ 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
+ 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
+ 2553802834U, 2445529972U, 2341847524U, 2242560872U
+};
+
+/* Adjustment table; provided to explain the numbers in the code below. */
+#if 0 /* BC CODE */
+for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
+ 11 44937.64284865548751208448
+ 10 45180.98734845585101160448
+ 9 45303.31936980687359311872
+ 8 45364.65110595323018870784
+ 7 45395.35850361789624614912
+ 6 45410.72259715102037508096
+ 5 45418.40724413220722311168
+ 4 45422.25021786898173001728
+ 3 45424.17186732298419044352
+ 2 45425.13273269940811464704
+ 1 45425.61317555035558641664
+ 0 45425.85339951654943850496
+#endif
+
+static png_uint_32
+png_exp(png_int_32 x)
+ /* Utility, the value 'x' must be in the range 0..0x1fffff */
+{
+ /* Obtain a 4-bit approximation */
+ png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];
+
+ /* Incorporate the low 12 bits - these decrease the returned value by
+ * multiplying by a number less than 1 if the bit is set. The multiplier
+ * is determined by the above table and the shift. Notice that the values
+ * converge on 45426 and this is used to allow linear interpolation of the
+ * low bits.
+ */
+ if (x & 0x800)
+ e -= (((e >> 16) * 44938U) + 16U) >> 5;
+
+ if (x & 0x400)
+ e -= (((e >> 16) * 45181U) + 32U) >> 6;
+
+ if (x & 0x200)
+ e -= (((e >> 16) * 45303U) + 64U) >> 7;
+
+ if (x & 0x100)
+ e -= (((e >> 16) * 45365U) + 128U) >> 8;
+
+ if (x & 0x080)
+ e -= (((e >> 16) * 45395U) + 256U) >> 9;
+
+ if (x & 0x040)
+ e -= (((e >> 16) * 45410U) + 512U) >> 10;
+
+ /* And handle the low 6 bits in a single block. */
+ e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
+
+ /* Handle the upper bits of x, note that this works for x up to 0x1fffff but
+ * fails for larger or negative x, where the shift (x >> 16) exceeds 31:
+ */
+ e >>= x >> 16;
+ return e;
+}
+
+#if 0 /* UNUSED */
+static png_byte
+png_exp8bit(png_int_32 lg2)
+{
+ /* The input is a negative fixed point (16:16) logarithm with a useable range
+ * of [0.0..8.0). Clamp the value so that the output of png_exp is in the
+ * range (254.5/255..0.5/255):
+ */
+ if (lg2 <= 185) /* -log2(254.5/255) */
+ return 255U;
+
+ else if (lg2 > 589453) /* -log2(0.5/255) */
+ return 0U;
+
+ else
+ {
+ /* Get a 32-bit value: */
+ png_uint_32 x = png_exp(lg2);
+
+ /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that
+ * the second, rounding, step can't overflow because of the first,
+ * subtraction, step.
+ */
+ x -= x >> 8;
+ return PNG_BYTE((x + 0x7fffffU) >> 24);
+ }
+}
+
+static png_uint_16
+png_exp16bit(png_int_32 lg2)
+{
+ if (lg2 <= 0) /* -log2(65534.5/65535) */
+ return 65535U;
+
+ else if (lg2 > 1114110) /* -log2(0.5/65535) */
+ return 0U;
+
+ else
+ {
+ /* Get a 32-bit value: */
+ png_uint_32 x = png_exp(lg2);
+
+ /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
+ x -= x >> 16;
+ return PNG_UINT_16((x + 32767U) >> 16);
+ }
+}
+#endif /* UNUSED */
+
+static png_uint_32
+png_exp_nbit(png_int_32 lg2, unsigned int n)
+{
+ /* These pre-computed limits give the low value of lg2 at and below which
+ * 2^(-lg2/65536) * (2^n-1) gives (2^n-1) and the high value of lg2 above
+ * which 2(^-lg2/65536) * (2^n-1) gives 0:
+ */
+ static const png_int_32 limits[16][2] =
+ {
+ { 65535, 65535 }, /* bits = 1 */
+ { 17238, 169408 }, /* bits = 2 */
+ { 7006, 249518 }, /* bits = 3 */
+ { 3205, 321577 }, /* bits = 4 */
+ { 1537, 390214 }, /* bits = 5 */
+ { 753, 457263 }, /* bits = 6 */
+ { 372, 523546 }, /* bits = 7 */
+ { 185, 589453 }, /* bits = 8 */
+ { 92, 655175 }, /* bits = 9 */
+ { 46, 720803 }, /* bits = 10 */
+ { 23, 786385 }, /* bits = 11 */
+ { 11, 851944 }, /* bits = 12 */
+ { 5, 917492 }, /* bits = 13 */
+ { 2, 983034 }, /* bits = 14 */
+ { 1, 1048573 }, /* bits = 15 */
+ { 0, 1114110 } /* bits = 16 */
+ };
+
+ /* If 'max' is 2^n-1: */
+ if (lg2 <= limits[n-1][0]) /* -log2((max-.5)/max) */
+ return (1U << n)-1U;
+
+ else if (lg2 > limits[n-1][1]) /* -log2(.5/max) */
+ return 0U;
+
+ else /* 'n' will be at least 2 */
+ {
+ /* Get a 32-bit value: */
+ png_uint_32 x = png_exp(lg2);
+
+ /* Convert the 32-bit value to 0..(2^n-1) by multiplying by 2^n-1: */
+ x -= x >> n;
+ return (x + ((1U<<(31U-n))-1U)) >> (32U-n);
+ }
+}
+#endif /* !FLOATING_ARITHMETIC */
+
+#if 0 /* UNUSED */
+static png_byte
+png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
+{
+ if (value == 0U)
+ return 0U;
+
+ else if (value >= 255U)
+ return 255U;
+
+ else
+ {
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly
+ * convert this to a floating point value. This includes values that
+ * would overflow if 'value' were to be converted to 'int'.
+ *
+ * Apparently GCC, however, does an intermediate conversion to (int)
+ * on some (ARM) but not all (x86) platforms, possibly because of
+ * hardware FP limitations. (E.g. if the hardware conversion always
+ * assumes the integer register contains a signed value.) This results
+ * in ANSI-C undefined behavior for large values.
+ *
+ * Other implementations on the same machine might actually be ANSI-C90
+ * conformant and therefore compile spurious extra code for the large
+ * values.
+ *
+ * We can be reasonably sure that an unsigned to float conversion
+ * won't be faster than an int to float one. Therefore this code
+ * assumes responsibility for the undefined behavior, which it knows
+ * can't happen because of the check above.
+ *
+ * Note the argument to this routine is an (unsigned int) because, on
+ * 16-bit platforms, it is assigned a value which might be out of
+ * range for an (int); that would result in undefined behavior in the
+ * caller if the *argument* ('value') were to be declared (int).
+ */
+ double r = 255*pow((int)/*SAFE*/value/255.,gamma_val*.00001);
+ if (r < .5)
+ return 0U;
+
+ else if (r >= 254.5)
+ return 255U;
+
+ r = floor(r+.5);
+ return (png_byte)/*SAFE*/r;
+# else
+ png_int_32 lg2 = png_log8bit(value);
+ png_int_32 res;
+
+ /* Overflow in the muldiv means underflow in the calculation, this is
+ * OK (it happens for ridiculously high gamma).
+ */
+ if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
+ return 0U; /* underflow */
+
+ return png_exp8bit(res);
+# endif
+ }
+}
+#endif /* UNUSED */
+
+/* libpng-1.7.0: this private function converts an n-bit input value to an
+ * m-bit output value.
+ */
+unsigned int
+png_gamma_nxmbit_correct(unsigned int value, png_fixed_point gamma_val,
+ unsigned int n/*input bits*/, unsigned int m/*output bits */)
+{
+ if (value == 0U)
+ return 0U;
+
+ else
+ {
+ unsigned int min = (1U<= min)
+ return mout;
+
+ else
+ {
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = value;
+ r /= min;
+ r = floor(mout * pow(r, gamma_val*.00001)+.5);
+ if (r < 1)
+ return 0U;
+
+ else if (r >= mout)
+ return mout;
+
+ return (unsigned int)/*SAFE*/r;
+# else
+ png_int_32 lg2 = png_log_nbit(value, n);
+ png_int_32 res;
+
+ if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
+ return 0U; /* underflow */
+
+ return png_exp_nbit(res, m);
+# endif
+ }
+ }
+}
+
+#if 0 /*UNUSED*/
+static unsigned int
+png_gamma_sbit_correct(unsigned int value, png_fixed_point gamma_val,
+ unsigned int n/*input bits*/, unsigned int sbits,
+ unsigned int m/*output bits */)
+ /* As above but the number of significant bits in 'n' is passed in. */
+{
+ if (sbits < n)
+ {
+ value >>= (n-sbits);
+ n = sbits;
+ }
+
+ return png_gamma_nxmbit_correct(value, gamma_val, n, m);
+}
+#endif /*UNUSED*/
+
+static int
+push_gamma_expand(png_transformp *transform, png_transform_controlp tc,
+ int need_alpha)
+ /* Utility to push a transform to expand low-bit-depth gray and, where
+ * required, tRNS chunks. The caller must return immediately if this
+ * returns true because the init of the new transform has been run in place
+ * of the caller's.
+ */
+{
+# define png_ptr (tc->png_ptr)
+ unsigned int expand = 0;
+
+ affirm(tc->init == PNG_TC_INIT_FINAL);
+
+ if (tc->bit_depth < 8U) /* low bit gray: expand to 8 bits */
+ expand = PNG_EXPAND_LBD_GRAY;
+
+ /* Gamma correction invalidates tRNS, so if it is being expanded and
+ * alpha is not being stripped expand it now.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
+ png_ptr->num_trans == 1 && (tc->invalid_info & PNG_INFO_tRNS) == 0)
+ {
+ if (need_alpha || (tc->expand_tRNS && !tc->strip_alpha))
+ expand |= PNG_EXPAND_tRNS;
+
+ else
+ tc->invalid_info |= PNG_INFO_tRNS;
+ }
+
+ if (expand == 0)
+ return 0; /* nothing needs to be done */
+
+ {
+ png_transformp tr = png_push_transform(png_ptr, sizeof (png_expand),
+ png_init_expand, transform, NULL/*don't run init*/);
+
+ debug(tr == *transform);
+ tr->args |= expand;
+
+ /* This must be run immediately, because it just got inserted where this
+ * transform is; this is safe, the caller must return immediately.
+ */
+ png_init_expand(transform, tc);
+ affirm(tr->fn != NULL); /* because it should need to do something! */
+ }
+
+ return 1;
+# undef png_ptr
+}
+
+/* Low bit depth gray gamma correction. The 1-bit case is a no-op because 0 and
+ * 1 always map to 0 and 1. The 2-bit case has the following possiblities:
+ *
+ * bits/correction: g0 g1 g2 g3 g4 g5 g6
+ * 00 -> 00 00 00 00 00 00 00
+ * 01 -> 11 10 10 01 00 00 00
+ * 10 -> 11 11 10 10 10 01 00
+ * 11 -> 11 11 11 11 11 11 11
+ *
+ * Where the breakpoints are:
+ *
+ * g0: correction <= 16595 (1 - log(2.5/3))
+ * g1: 16595 < correction <= 44966 (log(2.5/3)/log(2/3))
+ * g2: 44966 < correction <= 63092 (1 - log(1.5/3))
+ * g3: 63092 < correction <= 163092 (1 - log(.5/3))
+ * g4: 163092 < correction <= 170951 (log(1.5/3)/log(2/3))
+ * g5: 170951 < correction <= 441902 (log(.5/3)/log(2/3)
+ * g6 441902 < correction
+ *
+ * This can be done by bit-hacking on the byte values (4 pixels), given that
+ * the correction is fixed (indeed, it can be done on whole 32-bit values!)
+ *
+ * g0: B |= B>>1; B &= 0x55U; B |= B<<1; * either bit set
+ * g1: B ^= B>>1; B &= 0x55U; B += B; * one bit set
+ * g2: B &= (~B)>>1; B &= 0x55U; B += B; * low bit set, high bit unset
+ * g3: no-op
+ * g4: B &= (~B)>>1; B &= 0x55U; B -= B; * low bit set, high bit unset
+ * g5: B ^= B>>1; B &= 0x55U; B -= B; * one bit set
+ * g6: B &= B>>1; B &= 0x55U; B |= B<<1; * both bits set
+ */
+typedef struct
+{
+ png_transform tr;
+ png_fixed_point correct;
+ png_fixed_point to_gamma;
+ png_uint_32 shifts; /* 1 followed by up to 4 4-bit shifts */
+ png_uint_32 channel_scale[4]; /* up to 4 channel scale factors */
+ /* These factors are used:
+ *
+ * (input >> (shifts & 0xFU) * channel_scale + SCALE_R) >> SCALE_S
+ *
+ * Where the rounding value, SCALE_R and the shift SCALE_S are dependent
+ * on the bit depth:
+ *
+ * SCALE_S = 32 - bit_depth range 16..31
+ * SCALE_R = 1 << (SCALE_S-1)
+ */
+ unsigned int to_bit_depth;
+ unsigned int encode_alpha :1;
+ unsigned int optimize_alpha :1;
+} png_transform_gamma;
+
+static unsigned int
+init_gamma_sBIT(png_transform_gamma *tr, png_transform_controlp tc)
+ /* Returns true if sBIT processing is required, otherwise all relevant sBIT
+ * values match the from (tc) bit depth.
+ */
+{
+ /* The to_bit_depth and to_gamma fields are already set, but updated values
+ * are needed for sBIT and the shifts and channel_scale fields must be filled
+ * in correctly. The do_gamma setting says whether gamma correction will be
+ * done, but the scale factors are filled in regardless.
+ *
+ * The general scaling equation is:
+ *
+ * ((in >> shift) * factor + round) >> (32 - to_bit_depth)
+ *
+ * 'factor' is then the rounded value of:
+ *
+ * out_max
+ * ------- . (1 << (32-to_bit_depth))
+ * in_max
+ */
+# define png_ptr (tc->png_ptr)
+ const unsigned int to_bit_depth = tr->to_bit_depth;
+ const png_uint_32 numerator = ((1U<bit_depth;
+
+ /* The data in the gamma transform is stored in the order of the channels in
+ * the input row, which is the PNG order. It may be reversed below.
+ */
+ png_uint_32p channel_scale = tr->channel_scale;
+ png_uint_32 shifts = 0U;
+ unsigned int count = 0U;
+ unsigned int need_sBIT = 0U;
+
+ if (tc->format & PNG_FORMAT_FLAG_COLOR)
+ {
+ const unsigned int sBIT = tc->sBIT_R;
+
+ if (sBIT < from_bit_depth)
+ need_sBIT = 1U;
+
+ debug(sBIT > 0U && sBIT <= from_bit_depth);
+ shifts |= (from_bit_depth - sBIT) << count;
+ count += 4U;
+ /* round the scale: */
+ *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
+ }
+
+ {
+ const unsigned int sBIT = tc->sBIT_G;
+
+ if (sBIT < from_bit_depth)
+ need_sBIT = 1U;
+
+ debug(sBIT > 0U && sBIT <= from_bit_depth);
+ shifts |= (from_bit_depth - sBIT) << count;
+ count += 4U;
+ *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
+ }
+
+ if (tc->format & PNG_FORMAT_FLAG_COLOR)
+ {
+ const unsigned int sBIT = tc->sBIT_B;
+
+ if (sBIT < from_bit_depth)
+ need_sBIT = 1U;
+
+ debug(sBIT > 0U && sBIT <= from_bit_depth);
+ shifts |= (from_bit_depth - sBIT) << count;
+ count += 4U;
+ /* round the scale: */
+ *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
+ }
+
+ if (tc->format & PNG_FORMAT_FLAG_ALPHA)
+ {
+ const unsigned int sBIT = tc->sBIT_A;
+
+ if (sBIT < from_bit_depth)
+ need_sBIT = 1U;
+
+ debug(sBIT > 0U && sBIT <= from_bit_depth);
+ shifts |= (from_bit_depth - sBIT) << count;
+ count += 4U;
+ /* round the scale: */
+ *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
+ }
+
+ tr->shifts = shifts | (1U << count);
+
+ return need_sBIT;
+# undef png_ptr
+}
+
+static void
+reverse_gamma_sBIT(png_transform_gamma *tr)
+{
+ /* This is called for the 'down' gamma implementations, they read the shifts
+ * and the channel scales in reverse, so:
+ */
+ png_uint_32 shifts = tr->shifts;
+ png_uint_32 scales[4U];
+ unsigned int count = 0U;
+
+ tr->shifts = 1U;
+
+ while (shifts != 1U)
+ {
+ scales[3U-count] = tr->channel_scale[count];
+ ++count;
+ tr->shifts <<= 4;
+ tr->shifts |= shifts & 0xFU;
+ shifts >>= 4;
+ }
+
+ memcpy(tr->channel_scale, scales+(4U-count), count * sizeof (png_uint_32));
+}
+
+static void
+png_do_gamma8_up(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const png_fixed_point correct = tr->correct;
+ const unsigned int bit_depth = tr->to_bit_depth;
+ const png_uint_32 shifts = tr->shifts;
+
+ affirm(tc->bit_depth == 8U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+ debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */
+ debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */
+
+ tc->sp = dp;
+ tc->bit_depth = bit_depth;
+ tc->gamma = tr->to_gamma;
+
+ /* Handle the <8 bit output case differently because there can be no alpha
+ * channel.
+ */
+ if (bit_depth < 8U)
+ {
+ const unsigned int shift = shifts & 0xFU;
+ unsigned int bits = 8U;
+ unsigned int ob = 0U;
+
+ debug((shifts >> 4) == 1U && shift < 8U);
+ affirm(PNG_TC_CHANNELS(*tc) == 1);
+
+ do
+ {
+ const unsigned int inb = png_gamma_nxmbit_correct(
+ *sp++ >> shift, correct, 8U-shift, bit_depth);
+ bits -= bit_depth;
+ ob = ob | (inb << bits);
+ if (bits == 0U)
+ bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
+ }
+ while (sp < ep);
+
+ if (bits < 8U)
+ *dp++ = PNG_BYTE(ob);
+ }
+
+ else /* 8-bit --> 8-bit */
+ {
+ png_uint_32 alpha_scale;
+ const unsigned int channels = PNG_TC_CHANNELS(*tc);
+ unsigned int channel, alpha;
+
+ debug(bit_depth == 8U && (shifts >> (4*channels)) == 1U);
+
+ /* The alpha channel is always last, so if present checking against the
+ * top bits of 'channels' works because of the 1U shibboleth at the end.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0)
+ alpha_scale = alpha = 0U;
+
+ else
+ {
+ alpha = shifts >> (4U*(channels-1U));
+ alpha_scale = tr->channel_scale[channels-1U];
+ }
+
+ channel = 1U;
+
+ do
+ {
+ unsigned int inb = *sp++, shift;
+
+ if (channel == 1U)
+ channel = shifts;
+
+ shift = channel & 0xFU;
+ inb >>= shift;
+
+ /* The alpha channel is not gamma encoded but it may need some
+ * appropriate scaling.
+ */
+ if (channel == alpha)
+ inb = (inb * alpha_scale + 0x800000U) >> 24;
+
+ else
+ inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 8U);
+
+ channel >>= 4; /* for the next channel, or the shibboleth */
+ *dp++ = PNG_BYTE(inb);
+ }
+ while (sp < ep);
+
+ debug(channel == 1U);
+ }
+# undef png_ptr
+}
+
+static void
+png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/;
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const png_fixed_point correct = tr->correct;
+ const unsigned int bit_depth = tr->to_bit_depth;
+ const png_uint_32 shifts = tr->shifts;
+
+ affirm(tc->bit_depth == 16U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+ debug(!tr->optimize_alpha);
+
+ /* This is exactly the same as above but the input has 16 bits per component,
+ * not 8.
+ */
+ tc->sp = dp;
+ tc->bit_depth = bit_depth;
+ tc->gamma = tr->to_gamma;
+
+ /* Handle the <8 bit output case differently, the input cannot be color (at
+ * present) and, if there is an alpha channel, then it is for the
+ * low-bit-depth gray input case and we expect the alpha to be transparent.
+ */
+ if (bit_depth < 8U)
+ {
+ const unsigned int shift = shifts & 0xFU;
+ unsigned int bits = 8U;
+ unsigned int ob = 0U;
+
+ affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0U);
+
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0U)
+ {
+ debug((shifts >> 4) == 1U && shift < 16U);
+ debug(!tr->encode_alpha && !tr->optimize_alpha);
+
+ do
+ {
+ unsigned int inb = *sp++ << 8; /* high bits first */
+ inb = png_gamma_nxmbit_correct(
+ (inb + *sp++) >> shift, correct, 16U-shift, bit_depth);
+
+ bits -= bit_depth;
+ ob = ob | (inb << bits);
+ if (bits == 0U)
+ bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
+ }
+ while (sp < ep);
+
+ UNTESTED
+ }
+
+ else /* low bit GA intermediate format */
+ {
+ debug((shifts >> 8) == 1U && shift < 16U);
+ debug(!tr->encode_alpha && !tr->optimize_alpha);
+ debug(tc->transparent_alpha);
+
+ /* Gray is first then the alpha component, the alpha component is just
+ * mapped to 0 or 1.
+ */
+ do
+ {
+ unsigned int gray = *sp++ << 8; /* high bits first */
+ unsigned int alpha;
+ gray += *sp++;
+
+ alpha = (*sp++ << 8);
+ alpha += *sp++;
+
+ if (alpha == 0U)
+ gray = 0U; /* will be replaced later */
+
+ else
+ {
+ gray = png_gamma_nxmbit_correct(gray >> shift, correct,
+ 16U-shift, bit_depth);
+ debug(alpha == 65535U);
+ alpha = (1U << bit_depth)-1U;
+ }
+
+ bits -= bit_depth;
+ ob = ob | (gray << bits);
+ bits -= bit_depth;
+ ob = ob | (alpha << bits);
+
+ if (bits == 0U)
+ bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
+ }
+ while (sp < ep-2U);
+ }
+
+ if (bits < 8U)
+ *dp++ = PNG_BYTE(ob);
+
+ debug(sp == ep+1U);
+ }
+
+ else
+ {
+ png_uint_32 alpha_scale;
+ const unsigned int channels = PNG_TC_CHANNELS(*tc);
+ unsigned int channel, alpha;
+
+ debug((bit_depth == 8U || bit_depth == 16U) &&
+ (shifts >> (4*channels)) == 1U);
+
+ /* Note that 'encode_alpha' turns on gamma encoding of the alpha
+ * channel (and this is a really weird operation!)
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 || tr->encode_alpha)
+ alpha_scale = alpha = 0U;
+
+ else
+ {
+ alpha = shifts >> (4U*(channels-1U));
+ alpha_scale = tr->channel_scale[channels-1U];
+ }
+
+ channel = 1U;
+
+ if (bit_depth == 16U)
+ {
+ do
+ {
+ unsigned int inb = *sp++ << 8, shift;
+ inb += *sp++;
+
+ if (channel == 1U)
+ channel = shifts;
+
+ shift = channel & 0xFU;
+ inb >>= shift;
+
+ /* The 16-16bit scaling factor equation may be off-by-1 but this
+ * hardly matters for alpha or for gamma operations.
+ */
+ if (channel == alpha)
+ inb = (inb * alpha_scale + 0x8000U) >> 16;
+
+ else
+ inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 16U);
+
+ channel >>= 4; /* for the next channel, or the shibboleth */
+ *dp++ = PNG_BYTE(inb >> 8);
+ *dp++ = PNG_BYTE(inb);
+ }
+ while (sp < ep);
+
+ debug(channel == 1U && sp == ep+1U);
+ }
+
+ else /* bit_depth == 8U */
+ {
+ do
+ {
+ unsigned int inb = *sp++ << 8, shift;
+ inb += *sp++;
+
+ if (channel == 1U)
+ channel = shifts;
+
+ shift = channel & 0xFU;
+ inb >>= shift;
+
+ if (channel == alpha)
+ inb = (inb * alpha_scale + 0x800000U) >> 24;
+
+ else
+ inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 8U);
+
+ channel >>= 4; /* for the next channel, or the shibboleth */
+ *dp++ = PNG_BYTE(inb);
+ }
+ while (sp < ep);
+
+ debug(channel == 1U && sp == ep+1U);
+ }
+ }
+# undef png_ptr
+}
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+static void
+png_do_gamma16_up_optimize(png_transformp *transform, png_transform_controlp tc)
+ /* As above, but the alpha channel is 'optimized' */
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const png_fixed_point correct = tr->correct;
+
+ /* The input always as 16 bits, the output 8 or 16. There is always an alpha
+ * channel and it is converted to the 'optimized' form, where pixels with
+ * alpha not 0.0 or 1.0 are left in linear form (not gamma corrected.) Where
+ * bit depth convertion is required it is from 16-bits to 8-bits and the
+ * DIV257 macro can be used.
+ *
+ * The following affirms and NOT_REACHED cases are consequences of the way
+ * the background (compose) code works:
+ */
+ affirm(tr->optimize_alpha && !tr->encode_alpha && tc->bit_depth == 16U);
+
+ /* TODO: split this into separate functions */
+ switch (tr->to_bit_depth)
+ {
+ case 8U: /* 16-bit --> 8-bit */
+ tc->sp = dp;
+ tc->bit_depth = 8U;
+ tc->gamma = tr->to_gamma;
+
+ switch (PNG_TC_CHANNELS(*tc))
+ {
+ case 2:/* GA */
+ debug(tr->shifts == 0x100U);
+ ep -= 3U; /*SAFETY*/
+
+ do
+ {
+ png_uint_32 alpha = PNG_DIV257((sp[2] << 8) + sp[3]);
+
+ switch (alpha)
+ {
+ case 0U:
+ dp[1] = dp[0] = 0U;
+ break;
+
+ default: /* optimized case: linear color data */
+ dp[0] = png_check_byte(png_ptr,
+ PNG_DIV257((sp[0] << 8) + sp[1]));
+ dp[1] = PNG_BYTE(alpha);
+ break;
+
+ case 255U: /* opaque pixels are encoded */
+ dp[0] = PNG_BYTE(png_gamma_nxmbit_correct(
+ (sp[0] << 8) + sp[1], correct, 16U, 8U));
+ dp[1] = 255U;
+ break;
+ }
+
+ sp += 4U;
+ dp += 2U;
+ }
+ while (sp < ep);
+
+ debug(sp == ep+3U);
+ break;
+
+ case 4:/* RGBA */
+ debug(tr->shifts == 0x10000U);
+ ep -= 7U; /*SAFETY*/
+
+ do
+ {
+ png_uint_32 alpha = PNG_DIV257((sp[6] << 8) + sp[7]);
+
+ switch (alpha)
+ {
+ case 0U:
+ memset(dp, 0U, 4U);
+ break;
+
+ default: /* optimized case: linear color data */
+ dp[0] = PNG_BYTE(PNG_DIV257((sp[0] << 8) + sp[1]));
+ dp[1] = PNG_BYTE(PNG_DIV257((sp[2] << 8) + sp[3]));
+ dp[2] = PNG_BYTE(PNG_DIV257((sp[4] << 8) + sp[5]));
+ dp[3] = PNG_BYTE(alpha);
+ break;
+
+ case 255U: /* opaque pixels are encoded */
+ dp[0] = PNG_BYTE(png_gamma_nxmbit_correct(
+ (sp[0] << 8) + sp[1], correct, 16U, 8U));
+ dp[1] = PNG_BYTE(png_gamma_nxmbit_correct(
+ (sp[2] << 8) + sp[3], correct, 16U, 8U));
+ dp[2] = PNG_BYTE(png_gamma_nxmbit_correct(
+ (sp[4] << 8) + sp[5], correct, 16U, 8U));
+ dp[3] = 255U;
+ break;
+ }
+
+ sp += 8U;
+ dp += 4U;
+ }
+ while (sp < ep);
+
+ debug(sp == ep+7U);
+ break;
+
+ default:
+ NOT_REACHED;
+ break;
+ }
+ break;
+
+ case 16: /* 16-bit to 16-bit */
+ tc->sp = dp;
+ tc->bit_depth = 16U;
+ tc->gamma = tr->to_gamma;
+
+ switch (PNG_TC_CHANNELS(*tc))
+ {
+ case 2:/* GA */
+ debug(tr->shifts == 0x100U);
+ ep -= 3U; /*SAFETY*/
+
+ do
+ {
+ unsigned int alpha = (sp[2] << 8) + sp[3];
+
+ switch (alpha)
+ {
+ case 0U:
+ memset(dp, 0U, 4U);
+ break;
+
+ default: /* optimized case: linear color data */
+ if (dp != sp)
+ {
+ memcpy(dp, sp, 4U);
+ UNTESTED
+ }
+ break;
+
+ case 65535U: /* opaque pixels are encoded */
+ {
+ unsigned int gray = png_gamma_nxmbit_correct(
+ (sp[0] << 8) + sp[1], correct, 16U, 16U);
+ dp[0] = PNG_BYTE(gray >> 8);
+ dp[1] = PNG_BYTE(gray);
+ }
+ dp[3] = dp[2] = 255U;
+ break;
+ }
+
+ sp += 4U;
+ dp += 4U;
+ }
+ while (sp < ep);
+
+ debug(sp == ep+3U);
+ break;
+
+ case 4:/* RGBA */
+ debug(tr->shifts == 0x10000U);
+ ep -= 7U; /*SAFETY*/
+
+ do
+ {
+ unsigned int alpha = (sp[6] << 8) + sp[7];
+
+ switch (alpha)
+ {
+ case 0U:
+ memset(dp, 0U, 8U);
+ break;
+
+ default: /* optimized case: linear color data */
+ if (dp != sp)
+ {
+ memcpy(dp, sp, 8U);
+ UNTESTED
+ }
+ break;
+
+ case 65535U: /* opaque pixels are encoded */
+ {
+ unsigned int c = png_gamma_nxmbit_correct(
+ (sp[0] << 8) + sp[1], correct, 16U, 16U);
+ dp[0] = PNG_BYTE(c >> 8);
+ dp[1] = PNG_BYTE(c);
+
+ c = png_gamma_nxmbit_correct(
+ (sp[2] << 8) + sp[3], correct, 16U, 16U);
+ dp[2] = PNG_BYTE(c >> 8);
+ dp[3] = PNG_BYTE(c);
+
+ c = png_gamma_nxmbit_correct(
+ (sp[4] << 8) + sp[5], correct, 16U, 16U);
+ dp[4] = PNG_BYTE(c >> 8);
+ dp[5] = PNG_BYTE(c);
+ }
+ dp[7] = dp[6] = 255U;
+ break;
+ }
+
+ sp += 8U;
+ dp += 8U;
+ }
+ while (sp < ep);
+
+ debug(sp == ep+7U);
+ break;
+
+ default:
+ NOT_REACHED;
+ break;
+ }
+ break;
+
+ default:
+ NOT_REACHED;
+ break;
+ }
+# undef png_ptr
+}
+#endif /* READ_ALPHA_MODE */
+
+static void
+png_do_scale16_up(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const unsigned int bit_depth = tr->to_bit_depth;
+
+ affirm(tc->bit_depth == 16U && bit_depth < 8U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+
+ /* This is exactly the same as above but without the gamma correction and
+ * without the 8-bit target support. The code handles one or two channels,
+ * but the result is not a PNG format unless the number of channels is just
+ * 1 (grayscale).
+ *
+ * For multi-channel low bit depth the channels are packed into bytes using
+ * the standard PNG big-endian packing.
+ */
+ affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0);
+ /* The alpha shift is actually ignored; at present we only get here with an
+ * alpha channel if it is to be removed for transparent alpha processing.
+ */
+ debug(tc->format & PNG_FORMAT_FLAG_ALPHA ?
+ (tr->shifts >> 8) == 1U : (tr->shifts >> 4) == 1U);
+ debug(tc->transparent_alpha);
+
+ tc->sp = dp;
+ /* This is a pure scaling operation so sBIT is not invalidated or altered. */
+ tc->bit_depth = bit_depth;
+
+ /* TODO: maybe do this properly and use the alpha shift, but only the top bit
+ * matters.
+ */
+ {
+ const unsigned int shift = tr->shifts & 0xFU;
+ const png_uint_32 factor = tr->channel_scale[0];
+ const png_uint_32 round = 1U << (31U-bit_depth);
+ unsigned int bits = 8U;
+ unsigned int ob = 0U;
+
+ do
+ {
+ png_uint_32 inb = *sp++ << 8; /* high bits first */
+ inb += *sp++;
+
+ inb = ((inb >> shift) * factor + round) >> (32U-bit_depth);
+ bits -= bit_depth;
+ ob = ob | (inb << bits);
+ if (bits == 0U)
+ bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
+ }
+ while (sp < ep);
+
+ if (bits < 8U)
+ *dp++ = PNG_BYTE(ob);
+ }
+# undef png_ptr
+}
+
+static void
+png_do_gamma8_down(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp + 1U/*safety*/;
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const png_fixed_point correct = tr->correct;
+ const png_uint_32 shifts = tr->shifts;
+
+ affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+ debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */
+ debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */
+
+ sp += PNG_TC_ROWBYTES(*tc);
+ tc->sp = dp;
+ tc->bit_depth = tr->to_bit_depth;
+ tc->gamma = tr->to_gamma;
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ {
+ png_uint_32 alpha_scale;
+ unsigned int channel, alpha;
+
+ debug((shifts >> (4*PNG_TC_CHANNELS(*tc))) == 1U);
+
+ /* We are going down so alpha, if present, is first. Notice that the init
+ * routine has to reverse both 'shifts' and 'channel_scale' for the _down
+ * cases.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0)
+ alpha_scale = alpha = 0U;
+
+ else
+ {
+ alpha = shifts;
+ alpha_scale = tr->channel_scale[0U];
+ }
+
+ channel = 1U;
+
+ do /* 8-bit --> 16-bit */
+ {
+ unsigned int inb = *--sp, shift;
+
+ if (channel == 1U)
+ channel = shifts;
+
+ shift = channel & 0xFU;
+ inb >>= shift;
+
+ if (channel == alpha) /* unencoded alpha, must scale */
+ inb = (inb * alpha_scale + 0x8000U) >> 16;
+
+ else
+ inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 16U);
+
+ channel >>= 4;
+
+ *--dp = PNG_BYTE(inb);
+ *--dp = PNG_BYTE(inb >> 8);
+ }
+ while (dp > ep);
+
+ debug(channel == 1U && dp == ep-1U);
+ }
+# undef png_ptr
+}
+
+static void
+png_do_expand8_down(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp + 1U/*safety*/;
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+ const png_uint_32 shifts = tr->shifts;
+
+ affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+
+ sp += PNG_TC_ROWBYTES(*tc);
+ tc->sp = dp;
+ tc->bit_depth = 16U;
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ {
+ png_uint_32 channel = 1U;
+ png_const_uint_32p scale = 0U;
+
+ do /* 8-bit -> 16-bit */
+ {
+ unsigned int inb = *--sp, shift;
+
+ if (channel == 1U)
+ channel = shifts, scale = tr->channel_scale;
+
+ shift = channel & 0xFU;
+ channel >>= 4;
+ inb >>= shift;
+ inb = (inb * *scale++ + 0x8000U) >> 16;
+ /* dp starts beyond the end: */
+ *--dp = PNG_BYTE(inb);
+ *--dp = PNG_BYTE(inb >> 8);
+ }
+ while (dp > ep);
+
+ debug(channel == 1U && dp == ep-1U);
+ }
+# undef png_ptr
+}
+
+static void
+png_do_expand8_down_fast(png_transformp *transform, png_transform_controlp tc)
+ /* Optimized version of the above for when the sBIT settings are all a full 8
+ * bits (the normal case).
+ */
+{
+# define png_ptr (tc->png_ptr)
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp + 1U/*safety*/;
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+
+ affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
+ affirm(tr->shifts != 0U/*uninitialized*/);
+
+ sp += PNG_TC_ROWBYTES(*tc);
+ tc->sp = dp;
+ tc->bit_depth = 16U;
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ do
+ dp -= 2, dp[0] = dp[1] = *--sp;
+ while (dp > ep);
+
+ debug(dp == ep-1U);
+# undef png_ptr
+}
+
+static void
+png_init_gamma_uncached(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+
+ debug(tc->init == PNG_TC_INIT_FINAL);
+
+ /* Set this first; the result says if the sBIT data is significant, but it is
+ * ignored here.
+ */
+ (void)init_gamma_sBIT(tr, tc);
+
+ /* If png_set_alpha_mode is called but no background processing needs to be
+ * done (because there is no alpha channel or tRNS) we get to here with
+ * potentially spurious alpha mode flags.
+ */
+ if (!(tc->format & PNG_FORMAT_FLAG_ALPHA))
+ tr->encode_alpha = tr->optimize_alpha = 0U;
+
+ /* Use separate functions for the two input depths but not for the five
+ * possible output depths and four channel counts.
+ */
+ if (tc->bit_depth == 8U)
+ {
+ if (tr->to_bit_depth <= 8U)
+ tr->tr.fn = png_do_gamma8_up;
+
+ else
+ {
+ debug(tr->to_bit_depth == 16U);
+ reverse_gamma_sBIT(tr);
+ tr->tr.fn = png_do_gamma8_down;
+ }
+ }
+
+ else
+ {
+ affirm(tc->bit_depth == 16U);
+# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ if (!tr->optimize_alpha)
+ tr->tr.fn = png_do_gamma16_up;
+ else
+ tr->tr.fn = png_do_gamma16_up_optimize;
+# else /* !READ_ALPHA_MODE */
+ tr->tr.fn = png_do_gamma16_up;
+# endif /* !READ_ALPHA_MODE */
+ }
+
+ /* Since the 'do' routines always perform gamma correction they will always
+ * expand the significant bits to the full output bit depth.
+ */
+ tc->invalid_info |= PNG_INFO_sBIT;
+ tc->bit_depth = tr->to_bit_depth;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B =
+ png_check_byte(png_ptr, tc->bit_depth);
+ if (tr->encode_alpha)
+ tc->sBIT_A = tc->sBIT_G;
+ tc->gamma = tr->to_gamma;
+# undef png_ptr
+}
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+static unsigned int
+tc_sBIT(png_const_transform_controlp tc)
+ /* Determine the maximum number of significant bits in the row at this point.
+ * This uses the png_struct::sig_bit field if it has not been invalidated,
+ * otherwise it just returns the current bit depth.
+ */
+{
+ const png_structrp png_ptr = tc->png_ptr;
+ unsigned int bit_depth = tc->bit_depth;
+
+ if ((tc->invalid_info & PNG_INFO_sBIT) == 0U)
+ {
+ /* Normally the bit depth will not have been changed from the original PNG
+ * depth, but it currently is changed by the grayscale expand to 8 bits,
+ * an operation which doesn't invalidate sBIT.
+ */
+ unsigned int sBIT;
+
+ if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0U)
+ {
+ /* Must use the largest of the sBIT depths, except that unset values
+ * take priority.
+ */
+ sBIT = png_ptr->sig_bit.red && png_ptr->sig_bit.green &&
+ png_ptr->sig_bit.blue;
+
+ if (sBIT != 0U)
+ {
+ sBIT = png_ptr->sig_bit.red;
+
+ if (png_ptr->sig_bit.green > sBIT)
+ sBIT = png_ptr->sig_bit.green;
+ if (png_ptr->sig_bit.blue > sBIT)
+ sBIT = png_ptr->sig_bit.blue;
+ }
+ }
+
+ else
+ sBIT = png_ptr->sig_bit.gray;
+
+ if (sBIT > 0U && sBIT < bit_depth)
+ bit_depth = sBIT;
+ }
+
+ return bit_depth;
+}
+#else /* !READ_sBIT */
+# define tc_sBIT(tc) ((tc)->bit_depth)
+#endif /* READ_sBIT */
+
+static void
+png_init_gamma(png_transformp *transform, png_transform_controlp tc)
+{
+ const png_structrp png_ptr = tc->png_ptr;
+ png_transform_gamma *tr =
+ png_transform_cast(png_transform_gamma, *transform);
+
+ if (tc->init == PNG_TC_INIT_FORMAT)
+ {
+ /* This should only happen for the final encode gamma transform, which
+ * never initializes the target bit depth (see png_set_gamma and
+ * png_set_alpha_mode). The affirm is required here; in we can't continue
+ * safely if the bit depth has been set somehow.
+ */
+ debug(tr->tr.order == PNG_TR_GAMMA_ENCODE);
+ affirm(tr->to_gamma > 0 && tr->to_bit_depth == 0U);
+
+ /* At this point the output gamma should not have been set yet: */
+ debug(png_ptr->row_gamma == 0);
+
+ /* The following must be true; png_set_gamma and png_set_alpha_mode set
+ * (or default) the PNG gamma and other routines that insert a gamma
+ * transform must only do in PNG_TC_INIT_FINAL:
+ */
+ debug(tc->gamma > 0);
+
+ /* At this point the data gamma must be updated so that we get the correct
+ * png_struct::row_gamma at the end of the init:
+ */
+ tc->gamma = tr->to_gamma;
+
+ /* For safety invalidate the sBIT information too; we don't know yet
+ * whether a gamma transform will be required but if it is the sBIT
+ * information becomes invalid.
+ */
+ tc->invalid_info |= PNG_INFO_sBIT;
+ }
+
+ else /* PNG_TC_INIT_FINAL */
+ {
+ /* It is very bad if we get here when processing a row: */
+ affirm(tc->init == PNG_TC_INIT_FINAL && png_ptr->row_bit_depth > 0);
+
+ /* There are three cases:
+ *
+ * 1) Gamma correction is required, output bit depth may need to be
+ * defaulted.
+ * 2) Gamma correction is not required but a bit depth change is
+ * necessary.
+ * 3) Neither is required; the transform can be eliminated.
+ *
+ * First default the bit depth if it is not already set. Note that if the
+ * output is a palette then 'row_bit_depth' refers to the palette size and
+ * 8U must be used here. tc->palette is irrelevant; it only tells us that
+ * the data came from a palette.
+ */
+ if (tr->to_bit_depth == 0)
+ {
+ if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0U)
+ tr->to_bit_depth = 8U;
+
+ else
+ tr->to_bit_depth = png_ptr->row_bit_depth;
+ }
+
+ /* (1); is gamma correction required? If tc->gamma is 0 at this point it
+ * is not, but then the png_struct::row_gamma should be 0 too.
+ */
+ implies(tc->gamma == 0, png_ptr->row_gamma == 0);
+ implies(tr->to_gamma == 0, tc->gamma == 0);
+
+ if (!png_gamma_equal(png_ptr, tc->gamma, tr->to_gamma, &tr->correct,
+ tc_sBIT(tc)))
+ {
+ /* First make sure the input doesn't have a tRNS chunk which needs to
+ * be expanded now; if it does push_gamma_expand will push an
+ * appropriate transform *before* this one and we need to return
+ * immediately (the caller will call back to this function).
+ */
+ if (push_gamma_expand(transform, tc, 0/*need alpha*/))
+ {
+ affirm(tc->bit_depth >= 8U &&
+ (tc->invalid_info & PNG_INFO_tRNS) != 0U &&
+ *transform != &tr->tr);
+ return;
+ }
+
+ debug(*transform == &tr->tr && tc->bit_depth >= 8U);
+
+ /* The format is now 8 or 16-bit G, GA, RGB or RGBA and gamma
+ * correction is required.
+ */
+ png_init_gamma_uncached(transform, tc);
+ /* TODO: implement caching for the !tc->caching cases! */
+ return;
+ }
+
+ /* The cases where the two gamma values are close enough to be considered
+ * equal. The code lies about the gamma; this prevents apps and the
+ * simplified API getting into loops or bad conditions because the gamma
+ * was not set to the expected value.
+ *
+ * Note that png_transform_control::gamma is only set here if both the
+ * input and output gamma values are known, otherwise the transform
+ * introduces a spurious know gamma value.
+ */
+ if (tr->to_gamma > 0 && tc->gamma > 0)
+ tc->gamma = tr->to_gamma;
+
+ if (tr->to_bit_depth > tc->bit_depth)
+ {
+ /* This is either the to-linear operation, in which case the expected
+ * bit depth is 16U, or it is the final encode in the case where an
+ * 'expand' operation was also specified.
+ *
+ * We don't care about the PNG_TR_GAMMA_ENCODE case because we know
+ * that there has to be an expand operation further down the pipeline.
+ */
+ if (tr->tr.order < PNG_TR_GAMMA_ENCODE)
+ {
+ affirm(tr->to_bit_depth == 16U);
+
+ if (push_gamma_expand(transform, tc, 0/*need alpha*/))
+ {
+ affirm(tc->bit_depth == 8U &&
+ (tc->invalid_info & PNG_INFO_tRNS) != 0U &&
+ *transform != &tr->tr);
+ return;
+ }
+
+ debug(*transform == &tr->tr);
+ affirm(tc->bit_depth == 8U); /* if 16U we would not be here! */
+
+ /* not using byte_ops here, but if there is no sBIT required
+ * (normally the case) the fast code can be used:
+ */
+ if (init_gamma_sBIT(tr, tc))
+ tr->tr.fn = png_do_expand8_down;
+
+ else
+ tr->tr.fn = png_do_expand8_down_fast;
+
+ tc->bit_depth = 16U;
+ }
+
+ else /* PNG_TR_GAMMA_ENCODE: nothing need be done */
+ tr->tr.fn = NULL;
+ }
+
+ else if (tr->to_bit_depth < tc->bit_depth)
+ {
+ /* No gamma correction but bit depth *reduction* is required. Expect
+ * the 'from' bit depth to always be 16, otherwise this transform
+ * should not have been pushed. Also expect this to be the gamma
+ * 'encode' operation at the end of the arithmetic.
+ */
+ affirm(tc->bit_depth == 16U && tr->tr.order == PNG_TR_GAMMA_ENCODE);
+
+ /* If the target bit depth is 8-bit delay the operation and use the
+ * standard 16-8-bit scale code. For low bit depth do it now.
+ */
+ if (tr->to_bit_depth == 8U)
+ {
+ png_set_scale_16(png_ptr);
+ tr->tr.fn = NULL;
+ }
+
+ else /* low bit depth */
+ {
+ (void)init_gamma_sBIT(tr, tc);
+ tr->tr.fn = png_do_scale16_up;
+ tc->bit_depth = tr->to_bit_depth;
+ }
+ }
+
+ else /* gamma !significant and nothing to do */
+ tr->tr.fn = NULL;
+ }
+}
+
+#if !PNG_RELEASE_BUILD
+int /* PRIVATE(debug only) */
+png_gamma_check(png_const_structrp png_ptr, png_const_transform_controlp tc)
+ /* Debugging only routine to repeat the test used above to determine if the
+ * gamma was insignificant.
+ *
+ * NOTE: JB20160723: This may still be incorrect in a complicated transform
+ * pipeline because it uses 'tc_sBIT' for the end of the pipeline whereas the
+ * init above happens earlier. I don't think this matters because the test
+ * is only invoked if the gamma transform is eliminated or if there is a bug
+ * and in the former case the sBIT values should remain unchanged.
+ */
+{
+ png_fixed_point dummy;
+
+ return png_gamma_equal(png_ptr, png_ptr->row_gamma, tc->gamma, &dummy,
+ tc_sBIT(tc));
+}
+#endif /* !RELEASE_BUILD */
+
static png_fixed_point
-translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
- int is_screen)
+translate_gamma_flags(png_const_structrp png_ptr, png_fixed_point gamma,
+ int is_screen)
+ /* If 'is_screen' is set this returns the inverse of the supplied value; i.e.
+ * this routine always returns an encoding value.
+ */
{
/* Check for flag values. The main reason for having the old Mac value as a
* flag is that it is pretty near impossible to work out what the correct
* value is from Apple documentation - a working Mac system is needed to
* discover the value!
*/
- if (output_gamma == PNG_DEFAULT_sRGB ||
- output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
+ switch (gamma)
{
- /* If there is no sRGB support this just sets the gamma to the standard
- * sRGB value. (This is a side effect of using this function!)
- */
-# ifdef PNG_READ_sRGB_SUPPORTED
- png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
-# else
- PNG_UNUSED(png_ptr)
-# endif
- if (is_screen != 0)
- output_gamma = PNG_GAMMA_sRGB;
- else
- output_gamma = PNG_GAMMA_sRGB_INVERSE;
+ case PNG_DEFAULT_sRGB:
+ case PNG_GAMMA_sRGB:
+ case PNG_FP_1/PNG_GAMMA_sRGB: /* stupid case: -100000 */
+ gamma = PNG_GAMMA_sRGB_INVERSE;
+ break;
+
+ case PNG_GAMMA_MAC_18:
+ case PNG_FP_1/PNG_GAMMA_MAC_18: /* stupid case: -50000 */
+ gamma = PNG_GAMMA_MAC_INVERSE;
+ break;
+
+ default:
+ if (is_screen)
+ {
+ /* Check for a ridiculously low value; this will result in an
+ * overflow
+ * in the reciprocal calculation.
+ */
+ if (gamma < 5)
+ {
+ png_app_error(png_ptr, "invalid screen gamma (too low)");
+ gamma = 0;
+ }
+
+ else if (gamma != PNG_FP_1) /* optimize linear */
+ gamma = png_reciprocal(gamma);
+ }
+
+ else if (gamma <= 0)
+ {
+ png_app_error(png_ptr, "invalid file gamma (too low)");
+ gamma = 0;
+ }
+ break;
}
- else if (output_gamma == PNG_GAMMA_MAC_18 ||
- output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)
- {
- if (is_screen != 0)
- output_gamma = PNG_GAMMA_MAC_OLD;
- else
- output_gamma = PNG_GAMMA_MAC_INVERSE;
- }
-
- return output_gamma;
+ return gamma;
}
-# ifdef PNG_FLOATING_POINT_SUPPORTED
+static png_transform_gamma *
+add_gamma_transform(png_structrp png_ptr, unsigned int order,
+ png_fixed_point gamma, unsigned int bit_depth, int force)
+{
+ /* Add a png_transform_gamma transform at the given position; this is a
+ * utility which just adds the transform and (unconditionally) overwrites the
+ * to_gamma field. gamma must be valid. If 'force' is true the gamma value
+ * in an existing transform will be overwritten, otherwise this is just a
+ * default value.
+ */
+ png_transform_gamma *tr = png_transform_cast(png_transform_gamma,
+ png_add_transform(png_ptr, sizeof (png_transform_gamma), png_init_gamma,
+ order));
+
+ if (force || tr->to_gamma == 0)
+ tr->to_gamma = gamma;
+
+ tr->to_bit_depth = bit_depth;
+
+ return tr;
+}
+
+void PNGFAPI
+png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
+ png_fixed_point file_gamma)
+{
+ png_debug(1, "in png_set_gamma_fixed");
+
+ /* Validate the passed in file gamma value: */
+ file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
+
+ /* The returned value may be 0, this results in a png_app_error above which
+ * may be ignored; if that happens simply ignore the setting.
+ */
+ if (file_gamma > 0)
+ {
+ /* Set the colorspace gamma value unconditionally - this overrides the
+ * value in the PNG file if a gAMA chunk was present. png_set_alpha_mode
+ * provides a different, easier, way to default the file gamma.
+ */
+ png_ptr->colorspace.gamma = file_gamma;
+ if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
+ png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA;
+ else
+ png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+ }
+
+ /* Do the same thing with the screen gamma; check it and handle it if valid.
+ * This adds/sets the encoding of the final gamma transform in the chain.
+ * png_set_alpha_mode does the same thing.
+ */
+ scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
+
+ if (scrn_gamma > 0)
+ (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, scrn_gamma,
+ 0/*bit depth*/, 1/*force to_gamma to scrn_gamma*/);
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
static png_fixed_point
convert_gamma_value(png_structrp png_ptr, double output_gamma)
{
@@ -264,771 +3584,478 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma)
return png_fixed(png_ptr, output_gamma, "gamma value");
}
-# endif
-#endif /* READ_ALPHA_MODE || READ_GAMMA */
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-void PNGFAPI
-png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
- png_fixed_point output_gamma)
-{
- int compose = 0;
- png_fixed_point file_gamma;
-
- png_debug(1, "in png_set_alpha_mode");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
-
- /* Validate the value to ensure it is in a reasonable range. The value
- * is expected to be 1 or greater, but this range test allows for some
- * viewing correction values. The intent is to weed out users of this API
- * who use the inverse of the gamma value accidentally! Since some of these
- * values are reasonable this may have to be changed.
- */
- if (output_gamma < 70000 || output_gamma > 300000)
- png_error(png_ptr, "output gamma out of expected range");
-
- /* The default file gamma is the inverse of the output gamma; the output
- * gamma may be changed below so get the file value first:
- */
- file_gamma = png_reciprocal(output_gamma);
-
- /* There are really 8 possibilities here, composed of any combination
- * of:
- *
- * premultiply the color channels
- * do not encode non-opaque pixels
- * encode the alpha as well as the color channels
- *
- * The differences disappear if the input/output ('screen') gamma is 1.0,
- * because then the encoding is a no-op and there is only the choice of
- * premultiplying the color channels or not.
- *
- * png_set_alpha_mode and png_set_background interact because both use
- * png_compose to do the work. Calling both is only useful when
- * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along
- * with a default gamma value. Otherwise PNG_COMPOSE must not be set.
- */
- switch (mode)
- {
- case PNG_ALPHA_PNG: /* default: png standard */
- /* No compose, but it may be set by png_set_background! */
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
- break;
-
- case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
- compose = 1;
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
- /* The output is linear: */
- output_gamma = PNG_FP_1;
- break;
-
- case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */
- compose = 1;
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;
- /* output_gamma records the encoding of opaque pixels! */
- break;
-
- case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */
- compose = 1;
- png_ptr->transformations |= PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
- break;
-
- default:
- png_error(png_ptr, "invalid alpha mode");
- }
-
- /* Only set the default gamma if the file gamma has not been set (this has
- * the side effect that the gamma in a second call to png_set_alpha_mode will
- * be ignored.)
- */
- if (png_ptr->colorspace.gamma == 0)
- {
- png_ptr->colorspace.gamma = file_gamma;
- png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
- }
-
- /* But always set the output gamma: */
- png_ptr->screen_gamma = output_gamma;
-
- /* Finally, if pre-multiplying, set the background fields to achieve the
- * desired result.
- */
- if (compose != 0)
- {
- /* And obtain alpha pre-multiplication by composing on black: */
- memset(&png_ptr->background, 0, (sizeof png_ptr->background));
- png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */
- png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
- png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND;
-
- if (png_ptr->transformations & PNG_COMPOSE)
- png_error(png_ptr,
- "conflicting calls to set alpha mode and background");
-
- png_ptr->transformations |= PNG_COMPOSE;
- }
-}
-
-# ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
-{
- png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
- output_gamma));
-}
-# endif
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-/* Dither file to 8-bit. Supply a palette, the current number
- * of elements in the palette, the maximum number of elements
- * allowed, and a histogram if possible. If the current number
- * of colors is greater then the maximum number, the palette will be
- * modified to fit in the maximum number. "full_quantize" indicates
- * whether we need a quantizing cube set up for RGB images, or if we
- * simply are reducing the number of colors in a paletted image.
- */
-
-typedef struct png_dsort_struct
-{
- struct png_dsort_struct * next;
- png_byte left;
- png_byte right;
-} png_dsort;
-typedef png_dsort * png_dsortp;
-typedef png_dsort * * png_dsortpp;
-
-void PNGAPI
-png_set_quantize(png_structrp png_ptr, png_colorp palette,
- int num_palette, int maximum_colors, png_const_uint_16p histogram,
- int full_quantize)
-{
- png_debug(1, "in png_set_quantize");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= PNG_QUANTIZE;
-
- if (full_quantize == 0)
- {
- int i;
-
- png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
- (png_uint_32)(num_palette * (sizeof (png_byte))));
- for (i = 0; i < num_palette; i++)
- png_ptr->quantize_index[i] = (png_byte)i;
- }
-
- if (num_palette > maximum_colors)
- {
- if (histogram != NULL)
- {
- /* This is easy enough, just throw out the least used colors.
- * Perhaps not the best solution, but good enough.
- */
-
- int i;
-
- /* Initialize an array to sort colors */
- png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
- (png_uint_32)(num_palette * (sizeof (png_byte))));
-
- /* Initialize the quantize_sort array */
- for (i = 0; i < num_palette; i++)
- png_ptr->quantize_sort[i] = (png_byte)i;
-
- /* Find the least used palette entries by starting a
- * bubble sort, and running it until we have sorted
- * out enough colors. Note that we don't care about
- * sorting all the colors, just finding which are
- * least used.
- */
-
- for (i = num_palette - 1; i >= maximum_colors; i--)
- {
- int done; /* To stop early if the list is pre-sorted */
- int j;
-
- done = 1;
- for (j = 0; j < i; j++)
- {
- if (histogram[png_ptr->quantize_sort[j]]
- < histogram[png_ptr->quantize_sort[j + 1]])
- {
- png_byte t;
-
- t = png_ptr->quantize_sort[j];
- png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];
- png_ptr->quantize_sort[j + 1] = t;
- done = 0;
- }
- }
-
- if (done != 0)
- break;
- }
-
- /* Swap the palette around, and set up a table, if necessary */
- if (full_quantize != 0)
- {
- int j = num_palette;
-
- /* Put all the useful colors within the max, but don't
- * move the others.
- */
- for (i = 0; i < maximum_colors; i++)
- {
- if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
- {
- do
- j--;
- while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
-
- palette[i] = palette[j];
- }
- }
- }
- else
- {
- int j = num_palette;
-
- /* Move all the used colors inside the max limit, and
- * develop a translation table.
- */
- for (i = 0; i < maximum_colors; i++)
- {
- /* Only move the colors we need to */
- if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
- {
- png_color tmp_color;
-
- do
- j--;
- while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
-
- tmp_color = palette[j];
- palette[j] = palette[i];
- palette[i] = tmp_color;
- /* Indicate where the color went */
- png_ptr->quantize_index[j] = (png_byte)i;
- png_ptr->quantize_index[i] = (png_byte)j;
- }
- }
-
- /* Find closest color for those colors we are not using */
- for (i = 0; i < num_palette; i++)
- {
- if ((int)png_ptr->quantize_index[i] >= maximum_colors)
- {
- int min_d, k, min_k, d_index;
-
- /* Find the closest color to one we threw out */
- d_index = png_ptr->quantize_index[i];
- min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
- for (k = 1, min_k = 0; k < maximum_colors; k++)
- {
- int d;
-
- d = PNG_COLOR_DIST(palette[d_index], palette[k]);
-
- if (d < min_d)
- {
- min_d = d;
- min_k = k;
- }
- }
- /* Point to closest color */
- png_ptr->quantize_index[i] = (png_byte)min_k;
- }
- }
- }
- png_free(png_ptr, png_ptr->quantize_sort);
- png_ptr->quantize_sort = NULL;
- }
- else
- {
- /* This is much harder to do simply (and quickly). Perhaps
- * we need to go through a median cut routine, but those
- * don't always behave themselves with only a few colors
- * as input. So we will just find the closest two colors,
- * and throw out one of them (chosen somewhat randomly).
- * [We don't understand this at all, so if someone wants to
- * work on improving it, be our guest - AED, GRP]
- */
- int i;
- int max_d;
- int num_new_palette;
- png_dsortp t;
- png_dsortpp hash;
-
- t = NULL;
-
- /* Initialize palette index arrays */
- png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
- (png_uint_32)(num_palette * (sizeof (png_byte))));
- png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
- (png_uint_32)(num_palette * (sizeof (png_byte))));
-
- /* Initialize the sort array */
- for (i = 0; i < num_palette; i++)
- {
- png_ptr->index_to_palette[i] = (png_byte)i;
- png_ptr->palette_to_index[i] = (png_byte)i;
- }
-
- hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *
- (sizeof (png_dsortp))));
-
- num_new_palette = num_palette;
-
- /* Initial wild guess at how far apart the farthest pixel
- * pair we will be eliminating will be. Larger
- * numbers mean more areas will be allocated, Smaller
- * numbers run the risk of not saving enough data, and
- * having to do this all over again.
- *
- * I have not done extensive checking on this number.
- */
- max_d = 96;
-
- while (num_new_palette > maximum_colors)
- {
- for (i = 0; i < num_new_palette - 1; i++)
- {
- int j;
-
- for (j = i + 1; j < num_new_palette; j++)
- {
- int d;
-
- d = PNG_COLOR_DIST(palette[i], palette[j]);
-
- if (d <= max_d)
- {
-
- t = (png_dsortp)png_malloc_warn(png_ptr,
- (png_uint_32)(sizeof (png_dsort)));
-
- if (t == NULL)
- break;
-
- t->next = hash[d];
- t->left = (png_byte)i;
- t->right = (png_byte)j;
- hash[d] = t;
- }
- }
- if (t == NULL)
- break;
- }
-
- if (t != NULL)
- for (i = 0; i <= max_d; i++)
- {
- if (hash[i] != NULL)
- {
- png_dsortp p;
-
- for (p = hash[i]; p; p = p->next)
- {
- if ((int)png_ptr->index_to_palette[p->left]
- < num_new_palette &&
- (int)png_ptr->index_to_palette[p->right]
- < num_new_palette)
- {
- int j, next_j;
-
- if (num_new_palette & 0x01)
- {
- j = p->left;
- next_j = p->right;
- }
- else
- {
- j = p->right;
- next_j = p->left;
- }
-
- num_new_palette--;
- palette[png_ptr->index_to_palette[j]]
- = palette[num_new_palette];
- if (full_quantize == 0)
- {
- int k;
-
- for (k = 0; k < num_palette; k++)
- {
- if (png_ptr->quantize_index[k] ==
- png_ptr->index_to_palette[j])
- png_ptr->quantize_index[k] =
- png_ptr->index_to_palette[next_j];
-
- if ((int)png_ptr->quantize_index[k] ==
- num_new_palette)
- png_ptr->quantize_index[k] =
- png_ptr->index_to_palette[j];
- }
- }
-
- png_ptr->index_to_palette[png_ptr->palette_to_index
- [num_new_palette]] = png_ptr->index_to_palette[j];
-
- png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
- = png_ptr->palette_to_index[num_new_palette];
-
- png_ptr->index_to_palette[j] =
- (png_byte)num_new_palette;
-
- png_ptr->palette_to_index[num_new_palette] =
- (png_byte)j;
- }
- if (num_new_palette <= maximum_colors)
- break;
- }
- if (num_new_palette <= maximum_colors)
- break;
- }
- }
-
- for (i = 0; i < 769; i++)
- {
- if (hash[i] != NULL)
- {
- png_dsortp p = hash[i];
- while (p)
- {
- t = p->next;
- png_free(png_ptr, p);
- p = t;
- }
- }
- hash[i] = 0;
- }
- max_d += 96;
- }
- png_free(png_ptr, hash);
- png_free(png_ptr, png_ptr->palette_to_index);
- png_free(png_ptr, png_ptr->index_to_palette);
- png_ptr->palette_to_index = NULL;
- png_ptr->index_to_palette = NULL;
- }
- num_palette = maximum_colors;
- }
- if (png_ptr->palette == NULL)
- {
- png_ptr->palette = palette;
- }
- png_ptr->num_palette = (png_uint_16)num_palette;
-
- if (full_quantize != 0)
- {
- int i;
- png_bytep distance;
- int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +
- PNG_QUANTIZE_BLUE_BITS;
- int num_red = (1 << PNG_QUANTIZE_RED_BITS);
- int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
- int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
- png_size_t num_entries = ((png_size_t)1 << total_bits);
-
- png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
- (png_uint_32)(num_entries * (sizeof (png_byte))));
-
- distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
- (sizeof (png_byte))));
-
- memset(distance, 0xff, num_entries * (sizeof (png_byte)));
-
- for (i = 0; i < num_palette; i++)
- {
- int ir, ig, ib;
- int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
- int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
- int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
-
- for (ir = 0; ir < num_red; ir++)
- {
- /* int dr = abs(ir - r); */
- int dr = ((ir > r) ? ir - r : r - ir);
- int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
- PNG_QUANTIZE_GREEN_BITS));
-
- for (ig = 0; ig < num_green; ig++)
- {
- /* int dg = abs(ig - g); */
- int dg = ((ig > g) ? ig - g : g - ig);
- int dt = dr + dg;
- int dm = ((dr > dg) ? dr : dg);
- int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
-
- for (ib = 0; ib < num_blue; ib++)
- {
- int d_index = index_g | ib;
- /* int db = abs(ib - b); */
- int db = ((ib > b) ? ib - b : b - ib);
- int dmax = ((dm > db) ? dm : db);
- int d = dmax + dt + db;
-
- if (d < (int)distance[d_index])
- {
- distance[d_index] = (png_byte)d;
- png_ptr->palette_lookup[d_index] = (png_byte)i;
- }
- }
- }
- }
- }
-
- png_free(png_ptr, distance);
- }
-}
-#endif /* PNG_READ_QUANTIZE_SUPPORTED */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-void PNGFAPI
-png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
- png_fixed_point file_gamma)
-{
- png_debug(1, "in png_set_gamma_fixed");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- /* New in libpng-1.5.4 - reserve particular negative values as flags. */
- scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
- file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
-
- /* Checking the gamma values for being >0 was added in 1.5.4 along with the
- * premultiplied alpha support; this actually hides an undocumented feature
- * of the previous implementation which allowed gamma processing to be
- * disabled in background handling. There is no evidence (so far) that this
- * was being used; however, png_set_background itself accepted and must still
- * accept '0' for the gamma value it takes, because it isn't always used.
- *
- * Since this is an API change (albeit a very minor one that removes an
- * undocumented API feature) the following checks were only enabled in
- * libpng-1.6.0.
- */
- if (file_gamma <= 0)
- png_error(png_ptr, "invalid file gamma in png_set_gamma");
-
- if (scrn_gamma <= 0)
- png_error(png_ptr, "invalid screen gamma in png_set_gamma");
-
- /* Set the gamma values unconditionally - this overrides the value in the PNG
- * file if a gAMA chunk was present. png_set_alpha_mode provides a
- * different, easier, way to default the file gamma.
- */
- png_ptr->colorspace.gamma = file_gamma;
- png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
- png_ptr->screen_gamma = scrn_gamma;
-}
-
-# ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)
{
png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
- convert_gamma_value(png_ptr, file_gamma));
+ convert_gamma_value(png_ptr, file_gamma));
}
-# endif /* FLOATING_POINT_SUPPORTED */
+#endif /* FLOATING_POINT */
#endif /* READ_GAMMA */
-#ifdef PNG_READ_EXPAND_SUPPORTED
-/* Expand paletted images to RGB, expand grayscale images of
- * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
- * to alpha channels.
- */
-void PNGAPI
-png_set_expand(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_expand");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-
-/* GRR 19990627: the following three functions currently are identical
- * to png_set_expand(). However, it is entirely reasonable that someone
- * might wish to expand an indexed image to RGB but *not* expand a single,
- * fully transparent palette entry to a full alpha channel--perhaps instead
- * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
- * the transparent color with a particular RGB value, or drop tRNS entirely.
- * IOW, a future version of the library may make the transformations flag
- * a bit more fine-grained, with separate bits for each of these three
- * functions.
- *
- * More to the point, these functions make it obvious what libpng will be
- * doing, whereas "expand" can (and does) mean any number of things.
- *
- * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified
- * to expand only the sample depth but not to expand the tRNS to alpha
- * and its name was changed to png_set_expand_gray_1_2_4_to_8().
- */
-
-/* Expand paletted images to RGB. */
-void PNGAPI
-png_set_palette_to_rgb(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_palette_to_rgb");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-
-/* Expand grayscale images of less than 8-bit depth to 8 bits. */
-void PNGAPI
-png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_expand_gray_1_2_4_to_8");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= PNG_EXPAND;
-}
-
-/* Expand tRNS chunks to alpha channels. */
-void PNGAPI
-png_set_tRNS_to_alpha(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_tRNS_to_alpha");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise
- * it may not work correctly.)
- */
-void PNGAPI
-png_set_expand_16(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_expand_16");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
-}
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-void PNGAPI
-png_set_gray_to_rgb(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_gray_to_rgb");
-
- if (!png_rtran_ok(png_ptr, 0))
- return;
-
- /* Because rgb must be 8 bits or more: */
- png_set_expand_gray_1_2_4_to_8(png_ptr);
- png_ptr->transformations |= PNG_GRAY_TO_RGB;
-}
-#endif
-
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-void PNGFAPI
-png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
- png_fixed_point red, png_fixed_point green)
+static void
+png_do_rtog_48(png_transformp *transform, png_transform_controlp tc)
{
- png_debug(1, "in png_set_rgb_to_gray");
+# define png_ptr (tc->png_ptr)
+ const png_uint_32 r = (*transform)->args >> 16;
+ const png_uint_32 g = (*transform)->args & 0xFFFFU;
+ const png_uint_32 b = 65536U - r - g;
- /* Need the IHDR here because of the check on color_type below. */
- /* TODO: fix this */
- if (!png_rtran_ok(png_ptr, 1))
- return;
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U;
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
- switch(error_action)
+ debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR &&
+ (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U)));
+
+ tc->sp = dp;
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
+
+ while (sp <= ep)
{
- case PNG_ERROR_ACTION_NONE:
- png_ptr->transformations |= PNG_RGB_TO_GRAY;
- break;
+ png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r +
+ ((sp[2] << 8) + sp[3]) * g +
+ ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16;
+ debug(gray < 65536U);
+ *dp++ = PNG_BYTE(gray >> 8);
+ *dp++ = PNG_BYTE(gray);
+ sp += 6U;
+ }
+# undef png_ptr
+}
+
+static void
+png_do_rtog_64(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ const png_uint_32 r = (*transform)->args >> 16;
+ const png_uint_32 g = (*transform)->args & 0xFFFFU;
+ const png_uint_32 b = 65536U - r - g;
+
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U;
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ debug(tc->bit_depth == 16U &&
+ tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA &&
+ (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U)));
+
+ tc->sp = dp;
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
+
+ while (sp <= ep)
+ {
+ png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r +
+ ((sp[2] << 8) + sp[3]) * g +
+ ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16;
+
+ debug(gray < 65536U);
+ *dp++ = PNG_BYTE(gray >> 8);
+ *dp++ = PNG_BYTE(gray);
+ sp += 6U;
+ *dp++ = *sp++; /* alpha */
+ *dp++ = *sp++;
+ }
+# undef png_ptr
+}
+
+static void
+png_init_rgb_to_gray_arithmetic(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* This only gets used in the final init stage: */
+ debug(tc->init == PNG_TC_INIT_FINAL && tc->bit_depth == 16U &&
+ (tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA)) ==
+ PNG_FORMAT_FLAG_COLOR);
+
+ (*transform)->fn = (tc->format & PNG_FORMAT_FLAG_ALPHA) ? png_do_rtog_64 :
+ png_do_rtog_48;
+
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
+ tc->invalid_info |= PNG_INFO_sBIT;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(png_ptr, tc->bit_depth);
+# undef png_ptr
+}
+
+typedef struct
+{
+ png_transform tr;
+ png_fixed_point red_coefficient;
+ png_fixed_point green_coefficient;
+ unsigned int coefficients_set :1;
+ unsigned int error_action :2;
+} png_transform_rgb_to_gray;
+
+static void
+png_update_rgb_status(png_structrp png_ptr, png_transformp *transform)
+{
+ png_transform_rgb_to_gray *tr = png_transform_cast(png_transform_rgb_to_gray,
+ *transform);
+
+ png_ptr->rgb_to_gray_status = 1U;
+ tr->tr.fn = NULL; /* one warning/error only */
+
+ switch (tr->error_action)
+ {
case PNG_ERROR_ACTION_WARN:
- png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+ png_warning(png_ptr, "RGB to gray found nongray pixel");
break;
case PNG_ERROR_ACTION_ERROR:
- png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+ png_error(png_ptr, "RGB to gray found nongray pixel");
break;
default:
- png_error(png_ptr, "invalid error action to rgb_to_gray");
break;
}
+}
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
-#ifdef PNG_READ_EXPAND_SUPPORTED
- png_ptr->transformations |= PNG_EXPAND;
-#else
- {
- /* Make this an error in 1.6 because otherwise the application may assume
- * that it just worked and get a memory overwrite.
- */
- png_error(png_ptr,
- "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
+static void
+png_do_rgb_check24(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
+ * channels are not equal.
+ */
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U;
- /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */
- }
-#endif
+ debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_FLAG_COLOR);
+
+ while (sp <= ep)
{
- if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)
+ if ((sp[0] ^ sp[1]) | (sp[2] ^ sp[1]))
{
- png_uint_16 red_int, green_int;
-
- /* NOTE: this calculation does not round, but this behavior is retained
- * for consistency, the inaccuracy is very small. The code here always
- * overwrites the coefficients, regardless of whether they have been
- * defaulted or set already.
- */
- red_int = (png_uint_16)(((png_uint_32)red*32768)/100000);
- green_int = (png_uint_16)(((png_uint_32)green*32768)/100000);
-
- png_ptr->rgb_to_gray_red_coeff = red_int;
- png_ptr->rgb_to_gray_green_coeff = green_int;
-# if defined(PNG_COLORS_SPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
- png_ptr->colorspace.flags |= PNG_COLORSPACE_RGB_TO_GRAY_SET;
-# endif
+ png_update_rgb_status(png_ptr, transform);
+ break;
}
- else
- {
- if (red >= 0 && green >= 0)
- png_app_warning(png_ptr,
- "ignoring out of range rgb_to_gray coefficients");
+ sp += 3U;
+ }
+# undef png_ptr
+}
- /* Use the defaults, from the cHRM chunk if set, else the historical
- * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See
- * png_do_rgb_to_gray for more discussion of the values. In this case
- * the coefficients are not marked as 'set' and are not overwritten if
- * something has already provided a default.
+static void
+png_do_rgb_check32(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
+ * channels are not equal and alpha is not zero.
+ */
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 4U;
+
+ debug(tc->bit_depth == 8U &&
+ tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
+
+ while (sp <= ep)
+ {
+ if (((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) && sp[3] != 0)
+ {
+ png_update_rgb_status(png_ptr, transform);
+ break;
+ }
+
+ sp += 4U;
+ }
+# undef png_ptr
+}
+
+static void
+png_do_rgb_check48(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
+ * channels are not equal.
+ */
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U;
+
+ debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR);
+
+ while (sp <= ep)
+ {
+ if ((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) |
+ (sp[1] ^ sp[3]) | (sp[5] ^ sp[3]))
+ {
+ png_update_rgb_status(png_ptr, transform);
+ break;
+ }
+
+ sp += 6U;
+ }
+# undef png_ptr
+}
+
+static void
+png_do_rgb_check64(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
+ * channels are not equal and alpha is not zero.
+ */
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U;
+
+ debug(tc->bit_depth == 16U &&
+ tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
+
+ while (sp <= ep)
+ {
+ if (((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) |
+ (sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) &&
+ (sp[6] | sp[7]) != 0)
+ {
+ png_update_rgb_status(png_ptr, transform);
+ break;
+ }
+
+ sp += 8U;
+ }
+# undef png_ptr
+}
+
+static void
+png_init_rgb_to_gray(png_transformp *transform, png_transform_controlp tc)
+{
+ png_structrp png_ptr = tc->png_ptr;
+
+ /* Basic checks: if there is no color in the format this transform is not
+ * applicable.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
+ {
+ png_transform_rgb_to_gray *tr = png_transform_cast(
+ png_transform_rgb_to_gray, *transform);
+
+ /* no colormap allowed: */
+ affirm(tc->init && !(tc->format & PNG_FORMAT_FLAG_COLORMAP));
+ /* no extra flags yet: */
+ debug(!(tc->format &
+ PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA)));
+ /* at present no non-palette caching: */
+ implies(tc->caching, tc->palette);
+
+ if (tc->init == PNG_TC_INIT_FORMAT)
+ {
+ /* The convertion should just remove the 'COLOR' flag and do nothing
+ * else, but if a tRNS chunk is present this would invalidate it.
+ * Handle this by expanding it now.
*/
- if (png_ptr->rgb_to_gray_red_coeff == 0 &&
- png_ptr->rgb_to_gray_green_coeff == 0)
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
+ png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
{
- png_ptr->rgb_to_gray_red_coeff = 6968;
- png_ptr->rgb_to_gray_green_coeff = 23434;
- /* png_ptr->rgb_to_gray_blue_coeff = 2366; */
+ /* Only if expand was requested and not cancelled: */
+ if (tc->expand_tRNS && !tc->strip_alpha)
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+
+ tc->invalid_info |= PNG_INFO_tRNS; /* prevent expansion later */
}
+
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
+ }
+
+ else /* PNG_TC_INIT_FINAL */
+ {
+ unsigned int index; /* channel to select (invalid) */
+ png_byte sBIT_color; /* sBIT of that channel if valid */
+ png_fixed_point r, g; /* Coefficients in range 0..65536 */
+
+ /* Push a tRNS transform if required. Because this is a push the
+ * transform the init needs to be run now. This needs to go in
+ * before the check on r==g==b because a color key might be used.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
+ png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
+ {
+ if (tc->expand_tRNS && !tc->strip_alpha)
+ {
+ png_transformp tr_expand = png_push_transform(png_ptr,
+ sizeof (png_expand), png_init_expand, transform, NULL);
+
+ debug(*transform == tr_expand);
+ tr_expand->args |= PNG_EXPAND_tRNS;
+ png_init_expand(transform, tc);
+ /* Check for the infinite loop possibility: */
+ affirm((tc->invalid_info & PNG_INFO_tRNS) != 0);
+ return;
+ }
+
+ else
+ tc->invalid_info |= PNG_INFO_tRNS;
+ }
+
+ {
+ png_fixed_point red, green;
+
+ if (tr->coefficients_set)
+ {
+ red = tr->red_coefficient;
+ green = tr->green_coefficient;
+ }
+
+# ifdef PNG_COLORSPACE_SUPPORTED
+ else if ((png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_HAVE_ENDPOINTS+PNG_COLORSPACE_INVALID))
+ == PNG_COLORSPACE_HAVE_ENDPOINTS)
+ {
+ red = png_ptr->colorspace.end_points_XYZ.red_Y;
+ green = png_ptr->colorspace.end_points_XYZ.green_Y;
+ }
+# endif
+
+ else /* no colorspace support, assume sRGB */
+ {
+ /* From IEC 61966-2-1:1999, the reverse transformation from sRGB
+ * RGB values to XYZ D65 values (not CIEXYZ!). These are not
+ * exact inverses of the forward transformation; they only have
+ * four (decimal) digits of precision.
+ *
+ * API CHANGE: in 1.7.0 the sRGB values from the official IEC
+ * specification are used, previously libpng used values from
+ * Charles Poynton's ColorFAQ of 1998-01-04. The original page
+ * is gone, however up to date information can be found below:
+ *
+ * http://www.poynton.com/ColorFAQ.html
+ *
+ * At the time of reading (20150628) this web site quotes the
+ * same values as below and cites ITU Rec 709 as the source.
+ */
+ red = 21260;
+ green = 71520;
+ }
+
+ /* Prior to 1.7 this calculation was done with 15-bit precision,
+ * this is because the code was written pre-muldiv and tried to
+ * work round the problems caused by the signs in integer
+ * calculations.
+ */
+ (void)png_muldiv(&r, red, 65536, PNG_FP_1);
+ (void)png_muldiv(&g, green, 65536, PNG_FP_1);
+ }
+
+ /* If the convertion can be deduced to select a single channel do so.
+ * If the error action is set to error just copy the red channel, if
+ * the coefficients select just one channel use that.
+ */
+ if (tr->error_action == PNG_ERROR_ACTION_ERROR || r >= 65536)
+ index = 0U, sBIT_color = tc->sBIT_R; /* select red */
+
+ else if (g >= 65536)
+ index = 1U, sBIT_color = tc->sBIT_G; /* select green */
+
+ else if (r + g == 0)
+ index = 2U, sBIT_color = tc->sBIT_B; /* select blue */
+
+ else
+ index = 3U, sBIT_color = 0U/*UNUSED*/;
+
+ if (index == 3U)
+ {
+ /* Arithmetic will have to be done. For this we need linear 16-bit
+ * data which must then be converted back to the required bit depth,
+ * png_init_gamma handles this. It may push other expand operations
+ * (it shouldn't but it can), so give it some space.
+ *
+ * The gamma must be restored to the original value, 0U for the bit
+ * depth means use the output bit depth.
+ */
+ (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, tc->gamma,
+ 0U/*bit depth*/, 0/*default*/);
+
+ /* If png_init_gamma is called with tc->gamma 0 it does the right
+ * thing in PNG_TC_INIT_FINAL; it just does any required bit depth
+ * adjustment.
+ */
+ (void)add_gamma_transform(png_ptr, tr->tr.order + 0x10U, PNG_FP_1,
+ 16U, 1/*force: doesn't matter*/);
+
+ {
+ /* This init routine will update the sBIT information
+ * appropriately.
+ */
+ png_transformp tr_rtog = png_add_transform(png_ptr, 0/*size*/,
+ png_init_rgb_to_gray_arithmetic, tr->tr.order + 0x20U);
+
+ /* r and g are known to be in the range 0..65535, so pack them
+ * into the 'args' argument of a new transform.
+ */
+ tr_rtog->args = (((png_uint_32)r) << 16) + g;
+ }
+ }
+
+ else /* index < 3 */
+ {
+ /* TODO: does this need to select the correct sBIT value too? */
+ png_add_rgb_to_gray_byte_ops(png_ptr, tc, index,
+ tr->tr.order + 0x10U);
+ tc->sBIT_G = sBIT_color;
+ }
+
+ /* Prior to 1.7 libpng would always check for r!=g!=b. In 1.7 an extra
+ * error_action setting is added to prevent this overhead.
+ */
+ if (tr->error_action)
+ tr->tr.fn = tc->bit_depth == 8 ?
+ ((tc->format & PNG_FORMAT_FLAG_ALPHA) ?
+ png_do_rgb_check32 : png_do_rgb_check24) :
+ ((tc->format & PNG_FORMAT_FLAG_ALPHA) ?
+ png_do_rgb_check64 : png_do_rgb_check48);
+
+ else
+ tr->tr.fn = NULL; /* PNG_ERROR_ACTION_NO_CHECK */
+ }
+ }
+
+ else /* not color: transform not applicable */
+ (*transform)->fn = NULL;
+}
+
+void PNGFAPI
+png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
+ png_fixed_point red, png_fixed_point green)
+ /* API CHANGE: in 1.7 calling this on a palette PNG no longer causes the
+ * palette to be expanded (unless explicitly requested), rather it converts
+ * the palette to grayscale.
+ */
+{
+ /* The coefficients must be reasonable, the error handling is to warn (pre
+ * 1.7) or app error (1.7) and drop back to the cHRM definition of Y. The
+ * drop back is done in the init routine if relevant flag is unset. Passing
+ * negative values causes this default to be used without a warning.
+ */
+ int pset = 0;
+
+ if (red >= 0 && green >= 0)
+ {
+ if (red <= PNG_FP_1 && green <= PNG_FP_1 && red + green <= PNG_FP_1)
+ pset = 1;
+
+ else /* overflow */
+ png_app_error(png_ptr, "rgb_to_gray coefficients too large (ignored)");
+ }
+
+ {
+ png_transform_rgb_to_gray *tr =
+ png_transform_cast(png_transform_rgb_to_gray,
+ png_add_transform(png_ptr, sizeof (png_transform_rgb_to_gray),
+ png_init_rgb_to_gray, PNG_TR_RGB_TO_GRAY));
+
+ tr->error_action = 0x3U & error_action;
+
+ if (red < 0 || green < 0) /* use cHRM default */
+ tr->coefficients_set = 0U;
+
+ else if (pset) /* else bad coefficients which get ignored */
+ {
+ tr->coefficients_set = 1U;
+ tr->red_coefficient = red;
+ tr->green_coefficient = green;
}
}
}
@@ -1040,777 +4067,2677 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
void PNGAPI
png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
- double green)
+ double green)
{
png_set_rgb_to_gray_fixed(png_ptr, error_action,
png_fixed(png_ptr, red, "rgb to gray red coefficient"),
png_fixed(png_ptr, green, "rgb to gray green coefficient"));
}
#endif /* FLOATING POINT */
-
#endif /* RGB_TO_GRAY */
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
- defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-void PNGAPI
-png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
- read_user_transform_fn)
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+typedef struct
{
- png_debug(1, "in png_set_read_user_transform_fn");
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- png_ptr->transformations |= PNG_USER_TRANSFORM;
- png_ptr->read_user_transform_fn = read_user_transform_fn;
-#endif
-}
-#endif
-
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* In the case of gamma transformations only do transformations on images where
- * the [file] gamma and screen_gamma are not close reciprocals, otherwise it
- * slows things down slightly, and also needlessly introduces small errors.
- */
-static int /* PRIVATE */
-png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)
-{
- /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
- * correction as a difference of the overall transform from 1.0
- *
- * We want to compare the threshold with s*f - 1, if we get
- * overflow here it is because of wacky gamma values so we
- * turn on processing anyway.
- */
- png_fixed_point gtest;
- return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) ||
- png_gamma_significant(gtest);
-}
-#endif
-
-/* Initialize everything needed for the read. This includes modifying
- * the palette.
- */
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-static void
-gamma_correct_background(unsigned int value, unsigned int depth,
- png_uint_16p backgroundp, png_uint_16p background_1p,
- png_fixed_point gamma_correct, png_fixed_point gamma_to_1)
-{
- switch (depth)
+ png_transform tr;
+ struct
{
- case 8:
- if (gamma_correct != PNG_FP_1)
- *backgroundp = png_gamma_8bit_correct(value, gamma_correct);
+ png_color_16 background;
+ unsigned int need_expand :1; /* Background matches format of PNG */
+ unsigned int rgb_to_gray :1; /* RGB-to-gray transform found */
+ unsigned int compose_background :1; /* png_set_background */
+ unsigned int associate_alpha :1;
+ unsigned int encode_alpha :1;
+ unsigned int optimize_alpha :1;
+ unsigned int background_is_gray :1; /* Background color is gray */
+ unsigned int background_bit_depth :5; /* bit depth, 1..16 */
+ unsigned int ntrans :3; /* 1..6 bytes */
+ png_byte transparent_pixel[6];
+ png_byte background_pixel[6];
+ png_fixed_point background_gamma;
+ } st; /* to allow the whole state to be copied reliably */
+} png_transform_background;
- else
- *backgroundp = (png_uint_16)value;
+static void
+resolve_background_color(png_transform_background *tr,
+ png_transform_controlp tc)
+{
+ png_const_structp png_ptr = tc->png_ptr;
- if (gamma_to_1 != PNG_FP_1)
- *background_1p = png_gamma_16bit_correct(value*257, gamma_to_1);
-
- else
- *background_1p = (png_uint_16)(value*257);
+ /* Deduce the bit depth and color information for the background, the
+ * special case is when need_expand is set and the PNG has palette format,
+ * then (and only then) the background value is a palette index.
+ */
+ if (tr->st.need_expand && tc->palette)
+ {
+ unsigned int i = tr->st.background.index;
+ png_byte r, g, b;
+ if (i >= png_ptr->num_palette)
+ {
+ png_app_error(png_ptr, "background index out of range");
+ tr->tr.fn = NULL;
return;
+ }
- case 16:
- if (gamma_correct != PNG_FP_1)
- *backgroundp = png_gamma_16bit_correct(value, gamma_correct);
+ tr->st.background_bit_depth = 8U;
+ r = png_ptr->palette[i].red;
+ g = png_ptr->palette[i].green;
+ b = png_ptr->palette[i].blue;
- else
- *backgroundp = (png_uint_16)value;
+ if (r == g && g == b)
+ {
+ tr->st.background_is_gray = 1U;
+ tr->st.background.gray = g;
+ UNTESTED
+ }
- if (gamma_to_1 != PNG_FP_1)
- *background_1p = png_gamma_16bit_correct(value, gamma_to_1);
-
- else
- *background_1p = (png_uint_16)value;
-
- return;
-
- default:
- /* Low bit depth gray levels; do no harm. */
- break;
+ else
+ {
+ tr->st.background_is_gray = 0U;
+ tr->st.background.red = r;
+ tr->st.background.green = g;
+ tr->st.background.blue = b;
+ UNTESTED
+ }
}
- *backgroundp = (png_uint_16)value;
- *background_1p = 0; /* should not be used */
+ else /* background is not a palette index */
+ {
+ int use_rgb;
+ png_uint_16 mask;
+
+ /* First work out the bit depth and whether or not to use the RGB
+ * fields of the background.
+ */
+ if (tr->st.need_expand)
+ {
+ affirm(!(tc->format & PNG_FORMAT_FLAG_COLORMAP));
+ tr->st.background_bit_depth =
+ png_check_bits(png_ptr, png_ptr->bit_depth, 5U);
+ use_rgb = (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0;
+ }
+
+ else /* screen format background */
+ {
+ /* If the final output is in palette format assume the background
+ * is in a matching format. This covers two cases, an original
+ * COLORMAP PNG and png_set_quantize.
+ */
+ if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ tr->st.background_bit_depth = 8U;
+
+ else
+ tr->st.background_bit_depth =
+ png_check_bits(png_ptr, png_ptr->row_bit_depth, 5U);
+
+ use_rgb = (png_ptr->row_format & PNG_FORMAT_FLAG_COLOR) != 0;
+ }
+
+ /* The PNG spec says to use the low bits of the values, so we mask out
+ * the high bits here (at present no warning is produced if they are
+ * set.)
+ */
+ mask = png_check_u16(png_ptr, (1U << tr->st.background_bit_depth)-1U);
+
+ if (use_rgb)
+ {
+ png_uint_16 r, g, b;
+
+ r = tr->st.background.red & mask;
+ g = tr->st.background.green & mask;
+ b = tr->st.background.blue & mask;
+
+ if (r == g && g == b)
+ {
+ tr->st.background_is_gray = 1U;
+ tr->st.background.gray = g;
+ }
+
+ else
+ {
+ tr->st.background_is_gray = 0U;
+ tr->st.background.red = r;
+ tr->st.background.green = g;
+ tr->st.background.blue = b;
+ }
+ }
+
+ else /* gray */
+ {
+ tr->st.background_is_gray = 1U;
+ tr->st.background.gray = tr->st.background.gray & mask;
+ }
+ }
}
-static void /* PRIVATE */
-png_init_background_transformations(png_structrp png_ptr)
- /* Set the png_ptr->background and png_ptr->background_1 members correctly
- * for the bit depth and format.
- */
+static void
+gamma_correct_background_component(png_const_structrp png_ptr, png_uint_16p cp,
+ unsigned int bdc, png_fixed_point correction, unsigned int bdout)
+ /* Utility function for gamma_correct_background. */
{
- /* png_ptr->background is only assigned by png_set_background and
- * png_set_alpha_mode (which just zeros out the fields.) png_set_background
- * can set the PNG_FLAG_BACKGROUND_EXPAND flag if the input value is in the
- * file format, for example if it comes from a bKGD chunk.
- *
- * Under some circumstances deficiencies in the current libpng code mean that
- * the bit depth of the values must differ from the final bit depth; the bit
- * depth has to match that at which the processing of the image pixels
- * happens and this is not always the final bit depth. This is fixed up
- * here.
- *
- * First find the required depth.
- */
- unsigned int bit_depth, required_bit_depth;
- unsigned int color_type = png_ptr->color_type;
- const png_uint_32 transform = png_ptr->transformations;
- const int expand = (png_ptr->flags & PNG_FLAG_BACKGROUND_EXPAND) != 0;
+ unsigned int c = *cp;
- if (color_type & PNG_COLOR_MASK_PALETTE)
- required_bit_depth = bit_depth = 8;
+ /* 0.0 and 1.0 are unchanged (and common): */
+ if (c > 0U && c < (1U<png_ptr)
+ png_fixed_point correction = tc->gamma;
+ const unsigned int bdback = tr->st.background_bit_depth;
+ const unsigned int bdrow = tc->bit_depth;
+
+ /* This is harmless if it fails but it will damage the output pixels - they
+ * won't have the requested color depth accuracy where the background is
+ * used.
+ */
+ debug(bdback <= bdrow);
+ debug(tr->st.background_is_gray || (bdrow >= 8U && bdback >= 8U));
+
+ /* The background is assumed to be full precision; there is no sBIT
+ * information for it. The convertion converts from the current depth and
+ * gamma of the background to that in the transform control. It uses the
+ * full 16-bit precision when considering the gamma values even though this
+ * is probably spurious.
+ */
+ if (correction != 0 && (tr->st.background_gamma == 0 ||
+ png_gamma_equal(png_ptr, tr->st.background_gamma, correction,
+ &correction, 16U)))
+ correction = 0; /* no correction! */
+
+ if (tr->st.background_is_gray)
+ gamma_correct_background_component(png_ptr, &tr->st.background.gray,
+ bdback, correction, bdrow);
else
{
- required_bit_depth = bit_depth = png_ptr->bit_depth;
-
- /* But not PNG_EXPAND_16 at present because it happens after the compose
- * operation where the background is used!
- */
- if (bit_depth < 8 && (transform & PNG_EXPAND) != 0)
- required_bit_depth = 8;
+ gamma_correct_background_component(png_ptr, &tr->st.background.red,
+ bdback, correction, bdrow);
+ gamma_correct_background_component(png_ptr, &tr->st.background.green,
+ bdback, correction, bdrow);
+ gamma_correct_background_component(png_ptr, &tr->st.background.blue,
+ bdback, correction, bdrow);
}
- /* bit_depth and color_type now refer to the original file data and
- * required_bit_depth is correct for the processing libpng does, however it
- * does not necessarily match the output the application gets, fix that and
- * the color type here:
+ /* Regardless of whether there was a correction set the background gamma: */
+ tr->st.background_gamma = tc->gamma;
+ tr->st.background_bit_depth = png_check_bits(png_ptr, bdrow, 5U);
+# undef png_ptr
+}
+
+static void
+fill_background_pixel(png_transform_background *tr, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* Fill in 'background_pixel' if the appropriate sequence of bytes for the
+ * format given in the transform control.
*/
- if (expand == 0)
- {
- /* The background bit_depth and color_type need correcting */
- if ((transform & PNG_EXPAND) != 0)
- color_type &= ~PNG_COLOR_MASK_PALETTE;
+ unsigned int bdtc = tc->bit_depth;
- /* The RGB<->gray transformations do the to gray operation first, then the
- * from gray.
- */
- if ((transform & PNG_RGB_TO_GRAY) != 0)
- color_type &= ~PNG_COLOR_MASK_COLOR;
-
- if ((transform & PNG_GRAY_TO_RGB) != 0)
- color_type |= PNG_COLOR_MASK_COLOR;
-
- bit_depth = required_bit_depth;
-
- /* The expansion to 16 bits and the scaling back from 16 bits per
- * component to only 8 happens after the background processing (at
- * present) so these transforms only affect the screen value, not the
- * required value. Note that the 16_TO_8 conversions happen before the 8
- * to 16 one, so in theory both could occur - the order of the tests below
- * must be correct!
- *
- * TODO: Note that the second of these changes cause an input 16-bit
- * background value to be temporarily crushed to 8-bits per component,
- * losing precision. This is a bug and should be fixed.
- */
- if (bit_depth == 16 &&
- (transform & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0)
- bit_depth = 8;
-
- if (bit_depth == 8 && (color_type & PNG_COLOR_MASK_PALETTE) == 0 &&
- (transform & PNG_EXPAND_16) != 0)
- bit_depth = 16;
- }
-
- /* Now make the background have the correct format. This involves reading the
- * correct fields from png_ptr->background, adjusting the bit depth of the
- * result and potentially gamma correcting the value then calculating the
- * png_ptr->background_1 values too.
+ /* If necessary adjust the background pixel to the current row format (it is
+ * important to do this as late as possible to avoid spurious
+ * interconvertions).
*/
- {
- unsigned int mult = 1;
- png_fixed_point gamma_to_1, gamma_correct;
+ gamma_correct_background(tr, tc);
- switch (png_ptr->background_gamma_type)
+ if (tr->st.background_is_gray)
+ {
+ unsigned int g = tr->st.background.gray;
+
+ /* 'g' now has enough bits for the destination, note that in the case of
+ * low bit depth gray this causes the pixel to be replicated through the
+ * written byte. Fill all six bytes with the replicated background:
+ */
+ while (bdtc < 8U)
{
- case PNG_BACKGROUND_GAMMA_SCREEN:
- gamma_to_1 = png_ptr->screen_gamma;
- gamma_correct = PNG_FP_1;
+ g &= (1U << bdtc) - 1U; /* use only the low bits */
+ g |= g << bdtc;
+ bdtc <<= 1;
+ }
+
+ memset(tr->st.background_pixel, PNG_BYTE(g), 6U);
+ if (bdtc == 16U)
+ tr->st.background_pixel[0] = tr->st.background_pixel[2] =
+ tr->st.background_pixel[4] = PNG_BYTE(g >> 8);
+ /* Must not include the alpha channel here: */
+ tr->st.ntrans = png_check_bits(png_ptr,
+ ((tc->format & PNG_FORMAT_FLAG_COLOR)+1U) << (bdtc == 16U), 3U);
+ }
+
+ else
+ {
+ unsigned int r = tr->st.background.red;
+ unsigned int g = tr->st.background.green;
+ unsigned int b = tr->st.background.blue;
+
+ debug((tc->format & PNG_FORMAT_FLAG_COLOR) != 0);
+
+ switch (bdtc)
+ {
+ case 8U:
+ tr->st.background_pixel[0] = PNG_BYTE(r);
+ tr->st.background_pixel[1] = PNG_BYTE(g);
+ tr->st.background_pixel[2] = PNG_BYTE(b);
+ tr->st.ntrans = 3U;
break;
- case PNG_BACKGROUND_GAMMA_FILE:
- gamma_to_1 = png_reciprocal(png_ptr->colorspace.gamma);
- gamma_correct = png_reciprocal2(png_ptr->colorspace.gamma,
- png_ptr->screen_gamma);
- break;
-
- case PNG_BACKGROUND_GAMMA_UNIQUE:
- gamma_to_1 = png_reciprocal(png_ptr->background_gamma);
- gamma_correct = png_reciprocal2(png_ptr->background_gamma,
- png_ptr->screen_gamma);
+ case 16U:
+ tr->st.background_pixel[0] = PNG_BYTE(r>>8);
+ tr->st.background_pixel[1] = PNG_BYTE(r);
+ tr->st.background_pixel[2] = PNG_BYTE(g>>8);
+ tr->st.background_pixel[3] = PNG_BYTE(g);
+ tr->st.background_pixel[4] = PNG_BYTE(b>>8);
+ tr->st.background_pixel[5] = PNG_BYTE(b);
+ tr->st.ntrans = 6U;
break;
default:
- gamma_to_1 = PNG_FP_1;
- gamma_correct = PNG_FP_1;
+ NOT_REACHED;
+ }
+ }
+# undef png_ptr
+}
+
+/* Look for colors matching the trans_color in png_ptr and replace them. This
+ * must handle all the non-alpha formats.
+ */
+static void
+png_do_replace_tRNS_multi(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ const unsigned int cbytes = tr->st.ntrans;
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - cbytes/*safety*/;
+ const int copy = dp != sp;
+
+ /* We expect opaque and transparent pixels to be interleaved but with long
+ * sequences of each.
+ */
+ debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
+ PNG_TC_PIXEL_DEPTH(*tc) == cbytes << 3);
+ tc->invalid_info |= PNG_INFO_tRNS;
+ tc->sp = dp;
+
+ /* Look for pixels that match the transparent value, copying opaque ones as
+ * required.
+ */
+ do
+ {
+ const png_const_bytep opaque_start = sp;
+ size_t cb;
+
+ /* Find a transparent pixel, or the end: */
+ do
+ {
+ if (memcmp(sp, tr->st.transparent_pixel, cbytes) == 0) /*transparent*/
+ break;
+ sp += cbytes;
+ }
+ while (sp <= ep);
+
+ cb = sp - opaque_start;
+
+ /* Copy any opaque pixels: */
+ if (cb > 0)
+ {
+ if (copy)
+ memcpy(dp, opaque_start, cb);
+ dp += cb;
+ }
+
+ /* Set transparent pixels to the background (this has to be done one-by
+ * one; the case where all the bytes in the background are equal is not
+ * optimized.)
+ */
+ if (sp <= ep) do
+ {
+ memcpy(dp, tr->st.background_pixel, cbytes);
+ sp += cbytes;
+ dp += cbytes;
+ }
+ while (sp <= ep && memcmp(sp, tr->st.transparent_pixel, cbytes) == 0);
+ } while (sp <= ep);
+
+ debug(sp == ep+cbytes);
+# undef png_ptr
+}
+
+static void
+png_do_replace_tRNS_8(png_transformp *transform, png_transform_controlp tc)
+ /* The single byte version: 8-bit gray */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_alloc_size_t row_bytes = tc->width;
+ const int copy = dp != sp;
+ const int transparent_pixel = tr->st.transparent_pixel[0];
+ const int background_pixel = tr->st.background_pixel[0];
+
+ /* We expect opaque and transparent pixels to be interleaved but with long
+ * sequences of each.
+ */
+ debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
+ PNG_TC_PIXEL_DEPTH(*tc) == 8 && tr->st.ntrans == 1);
+ tc->invalid_info |= PNG_INFO_tRNS;
+ tc->sp = dp;
+
+ /* Now search for a byte that matches the transparent pixel. */
+ do
+ {
+ const png_const_bytep tp = png_voidcast(png_const_bytep,
+ memchr(sp, transparent_pixel, row_bytes));
+ png_alloc_size_t cb;
+
+ if (tp == NULL) /* all remaining pixels are opaque */
+ {
+ if (copy)
+ memcpy(dp, sp, row_bytes);
+ return;
+ }
+
+ cb = tp - sp;
+ if (cb > 0) /* some opaque pixels found */
+ {
+ if (copy)
+ memcpy(dp, sp, cb);
+ sp = tp;
+ dp += cb;
+ debug(row_bytes > cb);
+ row_bytes -= cb;
+ }
+
+ /* Now count the transparent pixels, this could use strspn but for the
+ * moment does not.
+ */
+ debug(row_bytes > 0);
+ ++sp; /* next to check, may be beyond the last */
+ while (--row_bytes > 0 && *sp == transparent_pixel) ++sp;
+
+ cb = sp - tp;
+ memset(dp, background_pixel, cb);
+ dp += cb;
+ } while (row_bytes > 0);
+ UNTESTED
+# undef png_ptr
+}
+
+static void
+png_do_set_row(png_transformp *transform, png_transform_controlp tc)
+ /* This is a no-op transform that both invalidates INFO from args and sets
+ * the entire row to the byte given in the top bits.
+ */
+{
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ tc->sp = dp;
+ memset(dp, (*transform)->args >> 24, PNG_TC_ROWBYTES(*tc));
+}
+
+static void
+png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc)
+{
+ /* This is the 2 or 4 bit depth grayscale case; the 1 bit case is handled by
+ * the two routines above and the 8-bit and 16-bit cases by the two before
+ * that.
+ *
+ * The transform contains pixel values that have been expanded to one byte,
+ * the code needs to match the tRNS pixel and substitute the background one
+ * in each byte.
+ */
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ const unsigned int copy = sp != dp;
+ const png_byte transparent_pixel = tr->st.transparent_pixel[0];
+ const png_byte background_pixel = tr->st.background_pixel[0];
+
+ /* We expect opaque and transparent pixels to be interleaved but with long
+ * sequences of each.
+ */
+ debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
+ PNG_TC_PIXEL_DEPTH(*tc) < 8 && tr->st.ntrans == 1);
+ tc->sp = dp;
+
+ /* Now search for a byte that contains the transparent pixel
+ *
+ * NOTE: this is the "strlen" algorithm, I first saw a variant implemented in
+ * Acorn RISC iX (strlen) around 1991, almost certainly derived from a
+ * suggestion by Alan Mycroft dating from April 27, 1987 (Mycroft was one of
+ * the authors of the 'Norcroft' compiler used for RISC iX, and well known to
+ * the RISC iX implementors.) See, e.g.:
+ *
+ * http://bits.stephan-brumme.com/null.html.
+ *
+ * The exact form used here is the one reported by Brumme; I haven't been
+ * able to find the original Mycroft posting, it was probably on comp.arch.
+ *
+ * The 4-bit and 2-bit versions (probably slower in the 4-bit case than the
+ * do-it-by-pixel version, but definately faster once 32-bit handling is
+ * implemented):
+ *
+ * 4 bit: (byte - 0x11) & ~byte & 0x88
+ * 2 bit: (byte - 0x55) & ~byte & 0xcc
+ *
+ * The generalizations to 32 bits (8 and 16 pixels per step) should be
+ * obvious.
+ *
+ * This algorithm reads pixels within a byte beyond the end of the row and,
+ * potentially, changes the non-existent pixels. This is harmless and not
+ * a security risk.
+ */
+ if (tc->bit_depth == 4U)
+ {
+ /* For the moment the algorithm isn't used; there are only two pixels in
+ * each byte so it is likely to be quicker to check as below:
+ */
+ do
+ {
+ const png_byte b = *sp++;
+ const unsigned int m = b ^ transparent_pixel;
+
+ if (m == 0U) /* both transparent */
+ *dp = background_pixel;
+
+ else if ((m & 0xF0U) == 0U) /* first transparent */
+ *dp = PNG_BYTE((background_pixel & 0xF0U) | (b & 0x0FU));
+
+ else if ((m & 0x0FU) == 0U) /* second transparent */
+ *dp = PNG_BYTE((background_pixel & 0x0FU) | (b & 0xF0U));
+
+ else if (copy) /* neither transparent */
+ *dp = b;
+
+ ++dp;
+ } while (sp < ep);
+ }
+
+ else
+ {
+ affirm(tc->bit_depth == 2U);
+
+ do
+ {
+ const png_byte b = *sp++;
+ const unsigned int m = b ^ transparent_pixel;
+
+ if (m == 0U) /* transparent */
+ *dp = background_pixel;
+
+ else if (0xAAU & ((m - 0x55U) & ~m))
+ {
+ /* One or more pixels transparent */
+ const unsigned int mask =
+ (m & 0xC0U ? 0xC0U : 0U) |
+ (m & 0x30U ? 0x30U : 0U) |
+ (m & 0x0CU ? 0x0CU : 0U) |
+ (m & 0x03U ? 0x03U : 0U);
+
+ *dp = PNG_BYTE((b & mask) | (background_pixel & ~mask));
+ }
+
+ else if (copy) /* no transparent pixels */
+ *dp = b;
+
+ ++dp;
+ } while (sp < ep);
+ }
+
+# undef png_ptr
+}
+
+static void
+png_do_background_with_transparent_GA8(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/;
+ const png_byte background_pixel = tr->st.background_pixel[0];
+
+ /* Because this is an alpha format and we are removing the alpha channel we
+ * can copy up.
+ */
+ debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_GA &&
+ tr->st.ntrans == 1U);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->sp = dp;
+
+ /* Look for pixels that have alpha 0; all others should have alpha 1.0,
+ * however they are simply treated as opaque regardless.
+ */
+ do
+ {
+ *dp++ = (sp[1] == 0U) ? background_pixel : sp[0];
+ sp += 2U;
+ } while (sp < ep);
+
+ debug(sp == ep+1U);
+# undef png_ptr
+}
+
+static void
+png_do_background_with_transparent_GA16(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
+
+ debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA &&
+ tr->st.ntrans == 2U);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->sp = dp;
+
+ do
+ {
+ if (sp[2] == 0U && sp[3] == 0U) /* transparent */
+ dp[0] = tr->st.background_pixel[0], dp[1] = tr->st.background_pixel[1];
+
+ else
+ dp[0] = sp[0], dp[1] = sp[1];
+
+ dp += 2U;
+ sp += 4U;
+ } while (sp < ep);
+
+ debug(sp == ep+3U);
+# undef png_ptr
+}
+
+static void
+png_do_background_with_transparent_GAlbd(png_transformp *transform,
+ png_transform_controlp tc)
+ /* This is the low-bit-depth gray case, the input is 1, 2 or 4-bit per
+ * channel gray-alpha.
+ */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ const unsigned int bit_depth = tc->bit_depth;
+ const unsigned int mask = (1U << bit_depth) - 1U;
+ const unsigned int back = tr->st.background_pixel[0] & mask;
+ unsigned int opos, ob, inb;
+
+ debug(bit_depth < 8U && tc->format == PNG_FORMAT_GA && tr->st.ntrans == 1U);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->sp = dp;
+
+ ob = 0U; /* output byte */
+ opos = 0U; /* bit index of previous output pixel (counts down) */
+ inb = 0U; /* quiet a GCC 4.8.5 warning */
+
+ for (;;)
+ {
+ /* The output is half the size of the input, so we need a new input byte
+ * for every 4 bits of output:
+ */
+ if (opos == 0U || opos == 4U)
+ {
+ if (sp >= ep)
+ break;
+
+ inb = *sp++;
+ }
+
+ /* Move to the next *output* pixel, this wraps when bits is 0U: */
+ opos = (opos - bit_depth) & 0x7U;
+
+ /* Extract the whole input pixel to the low bits of a temporary: */
+ {
+ unsigned int pixel = inb >> ((opos*2U) & 0x7U);
+
+ /* The alpha channel is second, check for a value of 0: */
+ if ((pixel & mask)/* A component*/ == 0U)
+ pixel = back;
+
+ else
+ {
+ debug((pixel & mask) == mask);
+ pixel = (pixel >> bit_depth) & mask; /* G component */
+ }
+
+ ob |= pixel << opos;
+ }
+
+ if (opos == 0U)
+ *dp++ = PNG_BYTE(ob), ob = 0U;
+ }
+
+ if (opos != 0U)
+ *dp++ = PNG_BYTE(ob);
+
+ debug(sp == ep);
+# undef png_ptr
+}
+
+static void
+png_do_background_with_transparent_RGBA8(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
+
+ debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_RGBA &&
+ tr->st.ntrans == 3U);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->sp = dp;
+
+ do
+ {
+ if (sp[3] == 0U) /* transparent */
+ memcpy(dp, tr->st.background_pixel, 3U);
+
+ else
+ memmove(dp, sp, 3U);
+
+ dp += 3U;
+ sp += 4U;
+ } while (sp < ep);
+
+ debug(sp == ep+3U);
+# undef png_ptr
+}
+
+static void
+png_do_background_with_transparent_RGBA16(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/;
+
+ debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA &&
+ tr->st.ntrans == 6U);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->sp = dp;
+
+ do
+ {
+ if (sp[6] == 0U && sp[7] == 0U) /* transparent */
+ memcpy(dp, tr->st.background_pixel, 6U);
+
+ else
+ memmove(dp, sp, 6U);
+
+ dp += 6U;
+ sp += 8U;
+ } while (sp < ep);
+
+ debug(sp == ep+7U);
+# undef png_ptr
+}
+
+static void
+png_init_background_transparent(png_transformp *transform,
+ png_transform_controlp tc)
+ /* Select the correct version of the above routines. */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+
+ debug(tc->init == PNG_TC_INIT_FINAL /* never called in 'FORMAT' */ &&
+ (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
+
+ /* Now we know the format on which processing will happen so it is possible
+ * to generate the correct fill pixel value to use.
+ */
+ fill_background_pixel(tr, tc);
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ tc->invalid_info |= PNG_INFO_sBIT;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(png_ptr, tc->bit_depth);
+
+ if (!(tc->format & PNG_FORMAT_FLAG_COLOR))
+ {
+ if (tc->bit_depth == 8U)
+ tr->tr.fn = png_do_background_with_transparent_GA8;
+
+ else if (tc->bit_depth == 16U)
+ tr->tr.fn = png_do_background_with_transparent_GA16;
+
+ else /* low-bit-depth gray with alpha (not a PNG format!) */
+ tr->tr.fn = png_do_background_with_transparent_GAlbd;
+ }
+
+ else /* color */
+ {
+ if (tc->bit_depth == 8U)
+ tr->tr.fn = png_do_background_with_transparent_RGBA8;
+
+ else
+ {
+ debug(tc->bit_depth == 16U);
+ tr->tr.fn = png_do_background_with_transparent_RGBA16;
+ }
+ }
+# undef png_ptr
+}
+
+/* The calculated values below have the range 0..65535*65535, the output has the
+ * range 0..65535, so divide by 65535. Two approaches are given here, one
+ * modifies the value in place, the other uses a more complex expression. With
+ * gcc on an AMD64 system the in-place approach is very slightly faster.
+ *
+ * The two expressions are slightly different in what they calculate but both
+ * give the exact answer (verified by exhaustive testing.)
+ *
+ * The macro must be given a png_uint_32 variable (lvalue), normally an auto
+ * variable.
+ */
+#ifndef PNG_COMPOSE_DIV_65535
+# ifdef PNG_COMPOSE_DIV_EXPRESSION_SUPPORTED
+# define PNG_COMPOSE_DIV_65535(v)\
+ (v = ((v + (v>>16) + (v>>31) + 32768U) >> 16))
+# else
+# define PNG_COMPOSE_DIV_65535(v)\
+ (v += v >> 16, v += v >> 31, v += 32768U, v >>= 16)
+# endif
+#endif
+
+static void
+png_do_background_alpha_GA(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
+ const unsigned int background = tr->st.background.gray;
+ const int copy = (sp != dp);
+ const int compose = tr->st.compose_background;
+
+ affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA &&
+ tr->st.background_bit_depth == 16U);
+
+ /* If gamma transforms are eliminated this might fail: */
+ debug(tr->st.background_gamma == tc->gamma ||
+ tr->st.background_gamma == 0 ||
+ tc->sBIT_G == 1);
+
+ tc->sp = tc->dp; /* nothing else changes */
+
+ do
+ {
+ const png_uint_32 alpha = (sp[2] << 8) + sp[3];
+
+ switch (alpha)
+ {
+ case 0U: /* transparent */
+ memset(dp, 0U, 4U);
+ break;
+
+ default:
+ {
+ png_uint_32 v = ((sp[0] << 8) + sp[1]) * alpha +
+ background * (65535U - alpha);
+
+ PNG_COMPOSE_DIV_65535(v);
+ debug(v <= 65535U);
+ dp[0] = PNG_BYTE(v >> 8);
+ dp[1] = PNG_BYTE(v);
+ }
+
+ if (compose)
+ dp[3] = dp[2] = 0xFFU; /* alpha; set to 1.0 */
+
+ else if (copy)
+ {
+ dp[2] = PNG_BYTE(alpha >> 8);
+ dp[3] = PNG_BYTE(alpha);
+ }
+ break;
+
+ case 65535U: /* opaque */
+ if (copy)
+ memcpy(dp, sp, 4U);
break;
}
-# define CORRECT(v, c)\
- gamma_correct_background((v)*mult, bit_depth,\
- &png_ptr->background.c, &png_ptr->background_1.c,\
- gamma_correct, gamma_to_1);\
- if (bit_depth > required_bit_depth)\
- png_ptr->background.c =\
- (png_uint_16)PNG_DIV257(png_ptr->background.c)
+ sp += 4U;
+ dp += 4U;
+ }
+ while (sp < ep);
- /* The multiplier 'mult' scales the values to 'required_depth',
- * 'bit_depth' is the depth of the resultant values.
- */
- while (bit_depth < required_bit_depth)
- mult += mult << bit_depth, bit_depth <<= 1;
+ debug(sp == ep+3U);
+# undef png_ptr
+}
- /* In the event that this still leaves the background bit depth greater
- * than the libpng required depth scale the values back to the 8-bit
- * range, the test below verifies that this is correct.
- */
- if (bit_depth > required_bit_depth)
+static void
+png_do_background_alpha_RGBA(png_transformp *transform,
+ png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/;
+ const unsigned int bred = tr->st.background.red;
+ const unsigned int bgreen = tr->st.background.green;
+ const unsigned int bblue = tr->st.background.blue;
+ const int copy = (sp != dp);
+ const int compose = tr->st.compose_background;
+
+ affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA &&
+ tr->st.background_bit_depth == 16U);
+
+ debug(tr->st.background_gamma == tc->gamma ||
+ tr->st.background_gamma == 0 ||
+ (tc->sBIT_R == 1 && tc->sBIT_G == 1 && tc->sBIT_B == 1));
+
+ tc->sp = tc->dp; /* nothing else changes */
+
+ do
+ {
+ const png_uint_32 alpha = (sp[6] << 8) + sp[7];
+
+ switch (alpha)
{
- if (bit_depth != 16 || required_bit_depth != 8)
- png_error(png_ptr, "internal error handling background depth");
+ case 0U: /* transparent */
+ memset(dp, 0U, 8U);
+ break;
+
+ default:
+ {
+ const png_uint_32 balpha = (65535U - alpha);
+ png_uint_32 r = ((sp[0] << 8) + sp[1]) * alpha + bred * balpha;
+ png_uint_32 g = ((sp[2] << 8) + sp[3]) * alpha + bgreen * balpha;
+ png_uint_32 b = ((sp[4] << 8) + sp[5]) * alpha + bblue * balpha;
+
+ PNG_COMPOSE_DIV_65535(r);
+ PNG_COMPOSE_DIV_65535(g);
+ PNG_COMPOSE_DIV_65535(b);
+ debug(r <= 65535U && g <= 65535U && b <= 65535U);
+ dp[0] = PNG_BYTE(r >> 8);
+ dp[1] = PNG_BYTE(r);
+ dp[2] = PNG_BYTE(g >> 8);
+ dp[3] = PNG_BYTE(g);
+ dp[4] = PNG_BYTE(b >> 8);
+ dp[5] = PNG_BYTE(b);
+ }
+
+ if (compose)
+ dp[7] = dp[6] = 0xFFU;
+
+ else if (copy)
+ {
+ dp[6] = PNG_BYTE(alpha >> 8);
+ dp[7] = PNG_BYTE(alpha);
+ }
+ break;
+
+ case 65535U: /* opaque */
+ if (copy)
+ memcpy(dp, sp, 8U);
+ break;
}
- if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
- {
- png_ptr->flags &= ~PNG_FLAG_BACKGROUND_IS_GRAY; /* checked below */
+ sp += 8U;
+ dp += 8U;
+ }
+ while (sp < ep);
- /* If need_expand was passed to png_set_background the background value
- * was in the file format, therefore if the file is a palette file the
- * background will have been an index into the palette. Notice that if
- * need_expand was false then the color is RGB even if the output still
- * has a palette.
+ debug(sp == ep+7U);
+# undef png_ptr
+}
+
+static void
+png_init_background_alpha_end(png_transformp *transform,
+ png_transform_controlp tc)
+ /* This is just the last part of png_init_background_alpha (below) */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+
+ debug(tc->init == PNG_TC_INIT_FINAL);
+
+ /* Repeat the tests at the end of png_init_background_alpha: */
+ affirm(tc->bit_depth == 16U && (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
+ debug(tc->gamma == 0 ||
+ !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc)));
+
+ /* tr->st.background_is_gray was filled in by resolve_background_color and
+ * records if either the background was a gray value or it was a color
+ * value with all the channels equal.
+ */
+ if (!tr->st.background_is_gray && !(tc->format & PNG_FORMAT_FLAG_COLOR))
+ {
+# ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /* Color background with gray data: this happens when there is a
+ * gray to RGB transform in the pipeline but it hasn't happened
+ * yet. Unfortunately it has to happen now to be able to do the
+ * compose against the colored background.
*/
- if (expand && (color_type & PNG_COLOR_MASK_PALETTE) != 0)
- {
- unsigned int background_index = png_ptr->background.index;
+ png_push_gray_to_rgb_byte_ops(transform, tc);
+ affirm((tc->format & PNG_FORMAT_FLAG_COLOR) != 0);
+ return;
+# else /* !GRAY_TO_RGB */
+ impossible("gray to RGB"); /* how can this happen? */
+# endif /* !GRAY_TO_RGB */
+ }
- if (background_index < png_ptr->num_palette &&
- png_ptr->palette != NULL)
+ /* The transform happens in two parts, a part to do the arithmetic on
+ * pixels where it is required followed by a part to replace transparent
+ * pixels. These two parts require different versions of the background
+ * pixel. Set up the second part first.
+ *
+ * This only happens with background composition, otherwise the
+ * transparent pixels are already 0 and nothing needs to be done.
+ */
+ if (tr->st.compose_background)
+ {
+ /* The transparent pixel handling happens *after* the data has been
+ * re-encoded to the output gamma:
+ */
+ png_transform_background *tr_alpha =
+ png_transform_cast(png_transform_background,
+ png_add_transform(png_ptr, sizeof (png_transform_background),
+ png_init_background_transparent, PNG_TR_GAMMA_ENCODE+0xF0U));
+
+ /* Copy the current state into the new png_transform_background: */
+ tr_alpha->st = tr->st;
+ tr_alpha->tr.args = tr->tr.args;
+ }
+
+ /* Now it is possible to overwrite tr->st.background with the linear version.
+ */
+ gamma_correct_background(tr, tc);
+
+ /* sBIT informationmust also be invalidated here, because a gamma
+ * transform may run before the transparent pixel handling.
+ */
+ tc->invalid_info |= PNG_INFO_sBIT;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(png_ptr, tc->bit_depth);
+
+ /* And select an appropriate function; there are only two choices: */
+ switch (tc->format)
+ {
+ case PNG_FORMAT_GA:
+ /* If the background format is color this indicates that there is a
+ * gray to RGB transform missing and we need it to happen before
+ * this point!
+ */
+ affirm(tr->st.background_is_gray);
+ tr->tr.fn = png_do_background_alpha_GA;
+ break;
+
+ case PNG_FORMAT_RGBA:
+ if (tr->st.background_is_gray)
+ tr->st.background.blue = tr->st.background.green =
+ tr->st.background.red = tr->st.background.gray;
+ tr->tr.fn = png_do_background_alpha_RGBA;
+ break;
+
+ default:
+ NOT_REACHED;
+ }
+# undef png_ptr
+}
+
+static void
+png_init_background_alpha(png_transformp *transform, png_transform_controlp tc)
+ /* This is used when alpha composition is required because the alpha channel
+ * may contain values that are between 0 and 1. Because doing alpha
+ * composition requires linear arithmetic the data is converted to 16-bit
+ * linear, however this means that the background pixel gets converted too
+ * and, for 16-bit output, this tends to smash the value. Consequently the
+ * algorithm used here is to skip those pixels and use the 'transparent
+ * alpha' routines to replace them after the gamma correction step.
+ */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+
+ debug(tc->init == PNG_TC_INIT_FINAL);
+ /* png_init_background ensures this is true: */
+ debug((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
+
+ /* Always push gamma transforms; don't try to optimize the case when they
+ * aren't needed because that would be an attempt to duplicate the tests in
+ * png_init_gamma and it might now work reliably.
+ *
+ * Need to push the to-linear transform *before* this transform and add gamma
+ * correction afterward to get back to the screen format. Do the afterward
+ * bit first to avoid complexity over *transform:
+ */
+ {
+ png_transform_gamma *tr_end = add_gamma_transform(png_ptr,
+ PNG_TR_GAMMA_ENCODE, tc->gamma, 0U/*bit depth*/, 0/*default*/);
+
+ /* Encoding the alpha channel happens in the last step, so this needs to
+ * be set here. Notice that in C++ terms we are very friendly with
+ * png_transform_gamma.
+ */
+ tr_end->encode_alpha = tr->st.encode_alpha;
+ tr_end->optimize_alpha = tr->st.optimize_alpha;
+ }
+
+ {
+ /* Now add tr_gamma before this transform, expect it to go in at
+ * *transform or the whole thing won't work:
+ */
+ png_transform_gamma *tr_gamma = png_transform_cast(png_transform_gamma,
+ png_push_transform(png_ptr, sizeof (png_transform_gamma),
+ png_init_gamma, transform, NULL/*don't run init*/));
+
+ /* This must happen before we run png_gamma_init: */
+ tr_gamma->to_gamma = PNG_FP_1;
+ tr_gamma->to_bit_depth = 16U;
+
+ /* Now run the this transform; it was pushed before this one, so it gets
+ * to do its init first and this function must return as the caller will
+ * immediately call here again.
+ */
+ debug(*transform == &tr_gamma->tr);
+ png_init_gamma(transform, tc);
+ affirm(tc->bit_depth == 16U &&
+ (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
+ /* This is only a 'debug' because it needs to replicate the test in
+ * png_init_gamma and that is easy to get wrong (a harmless mistake).
+ */
+ debug(tc->gamma == 0 ||
+ !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc)));
+ }
+
+ /* A transform was pushed, so this transform init will be run again: */
+ tr->tr.fn = png_init_background_alpha_end;
+# undef png_ptr
+}
+
+/* Handle alpha and tRNS via a background color */
+static void
+png_init_background(png_transformp *transform, png_transform_controlp tc)
+{
+ /* This init function is called right at the start, this means it can get at
+ * the tRNS values if appropriate. If not the RGB to gray transform comes
+ * next followed by PNG_TR_COMPOSE_ALPHA, which actually does the non-tRNS
+ * work.
+ */
+ png_structp png_ptr = tc->png_ptr;
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background, *transform);
+
+ if (tc->init == PNG_TC_INIT_FORMAT)
+ {
+ /* Background composition removes the alpha channel, so the other
+ * operations become irrelevant:
+ */
+ if (tr->st.compose_background)
+ tr->st.associate_alpha = tr->st.encode_alpha = tr->st.optimize_alpha =
+ 0U;
+
+ else if (!tr->st.associate_alpha)
+ {
+ /* There is nothing to do, delete the whole transform. */
+ tr->tr.fn = NULL;
+ return;
+ }
+
+ /* Else alpha association ('pre-multiplication') which is achieved by
+ * composing on a 0 background. The background color will be black (all
+ * zeros) and the background gamma will be zero.
+ */
+
+ /* Because we are in PNG_TC_INIT_FORMAT no other transforms will have been
+ * inserted between this one and an rgb-to-gray transform, so we can find
+ * out if rgb-to-gray has been requested:
+ */
+ tr->st.rgb_to_gray = tr->tr.next != NULL &&
+ tr->tr.next->order == PNG_TR_RGB_TO_GRAY;
+
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ /* Associated alpha does not strip the alpha channel! */
+ if (tr->st.compose_background)
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+ }
+
+ else if (!tc->palette &&
+ png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
+ {
+ /* tRNS will be expanded, or handled */
+ tc->invalid_info |= PNG_INFO_tRNS;
+ if (!tr->st.compose_background)
+ {
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ /* And in this case, only, because we are adding an alpha channel we
+ * need to have a channel depth of at least 8:
+ */
+ if (tc->bit_depth < 8U)
+ tc->bit_depth = 8U;
+ }
+ }
+
+ else /* no transparent pixels to change */
+ tr->tr.fn = NULL;
+ }
+
+ else /* PNG_TC_INIT_FINAL */
+ {
+ png_fixed_point correction;
+
+ debug(tc->init == PNG_TC_INIT_FINAL &&
+ ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ||
+ (!tc->palette && png_ptr->num_trans == 1 &&
+ !(tc->invalid_info & PNG_INFO_tRNS))));
+
+ /* The screen gamma is known, so the background gamma can be found, note
+ * that both the gamma values used below will be 0 if no gamma information
+ * was in the PNG and no gamma information has been provided by
+ * png_set_gamma or png_set_alpha_mode.
+ */
+ switch (tr->st.background_gamma)
+ {
+ case PNG_BACKGROUND_GAMMA_FILE:
+ /* png_init_transform_control has already found the file gamma,
+ * and because this is the first arithmetic transformation
+ * nothing has changed it.
+ */
+ tr->st.background_gamma = tc->gamma;
+ break;
+
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ tr->st.background_gamma = png_ptr->row_gamma;
+ break;
+
+ default:
+ /* already set */
+ break;
+ }
+
+ /* Work out what the background color is, this only depends on 'tc' for
+ * palette information, so it can be done now before we know the actual
+ * bit_depth/format that will be required:
+ */
+ resolve_background_color(tr, tc);
+
+ /* Is this format compatible with the current row data? If it is then it
+ * is possible to avoid the arithmetic if no alpha processing is required.
+ * This is a useful optimization because PNG files with just transparent
+ * pixels and no alpha are common.
+ *
+ * NOTE: if an RGB-to-gray transform is present this is fine so long as
+ * the background is gray, otherwise (non-gray background) there is a
+ * following gray-to-RGB transform and the now gray image must be
+ * composited on a color background.
+ */
+ if (tr->st.compose_background /* alpha channel stripped */ &&
+ (tr->st.background_is_gray ||
+ ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0 && !tr->st.rgb_to_gray))
+ /* color compatible */ &&
+ tc->bit_depth >= tr->st.background_bit_depth
+ /* bit depth compatible */ &&
+ (tc->transparent_alpha ||
+ (!tc->palette && png_ptr->num_trans == 1 &&
+ !(tc->invalid_info & PNG_INFO_tRNS)))
+ /* no alpha processing */ &&
+ png_gamma_equal(png_ptr, tc->gamma, png_ptr->row_gamma, &correction,
+ tc->bit_depth) /* gamma compatible (so no gamma processing) */)
+ {
+ /* How the operation gets performed depends on whether the current data
+ * has an alpha channel or not.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ affirm(tc->transparent_alpha);
+ /* This init routine does the sBIT handling: */
+ png_init_background_transparent(transform, tc);
+ }
+
+ else if (!tc->palette && png_ptr->num_trans == 1 &&
+ !(tc->invalid_info & PNG_INFO_tRNS))
+ {
+ /* The background pixel needs to be filled in now; no more init
+ * routines are called in this case. It is important to delay this
+ * as late as possible because it needs to know the actual tc format
+ * that must be used.
+ */
+ fill_background_pixel(tr, tc);
+
+ debug(!(png_ptr->color_type & PNG_COLOR_MASK_PALETTE));
+
+ /* The pixel depth should not have been changed yet: */
+ debug(PNG_PIXEL_DEPTH(*png_ptr) == PNG_TC_PIXEL_DEPTH(*tc));
+
+ /* The transparent_pixel value needs to be filled in. */
+ affirm(tr->st.ntrans ==
+ fill_transparent_pixel(png_ptr, tr->st.transparent_pixel));
+
+ /* The whole operation is a no-op if the transparent pixel and the
+ * background pixel match, even in the associated alpha case where
+ * both will be 0 throughout.
+ *
+ * NOTE: for palette images this test happens in the caching
+ * operation, so the answer is still correct.
+ *
+ * NOTE: for low bit depth gray both 'transparent_pixel' and
+ * 'background_pixel' have been expanded to fill a byte, so this
+ * works.
+ */
+ if (memcmp(tr->st.transparent_pixel, tr->st.background_pixel,
+ tr->st.ntrans) == 0)
+ tr->tr.fn = NULL;
+
+ /* Then the processing function depends on the pixel size: */
+ else if (tr->st.ntrans > 1U)
+ tr->tr.fn = png_do_replace_tRNS_multi;
+
+ else if (tc->bit_depth == 8U)
+ tr->tr.fn = png_do_replace_tRNS_8;
+
+ else if (tc->bit_depth == 1U)
{
- /* In fact 'mult' is always 1 at present in this case */
- CORRECT(png_ptr->palette[background_index].red, red);
- CORRECT(png_ptr->palette[background_index].green, green);
- CORRECT(png_ptr->palette[background_index].blue, blue);
+ /* This is the silly case: the replacement pixel does not match
+ * the transparent pixel (handled above) so either all the '0'
+ * bits are replaced by '1' or all the '1' bits are replaced by
+ * '0':
+ */
+ png_uint_32 args = tr->st.background_pixel[0];
+
+ args <<= 24;
+ args |= PNG_INFO_tRNS | PNG_INFO_sRGB;
+ tr->tr.args = args;
+ tr->tr.fn = png_do_set_row;
}
else
- {
- png_app_error(png_ptr, "out of range background index");
- memset(&png_ptr->background, 0, sizeof png_ptr->background);
- memset(&png_ptr->background_1, 0, sizeof png_ptr->background_1);
- }
+ tr->tr.fn = png_do_replace_tRNS_lbd;
+
+ tc->invalid_info |= PNG_INFO_tRNS | PNG_INFO_sBIT;
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
+ png_check_byte(png_ptr, tc->bit_depth);
}
else
{
- CORRECT(png_ptr->background.red, red);
- CORRECT(png_ptr->background.green, green);
- CORRECT(png_ptr->background.blue, blue);
+ /* Nothing to do; should have been eliminated before! */
+ tr->tr.fn = NULL;
+ NOT_REACHED;
}
+ }
- if (png_ptr->background.red == png_ptr->background.blue &&
- png_ptr->background.red == png_ptr->background.green)
+ else /* alpha, or maybe gamma, processing required */
+ {
+ /* Alpha case, add an appropriate transform; this has to be done
+ * *after* the RGB-to-gray case so move the transform info there:
+ */
+ png_transform_background *tr_alpha =
+ png_transform_cast(png_transform_background,
+ png_add_transform(png_ptr, sizeof (png_transform_background),
+ png_init_background_alpha, PNG_TR_COMPOSE_ALPHA));
+
+ /* Copy the current state into the new png_transform_background: */
+ tr_alpha->st = tr->st;
+ tr_alpha->tr.args = tr->tr.args;
+
+ /* The rest of the init occurs later; this transform is no longer
+ * needed.
+ */
+ tr->tr.fn = NULL;
+
+ /* Ensure that png_init_background_alpha gets an alpha channel, this
+ * needs to happen here because otherwise intervening transforms can
+ * invalidate tRNS.
+ */
+ tc->expand_tRNS = 1U;
+ if (tr->st.compose_background)
+ tc->strip_alpha = 0U;
+
+ /* And push the expand: */
+ (void)push_gamma_expand(transform, tc, 1/*need alpha*/);
+
+ /* Regardless of whether anything got pushed the following should now
+ * be true:
+ */
+ affirm((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 &&
+ tc->bit_depth >= 8U);
+ }
+ }
+}
+
+void PNGFAPI
+png_set_background_fixed(png_structrp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, png_fixed_point background_gamma)
+{
+ if (png_ptr != NULL)
+ {
+ if (background_color != NULL)
+ {
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background,
+ png_add_transform(png_ptr, sizeof (png_transform_background),
+ png_init_background, PNG_TR_COMPOSE));
+
+ /* This silently overwrites the information if png_set_background is
+ * called more than once.
+ */
+ tr->st.background = *background_color;
+ tr->st.need_expand = need_expand != 0;
+ tr->st.compose_background = 1U; /* png_set_background called */
+ switch (background_gamma_code)
{
- png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY;
- png_ptr->background.gray = png_ptr->background.red;
- png_ptr->background_1.gray = png_ptr->background_1.red;
- }
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ case PNG_BACKGROUND_GAMMA_FILE:
+ tr->st.background_gamma = background_gamma_code;
+ break;
- else
- png_ptr->background.gray = png_ptr->background_1.gray = 0;
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ if (background_gamma >= 16 && background_gamma <= 625000000)
+ {
+ tr->st.background_gamma = background_gamma;
+ break;
+ }
+
+ png_app_error(png_ptr, "gamma value out of range");
+ /* FALL THROUGH */
+ default:
+ png_app_error(png_ptr, "invalid gamma information");
+ tr->st.background_gamma = (need_expand ?
+ PNG_BACKGROUND_GAMMA_FILE : PNG_BACKGROUND_GAMMA_SCREEN);
+ break;
+ }
}
else
- {
- png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY;
-
- CORRECT(png_ptr->background.gray, gray);
-
- png_ptr->background.red =
- png_ptr->background.green =
- png_ptr->background.blue = png_ptr->background.gray;
-
- png_ptr->background_1.red =
- png_ptr->background_1.green =
- png_ptr->background_1.blue = png_ptr->background_1.gray;
- }
-# undef CORRECT
- }
-}
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
-
-static void /* PRIVATE */
-png_init_palette_transformations(png_structrp png_ptr)
-{
- int input_has_alpha = 0;
- int input_has_transparency = 0;
-
- if (png_ptr->num_trans > 0)
- {
- int i;
-
- /* Ignore if all the entries are opaque (unlikely!) */
- for (i=0; inum_trans; ++i)
- {
- if (png_ptr->trans_alpha[i] == 255)
- continue;
- else if (png_ptr->trans_alpha[i] == 0)
- input_has_transparency = 1;
- else
- {
- input_has_transparency = 1;
- input_has_alpha = 1;
- break;
- }
- }
- }
-
- /* If no alpha we can optimize. */
- if (input_has_alpha == 0)
- {
- /* Any alpha means background and associative alpha processing is
- * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
- * and ENCODE_ALPHA are irrelevant.
- */
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-
- if (input_has_transparency == 0)
- png_ptr->transformations &= ~PNG_COMPOSE;
+ png_app_error(png_ptr, "missing background color");
}
}
-static void /* PRIVATE */
-png_init_rgb_transformations(png_structrp png_ptr)
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_background(png_structrp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, double background_gamma)
{
- /* Added to libpng-1.5.4: check the color type to determine whether there
- * is any alpha or transparency in the image and simply cancel the
- * background and alpha mode stuff if there isn't.
- */
- int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0;
- int input_has_transparency = png_ptr->num_trans > 0;
-
- /* If no alpha we can optimize. */
- if (input_has_alpha == 0)
- {
- /* Any alpha means background and associative alpha processing is
- * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
- * and ENCODE_ALPHA are irrelevant.
- */
-# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-# endif
-
- if (input_has_transparency == 0)
- png_ptr->transformations &= ~PNG_COMPOSE;
- }
+ png_set_background_fixed(png_ptr, background_color, background_gamma_code,
+ need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
}
-
-void /* PRIVATE */
-png_init_read_transformations(png_structrp png_ptr)
-{
- png_debug(1, "in png_init_read_transformations");
-
- /* This internal function is called from png_read_start_row in pngrutil.c
- * and it is called before the 'rowbytes' calculation is done, so the code
- * in here can change or update the transformations flags.
- *
- * First do updates that do not depend on the details of the PNG image data
- * being processed.
- */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds
- * png_set_alpha_mode and this is another source for a default file gamma so
- * the test needs to be performed later - here. In addition prior to 1.5.4
- * the tests were repeated for the PALETTE color type here - this is no
- * longer necessary (and doesn't seem to have been necessary before.)
- */
- {
- /* The following temporary indicates if overall gamma correction is
- * required.
- */
- int gamma_correction = 0;
-
- if (png_ptr->colorspace.gamma != 0) /* has been set */
- {
- if (png_ptr->screen_gamma != 0) /* screen set too */
- gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
- png_ptr->screen_gamma);
-
- else
- /* Assume the output matches the input; a long time default behavior
- * of libpng, although the standard has nothing to say about this.
- */
- png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);
- }
-
- else if (png_ptr->screen_gamma != 0)
- /* The converse - assume the file matches the screen, note that this
- * perhaps undesireable default can (from 1.5.4) be changed by calling
- * png_set_alpha_mode (even if the alpha handling mode isn't required
- * or isn't changed from the default.)
- */
- png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);
-
- else /* neither are set */
- /* Just in case the following prevents any processing - file and screen
- * are both assumed to be linear and there is no way to introduce a
- * third gamma value other than png_set_background with 'UNIQUE', and,
- * prior to 1.5.4
- */
- png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;
-
- /* We have a gamma value now. */
- png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
-
- /* Now turn the gamma transformation on or off as appropriate. Notice
- * that PNG_GAMMA just refers to the file->screen correction. Alpha
- * composition may independently cause gamma correction because it needs
- * linear data (e.g. if the file has a gAMA chunk but the screen gamma
- * hasn't been specified.) In any case this flag may get turned off in
- * the code immediately below if the transform can be handled outside the
- * row loop.
- */
- if (gamma_correction != 0)
- png_ptr->transformations |= PNG_GAMMA;
-
- else
- png_ptr->transformations &= ~PNG_GAMMA;
- }
-#endif
-
- /* Certain transformations have the effect of preventing other
- * transformations that happen afterward in png_do_read_transformations,
- * resolve the interdependencies here. From the code of
- * png_do_read_transformations the order is:
- *
- * 1) PNG_EXPAND (including PNG_EXPAND_tRNS)
- * 2) PNG_STRIP_ALPHA (if no compose)
- * 3) PNG_RGB_TO_GRAY
- * 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY
- * 5) PNG_COMPOSE
- * 6) PNG_GAMMA
- * 7) PNG_STRIP_ALPHA (if compose)
- * 8) PNG_ENCODE_ALPHA
- * 9) PNG_SCALE_16_TO_8
- * 10) PNG_16_TO_8
- * 11) PNG_QUANTIZE (converts to palette)
- * 12) PNG_EXPAND_16
- * 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY
- * 14) PNG_INVERT_MONO
- * 15) PNG_INVERT_ALPHA
- * 16) PNG_SHIFT
- * 17) PNG_PACK
- * 18) PNG_BGR
- * 19) PNG_PACKSWAP
- * 20) PNG_FILLER (includes PNG_ADD_ALPHA)
- * 21) PNG_SWAP_ALPHA
- * 22) PNG_SWAP_BYTES
- * 23) PNG_USER_TRANSFORM [must be last]
- */
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_STRIP_ALPHA)
- {
- if (!(png_ptr->transformations & PNG_FILLER))
- png_ptr->transformations &= ~(PNG_INVERT_ALPHA|PNG_SWAP_ALPHA);
-
- if (!(png_ptr->transformations & PNG_COMPOSE))
- {
- /* Stripping the alpha channel happens immediately after the 'expand'
- * transformations, before all other transformations, so it cancels out
- * the alpha handling. It has the side effect negating the effect of
- * PNG_EXPAND_tRNS too:
- */
- png_ptr->transformations &= ~(PNG_ENCODE_ALPHA | PNG_EXPAND_tRNS);
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
-
- /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen
- * so transparency information would remain just so long as it wasn't
- * expanded. This produces unexpected API changes if the set of things
- * that do PNG_EXPAND_tRNS changes (perfectly possible given the
- * documentation - which says ask for what you want, accept what you
- * get.) This makes the behavior consistent from 1.5.4:
- */
- png_ptr->num_trans = 0;
- }
- }
-#endif /* STRIP_ALPHA supported, no COMPOSE */
+# endif /* FLOATING_POINT */
+#endif /* READ_BACKGROUND */
#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
- /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA
- * settings will have no effect.
- */
- if (!png_gamma_significant(png_ptr->screen_gamma))
+void PNGFAPI
+png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
+ png_fixed_point output_gamma)
+{
+ if (png_ptr != NULL)
{
- png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
- png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
- }
-#endif
+ /* Check the passed in output_gamma value; it must be valid and it must be
+ * converted to the reciprocal for use below:
+ */
+ output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- /* Make sure the coefficients for the rgb to gray conversion are set
- * appropriately.
- */
- if (png_ptr->transformations & PNG_RGB_TO_GRAY)
- png_colorspace_set_rgb_coefficients(png_ptr);
-#endif
-
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
- png_init_palette_transformations(png_ptr);
-
- else
- png_init_rgb_transformations(png_ptr);
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- /* Set up the background information if required. It is only used if
- * PNG_COMPOSE is specified.
- */
- if (png_ptr->transformations & PNG_COMPOSE)
- png_init_background_transformations(png_ptr);
-#endif
-
- /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations
- * can be performed directly on the palette, and some (such as rgb to gray)
- * can be optimized inside the palette. This is particularly true of the
- * composite (background and alpha) stuff, which can be pretty much all done
- * in the palette even if the result is expanded to RGB or gray afterward.
- *
- * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and
- * earlier and the palette stuff is actually handled on the first row. This
- * leads to the reported bug that the palette returned by png_get_PLTE is not
- * updated.
- */
-#if 0 /* NYI */
- png_do_palette_transformations(png_ptr);
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- /* This needs to change - in the palette image case a whole set of tables are
- * built when it would be quicker to just calculate the correct value for
- * each palette entry directly. Also, the test is too tricky - why check
- * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that
- * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the
- * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction
- * the gamma tables will not be built even if composition is required on a
- * gamma encoded value.
- *
- * In 1.5.4 this is addressed below by an additional check on the individual
- * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the
- * tables.
- */
- if ((png_ptr->transformations & PNG_GAMMA)
- || ((png_ptr->transformations & PNG_RGB_TO_GRAY)
- && (png_gamma_significant(png_ptr->colorspace.gamma) ||
- png_gamma_significant(png_ptr->screen_gamma)))
- || ((png_ptr->transformations & PNG_COMPOSE)
- && (png_gamma_significant(png_ptr->colorspace.gamma)
- || png_gamma_significant(png_ptr->screen_gamma)
-# ifdef PNG_READ_BACKGROUND_SUPPORTED
- || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE
- && png_gamma_significant(png_ptr->background_gamma))
-# endif
- )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA)
- && png_gamma_significant(png_ptr->screen_gamma))
- )
- {
- png_build_gamma_tables(png_ptr, png_ptr->bit_depth);
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- if (png_ptr->transformations & PNG_COMPOSE)
+ if (output_gamma > 0) /* Else an app_error has been signalled. */
{
- /* Issue a warning about this combination: because RGB_TO_GRAY is
- * optimized to do the gamma transform if present yet do_background has
- * to do the same thing if both options are set a
- * double-gamma-correction happens. This is true in all versions of
- * libpng to date.
+ /* Only set the colorspace gamma if it has not already been set (this
+ * has the side effect that the gamma in a second call to
+ * png_set_alpha_mode will be ignored.)
*/
- if (png_ptr->transformations & PNG_RGB_TO_GRAY)
- png_warning(png_ptr,
- "libpng does not support gamma+background+rgb_to_gray");
-
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ if ((png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_INVALID | PNG_COLORSPACE_HAVE_GAMMA)) !=
+ PNG_COLORSPACE_HAVE_GAMMA)
{
- unsigned int i, num_palette = png_ptr->num_palette;
- png_color back;
- png_color_16 back_1 = png_ptr->background_1;
- png_colorp palette = png_ptr->palette;
+ /* The default file gamma is the output gamma encoding: */
+ png_ptr->colorspace.gamma = output_gamma;
+ if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
+ png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA;
+ else
+ png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+ }
- back.red = (png_byte)png_ptr->background.red;
- back.green = (png_byte)png_ptr->background.green;
- back.blue = (png_byte)png_ptr->background.blue;
+ /* Always set the output gamma, note that it may be changed to PNG_FP_1
+ * for the associated alpha support. This means that the last call to
+ * png_set_gamma[_fixed] or png_set_alpha_mode sets the output gamma,
+ * which is probably what is expected.
+ */
+ {
+ png_transform_gamma *tr_gamma = add_gamma_transform(png_ptr,
+ PNG_TR_GAMMA_ENCODE,
+ mode == PNG_ALPHA_ASSOCIATED ? PNG_FP_1 : output_gamma, 0U,
+ 1/*force*/);
- for (i = 0; i < num_palette; i++)
+ /* Get a background transform and set the appropriate fields.
+ *
+ * png_set_background removes the alpha channel so it effectively
+ * disbles png_set_alpha_mode however png_set_alpha_mode is still
+ * useful to set a default gamma value.
+ */
+ png_transform_background *tr =
+ png_transform_cast(png_transform_background,
+ png_add_transform(png_ptr, sizeof (png_transform_background),
+ png_init_background, PNG_TR_COMPOSE));
+
+ /* There are really 8 possibilities here, composed of any
+ * combination of:
+ *
+ * premultiply the color channels
+ * do not encode non-opaque pixels (leave as linear)
+ * encode the alpha as well as the color channels
+ *
+ * The differences disappear if the input/output ('screen') gamma is
+ * 1.0, because then the encoding is a no-op and there is only the
+ * choice of premultiplying the color channels or not.
+ */
+ switch (mode)
{
- if (i < png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff)
+ case PNG_ALPHA_PNG: /* default: png standard */
+ /* No compose, but it may be set by png_set_background! This
+ * is the only mode that doesn't interfere with what
+ * png_set_background does.
+ */
+ tr->st.associate_alpha = 0U;
+ tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
+ tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
+ break;
+
+ case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
+ tr->st.associate_alpha = 1U;
+ tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
+ tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
+ break;
+
+ case PNG_ALPHA_OPTIMIZED:
+ /* associated with opaque pixels having the given gamma and
+ * non-opaque pixels being linear.
+ */
+ tr->st.associate_alpha = 1U;
+ tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
+ tr_gamma->optimize_alpha = tr->st.optimize_alpha = 1U;
+ /* output_gamma records the encoding of opaque pixels! */
+ break;
+
+ case PNG_ALPHA_BROKEN:
+ /* associated+non-linear+alpha encoded */
+ tr->st.associate_alpha = 1U;
+ tr_gamma->encode_alpha = tr->st.encode_alpha = 1U;
+ tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
+ break;
+
+ default:
+ png_app_error(png_ptr, "invalid alpha mode");
+ /* A return at this point is safe; if a background transform
+ * was created the init routine will remove it because
+ * nothing is set.
+ */
+ break;
+ } /* alpha mode switch */
+ } /* add gamma and background transforms */
+ } /* valid output gamma */
+ } /* png_ptr != NULL */
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
+{
+ png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
+ output_gamma));
+}
+#endif /* FLOATING_POINT */
+#endif /* READ_ALPHA_MODE */
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+typedef struct
+{
+ png_transform tr;
+ png_transform_control tc;
+ union
+ {
+ png_uint_32 u32[1]; /* ensure alignment */
+ png_uint_16 u16[1];
+ png_byte b8[1];
+ } cache;
+} png_transform_cache;
+
+#define png_transform_cache_size(size)\
+ (offsetof(png_transform_cache, cache)+(size))
+#define png_transform_cache_cast(pointer,size)\
+ png_voidcast(png_transform_cache*,\
+ png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer),\
+ png_transform_cache_size(size)))
+ /* This is like png_transform_cast except that 'size' is the size of the
+ * cache part in the above structure and the type returned is always
+ * 'png_transform_cache*'.
+ */
+
+/* Functions to handle the cache operation. These don't do any initialization;
+ * that happens below when PNG_TC_INIT_FINAL is being run on the whole list.
+ * These functions are only implemented for read so the transform control
+ * source and destination are always aligned.
+ *
+ * First some utility functions:
+ */
+static void
+png_transform_control_cp(png_transform_controlp tcDest,
+ png_const_transform_controlp tcSrc)
+{
+ /* Copy tcSrc over tcDest without overwriting the information specific to the
+ * row being transformed.
+ */
+ png_structp png_ptr = tcDest->png_ptr;
+ png_const_voidp sp = tcDest->sp;
+ png_voidp dp = tcDest->dp;
+ png_uint_32 width = tcDest->width;
+ unsigned int init = tcDest->init;
+
+ *tcDest = *tcSrc;
+
+ tcDest->png_ptr = png_ptr;
+ tcDest->sp = sp;
+ tcDest->dp = dp;
+ tcDest->width = width;
+ tcDest->init = png_check_bits(tcDest->png_ptr, init, 2);
+}
+
+#if !PNG_RELEASE_BUILD
+static int
+png_transform_control_eq(png_const_transform_controlp tc1,
+ png_const_transform_controlp tc2)
+{
+ /* Say if *tc1 == *tc2, ignoring differences in uncopied fields and 'cost':
+ */
+ return
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ tc1->gamma == tc2->gamma &&
+# endif
+ tc1->format == tc2->format &&
+ tc1->range == tc2->range &&
+ tc1->bit_depth == tc2->bit_depth &&
+ tc1->caching == tc2->caching &&
+ tc1->palette == tc2->palette;
+ /* invalid_info, cost, interchannel and channel_add are only set during
+ * init, so don't do the compare.
+ */
+}
+#endif /* !RELEASE_BUILD */
+
+/* Now the routines that actually perform the transform. There are two basic
+ * cases:
+ *
+ * 1) A cached transform that does not change the pixel size and where the pixel
+ * size 8 bits or less. This can be done by a 256-entry single byte lookup
+ * table, regardless of the bit depth. Two versions of the code exist, one
+ * which just transforms the row, the other which transforms and records the
+ * maximum pixel depth.
+ *
+ * 2) A cached transform that increases pixel depth. The destination pixel
+ * depth will always be a multiple of 8 bits, the source pixel will be less
+ * than or equal to 8 bits and will be in the PNG native (big endian) layout.
+ */
+#define png_ptr (tc->png_ptr) /* Used in all functions below */
+/* (1): single-byte cached transforms: */
+static void
+do_transform_cache_byte(png_transformp *trIn, png_transform_controlp tc)
+{
+ png_transform_cache *tr = png_transform_cache_cast(*trIn, 256U);
+
+ /* Copy the bytes through the 256-byte LUT: */
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp + PNG_TC_ROWBYTES(*tc);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+
+ tc->sp = dp;
+
+ do
+ *dp++ = tr->cache.b8[*sp++];
+ while (dp < ep);
+
+ png_transform_control_cp(tc, &tr->tc);
+}
+
+/* (2) A cached transform that increases pixel depth.
+ *
+ * There are six output depth possibilites, all a whole number of bytes:
+ *
+ * 1 byte, 8 bits: palette or grayscale
+ * 2 bytes, 16 bits: 16-bit grayscale or 8-bit gray+alpa
+ * 3 bytes, 24 bits: 8-bit RGB
+ * 4 bytes, 32 bits: 16-bit gray+alpha or 8-bit RGBA
+ * 6 bytes, 48 bits: 16-bit RGB
+ * 8 bytes, 64 bits: 16-bit RGBA
+ *
+ * The input must be 1, 2, 4 or 8-bit gray or palette. The first 1-byte case is
+ * handled for 8-bit gray/palette above, so there are 22 possibilities. The
+ * function names below are:
+ *
+ * do_transform_cache__
+ */
+#define transform_cache_size(ipd,opd) ((((1U << (ipd)) * (opd))+7U) >> 3)
+static void
+do_transform_cache_(png_transformp *trIn, png_transform_controlp tc,
+ unsigned int ipd, unsigned int opd)
+ /* This is the implementation for unknown ipd, opd, below it is called with
+ * fixed values. The purpose of this is to allow the compiler/system builder
+ * to decide how to optimize for size vs space vs speed. Note that this
+ * implementation, while it would work for 8 bit ipd, is not used in that
+ * case.
+ */
+{
+ png_transform_cache *tr =
+ png_transform_cache_cast(*trIn, transform_cache_size(ipd, opd));
+
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp;
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ unsigned int s, shift, mask;
+
+ sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */
+
+ png_transform_control_cp(tc, &tr->tc);
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ shift = 7U & -(tc->width * ipd);
+ /* MSB: shift right required to get last pixel */
+ mask = (1U << ipd) - 1U;
+ /* Mask to extract a single pixel from the low bits of a byte */
+ opd >>= 3;
+ /* Output pixel size in bytes */
+ s = *--sp;
+ /* The first byte; the last byte of the input row */
+
+ for (;;)
+ {
+ png_const_bytep opixel = (((s >> shift) & mask)+1U) * opd + tr->cache.b8;
+ /* Points to the byte after last byte of the output value */
+ unsigned int i;
+
+ for (i=0; isp);
+ tc->sp = ep; /* start of row, safe even if the above fails */
+}
+
+#define do_transform_cache(ipd,opd)\
+static void \
+do_transform_cache_##ipd##_##opd(png_transformp *tr, png_transform_controlp tc)\
+{\
+ do_transform_cache_(tr, tc, ipd, opd);\
+}
+
+#define TCLOW(opd)\
+do_transform_cache(1,opd)\
+do_transform_cache(2,opd)\
+do_transform_cache(4,opd)
+
+TCLOW(8)
+TCLOW(16)
+TCLOW(24)
+TCLOW(32)
+TCLOW(48)
+TCLOW(64)
+
+#undef TCLOW
+#undef do_transform_cache
+
+static void
+do_transform_cache_8_(png_transformp *trIn, png_transform_controlp tc,
+ unsigned int opd)
+ /* This is the 8-bit input implementation. */
+{
+ png_transform_cache *tr =
+ png_transform_cache_cast(*trIn, transform_cache_size(8, opd));
+
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep ep = dp;
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+
+ sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */
+
+ png_transform_control_cp(tc, &tr->tc);
+ dp += PNG_TC_ROWBYTES(*tc);
+
+ opd >>= 3; /* Output pixel size in bytes */
+ do
+ {
+ png_const_bytep opixel = (*--sp + 1U) * opd + tr->cache.b8;
+ /* Points to the byte after last byte of the output value */
+ unsigned int i;
+
+ for (i=0; i ep);
+
+ debug(dp == ep && sp == tc->sp);
+ tc->sp = ep; /* start of row, safe even if the above fails */
+}
+
+#define do_transform_cache(opd)\
+static void \
+do_transform_cache_8_##opd(png_transformp *tr, png_transform_controlp tc)\
+{\
+ do_transform_cache_8_(tr, tc, opd);\
+}
+
+/* The 8-bit to 8-bit case uses the byte transform code */
+do_transform_cache(16)
+do_transform_cache(24)
+do_transform_cache(32)
+do_transform_cache(48)
+do_transform_cache(64)
+
+#undef do_transform_cache
+
+#define do_transform_cache(ipd,opd) do_transform_cache_##ipd##_##opd
+
+#undef png_ptr
+
+typedef struct
+{
+ png_transformp *start;
+ /* This is a pointer to the pointer to the start of the list being cached,
+ * i.e. *start is the first transform in the list.
+ */
+ png_transform_control tstart;
+ /* This is the transform control at the start; i.e. before (*start)->fn is
+ * called. Note that for palette data it will contain the original
+ * palette format/bit-depth, not that passed to (*start)->fn which will
+ * represent the palette.
+ */
+ png_transformp *end;
+ png_transform_control tend;
+ /* The same data from the end of the run to be cached, i.e. after the
+ * function of the transform which *contains* '*end' (end points to
+ * tr->next).
+ */
+} png_cache_params, *png_cache_paramsp;
+
+static void
+init_caching(png_structp png_ptr, png_transform_controlp tend)
+ /* Given an already initialized tend turn on caching if appropriate. */
+{
+ /* Handle the colormap case, where a cache is always required: */
+ if (tend->format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* This turns starts the palette caching with the next transform: */
+ tend->palette = tend->caching = 1U;
+# ifdef PNG_READ_tRNS_SUPPORTED
+ tend->transparent_alpha = png_ptr->transparent_palette;
+# else /* !READ_tRNS */
+ tend->transparent_alpha = 0;
+ PNG_UNUSED(png_ptr)
+# endif /* !READ_tRNS */
+ tend->format = PNG_FORMAT_FLAG_COLOR;
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (png_ptr->num_trans > 0 && !(tend->invalid_info & PNG_INFO_tRNS))
+ {
+ tend->format |= PNG_FORMAT_FLAG_ALPHA;
+ }
+# endif /* READ_tRNS */
+ tend->bit_depth = 8U;
+ }
+
+ else if (PNG_TC_PIXEL_DEPTH(*tend) <= 8)
+ {
+ /* Cacheable pixel transforms; the pixel is less than 8 bits in size so
+ * the cache makes sense.
+ *
+ * TODO: check the cost estimate and the image size to avoid expensive
+ * caches of very small images.
+ */
+ tend->caching = 1U;
+ }
+
+ /* TODO: handle handle 8-bit GA/RGB/RGBA */
+}
+
+static void
+add_cache_transform(png_structp png_ptr, unsigned int order,
+ png_transform_fn fn, png_cache_paramsp cp,
+ png_const_bytep cache, unsigned int size)
+ /* Add a transform from the input format cp->tstart to the output format
+ * stored in cp->tend.
+ */
+{
+ affirm(size <= 2048U); /* 256 8-byte pixels at most */
+ {
+ png_transform_cache *tr = png_transform_cache_cast(
+ png_add_transform(png_ptr, png_transform_cache_size(size), fn, order),
+ size);
+
+ /* This must have replaced the transform in *cp->start: */
+ affirm(&tr->tr == *cp->start);
+
+ /* Fill in the respective members: */
+ tr->tc = cp->tend;
+ memcpy(tr->cache.b8, cache, size);
+
+ /* Skip this transform, because the calling routine has already executed
+ * the cache (it could be executed again, just to verify that it works;
+ * cp->tstart should be correct.)
+ */
+ cp->start = &tr->tr.next;
+ }
+}
+
+static unsigned int
+setup_palette_cache(png_structp png_ptr, png_byte cache[8*256])
+ /* This returns the number of entries in the cache; the width */
+{
+ const unsigned int num_palette = png_ptr->num_palette;
+# ifdef PNG_READ_tRNS_SUPPORTED
+ unsigned int num_trans = png_ptr->num_trans;
+# endif /* READ_tRNS */
+ const png_colorp palette = png_ptr->palette;
+ png_bytep p;
+ unsigned int i;
+# ifdef PNG_READ_tRNS_SUPPORTED
+ const png_bytep trans_alpha = png_ptr->trans_alpha;
+# endif /* READ_tRNS */
+
+ for (i=0, p=cache; i 0)
+ {
+ if (i < num_trans)
+ *p++ = trans_alpha[i];
+
+ else
+ *p++ = 0xFFU;
+ }
+# endif /* READ_tRNS */
+ }
+
+ return num_palette;
+}
+
+static void
+png_remove_PLTE_and_tRNS(png_structrp png_ptr)
+{
+ if (png_ptr->palette != NULL)
+ png_free(png_ptr, png_ptr->palette);
+
+ png_ptr->palette = NULL;
+ png_ptr->num_palette = 0;
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (png_ptr->trans_alpha != NULL)
+ png_free(png_ptr, png_ptr->trans_alpha);
+
+ png_ptr->trans_alpha = NULL;
+ png_ptr->num_trans = 0;
+# endif /* READ_tRNS */
+
+ png_ptr->palette_updated = 1U;
+}
+
+static void
+update_palette(png_structp png_ptr, png_cache_paramsp cp,
+ unsigned int max_depth)
+{
+ union
+ {
+ png_uint_32 u32[1];
+ png_uint_16 u16[1]; /* For alignment */
+ png_byte b8[8*256]; /* For 16-bit RGBA intermediate */
+ } cache;
+
+ /* The caller only calls this function if the initial transform control had
+ * the palette flag set, implying that the original 'format' was a COLORMAP
+ * one. Also this can only happen (at present) when starting the transform
+ * list, so:
+ */
+ affirm((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0); /* required */
+
+ /* Run the whole of the given list on the palette data. PNG_TC_INIT_FINAL
+ * has already been run; this is a full run (with init == 0).
+ */
+ {
+ unsigned int check_depth;
+ only_deb(png_transform_control orig = cp->tend;)
+
+ cp->tend = cp->tstart;
+ init_caching(png_ptr, &cp->tend);
+ /* And set up tend to actually work out the palette: */
+ cp->tend.init = 0U;
+ cp->tend.width = setup_palette_cache(png_ptr, cache.b8);
+ cp->tend.sp = cache.b8;
+ cp->tend.dp = cache.b8;
+
+ check_depth =
+ png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end);
+
+ /* If we get here these two things must be true or there are been some
+ * buggy difference of opinion between the INIT code and the actual run:
+ */
+ affirm(check_depth == max_depth && cp->tend.palette);
+
+ /* This should match the passed in final format obtained before, this
+ * debug statement detects discrepancies between the init code and the
+ * run code:
+ */
+ debug(png_transform_control_eq(&cp->tend, &orig));
+
+ /* Also, expect the palette to still be valid: */
+ debug((cp->tend.invalid_info & PNG_INFO_PLTE) == 0);
+ }
+
+ /* The result must be compatible with a PNG palette with respect to bit
+ * depth; specifically the expand-16 transform has no effect on palette data.
+ *
+ * The colormap setting must not have been re-introduced here either; there
+ * may be some quantize interactions here, neither can unexpected flags be
+ * handled; just COLOR and ALPHA.
+ */
+ affirm(cp->tend.bit_depth == 8 &&
+ (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) == 0);
+
+ /* Remove all the transforms between start(inclusive) and end(exclusive);
+ * they have been processed. The effect they had on the transform control
+ * is irrelevant because the caller re-instates the settings from tstart.
+ */
+ {
+ png_transformp list = *cp->start; /* list to free */
+
+ *cp->start = *cp->end; /* part of list not to be freed */
+ *cp->end = NULL; /* terminate the list to be freed */
+ cp->end = cp->start; /* else cp->end points to the end of the list! */
+
+ png_transform_free(png_ptr, &list);
+ }
+
+ /* Adjust the PNG palette and, if required, the tRNS entries. Note that
+ * if the transforms stripped the alpha channel from the palette num_trans
+ * will get set to 0 here.
+ *
+ * This is the point where the gamma gets frozen too. The alternative
+ * design is to pass palette, tRNS and gamma up the transform chain, but
+ * that doesn't work because the palette change would, apparently, have to
+ * be repeated on each row. This seems simpler at the cost of a little
+ * obscurity; the answer to the question, "Where does the palette get
+ * updated?", is "Here!"
+ *
+ * API CHANGE: (fix): previously the init code would silently overwrite
+ * the palette information shared with png_info, breaking the API for
+ * png_read_update_info, which doesn't update the info if it isn't called,
+ * by changing the palette and maybe tRNS when the first row was read!
+ *
+ * NOTE: PNG_FORMAT_FLAG_RANGE is lost at this point, even if the palette
+ * entries were shifted or inverted. This could be fixed, but it would
+ * complicate the libpng API to expose the information.
+ */
+ /* Write the transformed palette: */
+ {
+ png_colorp palette = png_voidcast(png_colorp, png_calloc(png_ptr,
+ sizeof (png_color[PNG_MAX_PALETTE_LENGTH])));
+ png_const_bytep p;
+ const int is_color = (cp->tend.format & PNG_FORMAT_FLAG_COLOR) != 0;
+ unsigned int i;
+# ifdef PNG_READ_tRNS_SUPPORTED
+ unsigned int num_trans = 0;
+ const int do_trans = (cp->tend.format & PNG_FORMAT_FLAG_ALPHA) != 0;
+ png_byte trans_alpha[PNG_MAX_PALETTE_LENGTH];
+# endif /* READ_tRNS */
+
+ memset(palette, 0xFFU, sizeof (png_color[PNG_MAX_PALETTE_LENGTH]));
+ png_free(png_ptr, png_ptr->palette);
+ png_ptr->palette = palette;
+
+ for (i=0, p=cache.b8; itend.width; ++i)
+ {
+ if (is_color)
+ {
+ palette[i].red = *p++;
+ palette[i].green = *p++;
+ palette[i].blue = *p++;
+ }
+
+ else
+ palette[i].blue = palette[i].green = palette[i].red = *p++;
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (do_trans)
+ {
+ png_byte a = *p++;
+ trans_alpha[i] = a;
+
+ /* Strip opaque entries from the end: */
+ if (a < 0xFFU)
+ num_trans = i+1;
+ }
+# endif /* READ_tRNS */
+ }
+
+ png_ptr->num_palette = png_check_bits(png_ptr, cp->tend.width, 9);
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ if (num_trans > 0)
+ {
+ png_bytep tRNS = png_voidcast(png_bytep, png_malloc(png_ptr,
+ PNG_MAX_PALETTE_LENGTH));
+
+ memset(tRNS, 0xFFU, PNG_MAX_PALETTE_LENGTH);
+
+ if (png_ptr->trans_alpha != NULL)
+ png_free(png_ptr, png_ptr->trans_alpha);
+
+ png_ptr->trans_alpha = tRNS;
+
+ memcpy(tRNS, trans_alpha, num_trans);
+ png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9);
+ }
+# endif /* READ_tRNS */
+ }
+
+ /* NOTE: the caller sets cp->start to cp->end and cp->tend to cp->tstart,
+ * this causes processing to continue with the palette format and the
+ * first unprocessed transform. The reset of the transform control loses the
+ * gamma information as well, of course, as any information about the palette
+ * and tRNS changes (such as the RANGE flags).
+ *
+ * The following ensures that png_read_update_info knows to update the
+ * palette in png_info (which is no longer shared).
+ */
+ png_ptr->palette_updated = 1U;
+}
+
+/* These structure and the save/restore routines that follow it exist to save
+ * data from a png_transform_control that is specific to the sample encoding of
+ * the PNG data, rather than the row format itself.
+ */
+typedef struct
+{
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ png_fixed_point gamma;
+# endif
+ png_byte sBIT_R;
+ png_byte sBIT_G;
+ png_byte sBIT_B;
+ png_byte sBIT_A; /* Signnificant bits in the row channels. */
+ unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */
+} png_tc_channel_data;
+
+static void
+save_cp_channel_data(png_tc_channel_data *save, png_const_transform_controlp tc)
+{
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ save->gamma = tc->gamma;
+# endif /* READ_GAMMA */
+
+ /* The sBIT information and the list of invalidated chunks must also be
+ * preserved:
+ */
+ save->sBIT_R = tc->sBIT_R;
+ save->sBIT_G = tc->sBIT_G;
+ save->sBIT_B = tc->sBIT_B;
+ save->sBIT_A = tc->sBIT_A;
+ save->invalid_info = tc->invalid_info;
+}
+
+static void
+restore_cp_channel_data(png_transform_controlp tc,
+ const png_tc_channel_data *save)
+ /* Reverse the above */
+{
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ tc->gamma = save->gamma;
+# endif /* READ_GAMMA */
+
+ tc->sBIT_R = save->sBIT_R;
+ tc->sBIT_G = save->sBIT_G;
+ tc->sBIT_B = save->sBIT_B;
+ tc->sBIT_A = save->sBIT_A;
+ tc->invalid_info = save->invalid_info;
+}
+
+static void
+make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
+{
+ /* At present the cache is just a byte lookup table. We need the original
+ * pixel depth to work out how big the working buffer needs to be.
+ */
+ const unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart);
+ const unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend);
+ unsigned int order; /* records position of start transform */
+ unsigned int width; /* width of cache in pixels */
+ png_tc_channel_data save; /* Record of the final channel info */
+ union
+ {
+ png_uint_32 u32[1];
+ png_uint_16 u16[1]; /* For alignment */
+ png_byte b8[8*256]; /* For 16-bit RGBA */
+ } cache;
+
+ debug(cp->tend.init == PNG_TC_INIT_FINAL);
+ affirm(opd <= 64 && max_depth <= 64); /* or the cache is not big enough */
+ affirm(ipd == opd || (opd & 0x7U) == 0);
+
+ if ((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ width = setup_palette_cache(png_ptr, cache.b8);
+
+ else switch (ipd)
+ {
+ /* The input to the cache is the full range of possible pixel values: */
+ case 1:
+ /* 2 1-bit pixels, MSB first */
+ cache.b8[0] = 0x40U;
+ width = 2;
+ break;
+
+ case 2:
+ /* 4 2-bit pixels, MSB first */
+ cache.b8[0] = 0x1BU;
+ width = 4;
+ break;
+
+ case 4:
+ /* 16 4-bit pixels, MSB first */
+ cache.b8[0] = 0x01U;
+ cache.b8[1] = 0x23U;
+ cache.b8[2] = 0x45U;
+ cache.b8[3] = 0x67U;
+ cache.b8[4] = 0x89U;
+ cache.b8[5] = 0xABU;
+ cache.b8[6] = 0xCDU;
+ cache.b8[7] = 0xEFU;
+ width = 16;
+ break;
+
+ case 8:
+ /* 256 8-bit pixels */
+ {
+ unsigned int i;
+
+ for (i=0; i<256; ++i)
+ cache.b8[i] = PNG_BYTE(i);
+ }
+ width = 256;
+ break;
+
+ default:
+ impossible("cache input bit depth");
+ }
+
+ /* Reset the transform control to run the transforms on this data, but save
+ * the channel info because the row processing functions do not always
+ * write it.
+ */
+ save_cp_channel_data(&save, &cp->tend);
+ cp->tend = cp->tstart;
+ init_caching(png_ptr, &cp->tend);
+ /* And set tend to work out the result of transforming each possible pixel
+ * value:
+ */
+ cp->tend.init = 0U;
+ cp->tend.width = width;
+ cp->tend.sp = cache.b8;
+ cp->tend.dp = cache.b8;
+
+ {
+ unsigned int check_depth =
+ png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end);
+
+ /* This must not change: */
+ affirm(PNG_TC_PIXEL_DEPTH(cp->tend) == opd && check_depth == max_depth);
+ }
+
+ /* Restore the potentially lost channel data. */
+ restore_cp_channel_data(&cp->tend, &save);
+
+ /* This is all the information required to cache the set of transforms
+ * between 'start' and 'end'. We take the transformed pixels and make a
+ * cache transform of them. The cache transform skips the work, transforms
+ * the row, and sets the tranform_control to (a copy of) cp->tend.
+ *
+ * Remove all the transforms between start(inclusive) and end(exclusive);
+ * they have been processed. The effect they had on the transform control
+ * is irrelevant because the caller re-instates the settings from tstart.
+ */
+ {
+ png_transformp list = *cp->start; /* list to free */
+
+ *cp->start = *cp->end; /* part of list not to be freed */
+ *cp->end = NULL; /* terminate the list to be freed */
+ cp->end = NULL; /* reset below */
+
+ order = list->order; /* used below when adding the cache transform */
+ png_transform_free(png_ptr, &list);
+ }
+
+ /* Make the required cache, as enumerated above there are 22 possibilities,
+ * this selects between them, fixes up the cache for the 'byte' cases (where
+ * multiple pixels can be handled byte-by-byte) and selects the correct
+ * transform function.
+ */
+ if (ipd == opd)
+ {
+ /* We already know that ipd is <= 8 bits, so we can expand this case to
+ * the byte transform. The complexity is that for ipd < 8 bits we only
+ * have information for individual pixel values and these may be
+ * pixel-swapped within the byte.
+ */
+ if (ipd < 8)
+ {
+ const int lsb = (cp->tend.format & PNG_FORMAT_FLAG_SWAPPED) != 0;
+ unsigned int ishift, b;
+ png_byte bcache[256];
+
+ switch (ipd)
+ {
+ case 1: ishift = 3U; break;
+ case 2: ishift = 2U; break;
+ case 4: ishift = 1U; break;
+ default: impossible("ipd");
+ }
+
+ /* Work out the right answer for each byte of pixels: */
+ for (b=0U; b<256U; ++b)
+ {
+ unsigned int o = 0U; /* output byte */
+ unsigned int p = 8U; /* right shift to find input pixel */
+
+ do
+ {
+ unsigned int q = ((1U<> (p-=ipd));
+ /* The input pixel. For a palette this value might be outside
+ * the range of palette indices, in which case simply insert
+ * '0':
+ */
+ if (q < width)
{
- if (png_ptr->trans_alpha[i] == 0)
- {
- palette[i] = back;
- }
- else /* if (png_ptr->trans_alpha[i] != 0xff) */
- {
- png_uint_16 v, w;
- unsigned int alpha = png_ptr->trans_alpha[i] * 257U;
- unsigned int shift = png_ptr->gamma_shift;
- unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0);
-
- v = png_ptr->gamma_to_1[palette[i].red];
- png_composite_16(w, v, alpha, back_1.red);
- palette[i].red = png_ptr->gamma_from_1[(w+add)>>shift];
-
- v = png_ptr->gamma_to_1[palette[i].green];
- png_composite_16(w, v, alpha, back_1.green);
- palette[i].green = png_ptr->gamma_from_1[(w+add)>>shift];
-
- v = png_ptr->gamma_to_1[palette[i].blue];
- png_composite_16(w, v, alpha, back_1.blue);
- palette[i].blue = png_ptr->gamma_from_1[(w+add)>>shift];
- }
+ unsigned int r = cache.b8[q >> ishift];
+ r >>= ((lsb ? q : ~q) & ((1U<gamma_table[palette[i].red];
- palette[i].green = png_ptr->gamma_table[palette[i].green];
- palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+ UNTESTED
}
}
+ while (p != 0U);
- /* Prevent the transformations being done again.
- *
- * NOTE: this is highly dubious; it removes the transformations in
- * place. This seems inconsistent with the general treatment of the
- * transformations elsewhere.
- */
- png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
- } /* color_type == PNG_COLOR_TYPE_PALETTE */
- }/* png_ptr->transformations & PNG_BACKGROUND */
+ bcache[b] = png_check_byte(png_ptr, o);
+ }
+
+ /* This is a byte transform, with the optional check-for-invalid-index
+ * functionality.
+ */
+ add_cache_transform(png_ptr, order, do_transform_cache_byte, cp,
+ bcache, 256U);
+ }
+
+ else /* ipd == 8 */
+ add_cache_transform(png_ptr, order, do_transform_cache_byte, cp,
+ cache.b8, 256U);
+ }
+
+ else
+ {
+ /* opd is a whole number of bytes, ipd is 1, 2, 4 or 8 and not equal to
+ * opd.
+ */
+ png_transform_fn fn;
+
+# define C(ipd,opd) ((ipd) + 8*(opd))
+ switch (C(ipd,opd))
+ {
+# define CASE(ipd,opd)\
+ case C(ipd,opd): fn = do_transform_cache(ipd,opd); break
+
+ CASE(1,8);
+ CASE(2,8);
+ CASE(4,8);
+ /* No 8,8 */
+
+# define CASES(opd)\
+ CASE(1,opd);\
+ CASE(2,opd);\
+ CASE(4,opd);\
+ CASE(8,opd)
+
+ CASES(16);
+ CASES(24);
+ CASES(32);
+ CASES(48);
+ CASES(64);
+# undef CASES
+# undef CASE
+
+ default:
+ impossible("cache bit depths");
+ }
+# undef C
+
+ /* In the event that the cache is not the full width implied by ipd zero
+ * the remaining bytes for security; otherwise they get copied into the
+ * cache transform and might get used. (Specifically if there is an
+ * out-of-range palette index they do get used!)
+ */
+ {
+ unsigned int size = transform_cache_size(ipd, opd);
+ png_alloc_size_t cachebytes = PNG_TC_ROWBYTES(cp->tend);
+
+ affirm(cachebytes <= sizeof cache.b8);
+
+ if (cachebytes < size)
+ memset(cache.b8+cachebytes, 0, size - cachebytes);
+
+ add_cache_transform(png_ptr, order, fn, cp, cache.b8, size);
+ }
+ }
+
+ /* Because a transform was inserted cp->end needs to be set to the new
+ * pointer to the original end. add_cache_transform sets cp->start to this,
+ * so:
+ */
+ cp->end = cp->start;
+
+ /* This invalidates the palette if that is what was cached because the
+ * palette and, if present, tRNS chunk did not get updated above.
+ */
+ if (cp->tstart.palette)
+ png_remove_PLTE_and_tRNS(png_ptr);
+}
+
+static void restore_cp(png_cache_paramsp cp)
+{
+ /* A utility to restore cp->tstart by copying it into cp->tend. This is used
+ * both in the palette case when restoring the transform control for the
+ * indexed data and in the case where no transforms were cached. It
+ * preserves the color-channel-specific data from cp->tend because in either
+ * case it is possible for this data to be modified without preserving any
+ * transforms, e.g. if only the gamma is changed but no gamma transform is
+ * retained because the change was not significant.
+ */
+ png_tc_channel_data save;
+
+ save_cp_channel_data(&save, &cp->tend);
+ cp->tend = cp->tstart;
+ restore_cp_channel_data(&cp->tend, &save);
+}
+
+static void
+handle_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
+{
+ /* There is nothing to do if there are no transforms between 'start' and
+ * 'end':
+ */
+ if (cp->start != cp->end)
+ {
+ only_deb(png_transformp tr_check = *cp->end;)
+
+ /* libpng doesn't currently implement any pixel size of more than 64 bits
+ * so:
+ */
+ affirm(max_depth <= 64);
+
+ if (cp->tend.palette)
+ {
+ /* The transforms being cached apply to the palette, the following
+ * transforms will apply to the original index data and the transformed
+ * data must be used to update the palette:
+ */
+ if (cp->tend.init == PNG_TC_INIT_FINAL)
+ update_palette(png_ptr, cp, max_depth);
+
+ cp->start = cp->end;
+ restore_cp(cp); /* reset to palette data */
+ }
else
- /* Transformation does not include PNG_BACKGROUND */
-#endif /* PNG_READ_BACKGROUND_SUPPORTED */
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- /* RGB_TO_GRAY needs to have non-gamma-corrected values! */
- && ((png_ptr->transformations & PNG_EXPAND) == 0 ||
- (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
-#endif
- )
{
- png_colorp palette = png_ptr->palette;
- int num_palette = png_ptr->num_palette;
- int i;
-
- /* NOTE: there are other transformations that should probably be in
- * here too.
+ /* Continue with the transform control in cp.tend; even if there was
+ * palette data in cp.tstart it has been expanded.
*/
- for (i = 0; i < num_palette; i++)
- {
- palette[i].red = png_ptr->gamma_table[palette[i].red];
- palette[i].green = png_ptr->gamma_table[palette[i].green];
- palette[i].blue = png_ptr->gamma_table[palette[i].blue];
- }
+ if (cp->tend.init == PNG_TC_INIT_FINAL)
+ make_cache(png_ptr, cp, max_depth);
- /* Done the gamma correction. */
- png_ptr->transformations &= ~PNG_GAMMA;
- } /* color_type == PALETTE && !PNG_BACKGROUND transformation */
+ cp->tstart = cp->tend; /* keep current context */
+ }
+
+ debug(tr_check == *cp->end);
}
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- else
-#endif
-#endif /* PNG_READ_GAMMA_SUPPORTED */
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- /* No GAMMA transformation (see the hanging else 4 lines above) */
- if ((png_ptr->transformations & PNG_COMPOSE) &&
- (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
+ else /* no transforms cached */
+ restore_cp(cp); /* removes any palette caching info */
+}
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+static void
+check_tRNS_for_alpha(png_structrp png_ptr)
+{
+ unsigned int num_trans = png_ptr->num_trans;
+
+ debug(png_ptr->color_type == PNG_COLOR_TYPE_PALETTE);
+
+ while (num_trans > 0)
{
- int i;
- int istop = (int)png_ptr->num_trans;
- png_color back;
- png_colorp palette = png_ptr->palette;
-
- back.red = (png_byte)png_ptr->background.red;
- back.green = (png_byte)png_ptr->background.green;
- back.blue = (png_byte)png_ptr->background.blue;
-
- for (i = 0; i < istop; i++)
{
- if (png_ptr->trans_alpha[i] == 0)
+ const png_byte trans = png_ptr->trans_alpha[--num_trans];
+
+ if (trans == 0xFFU)
+ continue;
+
+ if (trans > 0U)
+ return; /* Palette has at least one entry >0, <0xff */
+ }
+
+ /* There is some point to the tRNS chunk; it has a non-opaque entry, this
+ * code could truncate it but there is no obvious performance advantage to
+ * doing this.
+ */
+ while (num_trans > 0)
+ {
+ const png_byte trans = png_ptr->trans_alpha[--num_trans];
+
+ if (trans > 0U && trans < 0xFFU)
+ return;
+ }
+
+ /* Here if the above did not find an entry >0 && <0xFFU but did find a
+ * transparent entry (0u). Record this.
+ */
+ png_ptr->transparent_palette = 1U;
+ return;
+ }
+
+ /* All entries opaque; remove the tRNS data: */
+ png_ptr->num_trans = 0U;
+}
+#endif /* READ_tRNS */
+
+unsigned int /* PRIVATE */
+png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc)
+ /* This is called once for each init stage (PNG_TC_INIT_FORMAT and
+ * PNG_TC_INIT_FINAL) to run the transform list forwards, returning the
+ * maximum depth required to process the row. It handles caching of the
+ * transforms and the processing of the palette for color-mapped PNG data.
+ */
+{
+ png_transformp *list = &png_ptr->transform_list;
+ unsigned int max_depth, cache_start_depth;
+ png_cache_params cp;
+
+ /* PNG color-mapped data must be handled here so that the palette is updated
+ * correctly. png_set_palette_to_rgb causes the palette flag to be removed
+ * from the transform control but does no other change. png_set_quantize
+ * causes 8-bit RGB, RGBA or palette data to be converted into palette
+ * indices, setting the palette flag.
+ */
+# ifdef PNG_READ_tRNS_SUPPORTED
+ /* This happens once at the start to find out if the tRNS chunk consisted
+ * entirely of opaque (255) and/or transparent (0) entries.
+ */
+ if (tc->init == PNG_TC_INIT_FORMAT &&
+ png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ check_tRNS_for_alpha(png_ptr);
+# endif /* READ_tRNS */
+ cp.end = cp.start = list;
+ cp.tend = cp.tstart = *tc;
+ max_depth = cache_start_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
+
+ while (*cp.end != NULL)
+ {
+ png_transformp tr = *cp.end;
+
+ /* The user transform cannot be cached. */
+ if (tr->order >= PNG_TR_USER)
+ break;
+
+ /* If caching is not on and this transform is after PNG_TR_START_CACHE
+ * try to turn it on.
+ */
+ if (tr->order > PNG_TR_START_CACHE && !cp.tend.caching)
+ {
+ cp.start = cp.end;
+ cp.tstart = cp.tend;
+ init_caching(png_ptr, &cp.tend);
+
+ if (cp.tend.caching)
{
- palette[i] = back;
- }
-
- else if (png_ptr->trans_alpha[i] != 0xff)
- {
- /* The png_composite() macro is defined in png.h */
- png_composite(palette[i].red, palette[i].red,
- png_ptr->trans_alpha[i], back.red);
-
- png_composite(palette[i].green, palette[i].green,
- png_ptr->trans_alpha[i], back.green);
-
- png_composite(palette[i].blue, palette[i].blue,
- png_ptr->trans_alpha[i], back.blue);
+ cache_start_depth = max_depth;
+ max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
}
}
- png_ptr->transformations &= ~PNG_COMPOSE;
- }
-#endif /* PNG_READ_BACKGROUND_SUPPORTED */
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
- if ((png_ptr->transformations & PNG_SHIFT) &&
- !(png_ptr->transformations & PNG_EXPAND) &&
- (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
- {
- int i;
- int istop = png_ptr->num_palette;
- int shift = 8 - png_ptr->sig_bit.red;
-
- png_ptr->transformations &= ~PNG_SHIFT;
-
- /* significant bits can be in the range 1 to 7 for a meaninful result, if
- * the number of significant bits is 0 then no shift is done (this is an
- * error condition which is silently ignored.)
+ /* If the 'palette' flag is set and the next transform has order
+ * PNG_TR_ENCODING or later cache the results so far and continue with the
+ * original palette data (cp.tstart).
*/
- if (shift > 0 && shift < 8)
- for (i=0; ipalette[i].red;
+ if (cp.tend.palette && tr->order >= PNG_TR_ENCODING)
+ {
+ handle_cache(png_ptr, &cp, max_depth);
- component >>= shift;
- png_ptr->palette[i].red = (png_byte)component;
- }
+ /* The cache handling function must maintain cp.end; */
+ affirm(tr == *cp.end);
+ max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
+ if (max_depth < cache_start_depth)
+ max_depth = cache_start_depth;
+ }
- shift = 8 - png_ptr->sig_bit.green;
- if (shift > 0 && shift < 8)
- for (i=0; ipalette[i].green;
+ /* Now run the transform list entry: */
+ if (tr->fn != NULL)
+ {
+ tr->fn(cp.end, &cp.tend);
+ tr = *cp.end; /* in case something was inserted */
+ }
- component >>= shift;
- png_ptr->palette[i].green = (png_byte)component;
- }
+ if (tr->fn == NULL) /* delete this transform */
+ png_remove_transform(png_ptr, cp.end);
- shift = 8 - png_ptr->sig_bit.blue;
- if (shift > 0 && shift < 8)
- for (i=0; ipalette[i].blue;
+ else
+ {
+ /* Handle the initialization of the maximum pixel depth. */
+ unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
- component >>= shift;
- png_ptr->palette[i].blue = (png_byte)component;
- }
+ if (tc_depth > max_depth)
+ max_depth = tc_depth;
+
+ /* Advance to the next transform. */
+ cp.end = &tr->next;
+ }
}
-#endif /* PNG_READ_SHIFT_SUPPORTED */
+
+ /* At the end if still caching record the cache information (this is common;
+ * this is generally the case for an expanded palette.)
+ */
+ if (cp.tend.caching)
+ {
+ png_transformp tr = *cp.end;
+ handle_cache(png_ptr, &cp, max_depth);
+ affirm(tr == *cp.end);
+ max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
+ if (max_depth < cache_start_depth)
+ max_depth = cache_start_depth;
+ }
+
+ /* At the end run the init on the user transform: */
+ if (*cp.end != NULL)
+ {
+ png_transformp tr = *cp.end;
+ affirm(tr->order == PNG_TR_USER);
+ if (tr->fn != NULL)
+ tr->fn(cp.end, &cp.tend);
+ /* This cannot insert anything, so: */
+ affirm(tr == *cp.end && tr->next == NULL);
+
+ if (tr->fn == NULL) /* delete this transform */
+ png_remove_transform(png_ptr, cp.end);
+
+ else
+ {
+ unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
+
+ if (tc_depth > max_depth)
+ max_depth = tc_depth;
+ }
+ }
+
+ /* And write the input transform control: */
+ *tc = cp.tend;
+
+ return max_depth;
}
/* Modify the info structure to reflect the transformations. The
@@ -1822,3064 +6749,73 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
{
png_debug(1, "in png_read_transform_info");
-#ifdef PNG_READ_EXPAND_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND)
- {
- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
- {
- /* This check must match what actually happens in
- * png_do_expand_palette; if it ever checks the tRNS chunk to see if
- * it is all opaque we must do the same (at present it does not.)
+ /* WARNING: this is very basic at present. It just updates the format
+ * information. It should update the palette (and will eventually) as well
+ * as invalidating chunks that the transforms break.
+ */
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ info_ptr->format = png_ptr->row_format;
+ info_ptr->bit_depth = png_ptr->row_bit_depth;
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ /* If an info struct is used with a different png_ptr in a call to
+ * png_set_gAMA then the png_struct information won't be updated, this
+ * doesn't matter on write, but don't zap the value in the info on read
+ * unless it is known:
+ *
+ * TODO: review this whole mess.
*/
- if (png_ptr->num_trans > 0)
- info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-
- else
- info_ptr->color_type = PNG_COLOR_TYPE_RGB;
-
- info_ptr->bit_depth = 8;
- info_ptr->num_trans = 0;
-
- if (png_ptr->palette == NULL)
- png_error (png_ptr, "Palette is NULL in indexed image");
- }
- else
- {
- if (png_ptr->num_trans)
- {
- if (png_ptr->transformations & PNG_EXPAND_tRNS)
- info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
- }
- if (info_ptr->bit_depth < 8)
- info_ptr->bit_depth = 8;
-
- info_ptr->num_trans = 0;
- }
- }
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- /* The following is almost certainly wrong unless the background value is in
- * the screen space!
- */
- if (png_ptr->transformations & PNG_COMPOSE)
- info_ptr->background = png_ptr->background;
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4),
- * however it seems that the code in png_init_read_transformations, which has
- * been called before this from png_read_update_info->png_read_start_row
- * sometimes does the gamma transform and cancels the flag.
- *
- * TODO: this looks wrong; the info_ptr should end up with a gamma equal to
- * the screen_gamma value. The following probably results in weirdness if
- * the info_ptr is used by the app after the rows have been read.
- */
- info_ptr->colorspace.gamma = png_ptr->colorspace.gamma;
-#endif
-
- if (info_ptr->bit_depth == 16)
- {
-# ifdef PNG_READ_16BIT_SUPPORTED
-# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
- if (png_ptr->transformations & PNG_SCALE_16_TO_8)
- info_ptr->bit_depth = 8;
+ if (png_ptr->row_gamma > 0)
+ info_ptr->colorspace.gamma = png_ptr->row_gamma;
# endif
-# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
- if (png_ptr->transformations & PNG_16_TO_8)
- info_ptr->bit_depth = 8;
-# endif
+ /* Invalidate chunks marked as invalid: */
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ info_ptr->valid &= ~png_ptr->invalid_info;
-# else
- /* No 16 bit support: force chopping 16-bit input down to 8, in this case
- * the app program can chose if both APIs are available by setting the
- * correct scaling to use.
- */
-# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
- /* For compatibility with previous versions use the strip method by
- * default. This code works because if PNG_SCALE_16_TO_8 is already
- * set the code below will do that in preference to the chop.
+ /* If the palette or tRNS chunk was changed copy them over to the info
+ * structure; this may actually re-validate the PLTE or tRNS chunks,
+ * but only if png_ptr has a new version, otherwise the invalid_info
+ * settings from above can still invalidate the chunk.
*/
- png_ptr->transformations |= PNG_16_TO_8;
- info_ptr->bit_depth = 8;
-# else
-
-# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
- png_ptr->transformations |= PNG_SCALE_16_TO_8;
- info_ptr->bit_depth = 8;
-# else
-
- CONFIGURATION ERROR: you must enable at least one 16 to 8 method
-# endif
-# endif
-#endif /* !READ_16BIT_SUPPORTED */
- }
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
- if (png_ptr->transformations & PNG_GRAY_TO_RGB)
- info_ptr->color_type = (png_byte)(info_ptr->color_type |
- PNG_COLOR_MASK_COLOR);
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- if (png_ptr->transformations & PNG_RGB_TO_GRAY)
- info_ptr->color_type = (png_byte)(info_ptr->color_type &
- ~PNG_COLOR_MASK_COLOR);
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
- if (png_ptr->transformations & PNG_QUANTIZE)
- {
- if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
- (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
- png_ptr->palette_lookup && info_ptr->bit_depth == 8)
- {
- info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
- }
- }
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&
- info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
- {
- info_ptr->bit_depth = 16;
- }
-#endif
-
-#ifdef PNG_READ_PACK_SUPPORTED
- if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
- info_ptr->bit_depth = 8;
-#endif
-
- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
- info_ptr->channels = 1;
-
- else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
- info_ptr->channels = 3;
-
- else
- info_ptr->channels = 1;
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_STRIP_ALPHA)
- {
- info_ptr->color_type = (png_byte)(info_ptr->color_type &
- ~PNG_COLOR_MASK_ALPHA);
- info_ptr->num_trans = 0;
- }
-#endif
-
- if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
- info_ptr->channels++;
-
-#ifdef PNG_READ_FILLER_SUPPORTED
- /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */
- if ((png_ptr->transformations & PNG_FILLER) &&
- ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
- (info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))
- {
- info_ptr->channels++;
- /* If adding a true alpha channel not just filler */
- if (png_ptr->transformations & PNG_ADD_ALPHA)
- info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
- }
-#endif
-
-#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
-defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
- if (png_ptr->transformations & PNG_USER_TRANSFORM)
- {
- if (info_ptr->bit_depth < png_ptr->user_transform_depth)
- info_ptr->bit_depth = png_ptr->user_transform_depth;
-
- if (info_ptr->channels < png_ptr->user_transform_channels)
- info_ptr->channels = png_ptr->user_transform_channels;
- }
-#endif
-
- info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
- info_ptr->bit_depth);
-
- info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);
-
- /* Adding in 1.5.4: cache the above value in png_struct so that we can later
- * check in png_rowbytes that the user buffer won't get overwritten. Note
- * that the field is not always set - if png_read_update_info isn't called
- * the application has to either not do any transforms or get the calculation
- * right itself.
- */
- png_ptr->info_rowbytes = info_ptr->rowbytes;
-
-#ifndef PNG_READ_EXPAND_SUPPORTED
- if (png_ptr != NULL)
- return;
-#endif
-}
-
-#ifdef PNG_READ_PACK_SUPPORTED
-/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
- * without changing the actual values. Thus, if you had a row with
- * a bit depth of 1, you would end up with bytes that only contained
- * the numbers 0 or 1. If you would rather they contain 0 and 255, use
- * png_do_shift() after this.
- */
-static void
-png_do_unpack(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_unpack");
-
- if (row_info->bit_depth < 8)
- {
- png_uint_32 i;
- png_uint_32 row_width=row_info->width;
-
- switch (row_info->bit_depth)
- {
- case 1:
+ if (png_ptr->palette_updated)
{
- png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
- png_bytep dp = row + (png_size_t)row_width - 1;
- png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
- for (i = 0; i < row_width; i++)
- {
- *dp = (png_byte)((*sp >> shift) & 0x01);
-
- if (shift == 7)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift++;
-
- dp--;
- }
- break;
- }
-
- case 2:
- {
-
- png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
- png_bytep dp = row + (png_size_t)row_width - 1;
- png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
- for (i = 0; i < row_width; i++)
- {
- *dp = (png_byte)((*sp >> shift) & 0x03);
-
- if (shift == 6)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift += 2;
-
- dp--;
- }
- break;
- }
-
- case 4:
- {
- png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
- png_bytep dp = row + (png_size_t)row_width - 1;
- png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
- for (i = 0; i < row_width; i++)
- {
- *dp = (png_byte)((*sp >> shift) & 0x0f);
-
- if (shift == 4)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift = 4;
-
- dp--;
- }
- break;
- }
-
- default:
- break;
- }
- row_info->bit_depth = 8;
- row_info->pixel_depth = (png_byte)(8 * row_info->channels);
- row_info->rowbytes = row_width * row_info->channels;
- }
-}
-#endif
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
-/* Reverse the effects of png_do_shift. This routine merely shifts the
- * pixels back to their significant bits values. Thus, if you have
- * a row of bit depth 8, but only 5 are significant, this will shift
- * the values back to 0 through 31.
- */
-static void
-png_do_unshift(png_row_infop row_info, png_bytep row,
- png_const_color_8p sig_bits)
-{
- int color_type;
-
- png_debug(1, "in png_do_unshift");
-
- /* The palette case has already been handled in the _init routine. */
- color_type = row_info->color_type;
-
- if (color_type != PNG_COLOR_TYPE_PALETTE)
- {
- int shift[4];
- int channels = 0;
- int bit_depth = row_info->bit_depth;
-
- if (color_type & PNG_COLOR_MASK_COLOR)
- {
- shift[channels++] = bit_depth - sig_bits->red;
- shift[channels++] = bit_depth - sig_bits->green;
- shift[channels++] = bit_depth - sig_bits->blue;
- }
-
- else
- {
- shift[channels++] = bit_depth - sig_bits->gray;
- }
-
- if (color_type & PNG_COLOR_MASK_ALPHA)
- {
- shift[channels++] = bit_depth - sig_bits->alpha;
- }
-
- {
- int c, have_shift;
-
- for (c = have_shift = 0; c < channels; ++c)
- {
- /* A shift of more than the bit depth is an error condition but it
- * gets ignored here.
- */
- if (shift[c] <= 0 || shift[c] >= bit_depth)
- shift[c] = 0;
+ if (png_ptr->num_palette > 0)
+ png_set_PLTE(png_ptr, info_ptr, png_ptr->palette,
+ png_ptr->num_palette);
else
- have_shift = 1;
- }
-
- if (have_shift == 0)
- return;
- }
-
- switch (bit_depth)
- {
- default:
- /* Must be 1bpp gray: should not be here! */
- /* NOTREACHED */
- break;
-
- case 2:
- /* Must be 2bpp gray */
- /* assert(channels == 1 && shift[0] == 1) */
- {
- png_bytep bp = row;
- png_bytep bp_end = bp + row_info->rowbytes;
-
- while (bp < bp_end)
{
- int b = (*bp >> 1) & 0x55;
- *bp++ = (png_byte)b;
- }
- break;
- }
-
- case 4:
- /* Must be 4bpp gray */
- /* assert(channels == 1) */
- {
- png_bytep bp = row;
- png_bytep bp_end = bp + row_info->rowbytes;
- int gray_shift = shift[0];
- int mask = 0xf >> gray_shift;
-
- mask |= mask << 4;
-
- while (bp < bp_end)
- {
- int b = (*bp >> gray_shift) & mask;
- *bp++ = (png_byte)b;
- }
- break;
- }
-
- case 8:
- /* Single byte components, G, GA, RGB, RGBA */
- {
- png_bytep bp = row;
- png_bytep bp_end = bp + row_info->rowbytes;
- int channel = 0;
-
- while (bp < bp_end)
- {
- int b = *bp >> shift[channel];
- if (++channel >= channels)
- channel = 0;
- *bp++ = (png_byte)b;
- }
- break;
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- case 16:
- /* Double byte components, G, GA, RGB, RGBA */
- {
- png_bytep bp = row;
- png_bytep bp_end = bp + row_info->rowbytes;
- int channel = 0;
-
- while (bp < bp_end)
- {
- int value = (bp[0] << 8) + bp[1];
-
- value >>= shift[channel];
- if (++channel >= channels)
- channel = 0;
- *bp++ = (png_byte)(value >> 8);
- *bp++ = (png_byte)(value & 0xff);
- }
- break;
- }
-#endif
- }
- }
-}
-#endif
-
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
-/* Scale rows of bit depth 16 down to 8 accurately */
-static void
-png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_scale_16_to_8");
-
- if (row_info->bit_depth == 16)
- {
- png_bytep sp = row; /* source */
- png_bytep dp = row; /* destination */
- png_bytep ep = sp + row_info->rowbytes; /* end+1 */
-
- while (sp < ep)
- {
- /* The input is an array of 16 bit components, these must be scaled to
- * 8 bits each. For a 16 bit value V the required value (from the PNG
- * specification) is:
- *
- * (V * 255) / 65535
- *
- * This reduces to round(V / 257), or floor((V + 128.5)/257)
- *
- * Represent V as the two byte value vhi.vlo. Make a guess that the
- * result is the top byte of V, vhi, then the correction to this value
- * is:
- *
- * error = floor(((V-vhi.vhi) + 128.5) / 257)
- * = floor(((vlo-vhi) + 128.5) / 257)
- *
- * This can be approximated using integer arithmetic (and a signed
- * shift):
- *
- * error = (vlo-vhi+128) >> 8;
- *
- * The approximate differs from the exact answer only when (vlo-vhi) is
- * 128; it then gives a correction of +1 when the exact correction is
- * 0. This gives 128 errors. The exact answer (correct for all 16 bit
- * input values) is:
- *
- * error = (vlo-vhi+128)*65535 >> 24;
- *
- * An alternative arithmetic calculation which also gives no errors is:
- *
- * (V * 255 + 32895) >> 16
- */
-
- png_int_32 tmp = *sp++; /* must be signed! */
- tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24;
- *dp++ = (png_byte)tmp;
- }
-
- row_info->bit_depth = 8;
- row_info->pixel_depth = (png_byte)(8 * row_info->channels);
- row_info->rowbytes = row_info->width * row_info->channels;
- }
-}
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
-static void
-/* Simply discard the low byte. This was the default behavior prior
- * to libpng-1.5.4.
- */
-png_do_chop(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_chop");
-
- if (row_info->bit_depth == 16)
- {
- png_bytep sp = row; /* source */
- png_bytep dp = row; /* destination */
- png_bytep ep = sp + row_info->rowbytes; /* end+1 */
-
- while (sp < ep)
- {
- *dp++ = *sp;
- sp += 2; /* skip low byte */
- }
-
- row_info->bit_depth = 8;
- row_info->pixel_depth = (png_byte)(8 * row_info->channels);
- row_info->rowbytes = row_info->width * row_info->channels;
- }
-}
-#endif
-
-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
-static void
-png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_read_swap_alpha");
-
- {
- png_uint_32 row_width = row_info->width;
- if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- /* This converts from RGBA to ARGB */
- if (row_info->bit_depth == 8)
- {
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_byte save;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- save = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = save;
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- /* This converts from RRGGBBAA to AARRGGBB */
- else
- {
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_byte save[2];
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- save[0] = *(--sp);
- save[1] = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = save[0];
- *(--dp) = save[1];
- }
- }
-#endif
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- /* This converts from GA to AG */
- if (row_info->bit_depth == 8)
- {
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_byte save;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- save = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = save;
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- /* This converts from GGAA to AAGG */
- else
- {
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_byte save[2];
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- save[0] = *(--sp);
- save[1] = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = save[0];
- *(--dp) = save[1];
- }
- }
-#endif
- }
- }
-}
-#endif
-
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
-static void
-png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
-{
- png_uint_32 row_width;
- png_debug(1, "in png_do_read_invert_alpha");
-
- row_width = row_info->width;
- if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This inverts the alpha channel in RGBA */
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = (png_byte)(255 - *(--sp));
-
-/* This does nothing:
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- We can replace it with:
-*/
- sp-=3;
- dp=sp;
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- /* This inverts the alpha channel in RRGGBBAA */
- else
- {
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = (png_byte)(255 - *(--sp));
- *(--dp) = (png_byte)(255 - *(--sp));
-
-/* This does nothing:
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- We can replace it with:
-*/
- sp-=6;
- dp=sp;
- }
- }
-#endif
- }
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This inverts the alpha channel in GA */
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = (png_byte)(255 - *(--sp));
- *(--dp) = *(--sp);
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- else
- {
- /* This inverts the alpha channel in GGAA */
- png_bytep sp = row + row_info->rowbytes;
- png_bytep dp = sp;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = (png_byte)(255 - *(--sp));
- *(--dp) = (png_byte)(255 - *(--sp));
-/*
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
-*/
- sp-=2;
- dp=sp;
- }
- }
-#endif
- }
-}
-#endif
-
-#ifdef PNG_READ_FILLER_SUPPORTED
-/* Add filler channel if we have RGB color */
-static void
-png_do_read_filler(png_row_infop row_info, png_bytep row,
- png_uint_32 filler, png_uint_32 flags)
-{
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- png_byte hi_filler = (png_byte)((filler>>8) & 0xff);
-#endif
- png_byte lo_filler = (png_byte)(filler & 0xff);
-
- png_debug(1, "in png_do_read_filler");
-
- if (
- row_info->color_type == PNG_COLOR_TYPE_GRAY)
- {
- if (row_info->bit_depth == 8)
- {
- if (flags & PNG_FLAG_FILLER_AFTER)
- {
- /* This changes the data from G to GX */
- png_bytep sp = row + (png_size_t)row_width;
- png_bytep dp = sp + (png_size_t)row_width;
- for (i = 1; i < row_width; i++)
- {
- *(--dp) = lo_filler;
- *(--dp) = *(--sp);
- }
- *(--dp) = lo_filler;
- row_info->channels = 2;
- row_info->pixel_depth = 16;
- row_info->rowbytes = row_width * 2;
- }
-
- else
- {
- /* This changes the data from G to XG */
- png_bytep sp = row + (png_size_t)row_width;
- png_bytep dp = sp + (png_size_t)row_width;
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = *(--sp);
- *(--dp) = lo_filler;
- }
- row_info->channels = 2;
- row_info->pixel_depth = 16;
- row_info->rowbytes = row_width * 2;
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- else if (row_info->bit_depth == 16)
- {
- if (flags & PNG_FLAG_FILLER_AFTER)
- {
- /* This changes the data from GG to GGXX */
- png_bytep sp = row + (png_size_t)row_width * 2;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 1; i < row_width; i++)
- {
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- }
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
- row_info->channels = 2;
- row_info->pixel_depth = 32;
- row_info->rowbytes = row_width * 4;
- }
-
- else
- {
- /* This changes the data from GG to XXGG */
- png_bytep sp = row + (png_size_t)row_width * 2;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
- }
- row_info->channels = 2;
- row_info->pixel_depth = 32;
- row_info->rowbytes = row_width * 4;
- }
- }
-#endif
- } /* COLOR_TYPE == GRAY */
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
- {
- if (row_info->bit_depth == 8)
- {
- if (flags & PNG_FLAG_FILLER_AFTER)
- {
- /* This changes the data from RGB to RGBX */
- png_bytep sp = row + (png_size_t)row_width * 3;
- png_bytep dp = sp + (png_size_t)row_width;
- for (i = 1; i < row_width; i++)
- {
- *(--dp) = lo_filler;
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- }
- *(--dp) = lo_filler;
- row_info->channels = 4;
- row_info->pixel_depth = 32;
- row_info->rowbytes = row_width * 4;
- }
-
- else
- {
- /* This changes the data from RGB to XRGB */
- png_bytep sp = row + (png_size_t)row_width * 3;
- png_bytep dp = sp + (png_size_t)row_width;
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = lo_filler;
- }
- row_info->channels = 4;
- row_info->pixel_depth = 32;
- row_info->rowbytes = row_width * 4;
- }
- }
-
-#ifdef PNG_READ_16BIT_SUPPORTED
- else if (row_info->bit_depth == 16)
- {
- if (flags & PNG_FLAG_FILLER_AFTER)
- {
- /* This changes the data from RRGGBB to RRGGBBXX */
- png_bytep sp = row + (png_size_t)row_width * 6;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 1; i < row_width; i++)
- {
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- }
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
- row_info->channels = 4;
- row_info->pixel_depth = 64;
- row_info->rowbytes = row_width * 8;
- }
-
- else
- {
- /* This changes the data from RRGGBB to XXRRGGBB */
- png_bytep sp = row + (png_size_t)row_width * 6;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 0; i < row_width; i++)
- {
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = *(--sp);
- *(--dp) = hi_filler;
- *(--dp) = lo_filler;
+ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE);
}
- row_info->channels = 4;
- row_info->pixel_depth = 64;
- row_info->rowbytes = row_width * 8;
- }
- }
-#endif
- } /* COLOR_TYPE == RGB */
-}
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
-/* Expand grayscale files to RGB, with or without alpha */
-static void
-png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
-{
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- png_debug(1, "in png_do_gray_to_rgb");
-
- if (row_info->bit_depth >= 8 &&
- !(row_info->color_type & PNG_COLOR_MASK_COLOR))
- {
- if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
- {
- if (row_info->bit_depth == 8)
- {
- /* This changes G to RGB */
- png_bytep sp = row + (png_size_t)row_width - 1;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 0; i < row_width; i++)
- {
- *(dp--) = *sp;
- *(dp--) = *sp;
- *(dp--) = *(sp--);
- }
- }
-
- else
- {
- /* This changes GG to RRGGBB */
- png_bytep sp = row + (png_size_t)row_width * 2 - 1;
- png_bytep dp = sp + (png_size_t)row_width * 4;
- for (i = 0; i < row_width; i++)
- {
- *(dp--) = *sp;
- *(dp--) = *(sp - 1);
- *(dp--) = *sp;
- *(dp--) = *(sp - 1);
- *(dp--) = *(sp--);
- *(dp--) = *(sp--);
- }
- }
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This changes GA to RGBA */
- png_bytep sp = row + (png_size_t)row_width * 2 - 1;
- png_bytep dp = sp + (png_size_t)row_width * 2;
- for (i = 0; i < row_width; i++)
- {
- *(dp--) = *(sp--);
- *(dp--) = *sp;
- *(dp--) = *sp;
- *(dp--) = *(sp--);
- }
- }
-
- else
- {
- /* This changes GGAA to RRGGBBAA */
- png_bytep sp = row + (png_size_t)row_width * 4 - 1;
- png_bytep dp = sp + (png_size_t)row_width * 4;
- for (i = 0; i < row_width; i++)
- {
- *(dp--) = *(sp--);
- *(dp--) = *(sp--);
- *(dp--) = *sp;
- *(dp--) = *(sp - 1);
- *(dp--) = *sp;
- *(dp--) = *(sp - 1);
- *(dp--) = *(sp--);
- *(dp--) = *(sp--);
- }
- }
- }
- row_info->channels = (png_byte)(row_info->channels + 2);
- row_info->color_type |= PNG_COLOR_MASK_COLOR;
- row_info->pixel_depth = (png_byte)(row_info->channels *
- row_info->bit_depth);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
-}
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-/* Reduce RGB files to grayscale, with or without alpha
- * using the equation given in Poynton's ColorFAQ of 1998-01-04 at
- * (THIS LINK IS DEAD June 2008 but
- * versions dated 1998 through November 2002 have been archived at
- * http://web.archive.org/web/20000816232553/http://www.inforamp.net/
- * ~poynton/notes/colour_and_gamma/ColorFAQ.txt )
- * Charles Poynton poynton at poynton.com
- *
- * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
- *
- * which can be expressed with integers as
- *
- * Y = (6969 * R + 23434 * G + 2365 * B)/32768
- *
- * Poynton's current link (as of January 2003 through July 2011):
- *
- * has changed the numbers slightly:
- *
- * Y = 0.2126*R + 0.7152*G + 0.0722*B
- *
- * which can be expressed with integers as
- *
- * Y = (6966 * R + 23436 * G + 2366 * B)/32768
- *
- * Historically, however, libpng uses numbers derived from the ITU-R Rec 709
- * end point chromaticities and the D65 white point. Depending on the
- * precision used for the D65 white point this produces a variety of different
- * numbers, however if the four decimal place value used in ITU-R Rec 709 is
- * used (0.3127,0.3290) the Y calculation would be:
- *
- * Y = (6968 * R + 23435 * G + 2366 * B)/32768
- *
- * While this is correct the rounding results in an overflow for white, because
- * the sum of the rounded coefficients is 32769, not 32768. Consequently
- * libpng uses, instead, the closest non-overflowing approximation:
- *
- * Y = (6968 * R + 23434 * G + 2366 * B)/32768
- *
- * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk
- * (including an sRGB chunk) then the chromaticities are used to calculate the
- * coefficients. See the chunk handling in pngrutil.c for more information.
- *
- * In all cases the calculation is to be done in a linear colorspace. If no
- * gamma information is available to correct the encoding of the original RGB
- * values this results in an implicit assumption that the original PNG RGB
- * values were linear.
- *
- * Other integer coefficents can be used via png_set_rgb_to_gray(). Because
- * the API takes just red and green coefficients the blue coefficient is
- * calculated to make the sum 32768. This will result in different rounding
- * to that used above.
- */
-static int
-png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row)
-
-{
- int rgb_error = 0;
-
- png_debug(1, "in png_do_rgb_to_gray");
-
- if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) &&
- (row_info->color_type & PNG_COLOR_MASK_COLOR))
- {
- PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
- PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
- PNG_CONST png_uint_32 bc = 32768 - rc - gc;
- PNG_CONST png_uint_32 row_width = row_info->width;
- PNG_CONST int have_alpha =
- (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
-
- if (row_info->bit_depth == 8)
- {
- /* Notice that gamma to/from 1 are not necessarily inverses (if
- * there is an overall gamma correction). Prior to 1.5.5 this code
- * checked the linearized values for equality; this doesn't match
- * the documentation, the original values must be checked.
- */
- if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
- {
- PNG_CONST unsigned int shift = 15 + png_ptr->gamma_shift;
- PNG_CONST png_uint_32 add = 1U << (shift-1);
- png_bytep sp = row;
- png_bytep dp = row;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- unsigned int red = *(sp++);
- unsigned int green = *(sp++);
- unsigned int blue = *(sp++);
-
- if (red != green || red != blue)
- {
- red = png_ptr->gamma_to_1[red];
- green = png_ptr->gamma_to_1[green];
- blue = png_ptr->gamma_to_1[blue];
-
- rgb_error |= 1;
- *(dp++) = png_ptr->gamma_from_1[
- (rc*red + gc*green + bc*blue + add)>>shift];
- }
-
- else
- {
- /* If there is no overall correction the table will not be
- * set.
- */
- if (png_ptr->gamma_table != NULL)
- red = png_ptr->gamma_table[red];
-
- *(dp++) = (png_byte)red;
- }
-
- if (have_alpha != 0)
- *(dp++) = *(sp++);
- }
- }
-
- else
- {
- png_bytep sp = row;
- png_bytep dp = row;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- png_byte red = *(sp++);
- png_byte green = *(sp++);
- png_byte blue = *(sp++);
-
- if (red != green || red != blue)
- {
- rgb_error |= 1;
- *(dp++) = (png_byte)((rc*red+gc*green+bc*blue+16384)>>15);
- }
-
- else
- *(dp++) = red;
-
- if (have_alpha != 0)
- *(dp++) = *(sp++);
- }
- }
- }
-
- else /* RGB bit_depth == 16 */
- {
- if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL)
- {
- unsigned int shift = png_ptr->gamma_shift;
- unsigned int add = (shift > 0 ? (1U << (shift-1)) : 0);
- png_bytep sp = row;
- png_bytep dp = row;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 red, green, blue, w;
-
- red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
- green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
- blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
-
- if (red == green && red == blue)
- {
- if (png_ptr->gamma_16_table != NULL)
- w = png_ptr->gamma_16_table[(red+add) >> shift];
-
- else
- w = red;
- }
-
- else
- {
- png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red+add)
- >> shift];
- png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green+add)
- >> shift];
- png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue+add)
- >> shift];
- png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1
- + bc*blue_1 + 16384)>>15);
- w = png_ptr->gamma_16_from_1[(gray16+add) >> shift];
- rgb_error |= 1;
- }
-
- *(dp++) = (png_byte)((w>>8) & 0xff);
- *(dp++) = (png_byte)(w & 0xff);
-
- if (have_alpha != 0)
- {
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- }
- }
- }
-
- else
- {
- png_bytep sp = row;
- png_bytep dp = row;
- png_uint_32 i;
-
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 red, green, blue, gray16;
-
- red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
- green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
- blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
-
- if (red != green || red != blue)
- rgb_error |= 1;
-
- /* From 1.5.5 in the 16 bit case do the accurate conversion even
- * in the 'fast' case - this is because this is where the code
- * ends up when handling linear 16 bit data.
+# ifdef PNG_READ_tRNS
+ /* If the output format is not a palette format the tRNS
+ * information was a single color which is now invalid
+ * (probably), otherwise the array of tRNS values must be
+ * updated.
*/
- gray16 = (png_uint_16)((rc*red+gc*green+bc*blue+16384) >> 15);
- *(dp++) = (png_byte)((gray16>>8) & 0xff);
- *(dp++) = (png_byte)(gray16 & 0xff);
-
- if (have_alpha != 0)
+ if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
{
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- }
- }
- }
- }
-
- row_info->channels = (png_byte)(row_info->channels - 2);
- row_info->color_type = (png_byte)(row_info->color_type &
- ~PNG_COLOR_MASK_COLOR);
- row_info->pixel_depth = (png_byte)(row_info->channels *
- row_info->bit_depth);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
- return rgb_error;
-}
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
-/* Replace any alpha or transparency with the supplied background color.
- * "background" is already in the screen gamma, while "background_1" is
- * at a gamma of 1.0. Paletted files have already been taken care of.
- */
-static void
-png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
- png_const_bytep gamma_table = png_ptr->gamma_table;
- png_const_bytep gamma_from_1 = png_ptr->gamma_from_1;
- png_const_uint_16p gamma_to_1 = png_ptr->gamma_to_1;
- png_const_uint_16p gamma_16 = png_ptr->gamma_16_table;
- png_const_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1;
- png_const_uint_16p gamma_16_to_1 = png_ptr->gamma_16_to_1;
- PNG_CONST unsigned int shift = png_ptr->gamma_shift;
- PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0);
- PNG_CONST int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
-
- png_bytep sp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- png_debug(1, "in png_do_compose");
-
- {
- switch (row_info->color_type)
- {
- case PNG_COLOR_TYPE_GRAY:
- {
- switch (row_info->bit_depth)
- {
- case 1:
- {
- int bit_shift = 7;
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- if ((png_uint_16)((*sp >> bit_shift) & 0x01)
- == png_ptr->trans_color.gray)
- {
- unsigned int tmp = *sp & (0x7f7f >> (7 - bit_shift));
- tmp |= png_ptr->background.gray << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- if (bit_shift == 0)
- {
- bit_shift = 7;
- sp++;
- }
-
- else
- bit_shift--;
- }
- break;
- }
-
- case 2:
- {
-#if 0
- if (gamma_table != NULL)
- {
- int bit_shift = 6;
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- if ((png_uint_16)((*sp >> bit_shift) & 0x03)
- == png_ptr->trans_color.gray)
- {
- unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift));
- tmp |= png_ptr->background.gray << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- else
- {
- unsigned int p = (*sp >> bit_shift) & 0x03;
- unsigned int g = (gamma_table [p | (p << 2) |
- (p << 4) | (p << 6)] >> 6) & 0x03;
- unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift));
- tmp |= g << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- if (bit_shift == 0)
- {
- bit_shift = 6;
- sp++;
- }
-
- else
- bit_shift -= 2;
- }
- }
-
- else
-#endif
- {
- int bit_shift = 6;
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- if ((png_uint_16)((*sp >> bit_shift) & 0x03)
- == png_ptr->trans_color.gray)
- {
- unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift));
- tmp |= png_ptr->background.gray << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- if (bit_shift == 0)
- {
- bit_shift = 6;
- sp++;
- }
-
- else
- bit_shift -= 2;
- }
- }
- break;
- }
-
- case 4:
- {
-#if 0
- if (gamma_table != NULL)
- {
- int bit_shift = 4;
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- if ((png_uint_16)((*sp >> bit_shift) & 0x0f)
- == png_ptr->trans_color.gray)
- {
- unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift));
- tmp |= png_ptr->background.gray << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- else
- {
- unsigned int p = (*sp >> bit_shift) & 0x0f;
- unsigned int g = (gamma_table[p | (p << 4)] >> 4) &
- 0x0f;
- unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift));
- tmp |= g << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- if (bit_shift == 0)
- {
- bit_shift = 4;
- sp++;
- }
-
- else
- bit_shift -= 4;
- }
- }
-
- else
-#endif
- {
- int bit_shift = 4;
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- if ((png_uint_16)((*sp >> bit_shift) & 0x0f)
- == png_ptr->trans_color.gray)
- {
- unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift));
- tmp |= png_ptr->background.gray << bit_shift;
- *sp = (png_byte)(tmp & 0xff);
- }
-
- if (bit_shift == 0)
- {
- bit_shift = 4;
- sp++;
- }
-
- else
- bit_shift -= 4;
- }
- }
- break;
- }
-
- case 8:
- {
- if (gamma_table != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp++)
- {
- if (*sp == png_ptr->trans_color.gray)
- *sp = (png_byte)png_ptr->background.gray;
-
- else
- *sp = gamma_table[*sp];
- }
- }
+ if (png_ptr->num_trans > 0)
+ png_set_tRNS(png_ptr, info_ptr, png_ptr->trans_alpha,
+ png_ptr->num_trans, NULL/*trans color*/);
else
{
- sp = row;
- for (i = 0; i < row_width; i++, sp++)
- {
- if (*sp == png_ptr->trans_color.gray)
- *sp = (png_byte)png_ptr->background.gray;
- }
- }
- break;
- }
-
- case 16:
- {
- if (gamma_16 != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 2)
- {
- png_uint_16 v;
-
- v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
- if (v == png_ptr->trans_color.gray)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)((png_ptr->background.gray >> 8)
- & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.gray
- & 0xff);
- }
-
- else
- {
- v = gamma_16[(v+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 2)
- {
- png_uint_16 v;
-
- v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
- if (v == png_ptr->trans_color.gray)
- {
- *sp = (png_byte)((png_ptr->background.gray >> 8)
- & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.gray
- & 0xff);
- }
- }
- }
- break;
- }
-
- default:
- break;
- }
- break;
- }
-
- case PNG_COLOR_TYPE_RGB:
- {
- if (row_info->bit_depth == 8)
- {
- if (gamma_table != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 3)
- {
- if (*sp == png_ptr->trans_color.red &&
- *(sp + 1) == png_ptr->trans_color.green &&
- *(sp + 2) == png_ptr->trans_color.blue)
- {
- *sp = (png_byte)png_ptr->background.red;
- *(sp + 1) = (png_byte)png_ptr->background.green;
- *(sp + 2) = (png_byte)png_ptr->background.blue;
- }
-
- else
- {
- *sp = gamma_table[*sp];
- *(sp + 1) = gamma_table[*(sp + 1)];
- *(sp + 2) = gamma_table[*(sp + 2)];
- }
+ png_free_data(png_ptr, info_ptr, PNG_FREE_tRNS, 0);
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
}
}
else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 3)
- {
- if (*sp == png_ptr->trans_color.red &&
- *(sp + 1) == png_ptr->trans_color.green &&
- *(sp + 2) == png_ptr->trans_color.blue)
- {
- *sp = (png_byte)png_ptr->background.red;
- *(sp + 1) = (png_byte)png_ptr->background.green;
- *(sp + 2) = (png_byte)png_ptr->background.blue;
- }
- }
- }
- }
- else /* if (row_info->bit_depth == 16) */
- {
- if (gamma_16 != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 6)
- {
- png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
- png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
- + *(sp + 3));
-
- png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
- + *(sp + 5));
-
- if (r == png_ptr->trans_color.red &&
- g == png_ptr->trans_color.green &&
- b == png_ptr->trans_color.blue)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
- *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
- & 0xff);
- *(sp + 3) = (png_byte)(png_ptr->background.green
- & 0xff);
- *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
- & 0xff);
- *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
- }
-
- else
- {
- png_uint_16 v = gamma_16[(r+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
-
- v = gamma_16[(g+add) >> shift];
- *(sp + 2) = (png_byte)((v >> 8) & 0xff);
- *(sp + 3) = (png_byte)(v & 0xff);
-
- v = gamma_16[(b+add) >> shift];
- *(sp + 4) = (png_byte)((v >> 8) & 0xff);
- *(sp + 5) = (png_byte)(v & 0xff);
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 6)
- {
- png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
-
- png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
- + *(sp + 3));
-
- png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
- + *(sp + 5));
-
- if (r == png_ptr->trans_color.red &&
- g == png_ptr->trans_color.green &&
- b == png_ptr->trans_color.blue)
- {
- *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
- *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
- & 0xff);
- *(sp + 3) = (png_byte)(png_ptr->background.green
- & 0xff);
- *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
- & 0xff);
- *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
- }
- }
- }
- }
- break;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
+# endif /* READ_tRNS */
}
-
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- {
- if (row_info->bit_depth == 8)
- {
- if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
- gamma_table != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 2)
- {
- png_uint_16 a = *(sp + 1);
-
- if (a == 0xff)
- *sp = gamma_table[*sp];
-
- else if (a == 0)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)png_ptr->background.gray;
- }
-
- else
- {
- unsigned int v, w;
-
- v = gamma_to_1[*sp];
- png_composite_16(w, v, 257*a,
- png_ptr->background_1.gray);
-
- if (optimize == 0)
- w = gamma_from_1[(w+add)>>shift];
-
- else /* alpha pixels linear and approximate */
- w = PNG_DIV257(w);
-
- *sp = (png_byte)w;
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 2)
- {
- png_byte a = *(sp + 1);
-
- if (a == 0)
- *sp = (png_byte)png_ptr->background.gray;
-
- else if (a < 0xff)
- png_composite(*sp, *sp, a, png_ptr->background.gray);
- }
- }
- }
-
- else /* if (png_ptr->bit_depth == 16) */
- {
- if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
- gamma_16_to_1 != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 4)
- {
- png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
- + *(sp + 3));
-
- if (a == (png_uint_16)0xffff)
- {
- png_uint_16 v;
-
- v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- }
-
- else if (a == 0)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)((png_ptr->background.gray >> 8)
- & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
- }
-
- else
- {
- png_uint_16 g, v, w;
-
- g = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift];
- png_composite_16(v, g, a, png_ptr->background_1.gray);
-
- if (optimize == 0)
- w = gamma_16_from_1[(v+add) >> shift];
-
- else
- w = v;
-
- *sp = (png_byte)((w >> 8) & 0xff);
- *(sp + 1) = (png_byte)(w & 0xff);
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 4)
- {
- png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
- + *(sp + 3));
-
- if (a == 0)
- {
- *sp = (png_byte)((png_ptr->background.gray >> 8)
- & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
- }
-
- else if (a < 0xffff)
- {
- png_uint_16 g, v;
-
- g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
- png_composite_16(v, g, a, png_ptr->background.gray);
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- }
- }
- }
- }
- break;
- }
-
- case PNG_COLOR_TYPE_RGB_ALPHA:
- {
- if (row_info->bit_depth == 8)
- {
- if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
- gamma_table != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 4)
- {
- png_byte a = *(sp + 3);
-
- if (a == 0xff)
- {
- *sp = gamma_table[*sp];
- *(sp + 1) = gamma_table[*(sp + 1)];
- *(sp + 2) = gamma_table[*(sp + 2)];
- }
-
- else if (a == 0)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)png_ptr->background.red;
- *(sp + 1) = (png_byte)png_ptr->background.green;
- *(sp + 2) = (png_byte)png_ptr->background.blue;
- }
-
- else
- {
- unsigned int v, w;
- unsigned int alpha = a * 257U;
-
- v = gamma_to_1[*sp];
- png_composite_16(w, v, alpha,
- png_ptr->background_1.red);
-
- if (optimize == 0)
- w = gamma_from_1[(w+add)>>shift];
-
- else
- w = PNG_DIV257(w);
-
- *sp = (png_byte)w;
-
- v = gamma_to_1[*(sp + 1)];
- png_composite_16(w, v, alpha,
- png_ptr->background_1.green);
-
- if (optimize == 0)
- w = gamma_from_1[(w+add)>>shift];
-
- else
- w = PNG_DIV257(w);
-
- *(sp + 1) = (png_byte)w;
-
- v = gamma_to_1[*(sp + 2)];
- png_composite_16(w, v, alpha,
- png_ptr->background_1.blue);
-
- if (optimize == 0)
- w = gamma_from_1[(w+add)>>shift];
-
- else
- w = PNG_DIV257(w);
-
- *(sp + 2) = (png_byte)w;
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 4)
- {
- png_byte a = *(sp + 3);
-
- if (a == 0)
- {
- *sp = (png_byte)png_ptr->background.red;
- *(sp + 1) = (png_byte)png_ptr->background.green;
- *(sp + 2) = (png_byte)png_ptr->background.blue;
- }
-
- else if (a < 0xff)
- {
- png_composite(*sp, *sp, a, png_ptr->background.red);
-
- png_composite(*(sp + 1), *(sp + 1), a,
- png_ptr->background.green);
-
- png_composite(*(sp + 2), *(sp + 2), a,
- png_ptr->background.blue);
- }
- }
- }
- }
-
- else /* if (row_info->bit_depth == 16) */
- {
- if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
- gamma_16_to_1 != NULL)
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 8)
- {
- png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
- << 8) + (png_uint_16)(*(sp + 7)));
-
- if (a == (png_uint_16)0xffff)
- {
- png_uint_16 v;
-
- v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
-
- v = gamma_16[((sp[2]<<8)+sp[3]+add) >> shift];
- *(sp + 2) = (png_byte)((v >> 8) & 0xff);
- *(sp + 3) = (png_byte)(v & 0xff);
-
- v = gamma_16[((sp[4]<<8)+sp[5]+add) >> shift];
- *(sp + 4) = (png_byte)((v >> 8) & 0xff);
- *(sp + 5) = (png_byte)(v & 0xff);
- }
-
- else if (a == 0)
- {
- /* Background is already in screen gamma */
- *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
- *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
- & 0xff);
- *(sp + 3) = (png_byte)(png_ptr->background.green
- & 0xff);
- *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
- & 0xff);
- *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
- }
-
- else
- {
- png_uint_16 v, w;
-
- v = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift];
- png_composite_16(w, v, a, png_ptr->background_1.red);
-
- if (optimize == 0)
- w = gamma_16_from_1[(w+add) >> shift];
-
- *sp = (png_byte)((w >> 8) & 0xff);
- *(sp + 1) = (png_byte)(w & 0xff);
-
- v = gamma_16_to_1[((sp[2]<<8)+sp[3]+add) >> shift];
- png_composite_16(w, v, a, png_ptr->background_1.green);
-
- if (optimize == 0)
- w = gamma_16_from_1[(w+add) >> shift];
-
- *(sp + 2) = (png_byte)((w >> 8) & 0xff);
- *(sp + 3) = (png_byte)(w & 0xff);
-
- v = gamma_16_to_1[((sp[4]<<8)+sp[5]+add) >> shift];
- png_composite_16(w, v, a, png_ptr->background_1.blue);
-
- if (optimize == 0)
- w = gamma_16_from_1[(w+add) >> shift];
-
- *(sp + 4) = (png_byte)((w >> 8) & 0xff);
- *(sp + 5) = (png_byte)(w & 0xff);
- }
- }
- }
-
- else
- {
- sp = row;
- for (i = 0; i < row_width; i++, sp += 8)
- {
- png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
- << 8) + (png_uint_16)(*(sp + 7)));
-
- if (a == 0)
- {
- *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
- *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
- & 0xff);
- *(sp + 3) = (png_byte)(png_ptr->background.green
- & 0xff);
- *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
- & 0xff);
- *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
- }
-
- else if (a < 0xffff)
- {
- png_uint_16 v;
-
- png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
- png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
- + *(sp + 3));
- png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
- + *(sp + 5));
-
- png_composite_16(v, r, a, png_ptr->background.red);
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
-
- png_composite_16(v, g, a, png_ptr->background.green);
- *(sp + 2) = (png_byte)((v >> 8) & 0xff);
- *(sp + 3) = (png_byte)(v & 0xff);
-
- png_composite_16(v, b, a, png_ptr->background.blue);
- *(sp + 4) = (png_byte)((v >> 8) & 0xff);
- *(sp + 5) = (png_byte)(v & 0xff);
- }
- }
- }
- }
- break;
- }
-
- default:
- break;
- }
- }
+# endif /* READ_TRANSFORMS */
+# else /* !TRANSFORM_MECH */
+ PNG_UNUSED(png_ptr)
+ PNG_UNUSED(info_ptr)
+# endif /* !TRANSFORM_MECH */
}
-#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_READ_ALPHA_MODE_SUPPORTED */
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
-/* Gamma correct the image, avoiding the alpha channel. Make sure
- * you do this after you deal with the transparency issue on grayscale
- * or RGB images. If your bit depth is 8, use gamma_table, if it
- * is 16, use gamma_16_table and gamma_shift. Build these with
- * build_gamma_table().
- */
-static void
-png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
- png_const_bytep gamma_table = png_ptr->gamma_table;
- png_const_uint_16p gamma_16_table = png_ptr->gamma_16_table;
- int shift = png_ptr->gamma_shift;
- int add = (shift > 0 ? 1U << (shift-1) : 0);
-
- png_bytep sp;
- png_uint_32 i;
- png_uint_32 row_width=row_info->width;
-
- png_debug(1, "in png_do_gamma");
-
- /* Prior to libpng 1.7.0 this code would attempt to gamma correct 2 and 4 bit
- * gray level values, the results are ridiculously inaccurate. In 1.7.0 the
- * code is removed and a warning is introduced to catch cases where an
- * application might actually try it.
- */
- if (((row_info->bit_depth == 8 && gamma_table != NULL) ||
- (row_info->bit_depth == 16 && gamma_16_table != NULL)))
- {
- switch (row_info->color_type)
- {
- case PNG_COLOR_TYPE_RGB:
- {
- if (row_info->bit_depth == 8)
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- *sp = gamma_table[*sp];
- sp++;
- *sp = gamma_table[*sp];
- sp++;
- *sp = gamma_table[*sp];
- sp++;
- }
- }
-
- else /* if (row_info->bit_depth == 16) */
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 v;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
- }
- }
- break;
- }
-
- case PNG_COLOR_TYPE_RGB_ALPHA:
- {
- if (row_info->bit_depth == 8)
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- *sp = gamma_table[*sp];
- sp++;
-
- *sp = gamma_table[*sp];
- sp++;
-
- *sp = gamma_table[*sp];
- sp++;
-
- sp++;
- }
- }
-
- else /* if (row_info->bit_depth == 16) */
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 v;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 4;
- }
- }
- break;
- }
-
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- {
- if (row_info->bit_depth == 8)
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- *sp = gamma_table[*sp];
- sp += 2;
- }
- }
-
- else /* if (row_info->bit_depth == 16) */
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 v;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 4;
- }
- }
- break;
- }
-
- case PNG_COLOR_TYPE_GRAY:
- {
- if (row_info->bit_depth == 8)
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- *sp = gamma_table[*sp];
- sp++;
- }
- }
-
- else /*row_info->bit_depth == 16 */
- {
- sp = row;
- for (i = 0; i < row_width; i++)
- {
- png_uint_16 v;
-
- v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift];
- *sp = (png_byte)((v >> 8) & 0xff);
- *(sp + 1) = (png_byte)(v & 0xff);
- sp += 2;
- }
- }
- break;
- }
-
- default:
- break;
- }
- }
-}
-#endif
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-/* Encode the alpha channel to the output gamma (the input channel is always
- * linear.) Called only with color types that have an alpha channel. Needs the
- * from_1 tables.
- */
-static void
-png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
-{
- png_uint_32 row_width = row_info->width;
-
- png_debug(1, "in png_do_encode_alpha");
-
- if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
- {
- PNG_CONST unsigned int shift = png_ptr->gamma_shift;
- PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0);
-
- if (row_info->bit_depth == 8)
- {
- PNG_CONST png_bytep gamma_from_1 = png_ptr->gamma_from_1;
-
- if (gamma_from_1 != NULL)
- {
- PNG_CONST int step =
- (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
-
- /* The alpha channel is the last component: */
- row += step - 1;
-
- for (; row_width > 0; --row_width, row += step)
- *row = gamma_from_1[(257U**row+add)>>shift];
-
- return;
- }
- }
-
- else if (row_info->bit_depth == 16)
- {
- PNG_CONST png_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1;
-
- if (gamma_16_from_1 != NULL)
- {
- PNG_CONST int step =
- (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
-
- /* The alpha channel is the last component: */
- row += step - 2;
-
- for (; row_width > 0; --row_width, row += step)
- {
- png_uint_16 v;
-
- v = gamma_16_from_1[((row[0]<<8)+row[1]+add) >> shift];
- *row = (png_byte)((v >> 8) & 0xff);
- *(row + 1) = (png_byte)(v & 0xff);
- }
-
- return;
- }
- }
- }
-
- /* Only get to here if called with a weird row_info; no harm has been done,
- * so just issue a warning.
- */
- png_warning(png_ptr, "png_do_encode_alpha: unexpected call");
-}
-#endif
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
-/* Expands a palette row to an RGB or RGBA row depending
- * upon whether you supply trans and num_trans.
- */
-static void
-png_do_expand_palette(png_row_infop row_info, png_bytep row,
- png_const_colorp palette, png_const_bytep trans_alpha, int num_trans)
-{
- int shift, value;
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width=row_info->width;
-
- png_debug(1, "in png_do_expand_palette");
-
- if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
- {
- if (row_info->bit_depth < 8)
- {
- switch (row_info->bit_depth)
- {
- case 1:
- {
- sp = row + (png_size_t)((row_width - 1) >> 3);
- dp = row + (png_size_t)row_width - 1;
- shift = 7 - (int)((row_width + 7) & 0x07);
- for (i = 0; i < row_width; i++)
- {
- if ((*sp >> shift) & 0x01)
- *dp = 1;
-
- else
- *dp = 0;
-
- if (shift == 7)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift++;
-
- dp--;
- }
- break;
- }
-
- case 2:
- {
- sp = row + (png_size_t)((row_width - 1) >> 2);
- dp = row + (png_size_t)row_width - 1;
- shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
- for (i = 0; i < row_width; i++)
- {
- value = (*sp >> shift) & 0x03;
- *dp = (png_byte)value;
- if (shift == 6)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift += 2;
-
- dp--;
- }
- break;
- }
-
- case 4:
- {
- sp = row + (png_size_t)((row_width - 1) >> 1);
- dp = row + (png_size_t)row_width - 1;
- shift = (int)((row_width & 0x01) << 2);
- for (i = 0; i < row_width; i++)
- {
- value = (*sp >> shift) & 0x0f;
- *dp = (png_byte)value;
- if (shift == 4)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift += 4;
-
- dp--;
- }
- break;
- }
-
- default:
- break;
- }
- row_info->bit_depth = 8;
- row_info->pixel_depth = 8;
- row_info->rowbytes = row_width;
- }
-
- if (row_info->bit_depth == 8)
- {
- {
- if (num_trans > 0)
- {
- sp = row + (png_size_t)row_width - 1;
- dp = row + (png_size_t)(row_width << 2) - 1;
-
- for (i = 0; i < row_width; i++)
- {
- if ((int)(*sp) >= num_trans)
- *dp-- = 0xff;
-
- else
- *dp-- = trans_alpha[*sp];
-
- *dp-- = palette[*sp].blue;
- *dp-- = palette[*sp].green;
- *dp-- = palette[*sp].red;
- sp--;
- }
- row_info->bit_depth = 8;
- row_info->pixel_depth = 32;
- row_info->rowbytes = row_width * 4;
- row_info->color_type = 6;
- row_info->channels = 4;
- }
-
- else
- {
- sp = row + (png_size_t)row_width - 1;
- dp = row + (png_size_t)(row_width * 3) - 1;
-
- for (i = 0; i < row_width; i++)
- {
- *dp-- = palette[*sp].blue;
- *dp-- = palette[*sp].green;
- *dp-- = palette[*sp].red;
- sp--;
- }
-
- row_info->bit_depth = 8;
- row_info->pixel_depth = 24;
- row_info->rowbytes = row_width * 3;
- row_info->color_type = 2;
- row_info->channels = 3;
- }
- }
- }
- }
-}
-
-/* If the bit depth < 8, it is expanded to 8. Also, if the already
- * expanded transparency value is supplied, an alpha channel is built.
- */
-static void
-png_do_expand(png_row_infop row_info, png_bytep row,
- png_const_color_16p trans_color)
-{
- int shift, value;
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width=row_info->width;
-
- png_debug(1, "in png_do_expand");
-
- {
- if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
- {
- unsigned int gray = trans_color ? trans_color->gray : 0;
-
- if (row_info->bit_depth < 8)
- {
- switch (row_info->bit_depth)
- {
- case 1:
- {
- gray = (gray & 0x01) * 0xff;
- sp = row + (png_size_t)((row_width - 1) >> 3);
- dp = row + (png_size_t)row_width - 1;
- shift = 7 - (int)((row_width + 7) & 0x07);
- for (i = 0; i < row_width; i++)
- {
- if ((*sp >> shift) & 0x01)
- *dp = 0xff;
-
- else
- *dp = 0;
-
- if (shift == 7)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift++;
-
- dp--;
- }
- break;
- }
-
- case 2:
- {
- gray = (gray & 0x03) * 0x55;
- sp = row + (png_size_t)((row_width - 1) >> 2);
- dp = row + (png_size_t)row_width - 1;
- shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
- for (i = 0; i < row_width; i++)
- {
- value = (*sp >> shift) & 0x03;
- *dp = (png_byte)(value | (value << 2) | (value << 4) |
- (value << 6));
- if (shift == 6)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift += 2;
-
- dp--;
- }
- break;
- }
-
- case 4:
- {
- gray = (gray & 0x0f) * 0x11;
- sp = row + (png_size_t)((row_width - 1) >> 1);
- dp = row + (png_size_t)row_width - 1;
- shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
- for (i = 0; i < row_width; i++)
- {
- value = (*sp >> shift) & 0x0f;
- *dp = (png_byte)(value | (value << 4));
- if (shift == 4)
- {
- shift = 0;
- sp--;
- }
-
- else
- shift = 4;
-
- dp--;
- }
- break;
- }
-
- default:
- break;
- }
-
- row_info->bit_depth = 8;
- row_info->pixel_depth = 8;
- row_info->rowbytes = row_width;
- }
-
- if (trans_color != NULL)
- {
- if (row_info->bit_depth == 8)
- {
- gray = gray & 0xff;
- sp = row + (png_size_t)row_width - 1;
- dp = row + (png_size_t)(row_width << 1) - 1;
-
- for (i = 0; i < row_width; i++)
- {
- if (*sp == gray)
- *dp-- = 0;
-
- else
- *dp-- = 0xff;
-
- *dp-- = *sp--;
- }
- }
-
- else if (row_info->bit_depth == 16)
- {
- unsigned int gray_high = (gray >> 8) & 0xff;
- unsigned int gray_low = gray & 0xff;
- sp = row + row_info->rowbytes - 1;
- dp = row + (row_info->rowbytes << 1) - 1;
- for (i = 0; i < row_width; i++)
- {
- if (*(sp - 1) == gray_high && *(sp) == gray_low)
- {
- *dp-- = 0;
- *dp-- = 0;
- }
-
- else
- {
- *dp-- = 0xff;
- *dp-- = 0xff;
- }
-
- *dp-- = *sp--;
- *dp-- = *sp--;
- }
- }
-
- row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
- row_info->channels = 2;
- row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
- row_width);
- }
- }
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color)
- {
- if (row_info->bit_depth == 8)
- {
- png_byte red = (png_byte)(trans_color->red & 0xff);
- png_byte green = (png_byte)(trans_color->green & 0xff);
- png_byte blue = (png_byte)(trans_color->blue & 0xff);
- sp = row + (png_size_t)row_info->rowbytes - 1;
- dp = row + (png_size_t)(row_width << 2) - 1;
- for (i = 0; i < row_width; i++)
- {
- if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
- *dp-- = 0;
-
- else
- *dp-- = 0xff;
-
- *dp-- = *sp--;
- *dp-- = *sp--;
- *dp-- = *sp--;
- }
- }
- else if (row_info->bit_depth == 16)
- {
- png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
- png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
- png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
- png_byte red_low = (png_byte)(trans_color->red & 0xff);
- png_byte green_low = (png_byte)(trans_color->green & 0xff);
- png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
- sp = row + row_info->rowbytes - 1;
- dp = row + (png_size_t)(row_width << 3) - 1;
- for (i = 0; i < row_width; i++)
- {
- if (*(sp - 5) == red_high &&
- *(sp - 4) == red_low &&
- *(sp - 3) == green_high &&
- *(sp - 2) == green_low &&
- *(sp - 1) == blue_high &&
- *(sp ) == blue_low)
- {
- *dp-- = 0;
- *dp-- = 0;
- }
-
- else
- {
- *dp-- = 0xff;
- *dp-- = 0xff;
- }
-
- *dp-- = *sp--;
- *dp-- = *sp--;
- *dp-- = *sp--;
- *dp-- = *sp--;
- *dp-- = *sp--;
- *dp-- = *sp--;
- }
- }
- row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- row_info->channels = 4;
- row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
- }
-}
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* If the bit depth is 8 and the color type is not a palette type expand the
- * whole row to 16 bits. Has no effect otherwise.
- */
-static void
-png_do_expand_16(png_row_infop row_info, png_bytep row)
-{
- if (row_info->bit_depth == 8 &&
- row_info->color_type != PNG_COLOR_TYPE_PALETTE)
- {
- /* The row have a sequence of bytes containing [0..255] and we need
- * to turn it into another row containing [0..65535], to do this we
- * calculate:
- *
- * (input / 255) * 65535
- *
- * Which happens to be exactly input * 257 and this can be achieved
- * simply by byte replication in place (copying backwards).
- */
- png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
- png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */
- while (dp > sp)
- dp[-2] = dp[-1] = *--sp, dp -= 2;
-
- row_info->rowbytes *= 2;
- row_info->bit_depth = 16;
- row_info->pixel_depth = (png_byte)(row_info->channels * 16);
- }
-}
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
-static void
-png_do_quantize(png_row_infop row_info, png_bytep row,
- png_const_bytep palette_lookup, png_const_bytep quantize_lookup)
-{
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width=row_info->width;
-
- png_debug(1, "in png_do_quantize");
-
- if (row_info->bit_depth == 8)
- {
- if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup)
- {
- int r, g, b, p;
- sp = row;
- dp = row;
- for (i = 0; i < row_width; i++)
- {
- r = *sp++;
- g = *sp++;
- b = *sp++;
-
- /* This looks real messy, but the compiler will reduce
- * it down to a reasonable formula. For example, with
- * 5 bits per color, we get:
- * p = (((r >> 3) & 0x1f) << 10) |
- * (((g >> 3) & 0x1f) << 5) |
- * ((b >> 3) & 0x1f);
- */
- p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
- ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
- (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
- (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
- ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
- (PNG_QUANTIZE_BLUE_BITS)) |
- ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
- ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
-
- *dp++ = palette_lookup[p];
- }
-
- row_info->color_type = PNG_COLOR_TYPE_PALETTE;
- row_info->channels = 1;
- row_info->pixel_depth = row_info->bit_depth;
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
- palette_lookup != NULL)
- {
- int r, g, b, p;
- sp = row;
- dp = row;
- for (i = 0; i < row_width; i++)
- {
- r = *sp++;
- g = *sp++;
- b = *sp++;
- sp++;
-
- p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
- ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
- (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
- (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
- ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
- (PNG_QUANTIZE_BLUE_BITS)) |
- ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
- ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
-
- *dp++ = palette_lookup[p];
- }
-
- row_info->color_type = PNG_COLOR_TYPE_PALETTE;
- row_info->channels = 1;
- row_info->pixel_depth = row_info->bit_depth;
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
- quantize_lookup)
- {
- sp = row;
-
- for (i = 0; i < row_width; i++, sp++)
- {
- *sp = quantize_lookup[*sp];
- }
- }
- }
-}
-#endif /* PNG_READ_QUANTIZE_SUPPORTED */
-
-/* Transform the row. The order of transformations is significant,
- * and is very touchy. If you add a transformation, take care to
- * decide how it fits in with the other transformations here.
- */
-void /* PRIVATE */
-png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info)
-{
- png_debug(1, "in png_do_read_transformations");
-
- if (png_ptr->row_buf == NULL)
- {
- /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this
- * error is incredibly rare and incredibly easy to debug without this
- * information.
- */
- png_error(png_ptr, "NULL row buffer");
- }
-
- /* The following is debugging; prior to 1.5.4 the code was never compiled in;
- * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro
- * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for
- * all transformations, however in practice the ROW_INIT always gets done on
- * demand, if necessary.
- */
- if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 &&
- !(png_ptr->flags & PNG_FLAG_ROW_INIT))
- {
- /* Application has failed to call either png_read_start_image() or
- * png_read_update_info() after setting transforms that expand pixels.
- * This check added to libpng-1.2.19 (but not enabled until 1.5.4).
- */
- png_error(png_ptr, "Uninitialized row");
- }
-
-#ifdef PNG_READ_EXPAND_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND)
- {
- if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
- {
- png_do_expand_palette(row_info, png_ptr->row_buf + 1,
- png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);
- }
-
- else
- {
- if (png_ptr->num_trans &&
- (png_ptr->transformations & PNG_EXPAND_tRNS))
- png_do_expand(row_info, png_ptr->row_buf + 1,
- &(png_ptr->trans_color));
-
- else
- png_do_expand(row_info, png_ptr->row_buf + 1,
- NULL);
- }
- }
-#endif
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
- if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
- !(png_ptr->transformations & PNG_COMPOSE) &&
- (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
- row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
- png_do_strip_channel(row_info, png_ptr->row_buf + 1,
- 0 /* at_start == false, because SWAP_ALPHA happens later */);
-#endif
-
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- if (png_ptr->transformations & PNG_RGB_TO_GRAY)
- {
- int rgb_error =
- png_do_rgb_to_gray(png_ptr, row_info,
- png_ptr->row_buf + 1);
-
- if (rgb_error != 0)
- {
- png_ptr->rgb_to_gray_status=1;
- if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
- PNG_RGB_TO_GRAY_WARN)
- png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
-
- if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
- PNG_RGB_TO_GRAY_ERR)
- png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
- }
- }
-#endif
-
-/* From Andreas Dilger e-mail to png-implement, 26 March 1998:
- *
- * In most cases, the "simple transparency" should be done prior to doing
- * gray-to-RGB, or you will have to test 3x as many bytes to check if a
- * pixel is transparent. You would also need to make sure that the
- * transparency information is upgraded to RGB.
- *
- * To summarize, the current flow is:
- * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
- * with background "in place" if transparent,
- * convert to RGB if necessary
- * - Gray + alpha -> composite with gray background and remove alpha bytes,
- * convert to RGB if necessary
- *
- * To support RGB backgrounds for gray images we need:
- * - Gray + simple transparency -> convert to RGB + simple transparency,
- * compare 3 or 6 bytes and composite with
- * background "in place" if transparent
- * (3x compare/pixel compared to doing
- * composite with gray bkgrnd)
- * - Gray + alpha -> convert to RGB + alpha, composite with background and
- * remove alpha bytes (3x float
- * operations/pixel compared with composite
- * on gray background)
- *
- * Greg's change will do this. The reason it wasn't done before is for
- * performance, as this increases the per-pixel operations. If we would check
- * in advance if the background was gray or RGB, and position the gray-to-RGB
- * transform appropriately, then it would save a lot of work/time.
- */
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
- /* If gray -> RGB, do so now only if background is non-gray; else do later
- * for performance reasons
- */
- if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
- !(png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY))
- png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
-#endif
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- if (png_ptr->transformations & PNG_COMPOSE)
- png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- if ((png_ptr->transformations & PNG_GAMMA) &&
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- /* Because RGB_TO_GRAY does the gamma transform. */
- !(png_ptr->transformations & PNG_RGB_TO_GRAY) &&
-#endif
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- /* Because PNG_COMPOSE does the gamma transform if there is something to
- * do (if there is an alpha channel or transparency.)
- */
- !((png_ptr->transformations & PNG_COMPOSE) &&
- ((png_ptr->num_trans != 0) ||
- (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
-#endif
- /* Because png_init_read_transformations transforms the palette, unless
- * RGB_TO_GRAY will do the transform.
- */
- (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
- png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
- if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
- (png_ptr->transformations & PNG_COMPOSE) &&
- (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
- row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
- png_do_strip_channel(row_info, png_ptr->row_buf + 1,
- 0 /* at_start == false, because SWAP_ALPHA happens later */);
-#endif
-
-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
- if ((png_ptr->transformations & PNG_ENCODE_ALPHA) &&
- (row_info->color_type & PNG_COLOR_MASK_ALPHA))
- png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr);
-#endif
-
-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
- if (png_ptr->transformations & PNG_SCALE_16_TO_8)
- png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
- /* There is no harm in doing both of these because only one has any effect,
- * by putting the 'scale' option first if the app asks for scale (either by
- * calling the API or in a TRANSFORM flag) this is what happens.
- */
- if (png_ptr->transformations & PNG_16_TO_8)
- png_do_chop(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
- if (png_ptr->transformations & PNG_QUANTIZE)
- {
- png_do_quantize(row_info, png_ptr->row_buf + 1,
- png_ptr->palette_lookup, png_ptr->quantize_index);
-
- if (row_info->rowbytes == 0)
- png_error(png_ptr, "png_do_quantize returned rowbytes=0");
- }
-#endif /* PNG_READ_QUANTIZE_SUPPORTED */
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
- /* Do the expansion now, after all the arithmetic has been done. Notice
- * that previous transformations can handle the PNG_EXPAND_16 flag if this
- * is efficient (particularly true in the case of gamma correction, where
- * better accuracy results faster!)
- */
- if (png_ptr->transformations & PNG_EXPAND_16)
- png_do_expand_16(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
- /* NOTE: moved here in 1.5.4 (from much later in this list.) */
- if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
- (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY))
- png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_INVERT_SUPPORTED
- if (png_ptr->transformations & PNG_INVERT_MONO)
- png_do_invert(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_INVERT_ALPHA)
- png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_SHIFT_SUPPORTED
- if (png_ptr->transformations & PNG_SHIFT)
- png_do_unshift(row_info, png_ptr->row_buf + 1,
- &(png_ptr->shift));
-#endif
-
-#ifdef PNG_READ_PACK_SUPPORTED
- if (png_ptr->transformations & PNG_PACK)
- png_do_unpack(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
- /* Added at libpng-1.5.10 */
- if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
- png_ptr->num_palette_max >= 0)
- png_do_check_palette_indexes(png_ptr, row_info);
-#endif
-
-#ifdef PNG_READ_BGR_SUPPORTED
- if (png_ptr->transformations & PNG_BGR)
- png_do_bgr(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_PACKSWAP_SUPPORTED
- if (png_ptr->transformations & PNG_PACKSWAP)
- png_do_packswap(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_FILLER_SUPPORTED
- if (png_ptr->transformations & PNG_FILLER)
- png_do_read_filler(row_info, png_ptr->row_buf + 1,
- (png_uint_32)png_ptr->filler, png_ptr->flags);
-#endif
-
-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_SWAP_ALPHA)
- png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_READ_16BIT_SUPPORTED
-#ifdef PNG_READ_SWAP_SUPPORTED
- if (png_ptr->transformations & PNG_SWAP_BYTES)
- png_do_swap(row_info, png_ptr->row_buf + 1);
-#endif
-#endif
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- if (png_ptr->transformations & PNG_USER_TRANSFORM)
- {
- if (png_ptr->read_user_transform_fn != NULL)
- (*(png_ptr->read_user_transform_fn)) /* User read transform function */
- (png_ptr, /* png_ptr */
- row_info, /* row_info: */
- /* png_uint_32 width; width of row */
- /* png_size_t rowbytes; number of bytes in row */
- /* png_byte color_type; color type of pixels */
- /* png_byte bit_depth; bit depth of samples */
- /* png_byte channels; number of channels (1-4) */
- /* png_byte pixel_depth; bits per pixel (depth*channels) */
- png_ptr->row_buf + 1); /* start of pixel data for row */
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
- if (png_ptr->user_transform_depth)
- row_info->bit_depth = png_ptr->user_transform_depth;
-
- if (png_ptr->user_transform_channels)
- row_info->channels = png_ptr->user_transform_channels;
-#endif
- row_info->pixel_depth = (png_byte)(row_info->bit_depth *
- row_info->channels);
-
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width);
- }
-#endif
-}
-#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
-#endif /* PNG_READ_SUPPORTED */
+#endif /* READ_TRANSFORMS */
diff --git a/ext/libpng17/pngrutil.c b/ext/libpng17/pngrutil.c
index 2a9fc896bf..5f94385545 100644
--- a/ext/libpng17/pngrutil.c
+++ b/ext/libpng17/pngrutil.c
@@ -1,8 +1,12 @@
+#ifdef _MSC_VER
+#pragma warning (disable:4018)
+#pragma warning (disable:4146)
+#endif
/* pngrutil.c - utilities to read a PNG file
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2017 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.)
*
@@ -15,6 +19,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngrutil
#ifdef PNG_READ_SUPPORTED
@@ -76,7 +81,7 @@ png_get_uint_16)(png_const_bytep buf)
{
return PNG_U16(buf[0], buf[1]);
}
-#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */
+#endif /* READ_INT_FUNCTIONS */
/* This is an exported function however its error handling is too harsh for most
* internal use. For example if it were used for reading the chunk parameters
@@ -126,48 +131,9 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
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)
+png_crc_read(png_structrp png_ptr, png_voidp buf, png_uint_32 length)
{
if (png_ptr == NULL)
return;
@@ -176,10 +142,10 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 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.
+/* 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 true
+ * if the chunk should be discarded, otherwise false.
*/
int /* PRIVATE */
png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
@@ -200,62 +166,37 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
png_crc_read(png_ptr, tmpbuf, len);
}
- if (png_crc_error(png_ptr))
+ /* Compare the CRC stored in the PNG file with that calculated by libpng from
+ * the data it has read thus far. Do any required error handling. The
+ * second parameter is to allow a critical chunk (specifically PLTE) to be
+ * treated as ancillary.
+ */
{
- if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) ?
- !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) :
- (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))
+ png_byte crc_bytes[4];
+
+# ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
+# endif
+
+ png_read_data(png_ptr, crc_bytes, 4);
+
+ if (png_ptr->current_crc != crc_quiet_use &&
+ png_get_uint_32(crc_bytes) != png_ptr->crc)
{
- png_chunk_warning(png_ptr, "CRC error");
+ if (png_ptr->current_crc == crc_error_quit)
+ png_chunk_error(png_ptr, "CRC");
+
+ else
+ png_chunk_warning(png_ptr, "CRC");
+
+ /* The only way to discard a chunk at present is to issue a warning.
+ * TODO: quiet_discard.
+ */
+ return png_ptr->current_crc == crc_warn_discard;
}
-
- 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))
- {
- 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)
- 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);
+ return 0;
}
#if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
@@ -268,14 +209,13 @@ png_crc_error(png_structrp png_ptr)
* it will call png_error (via png_malloc) on failure. (warn == 2 means
* 'silent').
*/
-static png_bytep
+png_bytep /* PRIVATE */
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);
@@ -295,16 +235,16 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
else if (warn < 2) /* else silent */
{
if (warn != 0)
- png_chunk_warning(png_ptr, "insufficient memory to read chunk");
+ png_chunk_warning(png_ptr, "insufficient memory to read chunk");
else
- png_chunk_error(png_ptr, "insufficient memory to read chunk");
+ png_chunk_error(png_ptr, "insufficient memory to read chunk");
}
}
return buffer;
}
-#endif /* PNG_READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */
+#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
@@ -324,12 +264,12 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
* are minimal.
*/
(void)png_safecat(msg, (sizeof msg), 4, " using zstream");
-# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
- png_chunk_warning(png_ptr, msg);
- png_ptr->zowner = 0;
-# else
- png_chunk_error(png_ptr, msg);
-# endif
+#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
@@ -347,22 +287,22 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
*/
{
int ret; /* zlib return code */
-# if PNG_ZLIB_VERNUM >= 0x1240
+#if ZLIB_VERNUM >= 0x1240
+ int window_bits = 0;
-# if defined(PNG_SET_OPTION_SUPPORTED) && \
- defined(PNG_MAXIMUM_INFLATE_WINDOW)
- int window_bits;
+# if defined(PNG_SET_OPTION_SUPPORTED) && \
+ defined(PNG_MAXIMUM_INFLATE_WINDOW)
- if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
- PNG_OPTION_ON)
+ if (png_ptr->maximum_inflate_window)
window_bits = 15;
- else
- window_bits = 0;
-# else
-# define window_bits 0
-# endif
-# endif
+# endif
+#endif /* ZLIB_VERNUM >= 0x1240 */
+
+ /* Initialize the alloc/free callbacks every time: */
+ png_ptr->zstream.zalloc = png_zalloc;
+ png_ptr->zstream.zfree = png_zfree;
+ png_ptr->zstream.opaque = png_ptr;
/* Set this for safety, just in case the previous owner left pointers to
* memory allocations.
@@ -372,32 +312,44 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
png_ptr->zstream.next_out = NULL;
png_ptr->zstream.avail_out = 0;
- if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
+ /* If png_struct::zstream has been used before for decompression it does
+ * not need to be re-initialized, just reset.
+ */
+ if (png_ptr->zstream.state != NULL)
{
-# if PNG_ZLIB_VERNUM < 0x1240
- ret = inflateReset(&png_ptr->zstream);
-# else
- ret = inflateReset2(&png_ptr->zstream, window_bits);
-# endif
+#if ZLIB_VERNUM >= 0x1240
+ ret = inflateReset2(&png_ptr->zstream, window_bits);
+#else
+ ret = inflateReset(&png_ptr->zstream);
+#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 ZLIB_VERNUM >= 0x1240
+ ret = inflateInit2(&png_ptr->zstream, window_bits);
+#else
+ ret = inflateInit(&png_ptr->zstream);
+#endif
}
- if (ret == Z_OK)
+#if ZLIB_VERNUM >= 0x1240
+ /* Turn off validation of the ADLER32 checksum */
+ if (png_ptr->current_crc == crc_quiet_use)
+ ret = inflateReset2(&png_ptr->zstream, -window_bits);
+#endif
+
+ if (ret == Z_OK && png_ptr->zstream.state != NULL)
+ {
png_ptr->zowner = owner;
+ png_ptr->zstream_ended = 0;
+ }
else
- png_zstream_error(png_ptr, ret);
+ {
+ png_zstream_error(&png_ptr->zstream, ret);
+ png_ptr->zstream_ended = 1;
+ }
return ret;
}
@@ -407,28 +359,27 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
# 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.
+/* This is a wrapper for the zlib deflate call which will handle larger buffer
+ * sizes than uInt. The input is limited to png_uint_32, because invariably
+ * the input comes from a chunk which has a 31-bit length, the output can be
+ * anything that fits in a png_alloc_size_t.
*
- * 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).
+ * This internal function sets png_struct::zstream_ended when the end of the
+ * decoded data has been encountered; this includes both a normal end and
+ * error conditions.
*/
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)
+png_zlib_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
+ /* INPUT: */ png_const_bytep *next_in_ptr, png_uint_32p avail_in_ptr,
+ /* OUTPUT: */ png_bytep *next_out_ptr, png_alloc_size_t *avail_out_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;
+ png_alloc_size_t avail_out = *avail_out_ptr;
+ png_uint_32 avail_in = *avail_in_ptr;
+ png_bytep output = *next_out_ptr;
+ png_const_bytep input = *next_in_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
@@ -464,7 +415,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
* 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)
@@ -475,7 +425,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
/* 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)
@@ -501,7 +450,7 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
* end of the output buffer.
*/
ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH :
- (finish ? Z_FINISH : Z_SYNC_FLUSH));
+ (finish ? Z_FINISH : Z_SYNC_FLUSH));
} while (ret == Z_OK);
/* For safety kill the local buffer pointer now */
@@ -515,14 +464,21 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
/* 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;
+ *avail_out_ptr = avail_out;
+ if (output != NULL)
+ *next_out_ptr = png_ptr->zstream.next_out;
- if (avail_in > 0)
- *input_size_ptr -= avail_in;
+ *avail_in_ptr = avail_in;
+ *next_in_ptr = png_ptr->zstream.next_in;
- /* Ensure png_ptr->zstream.msg is set (even in the success case!) */
- png_zstream_error(png_ptr, ret);
+ /* Ensure png_ptr->zstream.msg is set, ret can't be Z_OK at this point.
+ */
+ debug(ret != Z_OK);
+
+ if (ret != Z_BUF_ERROR)
+ png_ptr->zstream_ended = 1;
+
+ png_zstream_error(&png_ptr->zstream, ret);
return ret;
}
@@ -530,15 +486,44 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
{
/* 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!
+ * used on errors! (The {next,avail}_{in,out} values are not changed.)
*/
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
+#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)
+{
+ png_uint_32 avail_in = *input_size_ptr;
+ png_alloc_size_t avail_out = *output_size_ptr;
+ int ret = png_zlib_inflate(png_ptr, owner, finish,
+ &input, &avail_in, &output, &avail_out);
+
+ /* And implement the non-zlib semantics (the size values are updated to the
+ * amounts consumed and written, not the amount remaining.)
+ */
+ *input_size_ptr -= avail_in;
+ *output_size_ptr -= avail_out;
+ return ret;
+}
+
+/* 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
@@ -546,9 +531,9 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
*/
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*/)
+ 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.
*
@@ -559,14 +544,14 @@ png_decompress_chunk(png_structrp png_ptr,
*/
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
+#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))
{
@@ -585,8 +570,8 @@ png_decompress_chunk(png_structrp png_ptr,
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);
+ /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
+ /* output: */ NULL, newlength);
if (ret == Z_STREAM_END)
{
@@ -606,15 +591,15 @@ png_decompress_chunk(png_structrp png_ptr,
*/
png_alloc_size_t new_size = *newlength;
png_alloc_size_t buffer_size = prefix_size + new_size +
- (terminate != 0);
+ (terminate != 0);
png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr,
- buffer_size));
+ 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);
+ png_ptr->read_buffer + prefix_size, &lzsize,
+ text + prefix_size, newlength);
if (ret == Z_STREAM_END)
{
@@ -659,7 +644,7 @@ png_decompress_chunk(png_structrp png_ptr,
* the extra space may otherwise be used as a Trojan Horse.
*/
if (ret == Z_STREAM_END &&
- chunklength - prefix_size != lzsize)
+ chunklength - prefix_size != lzsize)
png_chunk_benign_error(png_ptr, "extra compressed data");
}
@@ -667,14 +652,14 @@ png_decompress_chunk(png_structrp png_ptr,
{
/* Out of memory allocating the buffer */
ret = Z_MEM_ERROR;
- png_zstream_error(png_ptr, Z_MEM_ERROR);
+ png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR);
}
}
else
{
/* inflateReset failed, store the error message */
- png_zstream_error(png_ptr, ret);
+ png_zstream_error(&png_ptr->zstream, ret);
if (ret == Z_STREAM_END)
ret = PNG_UNEXPECTED_ZLIB_RETURN;
@@ -697,11 +682,11 @@ png_decompress_chunk(png_structrp png_ptr,
else
{
/* Application/configuration limits exceeded */
- png_zstream_error(png_ptr, Z_MEM_ERROR);
+ png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR);
return Z_MEM_ERROR;
}
}
-#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
+#endif /* READ_COMPRESSED_TEXT */
#ifdef PNG_READ_iCCP_SUPPORTED
/* Perform a partial read and decompress, producing 'avail_out' bytes and
@@ -709,8 +694,8 @@ png_decompress_chunk(png_structrp png_ptr,
*/
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)
+ 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)
{
@@ -750,7 +735,8 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
* streams.
*/
ret = inflate(&png_ptr->zstream,
- *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
+ *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));
@@ -758,7 +744,7 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
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);
+ png_zstream_error(&png_ptr->zstream, ret);
return ret;
}
@@ -768,27 +754,63 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
return Z_STREAM_ERROR;
}
}
-#endif
+#endif /* READ_iCCP */
+
+/* Chunk handling error handlers and utilities: */
+/* Utility to read the chunk data from the start without processing it;
+ * a skip function.
+ */
+static void
+png_handle_skip(png_structrp png_ptr)
+ /* Skip the entire chunk after the name,length header has been read: */
+{
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+}
+
+static void
+png_handle_error(png_structrp png_ptr
+# ifdef PNG_ERROR_TEXT_SUPPORTED
+ , png_const_charp error
+# else
+# define png_handle_error(pp,e) png_handle_error(pp)
+# endif
+ )
+ /* Handle an error detected immediately after the chunk header has been
+ * read; this skips the rest of the chunk data and the CRC then signals
+ * a *benign* chunk error.
+ */
+{
+ png_handle_skip(png_ptr);
+ png_chunk_benign_error(png_ptr, error);
+}
+
+#if defined (PNG_READ_gAMA_SUPPORTED) || defined (PNG_READ_sBIT_SUPPORTED) ||\
+ defined (PNG_READ_cHRM_SUPPORTED) || defined (PNG_READ_sRGB_SUPPORTED) ||\
+ defined (PNG_READ_iCCP_SUPPORTED) || defined (PNG_READ_tRNS_SUPPORTED) ||\
+ defined (PNG_READ_bKGD_SUPPORTED) || defined (PNG_READ_hIST_SUPPORTED) ||\
+ defined (PNG_READ_pHYs_SUPPORTED) || defined (PNG_READ_oFFs_SUPPORTED) ||\
+ defined (PNG_READ_sCAL_SUPPORTED) || defined (PNG_READ_tIME_SUPPORTED)
+static void
+png_handle_bad_length(png_structrp png_ptr)
+{
+ png_handle_error(png_ptr, "invalid length");
+}
+#endif /* chunks that can generate length errors */
/* Read and check the IDHR chunk */
-void /* PRIVATE */
-png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte buf[13];
png_uint_32 width, height;
- int bit_depth, color_type, compression_type, filter_type;
- int interlace_type;
+ png_byte bit_depth, color_type, compression_type, filter_method;
+ png_byte interlace_type;
png_debug(1, "in png_handle_IHDR");
- if (png_ptr->mode & PNG_HAVE_IHDR)
- 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;
+ /* Check the length (this is a chunk error; not benign) */
+ if (png_ptr->chunk_length != 13)
+ png_chunk_error(png_ptr, "invalid length");
png_crc_read(png_ptr, buf, 13);
png_crc_finish(png_ptr, 0);
@@ -798,99 +820,45 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
bit_depth = buf[8];
color_type = buf[9];
compression_type = buf[10];
- filter_type = buf[11];
+ filter_method = 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;
+ png_ptr->bit_depth = bit_depth;
+ png_ptr->interlaced = interlace_type;
+ png_ptr->color_type = color_type;
+ png_ptr->filter_method = filter_method;
- /* 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);
+ color_type, interlace_type, compression_type, filter_method);
}
/* Read and check the palette */
-void /* PRIVATE */
-png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr)
{
png_color palette[PNG_MAX_PALETTE_LENGTH];
- int num, i;
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
- png_colorp pal_ptr;
-#endif
+ png_uint_32 length = png_ptr->chunk_length;
+ png_uint_32 max_palette_length, num, i;
png_debug(1, "in png_handle_PLTE");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- 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)
- png_chunk_error(png_ptr, "duplicate");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
- {
- /* 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");
+ if (info_ptr == NULL)
return;
- }
-
- png_ptr->mode |= PNG_HAVE_PLTE;
if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
+ png_handle_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);
+ /* Skip the whole chunk: */
+ png_handle_skip(png_ptr);
return;
}
#endif
@@ -898,93 +866,52 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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");
-
+ png_chunk_report(png_ptr, "invalid length",
+ ((png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) ? PNG_CHUNK_ERROR :
+ PNG_CHUNK_FATAL));
return;
}
/* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */
- num = (int)length / 3;
+ num = length / 3U;
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
- for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
+ /* If the palette has 256 or fewer entries but is too large for the bit
+ * depth, we don't issue an error, to preserve the behavior of previous
+ * libpng versions. We silently truncate the unused extra palette entries
+ * here.
+ */
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ max_palette_length = (1U << png_ptr->bit_depth);
+ else
+ max_palette_length = PNG_MAX_PALETTE_LENGTH;
+
+ if (num > max_palette_length)
+ num = max_palette_length;
+
+ for (i = 0; i < num; ++i)
{
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)) /* 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))
- {
- if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)
- 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))
- {
- 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_crc_finish(png_ptr, length - num * 3U);
png_set_PLTE(png_ptr, info_ptr, palette, num);
+ /* Ok, make our own copy since the set succeeded: */
+ debug(png_ptr->palette == NULL); /* should only get set once */
+ png_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr,
+ sizeof (png_color[PNG_MAX_PALETTE_LENGTH])));
+ /* This works because we know png_set_PLTE also expands the palette to the
+ * full size:
+ */
+ memcpy(png_ptr->palette, info_ptr->palette,
+ sizeof (png_color[PNG_MAX_PALETTE_LENGTH]));
+ png_ptr->num_palette = info_ptr->num_palette;
+
/* 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
@@ -996,73 +923,56 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
*/
#ifdef PNG_READ_tRNS_SUPPORTED
if (png_ptr->num_trans > 0 ||
- (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
+ (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;
+ info_ptr->num_trans = 0;
png_chunk_benign_error(png_ptr, "tRNS must be after");
}
-#endif
+#endif /* READ_tRNS */
#ifdef PNG_READ_hIST_SUPPORTED
- if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
+ if ((info_ptr->valid & PNG_INFO_hIST) != 0)
png_chunk_benign_error(png_ptr, "hIST must be after");
-#endif
+#endif /* READ_hIST */
#ifdef PNG_READ_bKGD_SUPPORTED
- if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
+ if ((info_ptr->valid & PNG_INFO_bKGD) != 0)
png_chunk_benign_error(png_ptr, "bKGD must be after");
-#endif
+#endif /* READ_bKGD */
}
-void /* PRIVATE */
-png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr)
{
png_debug(1, "in png_handle_IEND");
- if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
- png_chunk_error(png_ptr, "out of place");
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
- 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");
+ /* Treat this as benign and terminate the PNG anyway: */
+ if (png_ptr->chunk_length != 0)
+ png_chunk_benign_error(png_ptr, "invalid length");
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)
+static void
+png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr)
{
png_fixed_point igamma;
png_byte buf[4];
png_debug(1, "in png_handle_gAMA");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
+ if (png_ptr->chunk_length != 4)
{
- 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");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1076,11 +986,13 @@ png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma);
png_colorspace_sync(png_ptr, info_ptr);
}
-#endif
+#else
+# define png_handle_gAMA NULL
+#endif /* READ_gAMA */
#ifdef PNG_READ_sBIT_SUPPORTED
-void /* PRIVATE */
-png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr)
{
unsigned int truelen, i;
png_byte sample_depth;
@@ -1088,20 +1000,9 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(1, "in png_handle_sBIT");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
- {
- 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))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ png_handle_error(png_ptr, "duplicate");
return;
}
@@ -1113,14 +1014,14 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else
{
- truelen = png_ptr->channels;
+ truelen = PNG_CHANNELS(*png_ptr);
sample_depth = png_ptr->bit_depth;
+ affirm(truelen <= 4);
}
- if (length != truelen || length > 4)
+ if (png_ptr->chunk_length != truelen)
{
- png_chunk_benign_error(png_ptr, "invalid");
- png_crc_finish(png_ptr, length);
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1156,31 +1057,22 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
}
-#endif
+#else
+# define png_handle_sBIT NULL
+#endif /* READ_sBIT */
#ifdef PNG_READ_cHRM_SUPPORTED
-void /* PRIVATE */
-png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte buf[32];
png_xy xy;
png_debug(1, "in png_handle_cHRM");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
+ if (png_ptr->chunk_length != 32)
{
- 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");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1207,7 +1099,7 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
xy.bluex == PNG_FIXED_ERROR ||
xy.bluey == PNG_FIXED_ERROR)
{
- png_chunk_benign_error(png_ptr, "invalid values");
+ png_chunk_benign_error(png_ptr, "invalid");
return;
}
@@ -1225,33 +1117,24 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
(void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
- 1/*prefer cHRM values*/);
+ 1/*prefer cHRM values*/);
png_colorspace_sync(png_ptr, info_ptr);
}
-#endif
+#else
+# define png_handle_cHRM NULL
+#endif /* READ_cHRM */
#ifdef PNG_READ_sRGB_SUPPORTED
-void /* PRIVATE */
-png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte intent;
png_debug(1, "in png_handle_sRGB");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
+ if (png_ptr->chunk_length != 1)
{
- 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");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1278,45 +1161,37 @@ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
(void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
png_colorspace_sync(png_ptr, info_ptr);
}
-#endif /* PNG_READ_sRGB_SUPPORTED */
+#else
+# define png_handle_sRGB NULL
+#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)
+static void
+png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr)
/* Note: this does not properly handle profiles that are > 64K under DOS */
{
png_const_charp errmsg = NULL; /* error message output, or no error */
+ png_uint_32 length = png_ptr->chunk_length;
int finished = 0; /* crc checked */
png_debug(1, "in png_handle_iCCP");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
- {
- 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 one character and there is a
- * terminator (0) byte and the compression method.
+ * 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");
+ png_handle_bad_length(png_ptr);
return;
}
/* If a colorspace error has already been output skip this chunk */
- if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
+ if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
{
png_crc_finish(png_ptr, length);
return;
@@ -1335,7 +1210,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
*/
read_length = 81; /* maximum */
if (read_length > length)
- read_length = (uInt)length;
+ read_length = (uInt)/*SAFE*/length;
png_crc_read(png_ptr, (png_bytep)keyword, read_length);
length -= read_length;
@@ -1365,8 +1240,8 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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*/);
+ (sizeof local_buffer), &length, profile_header, &size,
+ 0/*finish: don't, because the output is too small*/);
if (size == 0)
{
@@ -1376,14 +1251,14 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_get_uint_32(profile_header);
if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
- keyword, profile_length))
+ keyword, profile_length))
{
/* 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))
+ keyword, profile_length, profile_header,
+ (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0))
{
/* Now read the tag table; a variable size buffer is
* needed at this point, allocate one for the whole
@@ -1391,20 +1266,20 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
* that none of these stuff will overflow.
*/
const png_uint_32 tag_count = png_get_uint_32(
- profile_header+128);
+ profile_header+128);
png_bytep profile = png_read_buffer(png_ptr,
- profile_length, 2/*silent*/);
+ profile_length, 2/*silent*/);
if (profile != NULL)
{
memcpy(profile, profile_header,
- (sizeof 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);
+ (sizeof local_buffer), &length,
+ profile + (sizeof profile_header), &size, 0);
/* Still expect a buffer error because we expect
* there to be some tag data!
@@ -1412,22 +1287,26 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (size == 0)
{
if (png_icc_check_tag_table(png_ptr,
- &png_ptr->colorspace, keyword, profile_length,
- profile))
+ &png_ptr->colorspace, keyword, profile_length,
+ profile))
{
/* The profile has been validated for basic
* security issues, so read the whole thing in.
*/
size = profile_length - (sizeof profile_header)
- - 12 * tag_count;
+ - 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*/);
+ (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))
+ if (length > 0
+# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
+ && png_ptr->benign_error_action ==
+ PNG_ERROR
+# endif /* BENIGN_READ_ERRORS */
+ )
errmsg = "extra compressed data";
/* But otherwise allow extra data: */
@@ -1439,34 +1318,32 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
* keep going.
*/
png_chunk_warning(png_ptr,
- "extra compressed data");
+ "extra compressed data");
}
png_crc_finish(png_ptr, length);
finished = 1;
-# ifdef PNG_sRGB_SUPPORTED
+# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0
/* Check for a match against sRGB */
png_icc_set_sRGB(png_ptr,
- &png_ptr->colorspace, profile,
- png_ptr->zstream.adler);
-# endif
+ &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);
+ PNG_FREE_ICCP, 0);
info_ptr->iccp_name = png_voidcast(char*,
- png_malloc_base(png_ptr,
- keyword_length+1));
+ 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;
+ keyword_length+1);
info_ptr->iccp_profile = profile;
png_ptr->read_buffer = NULL; /*steal*/
info_ptr->free_me |= PNG_FREE_ICCP;
@@ -1476,7 +1353,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else
{
png_ptr->colorspace.flags |=
- PNG_COLORSPACE_INVALID;
+ PNG_COLORSPACE_INVALID;
errmsg = "out of memory";
}
}
@@ -1498,9 +1375,6 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else if (size > 0)
errmsg = "truncated";
-
- else
- errmsg = png_ptr->zstream.msg;
}
/* else png_icc_check_tag_table output an error */
@@ -1551,13 +1425,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (errmsg != NULL) /* else already output */
png_chunk_benign_error(png_ptr, errmsg);
}
-#endif /* PNG_READ_iCCP_SUPPORTED */
+#else
+# define png_handle_iCCP NULL
+#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)
+static void
+png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr)
/* Note: this does not properly handle chunks that are > 64K under DOS */
{
+ png_uint_32 length = png_ptr->chunk_length;
png_bytep entry_start, buffer;
png_sPLT_t new_palette;
png_sPLT_entryp pp;
@@ -1581,31 +1458,12 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (--png_ptr->user_chunk_cache_max == 1)
{
/* Warn the first time */
- png_chunk_benign_error(png_ptr, "chunk cache full");
+ png_chunk_benign_error(png_ptr, "no space in chunk cache");
png_crc_finish(png_ptr, length);
return;
}
}
-#endif
-
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
- {
- 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
+#endif /* USER_LIMITS */
buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
if (buffer == NULL)
@@ -1615,7 +1473,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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.
@@ -1633,9 +1490,9 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
++entry_start;
/* A sample depth should follow the separator, and we should be on it */
- if (entry_start > buffer + length - 2)
+ if (length < 2U || entry_start > buffer + (length - 2U))
{
- png_warning(png_ptr, "malformed sPLT chunk");
+ png_chunk_benign_error(png_ptr, "malformed");
return;
}
@@ -1649,7 +1506,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
/* Integrity-check the data length */
if (data_length % entry_size)
{
- png_warning(png_ptr, "sPLT chunk has bad length");
+ png_chunk_benign_error(png_ptr, "invalid length");
return;
}
@@ -1658,22 +1515,21 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (dl > max_dl)
{
- png_warning(png_ptr, "sPLT chunk too long");
+ png_chunk_benign_error(png_ptr, "exceeds system limits");
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)));
+ new_palette.entries = png_voidcast(png_sPLT_entryp, png_malloc_base(
+ png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry))));
if (new_palette.entries == NULL)
{
- png_warning(png_ptr, "sPLT chunk requires too much memory");
+ png_chunk_benign_error(png_ptr, "out of memory");
return;
}
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
for (i = 0; i < new_palette.nentries; i++)
{
pp = new_palette.entries + i;
@@ -1696,31 +1552,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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;
@@ -1729,30 +1560,30 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_free(png_ptr, new_palette.entries);
}
-#endif /* PNG_READ_sPLT_SUPPORTED */
+#else
+# define png_handle_sPLT NULL
+#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)
+static void
+png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr)
{
+ png_uint_32 num_trans;
png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
png_debug(1, "in png_handle_tRNS");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
+ png_ptr->num_trans = 0U; /* safety */
- else if (png_ptr->mode & PNG_HAVE_IDAT)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
{
- 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))
- {
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ /* libpng 1.7.0: this used to be a benign error, but it doesn't look very
+ * benign because it has security implications; libpng ignores the second
+ * tRNS, so if you can find something that ignores the first instead you
+ * can choose which image the user sees depending on the PNG decoder.
+ */
+ png_crc_finish(png_ptr, png_ptr->chunk_length);
+ png_chunk_error(png_ptr, "duplicate");
return;
}
@@ -1760,15 +1591,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
png_byte buf[2];
- if (length != 2)
+ if (png_ptr->chunk_length != 2)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
png_crc_read(png_ptr, buf, 2);
- png_ptr->num_trans = 1;
+ num_trans = 1U;
png_ptr->trans_color.gray = png_get_uint_16(buf);
}
@@ -1776,15 +1606,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
{
png_byte buf[6];
- if (length != 6)
+ if (png_ptr->chunk_length != 6)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
- png_crc_read(png_ptr, buf, length);
- png_ptr->num_trans = 1;
+ png_crc_read(png_ptr, buf, 6);
+ num_trans = 1U;
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);
@@ -1792,53 +1621,51 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
- if (!(png_ptr->mode & PNG_HAVE_PLTE))
+ /* png_find_chunk_op checks this: */
+ debug(png_ptr->mode & PNG_HAVE_PLTE);
+
+ num_trans = png_ptr->chunk_length;
+
+ if (num_trans > png_ptr->num_palette || num_trans == 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");
+ png_handle_bad_length(png_ptr);
return;
}
- if (length > png_ptr->num_palette || length > 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;
+ png_crc_read(png_ptr, readbuf, num_trans);
}
else
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid with alpha channel");
+ png_handle_error(png_ptr, "invalid");
return;
}
if (png_crc_finish(png_ptr, 0))
- {
- png_ptr->num_trans = 0;
return;
+
+ /* Set it into the info_struct: */
+ png_set_tRNS(png_ptr, info_ptr, readbuf, num_trans, &png_ptr->trans_color);
+
+ /* Now make a copy of the buffer if one is required (palette images). */
+ debug(png_ptr->trans_alpha == NULL);
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_ptr->trans_alpha = png_voidcast(png_bytep,
+ png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
+ memset(png_ptr->trans_alpha, 0xFFU, PNG_MAX_PALETTE_LENGTH);
+ memcpy(png_ptr->trans_alpha, info_ptr->trans_alpha, num_trans);
}
- /* 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));
-
- png_ptr->trans_alpha = info_ptr->trans_alpha;
+ png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9);
}
-#endif
+#else
+# define png_handle_tRNS NULL
+#endif /* READ_tRNS */
#ifdef PNG_READ_bKGD_SUPPORTED
-void /* PRIVATE */
-png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr)
{
unsigned int truelen;
png_byte buf[6];
@@ -1846,22 +1673,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(1, "in png_handle_bKGD");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if ((png_ptr->mode & PNG_HAVE_IDAT) ||
- (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
- !(png_ptr->mode & PNG_HAVE_PLTE)))
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD))
{
- 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))
- {
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ png_handle_error(png_ptr, "duplicate");
return;
}
@@ -1874,10 +1688,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else
truelen = 2;
- if (length != truelen)
+ if (png_ptr->chunk_length != truelen)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1903,9 +1716,11 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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;
+ background.red = png_check_u16(png_ptr, png_ptr->palette[buf[0]].red);
+ background.green =
+ png_check_u16(png_ptr, png_ptr->palette[buf[0]].green);
+ background.blue =
+ png_check_u16(png_ptr, png_ptr->palette[buf[0]].blue);
}
else
@@ -1934,40 +1749,30 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_set_bKGD(png_ptr, info_ptr, &background);
}
-#endif
+#else
+# define png_handle_bKGD NULL
+#endif /* READ_bKGD */
#ifdef PNG_READ_hIST_SUPPORTED
-void /* PRIVATE */
-png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr)
{
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))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE))
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "out of place");
+ png_handle_error(png_ptr, "duplicate");
return;
}
- else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
- {
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
- return;
- }
+ num = png_ptr->chunk_length / 2;
- num = length / 2 ;
-
- if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH)
+ if (num != png_ptr->num_palette || 2*num != png_ptr->chunk_length)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -1984,11 +1789,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_set_hIST(png_ptr, info_ptr, readbuf);
}
-#endif
+#else
+# define png_handle_hIST NULL
+#endif /* READ_hIST */
#ifdef PNG_READ_pHYs_SUPPORTED
-void /* PRIVATE */
-png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte buf[9];
png_uint_32 res_x, res_y;
@@ -1996,27 +1803,15 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(1, "in png_handle_pHYs");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "out of place");
+ png_handle_error(png_ptr, "duplicate");
return;
}
- else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ if (png_ptr->chunk_length != 9)
{
- 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");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -2030,11 +1825,13 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
unit_type = buf[8];
png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
}
-#endif
+#else
+# define png_handle_pHYs NULL
+#endif /* READ_pHYs */
-#ifdef PNG_READ_oFFs_SUPPORTED
-void /* PRIVATE */
-png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+#ifdef PNG_READ_oFFs_SUPPORTED /* EXTENSION, before IDAT, no duplicates */
+static void
+png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte buf[9];
png_int_32 offset_x, offset_y;
@@ -2042,27 +1839,15 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(1, "in png_handle_oFFs");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "out of place");
+ png_handle_error(png_ptr, "duplicate");
return;
}
- else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ if (png_ptr->chunk_length != 9)
{
- 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");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -2076,12 +1861,13 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
unit_type = buf[8];
png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
}
-#endif
+#else
+# define png_handle_oFFs NULL
+#endif /* READ_oFFs */
-#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)
+#ifdef PNG_READ_pCAL_SUPPORTED /* EXTENSION: before IDAT, no duplicates */
+static void
+png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr)
{
png_int_32 X0, X1;
png_byte type, nparams;
@@ -2091,52 +1877,40 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(1, "in png_handle_pCAL");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL))
{
- 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))
- {
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ png_handle_error(png_ptr, "duplicate");
return;
}
png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
- length + 1);
+ png_ptr->chunk_length + 1);
- buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
+ buffer = png_read_buffer(png_ptr, png_ptr->chunk_length+1, 2/*silent*/);
if (buffer == NULL)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "out of memory");
+ png_handle_error(png_ptr, "out of memory");
return;
}
- png_crc_read(png_ptr, buffer, length);
+ png_crc_read(png_ptr, buffer, png_ptr->chunk_length);
if (png_crc_finish(png_ptr, 0))
return;
- buffer[length] = 0; /* Null terminate the last string */
+ buffer[png_ptr->chunk_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;
+ endptr = buffer + png_ptr->chunk_length;
/* We need to have at least 12 bytes after the purpose string
* in order to get the parameter information.
*/
- if (endptr <= buf + 12)
+ if (endptr - buf <= 12)
{
png_chunk_benign_error(png_ptr, "invalid");
return;
@@ -2165,6 +1939,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
else if (type >= PNG_EQUATION_LAST)
{
png_chunk_benign_error(png_ptr, "unrecognized equation type");
+ return;
}
for (buf = units; *buf; buf++)
@@ -2172,7 +1947,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_debug(3, "Allocating pCAL parameters array");
- params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
+ params = png_voidcast(png_charpp, png_malloc_base(png_ptr,
nparams * (sizeof (png_charp))));
if (params == NULL)
@@ -2201,57 +1976,47 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
}
png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
- (png_charp)units, params);
+ (png_charp)units, params);
png_free(png_ptr, params);
}
-#endif
+#else
+# define png_handle_pCAL NULL
+#endif /* READ_pCAL */
#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)
+static void
+png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr)
{
+ png_uint_32 length = png_ptr->chunk_length;
png_bytep buffer;
png_size_t i;
int state;
png_debug(1, "in png_handle_sCAL");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (png_ptr->mode & PNG_HAVE_IDAT)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL))
{
- 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))
- {
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ png_handle_error(png_ptr, "duplicate");
return;
}
/* Need unit type, width, \0, height: minimum 4 bytes */
- else if (length < 4)
+ if (length < 4)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
- length + 1);
+ 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);
+ png_handle_error(png_ptr, "out of memory");
return;
}
@@ -2296,37 +2061,31 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
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);
+ (png_charp)buffer+1, (png_charp)buffer+heighti);
}
}
-#endif
+#else
+# define png_handle_sCAL NULL
+#endif /* READ_sCAL */
#ifdef PNG_READ_tIME_SUPPORTED
-void /* PRIVATE */
-png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+static void
+png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr)
{
png_byte buf[7];
png_time mod_time;
png_debug(1, "in png_handle_tIME");
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "duplicate");
+ png_handle_error(png_ptr, "duplicate");
return;
}
- if (png_ptr->mode & PNG_HAVE_IDAT)
- png_ptr->mode |= PNG_AFTER_IDAT;
-
- if (length != 7)
+ if (png_ptr->chunk_length != 7)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "invalid");
+ png_handle_bad_length(png_ptr);
return;
}
@@ -2344,13 +2103,15 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
png_set_tIME(png_ptr, info_ptr, &mod_time);
}
-#endif
+#else
+# define png_handle_tIME NULL
+#endif /* READ_tIME */
#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)
+static void
+png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr)
{
+ png_uint_32 length = png_ptr->chunk_length;
png_text text_info;
png_bytep buffer;
png_charp key;
@@ -2370,33 +2131,17 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (--png_ptr->user_chunk_cache_max == 1)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "chunk cache full");
+ png_handle_error(png_ptr, "no space in chunk cache");
return;
}
}
-#endif
-
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- if (png_ptr->mode & PNG_HAVE_IDAT)
- 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
+#endif /* USER_LIMITS */
buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
if (buffer == NULL)
{
- png_chunk_benign_error(png_ptr, "out of memory");
+ png_handle_error(png_ptr, "out of memory");
return;
}
@@ -2425,13 +2170,15 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (png_set_text_2(png_ptr, info_ptr, &text_info, 1))
png_warning(png_ptr, "Insufficient memory to process text chunk");
}
-#endif
+#else
+# define png_handle_tEXt NULL
+#endif /* READ_tEXt */
#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)
+static void
+png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr)
{
+ png_uint_32 length = png_ptr->chunk_length;
png_const_charp errmsg = NULL;
png_bytep buffer;
png_uint_32 keyword_length;
@@ -2449,25 +2196,20 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (--png_ptr->user_chunk_cache_max == 1)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "chunk cache full");
+ png_handle_error(png_ptr, "no space in chunk cache");
return;
}
}
-#endif
-
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- if (png_ptr->mode & PNG_HAVE_IDAT)
- png_ptr->mode |= PNG_AFTER_IDAT;
+#endif /* USER_LIMITS */
+ /* Note, "length" is sufficient here; we won't be adding
+ * a null terminator later.
+ */
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");
+ png_handle_error(png_ptr, "out of memory");
return;
}
@@ -2504,7 +2246,7 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
* and text chunks.
*/
if (png_decompress_chunk(png_ptr, length, keyword_length+2,
- &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+ &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
{
png_text text;
@@ -2534,13 +2276,15 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (errmsg != NULL)
png_chunk_benign_error(png_ptr, errmsg);
}
-#endif
+#else
+# define png_handle_zTXt NULL
+#endif /* READ_zTXt */
#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)
+static void
+png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr)
{
+ png_uint_32 length = png_ptr->chunk_length;
png_const_charp errmsg = NULL;
png_bytep buffer;
png_uint_32 prefix_length;
@@ -2558,25 +2302,17 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (--png_ptr->user_chunk_cache_max == 1)
{
- png_crc_finish(png_ptr, length);
- png_chunk_benign_error(png_ptr, "chunk cache full");
+ png_handle_error(png_ptr, "no space in chunk cache");
return;
}
}
-#endif
-
- if (!(png_ptr->mode & PNG_HAVE_IHDR))
- png_chunk_error(png_ptr, "missing IHDR");
-
- if (png_ptr->mode & PNG_HAVE_IDAT)
- png_ptr->mode |= PNG_AFTER_IDAT;
+#endif /* USER_LIMITS */
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");
+ png_handle_error(png_ptr, "out of memory");
return;
}
@@ -2628,7 +2364,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
/* 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 allocaton may overflow.
+ * systems the available allocation may overflow.
*/
++prefix_length;
@@ -2644,7 +2380,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
* iCCP and text chunks.
*/
if (png_decompress_chunk(png_ptr, length, prefix_length,
- &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+ &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
buffer = png_ptr->read_buffer;
else
@@ -2660,7 +2396,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
buffer[uncompressed_length+prefix_length] = 0;
- if (compressed != 0)
+ if (compressed == 0)
text.compression = PNG_ITXT_COMPRESSION_NONE;
else
@@ -2684,78 +2420,97 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
if (errmsg != NULL)
png_chunk_benign_error(png_ptr, errmsg);
}
-#endif
+#else
+# define png_handle_iTXt NULL
+#endif /* READ_iTXt */
+
+/* UNSUPPORTED CHUNKS */
+#define png_handle_sTER NULL
+#define png_handle_fRAc NULL
+#define png_handle_gIFg NULL
+#define png_handle_gIFt NULL
+#define png_handle_gIFx NULL
+#define png_handle_dSIG NULL
+
+/* IDAT has special treatment below */
+#define png_handle_IDAT NULL
+
+/******************************************************************************
+ * UNKNOWN HANDLING LOGIC
+ *
+ * There are three ways an unknown chunk may arise:
+ *
+ * 1) Chunks not in the spec.
+ * 2) Chunks in the spec where libpng support doesn't exist or has been compiled
+ * out. These are recognized, for a very small performance benefit at the
+ * cost of maintaining a png_known_chunks entry for each one.
+ * 3) Chunks supported by libpng which have been marked as 'unknown' by the
+ * application.
+ *
+ * Prior to 1.7.0 all three cases are handled the same way, in 1.7.0 some
+ * attempt is made to optimize (2) and (3) by storing flags in
+ * png_struct::known_unknown for chunks in the spec which have been marked for
+ * unknown handling.
+ *
+ * There are three things libpng can do with an unknown chunk, in order of
+ * preference:
+ *
+ * 1) If PNG_READ_USER_CHUNKS_SUPPORTED call an application supplied callback
+ * with all the chunk data. If this doesn't handle the chunk in prior
+ * versions of libpng the chunk would be stored if safe otherwise skipped.
+ * In 1.7.0 the specified chunk unknown handling is used.
+ * 2) If PNG_SAVE_UNKNOWN_CHUNKS_SUPPOPRTED the chunk may be saved in the
+ * info_struct (if there is one.)
+ * 3) The chunk can be skipped.
+ *
+ * In effect libpng tries each option in turn. (2) looks at any per-chunk
+ * unknown handling then, if one wasn't specified, the overall default.
+ *
+ * IHDR and IEND cannot be treated as unknown. PLTE and IDAT can. Prior to
+ * 1.7.0 they couldn't be skipped without a png_error. 1.7.0 adds an extension
+ * which allows any critical chunk to be skipped so long as IDAT is skipped; the
+ * logic for failing on critical chunks only applies if the image data is being
+ * processed.
+ *
+ * The default behavior is (3); unknown chunks are simply skipped. 1.7.0 uses
+ * this to optimize the read code when possible.
+ *
+ * In the read code PNG_READ_UNKNOWN_CHUNKS_SUPPORTED is set only if either (1)
+ * or (2) or both are supported.
+ *
+ *****************************************************************************/
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+static int
+png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name)
+{
+ png_byte chunk_string[5];
+
+ PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name);
+ return png_handle_as_unknown(png_ptr, chunk_string);
+}
+#endif /* SAVE_UNKNOWN_CHUNKS */
#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)
+static void
+png_make_unknown_chunk(png_structrp png_ptr, png_unknown_chunkp chunk,
+ png_bytep data)
{
- 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;
- }
+ chunk->data = data;
+ chunk->size = png_ptr->chunk_length;
+ PNG_CSTRING_FROM_CHUNK(chunk->name, png_ptr->chunk_name);
+ /* 'mode' is a flag array, only three of the bottom four bits are public: */
+ chunk->location =
+ png_ptr->mode & (PNG_HAVE_IHDR+PNG_HAVE_PLTE+PNG_AFTER_IDAT);
}
-#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
/* 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)
+ png_bytep chunk_data)
{
- 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
@@ -2766,75 +2521,47 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
* 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
+ *
+ * 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.
- */
+ /* The user callback takes precedence over the chunk handling option: */
if (png_ptr->read_user_chunk_fn != NULL)
{
- if (png_cache_unknown_chunk(png_ptr, length))
- {
- /* Callback to user unknown chunk handler */
- int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr,
- &png_ptr->unknown_chunk);
+ png_unknown_chunk unknown_chunk;
+ int ret;
- /* ret is:
- * negative: An error occured, 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");
+ /* Callback to user unknown chunk handler */
+ png_make_unknown_chunk(png_ptr, &unknown_chunk, chunk_data);
+ ret = png_ptr->read_user_chunk_fn(png_ptr, &unknown_chunk);
- else if (ret == 0)
- {
- /* Use the default handling, note that if there is per-chunk
- * handling specified it has already been set into 'keep'.
- *
- * NOTE: this is an API change in 1.7.0. Prior to 1.7.0 libpng
- * would force keep to PNG_HANDLE_CHUNK_IF_SAFE at this point,
- * and 1.6.0 would issue a warning if this caused a default of
- * discarding the chunk to be changed.
- */
- if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
- keep = png_ptr->unknown_default;
- }
+ /* 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)
+ return;
- else /* chunk was handled */
- {
- handled = 1;
- /* Critical chunks can be safely discarded at this point. */
- keep = PNG_HANDLE_CHUNK_NEVER;
- }
- }
+ else if (ret < 0)
+ png_chunk_error(png_ptr, "application error");
- else
- keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */
+ /* Else: use the default handling. */
}
-
- else
- /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */
-# endif /* PNG_READ_USER_CHUNKS_SUPPORTED */
+# endif /* READ_USER_CHUNKS */
# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
{
+ int keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name);
+
/* keep is currently just the per-chunk setting, if there was no
- * setting change it to the global default now (not that this may
+ * setting change it to the global default now (note that this may
* still be AS_DEFAULT) then obtain the cache of the chunk if required,
* if not simply skip the chunk.
*/
@@ -2842,95 +2569,54 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
keep = png_ptr->unknown_default;
if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
- (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
+ (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
- {
- if (!png_cache_unknown_chunk(png_ptr, length))
- keep = PNG_HANDLE_CHUNK_NEVER;
- }
+# 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;
- else
- png_crc_finish(png_ptr, length);
+ 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_unknown_chunk unknown_chunk;
+
+ png_make_unknown_chunk(png_ptr, &unknown_chunk,
+ chunk_data);
+ png_set_unknown_chunks(png_ptr, info_ptr, &unknown_chunk,
+ 1);
+ return;
+ }
+# ifdef PNG_USER_LIMITS_SUPPORTED
+ }
+# endif /* USER_LIMITS */
}
-# 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, "chunk cache full");
- /* 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 /* PNG_USER_LIMITS_SUPPORTED */
- /* 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 */
+# else /* !SAVE_UNKNOWN_CHUNKS */
PNG_UNUSED(info_ptr)
-# endif
+# endif /* !SAVE_UNKNOWN_CHUNKS */
- /* 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.
+ /* This is the 'skip' case, where the read callback (if any) returned 0 and
+ * the save code did not save the chunk.
*/
- 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 /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
-
- /* Check for unhandled critical chunks */
- if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
+ if (PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
png_chunk_error(png_ptr, "unhandled critical chunk");
}
+#endif /* READ_UNKNOWN_CHUNKS */
/* This function is called to verify that a chunk name is valid.
* This function can't have the "critical chunk check" incorporated
@@ -2945,835 +2631,950 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
*/
void /* PRIVATE */
-png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)
+png_check_chunk_name(png_const_structrp png_ptr, const png_uint_32 chunk_name)
{
int i;
+ png_uint_32 cn=chunk_name;
png_debug(1, "in png_check_chunk_name");
for (i=1; i<=4; ++i)
{
- int c = chunk_name & 0xff;
+ int c = cn & 0xff;
if (c < 65 || c > 122 || (c > 90 && c < 97))
png_chunk_error(png_ptr, "invalid chunk type");
- chunk_name >>= 8;
+ cn >>= 8;
+ }
+}
+void /* PRIVATE */
+png_check_chunk_length(png_const_structrp png_ptr, const png_uint_32 length)
+{
+ png_alloc_size_t limit = PNG_UINT_31_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 (png_ptr->chunk_name == png_IDAT)
+ {
+ /* color_type 0 x 2 3 4 x 6 */
+ int channels[]={1,0,3,1,2,0,4};
+ png_alloc_size_t idat_limit = PNG_UINT_31_MAX;
+ size_t row_factor =
+ (png_ptr->width * channels[png_ptr->color_type] *
+ (png_ptr->bit_depth > 8? 2: 1)
+ + 1 + (png_ptr->interlaced? 6: 0));
+ if (png_ptr->height > PNG_UINT_32_MAX/row_factor)
+ idat_limit=PNG_UINT_31_MAX;
+ else
+ idat_limit = png_ptr->height * row_factor;
+ row_factor = row_factor > 32566? 32566 : row_factor;
+ idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */
+ idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX;
+ limit = limit < idat_limit? idat_limit : limit;
+ }
+
+ if (length > limit)
+ {
+ png_debug2(0," length = %lu, limit = %lu",
+ (unsigned long)length,(unsigned long)limit);
+ png_chunk_error(png_ptr, "chunk data is too large");
}
}
-/* 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.
+/* This is the known chunk table; it contains an entry for each supported
+ * chunk.
*/
-void /* PRIVATE */
-png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display)
+static const struct
{
- unsigned int pixel_depth = png_ptr->transformed_pixel_depth;
- png_const_bytep sp = png_ptr->row_buf + 1;
- png_uint_32 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;
+ void (*handle)(png_structrp png_ptr, png_infop info_ptr);
+ png_uint_32 name;
+ unsigned int before :5;
+ unsigned int after :5;
+}
+png_known_chunks[] =
+/* To make the code easier to write the following defines are used, note that
+ * before_end should never trip - it would indicate that libpng attempted to
+ * read beyond the IEND chunk.
+ *
+ * 'within_IDAT' is used for IDAT chunks; PNG_AFTER_IDAT must not be set, but
+ * PNG_HAVE_IDAT may be set.
+ */
+#define before_end PNG_HAVE_IEND /* Should be impossible */
+#define within_IDAT (before_end+PNG_AFTER_IDAT)
+#define before_IDAT (within_IDAT+PNG_HAVE_IDAT)
+#define before_PLTE (before_IDAT+PNG_HAVE_PLTE)
+#define before_start (before_PLTE+PNG_HAVE_IHDR)
+#define at_start 0
+#define after_start PNG_HAVE_IHDR
+#define after_PLTE (after_start+PNG_HAVE_PLTE) /* NOTE: PLTE optional */
+#define after_IDAT (after_PLTE+PNG_AFTER_IDAT) /* NOTE: PLTE optional */
+
+/* See pngchunk.h for how this works: */
+#define PNG_CHUNK_END(n, c1, c2, c3, c4, before, after)\
+ { png_handle_ ## n, png_ ##n, before, after }
+#define PNG_CHUNK(n, c1, c2, c3, c4, before, after)\
+ PNG_CHUNK_END(n, c1, c2, c3, c4, before, after),
+#define PNG_CHUNK_BEGIN(n, c1, c2, c3, c4, before, after)\
+ PNG_CHUNK_END(n, c1, c2, c3, c4, before, after),
+{
+# include "pngchunk.h"
+};
+#undef PNG_CHUNK_START
+#undef PNG_CHUNK
+#undef PNG_CHUNK_END
+
+#define C_KNOWN ((sizeof png_known_chunks)/(sizeof png_known_chunks[0]))
+
+/* See: scripts/chunkhash.c for code to generate this. This reads the same
+ * description file (pngchunk.h) as is included above. Whenever
+ * that file is changed chunkhash needs to be re-run to generate the lines
+ * following this comment.
+ *
+ * PNG_CHUNK_HASH modifes its argument and returns an index. png_chunk_index is
+ * a function which does the same thing without modifying the value of the
+ * argument. Both macro and function always return a valid index; to detect
+ * known chunks it is necessary to check png_known_chunks[index].name against
+ * the hashed name.
+ */
+static const png_byte png_chunk_lut[64] =
+{
+ 10, 20, 7, 3, 0, 23, 8, 0, 0, 11, 24, 0, 0, 0, 0, 4,
+ 12, 0, 0, 0, 13, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0,
+ 0, 6, 17, 0, 15, 0, 5, 19, 26, 0, 0, 0, 18, 0, 0, 9,
+ 1, 0, 21, 0, 22, 14, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0
+};
+
+#define PNG_CHUNK_HASH(n)\
+ png_chunk_lut[0x3f & (((n += n >> 2),n += n >> 8),n += n >> 16)]
+
+static png_byte
+png_chunk_index(png_uint_32 name)
+{
+ name += name >> 2;
+ name += name >> 8;
+ name += name >> 16;
+ return png_chunk_lut[name & 0x3f];
+}
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+/* Mark a known chunk to be handled as unknown. */
+void /*PRIVATE*/
+png_cache_known_unknown(png_structrp png_ptr, png_const_bytep add, int keep)
+ /* Update the png_struct::known_unknown bit cache which stores whether each
+ * known chunk should be treated as unknown.
+ *
+ * This cache exists to avoid doing the search loop on every chunk while
+ * handling chunks. This code is only ever used if unknown handling is
+ * invoked, and the loop is isolated code; the function is called from
+ * add_one_chunk in pngset.c once for each unknown and while this is
+ * happening no other code is being run in this thread.
+ */
+{
+ /* The cache only stores whether or not to handle the chunk; specifically
+ * whether or not keep is 0.
+ */
+ png_uint_32 name = PNG_CHUNK_FROM_STRING(add);
+
+ debug(PNG_HANDLE_CHUNK_AS_DEFAULT == 0 && C_KNOWN <= 32);
+
+ /* But do not treat IHDR or IEND as unknown. This is historical; it
+ * always was this way, it's not clear if PLTE can always safely be
+ * treated as unknown, but it is allowed.
+ */
+ if (name != png_IHDR && name != png_IEND)
+ {
+ png_byte i = png_chunk_index(name);
+
+ if (png_known_chunks[i].name == name)
+ {
+ {
+ if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
+ {
+ png_ptr->known_unknown |= 1U << i;
+
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
+ (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
+ PNG_CHUNK_ANCILLARY(name)))
+ png_ptr->save_unknown |= 1U << i;
+
+ else /* PNG_HANDLE_CHUNK_NEVER || !SAFE */
+ png_ptr->save_unknown &= ~(1U << i);
+# endif /* SAVE_UNKNOWN_CHUNKS */
+ }
+
+ else
+ png_ptr->known_unknown &= ~(1U << i);
+ }
+ }
+
+ /* else this is not a known chunk */
+ }
+
+ else /* 1.7.0: inform the app writer; */
+ png_app_warning(png_ptr, "IHDR, IEND cannot be treated as unknown");
+
+}
+#endif /* HANDLE_AS_UNKNOWN */
+
+/* Handle chunk position requirements in a consistent way. The chunk must
+ * come after 'after' and before 'before', either of which may be 0. If it
+ * does the function returns true, if it does not an appropriate chunk error
+ * is issued; benign for non-critical chunks, fatal for critical ones.
+ */
+static int
+png_handle_position(png_const_structrp png_ptr, unsigned int chunk)
+{
+ unsigned int before = png_known_chunks[chunk].before;
+ unsigned int after = png_known_chunks[chunk].after;
+
+# ifdef PNG_ERROR_TEXT_SUPPORTED
+ png_const_charp error = NULL;
+# endif /* ERROR_TEXT */
+
+ /* PLTE is optional with all color types except PALETTE, so for the other
+ * color types clear it from the 'after' bits.
+ *
+ * TODO: find some better way of recognizing the case where there is a PLTE
+ * and it follows after_PLTE chunks (see the complex stuff in handle_PLTE.)
+ */
+ if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+ after &= PNG_BIC_MASK(PNG_HAVE_PLTE);
+
+ if ((png_ptr->mode & before) == 0 &&
+ (png_ptr->mode & after) == after)
+ return 1;
+
+ /* The error case; do before first (it is normally more important) */
+# ifdef PNG_ERROR_TEXT_SUPPORTED
+ switch (before & -before) /* Lowest set bit */
+ {
+ case 0:
+ /* Check 'after'; only one bit set. */
+ switch (after)
+ {
+ case PNG_HAVE_IHDR:
+ error = "missing IHDR";
+ break;
+
+ case PNG_HAVE_PLTE:
+ error = "must occur after PLTE";
+ break;
+
+ case PNG_AFTER_IDAT:
+ error = "must come after IDAT";
+ break;
+
+ default:
+ impossible("invalid 'after' position");
+ }
+ break;
+
+ case PNG_HAVE_IHDR:
+ error = "must occur first";
+ break;
+
+ case PNG_HAVE_PLTE:
+ error = "must come before PLTE";
+ break;
+
+ case PNG_HAVE_IDAT:
+ error = "must come before IDAT";
+ break;
+
+ default:
+ impossible("invalid 'before' position");
+ }
+# endif /* ERROR_TEXT */
+
+ png_chunk_report(png_ptr, error, PNG_CHUNK_CRITICAL(png_ptr->chunk_name) ?
+ PNG_CHUNK_FATAL : PNG_CHUNK_ERROR);
+ return 0;
+}
+
+/* This is the shared chunk handling function, used for both the sequential and
+ * progressive reader.
+ */
+png_chunk_op /* PRIVATE */
+png_find_chunk_op(png_structrp png_ptr)
+{
+ /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name
+ * and work out how it should be handled. This function checks the chunk
+ * location using png_struct::mode and will set the PNG_AFTER_IDAT bit if
+ * appropriate but otherwise makes no changes to the stream read state.
+ *
+ * png_chunk_skip Skip this chunk
+ * png_chunk_unknown This is an unknown chunk which can't be skipped;
+ * the unknown handler must be called with all the
+ * chunk data.
+ * png_chunk_process_all The caller must call png_chunk_handle to handle
+ * the chunk, when this call is made all the chunk
+ * data must be available to the handler.
+ * png_chunk_process_part The handler expects data in png_struct::zstream.
+ * {next,avail}_in and does not require all of the
+ * data at once (as png_read_process_IDAT).
+ */
+ png_uint_32 chunk_name = png_ptr->chunk_name;
+ unsigned int mode = png_ptr->mode;
+ unsigned int index;
+
+ /* This function should never be called if IEND has been set:
+ */
+ debug((mode & PNG_HAVE_IEND) == 0);
+
+ /* IDAT logic: we are only *after* IDAT when we start reading the first
+ * following (non-IDAT) chunk, this may already have been set in the IDAT
+ * handling code, but if IDAT is handled as unknown this doesn't happen.
+ */
+ if (chunk_name != png_IDAT && (mode & PNG_HAVE_IDAT) != 0)
+ mode = png_ptr->mode |= PNG_AFTER_IDAT;
+
+ index = png_chunk_index(chunk_name);
+
+ if (png_known_chunks[index].name == chunk_name)
+ {
+ /* Known chunks have a position requirement; check it, badly positioned
+ * chunks that do not error out in png_handle_position are simply skipped.
+ *
+ * API CHANGE: libpng 1.7.0: prior versions of libpng did not check
+ * ordering requirements for known chunks where the support for reading
+ * them had been configured out of libpng. This seems dangerous; the
+ * user chunk callback could still see them and crash as a result.
+ */
+ if (!png_handle_position(png_ptr, index))
+ return png_chunk_skip;
+
+ /* Do the mode update.
+ *
+ * API CHANGE 1.7.0: the 'HAVE' flags are now consistently set *before*
+ * the chunk is handled. Previously only IDAT was handled this way. This
+ * can only affect an app that was previously handling PLTE itself in a
+ * callback, however this seems to be impossible.
+ */
+ switch (chunk_name)
+ {
+ case png_IHDR: png_ptr->mode |= PNG_HAVE_IHDR; break;
+ case png_PLTE: png_ptr->mode |= PNG_HAVE_PLTE; break;
+ case png_IDAT: png_ptr->mode |= PNG_HAVE_IDAT; break;
+ case png_IEND: png_ptr->mode |= PNG_HAVE_IEND; break;
+ default: break;
+ }
+
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ /* A known chunk may still be treated as unknown. Check for that. */
+ if (!((png_ptr->known_unknown >> index) & 1U))
+# endif /* HANDLE_AS_UNKNOWN */
+ {
+ /* This is a known chunk that is not being treated as unknown. If
+ * it is IDAT then partial processing is done, otherwise (at present)
+ * the whole thing is processed in one shot
+ *
+ * TODO: this is a feature of the legacy use of the sequential read
+ * code in the handlers, fix this.
+ */
+ if (chunk_name == png_IDAT)
+ return png_chunk_process_part;
+
+ /* Check for a known chunk where support has been compiled out of
+ * libpng. We know it cannot be a critical chunk; support for those
+ * cannot be removed.
+ */
+ if (png_known_chunks[index].handle != NULL)
+ return png_chunk_process_all;
+
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ if (png_ptr->read_user_chunk_fn != NULL)
+ return png_chunk_unknown;
+# endif /* READ_USER_CHUNKS */
+
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ /* There is no per-chunk special handling set for this chunk
+ * (because of the test on known_unknown above) so only the
+ * default unknown handling behavior matters. We skip the chunk
+ * if the behavior is 'NEVER' or 'DEFAULT'. This is irrelevant
+ * if SAVE_UNKNOWN_CHUNKS is not supported.
+ */
+ if (png_ptr->unknown_default > PNG_HANDLE_CHUNK_NEVER)
+ return png_chunk_unknown;
+# endif /* SAVE_UNKNOWN_CHUNKS */
+
+ return png_chunk_skip;
+ }
+
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ else
+ {
+ /* Else this is a known chunk that is being treated as unknown. If
+ * there is a user callback the whole shebang is required:
+ */
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ if (png_ptr->read_user_chunk_fn != NULL)
+ return png_chunk_unknown;
+# endif /* READ_USER_CHUNKS */
+
+ /* No user callback, there is a possibility that we can skip this
+ * chunk:
+ */
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ if ((png_ptr->save_unknown >> index) & 1U)
+ return png_chunk_unknown;
+# endif /* SAVE_UNKNOWN_CHUNKS */
+
+ /* If this is a critical chunk and IDAT is not being skipped then
+ * this is an error. The only possibility here is PLTE on an
+ * image which is palette mapped. If the app ignores this error
+ * then there will be a more definate one in png_handle_unknown.
+ */
+ if (chunk_name == png_PLTE &&
+ png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_app_error(png_ptr, "skipping PLTE on palette image");
+
+ return png_chunk_skip;
+ }
+# endif /* HANDLE_AS_UNKNOWN */
+ }
+
+ else /* unknown chunk */
+ {
+ /* The code above implicitly validates the chunk name, however if a chunk
+ * name/type is not recognized it is necessary to validate it to ensure
+ * that the PNG stream isn't hopelessly damaged:
+ */
+ png_check_chunk_name(png_ptr, chunk_name);
+
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ if (png_ptr->read_user_chunk_fn != NULL)
+ return png_chunk_unknown;
+# endif /* READ_USER_CHUNKS */
+
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ /* There may be per-chunk handling, otherwise the default is used, this
+ * is the one place where the list needs to be searched:
+ */
+ {
+ int keep = png_chunk_unknown_handling(png_ptr, chunk_name);
+
+ 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(chunk_name)))
+ return png_chunk_unknown;
+ }
+# endif /* SAVE_UNKNOWN_CHUNKS */
+
+ /* The chunk will be skipped so it must not be a critical chunk, unless
+ * IDATs are being skipped too.
+ */
+ if (PNG_CHUNK_CRITICAL(chunk_name)
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ && !png_IDATs_skipped(png_ptr)
+# endif /* HANDLE_AS_UNKNOWN */
+ )
+ png_chunk_error(png_ptr, "unhandled critical chunk");
+
+ return png_chunk_skip;
+ }
+}
+
+void /* PRIVATE */
+png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr)
+ /* The chunk to handle is in png_struct::chunk_name,chunk_length.
+ *
+ * NOTE: at present it is only valid to call this after png_find_chunk_op
+ * has returned png_chunk_process_all and all the data is available for
+ * png_handle_chunk (via the libpng read callback.)
+ */
+{
+ png_uint_32 chunk_name = png_ptr->chunk_name;
+ unsigned int index = png_chunk_index(chunk_name);
+
+ /* So this must be true: */
+ affirm(png_known_chunks[index].name == chunk_name &&
+ png_known_chunks[index].handle != NULL);
+
+ png_known_chunks[index].handle(png_ptr, info_ptr);
+}
+
+static void
+copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
+ png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int clear)
+ /* Copy the row in row_buffer; this is the 'simple' case of combine_row
+ * where no adjustment to the pixel spacing is required.
+ */
+{
+ png_copy_row(png_ptr, dp, sp, x, width,
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format),
+# else
+ PNG_PIXEL_DEPTH(*png_ptr),
+# endif
+ clear/*clear partial byte at end of row*/, 1/*sp -> dp[x]*/);
+}
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+static void
+combine_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
+ png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int display)
+ /* 1.7.0: API CHANGE: prior to 1.7.0 read de-interlace was done in two steps,
+ * the first would expand a narrow pass by replicating pixels according to
+ * the inter-pixel spacing of the pixels from the pass in the image. It did
+ * not take account of any offset from the start of the image row of the
+ * first pixel. The second step happened in png_combine_row where the result
+ * was merged into the output rows.
+ *
+ * In 1.7.0 this is no longer done. Instead all the work happens here. This
+ * is only an API change for the progressive reader if the app didn't call
+ * png_combine_row, but rather expected an expanded row. It's not obvious
+ * why any user of the progressive reader would want this, particularly given
+ * the weird non-offseting of the start in the original
+ * 'png_do_read_interlace'; the behavior was completely undocumented.
+ *
+ * In 1.7.0 combine_row does all the work. It expects a raw uncompressed,
+ * de-filtered, transformed row and it either copies it if:
+ *
+ * 1) It is not interlaced.
+ * 2) libpng isn't handling the de-interlace.
+ * 3) This is pass 7 (i.e. '6' using the libpng 0-based numbering).
+ *
+ * The input data comes from png_struct and sp:
+ *
+ * sp[width(pixels)]; the row data from input[x(pixels)...]
+ * png_struct::pass; the pass
+ * png_struct::row_number; the row number in the *image*
+ * png_struct::row_bit_depth,
+ * png_struct::row_format; the pixel format, if TRANSFORM_MECH, else:
+ * png_struct::bit_depth,
+ * png_struct::color_type; the pixel format otherwise
+ *
+ * The destination pointer (but not size) and how to handle intermediate
+ * passes are arguments to the API. The destination is the pointer to the
+ * entire row buffer, not just the part from output[x] on. 'display' is
+ * interpreted as:
+ *
+ * 0: only overwrite destination pixels that will correspond to the source
+ * pixel in the final image. 'sparkle' mode.
+ * 1: overwrite the corresponding destination pixel and all following
+ * pixels (horizontally and, eventually, vertically) that will come
+ * from *later* passes. 'block' mode.
+ */
+{
+ const unsigned int pass = png_ptr->pass;
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.
+ /* Factor out the copy case first, the 'display' argument is irrelevant in
+ * these cases:
*/
- 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)
+ if (!png_ptr->do_interlace || png_ptr->pass == 6)
{
- /* 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) /* 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 */
+ copy_row(png_ptr, dp, sp, x, width, 0/*do not clear*/);
+ return;
}
- /* 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 && (png_ptr->transformations & PNG_INTERLACE) &&
- pass < 6 && (display == 0 ||
- /* The following copies everything for 'display' on passes 0, 2 and 4. */
- (display == 1 && (pass & 1) != 0)))
+ else /* not a simple copy */
{
- /* Narrow images may have no bits in a pass; the caller should handle
- * this, but this test is cheap:
+ const unsigned int pixel_depth =
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ png_ptr->row_bit_depth * PNG_IMAGE_PIXEL_CHANNELS(png_ptr->row_format);
+# else
+ PNG_PIXEL_DEPTH(*png_ptr);
+# endif
+ png_uint_32 row_width = png_ptr->width; /* output width */
+ /* The first source pixel is written to PNG_COL_FROM_PASS of the
+ * destination:
*/
- if (row_width <= PNG_PASS_START_COL(pass))
- return;
+ png_uint_32 dx = PNG_COL_FROM_PASS_COL(x, pass);
+ /* The corresponding offset within the 8x8 block: */
+ const unsigned int dstart = dx & 0x7U;
+ /* Find the first pixel written in any 8x8 block IN THIS PASS: */
+ const unsigned int pass_start = PNG_PASS_START_COL(pass);
+ /* Subsequent pixels are written PNG_PASS_COL_OFFSET further on: */
+ const unsigned int doffset = PNG_PASS_COL_OFFSET(pass);
+ /* In 'block' mode when PNG_PASS_START_COL(pass) is 0 (PNG passes 1,3,5,7)
+ * the same pixel is replicated doffset times, when PNG_PASS_START_COL is
+ * non-zero (PNG passes 2,4,6) it is replicated PNG_PASS_START_COL times.
+ * For 'sparkle' mode only one copy of the pixel is written:
+ */
+ unsigned int drep = display ? (pass_start ? pass_start : doffset) : 1;
- if (pixel_depth < 8)
+ /* Standard check for byte alignment */
+ debug(((x * pixel_depth/*OVERFLOW OK*/) & 0x7U) == 0U);
+
+ /* The caller should have excluded the narrow cases: */
+ affirm(row_width > dx);
+ row_width -= dx;
+ /* Advance dp to the start of the 8x8 block containing the first pixel to
+ * write, adjust dx to be an offset within the block:
+ */
+ dp += png_calc_rowbytes(png_ptr, pixel_depth, dx & ~0x7U);
+ dx &= 0x7U;
+
+ /* So each source pixel sp[i] is written to:
+ *
+ * dp[dstart + i*doffset]..dp[dstart + i*doffset + (drep-1)]
+ *
+ * Until we get to row_width. This is easy for pixels that are 8 or more
+ * bits deep; whole bytes are read and written, slightly more difficult
+ * when pixel_depth * drep is at least 8 bits, because then dstart *
+ * pixel_depth will always be a whole byte and most complex when source
+ * and destination require sub-byte addressing.
+ *
+ * Cherry pick the easy cases:
+ */
+ if (pixel_depth > 8U)
{
- /* 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)
+ /* Convert to bytes: */
+ const unsigned int pixel_bytes = pixel_depth >> 3;
- /* 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 /* !PNG_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)
- mask = MASK(pass, pixel_depth, display, 0);
-
- else
-# endif
- mask = MASK(pass, pixel_depth, display, 1);
+ dp += dstart * pixel_bytes;
for (;;)
{
- png_uint_32 m;
+ unsigned int c;
- /* 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 (drep > row_width)
+ drep = row_width;
- if (m != 0) /* something to copy */
- {
- if (m != 0xff)
- *dp = (png_byte)((*dp & ~m) | (*sp & m));
- else
- *dp = *sp;
- }
+ for (c=0U; c= row_width)
+ break;
- row_width -= pixels_per_byte;
- ++dp;
- ++sp;
+ row_width -= doffset;
+ dp += (doffset-drep) * pixel_bytes;
+ sp += pixel_bytes;
}
}
- else /* pixel_depth >= 8 */
+ else if (pixel_depth == 8U)
{
- 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.
+ /* Optimize the common 1-byte per pixel case (typical case for palette
+ * mapped images):
*/
- {
- unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth;
+ dp += dstart;
- row_width -= offset;
- dp += offset;
- sp += offset;
+ for (;;)
+ {
+ if (drep > row_width)
+ drep = row_width;
+
+ memset(dp, *sp++, drep);
+
+ if (doffset >= row_width)
+ break;
+
+ row_width -= doffset;
+ dp += doffset;
}
+ }
- /* Work out the bytes to copy. */
- if (display != 0)
+ else /* pixel_depth < 8 */
+ {
+ /* Pixels are 1, 2 or 4 bits in size. */
+ unsigned int spixel = *sp++;
+ unsigned int dbrep = pixel_depth * drep;
+ unsigned int spos = 0U;
+# ifdef PNG_READ_PACKSWAP_SUPPORTED
+ const int lsb =
+ (png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0;
+# endif /* READ_PACKSWAP */
+
+ if (dbrep >= 8U)
{
- /* 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.
+ /* brep must be greater than 1, the destination does not require
+ * sub-byte addressing except, maybe, at the end.
+ *
+ * db is the count of bytes required to replicate the source pixel
+ * drep times.
*/
- bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth;
+ debug((dbrep & 7U) == 0U);
+ dbrep >>= 3;
+ debug((dstart * pixel_depth & 7U) == 0U);
+ dp += (dstart * pixel_depth) >> 3;
- /* But don't allow this number to exceed the actual row width. */
- if (bytes_to_copy > row_width)
- bytes_to_copy = row_width;
- }
+ for (;;)
+ {
+ /* Fill a byte with copies of the next pixel: */
+ unsigned int spixel_rep = spixel;
- else /* normal row; Adam7 only ever gives us one pixel to copy. */
- bytes_to_copy = pixel_depth;
+# ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (lsb)
+ spixel_rep >>= spos;
+ else
+# endif /* READ_PACKSWAP */
+ spixel_rep >>= (8U-pixel_depth)-spos;
- /* 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 (;;)
+ switch (pixel_depth)
{
- *dp = *sp;
-
- if (row_width <= bytes_to_jump)
- return;
-
- dp += bytes_to_jump;
- sp += bytes_to_jump;
- row_width -= bytes_to_jump;
+ case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1;
+ /*FALL THROUGH*/
+ case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2;
+ /*FALL THROUGH*/
+ case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4;
+ /*FALL THROUGH*/
+ default: break;
}
- case 2:
- /* There is a possibility of a partial copy at the end here; this
- * slows the code down somewhat.
+ /* This may leave some pixels unwritten when there is a partial
+ * byte write required at the end:
*/
- do
+ if (drep > row_width)
+ drep = row_width, dbrep = (pixel_depth * drep) >> 3;
+
+ memset(dp, spixel_rep, dbrep);
+
+ if (doffset >= row_width)
{
- 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.
+ /* End condition; were all 'drep' pixels written at the end?
*/
- if (png_isaligned(dp, png_uint_32) &&
- png_isaligned(sp, png_uint_32) &&
- bytes_to_copy % (sizeof (png_uint_32)) == 0 &&
- bytes_to_jump % (sizeof (png_uint_32)) == 0)
+ drep = (pixel_depth * drep - (dbrep << 3));
+
+ if (drep)
{
- 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));
+ unsigned int mask;
- do
- {
- size_t c = bytes_to_copy;
- do
- {
- *dp32++ = *sp32++;
- c -= (sizeof (png_uint_32));
- }
- while (c > 0);
+ debug(drep < 8U);
+ dp += dbrep;
- 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.
+ /* Set 'mask' to have 0's where *dp must be overwritten
+ * with spixel_rep:
*/
- dp = (png_bytep)dp32;
- sp = (png_const_bytep)sp32;
- do
- *dp++ = *sp++;
- while (--row_width > 0);
- return;
+# ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (lsb)
+ mask = 0xff << drep;
+ else
+# endif /* READ_PACKSWAP */
+ mask = 0xff >> drep;
+
+ *dp = PNG_BYTE((*dp & mask) | (spixel_rep & ~mask));
}
- /* Else do it in 16-bit quantities, but only if the size is
- * not too large.
- */
+ break;
+ }
+
+ row_width -= doffset;
+ dp += (doffset * pixel_depth) >> 3;
+ spos += pixel_depth;
+ if (spos == 8U)
+ spixel = *sp++, spos = 0U;
+ } /* for (;;) */
+ } /* pixel_depth * drep >= 8 */
+
+ else /* pixel_depth * drep < 8 */
+ {
+ /* brep may be 1, pixel_depth may be 1, 2 or 4, dbrep is the number
+ * of bits to set.
+ */
+ unsigned int bstart = dstart * pixel_depth; /* in bits */
+ unsigned int dpixel;
+
+ dp += bstart >> 3;
+ bstart &= 7U;
+ dpixel = *dp;
+
+ /* dpixel: current *dp, being modified
+ * bstart: bit offset within dpixel
+ * drep: pixel size to write (used as a check against row_width)
+ * doffset: pixel step to next written destination
+ *
+ * spixel: current *sp, being read, and:
+ * spixel_rep: current pixel, replicated to fill a byte
+ * spos: bit offset within spixel
+ *
+ * Set dbrep to a mask for the bits to set:
+ */
+ dbrep = (1U<>= spos;
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));
+# endif /* READ_PACKSWAP */
+ spixel_rep >>= (8U-pixel_depth)-spos;
- 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 /* PNG_ALIGN_ code */
-
- /* The true default - use a memcpy: */
- for (;;)
+ switch (pixel_depth)
{
- 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 = row_width;
+ case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1;
+ /*FALL THROUGH*/
+ case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2;
+ /*FALL THROUGH*/
+ case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4;
+ /*FALL THROUGH*/
+ default: break;
}
- }
- /* NOT REACHED*/
- } /* pixel_depth >= 8 */
+ /* This may leave some pixels unwritten when there is a partial
+ * byte write required at the end:
+ */
+ if (drep > row_width)
+ drep = row_width, dbrep = (1U<<(pixel_depth*drep))-1U;
- /* Here if pixel_depth < 8 to check 'end_ptr' below. */
- }
- else
-#endif
+ {
+ unsigned int mask;
- /* 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));
+ /* Mask dbrep bits at bstart: */
+# ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (lsb)
+ mask = bstart;
+ else
+# endif /* READ_PACKSWAP */
+ mask = (8U-pixel_depth)-bstart;
+ mask = dbrep << mask;
- /* 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));
+ dpixel &= ~mask;
+ dpixel |= spixel_rep & mask;
+ }
+
+ if (doffset >= row_width)
+ {
+ *dp = PNG_BYTE(dpixel);
+ break;
+ }
+
+ row_width -= doffset;
+ bstart += doffset * pixel_depth;
+
+ if (bstart >= 8U)
+ {
+ *dp = PNG_BYTE(dpixel);
+ dp += bstart >> 3;
+ bstart &= 7U;
+ dpixel = *dp;
+ }
+
+ spos += pixel_depth;
+ if (spos == 8U)
+ spixel = *sp++, spos = 0U;
+ } /* for (;;) */
+ } /* pixel_depth * drep < 8 */
+ } /* pixel_depth < 8 */
+ } /* not a simple copy */
}
-#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 */)
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+void PNGAPI
+png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row,
+ png_const_bytep new_row)
{
- /* 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)
+ /* new_row is a flag here - if it is NULL then the app callback was called
+ * from an empty row (see the calls to png_struct::row_fn above), otherwise
+ * it must be png_struct::transformed_row
+ */
+ if (png_ptr != NULL && new_row != NULL)
{
- png_uint_32 final_width;
-
- final_width = row_info->width * png_pass_inc[pass];
-
- switch (row_info->pixel_depth)
+ if (new_row != png_ptr->row_buffer
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ && new_row != png_ptr->transformed_row
+# endif /* TRANSFORM_MECH */
+ )
+ png_app_error(png_ptr, "invalid call to png_progressive_combine_row");
+ else
{
- case 1:
+ png_uint_32 width = png_ptr->width;
+
+ if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
{
- 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)
- {
- 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;
+ const unsigned int pass = png_ptr->pass;
+ width = PNG_PASS_COLS(width, pass);
}
- 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)
- {
- 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)
- {
- 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;
- }
+ combine_row(png_ptr, old_row, new_row, 0U, width, 1/*blocky display*/);
}
-
- 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 /* PNG_READ_INTERLACING_SUPPORTED */
+#endif /* PROGRESSIVE_READ */
+#else /* !READ_INTERLACING */
+ /* No read deinterlace support, so 'combine' always reduces to 'copy', there
+ * is no 'display' argument:
+ */
+# define combine_row(pp, dp, sp, x, w, display)\
+ copy_row(pp, dp, sp, x, w, 0/*!clear*/)
+#endif /* !READ_INTERLACING */
static void
-png_read_filter_row_sub(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_sub(png_alloc_size_t row_bytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels)
{
- 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;
+ while (row_bytes >= bpp)
+ {
+ unsigned int i;
+
+ for (i=0; i 0)
{
- *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
- rp++;
+ *row = PNG_BYTE(*row + *prev_row);
+ ++row;
+ ++prev_row;
+ --row_bytes;
+ }
+
+ PNG_UNUSED(bpp)
+ PNG_UNUSED(prev_pixels)
+}
+
+static void
+png_read_filter_row_avg(png_alloc_size_t row_bytes, unsigned int bpp,
+ png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels)
+{
+ while (row_bytes >= bpp)
+ {
+ unsigned int i;
+
+ for (i=0; irowbytes;
- png_bytep rp = row;
- png_const_bytep pp = prev_row;
+ png_const_bytep rp_end = row + row_bytes;
+ png_byte a, c;
- for (i = 0; i < istop; i++)
- {
- *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
- rp++;
- }
-}
+ /* prev_pixels stores pixel a then c */
+ a = prev_pixels[0];
+ c = prev_pixels[1];
-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;
+ png_byte b;
+ int pa, pb, pc, p;
- a &= 0xff; /* From previous iteration or start */
b = *prev_row++;
p = b - c;
@@ -3792,43 +3593,39 @@ png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,
/* 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 (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;
+ a = 0xFFU & (a + *row);
+ *row++ = a;
}
+
+ PNG_UNUSED(bpp)
}
static void
-png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
- png_const_bytep prev_row)
+png_read_filter_row_paeth_multibyte_pixel(png_alloc_size_t row_bytes,
+ unsigned int bpp, png_bytep row, png_const_bytep prev_row,
+ png_const_bytep prev_pixels)
{
- 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).
- */
+ /* 'a' and 'c' for the first pixel come from prev_pixels: */
while (row < rp_end)
{
- int a = *row + *prev_row++;
- *row++ = (png_byte)a;
- }
+ png_byte a, b, c;
+ int pa, pb, pc, p;
- /* 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);
+ /* prev_pixels stores bpp bytes for 'a', the bpp for 'c': */
+ c = *(prev_pixels+bpp);
+ a = *prev_pixels++;
b = *prev_row++;
p = b - c;
@@ -3844,16 +3641,54 @@ png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
pc = (p + pc) < 0 ? -(p + pc) : p + pc;
# endif
- if (pb < pa) pa = pb, a = b;
+ if (pb < pa)
+ {
+ pa = pb; a = b;
+ }
if (pc < pa) a = c;
- a += *row;
- *row++ = (png_byte)a;
+ a = 0xFFU & (a + *row);
+ *row++ = a;
+ }
+
+ /* Remainder */
+ rp_end += row_bytes - bpp;
+
+ while (row < rp_end)
+ {
+ png_byte a, b, c;
+ int 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 = 0xFFU & (a + *row);
+ *row++ = a;
}
}
static void
-png_init_filter_functions(png_structrp pp)
+png_init_filter_functions(png_structrp pp, unsigned int bpp)
/* 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
@@ -3864,8 +3699,6 @@ png_init_filter_functions(png_structrp pp)
* 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;
@@ -3889,562 +3722,1071 @@ png_init_filter_functions(png_structrp pp)
#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)
+/* This is an IDAT specific wrapper for png_zlib_inflate; the input is already
+ * in png_ptr->zstream.{next,avail}_in however the output uses the full
+ * capabilities of png_zlib_inflate, returning a byte count of bytes read.
+ * This is just a convenience for IDAT processing.
+ *
+ * NOTE: this function works just fine after the zstream has ended, it just
+ * fills the buffer with zeros (outputing an error message once.)
+ */
+static png_alloc_size_t
+png_inflate_IDAT(png_structrp png_ptr, int finish,
+ /* OUTPUT: */ png_bytep output, png_alloc_size_t output_size)
{
- /* 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.
+ /* Expect Z_OK if !finsh and Z_STREAM_END if finish; if Z_STREAM_END is
+ * delivered when finish is not set the IDAT stream is truncated, if Z_OK is
+ * delivered when finish is set this is harmless and indicates that the
+ * stream end code has not been read.
+ *
+ * finish should be set as follows:
+ *
+ * 0: not reading the last row, stream not expected to end
+ * 1: reading the last row, stream expected to end
+ * 2: looking for stream end after the last row has been read, expect no
+ * more output and stream end.
*/
- if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST)
+ png_alloc_size_t original_size = output_size;
+ int ret = Z_STREAM_END; /* In case it ended ok before. */
+
+ if (!png_ptr->zstream_ended)
{
- if (pp->read_filter[0] == NULL)
- png_init_filter_functions(pp);
+ png_const_bytep next_in = png_ptr->zstream.next_in;
+ png_uint_32 avail_in = png_ptr->zstream.avail_in;
- pp->read_filter[filter-1](row_info, row, prev_row);
- }
-}
+ ret = png_zlib_inflate(png_ptr, png_IDAT, finish,
+ &next_in, &avail_in, &output, &output_size/*remaining*/);
-#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 */
+ debug(next_in == png_ptr->zstream.next_in);
+ debug(avail_in == png_ptr->zstream.avail_in);
+ debug(output == png_ptr->zstream.next_out);
+ /* But zstream.avail_out may be truncated to uInt */
- if (output == NULL)
- avail_out = 0;
-
- do
- {
- int ret;
- png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
-
- if (png_ptr->zstream.avail_in == 0)
+ switch (ret)
{
- 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.
+ case Z_STREAM_END:
+ /* The caller must set finish on the last row of the image (not
+ * the last row of the pass!)
*/
- if (png_ptr->chunk_name != png_IDAT)
- png_error(png_ptr, "Not enough image data");
- }
+ debug(png_ptr->zstream_ended);
- avail_in = png_ptr->IDAT_read_size;
+ if (!finish) /* early end */
+ break;
- if (avail_in > png_ptr->idat_size)
- avail_in = (uInt)png_ptr->idat_size;
+ if (output_size > 0) /* incomplete read */
+ {
+ if (finish == 2) /* looking for end; it has been found */
+ return original_size - output_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*/);
+ /* else those bytes are really needed: */
+ break;
+ }
- png_crc_read(png_ptr, buffer, avail_in);
- png_ptr->idat_size -= avail_in;
+ /* else: FALL THROUGH: success */
- png_ptr->zstream.next_in = buffer;
- png_ptr->zstream.avail_in = avail_in;
+ case Z_BUF_ERROR:
+ /* this is the success case: output or input is empty: */
+ original_size -= output_size; /* bytes written */
+
+ if (output_size > 0)
+ {
+ /* Some output still needed; if the next chunk is known
+ * to not be an IDAT then this is the truncation case.
+ */
+ affirm(avail_in == 0);
+
+ if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
+ {
+ /* Zlib doesn't know we are out of data, so this must be
+ * done here:
+ */
+ png_ptr->zstream_ended = 1;
+ break;
+ }
+ }
+
+ return original_size; /* bytes written */
+
+ default:
+ /* error */
+ break;
}
- /* 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.
+ /* The 'ended' flag should always be set if we get here, the success
+ * cases where the LZ stream hasn't reached an end or an error leave
+ * the function at the return above.
*/
- 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");
+ debug(png_ptr->zstream_ended);
}
+
+ /* This is the error return case; there was missing data, or an error.
+ * Either continue with a warning (once; hence the zstream_error flag)
+ * or png_error.
+ */
+ if (!png_ptr->zstream_error) /* first time */
+ {
+#ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
+ switch (png_ptr->IDAT_error_action)
+ {
+ case PNG_ERROR:
+ if(!strncmp(png_ptr->zstream.msg,"incorrect data check",20))
+ {
+ if (png_ptr->current_crc != crc_quiet_use)
+ png_chunk_warning(png_ptr, "ADLER32 checksum mismatch");
+ }
+
+ else
+ {
+ png_chunk_error(png_ptr, png_ptr->zstream.msg);
+ }
+ break;
+
+ case PNG_WARN:
+ png_chunk_warning(png_ptr, png_ptr->zstream.msg);
+ break;
+
+ default: /* ignore */
+ /* Keep going */
+ break;
+ }
+#else
+ {
+ if(!strncmp(png_ptr->zstream.msg,"incorrect data check",20))
+ png_chunk_warning(png_ptr, "ADLER32 checksum mismatch");
+ else
+ png_chunk_error(png_ptr, png_ptr->zstream.msg);
+ }
+#endif /* !BENIGN_ERRORS */
+
+ /* And prevent the report about too many IDATs on streams with internal
+ * LZ errors:
+ */
+ png_ptr->zstream_error = 1;
+ }
+
+ /* This is the error recovery case; fill the buffer with zeros. This is
+ * safe because it makes the filter byte 'NONE' and the row fairly innocent.
+ */
+ memset(output, 0, output_size);
+ return original_size;
+}
+
+/* SHARED IDAT HANDLING.
+ *
+ * This is the 1.7+ common read code; shared by both the progressive and
+ * sequential readers.
+ */
+/* Initialize the row buffers, etc. */
+void /* PRIVATE */
+png_read_start_IDAT(png_structrp png_ptr)
+{
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ /* This won't work at all if the app turned on unknown handling for IDAT
+ * chunks; the first IDAT has already been consumed!
+ */
+ if (png_ptr->known_unknown & 1U)
+ png_error(png_ptr, "Attempt to read image with unknown IDAT");
+# endif /* HANDLE_AS_UNKNOWN */
+
+ /* This is a missing read of the header information; we still haven't
+ * countered the first IDAT chunk. This can only happen in the sequential
+ * reader if the app didn't call png_read_info.
+ */
+ if (png_ptr->chunk_name != png_IDAT)
+ png_error(png_ptr, "Missing call to png_read_info");
+
+ /* Two things need to happen: first work out the effect of any
+ * transformations (if supported) on the row size, second, allocate
+ * row_buffer and claim the zstream.
+ */
+ png_init_row_info(png_ptr);
+
+ /* Now allocate the row buffer and, if that succeeds, claim the zstream.
+ */
+ png_ptr->row_buffer = png_voidcast(png_bytep, png_malloc(png_ptr,
+ png_calc_rowbytes(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width)));
+
+ if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg);
+}
+
+/* The process function gets called when there is some IDAT data to process
+ * and it just does the right thing with it. The zstream must have been claimed
+ * (owner png_IDAT) and the input data is in zstream.{next,avail}_in. The
+ * output next_{in,out} must not be changed by the caller; it is used
+ * internally.
+ *
+ * Result codes are as follows:
+ *
+ * png_row_incomplete: Insufficient IDAT data (from zstream) was present to
+ * process the next row. zstream.avail_in will be 0.
+ * png_row_process: A new row is available in the input buffer, it should be
+ * handled before the next call (if any) to this function.
+ * png_row_repeat: For interlaced images (only) this row is not in the pass,
+ * however the existing buffer may be displayed in lieu; if doing the
+ * 'blocky' (not 'sparkle') display the row should be displayed,
+ * otherwise treat as:
+ * png_row_skip: For interlaced images (only) the interlace pass has no data
+ * appropriate to this row, it should be skipped.
+ *
+ * In both of the two cases zstream.avail_in may be non-0, indicating that some
+ * IDAT data at zstream.next_in remains to be consumed. This data must be
+ * preserved and preset at the next call to the function.
+ *
+ * The function may also call png_error if an unrecoverable error occurs.
+ *
+ * The caller passes in a callback function and parameter to be called when row
+ * data is available. The callback is called repeatedly for each row to handle
+ * all the transformed row data.
+ */
+png_row_op /*PRIVATE*/
+png_read_process_IDAT(png_structrp png_ptr, png_bytep transformed_row,
+ png_bytep display_row, int save_row)
+{
+ /* Common sub-expressions. These are all constant across the whole PNG, but
+ * are recalculated here each time because this is fast and it only happens
+ * once per row + once per block of input data.
+ */
+ const unsigned int max_pixels = png_max_pixel_block(png_ptr);
+ const unsigned int pixel_depth = png_ptr->row_input_pixel_depth;
+ /* The number of input bytes read each time (cannot overflow because it is
+ * limited by PNG_ROW_BUFFER_SIZE):
+ */
+ const unsigned int input_byte_count = (max_pixels * pixel_depth) / 8U;
+ const unsigned int bpp = (pixel_depth+0x7U)>>3;
+ const png_uint_32 width = png_ptr->width;
+ const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE;
+
+ png_uint_32 row_number = png_ptr->row_number;
+ unsigned int pass = png_ptr->pass;
+ enum anonymous {
+ start_of_row = 0U, /* at the start of the row; read a filter byte */
+ need_row_bytes = 2U, /* reading the row */
+ processing_row = 3U /* control returned to caller to process the row */
+ } state = png_upcast(enum anonymous, png_ptr->row_state);
+
+ /* The caller is responsible for calling png_read_start_IDAT: */
+ affirm(png_ptr->zowner == png_IDAT);
+
+ /* Basic sanity checks: */
+ affirm(pixel_depth > 0U && pixel_depth <= 64U &&
+ input_byte_count <= PNG_ROW_BUFFER_SIZE &&
+ pixel_depth <= 8U*PNG_MAX_PIXEL_BYTES);
+
+ for (;;) switch (state)
+ {
+ png_alloc_size_t row_bytes_processed;
+ png_alloc_size_t bytes_read; /* bytes in pixel_buffer */
+ png_uint_32 pass_width;
+ png_byte row_filter;
+ union
+ {
+ PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
+ png_byte buffer[16U];
+ } previous_pixels;
+ union
+ {
+ PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
+ png_byte buffer[PNG_ROW_BUFFER_SIZE];
+ } pixel_buffer;
+
+ case need_row_bytes:
+ /* The above variables need to be restored: */
+ row_bytes_processed = png_ptr->row_bytes_read;
+ bytes_read = row_bytes_processed % input_byte_count;
+ row_bytes_processed -= bytes_read;
+
+ pass_width = width;
+ if (interlaced)
+ pass_width = PNG_PASS_COLS(pass_width, pass);
+
+ memcpy(pixel_buffer.buffer, png_ptr->scratch, bytes_read);
+ memcpy(previous_pixels.buffer, png_ptr->scratch+bytes_read, 2*bpp);
+ row_filter = png_ptr->scratch[bytes_read+2*bpp];
+
+ goto pixel_loop;
+
+ case processing_row:
+ /* When there was a previous row (not at the start of the image) the
+ * row number needs to be updated and, possibly, the pass number.
+ */
+ if (++row_number == png_ptr->height)
+ {
+ affirm(interlaced && pass < 6); /* else too many calls */
+
+ /* Start a new pass: there never is a pending filter byte so it
+ * is always necessary to read the filter byte of the next row.
+ */
+ png_ptr->pass = ++pass & 0x7;
+ row_number = 0U;
+ } /* end of pass */
+
+ png_ptr->row_number = row_number;
+
+ /* This is a new row, but it may not be in the pass data so it
+ * may be possible to simply return control to the caller to
+ * skip it or use the previous row as appropriate.
+ */
+ if (interlaced)
+ {
+ debug(pass <= 6);
+
+ /* This macro cannot overflow because the PNG width (and height)
+ * have already been checked to ensure that they are less than
+ * 2^31 (i.e. they are 31-bit values, not 32-bit values.)
+ */
+ pass_width = PNG_PASS_COLS(width, pass);
+
+ /* On average most rows are skipped, so do this first: */
+ if (pass_width == 0 ||
+ !PNG_ROW_IN_INTERLACE_PASS(row_number, pass))
+ {
+ /* Using the PNG specification numbering (pass+1), passes 1,
+ * 2, 4, 6 contribute to all the rows in 'block' interlaced
+ * filling mode. Pass 3 contributes to four rows (5,6,7,8),
+ * pass 5 to two rows (3,4 then 7,8) and pass 7 only to one
+ * (the one on which it is processed). have_row must be set
+ * appropriately; it is set when a row is processed (end of
+ * this function) and remains set while the 'block' mode of
+ * interlace handling should reuse the previous row for this
+ * row.
+ *
+ * Each pass row can be used in a fixed number of rows, shown
+ * in 'rows' below, the '*' indicates that the row is actually
+ * in the pass, the '^' that the previous '*' row is used in
+ * block display update and the '@' that the pass doesn't
+ * contribte at all to that row in block display mode:
+ *
+ * PASS: 0 1 2 3 4 5 6
+ * rows: 8 8 4 4 2 2 1
+ * 0: * * @ * @ * @
+ * 1: ^ ^ @ ^ @ ^ *
+ * 2: ^ ^ @ ^ * * @
+ * 3: ^ ^ @ ^ ^ ^ *
+ * 4: ^ ^ * * @ * @
+ * 5: ^ ^ ^ ^ @ ^ *
+ * 6: ^ ^ ^ ^ * * @
+ * 7: ^ ^ ^ ^ ^ ^ *
+ *
+ * The '@' signs are the interesting thing, since we know that
+ * this row isn't present in the pass data. Rewriting the
+ * above table with '1' for '@', little endian (i.e. row 0 at
+ * the LSB end):
+ *
+ * row: 76543210
+ * Pass 0: 00000000 0x00 [bit 3, 0x8 of row unset (always)]
+ * Pass 1: 00000000 0x00
+ * Pass 2: 00001111 0x0F [bit 2, 0x4 of row unset]
+ * Pass 3: 00000000 0x00
+ * Pass 4: 00110011 0x33 [bit 1, 0x2 of row unset]
+ * Pass 5: 00000000 0x00
+ * Pass 6: 01010101 0x55 [bit 0, 0x1 of row unset]
+ *
+ * PNG_PASS_BLOCK_SKIP(pass, row) can be written two ways;
+ *
+ * As a shift and a mask:
+ * (0x55330F00 >> ((pass >> 1) + (row & 7))) & ~pass & 1
+ *
+ * And, somewhat simpler, as a bit check on the low bits of
+ * row:
+ *
+ * ~((row) >> (3-(pass >> 1))) & ~pass & 1
+ */
+# define PNG_PASS_BLOCK_SKIP(pass, row)\
+ (~((row) >> (3U-((pass) >> 1))) & ~(pass) & 0x1U)
+
+ /* Hence: */
+ debug(png_ptr->row_state == processing_row);
+ return pass_width == 0 || PNG_PASS_BLOCK_SKIP(pass,
+ row_number) ? png_row_skip : png_row_repeat;
+ } /* skipped row */
+
+ /* processed; fall through to start_of_row */
+ } /* interlaced */
+
+ /* FALL THROUGH */
+ case start_of_row:
+ {
+ /* Read the filter byte for the next row, previous_pixels is just
+ * used as a temporary buffer; it is reset below.
+ */
+ png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 0/*finish*/,
+ previous_pixels.buffer, 1U);
+
+ /* This can be temporary; it verifies the invariants on how
+ * png_inflate_IDAT updates the {next,avail}_out fields:
+ */
+#ifndef __COVERITY__ /* Suppress bogus Coverity complaint */
+ debug(png_ptr->zstream.avail_out == 1-cb &&
+ png_ptr->zstream.next_out == cb + previous_pixels.buffer);
+#endif
+
+ /* next_out points to previous_pixels, for security do this: */
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->zstream.avail_out = 0U;
+
+ /* One byte, so we either got it or have to get more input data: */
+ if (cb != 1U)
+ {
+ affirm(cb == 0U && png_ptr->zstream.avail_in == 0U);
+ png_ptr->row_state = start_of_row;
+ return png_row_incomplete;
+ }
+ }
+
+ /* Check the filter byte. */
+ row_filter = previous_pixels.buffer[0];
+ if (row_filter >= PNG_FILTER_VALUE_LAST)
+ png_chunk_error(png_ptr, "invalid PNG filter");
+
+ /* These are needed for the filter check below: */
+ pass_width = width;
+ if (interlaced)
+ pass_width = PNG_PASS_COLS(pass_width, pass);
+
+ /* The filter is followed by the row data, but first check the
+ * filter byte; the spec requires that we invent an empty row
+ * if the first row of a pass requires it.
+ *
+ * Note that row_number is the image row.
+ */
+ if (row_number == PNG_PASS_START_ROW(pass)) switch (row_filter)
+ {
+ case PNG_FILTER_VALUE_UP:
+ /* x-0 == x, so do this optimization: */
+ row_filter = PNG_FILTER_VALUE_NONE;
+ break;
+
+ case PNG_FILTER_VALUE_PAETH:
+ /* The Paeth predictor is always the preceding (leftwards)
+ * value, so this is the same as sub:
+ */
+ row_filter = PNG_FILTER_VALUE_SUB;
+ break;
+
+ case PNG_FILTER_VALUE_AVG:
+ /* It would be possible to 'invent' a new filter that did
+ * AVG using only the previous byte; it's 'SUB' of half the
+ * preceding value, but this seems pointless. Zero out the
+ * row buffer to make AVG work.
+ */
+ memset(png_ptr->row_buffer, 0U,
+ PNG_ROWBYTES(pixel_depth, pass_width));
+ break;
+
+ default:
+ break;
+ } /* switch row_filter */
+
+ /* Always zero the 'previous pixel' out at the start of a row; this
+ * allows the filter code to ignore the row start.
+ */
+ memset(previous_pixels.buffer, 0U, sizeof previous_pixels.buffer);
+
+ row_bytes_processed = 0U;
+ bytes_read = 0U;
+
+ pixel_loop:
+ /* At this point the following must be set correctly:
+ *
+ * row_bytes_processed: bytes processed so far
+ * pass_width: width of a row in this pass in pixels
+ * pixel_depth: depth in bits of a pixel
+ * bytes_read: count of filtered bytes in pixel_buffer
+ * row_filter: filter byte for this row
+ * previous_pixels[0]: pixel 'a' for the filter
+ * previous_pixels[1]: pixel 'c' for the filter
+ * pixel_buffer[]: bytes_read filtered bytes
+ *
+ * The code in the loop decompresses PNG_ROW_BUFFER_SIZE filterd pixels
+ * from the input, unfilters and transforms them, then saves them to
+ * png_struct::row_buffer[row_bytes_processed...].
+ */
+ { /* pixel loop */
+ const png_alloc_size_t row_bytes =
+ PNG_ROWBYTES(pixel_depth, pass_width);
+ png_bytep row_buffer = png_ptr->row_buffer + row_bytes_processed;
+ unsigned int pixels;
+ png_uint_32 x;
+
+ /* Sanity check for potential buffer overwrite: */
+ affirm(row_bytes > row_bytes_processed);
+
+ /* Work out the current pixel index of the pixel at the start of the
+ * row buffer:
+ */
+ switch (pixel_depth)
+ {
+ case 1U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 3);
+ break;
+ case 2U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 2);
+ break;
+ case 4U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 1);
+ break;
+ case 8U: x = (png_uint_32)/*SAFE*/row_bytes_processed;
+ break;
+ default: x = (png_uint_32)/*SAFE*/(row_bytes_processed / bpp);
+ debug(row_bytes_processed % bpp == 0U);
+ break;
+ }
+
+ for (pixels = max_pixels; x < pass_width; x += pixels)
+ {
+ if (pixels > pass_width - x)
+ pixels = (unsigned int)/*SAFE*/(pass_width - x);
+
+ /* At the end of the image pass Z_FINISH to zlib to optimize the
+ * final read (very slightly, is this worth doing?) To do this
+ * work out if we are at the end.
+ */
+ {
+ const png_uint_32 height = png_ptr->height;
+
+ /* last_pass_row indicates that this is the last row in this
+ * pass (the test is optimized for the non-interlaced case):
+ */
+ const int last_pass_row = row_number+1 >= height ||
+ (interlaced && PNG_LAST_PASS_ROW(row_number,pass,height));
+
+ /* Set 'finish' if this is the last row in the last pass of
+ * the image:
+ */
+ const int finish = last_pass_row && (!interlaced ||
+ pass >= PNG_LAST_PASS(width, height));
+
+ const png_alloc_size_t bytes_to_read =
+ PNG_ROWBYTES(pixel_depth, pixels);
+
+ png_alloc_size_t cb;
+
+ affirm(bytes_to_read > bytes_read);
+ cb = png_inflate_IDAT(png_ptr, finish,
+ pixel_buffer.buffer + bytes_read,
+ bytes_to_read - bytes_read);
+ bytes_read += cb;
+
+ if (bytes_read < bytes_to_read)
+ {
+ /* Fewer bytes were read than needed: we need to stash all
+ * the information required at pixel_loop in png_struct so
+ * that the need_row_bytes case can restore it when more
+ * input is available.
+ */
+ debug(png_ptr->zstream.avail_in == 0U);
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->zstream.avail_out = 0U;
+
+ png_ptr->row_bytes_read = row_bytes_processed + bytes_read;
+ memcpy(png_ptr->scratch, pixel_buffer.buffer, bytes_read);
+ memcpy(png_ptr->scratch+bytes_read, previous_pixels.buffer,
+ 2*bpp);
+ png_ptr->scratch[bytes_read+2*bpp] = row_filter;
+ png_ptr->row_state = need_row_bytes;
+ return png_row_incomplete;
+ }
+
+ debug(bytes_read == bytes_to_read);
+ } /* fill pixel_buffer */
+
+ /* The buffer is full or the row is complete but the calculation
+ * was done using the pixel count, so double check against the
+ * byte count here:
+ */
+ implies(bytes_read != input_byte_count,
+ bytes_read == row_bytes - row_bytes_processed);
+
+ /* At this point all the required information to process the next
+ * block of pixels in the row has been read from the input stream
+ * and the original, filtered, row data is held in pixel_buffer.
+ *
+ * Because the buffer will be transformed after the unfilter
+ * operation we require whole pixels:
+ */
+ debug(bytes_read >= bpp && bytes_read % bpp == 0);
+
+ if (row_filter > PNG_FILTER_VALUE_NONE)
+ {
+ /* This is checked in the read code above: */
+ debug(row_filter < PNG_FILTER_VALUE_LAST);
+
+ /* Lazy init of the read functions, which allows hand crafted
+ * optimizations for 'bpp' (which does not change.)
+ */
+ if (png_ptr->read_filter[0] == NULL)
+ png_init_filter_functions(png_ptr, bpp);
+
+ /* Pixels 'a' then 'c' are in previous_pixels, pixel 'b' is in
+ * row_buffer and pixel 'x' (filtered) is in pixel_buffer.
+ */
+ png_ptr->read_filter[row_filter-1](bytes_read, bpp,
+ pixel_buffer.buffer, row_buffer, previous_pixels.buffer);
+ } /* do the filter */
+
+ /* Now pixel_buffer.buffer contains the *un*filtered bytes of the
+ * current row and row_buffer needs updating with these. First
+ * preserve pixels 'a' and 'c' for the next time round the loop
+ * (if necessary).
+ */
+ if (bytes_read < row_bytes - row_bytes_processed)
+ {
+ debug(bytes_read == input_byte_count);
+ memcpy(previous_pixels.buffer/* pixel 'a' */,
+ pixel_buffer.buffer + bytes_read - bpp, bpp);
+ memcpy(previous_pixels.buffer+bpp/* pixel 'c' */,
+ row_buffer + bytes_read - bpp, bpp);
+ }
+
+ /* Now overwrite the previous row pixels in row_buffer with the
+ * current row pixels:
+ */
+ memcpy(row_buffer, pixel_buffer.buffer, bytes_read);
+ row_buffer += bytes_read;
+ row_bytes_processed += bytes_read;
+ bytes_read = 0U; /* for next buffer */
+
+ /* Any transforms can now be performed along with any output
+ * handling (copy or interlace handling).
+ */
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ if (png_ptr->transform_list != NULL)
+ {
+ unsigned int max_depth;
+ png_transform_control tc;
+
+ png_init_transform_control(&tc, png_ptr);
+
+ tc.width = pixels;
+ tc.sp = tc.dp = pixel_buffer.buffer;
+
+ /* Run the list. It is ok if it doesn't end up doing
+ * anything; this can happen with a lazy init.
+ *
+ * NOTE: if the only thing in the list is a palette check
+ * function it can remove itself at this point.
+ */
+ max_depth = png_run_transform_list_forwards(png_ptr, &tc);
+
+ /* This is too late, a stack overwrite has already
+ * happened, but it may still prevent exploits:
+ */
+ affirm(max_depth <= png_ptr->row_max_pixel_depth);
+
+ /* It is very important that the transform produces the
+ * same pixel format as the TC_INIT steps:
+ */
+ affirm(png_ptr->row_format == tc.format &&
+ png_ptr->row_range == tc.range &&
+ png_ptr->row_bit_depth == tc.bit_depth);
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ /* This checks the output gamma taking into account the
+ * fact that small gamma changes are eliminated.
+ */
+ debug(png_ptr->row_gamma == tc.gamma ||
+ png_gamma_check(png_ptr, &tc));
+# endif /* READ_GAMMA */
+
+ /* If the caller needs the row saved (for the progressive
+ * read API) or if this PNG is interlaced and this row may
+ * be required in a subsequent pass (any pass before the
+ * last one) then it is stored in
+ * png_struct::transformed_row, and that may need to be
+ * allocated here.
+ */
+# if defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\
+ defined(PNG_READ_INTERLACING_SUPPORTED)
+ if (png_ptr->transform_list != NULL &&
+ (save_row
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ || (png_ptr->do_interlace && pass < 6U)
+# endif /* READ_INTERLACING */
+ ))
+ {
+ if (png_ptr->transformed_row == NULL)
+ png_ptr->transformed_row = png_voidcast(png_bytep,
+ png_malloc(png_ptr, png_calc_rowbytes(png_ptr,
+ png_ptr->row_bit_depth *
+ PNG_FORMAT_CHANNELS(png_ptr->row_format),
+ save_row ? width : (width+1U)>>1)));
+
+ copy_row(png_ptr, png_ptr->transformed_row,
+ pixel_buffer.buffer, x, pixels, 1/*clear*/);
+ }
+# endif /* PROGRESSIVE_READ || READ_INTERLACING */
+ } /* transform_list != NULL */
+# endif /* TRANSFORM_MECH */
+
+ /* There are now 'pixels' possibly transformed pixels starting at
+ * row pixel x, where 'x' is an index in the interlaced row if
+ * interlacing is happening. Handle this row.
+ */
+ if (transformed_row != NULL)
+ combine_row(png_ptr, transformed_row, pixel_buffer.buffer,
+ x, pixels, 0/*!display*/);
+
+ if (display_row != NULL)
+ combine_row(png_ptr, display_row, pixel_buffer.buffer, x,
+ pixels, 1/*display*/);
+ } /* for x < pass_width */
+ } /* pixel loop */
+
+ png_ptr->row_state = processing_row;
+ return png_row_process;
+
+ default:
+ impossible("bad row state");
+ } /* forever switch */
+
+ PNG_UNUSED(save_row) /* May not be used above */
}
void /* PRIVATE */
+png_read_free_row_buffers(png_structrp png_ptr)
+{
+ /* The transformed row only gets saved if needed: */
+# if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\
+ defined(PNG_READ_INTERLACING_SUPPORTED)) &&\
+ defined(PNG_TRANSFORM_MECH_SUPPORTED)
+ if (png_ptr->transformed_row != NULL)
+ {
+ png_free(png_ptr, png_ptr->transformed_row);
+ png_ptr->transformed_row = NULL;
+ }
+# endif /* PROGRESSIVE_READ || READ_INTERLACING */
+
+ if (png_ptr->row_buffer != NULL)
+ {
+ png_free(png_ptr, png_ptr->row_buffer);
+ png_ptr->row_buffer = NULL;
+ }
+}
+
+/* Complete reading of the IDAT chunks. This returns 0 if more data is to
+ * be read, 1 if the zlib stream has terminated. Call this routine with
+ * zstream.avail_in greater than zero unless there is no more input data.
+ * When zstream_avail_in is 0 on entry and the stream does not terminate
+ * an "IDAT truncated" error will be output.
+ */
+int /* PRIVATE */
png_read_finish_IDAT(png_structrp png_ptr)
{
+ enum
+ {
+ no_error = 0,
+ LZ_too_long,
+ IDAT_too_long,
+ IDAT_truncated
+ } error = no_error;
+
+ /* Release the rowd buffers first; they can use considerable amounts of
+ * memory.
+ */
+ png_read_free_row_buffers(png_ptr);
+
+ affirm(png_ptr->zowner == png_IDAT); /* else this should not be called */
+
/* 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))
+ if (!png_ptr->zstream_ended)
{
- /* 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 */
+ int end_of_IDAT = png_ptr->zstream.avail_in == 0;
+ png_byte b[1];
+ png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 2/*finish*/, b, 1);
- /* Now clear everything out for safety; the following may not have been
- * done.
- */
- if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+ debug(png_ptr->zstream.avail_out == 1-cb &&
+ png_ptr->zstream.next_out == cb + b);
+
+ /* As above, for safety do this: */
+ png_ptr->zstream.next_out = NULL;
+ png_ptr->zstream.avail_out = 0;
+
+ /* No data is expected, either compressed or in the IDAT: */
+ if (cb != 0)
+ error = LZ_too_long;
+
+ else if (png_ptr->zstream.avail_in == 0 /* && cb == 0 */)
{
- png_ptr->mode |= PNG_AFTER_IDAT;
- png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+ /* This is the normal case but there may still be some waiting codes
+ * (including the adler32 that follow the LZ77 end code; so we can
+ * have at least 5 bytes after the end of the row data before the
+ * end of the stream.
+ */
+ if (!png_ptr->zstream_ended)
+ {
+ if (!end_of_IDAT)
+ return 0; /* keep reading, no detectable error yet */
+
+ error = IDAT_truncated;
+ }
+
+ /* Else there may still be an error; too much IDAT, but we can't
+ * tell.
+ */
}
}
- /* If the zstream has not been released do it now *and* terminate the reading
- * of the final IDAT chunk.
+ /* If there is still pending zstream input then there was too much IDAT
+ * data:
+ */
+ if (!error && png_ptr->zstream.avail_in > 0)
+ error = IDAT_too_long;
+
+ /* Either this is the success case or an error has been detected and
+ * warned about.
*/
- 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;
+ int ret = inflateEnd(&png_ptr->zstream);
- /* 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.
+ /* In fact we expect this to always succeed, so it is a good idea to
+ * catch it in pre-release builds:
*/
- (void)png_crc_finish(png_ptr, png_ptr->idat_size);
- }
-}
+ debug_handled(ret == Z_OK);
-void /* PRIVATE */
-png_read_finish_row(png_structrp png_ptr)
-{
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* 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};
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
-
- png_debug(1, "in png_read_finish_row");
- png_ptr->row_number++;
- if (png_ptr->row_number < png_ptr->num_rows)
- return;
-
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- if (png_ptr->interlaced)
- {
- 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
+ if (ret != Z_OK)
{
- png_ptr->pass++;
+ /* This is just a warning; it's safe, and the zstream_error flag is
+ * not set.
+ */
+ png_zstream_error(&png_ptr->zstream, ret);
+ png_chunk_warning(png_ptr, png_ptr->zstream.msg);
+ }
+ }
- if (png_ptr->pass >= 7)
+ /* Output an error message if required: */
+ if (error && !png_ptr->zstream_error)
+ {
+ switch (error)
+ {
+ case LZ_too_long:
+ png_benign_error(png_ptr, "compressed data too long");
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];
+ case IDAT_too_long:
+ png_benign_error(png_ptr, "uncompressed data too long");
+ break;
- if (!(png_ptr->transformations & PNG_INTERLACE))
- {
- 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];
- }
+ case IDAT_truncated:
+ png_benign_error(png_ptr, "data truncated");
+ break;
- else /* if (png_ptr->transformations & PNG_INTERLACE) */
- break; /* libpng deinterlacing sees every row */
+ default:
+ case no_error: /* Satisfy the compiler */
+ break;
+ }
- } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
-
- if (png_ptr->pass < 7)
- return;
- }
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
-
- /* Here after at the end of the last row of the last pass. */
- png_read_finish_IDAT(png_ptr);
-}
-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
-
-void /* PRIVATE */
-png_read_start_row(png_structrp png_ptr)
-{
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- /* 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};
-#endif
-
- 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
-#ifdef PNG_READ_INTERLACING_SUPPORTED
- if (png_ptr->interlaced)
- {
- if (!(png_ptr->transformations & PNG_INTERLACE))
- 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];
+ png_ptr->zstream_error = 1;
}
- else
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
- {
- 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 simpliar 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.
+ /* WARNING: leave {next,avail}_in set here, the progressive reader uses these
+ * to complete the PNG chunk CRC calculation.
*/
-#ifdef PNG_READ_PACK_SUPPORTED
- if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
- max_pixel_depth = 8;
-#endif
+ png_ptr->zstream_ended = 1;
+ png_ptr->zowner = 0;
-#ifdef PNG_READ_EXPAND_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND)
+ return 1; /* end of stream */
+}
+
+/* Optional call to update the users info_ptr structure, can be used from both
+ * the progressive and sequential reader, but the app must call it.
+ */
+void PNGAPI
+png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)
+{
+ png_debug(1, "in png_read_update_info");
+
+ if (png_ptr != NULL)
{
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ if (png_ptr->zowner != png_IDAT)
{
- if (png_ptr->num_trans)
- max_pixel_depth = 32;
+ png_read_start_IDAT(png_ptr);
- 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)
- max_pixel_depth *= 2;
- }
-
- else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
- {
- if (png_ptr->num_trans)
- {
- max_pixel_depth *= 4;
- max_pixel_depth /= 3;
- }
- }
- }
-#endif
-
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND_16)
- {
-# 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)
- {
- 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))
- {
- 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)
- {
- if (
-#ifdef PNG_READ_EXPAND_SUPPORTED
- (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
-#endif
-#ifdef PNG_READ_FILLER_SUPPORTED
- (png_ptr->transformations & (PNG_FILLER)) ||
-#endif
- png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- if (max_pixel_depth <= 16)
- max_pixel_depth = 32;
-
- else
- max_pixel_depth = 64;
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ png_read_transform_info(png_ptr, info_ptr);
+# else
+ PNG_UNUSED(info_ptr)
+# endif
}
+ /* New in 1.6.0 this avoids the bug of doing the initializations twice */
else
- {
- if (max_pixel_depth <= 8)
- {
- if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- max_pixel_depth = 32;
+ png_app_error(png_ptr,
+ "png_read_update_info/png_start_read_image: duplicate call");
+ }
+}
+
+png_int_32 /* PRIVATE */
+png_read_setting(png_structrp png_ptr, png_uint_32 setting,
+ png_uint_32 parameter, png_int_32 value)
+{
+ /* Caller checks the arguments for basic validity */
+ int only_get = (setting & PNG_SF_GET) != 0U;
+
+ if (only_get) /* spurious: in case it isn't used */
+ setting &= ~PNG_SF_GET;
+
+ switch (setting)
+ {
+# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ case PNG_SR_COMPRESS_buffer_size:
+ if (parameter > 0 && parameter <= ZLIB_IO_MAX)
+ {
+ png_ptr->IDAT_size = parameter;
+ return 0; /* Cannot return a 32-bit value */
+ }
else
- max_pixel_depth = 24;
+ return PNG_EINVAL;
+# endif /* SEQUENTIAL_READ */
+
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ case PNG_SR_GAMMA_threshold:
+ if (parameter <= 0xFFFF)
+ {
+ if (!only_get)
+ png_ptr->gamma_threshold = PNG_UINT_16(parameter);
+
+ return (png_int_32)/*SAFE*/parameter;
+ }
+
+ return PNG_EDOM;
+
+#if 0 /*NYI*/
+ case PNG_SR_GAMMA_accuracy:
+ if (parameter <= 1600)
+ {
+ if (!only_get)
+ png_ptr->gamma_accuracy = parameter;
+
+ return (png_int_32)/*SAFE*/parameter;
+ }
+
+ return PNG_EDOM;
+#endif /*NYI*/
+# endif /* READ_GAMMA */
+
+ case PNG_SR_CRC_ACTION:
+ /* Tell libpng how we react to CRC errors in critical chunks */
+ switch (parameter)
+ {
+ case PNG_CRC_NO_CHANGE: /* Leave setting as is */
+ break;
+
+ case PNG_CRC_WARN_USE: /* Warn/use data */
+ png_ptr->critical_crc = crc_warn_use;
+ break;
+
+ case PNG_CRC_QUIET_USE: /* Quiet/use data */
+ png_ptr->critical_crc = crc_quiet_use;
+ break;
+
+ default:
+ case PNG_CRC_WARN_DISCARD: /* Not valid for critical data */
+ return PNG_EINVAL;
+
+ case PNG_CRC_ERROR_QUIT: /* Error/quit */
+ case PNG_CRC_DEFAULT:
+ png_ptr->critical_crc = crc_error_quit;
+ break;
}
- else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- max_pixel_depth = 64;
+ /* Tell libpng how we react to CRC errors in ancillary chunks */
+ switch (value)
+ {
+ case PNG_CRC_NO_CHANGE: /* Leave setting as is */
+ break;
- else
- max_pixel_depth = 48;
- }
+ case PNG_CRC_WARN_USE: /* Warn/use data */
+ png_ptr->ancillary_crc = crc_warn_use;
+ break;
+
+ case PNG_CRC_QUIET_USE: /* Quiet/use data */
+ png_ptr->ancillary_crc = crc_quiet_use;
+ break;
+
+ case PNG_CRC_ERROR_QUIT: /* Error/quit */
+ png_ptr->ancillary_crc = crc_error_quit;
+ break;
+
+ case PNG_CRC_WARN_DISCARD: /* Warn/discard data */
+ case PNG_CRC_DEFAULT:
+ png_ptr->ancillary_crc = crc_warn_discard;
+ break;
+
+ default:
+ return PNG_EINVAL;
+ }
+
+ return 0; /* success */
+
+# ifdef PNG_SET_OPTION_SUPPORTED
+ case PNG_SRW_OPTION:
+ switch (parameter)
+ {
+ case PNG_MAXIMUM_INFLATE_WINDOW:
+ if (png_ptr->maximum_inflate_window)
+ {
+ if (!value && !only_get)
+ png_ptr->maximum_inflate_window = 0U;
+ return PNG_OPTION_ON;
+ }
+
+ else
+ {
+ if (value && !only_get)
+ png_ptr->maximum_inflate_window = 1U;
+ return PNG_OPTION_OFF;
+ }
+
+ default:
+ return PNG_OPTION_UNSET;
+ }
+# endif /* SET_OPTION */
+
+# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+ case PNG_SRW_CHECK_FOR_INVALID_INDEX:
+ /* The 'enabled' value is a FORTRAN style three-state: */
+ if (value > 0)
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON;
+
+ else if (value < 0)
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF;
+
+ else
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT;
+
+ return 0;
+# endif /* READ_CHECK_FOR_INVALID_INDEX */
+
+# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
+ case PNG_SRW_ERROR_HANDLING:
+ /* The parameter is a bit mask of what to set, the value is what to
+ * set it to.
+ */
+ if (value >= PNG_IGNORE && value <= PNG_ERROR &&
+ parameter <= PNG_ALL_ERRORS)
+ {
+ if ((parameter & PNG_BENIGN_ERRORS) != 0U)
+ png_ptr->benign_error_action = value & 0x3U;
+
+ if ((parameter & PNG_APP_WARNINGS) != 0U)
+ png_ptr->app_warning_action = value & 0x3U;
+
+ if ((parameter & PNG_APP_ERRORS) != 0U)
+ png_ptr->app_error_action = value & 0x3U;
+
+ if ((parameter & PNG_IDAT_ERRORS) != 0U)
+ png_ptr->IDAT_error_action = value & 0x3U;
+
+ return 0;
+ }
+
+ return PNG_EINVAL;
+# endif /* BENIGN_READ_ERRORS */
+
+ default:
+ return PNG_ENOSYS; /* not supported (whatever it is) */
}
-#endif
-
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
-defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
- if (png_ptr->transformations & PNG_USER_TRANSFORM)
- {
- 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->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)
- 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->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)
- {
- 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 /* PNG_READ_SUPPORTED */
+#endif /* READ */
diff --git a/ext/libpng17/pngset.c b/ext/libpng17/pngset.c
index f784fbf08b..abd13c377c 100644
--- a/ext/libpng17/pngset.c
+++ b/ext/libpng17/pngset.c
@@ -2,7 +2,7 @@
/* pngset.c - storage of image information into info struct
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -17,6 +17,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngset
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
@@ -59,7 +60,7 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
xy.whitey = white_y;
if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy,
- 2/* override with app values*/))
+ 2/* override with app values*/) != 0)
info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
png_colorspace_sync_info(png_ptr, info_ptr);
@@ -90,7 +91,8 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
XYZ.blue_Y = int_blue_Y;
XYZ.blue_Z = int_blue_Z;
- if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2))
+ if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace,
+ &XYZ, 2) != 0)
info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
png_colorspace_sync_info(png_ptr, info_ptr);
@@ -103,14 +105,14 @@ png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
double green_x, double green_y, double blue_x, double blue_y)
{
png_set_cHRM_fixed(png_ptr, info_ptr,
- png_fixed(png_ptr, white_x, "cHRM White X"),
- png_fixed(png_ptr, white_y, "cHRM White Y"),
- png_fixed(png_ptr, red_x, "cHRM Red X"),
- png_fixed(png_ptr, red_y, "cHRM Red Y"),
- png_fixed(png_ptr, green_x, "cHRM Green X"),
- png_fixed(png_ptr, green_y, "cHRM Green Y"),
- png_fixed(png_ptr, blue_x, "cHRM Blue X"),
- png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
+ png_fixed(png_ptr, white_x, "cHRM White X"),
+ png_fixed(png_ptr, white_y, "cHRM White Y"),
+ png_fixed(png_ptr, red_x, "cHRM Red X"),
+ png_fixed(png_ptr, red_y, "cHRM Red Y"),
+ png_fixed(png_ptr, green_x, "cHRM Green X"),
+ png_fixed(png_ptr, green_y, "cHRM Green Y"),
+ png_fixed(png_ptr, blue_x, "cHRM Blue X"),
+ png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
}
void PNGAPI
@@ -119,19 +121,19 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X,
double blue_X, double blue_Y, double blue_Z)
{
png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,
- png_fixed(png_ptr, red_X, "cHRM Red X"),
- png_fixed(png_ptr, red_Y, "cHRM Red Y"),
- png_fixed(png_ptr, red_Z, "cHRM Red Z"),
- png_fixed(png_ptr, green_X, "cHRM Red X"),
- png_fixed(png_ptr, green_Y, "cHRM Red Y"),
- png_fixed(png_ptr, green_Z, "cHRM Red Z"),
- png_fixed(png_ptr, blue_X, "cHRM Red X"),
- png_fixed(png_ptr, blue_Y, "cHRM Red Y"),
- png_fixed(png_ptr, blue_Z, "cHRM Red Z"));
+ png_fixed(png_ptr, red_X, "cHRM Red X"),
+ png_fixed(png_ptr, red_Y, "cHRM Red Y"),
+ png_fixed(png_ptr, red_Z, "cHRM Red Z"),
+ png_fixed(png_ptr, green_X, "cHRM Green X"),
+ png_fixed(png_ptr, green_Y, "cHRM Green Y"),
+ png_fixed(png_ptr, green_Z, "cHRM Green Z"),
+ png_fixed(png_ptr, blue_X, "cHRM Blue X"),
+ png_fixed(png_ptr, blue_Y, "cHRM Blue Y"),
+ png_fixed(png_ptr, blue_Z, "cHRM Blue Z"));
}
-# endif /* PNG_FLOATING_POINT_SUPPORTED */
+# endif /* FLOATING_POINT */
-#endif /* PNG_cHRM_SUPPORTED */
+#endif /* cHRM */
#ifdef PNG_gAMA_SUPPORTED
void PNGFAPI
@@ -162,8 +164,6 @@ void PNGAPI
png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
png_const_uint_16p hist)
{
- int i;
-
png_debug1(1, "in %s storage function", "hIST");
if (png_ptr == NULL || info_ptr == NULL)
@@ -189,13 +189,18 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->hist == NULL)
{
png_warning(png_ptr, "Insufficient memory for hIST chunk data");
+
return;
}
info_ptr->free_me |= PNG_FREE_HIST;
- for (i = 0; i < info_ptr->num_palette; i++)
- info_ptr->hist[i] = hist[i];
+ {
+ unsigned int i;
+
+ for (i = 0; i < info_ptr->num_palette; i++)
+ info_ptr->hist[i] = hist[i];
+ }
info_ptr->valid |= PNG_INFO_hIST;
}
@@ -214,31 +219,16 @@ png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr,
info_ptr->width = width;
info_ptr->height = height;
- info_ptr->bit_depth = (png_byte)bit_depth;
- info_ptr->color_type = (png_byte)color_type;
- info_ptr->compression_type = (png_byte)compression_type;
- info_ptr->filter_type = (png_byte)filter_type;
- info_ptr->interlace_type = (png_byte)interlace_type;
+ info_ptr->bit_depth = png_check_bits(png_ptr, bit_depth, 6);
+ info_ptr->format = png_check_bits(png_ptr,
+ PNG_FORMAT_FROM_COLOR_TYPE(color_type), PNG_RF_BITS);
+ info_ptr->compression_type = png_check_byte(png_ptr, compression_type);
+ info_ptr->filter_type = png_check_byte(png_ptr, filter_type);
+ info_ptr->interlace_type = png_check_byte(png_ptr, interlace_type);
png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,
- info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
+ info_ptr->bit_depth, color_type, info_ptr->interlace_type,
info_ptr->compression_type, info_ptr->filter_type);
-
- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
- info_ptr->channels = 1;
-
- else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
- info_ptr->channels = 3;
-
- else
- info_ptr->channels = 1;
-
- if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
- info_ptr->channels++;
-
- info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
-
- info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
}
#ifdef PNG_oFFs_SUPPORTED
@@ -253,7 +243,7 @@ png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr,
info_ptr->x_offset = offset_x;
info_ptr->y_offset = offset_y;
- info_ptr->offset_unit_type = (png_byte)unit_type;
+ info_ptr->offset_unit_type = png_check_byte(png_ptr, unit_type);
info_ptr->valid |= PNG_INFO_oFFs;
}
#endif
@@ -270,7 +260,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
png_debug1(1, "in %s storage function", "pCAL");
if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL
- || (nparams > 0 && params == NULL))
+ || (nparams > 0 && params == NULL))
return;
length = strlen(purpose) + 1;
@@ -288,16 +278,19 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
/* Validate params[nparams] */
for (i=0; ipcal_purpose = png_voidcast(png_charp,
- png_malloc_warn(png_ptr, length));
+ png_malloc_warn(png_ptr, length));
if (info_ptr->pcal_purpose == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL purpose");
+
return;
}
@@ -306,19 +299,20 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
png_debug(3, "storing X0, X1, type, and nparams in info");
info_ptr->pcal_X0 = X0;
info_ptr->pcal_X1 = X1;
- info_ptr->pcal_type = (png_byte)type;
- info_ptr->pcal_nparams = (png_byte)nparams;
+ info_ptr->pcal_type = png_check_byte(png_ptr, type);
+ info_ptr->pcal_nparams = png_check_byte(png_ptr, nparams);
length = strlen(units) + 1;
png_debug1(3, "allocating units for info (%lu bytes)",
- (unsigned long)length);
+ (unsigned long)length);
info_ptr->pcal_units = png_voidcast(png_charp,
- png_malloc_warn(png_ptr, length));
+ png_malloc_warn(png_ptr, length));
if (info_ptr->pcal_units == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL units");
+
return;
}
@@ -330,6 +324,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->pcal_params == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL params");
+
return;
}
@@ -346,6 +341,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->pcal_params[i] == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL parameter");
+
return;
}
@@ -383,18 +379,19 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh))
png_error(png_ptr, "Invalid sCAL height");
- info_ptr->scal_unit = (png_byte)unit;
+ info_ptr->scal_unit = png_check_byte(png_ptr, unit);
++lengthw;
png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);
info_ptr->scal_s_width = png_voidcast(png_charp,
- png_malloc_warn(png_ptr, lengthw));
+ png_malloc_warn(png_ptr, lengthw));
if (info_ptr->scal_s_width == NULL)
{
png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+
return;
}
@@ -405,7 +402,7 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);
info_ptr->scal_s_height = png_voidcast(png_charp,
- png_malloc_warn(png_ptr, lengthh));
+ png_malloc_warn(png_ptr, lengthh));
if (info_ptr->scal_s_height == NULL)
{
@@ -413,6 +410,7 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
info_ptr->scal_s_width = NULL;
png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+
return;
}
@@ -444,9 +442,9 @@ png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit,
char sheight[PNG_sCAL_MAX_DIGITS+1];
png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width,
- PNG_sCAL_PRECISION);
+ PNG_sCAL_PRECISION);
png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height,
- PNG_sCAL_PRECISION);
+ PNG_sCAL_PRECISION);
png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
}
@@ -494,7 +492,7 @@ png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr,
info_ptr->x_pixels_per_unit = res_x;
info_ptr->y_pixels_per_unit = res_y;
- info_ptr->phys_unit_type = (png_byte)unit_type;
+ info_ptr->phys_unit_type = png_check_byte(png_ptr, unit_type);
info_ptr->valid |= PNG_INFO_pHYs;
}
#endif
@@ -504,57 +502,69 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
png_const_colorp palette, int num_palette)
{
+ png_uint_32 max_palette_length;
+
png_debug1(1, "in %s storage function", "PLTE");
if (png_ptr == NULL || info_ptr == NULL)
return;
- if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH)
+ max_palette_length = (info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ?
+ (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
+
+ if (num_palette < 0 || num_palette > (int) max_palette_length)
{
- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
- png_error(png_ptr, "Invalid palette length");
+ if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ png_chunk_error(png_ptr, "Invalid palette length");
else
{
- png_warning(png_ptr, "Invalid palette length");
+ png_chunk_report(png_ptr, "Invalid palette length", PNG_CHUNK_ERROR);
return;
}
}
- if ((num_palette > 0 && palette == NULL) ||
- (num_palette == 0
-# ifdef PNG_MNG_FEATURES_SUPPORTED
- && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0
-# endif
- ))
- {
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ if ((num_palette > 0 && palette == NULL) || (num_palette == 0 &&
+ (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0))
png_error(png_ptr, "Invalid palette");
- return;
- }
+#else
+ if ((num_palette > 0 && palette == NULL) || num_palette == 0)
+ png_error(png_ptr, "Invalid palette");
+#endif /* MNG_FEATURES */
- /* It may not actually be necessary to set png_ptr->palette here;
- * we do it for backward compatibility with the way the png_handle_tRNS
- * function used to do the allocation.
- *
- * 1.6.0: the above statement appears to be incorrect; something has to set
- * the palette inside png_struct on read.
- */
png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
/* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
- * of num_palette entries, in case of an invalid PNG file that has
- * too-large sample values.
+ * of num_palette entries, in case of an invalid PNG file or incorrect
+ * call to png_set_PLTE() with too-large sample values.
*/
- png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
+ info_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr,
PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
if (num_palette > 0)
- memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color)));
- info_ptr->palette = png_ptr->palette;
- info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
+ memcpy(info_ptr->palette, palette, num_palette * (sizeof (png_color)));
+ /* Set the remainder of the palette entries to something recognizable; the
+ * code used to leave them set to 0, which made seeing palette index errors
+ * difficult.
+ */
+ if (num_palette < PNG_MAX_PALETTE_LENGTH)
+ {
+ int i;
+ png_color c;
+
+ memset(&c, 0x42, sizeof c); /* fill in any padding */
+ c.red = 0xbe;
+ c.green = 0xad;
+ c.blue = 0xed; /* Visible in memory as 'beaded' */
+
+ for (i=num_palette; ipalette[i] = c;
+ }
+
+ info_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9);
info_ptr->free_me |= PNG_FREE_PLTE;
-
info_ptr->valid |= PNG_INFO_PLTE;
}
@@ -595,7 +605,8 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
if (png_ptr == NULL || info_ptr == NULL)
return;
- if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent))
+ if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace,
+ srgb_intent) != 0)
{
/* This causes the gAMA and cHRM to be written too */
info_ptr->colorspace.flags |=
@@ -633,7 +644,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
*/
{
int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,
- proflen, profile, info_ptr->color_type);
+ proflen, profile, (info_ptr->format & PNG_FORMAT_FLAG_COLOR) != 0);
png_colorspace_sync_info(png_ptr, info_ptr);
@@ -643,7 +654,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
/* But do write the gAMA and cHRM chunks from the profile. */
info_ptr->colorspace.flags |=
- PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
+ PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
}
length = strlen(name)+1;
@@ -652,18 +663,20 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
if (new_iccp_name == NULL)
{
png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk");
+
return;
}
memcpy(new_iccp_name, name, length);
new_iccp_profile = png_voidcast(png_bytep,
- png_malloc_warn(png_ptr, proflen));
+ png_malloc_warn(png_ptr, proflen));
if (new_iccp_profile == NULL)
{
png_free(png_ptr, new_iccp_name);
png_benign_error(png_ptr,
"Insufficient memory to process iCCP profile");
+
return;
}
@@ -671,7 +684,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
- info_ptr->iccp_proflen = proflen;
info_ptr->iccp_name = new_iccp_name;
info_ptr->iccp_profile = new_iccp_profile;
info_ptr->free_me |= PNG_FREE_ICCP;
@@ -679,6 +691,77 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
}
#endif
+#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_tIME_SUPPORTED)
+static png_byte
+get_location(png_const_structrp png_ptr)
+ /* Return the correct location flag for a chunk. For a png_set_
+ * called during read this is the current read location, for a
+ * png_set_ called during write it is the following write location
+ * (because the chunks at the current location have already been written.)
+ * For a png_set_ called before read starts (none of the 'position'
+ * mode bits are set) the position is set to the start (PNG_HAVE_IHDR). For
+ * a png_set_chunk> called before write starts PNG_HAVE_PLTE|PNG_AFTER_IDAT
+ * are set because we don't know whether this is the main png_info or the one
+ * for use after the IDAT from png_write_end.
+ *
+ * The latter behavior gives compatibility with the old behavior of
+ * png_set_text; it only wrote text chunks after the PLTE or IDAT and it just
+ * wrote them once.
+ */
+{
+ if ((png_ptr->mode & PNG_AFTER_IDAT) != 0U)
+ return PNG_AFTER_IDAT;
+
+ else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0U)
+ {
+ /* In a write operation PNG_HAVE_PLTE is set when the chunks before the
+ * IDAT are written (in png_write_info), so the location needs to be after
+ * IDAT. In a read the chunk was read after the PLTE but before the IDAT.
+ */
+ if (png_ptr->read_struct)
+ return PNG_HAVE_PLTE;
+
+ else /* write struct */
+ return PNG_AFTER_IDAT;
+ }
+
+ else if ((png_ptr->mode & PNG_HAVE_IHDR) != 0U)
+ {
+ /* For read this means the chunk is between the IHDR and any PLTE; there
+ * may be none but then there is no order to preserve.
+ *
+ * For write png_write_IHDR has been called and that means that the info
+ * before PLTE has been written, so the chunk goes after.
+ */
+ if (png_ptr->read_struct)
+ return PNG_HAVE_IHDR;
+
+ else /* write struct */
+ return PNG_HAVE_PLTE;
+ }
+
+ else if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0U)
+ {
+ /* This should not happen on read, on write it means that the app has
+ * started writing so assume the text is meant to go before PLTE.
+ */
+ return PNG_HAVE_IHDR;
+ }
+
+ /* Either a png_set_ from the application during read before reading
+ * starts (so mode is 0) or the same for write.
+ */
+ else
+ {
+ if (png_ptr->read_struct)
+ return PNG_HAVE_IHDR;
+
+ else
+ return PNG_HAVE_PLTE|PNG_AFTER_IDAT;
+ }
+}
+#endif /* TEXT || tIME */
+
#ifdef PNG_TEXT_SUPPORTED
void PNGAPI
png_set_text(png_structrp png_ptr, png_inforp info_ptr,
@@ -697,7 +780,7 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
{
int i;
- png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" :
+ png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11 :
(unsigned long)png_ptr->chunk_name);
if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL)
@@ -714,6 +797,11 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
int max_text;
png_textp new_text = NULL;
+ /* The code below goes horribly wrong if old_num_text ever ends up
+ * negative, so:
+ */
+ affirm(old_num_text >= 0);
+
/* Calculate an appropriate max_text, checking for overflow. */
max_text = old_num_text;
if (num_text <= INT_MAX - max_text)
@@ -727,18 +815,18 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
else
max_text = INT_MAX;
- /* Now allocate a new array and copy the old members in, this does all
+ /* Now allocate a new array and copy the old members in; this does all
* the overflow checks.
*/
new_text = png_voidcast(png_textp,png_realloc_array(png_ptr,
- info_ptr->text, old_num_text, max_text-old_num_text,
- sizeof *new_text));
+ info_ptr->text, old_num_text, max_text-old_num_text,
+ sizeof *new_text));
}
if (new_text == NULL)
{
png_chunk_report(png_ptr, "too many text chunks",
- PNG_CHUNK_WRITE_ERROR);
+ PNG_CHUNK_WRITE_ERROR);
return 1;
}
@@ -766,7 +854,7 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)
{
png_chunk_report(png_ptr, "text compression mode is out of range",
- PNG_CHUNK_WRITE_ERROR);
+ PNG_CHUNK_WRITE_ERROR);
continue;
}
@@ -795,14 +883,20 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
else
lang_key_len = 0;
}
-# else /* PNG_iTXt_SUPPORTED */
+# else /* iTXt */
{
png_chunk_report(png_ptr, "iTXt chunk not supported",
- PNG_CHUNK_WRITE_ERROR);
+ PNG_CHUNK_WRITE_ERROR);
continue;
}
# endif
+ /* Record the location for posterity. On the read side this just says
+ * where the chunk was (approximately). On the write side it says how far
+ * through the write process libpng was before this API was called.
+ */
+ textp->location = get_location(png_ptr);
+
if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
{
text_length = 0;
@@ -827,7 +921,8 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
if (textp->key == NULL)
{
png_chunk_report(png_ptr, "text chunk: out of memory",
- PNG_CHUNK_WRITE_ERROR);
+ PNG_CHUNK_WRITE_ERROR);
+
return 1;
}
@@ -891,23 +986,38 @@ png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_debug1(1, "in %s storage function", "tIME");
- if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL ||
- (png_ptr->mode & PNG_WROTE_tIME))
+ if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL)
return;
+ /* It is valid to do set the tIME chunk after the IDAT has been written, but
+ * not if one has already been written. This was ignored before - the
+ * previous time was used - this is a bad thing.
+ */
+ if ((info_ptr->valid & PNG_INFO_tIME) != 0 /* Changing a time chunk */ &&
+ (png_ptr->mode & PNG_HAVE_IHDR) != 0 /* after writing started */)
+ {
+ /* So it can be *set* but it can't be *changed* after the info before PLTE
+ * has been written. (Note that putting tIME into an unknown chunk
+ * currently gets round this; to be fixed.)
+ */
+ png_app_error(png_ptr, "cannot change tIME after writing starts");
+ return;
+ }
+
if (mod_time->month == 0 || mod_time->month > 12 ||
mod_time->day == 0 || mod_time->day > 31 ||
mod_time->hour > 23 || mod_time->minute > 59 ||
mod_time->second > 60)
{
- png_warning(png_ptr, "Ignoring invalid time value");
+ png_app_error(png_ptr, "Ignoring invalid time value");
return;
}
info_ptr->mod_time = *mod_time;
+ info_ptr->time_location = get_location(png_ptr);
info_ptr->valid |= PNG_INFO_tIME;
}
-#endif
+#endif /* tIME */
#ifdef PNG_tRNS_SUPPORTED
void PNGAPI
@@ -919,11 +1029,11 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
if (png_ptr == NULL || info_ptr == NULL)
return;
- if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ if ((info_ptr->format & PNG_FORMAT_FLAG_ALPHA) != 0)
png_chunk_report(png_ptr,
- "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR);
+ "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR);
- else if (info_ptr->color_type & PNG_COLOR_MASK_PALETTE)
+ else if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
{
int max_num;
@@ -931,23 +1041,23 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
/* Do this just in case the old data was not owned by libpng: */
- info_ptr->valid &= ~PNG_INFO_tRNS;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
info_ptr->trans_alpha = NULL;
info_ptr->num_trans = 0;
/* Expect png_set_PLTE to happen before png_set_tRNS, so num_palette will
* be set, but this is not a requirement of the API.
*/
- if (png_ptr->num_palette)
- max_num = png_ptr->num_palette;
+ if (info_ptr->num_palette)
+ max_num = info_ptr->num_palette;
else
- max_num = 1 << png_ptr->bit_depth;
+ max_num = 1 << info_ptr->bit_depth;
if (num_trans > max_num)
{
png_chunk_report(png_ptr, "png_set_tRNS: num_trans too large",
- PNG_CHUNK_ERROR);
+ PNG_CHUNK_ERROR);
/* If control returns simply limit it; the behavior prior to 1.7 was to
* issue a warning and skip the palette in png_write_tRNS.
*/
@@ -964,34 +1074,33 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
* indices.
*/
info_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr,
- PNG_MAX_PALETTE_LENGTH));
+ PNG_MAX_PALETTE_LENGTH));
info_ptr->free_me |= PNG_FREE_TRNS;
memcpy(info_ptr->trans_alpha, trans_alpha,
- (unsigned)/*SAFE*/num_trans);
+ (unsigned)/*SAFE*/num_trans);
info_ptr->valid |= PNG_INFO_tRNS;
- info_ptr->num_trans = (png_uint_16)num_trans; /* SAFE */
+ info_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9);
}
}
else /* not a PALETTE image */
{
- /* Invalidate any prior transparent color, set num_trans too, it is not
+ /* Invalidate any prior transparent color and set num_trans. It is not
* used internally in this case but png_get_tRNS still returns it.
*/
- info_ptr->valid &= ~PNG_INFO_tRNS;
+ info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
info_ptr->num_trans = 0; /* for png_get_tRNS */
- if (trans_color != NULL)
+ if (trans_color != NULL && info_ptr->bit_depth < 16)
{
- int sample_max = (1 << info_ptr->bit_depth);
+ unsigned int sample_max = (1U << info_ptr->bit_depth) - 1U;
- if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
- trans_color->gray <= sample_max) ||
- (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+ if (!(info_ptr->format & PNG_FORMAT_FLAG_COLOR) ?
+ trans_color->gray <= sample_max :
trans_color->red <= sample_max &&
trans_color->green <= sample_max &&
- trans_color->blue <= sample_max))
+ trans_color->blue <= sample_max)
{
info_ptr->trans_color = *trans_color;
info_ptr->valid |= PNG_INFO_tRNS;
@@ -1000,8 +1109,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
else
png_chunk_report(png_ptr,
- "tRNS chunk has out-of-range samples for bit_depth",
- PNG_CHUNK_ERROR);
+ "tRNS chunk has out-of-range samples for bit_depth",
+ PNG_CHUNK_ERROR);
}
}
}
@@ -1029,8 +1138,8 @@ png_set_sPLT(png_structrp png_ptr,
* overflows. Notice that the parameters are (int) and (size_t)
*/
np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr,
- info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
- sizeof *np));
+ info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
+ sizeof *np));
if (np == NULL)
{
@@ -1061,8 +1170,8 @@ png_set_sPLT(png_structrp png_ptr,
np->depth = entries->depth;
- /* In the even of out-of-memory just return - there's no point keeping on
- * trying to add sPLT chunks.
+ /* In the event of out-of-memory just return - there's no point keeping
+ * on trying to add sPLT chunks.
*/
length = strlen(entries->name) + 1;
np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length));
@@ -1073,8 +1182,8 @@ png_set_sPLT(png_structrp png_ptr,
memcpy(np->name, entries->name, length);
/* IMPORTANT: we have memory now that won't get freed if something else
- * goes wrong, this code must free it. png_malloc_array produces no
- * warnings, use a png_chunk_report (below) if there is an error.
+ * goes wrong; this code must free it. png_malloc_array produces no
+ * warnings; use a png_chunk_report (below) if there is an error.
*/
np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr,
entries->nentries, sizeof (png_sPLT_entry)));
@@ -1082,6 +1191,7 @@ png_set_sPLT(png_structrp png_ptr,
if (np->entries == NULL)
{
png_free(png_ptr, np->name);
+ np->name = NULL;
break;
}
@@ -1090,7 +1200,7 @@ png_set_sPLT(png_structrp png_ptr,
* checked it when doing the allocation.
*/
memcpy(np->entries, entries->entries,
- entries->nentries * sizeof (png_sPLT_entry));
+ entries->nentries * sizeof (png_sPLT_entry));
/* Note that 'continue' skips the advance of the out pointer and out
* count, so an invalid entry is not added.
@@ -1098,13 +1208,14 @@ png_set_sPLT(png_structrp png_ptr,
info_ptr->valid |= PNG_INFO_sPLT;
++(info_ptr->splt_palettes_num);
++np;
+ ++entries;
}
- while (++entries, --nentries);
+ while (--nentries);
if (nentries > 0)
png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
}
-#endif /* PNG_sPLT_SUPPORTED */
+#endif /* sPLT */
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
static png_byte
@@ -1113,17 +1224,17 @@ check_location(png_const_structrp png_ptr, int location)
location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT);
/* New in 1.6.0; copy the location and check it. This is an API
- * change, previously the app had to use the
+ * change; previously the app had to use the
* png_set_unknown_chunk_location API below for each chunk.
*/
- if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT))
+ if (location == 0 && !png_ptr->read_struct)
{
/* Write struct, so unknown chunks come from the app */
png_app_warning(png_ptr,
- "png_set_unknown_chunks now expects a valid location");
+ "png_set_unknown_chunks now expects a valid location");
/* Use the old behavior */
- location = (png_byte)(png_ptr->mode &
- (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
+ location = png_check_byte(png_ptr, png_ptr->mode &
+ (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
}
/* This need not be an internal error - if the app calls
@@ -1141,17 +1252,17 @@ check_location(png_const_structrp png_ptr, int location)
/* The cast is safe because 'location' is a bit mask and only the low four
* bits are significant.
*/
- return (png_byte)location;
+ return png_check_byte(png_ptr, location);
}
void PNGAPI
png_set_unknown_chunks(png_structrp png_ptr,
- png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
+ png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
{
png_unknown_chunkp np;
if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
- unknowns == NULL)
+ unknowns == NULL)
return;
/* Check for the failure cases where support has been disabled at compile
@@ -1160,19 +1271,19 @@ png_set_unknown_chunks(png_structrp png_ptr,
* code) but may be meaningless if the read or write handling of unknown
* chunks is not compiled in.
*/
-# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \
- defined(PNG_READ_SUPPORTED)
- if (png_ptr->mode & PNG_IS_READ_STRUCT)
+# ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ if (png_ptr->read_struct)
{
png_app_error(png_ptr, "no unknown chunk support on read");
+
return;
}
# endif
-# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \
- defined(PNG_WRITE_SUPPORTED)
- if (!(png_ptr->mode & PNG_IS_READ_STRUCT))
+# ifndef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "no unknown chunk support on write");
+
return;
}
# endif
@@ -1188,9 +1299,7 @@ png_set_unknown_chunks(png_structrp png_ptr,
if (np == NULL)
{
- png_chunk_report(png_ptr, "too many unknown chunks",
- PNG_CHUNK_WRITE_ERROR);
-
+ png_chunk_report(png_ptr, "too many unknown chunks", PNG_CHUNK_ERROR);
return;
}
@@ -1218,12 +1327,12 @@ png_set_unknown_chunks(png_structrp png_ptr,
else
{
np->data = png_voidcast(png_bytep,
- png_malloc_base(png_ptr, unknowns->size));
+ png_malloc_base(png_ptr, unknowns->size));
if (np->data == NULL)
{
png_chunk_report(png_ptr, "unknown chunk: out of memory",
- PNG_CHUNK_WRITE_ERROR);
+ PNG_CHUNK_ERROR);
/* But just skip storing the unknown chunk */
continue;
}
@@ -1255,7 +1364,7 @@ png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr,
{
png_app_error(png_ptr, "invalid unknown chunk location");
/* Fake out the pre 1.6.0 behavior: */
- if ((location & PNG_HAVE_IDAT)) /* undocumented! */
+ if ((location & PNG_HAVE_IDAT) != 0) /* undocumented! */
location = PNG_AFTER_IDAT;
else
@@ -1270,44 +1379,34 @@ png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr,
else if (png_ptr != NULL)
png_app_warning(png_ptr, "unknown chunk index out of range");
}
-#endif
-
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-png_uint_32 PNGAPI
-png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features)
-{
- png_debug(1, "in png_permit_mng_features");
-
- if (png_ptr == NULL)
- return 0;
-
- png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES;
-
- return png_ptr->mng_features_permitted;
-}
-#endif
+#endif /* STORE_UNKNOWN_CHUNKS */
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
static unsigned int
-add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep)
+add_one_chunk(png_structrp png_ptr, png_bytep list, unsigned int count,
+ png_const_bytep add, int keep)
{
unsigned int i;
/* Utility function: update the 'keep' state of a chunk if it is already in
* the list, otherwise add it to the list.
*/
- for (i=0; i= PNG_HANDLE_CHUNK_LAST)
{
png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep");
return;
}
+# ifndef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ /* This is only a warning; if the application handles the chunk with a
+ * read callback it may work fine.
+ */
+ if (png_ptr->read_struct && keep >= PNG_HANDLE_CHUNK_IF_SAFE)
+ png_app_warning(png_ptr,
+ "png_set_keep_unknown_chunks: unsupported keep");
+# endif
+
if (num_chunks_in <= 0)
{
- png_ptr->unknown_default = keep;
+ png_ptr->unknown_default = (unsigned)keep & 0x3;
/* '0' means just set the flags, so stop here */
if (num_chunks_in == 0)
@@ -1364,7 +1475,7 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep,
};
chunk_list = chunks_to_ignore;
- num_chunks = (sizeof chunks_to_ignore)/5;
+ num_chunks = (unsigned int)/*SAFE*/(sizeof chunks_to_ignore)/5U;
}
else /* num_chunks_in > 0 */
@@ -1424,12 +1535,15 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep,
unsigned int i;
for (i=0; irow_pointers && (info_ptr->row_pointers != row_pointers))
+ if (info_ptr->row_pointers != NULL &&
+ (info_ptr->row_pointers != row_pointers))
png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
info_ptr->row_pointers = row_pointers;
@@ -1499,63 +1615,25 @@ png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,
#endif
void PNGAPI
-png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size)
+png_set_compression_buffer_size(png_structrp png_ptr, png_alloc_size_t size)
{
- if (png_ptr == NULL)
- return;
+ if (png_ptr == NULL)
+ return;
- if (size == 0 || size > PNG_UINT_31_MAX)
- png_error(png_ptr, "invalid compression buffer size");
+ if (size == 0 ||
+ size > (png_ptr->read_struct ? ZLIB_IO_MAX : PNG_UINT_31_MAX))
+ png_error(png_ptr, "invalid compression buffer size");
-# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
- if (png_ptr->mode & PNG_IS_READ_STRUCT)
- {
- png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
- return;
- }
-# endif
-
-# ifdef PNG_WRITE_SUPPORTED
- if (!(png_ptr->mode & PNG_IS_READ_STRUCT))
- {
- if (png_ptr->zowner != 0)
- {
- png_warning(png_ptr,
- "Compression buffer size cannot be changed because it is in use");
- return;
- }
-
- if (size > ZLIB_IO_MAX)
- {
- png_warning(png_ptr,
- "Compression buffer size limited to system maximum");
- size = ZLIB_IO_MAX; /* must fit */
- }
-
- else if (size < 6)
- {
- /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
- * if this is permitted.
- */
- png_warning(png_ptr,
- "Compression buffer size cannot be reduced below 6");
- return;
- }
-
- if (png_ptr->zbuffer_size != size)
- {
- png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
- png_ptr->zbuffer_size = (uInt)size;
- }
- }
-# endif
+# if (defined PNG_SEQUENTIAL_READ_SUPPORTED) || defined PNG_WRITE_SUPPORTED
+ png_ptr->IDAT_size = (png_uint_32)/*SAFE*/size;
+# endif /* SEQUENTIAL_READ || WRITE */
}
void PNGAPI
png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask)
{
- if (png_ptr && info_ptr)
- info_ptr->valid &= ~mask;
+ if (png_ptr != NULL && info_ptr != NULL)
+ info_ptr->valid &= PNG_BIC_MASK(mask);
}
@@ -1567,13 +1645,13 @@ png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max,
{
/* Images with dimensions larger than these limits will be
* rejected by png_set_IHDR(). To accept any PNG datastream
- * regardless of dimensions, set both limits to 0x7ffffffL.
+ * regardless of dimensions, set both limits to 0x7fffffffL.
*/
- if (png_ptr != NULL)
- {
- png_ptr->user_width_max = user_width_max;
- png_ptr->user_height_max = user_height_max;
- }
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->user_width_max = user_width_max;
+ png_ptr->user_height_max = user_height_max;
}
/* This function was added to libpng 1.4.0 */
@@ -1592,50 +1670,5 @@ png_set_chunk_malloc_max (png_structrp png_ptr,
if (png_ptr != NULL)
png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
}
-#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
-
-
-#ifdef PNG_BENIGN_ERRORS_SUPPORTED
-void PNGAPI
-png_set_benign_errors(png_structrp png_ptr, int allowed)
-{
- png_debug(1, "in png_set_benign_errors");
-
- /* If allowed is 1, png_benign_error() is treated as a warning.
- *
- * If allowed is 0, png_benign_error() is treated as an error (which
- * is the default behavior if png_set_benign_errors() is not called).
- */
-
- if (allowed != 0)
- png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN |
- PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN;
-
- else
- png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN |
- PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN);
-}
-#endif /* PNG_BENIGN_ERRORS_SUPPORTED */
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
- /* Whether to report invalid palette index; added at libng-1.5.10.
- * It is possible for an indexed (color-type==3) PNG file to contain
- * pixels with invalid (out-of-range) indexes if the PLTE chunk has
- * fewer entries than the image's bit-depth would allow. We recover
- * from this gracefully by filling any incomplete palette with zeroes
- * (opaque black). By default, when this occurs libpng will issue
- * a benign error. This API can be used to override that behavior.
- */
-void PNGAPI
-png_set_check_for_invalid_index(png_structrp png_ptr, int allowed)
-{
- png_debug(1, "in png_set_check_for_invalid_index");
-
- if (allowed > 0)
- png_ptr->num_palette_max = 0;
-
- else
- png_ptr->num_palette_max = -1;
-}
-#endif
-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
+#endif /* ?SET_USER_LIMITS */
+#endif /* READ || WRITE */
diff --git a/ext/libpng17/pngstruct.h b/ext/libpng17/pngstruct.h
index e4341eede6..c6ca8475e0 100644
--- a/ext/libpng17/pngstruct.h
+++ b/ext/libpng17/pngstruct.h
@@ -1,12 +1,11 @@
/* pngstruct.h - header file for PNG reference library
*
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
- * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- *
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
@@ -27,14 +26,14 @@
#ifndef ZLIB_CONST
/* We must ensure that zlib uses 'const' in declarations. */
# define ZLIB_CONST
-#endif
+#endif /* !ZLIB_CONST */
#include PNG_ZLIB_HEADER
#ifdef const
/* zlib.h sometimes #defines const to nothing, undo this. */
# undef const
-#endif
+#endif /* const */
/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
* with older builds.
@@ -42,10 +41,10 @@
#if ZLIB_VERNUM < 0x1260
# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
-#else
+#else /* ZLIB_VERNUM >= 0x1260 */
# define PNGZ_MSG_CAST(s) (s)
# define PNGZ_INPUT_CAST(b) (b)
-#endif
+#endif /* ZLIB_VERNUM >= 0x1260 */
/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
* can handle at once. This type need be no larger than 16 bits (so maximum of
@@ -56,20 +55,19 @@
* even improve performance on some systems (and degrade it on others.)
*/
#ifndef ZLIB_IO_MAX
-# define ZLIB_IO_MAX ((uInt)-1)
-#endif
+# ifdef __COVERITY__
+# define ZLIB_IO_MAX ((uInt)255U) /* else COVERITY whines */
+# else /* !COVERITY */
+# define ZLIB_IO_MAX ((uInt)-1)
+# endif /* !COVERITY */
+#endif /* !ZLIB_IO_MAX */
#ifdef PNG_WRITE_SUPPORTED
-/* The type of a compression buffer list used by the write code. */
-typedef struct png_compression_buffer
-{
- struct png_compression_buffer *next;
- png_byte output[1]; /* actually zbuf_size */
-} png_compression_buffer, *png_compression_bufferp;
-
-#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
- (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
-#endif
+/* The write compression control (allocated on demand).
+ * TODO: use this for the read state too.
+ */
+typedef struct png_zlib_state *png_zlib_statep;
+#endif /* WRITE */
/* Colorspace support; structures used in png_struct, png_info and in internal
* functions to hold and communicate information about the color space.
@@ -103,7 +101,7 @@ typedef struct png_XYZ
#endif /* COLORSPACE */
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
-/* A colorspace is all the above plus, potentially, profile information,
+/* A colorspace is all the above plus, potentially, profile information;
* however at present libpng does not use the profile internally so it is only
* stored in the png_info struct (if iCCP is supported.) The rendering intent
* is retained here and is checked.
@@ -116,13 +114,13 @@ typedef struct png_colorspace
{
#ifdef PNG_GAMMA_SUPPORTED
png_fixed_point gamma; /* File gamma */
-#endif
+#endif /* GAMMA */
#ifdef PNG_COLORSPACE_SUPPORTED
png_xy end_points_xy; /* End points as chromaticities */
png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */
png_uint_16 rendering_intent; /* Rendering intent of a profile */
-#endif
+#endif /* COLORSPACE */
/* Flags are always defined to simplify the code. */
png_uint_16 flags; /* As defined below */
@@ -131,33 +129,251 @@ typedef struct png_colorspace
typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
/* General flags for the 'flags' field */
-#define PNG_COLORSPACE_HAVE_GAMMA 0x0001
-#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002
-#define PNG_COLORSPACE_HAVE_INTENT 0x0004
-#define PNG_COLORSPACE_FROM_gAMA 0x0008
-#define PNG_COLORSPACE_FROM_cHRM 0x0010
-#define PNG_COLORSPACE_FROM_sRGB 0x0020
-#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
-#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */
-#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100 /* user specified coeffs */
-#define PNG_COLORSPACE_INVALID 0x8000
-#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags))
+#define PNG_COLORSPACE_HAVE_GAMMA 0x0001U
+#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002U
+#define PNG_COLORSPACE_HAVE_INTENT 0x0004U
+#define PNG_COLORSPACE_FROM_gAMA 0x0008U
+#define PNG_COLORSPACE_FROM_cHRM 0x0010U
+#define PNG_COLORSPACE_FROM_sRGB 0x0020U
+#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040U
+#define PNG_COLORSPACE_MATCHES_sRGB 0x0080U /* exact match on profile */
+#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100U /* user specified coeffs */
+#define PNG_COLORSPACE_INVALID 0x8000U
+#define PNG_COLORSPACE_CANCEL(flags) (0xffffU - (flags))
#endif /* COLORSPACE || GAMMA */
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+/***************************** READ and WRITE TRANSFORMS ***********************
+ * These structures are used in pngrtran.c, pngwtran.c and pngtrans.c to hold
+ * information about transforms in progress. This mechanism was introduced in
+ * libpng 1.7.0 to ensure reliable transform code and to fix multiple bugs in
+ * the pre-1.7 transform handling.
+ *
+ * Prior to 1.7.0 the internal transform routines took a png_row_infop, like the
+ * user transform function, but without the png_ptr because it was never used.
+ * In 1.7.0 a separate internal structure is used in place of this to allow both
+ * future development to change the structure.
+ *
+ * The values in this structure will normally be changed by transformation
+ * implementations.
+ ***************************** READ and WRITE TRANSFORMS **********************/
+typedef struct
+{
+ png_structp png_ptr; /* png_struct for error handling and some
+ * transform parameters. May be aliased.
+ */
+ png_const_voidp sp; /* Source; the input row. */
+ png_voidp dp; /* Output buffer for the transformed row,
+ * this may be the same as sp.
+ */
+ /* If the row is changed the tranform routine must write the result to
+ * dp[] and set sp to dp, otherwise it must not write to dp and must leave
+ * sp unchanged. dp[] and sp[] are both 'malloc' aligned; i.e. they have
+ * the system alignment, so the data can be read as any valid ANSI-C
+ * type.
+ */
+ png_uint_32 width; /* width of row */
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ png_fixed_point gamma; /* Current gamma of the row data */
+ /* When a row is being transformed this contains the current gamma of the
+ * data if known. During initialization the value is used to accumulate
+ * information for png_struct::row_gamma in the first step,
+ * PNG_TC_INIT_FORMAT, then used to insert the correct gamma transforms
+ * during PNG_TC_INIT_FINAL. The field is only used on read; write
+ * transforms do not modify the gamma of the data.
+ */
+# endif /* READ_GAMMA */
+ unsigned int format; /* As pngstruct::row_format below */
+ unsigned int range; /* Count of range transforms */
+# define PNG_TC_CHANNELS(tc) PNG_FORMAT_CHANNELS((tc).format)
+ unsigned int bit_depth; /* bit depth of row */
+ png_byte sBIT_R;
+ png_byte sBIT_G;
+ png_byte sBIT_B;
+ png_byte sBIT_A; /* Signnificant bits in the row channels. */
+ /* The above four values are initially set to the number of bits significant
+ * in the input PNG data, R/G/B are set to the same (gray) value for
+ * grayscale input. All values are set to the bit depth if there is no sBIT
+ * chunk, if there is no alpha channel sBIT_A is set to the bit depth.
+ *
+ * When any potentially spurious bits have been cleared PNG_INFO_sBIT will be
+ * set in invalid_info. From this point on the above values record the
+ * approximate number of bits of accuracy in the channels and the lower bits
+ * should be preserved; they potentially contain useful information.
+ */
+# define PNG_TC_PIXEL_DEPTH(tc) (PNG_TC_CHANNELS(tc) * (tc).bit_depth)
+# define PNG_TC_ROWBYTES(tc) PNG_ROWBYTES(PNG_TC_PIXEL_DEPTH(tc), (tc).width)
+ unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */
+ unsigned int cost; /* Cache cost */
+# define PNG_CACHE_COST_LIMIT 0x100U
+ /* This is a runtime structure, so size doesn't matter much, and it helps
+ * code reliability to use real member names here. Feel free to experiment
+ * with integer values rather than bitfields.
+ */
+ unsigned int init :2; /* 0 for processing, non zero for init: */
+# define PNG_TC_INIT_FORMAT 0x01U /* Initialization step 1: just set 'format',
+ * 'bit_depth' and 'gamma' to the output
+ * values iff the transform corresponds to
+ * a user requested change to those values.
+ */
+# define PNG_TC_INIT_FINAL 0x03U /* Initialization step 2; set the 'format'
+ * 'bit_depth' and 'gamma' to the values the
+ * transform will actually produce (which
+ * need not be the same as the above).
+ */
+ /* During initialization 'init' must be set and sp and dp may be NULL. If
+ * neither flag is set sp and dp must be non-NULL.
+ *
+ * When the transform runs it must update 'format', 'bit_depth' and 'gamma'
+ * to the values previously reported during PNG_TC_INIT_FINAL; not doing so
+ * may result in an affirm from a later transform.
+ */
+ unsigned int caching :1; /* The color values are being used to
+ * generate a cache of the transforms.
+ */
+ unsigned int palette :1; /* The values come from a PNG palette and
+ * the palette will not be expanded. The
+ * CACHE flag must be set too. A
+ * transform which causes the palette to
+ * be expanded must clear this flag.
+ */
+#if 0 /* NYI */
+ unsigned int interchannel:1; /* Set by a transform that combines two or
+ * more channels together; for example
+ * alpha composition or RGB to gray.
+ */
+#endif /* NYI */
+ unsigned int channel_add :1; /* A channel (alpha/filler) was added */
+ unsigned int strip_alpha :1; /* Set if the alpha channel will be
+ * stripped on read, this also prevents
+ * the tRNS chunk being expanded. Only
+ * some transforms check this, depending
+ * on the handling order and checks in
+ * pre-1.7 versions.
+ */
+ unsigned int expand_tRNS :1; /* Set if the tRNS chunk should be
+ * expanded (ignored if read_strip_alpha
+ * is set). If this is *not* set
+ * transforms which do not use alpha/tRNS
+ * but would invalidate it (such as
+ * simple gamma correction) will simply
+ * mark the tRNS info as invalid.
+ */
+ unsigned int transparent_alpha :1; /* Indicates that the alpha channel
+ * consists entirely of opaque (1.0 alpha)
+ * or completely transparent (0.0 alpha)
+ * pixels. Set when tRNS is expanded to
+ * alpha.
+ */
+ unsigned int optimized_alpha :1; /* Meaningful only when bit_depth is
+ * 16 and gamma is 1 or unknown (0).
+ * Indicates that pixels which are opaque
+ * (alpha 1.0) have not been expanded to
+ * 16-bit linear; instead these pixels
+ * are encoded in the final format in
+ * png_struct::row_bit_depth and
+ * png_struct::row_gamma. This will
+ * invariably match the file format.
+ */
+} png_transform_control, *png_transform_controlp;
+
+typedef const png_transform_control *png_const_transform_controlp;
+typedef const png_row_info *png_const_row_infop;
+
+typedef struct png_transform *png_transformp; /* Forward declaration */
+typedef void (*png_transform_free_fn)(/* Function to free a transform */
+ png_const_structrp png_ptr,
+ png_transformp transform); /* pointer to this transform */
+ /* This function need not exist in a transform, it must free all the data
+ * allocated within the transform but not the transform itself. It is called
+ * from png_transform_free.
+ */
+typedef void (*png_transform_fn)(/* Function to implement a transform */
+ png_transformp *transform, /* pointer to this transform */
+ png_transform_controlp control); /* row information */
+ /* The transform function has two modes of operation:
+ *
+ * 1) Initialization. The list of transforms is processed from the start to
+ * the end and each function is called with one of hte PNG_TC_INIT_ flags
+ * set in control->flags, control->dp and control->sp may be NULL.
+ *
+ * For read the control structure contains the input row format and bit
+ * depth, the transform function changes this to represent what the
+ * transform will produce when it runs.
+ *
+ * For write the control structure contains the *required* output format
+ * and bit depth. The transform function changes this to the values that
+ * it needs to produce the required values.
+ *
+ * In both cases the transform function may update the 'fn' function to a
+ * new function to perform the desired transform; this allows considerable
+ * optimization on multi-row images.
+ *
+ * In both cases the caller considers the pixel bit depth changes and
+ * records the maximum required so that it can allocate a suitably sized
+ * buffer.
+ *
+ * 2) Execution.
+ *
+ * In the read case the transforms are processed in the stored order and
+ * must transform the row data appropriately *and* update the bit depth
+ * and format as before.
+ *
+ * In the write case the transforms are called in the reverse order and
+ * the input bit depth and format should match the required values.
+ *
+ * It is valid during initialization for the transform function to push
+ * another transform into the list in either the read or the write case if
+ * the transform cannot handle (read) or produce (write) the required format. * The transform pushes another transform into the list ahead of itself (at
+ * *transform) and runs that initialization; when control is returned to the
+ * caller the caller will re-run the transform initialization.
+ *
+ * It is also valid (during initialization) to push new transforms onto the
+ * list, just so long as the order of the transform is greater than the
+ * current transform (so that the caller will still call the new transform
+ * initialization.)
+ *
+ * In the write case the user transform callback might still end up producing
+ * an unexpected format, but at present this is unavoidable; the libpng API
+ * is extremely inconsistent in how a user transform reports the changes it
+ * made.
+ *
+ * TODO: fix this, probably with an API change in 1.7.0
+ */
+typedef struct png_transform /* Linked list of transform functions */
+{
+ png_transformp next; /* Next transform in the list */
+ png_transform_fn fn; /* Function to implement the transform */
+ png_transform_free_fn free; /* Free allocated data, normally NULL */
+ unsigned int order; /* Order of the transform in the list. */
+ unsigned int size; /* Size of this structure (max 65535) */
+ png_uint_32 args; /* Optional transform arguments. */
+} png_transform;
+#endif /* TRANSFORM_MECH */
+
+/* Action to take on CRC errors (four values) */
+typedef enum
+{
+ crc_error_quit = PNG_CRC_ERROR_QUIT-1,
+ crc_warn_discard = PNG_CRC_WARN_DISCARD-1,
+ crc_warn_use = PNG_CRC_WARN_USE-1,
+ crc_quiet_use = PNG_CRC_QUIET_USE-1
+} png_crc_action;
+
struct png_struct_def
{
/* Rearranged in libpng 1.7 to attempt to lessen padding; in general
- * (char), (short), (int) and pointer types are kept separate, however
+ * (char), (short), (int) and pointer types are kept separate; however
* associated members under the control of the same #define are still
* together.
*/
#ifdef PNG_SETJMP_SUPPORTED
/* jmp_buf can have very high alignment requirements on some systems, so put
* it first (the other setjmp members are later as they are infrequently
- * accesed.)
+ * accessed.)
*/
jmp_buf jmp_buf_local;
-#endif
+#endif /* SETJMP */
/* Next the frequently accessed fields. Many processors perform arithmetic
* in the address pipeline, but frequently the amount of addition or
@@ -165,59 +381,223 @@ struct png_struct_def
* the hope is that such processors will generate code that is both smaller
* and faster.
*/
+#ifdef PNG_READ_SUPPORTED
png_colorp palette; /* palette from the input file */
- size_t rowbytes; /* size of row in bytes */
- size_t info_rowbytes; /* cache of updated row bytes */
+#endif /* READ */
+#ifdef PNG_READ_tRNS_SUPPORTED
+ png_bytep trans_alpha; /* alpha values for paletted files */
+#endif /* READ_tRNS */
+
png_uint_32 width; /* width of image in pixels */
png_uint_32 height; /* height of image in pixels */
- png_uint_32 num_rows; /* number of rows in current pass */
- png_uint_32 usr_width; /* width of row at start of write */
- png_uint_32 iwidth; /* width of current interlaced row in pixels */
- png_uint_32 row_number; /* current row in interlace pass */
png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */
+#ifdef PNG_READ_SUPPORTED
+ png_uint_32 chunk_length; /* Length (possibly remaining) in said chunk. */
+#endif /* READ */
png_uint_32 crc; /* current chunk CRC value */
- png_uint_32 mode; /* tells us where we are in the PNG file */
- png_uint_32 flags; /* flags indicating various things to libpng */
- png_uint_32 transformations;/* which transformations to perform */
- png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */
- png_uint_32 free_me; /* items libpng is responsible for freeing */
- int maximum_pixel_depth; /* pixel depth used for the row buffers */
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
- int num_palette_max; /* maximum palette index found in IDAT */
-#endif
-
- png_uint_16 num_palette; /* number of color entries in palette */
- png_uint_16 num_trans; /* number of transparency values */
+ unsigned int mode :6; /* where we are in the PNG file */
+ unsigned int read_struct :1; /* this is a read (not write) struct */
+ unsigned int num_palette :9; /* number of color entries in palette */
+#ifdef PNG_READ_tRNS_SUPPORTED
+ unsigned int num_trans :9; /* number of transparency values */
+ unsigned int transparent_palette :1; /* if they are all 0 or 255 */
+#endif /* READ_tRNS */
+#ifdef PNG_PALETTE_MAX_SUPPORTED
+ unsigned int palette_index_max :8; /* maximum palette index in IDAT */
+ unsigned int palette_index_check :2; /* one of the following: */
+# define PNG_PALETTE_CHECK_DEFAULT 0U
+# define PNG_PALETTE_CHECK_OFF 1U
+# define PNG_PALETTE_CHECK_ON 2U
+ unsigned int palette_index_have_max :1; /* max is being set */
+ unsigned int palette_index_check_issued :1; /* error message output */
+#endif /* PALETTE_MAX */
+#ifdef PNG_READ_tRNS_SUPPORTED
+ png_color_16 trans_color; /* transparent color for non-paletted files */
+#endif /* READ_tRNS */
+#ifdef PNG_READ_sBIT_SUPPORTED
+ png_color_8 sig_bit; /* significant bits in each channel */
+#endif /* READ_sBIT */
/* Single byte values, typically used either to save space or to hold 1-byte
* values from the PNG chunk specifications.
*/
- png_byte compression_type; /* file compression type (always 0) */
- png_byte filter; /* file filter type (always 0) */
+ png_byte filter_method; /* file filter type (only non-0 with MNG) */
png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
- png_byte pass; /* current interlace pass (0 - 6) */
- png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */
png_byte color_type; /* color type of file */
png_byte bit_depth; /* bit depth of file */
- png_byte pixel_depth; /* number of bits per pixel */
- png_byte channels; /* number of channels in file */
- png_byte sig_bytes; /* magic bytes read/written from start of file */
- png_byte transformed_pixel_depth;
- /* pixel depth after read/write transforms */
+ png_byte sig_bytes; /* magic bytes read/written at start of file */
+
+#ifdef PNG_READ_SUPPORTED
+#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
+ /* The png_struct colorspace structure is only required on read - on write it
+ * is in (just) the info_struct.
+ */
+ png_colorspace colorspace;
+#endif /* COLORSPACE || GAMMA */
+#endif /* READ */
+
+ /* Transform handling */
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ png_transformp transform_list; /* List of transformation to perform. */
+#endif /* TRANSFORM_MECH */
+
+ /* ROW BUFFERS and CONTROL
+ *
+ * Members used for image row compression (write) or decompression (read).
+ * filter byte (which is in next_filter.) All fields are only used during
+ * IDAT processing and start of 0.
+ */
+#ifdef PNG_READ_SUPPORTED
+ png_bytep row_buffer; /* primary row buffer */
+#if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\
+ defined(PNG_READ_INTERLACING_SUPPORTED)) &&\
+ defined(PNG_TRANSFORM_MECH_SUPPORTED)
+ png_bytep transformed_row; /* pointer to the transformed row, if
+ * required. May point to row_buffer.
+ */
+#endif /* (PROGRESSIVE_READ || READ_INTERLACING) && TRANSFORM_MECH */
+
+ png_alloc_size_t row_bytes_read; /* Total read in row */
+#endif /* READ */
+
+ png_uint_32 row_number; /* current row in pass */
+#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ png_fixed_point row_gamma; /* Gamma of final output */
+#if 0 /* NYI */
+ unsigned int gamma_accuracy;
+ /* LINEAR gamma cache table size (in bits) times 100; for non-linear
+ * tables the value used is gamma_accuracy/gamma where 'gamma' is the
+ * encoding value of the data (typically less than 1).
+ *
+ * default: PNG_DEFAULT_GAMMA_ACCURACY (665)
+ */
+#endif /* NYI */
+ png_uint_16 gamma_threshold;
+ /* Gamma threshold value as a fixed-point value in the range 0..1; the
+ * threshold at or below which gamma correction is skipped. '0' forces
+ * gamma correction even when there is none because the input and output
+ * gammas are equal.
+ *
+ * default: PNG_GAMMA_THRESHOLD_FIXED (153)
+ */
+#endif /* READ_GAMMA */
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */
+ unsigned int palette_updated:1; /* png_struct::palette changed */
+#endif /* READ_TRANSFORMS */
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+ unsigned int read_started :1; /* at least one call to png_read_row */
+#endif /* SEQUENTIAL_READ */
+ /* The next field is just used by the read IDAT process functions to store
+ * the state of IDAT processing; they should not be altered or used by other
+ * functions.
+ */
+ unsigned int row_state :2; /* state of row parsing (internal) */
+#endif /* READ */
+
+#if defined (PNG_READ_INTERLACING_SUPPORTED) ||\
+ defined (PNG_WRITE_INTERLACING_SUPPORTED)
+ unsigned int do_interlace :1; /* libpng handles the interlace */
+# endif /* R/W INTERLACING */
+
+ unsigned int pass :3; /* current (interlace) pass (0 - 6) */
+
+ /* The following fields are set by png_row_init to the pixel depths of the
+ * pixels at various states. If transforms are not supported they will
+ * always be the same value:
+ *
+ * READ WRITE
+ * input: PNG From application
+ * output: To application PNG
+ * max: Largest in transform
+ */
+ unsigned int row_input_pixel_depth :8;
+ unsigned int row_output_pixel_depth :8;
+ unsigned int row_max_pixel_depth :8;
+
+# define PNG_RF_BITS 9 /* Number of bits required for the row format (below) */
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ /* The following fields describe the format of the user row; the output on
+ * read or the input on write, and give the maximum pixel depth, which
+ * controls the row buffer allocation size (row_allocated_bytes) above.
+ */
+ unsigned int row_range :3; /* range error count */
+ unsigned int row_bit_depth :6; /* bits per channel (up to 32) */
+ unsigned int row_format:PNG_RF_BITS;/* format of output(R)/input(W) row: */
+ /* PNG_FORMAT_FLAG_ALPHA 0x01U format with an alpha channel
+ * PNG_FORMAT_FLAG_COLOR 0x02U color format: otherwise grayscale
+ * PNG_FORMAT_FLAG_LINEAR 0x04U NOT used (informational)
+ * PNG_FORMAT_FLAG_COLORMAP 0x08U image data is color-mapped
+ * PNG_FORMAT_FLAG_BGR 0x10U BGR colors, else order is RGB
+ * PNG_FORMAT_FLAG_AFIRST 0x20U alpha channel comes first *
+ * PNG_FORAMT_FLAG_AFILLER 0x40U The 'alpha' channel is a filler:
+ * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel
+ * is not an alpha value and (therefore) cannot be used for alpha
+ * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT
+ * will return a color type *without* PNG_COLOR_MASK_ALPHA, however
+ * PNG_FORMAT_CHANNELS will return the correct number, including the
+ * filler channel.
+ * PNG_FORMAT_FLAG_SWAPPED 0x80U bytes or bits swapped:
+ * When the bit depth is 16 this means that the bytes within the
+ * components have been swapped, when the bit depth is less than 8
+ * it means the pixels within the bytes have been swapped. It should
+ * not be set for 8-bit compononents (it is meaningless).
+ * PNG_FORMAT_FLAG_RANGE 0x100U component range not 0..bit-depth:
+ * Low-bit-depth grayscale components have been unpacked into bytes
+ * without scaling, or RGB[A] pixels have been shifted back to the
+ * significant-bit range from the sBIT chunk or channels (currently
+ * alpha or gray) have been inverted.
+ * PNG_FORMAT_FLAG_INVALID NOT STORED HERE
+ */
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ unsigned int info_format:PNG_RF_BITS;
+ /* This field is used to validate the png_info used to write the
+ * IHDR. This is a new check in 1.7.0; previously it was possible to pass
+ * a png_info from a png_read with the read tranform information in the
+ * format having manually removed the required transforms from the rows
+ * passed to png_write_row.
+ */
+#endif /* WRITE_TRANSFORMS */
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ unsigned int write_invert_alpha :1;
+ /* This indicates the png_set_invert_alpha was called, it is used by the
+ * write code to implement the transform without needing to run the whole
+ * transform mechanism on the PNG palette data.
+ */
+#endif /* WRITE_INVERT_ALPHA */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ unsigned int rgb_to_gray_status :1;
+ /* If set an RGB pixel was encountered by the RGB to gray transform
+ * wherein !(r==g==b).
+ */
+#endif /* RGB_TO_GRAY */
+#endif /* TRANSFORM_MECH */
+
+#ifdef PNG_READ_SUPPORTED
+ /* These, and IDAT_size below, control how much input and output (at most) is
+ * available to zlib during read decompression.
+ */
+ png_alloc_size_t read_buffer_size; /* current size of the buffer */
+#endif /* READ */
+
+#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+ png_uint_32 IDAT_size; /* limit on IDAT read and write IDAT size */
+#endif /* SEQUENTIAL_READ || WRITE */
/* ERROR HANDLING */
#ifdef PNG_SETJMP_SUPPORTED
jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */
png_longjmp_ptr longjmp_fn; /* setjmp non-local goto function. */
size_t jmp_buf_size; /* size of *jmp_buf_ptr, if allocated */
-#endif
+#endif /* SETJMP */
/* Error/warning callbacks */
png_error_ptr error_fn; /* print an error message and abort */
#ifdef PNG_WARNINGS_SUPPORTED
png_error_ptr warning_fn; /* print a warning and continue */
-#endif
+#endif /* WARNINGS */
png_voidp error_ptr; /* user supplied data for the above */
/* MEMORY ALLOCATION */
@@ -225,29 +605,31 @@ struct png_struct_def
png_malloc_ptr malloc_fn; /* allocate memory */
png_free_ptr free_fn; /* free memory */
png_voidp mem_ptr; /* user supplied data for the above */
-#endif
+#endif /* USER_MEM */
/* IO and BASIC READ/WRITE SUPPORT */
png_voidp io_ptr; /* user supplied data for IO callbacks */
+ png_rw_ptr rw_data_fn; /* read/write some bytes (must succeed) */
#ifdef PNG_READ_SUPPORTED
- png_rw_ptr read_data_fn; /* read some bytes (must succeed) */
png_read_status_ptr read_row_fn; /* called after each row is decoded */
png_bytep read_buffer; /* buffer for reading chunk data */
/* During read the following array is set up to point to the appropriate
* un-filter function, this allows per-image and per-processor optimization.
*/
- void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
- png_bytep row, png_const_bytep prev_row);
+ void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_alloc_size_t row_bytes,
+ unsigned int bpp, png_bytep row, png_const_bytep prev_row,
+ png_const_bytep prev_pixels);
+#endif /* READ */
-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
- /* The png_struct colorspace structure is only required on read - on write it
- * is in (just) the info_struct.
- */
- png_colorspace colorspace;
-#endif
-#endif /* PNG_READ_SUPPORTED */
+#ifdef PNG_WRITE_SUPPORTED
+ png_write_status_ptr write_row_fn; /* called after each row is encoded */
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_flush_ptr output_flush_fn; /* Function for flushing output */
+#endif /* WRITE_FLUSH */
+#endif /* WRITE */
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
png_uint_32 user_width_max; /* Maximum width on read */
@@ -257,14 +639,14 @@ struct png_struct_def
* - it is decremented as memory is allocated.
*/
png_alloc_size_t user_chunk_malloc_max;
-#endif
+#endif /* SET_USER_LIMITS */
#ifdef PNG_USER_LIMITS_SUPPORTED
/* limit on total *number* of sPLT, text and unknown chunks that can be
* stored. 0 means unlimited. This field is a counter - it is decremented
* as chunks are encountered.
*/
png_uint_32 user_chunk_cache_max;
-#endif
+#endif /* USER_LIMITS */
/* The progressive reader gets passed data and calls application handling
* functions when appropriate.
@@ -278,81 +660,24 @@ struct png_struct_def
png_bytep save_buffer_ptr; /* current location in save_buffer */
png_bytep save_buffer; /* buffer for previously read data */
png_bytep current_buffer_ptr; /* current location in current_buffer */
- png_bytep current_buffer; /* buffer for recently used data */
size_t save_buffer_size; /* amount of data now in save_buffer */
size_t save_buffer_max; /* total size of save_buffer */
- size_t buffer_size; /* total amount of available input data */
size_t current_buffer_size; /* amount of data now in current_buffer */
+ size_t buffer_size; /* total amount of available input data */
- png_uint_32 push_length; /* size of current input chunk */
- png_uint_32 skip_length; /* bytes to skip in input data */
-
- int process_mode; /* what push library is currently doing */
- int cur_palette; /* current push library palette index */
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
- png_rw_ptr write_data_fn;/* write some bytes (must succeed) */
- png_write_status_ptr write_row_fn; /* called after each row is encoded */
-#endif
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
- png_flush_ptr output_flush_fn; /* Function for flushing output */
- png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
- png_uint_32 flush_rows; /* number of rows written since last flush */
-#endif
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- png_bytep prev_filters; /* filter type(s) of previous row(s) */
- png_uint_16p filter_weights; /* weight(s) for previous line(s) */
- png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */
- png_uint_16p filter_costs; /* relative filter calculation cost */
- png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */
- png_byte heuristic_method; /* heuristic for row filter selection */
- png_byte num_prev_filters; /* number of weights for previous rows */
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
- png_byte usr_bit_depth; /* bit depth of users row */
- png_byte usr_channels; /* channels at start of write */
-#endif
+ unsigned int process_mode :8;
+ /* This is one or two four bit codes describing the current state of the
+ * 'push' reader. Normally the low four bits are a state code, however in
+ * some cases this may be pushed to the top four bits and replaced by a
+ * different temporary state code. The value is, in effect, a two entry
+ * stack.
+ */
+#endif /* PROGRESSIVE_READ */
#ifdef PNG_IO_STATE_SUPPORTED
png_uint_32 io_state; /* tells the app read/write progress */
-#endif
-
- /* ROW BUFFERS
- *
- * Members that hold pointers to the decompressed image rows.
- */
- png_bytep row_buf; /* buffer for the current (unfiltered) row */
-#if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED)
- png_bytep prev_row; /* buffer to save the previous (unfiltered) row */
-#endif
-
-#ifdef PNG_READ_SUPPORTED
- /* The row_buf and prev_row pointers are misaligned so that the start of the
- * row - after the filter byte - is aligned, the 'big_' pointers record the
- * original allocated pointer.
- */
- png_bytep big_row_buf;
- png_bytep big_prev_row;
- size_t big_row_buf_size; /* Actual size of both */
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
- /* This is somewhat excessive, there is no obvious reason on write to
- * allocate a buffer for each possible filtered row, only for the one being
- * tested and the current best.
- *
- * TODO: fix this
- */
- png_bytep sub_row; /* buffer to save "sub" row when filtering */
- png_bytep up_row; /* buffer to save "up" row when filtering */
- png_bytep avg_row; /* buffer to save "avg" row when filtering */
- png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */
-#endif
+#endif /* IO_STATE */
/* UNKNOWN CHUNK HANDLING */
/* TODO: this is excessively complicated, there are multiple ways of doing
@@ -366,195 +691,87 @@ struct png_struct_def
* set_unknown_chunks interface.)
*/
png_voidp user_chunk_ptr;
-#endif
+#endif /* USER_CHUNKS */
#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
/* This is called back from the unknown chunk handling */
png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
-#endif
-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
- /* Temporary storage for unknown chunk that the library doesn't recognize,
- * used while reading the chunk.
- */
- png_unknown_chunk unknown_chunk;
-#endif
+#endif /* READ_USER_CHUNKS */
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ png_uint_32 known_unknown; /* Bit mask of known chunks to be treated as
+ * unknown in the read code.
+ */
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ png_uint_32 save_unknown; /* Whether to save or skip these chunks:
+ * 'save' is 'known & save', 'skip' is
+ * 'known & ~save'.
+ */
+# define png_IDATs_skipped(pp) (((pp)->known_unknown & ~(pp)->save_unknown)&1U)
+#else /* !SAVE_UNKNOWN_CHUNKS */
+# define png_IDATs_skipped(pp) ((pp)->known_unknown & 1U)
+#endif /* !SAVE_UNKNOWN_CHUNKS */
+#endif /* HANDLE_AS_UNKNOWN */
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name
* followed by a PNG_HANDLE_* byte */
- int unknown_default; /* As PNG_HANDLE_* */
+ unsigned int unknown_default :2; /* As PNG_HANDLE_* */
unsigned int num_chunk_list; /* Number of entries in the list */
-#endif
-
- /* USER TRANSFORM SUPPORT */
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- png_user_transform_ptr read_user_transform_fn; /* user read transform */
-#endif
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
- png_user_transform_ptr write_user_transform_fn; /* user write transform */
-#endif
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
- png_voidp user_transform_ptr; /* user supplied data for the above */
- png_byte user_transform_depth; /* bit depth of user transformed pixels */
- png_byte user_transform_channels; /* channels in user transformed pixels */
-#endif
-
- /* READ TRANSFORM SUPPORT
- *
- * Quite a lot of things can be done to the original image data on read, and
- * most of these are configurable. The data required by the configurable
- * read transforms should be stored here. The png_color_16 and png_color_8
- * structures have low alignment requirements and odd sizes, so may cause
- * misalignment when present. Member alignment is as follows:
- *
- * png_color_16 png_uint_16
- * png_color_8 png_byte
- */
- /* GAMMA/BACKGROUND/ALPHA-MODE/RGB-TO-GRAY/tRNS/sBIT
- *
- * These things are all interrelated because they need some or all of the
- * gamma tables. Some attempt has been made below to order these members by
- * size, so that as little padding as possible is required.
- */
-#ifdef PNG_READ_GAMMA_SUPPORTED
- png_bytep gamma_table; /* gamma table for 8-bit depth files */
- png_uint_16p gamma_16_table; /* gamma table for 16-bit depth files */
-
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED) ||\
- defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- png_bytep gamma_from_1; /* converts from 1.0 to screen */
- png_uint_16p gamma_to_1; /* converts from file to 1.0 */
- png_uint_16p gamma_16_from_1; /* converts from 1.0 to screen */
- png_uint_16p gamma_16_to_1; /* converts from file to 1.0 */
-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
-#endif /* PNG_READ_GAMMA_SUPPORTED */
-
-#if defined(PNG_READ_tRNS_SUPPORTED) || \
- defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_EXPAND_SUPPORTED)
- png_bytep trans_alpha; /* alpha values for paletted files */
-#endif
-
- /* Integer values */
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- png_fixed_point background_gamma;
-#endif
-#ifdef PNG_READ_GAMMA_SUPPORTED
- png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
- int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */
-#endif
-
- /* png_color_16 */
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- png_color_16 background; /* background color in screen gamma space */
- png_color_16 background_1; /* background normalized to gamma 1.0 */
-#endif
-#if defined(PNG_READ_tRNS_SUPPORTED) || \
- defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_EXPAND_SUPPORTED)
- png_color_16 trans_color; /* transparent color for non-paletted files */
-#endif
-
- /* png_uint_16 */
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- png_uint_16 rgb_to_gray_red_coeff;
- png_uint_16 rgb_to_gray_green_coeff;
- /* The blue coefficient is calculated from the above */
-#endif
-
- /* png_color_8 */
-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_sBIT_SUPPORTED)
- png_color_8 sig_bit; /* significant bits in each available channel */
-#endif
-
- /* png_byte */
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
- defined(PNG_READ_ALPHA_MODE_SUPPORTED)
- png_byte background_gamma_type;
-#endif
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
- png_byte rgb_to_gray_status;
-#endif
-
- /* SHIFT - both READ_SHIFT and WRITE_SHIFT */
-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
- png_color_8 shift; /* shift for significant bit tranformation */
-#endif
-
- /* FILLER SUPPORT (pixel expansion or read, contraction on write) */
-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
- png_uint_16 filler; /* filler bytes for pixel expansion */
-#endif
-
- /* QUANTIZE (convert to color-mapped) */
-#ifdef PNG_READ_QUANTIZE_SUPPORTED
- png_bytep palette_lookup; /* lookup table for quantizing */
- png_bytep quantize_index; /* index translation for palette files */
- png_bytep quantize_sort; /* working sort array */
- png_bytep index_to_palette; /* where the original index currently is in the
- * palette
- */
- png_bytep palette_to_index; /* which original index points to this palette
- * color
- */
-#endif
-
- /* MNG SUPPORT */
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- png_uint_32 mng_features_permitted;
- png_byte filter_type;
-#endif
-
- /* Options */
-#ifdef PNG_SET_OPTION_SUPPORTED
- png_byte options; /* On/off state (up to 4 options) */
-#endif
+#endif /* SET_UNKNOWN_CHUNKS */
/* COMPRESSION AND DECOMPRESSION SUPPORT.
*
* zlib expects a 'zstream' as the fundamental control structure, it allows
* all the parameters to be passed as one pointer.
*/
- z_stream zstream; /* decompression structure */
+ png_uint_32 zowner; /* ID (chunk type) of zlib owner, 0 if none */
+# ifdef PNG_WRITE_SUPPORTED
+ png_zlib_statep zlib_state; /* State of zlib compression */
+# endif /* WRITE */
+# ifdef PNG_READ_SUPPORTED
+ z_stream zstream; /* decompression structure */
+ unsigned int zstream_ended:1; /* no more zlib output available [read] */
+ unsigned int zstream_error:1; /* zlib error message has been output [read] */
+# endif /* READ */
+# ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+ unsigned int zstream_eod :1; /* all the required uncompressed data has been
+ * received; set by the zstream using code for
+ * its own purposes. [progressive read] */
+# endif /* PROGRESSIVE_READ */
+# ifdef PNG_BENIGN_ERRORS_SUPPORTED
+ unsigned int benign_error_action :2;
+ unsigned int app_warning_action :2;
+ unsigned int app_error_action :2;
+# ifdef PNG_READ_SUPPORTED
+ unsigned int IDAT_error_action :2;
+# endif /* READ */
+# endif /* BENIGN_ERRORS */
+# ifdef PNG_READ_SUPPORTED
+ /* CRC checking actions, one for critical chunks, one for ancillary
+ * chunks.
+ */
+ unsigned int critical_crc :2;
+ unsigned int ancillary_crc :2;
+ unsigned int current_crc :2; /* Cache of one or other of the above */
+# endif
+# ifdef PNG_SET_OPTION_SUPPORTED
+# ifdef PNG_READ_SUPPORTED
+ unsigned int maximum_inflate_window :1U;
+# endif /* READ */
+ unsigned int skip_sRGB_profile_check :1U;
+# endif /* SET_OPTION */
-#ifdef PNG_READ_SUPPORTED
- /* These, and IDAT_read_size below, control how much input and output (at
- * most) is available to zlib.
+ /* MNG SUPPORT */
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ unsigned int mng_features_permitted :3;
+#endif /* MNG_FEATURES */
+
+ /* SCRATCH buffers, used when control returns to the application or a read
+ * loop.
*/
- png_uint_32 idat_size; /* current IDAT size for read */
- png_alloc_size_t read_buffer_size; /* current size of the buffer */
-#endif
-
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
- int zlib_text_level; /* holds zlib compression level */
- int zlib_text_method; /* holds zlib compression method */
- int zlib_text_window_bits; /* holds zlib compression window bits */
- int zlib_text_mem_level; /* holds zlib compression memory level */
- int zlib_text_strategy; /* holds zlib compression strategy */
-#endif
-
-#ifdef PNG_WRITE_SUPPORTED
- int zlib_level; /* holds zlib compression level */
- int zlib_method; /* holds zlib compression method */
- int zlib_window_bits; /* holds zlib compression window bits */
- int zlib_mem_level; /* holds zlib compression memory level */
- int zlib_strategy; /* holds zlib compression strategy */
-
- int zlib_set_level; /* Actual values set into the zstream on write */
- int zlib_set_method;
- int zlib_set_window_bits;
- int zlib_set_mem_level;
- int zlib_set_strategy;
-
- png_compression_bufferp zbuffer_list; /* Created on demand during write */
- uInt zbuffer_size; /* size of the actual zlib buffer */
-#endif
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
- uInt IDAT_read_size; /* limit on read buffer size for IDAT */
-#endif
+# ifdef PNG_READ_SUPPORTED
+ png_byte scratch[PNG_ROW_BUFFER_SIZE+16U];
+# endif /* READ */
};
#endif /* PNGSTRUCT_H */
diff --git a/ext/libpng17/pngtest.c b/ext/libpng17/pngtest.c
index 9add642142..d872d803e7 100644
--- a/ext/libpng17/pngtest.c
+++ b/ext/libpng17/pngtest.c
@@ -1,8 +1,8 @@
/* pngtest.c - a simple test program to test libpng
*
- * Last changed in libpng 1.6.11 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.26 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -31,11 +31,6 @@
* of files at once by typing "pngtest -m file1.png file2.png ..."
*/
-#ifdef _MSC_VER
-#pragma warning (disable:4996)
-#pragma warning (disable:4244)
-#endif
-
#define _POSIX_SOURCE 1
#include
@@ -66,10 +61,11 @@
defined PNG_READ_sBIT_SUPPORTED &&\
defined PNG_READ_sCAL_SUPPORTED &&\
defined PNG_READ_sRGB_SUPPORTED &&\
+ defined PNG_READ_sPLT_SUPPORTED &&\
defined PNG_READ_tEXt_SUPPORTED &&\
defined PNG_READ_tIME_SUPPORTED &&\
defined PNG_READ_zTXt_SUPPORTED &&\
- defined PNG_WRITE_INTERLACING_SUPPORTED
+ (defined PNG_WRITE_INTERLACING_SUPPORTED || PNG_LIBPNG_VER >= 10700)
#ifdef PNG_ZLIB_HEADER
# include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */
@@ -106,6 +102,10 @@ typedef FILE * png_FILE_p;
# define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */
#endif
+#ifndef PNG_UNUSED
+# define PNG_UNUSED(param) (void)param;
+#endif
+
/* Turn on CPU timing
#define PNGTEST_TIMING
*/
@@ -123,6 +123,22 @@ static float t_start, t_stop, t_decode, t_encode, t_misc;
#define PNG_tIME_STRING_LENGTH 29
static int tIME_chunk_present = 0;
static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present";
+
+#if PNG_LIBPNG_VER < 10619
+#define png_convert_to_rfc1123_buffer(ts, t) tIME_to_str(read_ptr, ts, t)
+
+static int
+tIME_to_str(png_structp png_ptr, png_charp ts, png_const_timep t)
+{
+ png_const_charp str = png_convert_to_rfc1123(png_ptr, t);
+
+ if (str == NULL)
+ return 0;
+
+ strcpy(ts, str);
+ return 1;
+}
+#endif /* older libpng */
#endif
static int verbose = 0;
@@ -190,16 +206,14 @@ write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass)
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-/* Example of using user transform callback (we don't transform anything,
- * but merely examine the row filters. We set this to 256 rather than
- * 5 in case illegal filter values are present.)
+/* Example of using a user transform callback (doesn't do anything at present).
*/
-static png_uint_32 filters_used[256];
static void PNGCBAPI
-count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data)
+read_user_callback(png_structp png_ptr, png_row_infop row_info, png_bytep data)
{
- if (png_ptr != NULL && row_info != NULL)
- ++filters_used[*(data - 1)];
+ PNG_UNUSED(png_ptr)
+ PNG_UNUSED(row_info)
+ PNG_UNUSED(data)
}
#endif
@@ -226,96 +240,97 @@ count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data)
* png_byte pixel_depth bits per pixel (depth*channels)
*/
- /* Counts the number of zero samples (or zero pixels if color_type is 3 */
+ /* Counts the number of zero samples (or zero pixels if color_type is 3 */
- if (row_info->color_type == 0 || row_info->color_type == 3)
- {
- int pos = 0;
- png_uint_32 n, nstop;
+ if (row_info->color_type == 0 || row_info->color_type == 3)
+ {
+ int pos = 0;
+ png_uint_32 n, nstop;
- for (n = 0, nstop=row_info->width; nbit_depth == 1)
- {
- if (((*dp << pos++ ) & 0x80) == 0)
- zero_samples++;
+ for (n = 0, nstop=row_info->width; nbit_depth == 1)
+ {
+ if (((*dp << pos++ ) & 0x80) == 0)
+ zero_samples++;
- if (pos == 8)
- {
- pos = 0;
- dp++;
- }
- }
+ if (pos == 8)
+ {
+ pos = 0;
+ dp++;
+ }
+ }
- if (row_info->bit_depth == 2)
- {
- if (((*dp << (pos+=2)) & 0xc0) == 0)
- zero_samples++;
+ if (row_info->bit_depth == 2)
+ {
+ if (((*dp << (pos+=2)) & 0xc0) == 0)
+ zero_samples++;
- if (pos == 8)
- {
- pos = 0;
- dp++;
- }
- }
+ if (pos == 8)
+ {
+ pos = 0;
+ dp++;
+ }
+ }
- if (row_info->bit_depth == 4)
- {
- if (((*dp << (pos+=4)) & 0xf0) == 0)
- zero_samples++;
+ if (row_info->bit_depth == 4)
+ {
+ if (((*dp << (pos+=4)) & 0xf0) == 0)
+ zero_samples++;
- if (pos == 8)
- {
- pos = 0;
- dp++;
- }
- }
+ if (pos == 8)
+ {
+ pos = 0;
+ dp++;
+ }
+ }
- if (row_info->bit_depth == 8)
- if (*dp++ == 0)
- zero_samples++;
+ if (row_info->bit_depth == 8)
+ if (*dp++ == 0)
+ zero_samples++;
- if (row_info->bit_depth == 16)
- {
- if ((*dp | *(dp+1)) == 0)
- zero_samples++;
- dp+=2;
- }
- }
- }
- else /* Other color types */
- {
- png_uint_32 n, nstop;
- int channel;
- int color_channels = row_info->channels;
- if (row_info->color_type > 3)color_channels--;
+ if (row_info->bit_depth == 16)
+ {
+ if ((*dp | *(dp+1)) == 0)
+ zero_samples++;
+ dp+=2;
+ }
+ }
+ }
+ else /* Other color types */
+ {
+ png_uint_32 n, nstop;
+ int channel;
+ int color_channels = row_info->channels;
+ if (row_info->color_type > 3)
+ color_channels--;
- for (n = 0, nstop=row_info->width; nbit_depth == 8)
- if (*dp++ == 0)
- zero_samples++;
+ for (n = 0, nstop=row_info->width; nbit_depth == 8)
+ if (*dp++ == 0)
+ zero_samples++;
- if (row_info->bit_depth == 16)
- {
- if ((*dp | *(dp+1)) == 0)
- zero_samples++;
+ if (row_info->bit_depth == 16)
+ {
+ if ((*dp | *(dp+1)) == 0)
+ zero_samples++;
- dp+=2;
- }
- }
- if (row_info->color_type > 3)
- {
- dp++;
- if (row_info->bit_depth == 16)
- dp++;
- }
- }
- }
+ dp+=2;
+ }
+ }
+ if (row_info->color_type > 3)
+ {
+ dp++;
+ if (row_info->bit_depth == 16)
+ dp++;
+ }
+ }
+ }
}
-#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */
+#endif /* WRITE_USER_TRANSFORM */
#ifndef PNG_STDIO_SUPPORTED
/* START of code to validate stdio-free compilation */
@@ -330,10 +345,10 @@ count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data)
#ifdef PNG_IO_STATE_SUPPORTED
void
pngtest_check_io_state(png_structp png_ptr, png_size_t data_length,
- png_uint_32 io_op);
+ png_uint_32 io_op);
void
pngtest_check_io_state(png_structp png_ptr, png_size_t data_length,
- png_uint_32 io_op)
+ png_uint_32 io_op)
{
png_uint_32 io_state = png_get_io_state(png_ptr);
int err = 0;
@@ -424,7 +439,7 @@ pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
pngtest_check_io_state(png_ptr, length, PNG_IO_WRITING);
#endif
}
-#endif /* !PNG_STDIO_SUPPORTED */
+#endif /* !STDIO */
/* This function is called when there is a warning, but the library thinks
* it can continue anyway. Replacement functions don't have to do anything
@@ -473,7 +488,7 @@ pngtest_error(png_structp png_ptr, png_const_charp message)
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
/* Allocate memory. For reasonable files, size should never exceed
- * 64K. However, zlib may allocate more then 64K if you don't tell
+ * 64K. However, zlib may allocate more than 64K if you don't tell
* it not to. See zconf.h and png.h for more information. zlib does
* need to allocate exactly 64K, so whatever you call here must
* have the ability to do that.
@@ -517,7 +532,7 @@ PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size)
memory_infop pinfo;
png_set_mem_fn(png_ptr, NULL, NULL, NULL);
pinfo = (memory_infop)png_malloc(png_ptr,
- (sizeof *pinfo));
+ (sizeof *pinfo));
pinfo->size = size;
current_allocation += size;
total_allocation += size;
@@ -547,7 +562,7 @@ PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size)
if (verbose != 0)
printf("png_malloc %lu bytes at %p\n", (unsigned long)size,
- pinfo->pointer);
+ pinfo->pointer);
return (png_voidp)(pinfo->pointer);
}
@@ -569,6 +584,7 @@ png_debug_free(png_structp png_ptr, png_voidp ptr)
}
/* Unlink the element from the list. */
+ if (pinformation != NULL)
{
memory_infop *ppinfo = &pinformation;
@@ -585,15 +601,14 @@ png_debug_free(png_structp png_ptr, png_voidp ptr)
/* We must free the list element too, but first kill
the memory that is to be freed. */
memset(ptr, 0x55, pinfo->size);
- if (pinfo != NULL)
- free(pinfo);
+ free(pinfo);
pinfo = NULL;
break;
}
if (pinfo->next == NULL)
{
- fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr);
+ fprintf(STDERR, "Pointer %p not found\n", ptr);
break;
}
@@ -609,7 +624,7 @@ png_debug_free(png_structp png_ptr, png_voidp ptr)
free(ptr);
ptr = NULL;
}
-#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */
+#endif /* USER_MEM && DEBUG */
/* END of code to test memory allocation/deallocation */
@@ -648,16 +663,16 @@ set_location(png_structp png_ptr, struct user_chunk_data *data, int what)
{
int location;
- if ((data->location[0] & what) || (data->location[1] & what))
+ if ((data->location[0] & what) != 0 || (data->location[1] & what) != 0)
return 0; /* already have one of these */
- /* Find where we are (the code below zeros info_ptr to indicate that the
+ /* Find where we are (the code below zeroes info_ptr to indicate that the
* chunks before the first IDAT have been read.)
*/
if (data->info_ptr == NULL) /* after IDAT */
location = what | after_IDAT;
- else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE))
+ else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE) != 0)
location = what | before_IDAT;
else
@@ -704,7 +719,7 @@ read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk)
if (chunk->data[0] != 0 && chunk->data[0] != 1)
return (-1); /* Invalid mode */
- if (set_location(png_ptr, my_user_chunk_data, have_sTER))
+ if (set_location(png_ptr, my_user_chunk_data, have_sTER) != 0)
{
my_user_chunk_data->sTER_mode=chunk->data[0];
return (1);
@@ -723,7 +738,7 @@ read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk)
if (chunk->size != 9)
return (-1); /* Error return */
- if (!set_location(png_ptr, my_user_chunk_data, have_vpAg))
+ if (set_location(png_ptr, my_user_chunk_data, have_vpAg) == 0)
return (0); /* duplicate vpAg */
my_user_chunk_data->vpAg_width = png_get_uint_31(png_ptr, chunk->data);
@@ -754,9 +769,9 @@ write_vpAg_chunk(png_structp write_ptr)
if (verbose != 0)
fprintf(STDERR, " vpAg = %lu x %lu, units = %d\n",
- (unsigned long)user_chunk_data.vpAg_width,
- (unsigned long)user_chunk_data.vpAg_height,
- user_chunk_data.vpAg_units);
+ (unsigned long)user_chunk_data.vpAg_width,
+ (unsigned long)user_chunk_data.vpAg_height,
+ user_chunk_data.vpAg_units);
png_save_uint_32(vpag_chunk_data, user_chunk_data.vpAg_width);
png_save_uint_32(vpag_chunk_data + 4, user_chunk_data.vpAg_height);
@@ -784,8 +799,8 @@ write_chunks(png_structp write_ptr, int location)
write_vpAg_chunk(write_ptr);
}
}
-#endif /* PNG_WRITE_SUPPORTED */
-#else /* !PNG_READ_USER_CHUNKS_SUPPORTED */
+#endif /* WRITE */
+#else /* !READ_USER_CHUNKS */
# define write_chunks(pp,loc) ((void)0)
#endif
/* END of code to demonstrate user chunk support */
@@ -796,8 +811,8 @@ write_chunks(png_structp write_ptr, int location)
*/
#ifdef PNG_TEXT_SUPPORTED
static void
-pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr,
- int num_text)
+pngtest_check_text_support(png_structp png_ptr, png_textp text_ptr,
+ int num_text)
{
while (num_text > 0)
{
@@ -809,6 +824,8 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr,
case PNG_TEXT_COMPRESSION_zTXt:
# ifndef PNG_WRITE_zTXt_SUPPORTED
++unsupported_chunks;
+ /* In libpng 1.7 this now does an app-error, so stop it: */
+ text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
# endif
break;
@@ -816,6 +833,7 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr,
case PNG_ITXT_COMPRESSION_zTXt:
# ifndef PNG_WRITE_iTXt_SUPPORTED
++unsupported_chunks;
+ text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
# endif
break;
@@ -842,16 +860,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
png_structp write_ptr;
png_infop write_info_ptr;
png_infop write_end_info_ptr;
+#ifdef PNG_WRITE_FILTER_SUPPORTED
int interlace_preserved = 1;
-#else
+#endif /* WRITE_FILTER */
+#else /* !WRITE */
png_structp write_ptr = NULL;
png_infop write_info_ptr = NULL;
png_infop write_end_info_ptr = NULL;
-#endif
+#endif /* !WRITE */
png_bytep row_buf;
png_uint_32 y;
png_uint_32 width, height;
- int num_pass = 1, pass;
+ volatile int num_passes;
+ int pass;
int bit_depth, color_type;
row_buf = NULL;
@@ -873,26 +894,26 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
pngtest_debug("Allocating read and write structures");
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
read_ptr =
- png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL,
- NULL, NULL, NULL, png_debug_malloc, png_debug_free);
+ png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL,
+ NULL, NULL, NULL, png_debug_malloc, png_debug_free);
#else
read_ptr =
- png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
png_set_error_fn(read_ptr, &error_parameters, pngtest_error,
- pngtest_warning);
+ pngtest_warning);
#ifdef PNG_WRITE_SUPPORTED
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
write_ptr =
- png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL,
- NULL, NULL, NULL, png_debug_malloc, png_debug_free);
+ png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL,
+ NULL, NULL, NULL, png_debug_malloc, png_debug_free);
#else
write_ptr =
- png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
png_set_error_fn(write_ptr, &error_parameters, pngtest_error,
- pngtest_warning);
+ pngtest_warning);
#endif
pngtest_debug("Allocating read_info, write_info and end_info structures");
read_info_ptr = png_create_info_struct(read_ptr);
@@ -905,7 +926,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
init_callback_info(read_info_ptr);
png_set_read_user_chunk_fn(read_ptr, &user_chunk_data,
- read_user_chunk_callback);
+ read_user_chunk_callback);
#endif
#ifdef PNG_SETJMP_SUPPORTED
@@ -943,15 +964,16 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
#endif
#endif
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
if (strict != 0)
{
/* Treat png_benign_error() as errors on read */
png_set_benign_errors(read_ptr, 0);
-#ifdef PNG_WRITE_SUPPORTED
+# ifdef PNG_WRITE_SUPPORTED
/* Treat them as errors on write */
png_set_benign_errors(write_ptr, 0);
-#endif
+# endif
/* if strict is not set, then app warnings and errors are treated as
* warnings in release builds, but not in unstable builds; this can be
@@ -964,10 +986,15 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
/* Allow application (pngtest) errors and warnings to pass */
png_set_benign_errors(read_ptr, 1);
-#ifdef PNG_WRITE_SUPPORTED
+ /* Turn off CRC and ADLER32 checking while reading */
+ png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+
+# ifdef PNG_WRITE_SUPPORTED
png_set_benign_errors(write_ptr, 1);
-#endif
+# endif
+
}
+#endif /* BENIGN_ERRORS */
pngtest_debug("Initializing input and output streams");
#ifdef PNG_STDIO_SUPPORTED
@@ -980,9 +1007,9 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
# ifdef PNG_WRITE_SUPPORTED
png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data,
# ifdef PNG_WRITE_FLUSH_SUPPORTED
- pngtest_flush);
+ pngtest_flush);
# else
- NULL);
+ NULL);
# endif
# endif
#endif
@@ -1004,14 +1031,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
}
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- {
- int i;
-
- for (i = 0; i<256; i++)
- filters_used[i] = 0;
-
- png_set_read_user_transform_fn(read_ptr, count_filters);
- }
+ png_set_read_user_transform_fn(read_ptr, read_user_callback);
#endif
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
zero_samples = 0;
@@ -1029,11 +1049,11 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
*/
#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS,
- NULL, 0);
+ NULL, 0);
#endif
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
- NULL, 0);
+ NULL, 0);
#endif
#endif
@@ -1054,43 +1074,43 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
int interlace_type, compression_type, filter_type;
if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth,
- &color_type, &interlace_type, &compression_type, &filter_type))
+ &color_type, &interlace_type, &compression_type, &filter_type) != 0)
{
png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth,
- color_type, interlace_type, compression_type, filter_type);
-#ifndef PNG_READ_INTERLACING_SUPPORTED
- /* num_pass will not be set below, set it here if the image is
- * interlaced: what happens is that write interlacing is *not* turned
- * on an the partial interlaced rows are written directly.
+ color_type, interlace_type, compression_type, filter_type);
+ /* num_passes may not be available below if interlace support is not
+ * provided by libpng for both read and write.
*/
switch (interlace_type)
{
case PNG_INTERLACE_NONE:
- num_pass = 1;
+ num_passes = 1;
break;
case PNG_INTERLACE_ADAM7:
- num_pass = 7;
- break;
+ num_passes = 7;
+ break;
default:
- png_error(read_ptr, "invalid interlace type");
- /*NOT REACHED*/
+ png_error(read_ptr, "invalid interlace type");
+ /*NOT REACHED*/
}
-#endif
}
+
+ else
+ png_error(read_ptr, "png_get_IHDR failed");
}
#ifdef PNG_FIXED_POINT_SUPPORTED
#ifdef PNG_cHRM_SUPPORTED
{
png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
- blue_y;
+ blue_y;
if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y,
- &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y))
+ &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0)
{
png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x,
- red_y, green_x, green_y, blue_x, blue_y);
+ red_y, green_x, green_y, blue_x, blue_y);
}
}
#endif
@@ -1098,7 +1118,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
{
png_fixed_point gamma;
- if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma))
+ if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma) != 0)
png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma);
}
#endif
@@ -1107,13 +1127,13 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
#ifdef PNG_cHRM_SUPPORTED
{
double white_x, white_y, red_x, red_y, green_x, green_y, blue_x,
- blue_y;
+ blue_y;
if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x,
- &red_y, &green_x, &green_y, &blue_x, &blue_y))
+ &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0)
{
png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x,
- red_y, green_x, green_y, blue_x, blue_y);
+ red_y, green_x, green_y, blue_x, blue_y);
}
}
#endif
@@ -1121,7 +1141,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
{
double gamma;
- if (png_get_gAMA(read_ptr, read_info_ptr, &gamma))
+ if (png_get_gAMA(read_ptr, read_info_ptr, &gamma) != 0)
png_set_gAMA(write_ptr, write_info_ptr, gamma);
}
#endif
@@ -1135,10 +1155,10 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
int compression_type;
if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type,
- &profile, &proflen))
+ &profile, &proflen) != 0)
{
png_set_iCCP(write_ptr, write_info_ptr, name, compression_type,
- profile, proflen);
+ profile, proflen);
}
}
#endif
@@ -1146,7 +1166,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
{
int intent;
- if (png_get_sRGB(read_ptr, read_info_ptr, &intent))
+ if (png_get_sRGB(read_ptr, read_info_ptr, &intent) != 0)
png_set_sRGB(write_ptr, write_info_ptr, intent);
}
#endif
@@ -1154,14 +1174,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
png_colorp palette;
int num_palette;
- if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette))
+ if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette) != 0)
png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
}
#ifdef PNG_bKGD_SUPPORTED
{
png_color_16p background;
- if (png_get_bKGD(read_ptr, read_info_ptr, &background))
+ if (png_get_bKGD(read_ptr, read_info_ptr, &background) != 0)
{
png_set_bKGD(write_ptr, write_info_ptr, background);
}
@@ -1171,7 +1191,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
{
png_uint_16p hist;
- if (png_get_hIST(read_ptr, read_info_ptr, &hist))
+ if (png_get_hIST(read_ptr, read_info_ptr, &hist) != 0)
png_set_hIST(write_ptr, write_info_ptr, hist);
}
#endif
@@ -1181,7 +1201,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
int unit_type;
if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y,
- &unit_type))
+ &unit_type) != 0)
{
png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type);
}
@@ -1195,10 +1215,10 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
int type, nparams;
if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type,
- &nparams, &units, ¶ms))
+ &nparams, &units, ¶ms) != 0)
{
png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type,
- nparams, units, params);
+ nparams, units, params);
}
}
#endif
@@ -1207,7 +1227,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
png_uint_32 res_x, res_y;
int unit_type;
- if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type))
+ if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y,
+ &unit_type) != 0)
png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type);
}
#endif
@@ -1215,7 +1236,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
{
png_color_8p sig_bit;
- if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
+ if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit) != 0)
png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
}
#endif
@@ -1227,7 +1248,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
double scal_width, scal_height;
if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width,
- &scal_height))
+ &scal_height) != 0)
{
png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height);
}
@@ -1239,7 +1260,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
png_charp scal_width, scal_height;
if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width,
- &scal_height))
+ &scal_height) != 0)
{
png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width,
scal_height);
@@ -1248,6 +1269,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
#endif
#endif
#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+ {
+ png_sPLT_tp entries;
+
+ int num_entries = (int) png_get_sPLT(read_ptr, read_info_ptr, &entries);
+ if (num_entries)
+ {
+ png_set_sPLT(write_ptr, write_info_ptr, entries, num_entries);
+ }
+ }
+#endif
+
#ifdef PNG_TEXT_SUPPORTED
{
png_textp text_ptr;
@@ -1267,7 +1301,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
for (i=0; i 0)
+ pass_height = PNG_PASS_ROWS(height, pass);
+
+ else
+ pass_height = 0;
+ }
+
+ else /* not interlaced */
+ pass_height = height;
+# else
+# define pass_height height
+# endif
+
pngtest_debug1("Writing row data for pass %d", pass);
- for (y = 0; y < height; y++)
+ for (y = 0; y < pass_height; y++)
{
#ifndef SINGLE_ROWBUF_ALLOC
pngtest_debug2("Allocating row buffer (pass %d, y = %u)...", pass, y);
- row_buf = (png_bytep)png_malloc(read_ptr,
- png_get_rowbytes(read_ptr, read_info_ptr));
- pngtest_debug2("\t0x%08lx (%u bytes)", (unsigned long)row_buf,
- png_get_rowbytes(read_ptr, read_info_ptr));
+ row_buf = (png_bytep)png_malloc(read_ptr,
+ png_get_rowbytes(read_ptr, read_info_ptr));
+
+ pngtest_debug2("\t0x%08lx (%lu bytes)", (unsigned long)row_buf,
+ (unsigned long)png_get_rowbytes(read_ptr, read_info_ptr));
#endif /* !SINGLE_ROWBUF_ALLOC */
png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1);
@@ -1408,7 +1476,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
t_encode += (t_stop - t_start);
t_start = t_stop;
#endif
-#endif /* PNG_WRITE_SUPPORTED */
+#endif /* WRITE */
#ifndef SINGLE_ROWBUF_ALLOC
pngtest_debug2("Freeing row buffer (pass %d, y = %u)", pass, y);
@@ -1449,7 +1517,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
for (i=0; i 0)
{
fprintf(STDERR, "\n %s: unsupported chunks (%d)%s",
- inname, unsupported_chunks, strict ? ": IGNORED --strict!" : "");
+ inname, unsupported_chunks, strict ? ": IGNORED --strict!" : "");
}
# endif
else if (warning_count > 0)
{
fprintf(STDERR, "\n %s: %d libpng warnings found",
- inname, warning_count);
+ inname, warning_count);
if (strict != 0)
return (1);
@@ -1603,7 +1671,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
return (1);
}
-#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */
+#if defined (PNG_WRITE_SUPPORTED) /* else nothing was written */ &&\
+ defined (PNG_WRITE_FILTER_SUPPORTED)
if (interlace_preserved != 0) /* else the files will be changed */
{
for (;;)
@@ -1618,18 +1687,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
if (num_in != num_out)
{
fprintf(STDERR, "\nFiles %s and %s are of a different size\n",
- inname, outname);
+ inname, outname);
if (wrote_question == 0 && unsupported_chunks == 0)
{
fprintf(STDERR,
- " Was %s written with the same maximum IDAT chunk size (%d bytes),",
- inname, PNG_ZBUF_SIZE);
+ " Was %s written with the same maximum IDAT"
+ " chunk size (%d bytes),",
+ inname, PNG_ZBUF_SIZE);
fprintf(STDERR,
- "\n filtering heuristic (libpng default), compression");
+ "\n filtering heuristic (libpng default), compression");
fprintf(STDERR,
- " level (zlib default),\n and zlib version (%s)?\n\n",
- ZLIB_VERSION);
+ " level (zlib default),\n and zlib version (%s)?\n\n",
+ ZLIB_VERSION);
wrote_question = 1;
}
@@ -1649,17 +1719,18 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
if (memcmp(inbuf, outbuf, num_in))
{
fprintf(STDERR, "\nFiles %s and %s are different\n", inname,
- outname);
+ outname);
if (wrote_question == 0 && unsupported_chunks == 0)
{
fprintf(STDERR,
- " Was %s written with the same maximum IDAT chunk size (%d bytes),",
+ " Was %s written with the same maximum"
+ " IDAT chunk size (%d bytes),",
inname, PNG_ZBUF_SIZE);
fprintf(STDERR,
- "\n filtering heuristic (libpng default), compression");
+ "\n filtering heuristic (libpng default), compression");
fprintf(STDERR,
- " level (zlib default),\n and zlib version (%s)?\n\n",
+ " level (zlib default),\n and zlib version (%s)?\n\n",
ZLIB_VERSION);
wrote_question = 1;
}
@@ -1680,7 +1751,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
}
}
}
-#endif /* PNG_WRITE_SUPPORTED */
+#endif /* WRITE && WRITE_FILTER */
FCLOSE(fpin);
FCLOSE(fpout);
@@ -1703,17 +1774,19 @@ main(int argc, char *argv[])
int multiple = 0;
int ierror = 0;
+ png_structp dummy_ptr;
+
fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING);
fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION);
fprintf(STDERR, "%s", png_get_copyright(NULL));
/* Show the version of libpng used in building the library */
fprintf(STDERR, " library (%lu):%s",
- (unsigned long)png_access_version_number(),
- png_get_header_version(NULL));
+ (unsigned long)png_access_version_number(),
+ png_get_header_version(NULL));
/* Show the version of libpng used in building the application */
fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER,
- PNG_HEADER_VERSION_STRING);
+ PNG_HEADER_VERSION_STRING);
/* Do some consistency checking on the memory allocation settings, I'm
* not sure this matters, but it is nice to know, the first of these
@@ -1731,7 +1804,7 @@ main(int argc, char *argv[])
if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING))
{
fprintf(STDERR,
- "Warning: versions are different between png.h and png.c\n");
+ "Warning: versions are different between png.h and png.c\n");
fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING);
fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver);
++ierror;
@@ -1785,19 +1858,20 @@ main(int argc, char *argv[])
}
}
- if (!multiple && argc == 3 + verbose)
- outname = argv[2 + verbose];
+ if (multiple == 0 && argc == 3 + verbose)
+ outname = argv[2 + verbose];
- if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2))
+ if ((multiple == 0 && argc > 3 + verbose) ||
+ (multiple != 0 && argc < 2))
{
- fprintf(STDERR,
- "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
- argv[0], argv[0]);
- fprintf(STDERR,
- " reads/writes one PNG file (without -m) or multiple files (-m)\n");
- fprintf(STDERR,
- " with -m %s is used as a temporary file\n", outname);
- exit(1);
+ fprintf(STDERR,
+ "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n",
+ argv[0], argv[0]);
+ fprintf(STDERR,
+ " reads/writes one PNG file (without -m) or multiple files (-m)\n");
+ fprintf(STDERR,
+ " with -m %s is used as a temporary file\n", outname);
+ exit(1);
}
if (multiple != 0)
@@ -1810,30 +1884,24 @@ main(int argc, char *argv[])
{
int kerror;
fprintf(STDERR, "\n Testing %s:", argv[i]);
+#if PNG_DEBUG > 0
+ fprintf(STDERR, "\n");
+#endif
kerror = test_one_file(argv[i], outname);
if (kerror == 0)
{
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- int k;
-#endif
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
fprintf(STDERR, "\n PASS (%lu zero samples)\n",
- (unsigned long)zero_samples);
+ (unsigned long)zero_samples);
#else
fprintf(STDERR, " PASS\n");
#endif
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- for (k = 0; k<256; k++)
- if (filters_used[k])
- fprintf(STDERR, " Filter %d was used %lu times\n",
- k, (unsigned long)filters_used[k]);
-#endif
#ifdef PNG_TIME_RFC1123_SUPPORTED
- if (tIME_chunk_present != 0)
- fprintf(STDERR, " tIME = %s\n", tIME_string);
+ if (tIME_chunk_present != 0)
+ fprintf(STDERR, " tIME = %s\n", tIME_string);
- tIME_chunk_present = 0;
-#endif /* PNG_TIME_RFC1123_SUPPORTED */
+ tIME_chunk_present = 0;
+#endif /* TIME_RFC1123 */
}
else
@@ -1844,20 +1912,20 @@ main(int argc, char *argv[])
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
if (allocation_now != current_allocation)
fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
- current_allocation - allocation_now);
+ current_allocation - allocation_now);
if (current_allocation != 0)
{
memory_infop pinfo = pinformation;
fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
- current_allocation);
+ current_allocation);
while (pinfo != NULL)
{
- fprintf(STDERR, " %lu bytes at %x\n",
- (unsigned long)pinfo->size,
- (unsigned int)pinfo->pointer);
+ fprintf(STDERR, " %lu bytes at %p\n",
+ (unsigned long)pinfo->size,
+ pinfo->pointer);
pinfo = pinfo->next;
}
}
@@ -1865,13 +1933,13 @@ main(int argc, char *argv[])
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
fprintf(STDERR, " Current memory allocation: %10d bytes\n",
- current_allocation);
+ current_allocation);
fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
- maximum_allocation);
+ maximum_allocation);
fprintf(STDERR, " Total memory allocation: %10d bytes\n",
- total_allocation);
+ total_allocation);
fprintf(STDERR, " Number of allocations: %10d\n",
- num_allocations);
+ num_allocations);
#endif
}
@@ -1891,7 +1959,12 @@ main(int argc, char *argv[])
status_dots_requested = 0;
if (i == 0 || verbose == 1 || ierror != 0)
+ {
fprintf(STDERR, "\n Testing %s:", inname);
+#if PNG_DEBUG > 0
+ fprintf(STDERR, "\n");
+#endif
+ }
kerror = test_one_file(inname, outname);
@@ -1899,32 +1972,28 @@ main(int argc, char *argv[])
{
if (verbose == 1 || i == 2)
{
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- int k;
-#endif
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
fprintf(STDERR, "\n PASS (%lu zero samples)\n",
- (unsigned long)zero_samples);
+ (unsigned long)zero_samples);
#else
fprintf(STDERR, " PASS\n");
#endif
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- for (k = 0; k<256; k++)
- if (filters_used[k])
- fprintf(STDERR, " Filter %d was used %lu times\n",
- k, (unsigned long)filters_used[k]);
-#endif
#ifdef PNG_TIME_RFC1123_SUPPORTED
if (tIME_chunk_present != 0)
fprintf(STDERR, " tIME = %s\n", tIME_string);
-#endif /* PNG_TIME_RFC1123_SUPPORTED */
+#endif /* TIME_RFC1123 */
}
}
else
{
if (verbose == 0 && i != 2)
+ {
fprintf(STDERR, "\n Testing %s:", inname);
+#if PNG_DEBUG > 0
+ fprintf(STDERR, "\n");
+#endif
+ }
fprintf(STDERR, " FAIL\n");
ierror += kerror;
@@ -1932,19 +2001,19 @@ main(int argc, char *argv[])
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
if (allocation_now != current_allocation)
fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n",
- current_allocation - allocation_now);
+ current_allocation - allocation_now);
if (current_allocation != 0)
{
memory_infop pinfo = pinformation;
fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
- current_allocation);
+ current_allocation);
while (pinfo != NULL)
{
- fprintf(STDERR, " %lu bytes at %x\n",
- (unsigned long)pinfo->size, (unsigned int)pinfo->pointer);
+ fprintf(STDERR, " %lu bytes at %p\n",
+ (unsigned long)pinfo->size, pinfo->pointer);
pinfo = pinfo->next;
}
}
@@ -1952,13 +2021,13 @@ main(int argc, char *argv[])
}
#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG
fprintf(STDERR, " Current memory allocation: %10d bytes\n",
- current_allocation);
+ current_allocation);
fprintf(STDERR, " Maximum memory allocation: %10d bytes\n",
- maximum_allocation);
+ maximum_allocation);
fprintf(STDERR, " Total memory allocation: %10d bytes\n",
- total_allocation);
+ total_allocation);
fprintf(STDERR, " Number of allocations: %10d\n",
- num_allocations);
+ num_allocations);
#endif
}
@@ -1967,13 +2036,13 @@ main(int argc, char *argv[])
t_misc += (t_stop - t_start);
t_start = t_stop;
fprintf(STDERR, " CPU time used = %.3f seconds",
- (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC);
+ (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC);
fprintf(STDERR, " (decoding %.3f,\n",
- t_decode/(float)CLOCKS_PER_SEC);
+ t_decode/(float)CLOCKS_PER_SEC);
fprintf(STDERR, " encoding %.3f ,",
- t_encode/(float)CLOCKS_PER_SEC);
+ t_encode/(float)CLOCKS_PER_SEC);
fprintf(STDERR, " other %.3f seconds)\n\n",
- t_misc/(float)CLOCKS_PER_SEC);
+ t_misc/(float)CLOCKS_PER_SEC);
#endif
if (ierror == 0)
@@ -1982,6 +2051,24 @@ main(int argc, char *argv[])
else
fprintf(STDERR, " libpng FAILS test\n");
+ dummy_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ fprintf(STDERR, " Default limits:\n");
+ fprintf(STDERR, " width_max = %lu\n",
+ (unsigned long) png_get_user_width_max(dummy_ptr));
+ fprintf(STDERR, " height_max = %lu\n",
+ (unsigned long) png_get_user_height_max(dummy_ptr));
+ if (png_get_chunk_cache_max(dummy_ptr) == 0)
+ fprintf(STDERR, " cache_max = unlimited\n");
+ else
+ fprintf(STDERR, " cache_max = %lu\n",
+ (unsigned long) png_get_chunk_cache_max(dummy_ptr));
+ if (png_get_chunk_malloc_max(dummy_ptr) == 0)
+ fprintf(STDERR, " malloc_max = unlimited\n");
+ else
+ fprintf(STDERR, " malloc_max = %lu\n",
+ (unsigned long) png_get_chunk_malloc_max(dummy_ptr));
+ png_destroy_read_struct(&dummy_ptr, NULL, NULL);
+
return (int)(ierror != 0);
}
#else
@@ -1989,11 +2076,11 @@ int
main(void)
{
fprintf(STDERR,
- " test ignored because libpng was not built with read support\n");
+ " test ignored because libpng was not built with read support\n");
/* And skip this test */
return PNG_LIBPNG_VER < 10600 ? 0 : 77;
}
#endif
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_7_0beta35 Your_png_h_is_not_version_1_7_0beta35;
+typedef png_libpng_version_1_7_0beta90 Your_png_h_is_not_version_1_7_0beta90;
diff --git a/ext/libpng17/pngtrans.c b/ext/libpng17/pngtrans.c
index 22499e49c2..3b62749832 100644
--- a/ext/libpng17/pngtrans.c
+++ b/ext/libpng17/pngtrans.c
@@ -1,8 +1,8 @@
/* pngtrans.c - transforms the data in a row (used by both readers and writers)
*
- * Last changed in libpng 1.6.11 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -10,88 +10,971 @@
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
-
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngtrans
-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#ifdef _XOPEN_SOURCE
+# include
+#endif /* for swab */
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-/* Turn on BGR-to-RGB mapping */
-void PNGAPI
-png_set_bgr(png_structrp png_ptr)
+/* Memory format enquiries */
+#ifdef PNG_GAMMA_SUPPORTED
+static png_fixed_point
+memory_gamma(png_const_structrp png_ptr)
{
- png_debug(1, "in png_set_bgr");
+# ifdef PNG_READ_GAMMA_SUPPORTED
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ if (png_ptr->read_struct)
+ return png_ptr->row_gamma;
+# endif /* TRANSFORM_MECH */
+# endif /* READ_GAMMA */
- if (png_ptr == NULL)
- return;
+ /* Else either no READ_GAMMA support or this is a write struct; in both
+ * cases there are no gamma transforms. In the write case the set of the
+ * gamma in the info may not have been copied to the png_struct.
+ */
+# if defined(PNG_GAMMA_SUPPORTED) && defined(PNG_READ_SUPPORTED)
+ if ((png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
+ PNG_COLORSPACE_HAVE_GAMMA)
+ return png_ptr->colorspace.gamma;
+# else /* !(GAMMA && READ) */
+ PNG_UNUSED(png_ptr)
+# endif /* !(GAMMA && READ) */
- png_ptr->transformations |= PNG_BGR;
+ /* '0' means the value is not know: */
+ return 0;
}
-#endif
+#endif /* GAMMA */
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-/* Turn on 16 bit byte swapping */
-void PNGAPI
-png_set_swap(png_structrp png_ptr)
+unsigned int PNGAPI
+png_memory_format(png_structrp png_ptr)
{
- png_debug(1, "in png_set_swap");
-
- if (png_ptr == NULL)
- return;
-
- if (png_ptr->bit_depth == 16)
- png_ptr->transformations |= PNG_SWAP_BYTES;
-}
-#endif
-
-#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
-/* Turn on pixel packing */
-void PNGAPI
-png_set_packing(png_structrp png_ptr)
-{
- png_debug(1, "in png_set_packing");
-
- if (png_ptr == NULL)
- return;
-
- if (png_ptr->bit_depth < 8)
+ /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the
+ * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the
+ * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR:
+ *
+ * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call
+ * png_memory_gamma to find the correct value.
+ *
+ * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call
+ * png_memory_channel_depth to find the correct value.
+ *
+ * It is only valid to call these APIS *after* either png_read_update_info
+ * or png_start_read_image on read or after the first row of an image has
+ * been written on write.
+ */
+ if (png_ptr != NULL)
{
- png_ptr->transformations |= PNG_PACK;
-# ifdef PNG_WRITE_SUPPORTED
- png_ptr->usr_bit_depth = 8;
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ unsigned int format = png_ptr->row_format;
+# else /* !TRANSFORM_MECH */
+ unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type);
+# endif /* !TRANSFORM_MECH */
+
+ if (png_ptr->read_struct) /* else no way to find the gamma! */
+ {
+# ifdef PNG_GAMMA_SUPPORTED
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ unsigned int bit_depth = png_ptr->row_bit_depth;
+# else /* !TRANSFORM_MECH */
+ unsigned int bit_depth = png_ptr->bit_depth;
+# endif /* !TRANSFORM_MECH */
+
+ /* Now work out whether this is a valid simplified API format. */
+ switch (bit_depth)
+ {
+ case 8U:
+ {
+ png_fixed_point gamma = memory_gamma(png_ptr);
+
+ if (!PNG_GAMMA_IS_sRGB(gamma))
+ format |= PNG_FORMAT_FLAG_INVALID;
+ }
+ break;
+
+ case 16:
+ if (memory_gamma(png_ptr) == PNG_GAMMA_LINEAR)
+ {
+ static const union
+ {
+ png_uint_16 u16;
+ png_byte u8[2];
+ } sex = { 1U };
+
+ format |= PNG_FORMAT_FLAG_LINEAR;
+
+ /* But the memory layout of the 16-bit quantities must also
+ * match; we need swapped data on LSB platforms.
+ */
+ if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0))
+ break; /* ok */
+ }
+
+ /* FALL THROUGH*/
+ default: /* bit depth not supported for simplified API */
+ format |= PNG_FORMAT_FLAG_INVALID;
+ break;
+ }
+# else /* !GAMMA */
+ /* We have no way of knowing if the gamma value matches that
+ * expected by the simplified API so mark the format as invalid:
+ */
+ format |= PNG_FORMAT_FLAG_INVALID;
+# endif
+ } /* read_struct */
+
+ return format;
+ }
+
+ return 0;
+}
+
+unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr)
+{
+ /* The actual depth of each channel in the image. */
+ if (png_ptr != NULL)
+ {
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ return png_ptr->row_bit_depth;
+# else
+ return png_ptr->bit_depth;
# endif
}
-}
-#endif
-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-/* Turn on packed pixel swapping */
-void PNGAPI
-png_set_packswap(png_structrp png_ptr)
+ return 0;
+}
+
+#ifdef PNG_GAMMA_SUPPORTED
+png_fixed_point PNGAPI
+png_memory_gamma(png_structrp png_ptr)
{
- png_debug(1, "in png_set_packswap");
-
- if (png_ptr == NULL)
- return;
-
- if (png_ptr->bit_depth < 8)
- png_ptr->transformations |= PNG_PACKSWAP;
+ /* The actual gamma of the image data, scaled by 100,000. This is the
+ * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will
+ * return 0.
+ *
+ * On write this invariably returns 0; libpng does not change the gamma of
+ * the data on write.
+ *
+ * Note that this is not always the exact inverse of the 'screen gamma'
+ * passed to png_set_gamma; internal optimizations remove attempts to make
+ * small changes to the gamma value. This function returns the actual
+ * output value.
+ */
+ return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0;
}
-#endif
+#endif /* GAMMA */
-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
-void PNGAPI
-png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
+/* These are general purpose APIs that deal with the row buffer format in both
+ * the read and write case. The png_struct::row_* members describe the
+ * in-memory format of the image data based on the transformations requested by
+ * the application.
+ */
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+png_voidp /* PRIVATE */
+png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line,
+ png_transformp tr, size_t size)
{
- png_debug(1, "in png_set_shift");
+ /* Given a pointer to a transform, 'tr' validate that the underlying derived
+ * class has size 'size' using the tr->size field and return the same
+ * pointer. If there is a size mismatch the function does an affirm using
+ * the given line number.
+ */
+ if (tr->size != size)
+ png_affirm(png_ptr, param_deb("transform upcast") src_line);
- if (png_ptr == NULL)
- return;
+ return tr;
+}
- png_ptr->transformations |= PNG_SHIFT;
- png_ptr->shift = *true_bits;
+void /* PRIAVE */
+png_transform_free(png_const_structrp png_ptr, png_transformp *list)
+{
+ if (*list != NULL)
+ {
+ png_transform_free(png_ptr, &(*list)-> next);
+ if ((*list)->free != NULL)
+ (*list)->free(png_ptr, *list);
+ png_free(png_ptr, *list);
+ *list = NULL;
+ }
+}
+
+/* Utility to initialize a png_transform_control for read or write. */
+void /* PRIVATE */
+png_init_transform_control(png_transform_controlp tc, png_structp png_ptr)
+{
+ png_byte bd; /* bit depth of the row */
+ png_byte cd; /* bit depth of color information */
+
+ memset(tc, 0, sizeof *tc);
+ tc->png_ptr = png_ptr; /* ALIAS */
+ tc->sp = tc->dp = NULL;
+ tc->width = 0;
+
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ /* The file gamma is set by png_set_gamma, as well as being read from the
+ * input PNG gAMA chunk, if present, we don't have a png_info so can't get
+ * the set_gAMA value but this doesn't matter because on read the gamma
+ * value is in png_struct::colorspace and on write it isn't used.
+ */
+ if ((png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
+ PNG_COLORSPACE_HAVE_GAMMA)
+ {
+ tc->gamma = png_ptr->colorspace.gamma;
+ debug(tc->gamma > 0);
+ }
+
+ else
+ {
+ /* There is no input gamma, so there should be no overall gamma
+ * correction going on. This test works because the various things
+ * that set an output gamma also default the input gamma.
+ */
+ debug(png_ptr->row_gamma == 0);
+ }
+# endif
+
+ /* Validate bit depth and color type here */
+ cd = bd = png_ptr->bit_depth;
+
+ switch (png_ptr->color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U || bd == 16U);
+ tc->format = 0U;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U);
+ tc->format = PNG_FORMAT_FLAG_COLORMAP | PNG_FORMAT_FLAG_COLOR;
+ cd = 8U;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ affirm(bd == 8U || bd == 16U);
+ tc->format = PNG_FORMAT_FLAG_ALPHA;
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ affirm(bd == 8U || bd == 16U);
+ tc->format = PNG_FORMAT_FLAG_COLOR;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ affirm(bd == 8U || bd == 16U);
+ tc->format = PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA;
+ break;
+
+ default:
+ impossible("PNG color type");
+ }
+
+ tc->bit_depth = bd;
+ tc->range = 0;
+
+ /* Preset the sBIT data to full precision/handled. */
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = cd;
+# ifdef PNG_READ_sBIT_SUPPORTED
+ {
+ int handled = 1;
+
+ if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+ {
+ png_byte c = png_ptr->sig_bit.red;
+ if (c > 0 && c < cd)
+ {
+ tc->sBIT_R = c;
+ handled = 0;
+ }
+
+ c = png_ptr->sig_bit.green;
+ if (c > 0 && c < cd)
+ {
+ tc->sBIT_G = c;
+ handled = 0;
+ }
+
+ c = png_ptr->sig_bit.blue;
+ if (c > 0 && c < cd)
+ {
+ tc->sBIT_B = c;
+ handled = 0;
+ }
+ }
+
+ else /* grayscale */
+ {
+ png_byte c = png_ptr->sig_bit.gray;
+ if (c > 0 && c < cd)
+ {
+ tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = c;
+ handled = 0;
+ }
+ }
+
+ /* The palette-mapped format doesn't store alpha information, an
+ * omission in the spec that is difficult to fix. Notice that
+ * 'handled' is not cleared below, this is because the alpha channel is
+ * always linear, so the sBIT_A value can always be treated as a
+ * precision value.
+ */
+ if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+ {
+ png_byte c = png_ptr->sig_bit.alpha;
+ if (c > 0 && c < cd)
+ tc->sBIT_A = c;
+ }
+
+ /* If 'handled' did not get cleared there is no sBIT information. */
+ if (handled)
+ tc->invalid_info = PNG_INFO_sBIT;
+ }
+# else /* !READ_sBIT */
+ /* No sBIT information */
+ tc->invalid_info = PNG_INFO_sBIT;
+# endif /* !READ_sBIT */
+}
+
+png_transformp /*PRIVATE*/
+png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
+ unsigned int order)
+{
+ /* Add a transform. This is a minimal implementation; the order is just
+ * controlled by 'order', the result is a point to the new transform, or
+ * to an existing one if one was already in the list.
+ */
+ png_transformp *p = &png_ptr->transform_list;
+
+ while (*p != NULL && (*p)->order < order)
+ p = &(*p)->next;
+
+ if (size == 0)
+ size = sizeof (png_transform);
+
+ else
+ affirm(size >= sizeof (png_transform));
+
+ if (*p == NULL || (*p)->order > order)
+ {
+ png_transformp t;
+
+ t = png_voidcast(png_transformp, png_malloc(png_ptr, size));
+ memset(t, 0, size); /* zeros out the extra data too */
+ /* *p comes after the new entry, t: */
+ t->next = *p;
+ t->fn = fn;
+ t->free = NULL;
+ t->order = order;
+ t->size = 0xFFFFU & size;
+ *p = t;
+ return t;
+ }
+
+ else /* (*p)->order matches order, return *p */
+ {
+ affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size);
+ return *p;
+ }
+}
+
+png_transformp /* PRIVATE */
+png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
+ png_transformp *transform, png_transform_controlp tc)
+{
+ png_transformp tr = *transform;
+ unsigned int order = tr->order;
+
+ /* Basic loop detection: */
+ affirm(fn != NULL && tr->fn != fn);
+
+ /* Move the following transforms up: */
+ {
+ unsigned int old_order = order;
+
+ do
+ {
+ tr->order = ++old_order;
+ tr = tr->next;
+ }
+ while (tr != NULL && tr->order == old_order);
+
+ affirm(tr == NULL || tr->order > old_order);
+ }
+
+ *transform = png_add_transform(png_ptr, size, fn, order);
+
+ if (tc != NULL)
+ fn(transform, tc);
+
+ return *transform;
+}
+
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+static png_transformp
+png_find_transform(png_const_structrp png_ptr, unsigned int order)
+ /* Find a transform with the given order, or return NULL. Currently only
+ * used here.
+ */
+{
+ png_transformp p = png_ptr->transform_list;
+
+ for (;;)
+ {
+ if (p == NULL || p->order > order)
+ return NULL;
+
+ if (p->order == order)
+ return p;
+
+ p = p->next;
+ }
+}
+#endif /* USER_TRANSFORM_PTR */
+
+static void
+remove_transform(png_const_structp png_ptr, png_transformp *transform)
+ /* Remove a transform on a running list */
+{
+ png_transformp tp = *transform;
+ png_transformp next = tp->next;
+
+ *transform = next;
+ tp->next = NULL;
+ png_transform_free(png_ptr, &tp);
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+void /* PRIVATE */
+png_remove_transform(png_const_structp png_ptr, png_transformp *transform)
+{
+ remove_transform(png_ptr, transform);
+}
+#endif /* READ_TRANSFORMS */
+
+static unsigned int
+run_transform_list_forwards(png_transform_controlp tc, png_transformp *start,
+ png_transformp end/*NULL for whole list*/)
+ /* Called from the init code and below, the caller must initialize 'tc' */
+{
+ png_const_structp png_ptr = tc->png_ptr;
+ unsigned int max_depth = PNG_TC_PIXEL_DEPTH(*tc);
+
+ /* Caller guarantees that *start is non-NULL */
+ debug(*start != NULL);
+
+ do
+ {
+ if ((*start)->fn != NULL)
+ (*start)->fn(start, tc);
+
+ if ((*start)->fn == NULL) /* delete this transform */
+ remove_transform(png_ptr, start);
+
+ else
+ {
+ /* Handle the initialization of the maximum pixel depth. */
+ unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc);
+
+ if (tc_depth > max_depth)
+ max_depth = tc_depth;
+
+ /* Advance to the next transform. */
+ start = &(*start)->next;
+ }
+ }
+ while (*start != NULL && *start != end);
+
+ /* This only goes wrong if 'end' was non-NULL and not in the list: */
+ debug(*start == end);
+
+ return max_depth;
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+unsigned int /* PRIVATE */
+png_run_this_transform_list_forwards(png_transform_controlp tc,
+ png_transformp *start, png_transformp end)
+{
+ return run_transform_list_forwards(tc, start, end);
+}
+#endif /* READ_TRANSFORMS */
+
+#ifdef PNG_READ_SUPPORTED
+unsigned int /* PRIVATE */
+png_run_transform_list_forwards(png_structp png_ptr, png_transform_controlp tc)
+{
+ if (png_ptr->transform_list != NULL)
+ return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
+
+ else
+ return PNG_PIXEL_DEPTH(*png_ptr);
+}
+#endif /* READ */
+
+#ifdef PNG_WRITE_SUPPORTED /* only used from pngwrite.c */
+static unsigned int
+run_transform_list_backwards(png_transform_controlp tc, png_transformp *list)
+{
+ png_const_structp png_ptr = tc->png_ptr;
+ unsigned int max_depth = 0;
+
+ if ((*list)->next != NULL)
+ max_depth = run_transform_list_backwards(tc, &(*list)->next);
+
+ /* Note that the above might change (*list)->next, but it can't change
+ * *list itself.
+ */
+ if ((*list)->fn != NULL)
+ (*list)->fn(list, tc);
+
+ /* If that set 'fn' to NULL this transform must be removed; this is how
+ * (*list)->next gets changed in our caller:
+ */
+ if ((*list)->fn == NULL)
+ remove_transform(png_ptr, list);
+
+ else
+ {
+ unsigned int depth = PNG_TC_PIXEL_DEPTH(*tc);
+
+ if (depth > max_depth)
+ max_depth = depth;
+ }
+
+ return max_depth;
+}
+
+void /* PRIVATE */
+png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc)
+{
+ if (png_ptr->transform_list != NULL)
+ {
+ /* This doesn't take account of the base PNG depth, but that shouldn't
+ * matter, it's just a check:
+ */
+ unsigned int max_depth =
+ run_transform_list_backwards(tc, &png_ptr->transform_list);
+
+ /* Better late than never (if this fires a memory overwrite has happened):
+ */
+ affirm(max_depth <= png_ptr->row_max_pixel_depth);
+ }
+}
+#endif /* WRITE */
+
+static unsigned int
+init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start)
+ /* Called each time to run the transform list once during initialization. */
+{
+ png_init_transform_control(tc, png_ptr);
+ tc->init = start ? PNG_TC_INIT_FORMAT : PNG_TC_INIT_FINAL;
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ if (png_ptr->read_struct)
+ return png_read_init_transform_mech(png_ptr, tc);
+ else
+# endif
+ return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
+}
+#endif /* TRANSFORM_MECH */
+
+#ifdef PNG_PALETTE_MAX_SUPPORTED
+static int
+set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max,
+ unsigned int format_max)
+ /* Called whenever a new maximum pixel value is found */
+{
+ /* One of these must be true: */
+# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+ if (max >= (tr->args & 0x1FFU) && !png_ptr->palette_index_check_issued)
+ {
+ /* In 1.7 only issue the error/warning by default; the 'check' API is
+ * used to enable/disable the check. Assume that if the app enabled it
+ * then the app will be checking the result with get_palette_max in
+ * read. In write an error results unless the check is disabled.
+ */
+ if (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
+# ifdef PNG_WRITE_SUPPORTED
+ || (!png_ptr->read_struct &&
+ png_ptr->palette_index_check != PNG_PALETTE_CHECK_OFF)
+# endif /* WRITE */
+ )
+ {
+# ifdef PNG_READ_SUPPORTED
+# ifdef PNG_WRITE_SUPPORTED
+ if (png_ptr->read_struct)
+# endif /* WRITE */
+ png_chunk_benign_error(png_ptr, "palette index too large");
+# ifdef PNG_WRITE_SUPPORTED
+ else
+# endif
+# endif /* READ */
+# ifdef PNG_WRITE_SUPPORTED
+ png_error(png_ptr, "palette index too large");
+# endif /* WRITE */
+ }
+
+ png_ptr->palette_index_check_issued = 1;
+ }
+# endif /* CHECK_FOR_INVALID_INDEX */
+# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+ png_ptr->palette_index_max = png_check_byte(png_ptr, max);
+# endif
+
+ if (max == format_max)
+ {
+ tr->fn = NULL; /* no point continuing once the max has been seen */
+ return 1; /* stop */
+ }
+
+ return 0; /* keep going */
+}
+
+static void
+palette_max_1bpp(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_uint_32 width = tc->width;
+
+ while (width >= 8)
+ {
+ if (*sp++) break;
+ width -= 8;
+ }
+
+ if (width < 8)
+ {
+ if (width == 0 ||
+ (*sp & (((1U<png_ptr, *tr, 1U, 1U);
+}
+
+static void
+palette_max_2bpp(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_uint_32 width = tc->width;
+ const png_uint_32 args = (*tr)->args;
+ unsigned int max = args >> 24; /* saved maximum */
+
+ while (width > 0)
+ {
+ png_uint_32 input = 0U, test;
+ unsigned int new_max;
+
+ /* This just skips 0 bytes: */
+ while (width > 0)
+ {
+ unsigned int next = *sp++;
+
+ /* There may be partial pixels at the end, just remove the absent
+ * pixels with a right shift:
+ */
+ if (width >= 4)
+ width -= 4;
+ else
+ next >>= (4U-width) * 2U, width = 0;
+
+ if (next)
+ {
+ input = (input << 8) | next;
+ if ((input & 0xFF000000U) != 0)
+ break;
+ }
+ }
+
+ test = input & 0xAAAAAAAAU;
+
+ if (test != 0)
+ {
+ if ((input & (test >> 1)) != 0)
+ new_max = 3U; /* both bits set in at least one pixel */
+
+ else if (max < 2U)
+ new_max = 2U;
+
+ else
+ continue; /* no change to max */
+ }
+
+ else /* test is 0 */ if (input != 0 && max == 0)
+ new_max = 1U;
+
+ else /* input is 0, or max is at least 1 */
+ continue;
+
+ /* new_max is greater than max: */
+ if (set_palette_max(tc->png_ptr, *tr, new_max, 3U))
+ return;
+
+ /* Record new_max: */
+ max = new_max;
+ }
+
+ /* End of input, check the next line. */
+ (*tr)->args = (max << 24) + (args & 0xFFFFFFU);
+}
+
+static void
+palette_max_4bpp(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_uint_32 width = tc->width;
+ const png_uint_32 args = (*tr)->args;
+ unsigned int max = args >> 24; /* saved maximum */
+
+ while (width > 0)
+ {
+ unsigned int input = *sp++;
+
+ if (width >= 2)
+ width -= 2;
+ else
+ input >>= 1, width = 0;
+
+ if ((input & 0xFU) > max)
+ max = input & 0xFU;
+
+ if (((input >> 4) & 0xFU) > max)
+ max = (input >> 4) & 0xFU;
+ }
+
+ if (max > (args >> 24))
+ {
+ if (set_palette_max(tc->png_ptr, *tr, max, 15U))
+ return;
+
+ (*tr)->args = (max << 24) + (args & 0xFFFFFFU);
+ }
+}
+
+static void
+palette_max_8bpp(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_uint_32 width = tc->width;
+ const png_uint_32 args = (*tr)->args;
+ unsigned int max = args >> 24; /* saved maximum */
+
+ while (width > 0)
+ {
+ unsigned int input = *sp++;
+
+ if (input > max)
+ max = input;
+
+ --width;
+ }
+
+ if (max > (args >> 24))
+ {
+ if (set_palette_max(tc->png_ptr, *tr, max, 255U))
+ return;
+
+ (*tr)->args = (max << 24) + (args & 0xFFFFFFU);
+ }
+}
+
+static void
+palette_max_init(png_transformp *tr, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ affirm((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0);
+ debug(tc->init);
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ /* Record the palette depth to check here along with the running total
+ * in the top 8 bits (initially 0, which is always valid).
+ */
+ (*tr)->args = png_ptr->num_palette;
+
+ switch (tc->bit_depth)
+ {
+ case 1: (*tr)->fn = palette_max_1bpp; break;
+ case 2: (*tr)->fn = palette_max_2bpp; break;
+ case 4: (*tr)->fn = palette_max_4bpp; break;
+ case 8: (*tr)->fn = palette_max_8bpp; break;
+ default:impossible("palette bit depth");
+ }
+
+ png_ptr->palette_index_have_max = 1U;
+ }
+# undef png_ptr
+}
+#endif /* PALETTE_MAX */
+
+#ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+int PNGAPI
+png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+ if (png_ptr != NULL && png_ptr->palette_index_have_max)
+ return png_ptr->palette_index_max;
+
+ /* This indicates to the caller that the information is not available: */
+ return -1;
+ PNG_UNUSED(info_ptr)
+}
+#endif /* GET_PALETTE_MAX */
+
+void /* PRIVATE */
+png_init_row_info(png_structrp png_ptr)
+{
+ /* PNG pixels never exceed 64 bits in depth: */
+ const png_byte png_depth =
+ png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U);
+
+# ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ /* The palette index check stuff is *on* automatically. To handle this
+ * add it here, if it is supported.
+ */
+# ifdef PNG_PALETTE_MAX_SUPPORTED
+ /* The logic here is a little complex because of the plethora of
+ * #defines controlling this stuff.
+ */
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && (
+# if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\
+ defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED)
+ (png_ptr->read_struct
+# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+ && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON ||
+ (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
+ && png_ptr->num_palette < (1U << png_ptr->bit_depth)))
+# endif /* READ_CHECK_FOR_INVALID_INDEX */
+ )
+# else /* no READ support */
+ 0
+# endif /* READ checks */
+ ||
+# if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\
+ defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
+ (!png_ptr->read_struct
+# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+ && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON ||
+ (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
+ && png_ptr->num_palette < (1U << png_ptr->bit_depth)))
+# endif /* WRITE_CHECK_FOR_INVALID_INDEX */
+ )
+# else /* no WRITE support */
+ 0
+# endif /* WRITE checks */
+ ))
+ png_add_transform(png_ptr, 0/*size*/, palette_max_init,
+ PNG_TR_CHECK_PALETTE);
+# endif
+
+ /* Application transforms may change the format of the data or, when
+ * producing interlaced images, the number of pixels in a line. This code
+ * determines the maximum pixel depth required and allows transformations
+ * a chance to initialize themselves.
+ */
+ if (png_ptr->transform_list != NULL)
+ {
+ png_transform_control tc;
+
+ (void)init_transform_mech(png_ptr, &tc, 1/*start*/);
+
+ png_ptr->row_format = png_check_bits(png_ptr, tc.format, PNG_RF_BITS);
+ affirm(tc.bit_depth <= 32);
+ png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6);
+ png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3);
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ png_ptr->row_gamma = tc.gamma;
+# endif /* READ_GAMMA */
+
+ /* The above may have cancelled all the transforms in the list. */
+ if (png_ptr->transform_list != NULL)
+ {
+ /* Run the transform list again, also forward, and accumulate the
+ * maximum pixel depth. At this point the transforms can swap
+ * out their initialization code.
+ */
+ unsigned int max_depth =
+ init_transform_mech(png_ptr, &tc, 0/*final*/);
+
+ /* init_transform_mech is expected to take the input depth into
+ * account:
+ */
+ debug(max_depth >= png_depth);
+ if (max_depth < png_depth)
+ max_depth = png_depth;
+ affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U));
+
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ /* Set this now because it only gets resolved finally at this
+ * point.
+ */
+ png_ptr->invalid_info = tc.invalid_info;
+# endif /* READ_TRANSFORMS */
+
+ /* And check the transform fields: */
+ affirm(png_ptr->row_format == tc.format &&
+ png_ptr->row_range == tc.range &&
+ png_ptr->row_bit_depth == tc.bit_depth);
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ affirm(png_ptr->row_gamma == tc.gamma);
+# endif /* READ_GAMMA */
+
+ png_ptr->row_max_pixel_depth =
+ png_check_bits(png_ptr, max_depth, 8U);
+
+ /* On 'read' input_depth is the PNG pixel depth and output_depth is
+ * the depth of the pixels passed to the application, but on 'write'
+ * the transform list is reversed so output_depth is the PNG depth
+ * and input_depth the application depth.
+ */
+ {
+ const png_byte app_depth =
+ png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U);
+
+ affirm(app_depth <= max_depth);
+
+ if (png_ptr->read_struct)
+ {
+ png_ptr->row_input_pixel_depth = png_depth;
+ png_ptr->row_output_pixel_depth = app_depth;
+ }
+
+ else
+ {
+ png_ptr->row_input_pixel_depth = app_depth;
+ png_ptr->row_output_pixel_depth = png_depth;
+ }
+
+ return; /* to skip the default settings below */
+ }
+ }
+ }
+
+ else /* png_ptr->transform_list == NULL */
+ {
+ png_ptr->row_format = png_check_bits(png_ptr,
+ PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type), PNG_RF_BITS);
+ png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth,
+ 6);
+ png_ptr->row_range = 0;
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ if ((png_ptr->colorspace.flags &
+ (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
+ PNG_COLORSPACE_HAVE_GAMMA)
+ png_ptr->row_gamma = png_ptr->colorspace.gamma;
+# endif /* READ_GAMMA */
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ png_ptr->invalid_info = 0U;
+# endif /* READ_TRANSFORMS */
+ }
+# endif /* TRANSFORM_MECH */
+
+ /* We get here if there are no transforms therefore no change to the pixel
+ * bit depths.
+ */
+ png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth =
+ png_ptr->row_input_pixel_depth = png_depth;
}
-#endif
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
defined(PNG_WRITE_INTERLACING_SUPPORTED)
@@ -100,732 +983,2511 @@ png_set_interlace_handling(png_structrp png_ptr)
{
png_debug(1, "in png_set_interlace handling");
- if (png_ptr && png_ptr->interlaced)
+ if (png_ptr != 0)
{
- png_ptr->transformations |= PNG_INTERLACE;
- return (7);
+ if (png_ptr->read_struct)
+ {
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->interlaced)
+ {
+ png_ptr->do_interlace = 1;
+ return PNG_INTERLACE_ADAM7_PASSES;
+ }
+
+ return 1;
+# else /* !READ_INTERLACING */
+ png_app_error(png_ptr, "no de-interlace support");
+ /* return 0 below */
+# endif /* !READ_INTERLACING */
+ }
+
+ else /* write */
+ {
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ if (png_ptr->interlaced)
+ {
+ png_ptr->do_interlace = 1;
+ return PNG_INTERLACE_ADAM7_PASSES;
+ }
+
+ return 1;
+# else /* !WRITE_INTERLACING */
+ png_app_error(png_ptr, "no interlace support");
+ /* return 0 below */
+# endif /* !WRITE_INTERLACING */
+ }
}
- return (1);
+ /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */
+ return 0;
}
-#endif
+#endif /* READ_INTERLACING || WRITE_INTERLACING */
+
+#ifdef PNG_MNG_READ_FEATURES_SUPPORTED
+/* Undoes intrapixel differencing, this is called immediately after the PNG
+ * filter has been undone.
+ */
+static void
+png_do_read_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
+ * are overwriting sp[]
+ */
+ do
+ {
+ *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
+ *dp++ = *++sp; /* green */
+ *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
+ sp += 2;
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_read_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ do
+ {
+ *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
+ *dp++ = *++sp; /* green */
+ *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
+ sp += 2;
+ *dp++ = *sp++; /* alpha */
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_read_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ /* The input consists of 16-bit values and, by examination of the code
+ * (please, someone, check; I didn't read the spec) the differencing is done
+ * against the 16-bit green value.
+ */
+ do
+ {
+ unsigned int red = png_get_uint_16(sp + 0);
+ unsigned int green = png_get_uint_16(sp + 2);
+ unsigned int blue = png_get_uint_16(sp + 4);
+ sp += 6;
+
+ red += green;
+ blue += green;
+
+ *dp++ = PNG_BYTE(red >> 8);
+ *dp++ = PNG_BYTE(red);
+ *dp++ = PNG_BYTE(green >> 8);
+ *dp++ = PNG_BYTE(green);
+ *dp++ = PNG_BYTE(blue >> 8);
+ *dp++ = PNG_BYTE(blue);
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_read_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ /* As above but copy the alpha over too. */
+ do
+ {
+ unsigned int red = png_get_uint_16(sp + 0);
+ unsigned int green = png_get_uint_16(sp + 2);
+ unsigned int blue = png_get_uint_16(sp + 4);
+ sp += 6;
+
+ red += green;
+ blue += green;
+
+ *dp++ = PNG_BYTE(red >> 8);
+ *dp++ = PNG_BYTE(red);
+ *dp++ = PNG_BYTE(green >> 8);
+ *dp++ = PNG_BYTE(green);
+ *dp++ = PNG_BYTE(blue >> 8);
+ *dp++ = PNG_BYTE(blue);
+ *dp++ = *sp++;
+ *dp++ = *sp++; /* alpha */
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_init_read_intrapixel(png_transformp *tr, png_transform_controlp tc)
+{
+ /* Double check the permitted MNG features in case the app turned the feature
+ * on then off again. Also make sure the color type is acceptable; it must
+ * be RGB or RGBA.
+ */
+ png_const_structp png_ptr = tc->png_ptr;
+
+ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+ (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
+ (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
+ PNG_FORMAT_FLAG_COLOR)
+ {
+ if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
+ {
+ case 24: (*tr)->fn = png_do_read_intrapixel_RGB8; break;
+ case 32: (*tr)->fn = png_do_read_intrapixel_RGBA8; break;
+ case 48: (*tr)->fn = png_do_read_intrapixel_RGB16; break;
+ case 64: (*tr)->fn = png_do_read_intrapixel_RGBA16; break;
+ default: impossible("bit depth");
+ }
+ }
+
+ else
+ (*tr)->fn = NULL;
+}
+#endif /* MNG_READ_FEATURES_SUPPORTED */
+
+#ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
+/* This is just the forward direction of the above:
+ *
+ * red := red - green
+ * blue:= blue- green
+ *
+ * Alpha is not changed.
+ */
+static void
+png_do_write_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
+ * are overwriting sp[]
+ */
+ do
+ {
+ *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
+ *dp++ = *++sp; /* green */
+ *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
+ sp += 2;
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_write_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ do
+ {
+ *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
+ *dp++ = *++sp; /* green */
+ *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
+ sp += 2;
+ *dp++ = *sp++; /* alpha */
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_write_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ do
+ {
+ unsigned int red = png_get_uint_16(sp + 0);
+ unsigned int green = png_get_uint_16(sp + 2);
+ unsigned int blue = png_get_uint_16(sp + 4);
+ sp += 6;
+
+ red -= green;
+ blue -= green;
+
+ *dp++ = PNG_BYTE(red >> 8);
+ *dp++ = PNG_BYTE(red);
+ *dp++ = PNG_BYTE(green >> 8);
+ *dp++ = PNG_BYTE(green);
+ *dp++ = PNG_BYTE(blue >> 8);
+ *dp++ = PNG_BYTE(blue);
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_do_write_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
+{
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_uint_32 width = tc->width;
+
+ tc->sp = dp;
+
+ /* As above but copy the alpha over too. */
+ do
+ {
+ unsigned int red = png_get_uint_16(sp + 0);
+ unsigned int green = png_get_uint_16(sp + 2);
+ unsigned int blue = png_get_uint_16(sp + 4);
+ sp += 6;
+
+ red -= green;
+ blue -= green;
+
+ *dp++ = PNG_BYTE(red >> 8);
+ *dp++ = PNG_BYTE(red);
+ *dp++ = PNG_BYTE(green >> 8);
+ *dp++ = PNG_BYTE(green);
+ *dp++ = PNG_BYTE(blue >> 8);
+ *dp++ = PNG_BYTE(blue);
+ *dp++ = *sp++;
+ *dp++ = *sp++; /* alpha */
+ }
+ while (--width > 0);
+
+# define png_ptr (tc->png_ptr)
+ UNTESTED
+# undef png_ptr
+ PNG_UNUSED(tr)
+}
+
+static void
+png_init_write_intrapixel(png_transformp *tr, png_transform_controlp tc)
+{
+ /* Write filter_method 64 (intrapixel differencing) only if:
+ *
+ * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED, and;
+ * 2. Libpng did not write a PNG signature (this filter_method is only
+ * used in PNG datastreams that are embedded in MNG datastreams),
+ * and;
+ * 3. The application called png_permit_mng_features with a mask that
+ * included PNG_FLAG_MNG_FILTER_64, and;
+ * 4. The filter_method is 64, and;
+ * 5. The color_type is RGB or RGBA
+ */
+ png_const_structp png_ptr = tc->png_ptr;
+
+ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+ (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
+ (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
+ PNG_FORMAT_FLAG_COLOR)
+ {
+ if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
+ {
+ case 24: (*tr)->fn = png_do_write_intrapixel_RGB8; break;
+ case 32: (*tr)->fn = png_do_write_intrapixel_RGBA8; break;
+ case 48: (*tr)->fn = png_do_write_intrapixel_RGB16; break;
+ case 64: (*tr)->fn = png_do_write_intrapixel_RGBA16; break;
+ default: impossible("bit depth");
+ }
+ }
+
+ else
+ (*tr)->fn = NULL;
+}
+#endif /* MNG_WRITE_FEATURES */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+png_uint_32 PNGAPI
+png_permit_mng_features(png_structrp png_ptr, png_uint_32 mng_features)
+{
+ if (png_ptr != NULL)
+ {
+# ifdef PNG_MNG_READ_FEATURES_SUPPORTED
+ if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
+ png_add_transform(png_ptr, 0/*size*/, png_init_read_intrapixel,
+ PNG_TR_MNG_INTRAPIXEL);
+# else /* !MNG_READ_FEATURES */
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "MNG not supported on read");
+ return;
+ }
+# endif /* !MNG_READ_FEATURES */
+
+# ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
+ if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
+ png_add_transform(png_ptr, 0/*size*/, png_init_write_intrapixel,
+ PNG_TR_MNG_INTRAPIXEL);
+# else /* !MNG_WRITE_FEATURES */
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "MNG not supported on write");
+ return;
+ }
+# endif /* !MNG_WRITE_FEATURES */
+
+ return png_ptr->mng_features_permitted =
+ mng_features & PNG_ALL_MNG_FEATURES;
+ }
+
+ return 0;
+}
+#endif /* MNG_FEATURES */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) ||\
+ defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) ||\
+ defined(PNG_READ_SWAP_ALPHA_SUPPORTED) ||\
+ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) ||\
+ defined(PNG_READ_FILLER_SUPPORTED) ||\
+ defined(PNG_WRITE_FILLER_SUPPORTED) ||\
+ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) ||\
+ defined(PNG_READ_STRIP_16_TO_8_SUPPORTED) ||\
+ defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) ||\
+ defined(PNG_READ_EXPAND_16_SUPPORTED) ||\
+ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* This is a generic transform which manipulates the bytes in an input row. The
+ * manipulations supported are:
+ *
+ * Channel addition (alpha or filler)
+ * Channel removal (alpha or filler)
+ * Channel swaps - RGB to BGR, alpha/filler from last to first and vice versa
+ *
+ * The output is described in blocks of output pixel size 4-bit codes encoded
+ * as follows:
+ *
+ * 0 Advance the source pointer by the source pixel size, start the
+ * code list again. This code doesn't actually exist; it is simply
+ * the result of emptying the code list.
+ * 1..3 An error (ignored; treated like 0)
+ * 4..7 Put filler[code-4] into the output
+ * 8..15 Put source byte[code-8] in the output
+ *
+ * The codes are held in a png_uint_32 parameter. transform->args is used by
+ * the init routine to work out the required codes. The format change is a mask
+ * which is XORed with the tc format. Note that the init routine works out
+ * whether to work from the beginning or end of the row and the codes are always
+ * stored LSB first in the order needed.
+ */
+typedef struct
+{
+ png_transform tr;
+ png_uint_32 codes; /* As above */
+ unsigned int format; /* format after transform */
+ unsigned int bit_depth; /* bit depth after transform */
+ png_byte filler[4]; /* Filler or alpha bytes, LSB first (see below) */
+} png_transform_byte_op;
+
+static void
+png_do_byte_ops_up(png_transformp *transform, png_transform_controlp tc)
+ /* Row width is unchanged or decreasing */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op, *transform);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
+ const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
+ debug(tc->bit_depth == 8 || tc->bit_depth == 16);
+ debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0);
+
+ tc->sp = tc->dp;
+ tc->format = tr->format;
+ tc->bit_depth = tr->bit_depth;
+
+ /* 'output' is a 32-byte buffer that is used to delay writes for 16 bytes,
+ * avoiding overwrite when source and destination buffers are the same.
+ * 'hwm' is either 32 or 16, initially '32', when the byte counter 'i'
+ * reaches 'hwm' the last-but-one 16 bytes are written; the bytes
+ * [hwm..hwm+15] modulo 32. hwm is then swapped to hwm+16 mod 32 and i
+ * continues to advance. i is always below hwm.
+ *
+ * At the end the whole remaining buffer from hwm to i is written.
+ */
+ {
+ const png_uint_32 codes = tr->codes;
+ png_uint_32 code = codes;
+ unsigned int i, hwm; /* buffer index and high-water-mark */
+ png_byte output[32];
+
+ hwm = 32;
+
+ for (i=0;;)
+ {
+ unsigned int next_code = code & 0xf;
+
+ if (next_code >= 8)
+ output[i++] = sp[next_code-8];
+
+ else if (next_code >= 4)
+ output[i++] = tr->filler[next_code - 4];
+
+ else /* end code */
+ {
+ sp += sp_advance;
+
+ if (sp >= ep)
+ break; /* i may be == hwm at this point. */
+
+ code = codes;
+ continue; /* no ouput produced, skip the check */
+ }
+
+ code >>= 4; /* find the next code */
+
+ if (i == hwm)
+ {
+ hwm &= 0x10U; /* 0 or 16 */
+ memcpy(dp, output + hwm, 16);
+ dp += 16;
+ i = hwm; /* reset i if hwm was 32 */
+ /* hwm is only ever 16 or 32: */
+ hwm += 16;
+ }
+ }
+
+ /* Write from hwm to (i-1), the delay means there is always something to
+ * write.
+ */
+ hwm &= 0x10U;
+ if (hwm == 16)
+ {
+ debug(i <= 16);
+ memcpy(dp, output + hwm, 16);
+ dp += 16;
+ }
+
+ if (i > 0)
+ memcpy(dp, output, i);
+
+# ifndef PNG_RELEASE_BUILD
+ dp += i;
+ /* The macro expansion exceeded the limit on ANSI strings, so split it:
+ */
+ dp -= PNG_TC_ROWBYTES(*tc);
+ debug(dp == tc->dp);
+# endif
+ }
+
+ debug(sp == ep);
+# undef png_ptr
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static void
+png_do_byte_ops_down(png_transformp *transform, png_transform_controlp tc)
+ /* Row width is increasing */
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op, *transform);
+ const png_const_bytep ep = png_voidcast(png_const_bytep, tc->sp);
+ const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
+ png_const_bytep sp = ep + PNG_TC_ROWBYTES(*tc);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_alloc_size_t dest_rowbytes;
+
+ debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
+ debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U);
+
+ tc->sp = tc->dp;
+ tc->format = tr->format;
+ tc->bit_depth = tr->bit_depth;
+ dest_rowbytes = PNG_TC_ROWBYTES(*tc);
+ dp += dest_rowbytes;
+
+ /* In this case the 32-byte buffer is written downwards with a writes delayed
+ * by 16 bytes as before. 'hwm' is lower than i; 0 or 16.
+ */
+ {
+ const png_uint_32 codes = tr->codes;
+ png_uint_32 code = codes;
+ unsigned int size, hwm, i;
+ png_byte output[32] = { 0 };
+
+ /* This catches an empty codes array, which would cause all the input to
+ * be skipped and, potentially, a garbage output[] to be written (once) to
+ * *dp.
+ */
+ affirm((codes & 0xFU) >= 4U);
+
+ /* Align the writes to a 16-byte multiple from the start of the
+ * destination buffer:
+ */
+ size = dest_rowbytes & 0xFU;
+ if (size == 0U) size = 16U;
+ i = size+16U;
+ sp -= sp_advance; /* Move 1 pixel back */
+ hwm = 0U;
+
+ for (;;)
+ {
+ unsigned int next_code = code & 0xFU;
+
+ if (next_code >= 8U)
+ output[--i] = sp[next_code-8U];
+
+ else if (next_code >= 4U)
+ output[--i] = tr->filler[next_code - 4U];
+
+ else /* end code */
+ {
+ sp -= sp_advance;
+
+ if (sp < ep)
+ break;
+
+ code = codes;
+ continue; /* no ouput produced, skip the check */
+ }
+
+ code >>= 4; /* find the next code */
+
+ if (i == hwm)
+ {
+ /* A partial copy comes at the beginning to align the copies to a
+ * 16-byte boundary. The bytes to be written are the bytes
+ * i+16..(hwm-1) except that the partial buffer may reduce this.
+ */
+ dp -= size;
+ hwm ^= 0x10U; /* == i+16 mod 32 */
+ memcpy(dp, output + hwm, size);
+ size = 16U;
+ if (i == 0U) i = 32U;
+ }
+ }
+
+ /* The loop above only exits with an exit code, so 'i' has been checked
+ * against 'hwm' before and, because of the alignment, i will always be
+ * either 16 or 32:
+ */
+ debug((i == 16U || i == 32U) & (((i & 0x10U)^0x10U) == hwm));
+ debug(sp+sp_advance == ep);
+
+ /* At the end the bytes i..(hwm-1) need to be written, with the proviso
+ * that 'size' will be less than 16 for short rows. If 'size' is still a
+ * short value then the range to be written is output[i..16+(size-1)],
+ * otherwise (size == 16) either this is the first write and a full 32
+ * bytes will be written (hwm == 0, i == 32) or 16 bytes need to be
+ * written.
+ */
+ if (size < 16U)
+ {
+ debug(i == 16U);
+ dp -= size;
+ memcpy(dp, output + i, size);
+ }
+
+ else /* size == 16 */
+ {
+ debug(size == 16U);
+
+ /* Write i..(hwm-1); 16 or 32 bytes, however if 32 bytes are written
+ * they are contiguous and i==0.
+ *
+ * hwm is 0 or 16, i is 16 or 32, swap 0 and 32:
+ */
+ if (hwm == 0U) hwm = 32U;
+ if (i == 32U) i = 0U;
+ affirm(i < hwm);
+ debug(hwm == i+16U || (i == 0U && hwm == 32U));
+
+ hwm -= i;
+ dp -= hwm;
+ memcpy(dp, output+i, hwm);
+ }
+ }
+
+ debug(dp == png_upcast(png_bytep, tc->dp));
+
+# undef png_ptr
+}
+#endif /* READ_TRANSFORMS */
+
+/* 16 bit byte swapping */
+static void
+png_do_bswap(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op, *transform);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ const png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
+
+ tc->format = tr->format;
+ tc->bit_depth = tr->bit_depth;
+ tc->sp = dp;
+
+# ifdef _XOPEN_SOURCE
+ /* byte swapping often has incredibly fast implementations because of the
+ * importance in handling ethernet traffic. X/Open defines swab() for
+ * this purpose and it is widely supported and normally incredibly fast:
+ */
+ debug((rowbytes & 1) == 0);
+ swab(sp, dp, rowbytes);
+# else /* !_XOPEN_SOURCE */
+ {
+ const png_const_bytep ep = sp + rowbytes - 1;
+
+ while (sp < ep)
+ {
+ png_byte b0 = *sp++;
+ *dp++ = *sp++;
+ *dp++ = b0;
+ }
+
+ debug(sp == ep+1); /* even number of bytes */
+ }
+# endif
+
+ PNG_UNUSED(transform)
+# undef png_ptr
+}
+
+/* The following flags, store in tr->args, are set by the relevant PNGAPI
+ * png_set calls then resolved below.
+ */
+#define PNG_BO_STRIP_ALPHA 0x0001U /* Remove an alpha channel (read only) */
+#define PNG_BO_CHOP_16_TO_8 0x0002U /* Chop 16-bit to 8-bit channels */
+#define PNG_BO_GRAY_TO_RGB 0x0004U /* G <-> RGB; replicate channels */
+/* QUANTIZE happens here */
+#define PNG_BO_EXPAND_16 0x0008U /* Expand 8-bit channels to 16-bit */
+#define PNG_BO_BGR 0x0010U /* RGB <-> BGR */
+#define PNG_BO_FILLER 0x0020U /* Add a filler/alpha */
+#define PNG_BO_SWAP_ALPHA 0x0040U /* xA <-> Ax; alpha swap */
+#define PNG_BO_SWAP_16 0x0080U /* 16-bit channel byte swapping */
+
+/* The following are additional flags to qualify the transforms: */
+#define PNG_BO_FILLER_ALPHA 0x4000U /* The filler is an alpha value */
+#define PNG_BO_FILLER_FIRST 0x8000U /* The filler comes first */
+
+static void
+png_init_byte_ops(png_transformp *transform, png_transform_controlp tc)
+{
+ /* In the absence of png_set_quantize none of the above operations apply to a
+ * palette row except indirectly; they may apply if the palette was expanded,
+ * but this happens earlier in the pipeline.
+ *
+ * In the presence of png_set_quantize the rules are considerably more
+ * complex. In libpng 1.6.0 the following operations occur before
+ * png_do_quantize:
+ *
+ * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, but only sometimes)
+ * PNG_BO_STRIP_ALPHA (png_do_strip_channel; removes alpha)
+ * encode_alpha
+ * scale_16_to_8
+ * PNG_BO_CHOP_16_TO_8 (png_do_chop)
+ *
+ * The following occur afterward:
+ *
+ * PNG_BO_EXPAND_16 (png_do_expand_16)
+ * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, normally)
+ * PNG_BO_BGR (png_do_bgr)
+ * PNG_BO_FILLER (png_do_read_filler)
+ * PNG_BO_SWAP_ALPHA (png_do_read_swap_alpha)
+ * PNG_BO_SWAP_16 (png_do_swap; 16-bit byte swap)
+ *
+ * The gray to RGB operation needs to occur early for GA or gray+tRNS images
+ * where the pixels are being composed on a non-gray value. For the moment
+ * we assume that if this is necessary the following 'init' code will see RGB
+ * at this point.
+ *
+ * The quantize operation operates only if:
+ *
+ * 1) tc->bit_depth is 8
+ * 2) The color type exactly matches that required by the parameters to
+ * png_set_quantize; it can be RGB, RGBA or palette, but
+ * png_set_quantize (not the init routine) determines this.
+ *
+ * To avoid needing to know this here the two stage initialization is used
+ * with two transforms, one pre-quantization the other post. In the first
+ * stage the correct row format and depth is set up. In the second stage the
+ * pre-quantization transform looks for a post-quantization transform
+ * immediately following and, if it exists, transfers its flags to that.
+ */
+ png_structp png_ptr = tc->png_ptr;
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op, *transform);
+ png_uint_32 args = tr->tr.args;
+ const unsigned int png_format = tc->format;
+ unsigned int format = png_format; /* memory format */
+ const unsigned int png_bit_depth = tc->bit_depth;
+ unsigned int bit_depth = png_bit_depth; /* memory bit depth */
+
+ debug(tc->init);
+
+ /* Channel swaps do not occur on COLORMAP format data at present because the
+ * COLORMAP is limited to 1 byte per pixel (so there is nothing to
+ * manipulate). Likewise for low bit depth gray, however the code below may
+ * widen 8-bit gray to RGB.
+ */
+ if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U)
+ {
+ tr->tr.fn = NULL;
+ return;
+ }
+
+ /* This will normally happen in TC_INIT_FORMAT, but if there is a
+ * png_do_quantize operation which doesn't apply (this is unlikely) it will
+ * happen in TC_INIT_FINAL.
+ */
+ if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ)
+ {
+ debug(tr->tr.order == PNG_TR_CHANNEL_PREQ);
+
+ /* So we can merge this transform into the next one, note that because the
+ * PNG_BO_FILLER operation is POSTQ we don't need to copy anything other
+ * than the flags.
+ */
+ debug((args & tr->tr.next->args) == 0U);
+ tr->tr.next->args |= args;
+ tr->tr.fn = NULL;
+ return;
+ }
+
+ /* Else compact the flags for this transform - this is done in both
+ * TC_INIT_FORMAT and TC_INIT_FINAL because it is safer that way; the copy
+ * above shouldn't actually affect the results but might result in TO8 and
+ * TO16 cancelling each other because they are in separate transforms before
+ * the merge above.
+ *
+ * QUIET API CHANGE:
+ * For compatiblity with earlier versions of libpng these tests need to
+ * occur in the same order as the earlier transforms; 'TO8' combined with
+ * 'TO16' did actually do something to 16-bit data, however now it just
+ * preserves the original bit depth.
+ */
+ if ((args & PNG_BO_STRIP_ALPHA) != 0U)
+ {
+ if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U)
+ format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_STRIP_ALPHA);
+ }
+
+ if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
+ {
+ /* This is the quiet API CHANGE; in fact it isn't necessary, but it
+ * seems likely that requesting both operations is a mistake:
+ */
+ if ((args & PNG_BO_EXPAND_16) != 0U)
+ args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8|PNG_BO_EXPAND_16);
+
+ else if (bit_depth == 16U)
+ {
+ bit_depth = 8U;
+
+ /* This also makes the tRNS chunk unusable: */
+ tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
+ /* These need further processing: PNG_INFO_sBIT, PNG_INFO_bKGD */
+ }
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8);
+ }
+
+ /* QUANTIZE happens here */
+
+ if ((args & PNG_BO_EXPAND_16) != 0U)
+ {
+ /* This only does the 8 to 16-bit part of the expansion by multiply by
+ * 65535/255 (257) using byte replication. The cases of low bit depth
+ * gray being expanded to 16-bit have to be handled separately.
+ */
+ if (bit_depth == 8U)
+ bit_depth = 16U;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_EXPAND_16);
+ }
+
+ if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
+ {
+ if ((format & PNG_FORMAT_FLAG_COLOR) == 0U)
+ format |= PNG_FORMAT_FLAG_COLOR;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_GRAY_TO_RGB);
+ }
+
+ if ((args & PNG_BO_BGR) != 0U)
+ {
+ /* This does not happen on colormaps: */
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0U && !tc->palette)
+ format |= PNG_FORMAT_FLAG_BGR;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_BGR);
+ }
+
+ if ((args & PNG_BO_FILLER) != 0U)
+ {
+ if ((format & PNG_FORMAT_FLAG_ALPHA) == 0U)
+ {
+ format |= PNG_FORMAT_FLAG_ALPHA;
+ tc->channel_add = 1U;
+ /* And SWAP_ALPHA did not occur, because prior to 1.7.0 the filler op
+ * did not set ALPHA in the color type, so use SWAP_ALPHA to handle the
+ * before/after filler location.
+ *
+ * NOTE: this occurs twice, once in TC_START and once in TC_FINAL, but
+ * that is ok, the operations are idempotent.
+ *
+ * For colormaps (tc->palette set) the filler will just end up setting
+ * all the tRNS entries and PNG_BO_SWAP_ALPHA will be cancelled below.
+ */
+ if ((args & PNG_BO_FILLER_FIRST) != 0U)
+ args |= PNG_BO_SWAP_ALPHA;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
+
+ if (!(args & PNG_BO_FILLER_ALPHA)) /* filler is not alpha */
+ format |= PNG_FORMAT_FLAG_AFILLER;
+ }
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_FILLER);
+ }
+
+ if ((args & PNG_BO_SWAP_ALPHA) != 0U)
+ {
+ /* This does not happen on color maps: */
+ if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U && !tc->palette)
+ format |= PNG_FORMAT_FLAG_AFIRST;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
+ }
+
+ if ((args & PNG_BO_SWAP_16) != 0U)
+ {
+ if (bit_depth == 16U)
+ format |= PNG_FORMAT_FLAG_SWAPPED;
+
+ else
+ args &= PNG_BIC_MASK(PNG_BO_SWAP_16);
+ }
+
+ if (args != 0U)
+ {
+ /* At the end (TC_INIT_FINAL) work out the mapping array using the codes
+ * defined above and store the format and bit depth changes (as changes,
+ * so they will work either forward or backward). The filler array must
+ * be set up by the png_set API.
+ */
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ const unsigned int png_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
+
+ tc->format = format;
+ tc->bit_depth = bit_depth;
+
+ {
+ const unsigned int memory_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
+ unsigned int code_size, src_size;
+ int go_down;
+ png_byte codes[8];
+
+ /* The codes array maps the PNG format into the memory format
+ * assuming the mapping works upwards in the address space.
+ * Initially ignore the bit depth and just work on the first four
+ * bytes.
+ */
+ codes[0] = 8U;
+ codes[1] = 9U;
+ codes[2] = 10U;
+ codes[3] = 11U;
+ codes[7] = codes[6] = codes[5] = codes[4] = 0U/*error*/;
+
+ /* PNG_BO_STRIP_ALPHA: handled by memory_pixel_size */
+ /* PNG_BO_CHOP_16_TO_8: handled below */
+ /* PNG_BO_EXPAND_16: handled below */
+
+ if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
+ {
+ codes[3] = 9U; /* alpha, if present */
+ codes[2] = codes[1] = 8U;
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ /* Gray to RGB, so copy the tRNS G value into r,g,b: */
+ if (png_ptr->num_trans == 1U)
+ png_ptr->trans_color.blue =
+ png_ptr->trans_color.green =
+ png_ptr->trans_color.red =
+ png_ptr->trans_color.gray;
+# endif /* READ_tRNS */
+ }
+
+ /* 'BGR' and gray-to-RGB are mutually exclusive; with gray-to-RGB
+ * codes[0] == codes[2] == 8
+ */
+ else if ((args & PNG_BO_BGR) != 0U)
+ {
+ codes[0] = 10U;
+ codes[2] = 8U;
+ }
+
+ if ((args & PNG_BO_FILLER) != 0U)
+ {
+ /* The filler alway goes after; for a 'before' filler the code
+ * above turns on SWAP_ALPHA too. The gray-to-RGB transform has
+ * happened already, so the location of the filler channel is
+ * given by 'format':
+ */
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
+ codes[3] = 4U; /* low byte of filler */
+
+ else
+ codes[1] = 4U;
+ }
+
+ if ((args & PNG_BO_SWAP_ALPHA) != 0U)
+ {
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
+ {
+ /* BGR may have swapped the early codes. gray-to-RGB may have
+ * set them all to '8':
+ */
+ png_byte acode = codes[3];
+ codes[3] = codes[2];
+ codes[2] = codes[1];
+ codes[1] = codes[0];
+ codes[0] = acode;
+ }
+
+ else /* GA format */
+ codes[0] = codes[1], codes[1] = 8U;
+ }
+
+ /* PNG_BO_SWAP_16: 16-bit only, handled below */
+
+ /* Now the 16-bit dependent stuff. */
+ if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
+ {
+ /* 16-bit input, 8-bit output, happens before FILLER so the
+ * filler must be an 8-bit value. Apart from a filler code (4 in
+ * this case) the code must be adjusted from byte 'x' to byte
+ * '2x' to select the MSB of each 16-bit channel.
+ *
+ * We must use PNG_FORMAT_CHANNELS here because the memory pixel
+ * size might (in the future) include a TO16 operation.
+ */
+ unsigned int i = PNG_FORMAT_CHANNELS(format);
+
+ while (i > 0U)
+ {
+ unsigned int code = codes[--i];
+
+ if (code > 8U) /* 8, 4 need not change */
+ codes[i] = PNG_BYTE(8U+2U*(code-8U));
+ }
+ }
+
+ if ((args & PNG_BO_EXPAND_16) != 0U)
+ {
+ /* Don't expect this with CHOP, but it will work, setting the low
+ * 8-bits of each 16-bit value to the high bits.
+ */
+ unsigned int i = PNG_FORMAT_CHANNELS(format);
+
+ while (i > 0U)
+ {
+ png_byte code = codes[--i];
+
+ /* BSWAP is after FILLER, however the data passed in is a
+ * machine native png_uint_16. We don't know until this init
+ * routine whether the data is an 8 or 16-bit value because we
+ * don't know the full set of transforms the app will apply
+ * when the png_set_filler API is called.
+ *
+ * This means that the data in tr->filler[] needs to have the
+ * low bits in a known place, so the code here puts the low 8
+ * bits in filler[0], code 4. Hence the following:
+ */
+ if (code == 4U)
+ codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
+
+ else
+ codes[2U*i] = codes[2U*i+1U] = code;
+ }
+
+# ifdef PNG_READ_tRNS_SUPPORTED
+ /* We're just duplicating bytes, so the tRNS chunk can be
+ * maintained if present. If the tRNS is for a colormap this
+ * produces garbage in trans_color, but it isn't used.
+ */
+ if (png_ptr->num_trans == 1U)
+ {
+# define TO16(x) x = PNG_UINT_16((x & 0xFFU) * 0x101U)
+ TO16(png_ptr->trans_color.gray);
+ TO16(png_ptr->trans_color.red);
+ TO16(png_ptr->trans_color.green);
+ TO16(png_ptr->trans_color.blue);
+# undef TO16
+ }
+# endif /* READ_tRNS */
+ }
+
+ else if (bit_depth == 16U)
+ {
+ /* 16-bit input and output. */
+ unsigned int i = PNG_FORMAT_CHANNELS(format);
+
+ while (i > 0U)
+ {
+ unsigned int code = codes[--i];
+
+ if (code == 4U) /* as above */
+ codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
+
+ else
+ {
+ codes[2U*i] = PNG_BYTE(8U+2U*(code-8U));
+ codes[2U*i+1U] = PNG_BYTE(8U+2U*(code-8U)+1U);
+ }
+ }
+ }
+
+ if ((args & PNG_BO_SWAP_16) != 0U)
+ {
+ /* bswap the memory bytes. */
+ unsigned int i;
+ png_byte bswap_codes[sizeof codes];
+
+ debug((memory_pixel_size & 1U) == 0U);
+
+ for (i=0U; iread_struct)
+ {
+ /* There are no write transforms that add data to the PNG
+ * file; the 'filler' transform removes a channel, but that is
+ * the limit of the changes.
+ */
+ unsigned int i = 0U;
+ png_byte write_codes[8U];
+
+ memset(write_codes, 0, sizeof write_codes);
+
+ while (i= 8U) /* 8+index of PNG byte */
+ write_codes[code-8U] = PNG_BYTE(8U+i);
+ /* else this is a filler byte to be removed */
+ else
+ debug(code == 4U || code == 5U);
+
+ ++i;
+ }
+
+ code_size = png_pixel_size;
+ src_size = memory_pixel_size;
+ tr->format = png_format;
+ tr->bit_depth = png_bit_depth;
+
+ /* The PNG size should always be <= to the memory size, the
+ * source pointer will be the memory, the destination the PNG
+ * format, so it should always be possible to do the upwards
+ * copy.
+ */
+ go_down = png_pixel_size > memory_pixel_size;
+ affirm(!go_down);
+ memcpy(codes, write_codes, sizeof codes);
+ }
+
+ else
+# endif /* WRITE_TRANSFORMS */
+ {
+ code_size = memory_pixel_size;
+ src_size = png_pixel_size;
+ tr->format = format;
+ tr->bit_depth = bit_depth;
+ go_down = png_pixel_size < memory_pixel_size;
+ }
+
+ /* Record this for debugging: */
+ tr->tr.args = args;
+
+ /* For the same-pixel-size case check for a bswap; this is available
+ * in heavily optimized forms and is a common operation (50% of the
+ * time) with 16-bit PNG data, particularly given the handling in
+ * the simplified API.
+ */
+ if (!go_down)
+ {
+ if (memory_pixel_size == png_pixel_size)
+ {
+ int the_same = 1;
+ int swapped = (memory_pixel_size & 1) == 0; /* even count */
+ unsigned int i;
+
+ for (i=0U; itr.fn = png_do_bswap;
+ return;
+ }
+
+ else if (the_same)
+ impossible("not reached");
+ }
+
+ tr->tr.fn = png_do_byte_ops_up;
+
+ /* Construct the code, forwards: */
+ {
+ unsigned int i = code_size;
+ png_uint_32 code = 0U;
+
+ while (i > 0U)
+ {
+ unsigned int next = codes[--i];
+
+ code <<= 4U;
+
+ if ((next >= 8U && next < 8U+src_size) ||
+ next == 4U || next == 5U)
+ code += next;
+
+ else
+ impossible("invalid code (up)");
+ }
+
+ tr->codes = code;
+ }
+ }
+
+ else /* go_down */
+# ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ {
+ tr->tr.fn = png_do_byte_ops_down;
+
+ /* Construct the code, backwards: */
+ {
+ unsigned int i = 0U;
+ png_uint_32 code = 0U;
+
+ while (i < code_size)
+ {
+ unsigned int next = codes[i++];
+
+ code <<= 4;
+
+ if ((next >= 8U && next < 8U+src_size) ||
+ next == 4U || next == 5U)
+ code += next;
+
+ else
+ impossible("invalid code (down)");
+ }
+
+ tr->codes = code;
+ }
+ }
+# else /* !READ_TRANSFORMS */
+ impossible("not reached"); /* because of the affirm above */
+# endif /* !READ_TRANSFORMS */
+ }
+ }
+
+ else /* TC_INIT_FORMAT: just store modified 'args' */
+ {
+ tc->format = format;
+ tc->bit_depth = bit_depth;
+ tr->tr.args = args;
+ }
+ }
+
+ else /* the transform is not applicable */
+ tr->tr.fn = NULL;
+}
+#endif /* SWAP poo */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+static void
+png_init_rgb_to_gray_byte_ops(png_transformp *transform,
+ png_transform_controlp tc)
+{
+ /* This just delay initializes the function; all the transform initialization
+ * has been done below.
+ */
+ (*transform)->fn = png_do_byte_ops_up;
+
+ /* If this happens on a row do the transform immediately: */
+ if (!tc->init)
+ png_do_byte_ops_up(transform, tc);
+
+ else
+ {
+ /* This doing the init - update the row information here */
+# define png_ptr (tc->png_ptr)
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op, *transform);
+
+ debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
+ debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U &&
+ (tc->format & PNG_FORMAT_FLAG_COLOR) != 0U);
+
+ tc->format = tr->format;
+ tc->bit_depth = tr->bit_depth;
+# undef png_ptr
+ }
+}
+
+void /* PRIVATE */
+png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc,
+ unsigned int index, unsigned int order)
+ /* Add a byte_ops transform to convert RGB or RGBA data to 'gray' by
+ * selecting just the given change [index] The transform added is added at
+ * 'order'.
+ */
+{
+ png_transform_byte_op *tr = png_transform_cast(png_transform_byte_op,
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_rgb_to_gray_byte_ops, order));
+
+ affirm((tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
+ PNG_FORMAT_FLAG_COLOR &&
+ index <= 2 && tc->init == PNG_TC_INIT_FINAL);
+
+ tr->format = tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
+ tr->bit_depth = tc->bit_depth;
+
+ /* For 1 byte channel [index] plus, maybe, alpha: */
+ if (tc->bit_depth == 8)
+ tr->codes = 8U + index +
+ ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? (8U+3U) << 4 : 0U);
+
+ else
+ {
+ affirm(tc->bit_depth == 16);
+
+ /* As above, but two bytes; [2*index] and [2*index+1] */
+ index *= 2U;
+ tr->codes = (8U + index) + ((9U + index) << 4) +
+ ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ?
+ ((8U+6U) + ((9U+6U) << 4)) << 8 : 0U);
+ }
+}
+#endif /* READ_RGB_TO_GRAY */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\
+ defined(PNG_READ_BACKGROUND_SUPPORTED)
+void /* PRIVATE */
+png_push_gray_to_rgb_byte_ops(png_transformp *transform,
+ png_transform_controlp tc)
+ /* This is an init-time utility to add appropriate byte ops to expand a
+ * grayscale PNG data set to RGB.
+ */
+{
+# define png_ptr (tc->png_ptr)
+ png_transformp tr = png_push_transform(png_ptr,
+ sizeof (png_transform_byte_op), png_init_byte_ops, transform, NULL);
+
+ tr->args = PNG_BO_GRAY_TO_RGB;
+ debug(tr == *transform);
+ png_init_byte_ops(transform, tc);
+# undef png_ptr
+}
+#endif /* GRAY_TO_RGB && READ_BACKGROUND */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+void /* PRIVATE */
+png_add_strip_alpha_byte_ops(png_structrp png_ptr)
+{
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op), png_init_byte_ops,
+ PNG_TR_CHANNEL_PREQ)->args |= PNG_BO_STRIP_ALPHA;
+}
+#endif /* READ_STRIP_ALPHA */
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+/* Chop 16-bit depth files to 8-bit depth */
+void PNGAPI
+png_set_strip_16(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
+ PNG_BO_CHOP_16_TO_8;
+}
+#endif /* READ_STRIP_16_TO_8 */
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+void PNGAPI
+png_set_gray_to_rgb(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
+ PNG_BO_GRAY_TO_RGB;
+ }
+}
+#endif /* READ_GRAY_TO_RGB */
+
+/* QUANTIZE */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16-bit channels. PNG_BO_EXPAND_16 also expands the tRNS chunk if
+ * it is present, but it requires low bit depth grayscale expanded first. This
+ * must also force palette to RGB.
+ */
+void PNGAPI
+png_set_expand_16(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_set_palette_to_rgb(png_ptr);
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
+ PNG_BO_EXPAND_16;
+ }
+}
+#endif /* READ_EXPAND_16 */
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+void PNGAPI
+png_set_bgr(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+# ifndef PNG_READ_BGR_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_bgr not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_BGR_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_bgr not supported on write");
+ return;
+ }
+# endif
+
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
+ PNG_BO_BGR;
+ }
+}
+#endif /* BGR */
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* This includes png_set_filler and png_set_add_alpha. The only difference
+ * between the two is that the latter resulted in PNG_COLOR_MASK_ALPHA being
+ * added to the info_ptr color type, if png_read_update_info was called whereas
+ * the former did not.
+ *
+ * Regardless of whether the added channel resulted in the change to the
+ * png_info color type, the SWAP_ALPHA transform was not performed, even though
+ * it apparently occured after the add, because PNG_COLOR_MASK_ALPHA was never
+ * set in the 1.6 'row_info'.
+ *
+ * Consequently 'SWAP_ALPHA' and 'FILLER' were independent; one or the other
+ * would occur depending on the color type (not the number of channels) prior to
+ * the two transforms.
+ *
+ * Prior to 1.7.0 the app could obtain information about the memory format by
+ * calling png_read_update_info followed by png_get_color_type and
+ * png_get_channels. The first would return PNG_COLOR_TYPE..._ALPHA if
+ * png_set_add_alpha was performed and the base type if png_set_filler was
+ * performed, however in both cases png_get_channels would return the extra
+ * channel; 2 or 4.
+ *
+ * The app could also insert a user transform callback and view the color type
+ * in the old "row_info" structure, however this resulted in an inconsistent
+ * color type because png_set_alpha did not add COLOR_MASK_ALPHA to the color
+ * type.
+ *
+ * Hence API CHANGE: 1.7.0, row transform callbacks now see the same color type
+ * as reported by png_get_color_type after png_read_update_info.
+ */
/* Add a filler byte on read, or remove a filler or alpha byte on write.
* The filler type has changed in v0.95 to allow future 2-byte fillers
* for 48-bit input data, as well as to avoid problems with some compilers
* that don't like bytes as parameters.
*/
+static void
+set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc, int alpha)
+{
+ if (png_ptr != NULL)
+ {
+ if (filler_loc != PNG_FILLER_BEFORE && filler_loc != PNG_FILLER_AFTER)
+ {
+ png_app_error(png_ptr, "png_set_filler: invalid filler location");
+ return;
+ }
+
+# ifndef PNG_READ_SWAP_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_filler not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_SWAP_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_filler not supported on write");
+ return;
+ }
+# endif
+
+ {
+ png_transform_byte_op *tr =
+ png_transform_cast(png_transform_byte_op,
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_POSTQ));
+ png_uint_32 args = PNG_BO_FILLER;
+
+ if (filler_loc == PNG_FILLER_BEFORE)
+ args |= PNG_BO_FILLER_FIRST;
+
+ if (alpha)
+ args |= PNG_BO_FILLER_ALPHA;
+
+ tr->tr.args |= args;
+
+ /* The filler must be stored LSByte first: */
+ tr->filler[0] = PNG_BYTE(filler >> 0);
+ tr->filler[1] = PNG_BYTE(filler >> 8);
+ tr->filler[2] = PNG_BYTE(filler >> 16);
+ tr->filler[3] = PNG_BYTE(filler >> 24);
+ }
+ }
+}
+
void PNGAPI
png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
- png_debug(1, "in png_set_filler");
-
- if (png_ptr == NULL)
- return;
-
- /* In libpng 1.6 it is possible to determine whether this is a read or write
- * operation and therefore to do more checking here for a valid call.
- */
- if (png_ptr->mode & PNG_IS_READ_STRUCT)
- {
-# ifdef PNG_READ_FILLER_SUPPORTED
- /* On read png_set_filler is always valid, regardless of the base PNG
- * format, because other transformations can give a format where the
- * filler code can execute (basically an 8 or 16-bit component RGB or G
- * format.)
- *
- * NOTE: usr_channels is not used by the read code! (This has led to
- * confusion in the past.) The filler is only used in the read code.
- */
- png_ptr->filler = (png_uint_16)filler;
-# else
- png_app_error(png_ptr, "png_set_filler not supported on read");
- PNG_UNUSED(filler) /* not used in the write case */
- return;
-# endif
- }
-
- else /* write */
- {
-# ifdef PNG_WRITE_FILLER_SUPPORTED
- /* On write the usr_channels parameter must be set correctly at the
- * start to record the number of channels in the app-supplied data.
- */
- switch (png_ptr->color_type)
- {
- case PNG_COLOR_TYPE_RGB:
- png_ptr->usr_channels = 4;
- break;
-
- case PNG_COLOR_TYPE_GRAY:
- if (png_ptr->bit_depth >= 8)
- {
- png_ptr->usr_channels = 2;
- break;
- }
-
- else
- {
- /* There simply isn't any code in libpng to strip out bits
- * from bytes when the components are less than a byte in
- * size!
- */
- png_app_error(png_ptr,
- "png_set_filler is invalid for low bit depth gray output");
- return;
- }
-
- default:
- png_app_error(png_ptr,
- "png_set_filler: inappropriate color type");
- return;
- }
-# else
- png_app_error(png_ptr, "png_set_filler not supported on write");
- return;
-# endif
- }
-
- /* Here on success - libpng supports the operation, set the transformation
- * and the flag to say where the filler channel is.
- */
- png_ptr->transformations |= PNG_FILLER;
-
- if (filler_loc == PNG_FILLER_AFTER)
- png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
-
- else
- png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+ set_filler(png_ptr, filler, filler_loc, 0/*!alpha*/);
}
/* Added to libpng-1.2.7 */
void PNGAPI
png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
- png_debug(1, "in png_set_add_alpha");
-
- if (png_ptr == NULL)
- return;
-
- png_set_filler(png_ptr, filler, filler_loc);
- /* The above may fail to do anything. */
- if (png_ptr->transformations & PNG_FILLER)
- png_ptr->transformations |= PNG_ADD_ALPHA;
+ set_filler(png_ptr, filler, filler_loc, 1/*alpha*/);
}
-
-#endif
+#endif /* FILLER */
#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
void PNGAPI
png_set_swap_alpha(png_structrp png_ptr)
{
- png_debug(1, "in png_set_swap_alpha");
+ if (png_ptr != NULL)
+ {
+# ifndef PNG_READ_SWAP_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_swap_alpha not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_SWAP_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_swap_alpha not supported on write");
+ return;
+ }
+# endif
- if (png_ptr == NULL)
- return;
-
- png_ptr->transformations |= PNG_SWAP_ALPHA;
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
+ PNG_BO_SWAP_ALPHA;
+ }
}
-#endif
+#endif /* SWAP_ALPHA */
-#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+void PNGAPI
+png_set_swap(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+# ifndef PNG_READ_SWAP_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_swap not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_SWAP_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_swap not supported on write");
+ return;
+ }
+# endif
+
+ png_add_transform(png_ptr, sizeof (png_transform_byte_op),
+ png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
+ PNG_BO_SWAP_16;
+ }
+}
+#endif /* SWAP */
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
+ defined(PNG_WRITE_PACKSWAP_SUPPORTED) ||\
+ defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
+ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
+ defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+static png_alloc_size_t
+row_align(png_transform_controlp tc)
+ /* Utiltity to align the source row (sp) in a transform control; it does this
+ * by simply copying it to dp if it is not already aligned. As a convenience
+ * the utility returns the number of bytes in the row.
+ */
+{
+ png_const_structp png_ptr = tc->png_ptr;
+ png_const_voidp sp = tc->sp;
+ png_voidp dp = tc->dp;
+ png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
+
+ /* For alignment; if png_alignof is not supported by the compiler this will
+ * always do an initial memcpy if the source and destination are not the
+ * same. We can only get here for write; the read case always uses locally
+ * allocated buffers, only write reads from the application data directly.
+ */
+# ifdef png_alignof
+ debug(png_isaligned(dp, png_uint_32));
+# endif
+ if (sp != dp && !png_ptr->read_struct && !png_isaligned(sp, png_uint_32))
+ {
+ UNTESTED
+ memcpy(dp, sp, rowbytes);
+ tc->sp = dp;
+ }
+
+ return rowbytes;
+}
+#endif /* Stuff that needs row_align */
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
+ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
+ defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Bit-ops; invert bytes. This works for mono inverts too because even the low
+ * bit depths can be handled as bytes (since there can be no intervening
+ * channels).
+ */
+#define PNG_B_INVERT_MONO 1U
+#define PNG_B_INVERT_RGB 2U /* not set, used below */
+#define PNG_B_INVERT_ALPHA 4U
+
+typedef struct
+{
+ png_transform tr;
+ unsigned int step0; /* initial advance on sp and dp */
+ unsigned int step; /* advance after start */
+ png_uint_32 mask; /* XOR mask */
+} png_transform_bit_op;
+
+static void
+png_do_invert_all(png_transformp *transform, png_transform_controlp tc)
+{
+ const png_const_structp png_ptr = tc->png_ptr;
+ /* Invert the whole row, quickly */
+ const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
+ png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
+ png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
+
+ tc->sp = dp;
+
+ if (png_ptr->read_struct)
+ {
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+ tc->range++;
+ }
+
+ else if (--(tc->range) == 0)
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
+
+ while (png_upcast(void*,dp) < dp_end)
+ *dp++ = ~*sp++;
+
+ PNG_UNUSED(transform)
+}
+
+static void
+png_do_invert_channel(png_transformp *transform, png_transform_controlp tc)
+{
+ const png_const_structp png_ptr = tc->png_ptr;
+ /* Invert just one channel in the row. */
+ const png_transform_bit_op * const tr =
+ png_transform_cast(png_transform_bit_op, *transform);
+ const png_uint_32 mask = tr->mask;
+ const unsigned int step = tr->step;
+ const unsigned int step0 = tr->step0;
+ const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
+ png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
+ png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
+
+ tc->sp = dp;
+
+ if (png_ptr->read_struct)
+ {
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+ tc->range++;
+ }
+
+ else if (--(tc->range) == 0)
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
+
+ if (sp == dp || step == 1)
+ {
+ sp += step0;
+ dp += step0;
+
+ while (png_upcast(void*,dp) < dp_end)
+ *dp = *sp ^ mask, dp += step, sp += step;
+ }
+
+ else /* step == 2, copy required */
+ {
+ if (step0) /* must be 1 */
+ *dp++ = *sp++;
+
+ while (png_upcast(void*,dp) < dp_end)
+ {
+ *dp++ = *sp++ ^ mask;
+ if (!(png_upcast(void*,dp) < dp_end))
+ break;
+ *dp++ = *sp++;
+ }
+ }
+
+ PNG_UNUSED(transform)
+}
+
+static void
+png_init_invert(png_transformp *transform, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_transform_bit_op *tr =
+ png_transform_cast(png_transform_bit_op, *transform);
+ png_uint_32 invert = tr->tr.args;
+ png_uint_32 present; /* channels present */
+
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ present = 0;
+
+ else /* not color mapped */
+ {
+ if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
+ present = PNG_B_INVERT_RGB;
+ else
+ present = PNG_B_INVERT_MONO;
+
+ if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ present |= PNG_B_INVERT_ALPHA;
+ }
+
+ /* Cannot invert things that aren't there: */
+ invert &= present;
+
+ /* If nothing can be inverted is present the transform is not applicable: */
+ if (invert == 0)
+ (*transform)->fn = NULL;
+
+ else
+ {
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+ tc->range++;
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ /* If everything that is present is to be inverted just invert the
+ * whole row:
+ */
+ if (invert == present)
+ (*transform)->fn = png_do_invert_all;
+
+ else
+ {
+ /* One thing is to be inverted, G or A: */
+ unsigned int channels = PNG_TC_CHANNELS(*tc);
+ unsigned int channel =
+ (tc->format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels-1;
+
+ affirm(channels == 2 || channels == 4);
+
+ if (invert != PNG_B_INVERT_ALPHA)
+ {
+ debug(invert == PNG_B_INVERT_MONO && channels == 2 &&
+ present == PNG_B_INVERT_MONO+PNG_B_INVERT_ALPHA);
+ channel = (channels-1) - channel;
+ }
+
+ affirm(tc->bit_depth == 8 || tc->bit_depth == 16);
+
+ /* So channels[channel] is to be inverted, make a mask: */
+ {
+ union
+ {
+ png_byte bytes[8];
+ png_uint_32 words[2];
+ } masks;
+
+ memset(&masks, 0, sizeof masks);
+
+ if (tc->bit_depth == 8)
+ {
+ /* channels is 2 or 4, channel < 4. */
+ masks.bytes[channel+channels] = masks.bytes[channel] = 0xff;
+ tr->step = 1;
+ tr->mask = masks.words[0];
+ tr->step0 = 0;
+ }
+
+ else /* tc->bit_depth == 16 */
+ {
+ channel <<= 1; /* in bytes */
+ masks.bytes[channel+1] = masks.bytes[channel] = 0xff;
+
+ if (channels == 2)
+ {
+ tr->step = 1;
+ tr->mask = masks.words[0];
+ tr->step0 = 0;
+ }
+
+ else /* channels == 4 */
+ {
+ tr->step = 2;
+
+ if (masks.words[0] == 0)
+ {
+ tr->mask = masks.words[1];
+ tr->step0 = 1;
+ }
+
+ else
+ {
+ tr->mask = masks.words[0];
+ tr->step0 = 0;
+ }
+ }
+ }
+ }
+
+ (*transform)->fn = png_do_invert_channel;
+ }
+ }
+ }
+# undef png_ptr
+}
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
void PNGAPI
png_set_invert_alpha(png_structrp png_ptr)
{
- png_debug(1, "in png_set_invert_alpha");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->transformations |= PNG_INVERT_ALPHA;
+ if (png_ptr != NULL)
+ {
+ png_add_transform(png_ptr, sizeof (png_transform_bit_op),
+ png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_ALPHA;
+# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ /* This is necessary to avoid palette processing on write; the only
+ * transform that applies to colormapped images is the tRNS chunk
+ * invert.
+ */
+ png_ptr->write_invert_alpha = 1U;
+# endif
+ }
}
-#endif
+#endif /* INVERT_ALPHA */
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
void PNGAPI
png_set_invert_mono(png_structrp png_ptr)
{
- png_debug(1, "in png_set_invert_mono");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->transformations |= PNG_INVERT_MONO;
+ if (png_ptr != NULL)
+ png_add_transform(png_ptr, sizeof (png_transform_bit_op),
+ png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_MONO;
}
+#endif /* INVERT */
+#endif /* INVERT_ALPHA || INVERT */
-/* Invert monochrome grayscale data */
-void /* PRIVATE */
-png_do_invert(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_invert");
-
- /* This test removed from libpng version 1.0.13 and 1.2.0:
- * if (row_info->bit_depth == 1 &&
- */
- if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
- {
- png_bytep rp = row;
- png_size_t i;
- png_size_t istop = row_info->rowbytes;
-
- for (i = 0; i < istop; i++)
- {
- *rp = (png_byte)(~(*rp));
- rp++;
- }
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
- row_info->bit_depth == 8)
- {
- png_bytep rp = row;
- png_size_t i;
- png_size_t istop = row_info->rowbytes;
-
- for (i = 0; i < istop; i += 2)
- {
- *rp = (png_byte)(~(*rp));
- rp += 2;
- }
- }
-
-#ifdef PNG_16BIT_SUPPORTED
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
- row_info->bit_depth == 16)
- {
- png_bytep rp = row;
- png_size_t i;
- png_size_t istop = row_info->rowbytes;
-
- for (i = 0; i < istop; i += 4)
- {
- *rp = (png_byte)(~(*rp));
- *(rp + 1) = (png_byte)(~(*(rp + 1)));
- rp += 4;
- }
- }
-#endif
-}
-#endif
-
-#ifdef PNG_16BIT_SUPPORTED
-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-/* Swaps byte order on 16 bit depth images */
-void /* PRIVATE */
-png_do_swap(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_swap");
-
- if (row_info->bit_depth == 16)
- {
- png_bytep rp = row;
- png_uint_32 i;
- png_uint_32 istop= row_info->width * row_info->channels;
-
- for (i = 0; i < istop; i++, rp += 2)
- {
- png_byte t = *rp;
- *rp = *(rp + 1);
- *(rp + 1) = t;
- }
- }
-}
-#endif
-#endif
-
-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-static PNG_CONST png_byte onebppswaptable[256] = {
- 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
- 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
- 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
- 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
- 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
- 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
- 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
- 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
- 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
- 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
- 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
- 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
- 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
- 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
- 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
- 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
- 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
- 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
- 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
- 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
- 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
- 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
- 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
- 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
- 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
- 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
- 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
- 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
- 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
- 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
- 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
- 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
-};
-
-static PNG_CONST png_byte twobppswaptable[256] = {
- 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
- 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
- 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
- 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
- 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
- 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
- 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
- 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
- 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
- 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
- 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
- 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
- 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
- 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
- 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
- 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
- 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
- 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
- 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
- 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
- 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
- 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
- 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
- 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
- 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
- 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
- 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
- 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
- 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
- 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
- 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
- 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
-};
-
-static PNG_CONST png_byte fourbppswaptable[256] = {
- 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
- 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
- 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
- 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
- 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
- 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
- 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
- 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
- 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
- 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
- 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
- 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
- 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
- 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
- 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
- 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
- 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
- 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
- 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
- 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
- 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
- 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
- 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
- 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
- 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
- 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
- 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
- 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
- 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
- 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
- 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
- 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
-};
-
-/* Swaps pixel packing order within bytes */
-void /* PRIVATE */
-png_do_packswap(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_packswap");
-
- if (row_info->bit_depth < 8)
- {
- png_bytep rp;
- png_const_bytep end, table;
-
- end = row + row_info->rowbytes;
-
- if (row_info->bit_depth == 1)
- table = onebppswaptable;
-
- else if (row_info->bit_depth == 2)
- table = twobppswaptable;
-
- else if (row_info->bit_depth == 4)
- table = fourbppswaptable;
-
- else
- return;
-
- for (rp = row; rp < end; rp++)
- *rp = table[*rp];
- }
-}
-#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */
-
-#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
- defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
-/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
- * somewhat weird combination of flags to determine what to do. All the calls
- * to png_do_strip_filler are changed in 1.5.2 to call this instead with the
- * correct arguments.
- *
- * The routine isn't general - the channel must be the channel at the start or
- * end (not in the middle) of each pixel.
+/*
+ * WARNING
+ * WARNING
+ * WARNING
+ * WARNING
+ * WARNING The transforms below are temporary; they can and will be
+ * WARNING heavily optimized before release.
+ * WARNING
+ * WARNING
+ * WARNING
*/
-void /* PRIVATE */
-png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+typedef struct
{
- png_bytep sp = row; /* source pointer */
- png_bytep dp = row; /* destination pointer */
- png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
+ png_transform tr;
+ png_color_8 true_bits;
+} png_transform_shift;
- /* At the start sp will point to the first byte to copy and dp to where
- * it is copied to. ep always points just beyond the end of the row, so
- * the loop simply copies (channels-1) channels until sp reaches ep.
- *
- * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
- * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
- */
+/* Shift pixel values to take advantage of whole range. Pass the
+ * true number of bits in bit_depth. The row should be packed
+ * according to tc->bit_depth. Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ *
+ * NOTE: this is horrible complexity for no value. Once people suggested they
+ * were selling 16-bit displays with 5:6:5 bits spread R:G:B but so far as I
+ * could determine these displays produced intermediate grey (uncolored) colors,
+ * which is impossible with a true 5:6:5, so most likely 5:6:5 was marketing.
+ */
+static unsigned int
+set_shifts(unsigned int format, unsigned int bit_depth,
+ png_const_color_8p true_bits, int *shift_start, int *shift_dec)
+{
+ unsigned int channels = 0;
- /* GA, GX, XG cases */
- if (row_info->channels == 2)
+ if ((format & (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) ==
+ (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST))
+ ++channels; /* filled in below */
+
+ if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
{
- if (row_info->bit_depth == 8)
- {
- if (at_start != 0) /* Skip initial filler */
- ++sp;
- else /* Skip initial channel and, for sp, the filler */
- sp += 2, ++dp;
+ unsigned int offset = /* 0 or 2 as appropriate for red */
+ ((format & PNG_FORMAT_FLAG_BGR) != 0) << 1;
- /* For a 1 pixel wide image there is nothing to do */
- while (sp < ep)
- *dp++ = *sp, sp += 2;
+ shift_start[channels+offset] = bit_depth - true_bits->red;
+ if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->red;
- row_info->pixel_depth = 8;
- }
+ shift_start[channels+1] = bit_depth - true_bits->green;
+ if (shift_dec != NULL) shift_dec[channels+1] = true_bits->green;
- else if (row_info->bit_depth == 16)
- {
- if (at_start != 0) /* Skip initial filler */
- sp += 2;
- else /* Skip initial channel and, for sp, the filler */
- sp += 4, dp += 2;
+ offset ^= 2; /* for blue */
+ shift_start[channels+offset] = bit_depth - true_bits->blue;
+ if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue;
- while (sp < ep)
- *dp++ = *sp++, *dp++ = *sp, sp += 3;
-
- row_info->pixel_depth = 16;
- }
-
- else
- return; /* bad bit depth */
-
- row_info->channels = 1;
-
- /* Finally fix the color type if it records an alpha channel */
- if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- row_info->color_type = PNG_COLOR_TYPE_GRAY;
+ channels += 3;
}
- /* RGBA, RGBX, XRGB cases */
- else if (row_info->channels == 4)
+ else /* no color: gray */
{
- if (row_info->bit_depth == 8)
- {
- if (at_start != 0) /* Skip initial filler */
- ++sp;
- else /* Skip initial channels and, for sp, the filler */
- sp += 4, dp += 3;
-
- /* Note that the loop adds 3 to dp and 4 to sp each time. */
- while (sp < ep)
- *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2;
-
- row_info->pixel_depth = 24;
- }
-
- else if (row_info->bit_depth == 16)
- {
- if (at_start != 0) /* Skip initial filler */
- sp += 2;
- else /* Skip initial channels and, for sp, the filler */
- sp += 8, dp += 6;
-
- while (sp < ep)
- {
- /* Copy 6 bytes, skip 2 */
- *dp++ = *sp++, *dp++ = *sp++;
- *dp++ = *sp++, *dp++ = *sp++;
- *dp++ = *sp++, *dp++ = *sp, sp += 3;
- }
-
- row_info->pixel_depth = 48;
- }
-
- else
- return; /* bad bit depth */
-
- row_info->channels = 3;
-
- /* Finally fix the color type if it records an alpha channel */
- if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- row_info->color_type = PNG_COLOR_TYPE_RGB;
+ shift_start[channels] = bit_depth - true_bits->gray;
+ if (shift_dec != NULL) shift_dec[channels] = true_bits->gray;
+ ++channels;
}
- else
- return; /* The filler channel has gone already */
+ if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ const unsigned int offset =
+ (format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels++;
- /* Fix the rowbytes value. */
- row_info->rowbytes = dp-row;
+ shift_start[offset] = bit_depth - true_bits->alpha;
+ if (shift_dec != NULL) shift_dec[offset] = true_bits->alpha;
+ }
+
+ return channels;
}
-#endif
-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-/* Swaps red and blue bytes within a pixel */
-void /* PRIVATE */
-png_do_bgr(png_row_infop row_info, png_bytep row)
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+static void
+png_do_shift(png_transformp *transform, png_transform_controlp tc)
{
- png_debug(1, "in png_do_bgr");
+# define png_ptr (tc->png_ptr)
+ png_transform_shift *tr =
+ png_transform_cast(png_transform_shift, *transform);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
+
+ png_debug(1, "in png_do_shift");
+
+ if (--(tc->range) == 0)
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
+
+ tc->sp = dp;
- if ((row_info->color_type & PNG_COLOR_MASK_COLOR))
{
- png_uint_32 row_width = row_info->width;
- if (row_info->bit_depth == 8)
+ int shift_start[4], shift_dec[4];
+ unsigned int channels = set_shifts(tc->format, tc->bit_depth,
+ &tr->true_bits, shift_start, shift_dec);
+
+ debug(PNG_TC_CHANNELS(*tc) == channels);
+
+ /* With low res depths, could only be grayscale, so one channel */
+ if (tc->bit_depth < 8)
{
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ unsigned int mask;
+
+ UNTESTED
+ affirm(channels == 1);
+ /* This doesn't matter but we expect to run before packswap: */
+ debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
+
+ if (tr->true_bits.gray == 1 && tc->bit_depth == 2)
+ mask = 0x55;
+
+ else if (tc->bit_depth == 4 && tr->true_bits.gray == 3)
+ mask = 0x11;
+
+ else
+ mask = 0xff;
+
+ while (dp < dp_end)
{
- png_bytep rp;
- png_uint_32 i;
+ int j;
+ unsigned int v, out;
- for (i = 0, rp = row; i < row_width; i++, rp += 3)
+ v = *sp++;
+ out = 0;
+
+ for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
{
- png_byte save = *rp;
- *rp = *(rp + 2);
- *(rp + 2) = save;
- }
- }
+ if (j > 0)
+ out |= v << j;
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- png_bytep rp;
- png_uint_32 i;
-
- for (i = 0, rp = row; i < row_width; i++, rp += 4)
- {
- png_byte save = *rp;
- *rp = *(rp + 2);
- *(rp + 2) = save;
+ else
+ out |= (v >> (-j)) & mask;
}
+
+ *dp++ = png_check_byte(png_ptr, out);
}
}
-#ifdef PNG_16BIT_SUPPORTED
- else if (row_info->bit_depth == 16)
+ else if (tc->bit_depth == 8)
{
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ unsigned int c = 0;
+
+ UNTESTED
+ while (dp < dp_end)
{
- png_bytep rp;
- png_uint_32 i;
- for (i = 0, rp = row; i < row_width; i++, rp += 6)
+ int j;
+ unsigned int v, out;
+
+ v = *sp++;
+ out = 0;
+
+ for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
{
- png_byte save = *rp;
- *rp = *(rp + 4);
- *(rp + 4) = save;
- save = *(rp + 1);
- *(rp + 1) = *(rp + 5);
- *(rp + 5) = save;
- }
- }
+ if (j > 0)
+ out |= v << j;
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- png_bytep rp;
- png_uint_32 i;
-
- for (i = 0, rp = row; i < row_width; i++, rp += 8)
- {
- png_byte save = *rp;
- *rp = *(rp + 4);
- *(rp + 4) = save;
- save = *(rp + 1);
- *(rp + 1) = *(rp + 5);
- *(rp + 5) = save;
+ else
+ out |= v >> (-j);
}
+
+ *dp++ = png_check_byte(png_ptr, out);
+ if (++c == channels) c = 0;
+ }
+ }
+
+ else /* tc->bit_depth == 16 */
+ {
+ unsigned int c = 0, s0, s1;
+
+ UNTESTED
+ if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
+ s0 = 0, s1 = 8; /* LSB */
+
+ else
+ s0 = 8, s1 = 0; /* MSB */
+
+ while (dp < dp_end)
+ {
+ int j;
+ unsigned int value, v;
+
+ v = *sp++ << s0;
+ v += *sp++ << s1;
+ value = 0;
+
+ for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+ {
+ if (j > 0)
+ value |= v << j;
+
+ else
+ value |= v >> (-j);
+ }
+
+ *dp++ = PNG_BYTE(value >> s0);
+ *dp++ = PNG_BYTE(value >> s1);
}
}
-#endif
}
+# undef png_ptr
}
-#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */
+#endif /* WRITE_SHIFT */
-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
- defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
-/* Added at libpng-1.5.10 */
-void /* PRIVATE */
-png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* Reverse the effects of png_do_shift. This routine merely shifts the
+ * pixels back to their significant bits values. Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+static void
+png_do_unshift(png_transformp *transform, png_transform_controlp tc)
{
- if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
- png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
+# define png_ptr (tc->png_ptr)
+ png_transform_shift *tr =
+ png_transform_cast(png_transform_shift, *transform);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+ png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
+
+ png_debug(1, "in png_do_unshift");
+
+ tc->range++;
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+
{
- /* Calculations moved outside switch in an attempt to stop different
- * compiler warnings. 'padding' is in *bits* within the last byte, it is
- * an 'int' because pixel_depth becomes an 'int' in the expression below,
- * and this calculation is used because it avoids warnings that other
- * forms produced on either GCC or MSVC.
- */
- int padding = (-row_info->pixel_depth * row_info->width) & 7;
- png_bytep rp = png_ptr->row_buf + row_info->rowbytes;
+ int shift[4];
+ unsigned int channels = set_shifts(tc->format, tc->bit_depth,
+ &tr->true_bits, shift, NULL);
+
+ debug(PNG_TC_CHANNELS(*tc) == channels);
- switch (row_info->bit_depth)
{
- case 1:
+ unsigned int c, have_shift;
+
+ for (c = have_shift = 0; c < channels; ++c)
{
- /* in this case, all bytes must be 0 so we don't need
- * to unpack the pixels except for the rightmost one.
+ /* A shift of more than the bit depth is an error condition but it
+ * gets ignored here.
*/
- for (; rp > png_ptr->row_buf; rp--)
- {
- if (*rp >> padding != 0)
- png_ptr->num_palette_max = 1;
- padding = 0;
- }
+ if (shift[c] <= 0 || (unsigned)/*SAFE*/shift[c] >= tc->bit_depth)
+ shift[c] = 0;
- break;
+ else
+ have_shift = 1;
}
+ if (have_shift == 0)
+ return;
+ }
+
+ /* The code below will copy sp to dp, so: */
+ tc->sp = dp;
+
+ switch (tc->bit_depth)
+ {
+ default:
+ /* Must be 1bpp gray: should not be here! */
+ impossible("unshift bit depth");
+ /* NOTREACHED */
+ break;
+
case 2:
- {
- for (; rp > png_ptr->row_buf; rp--)
- {
- int i = ((*rp >> padding) & 0x03);
-
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
-
- i = (((*rp >> padding) >> 2) & 0x03);
-
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
-
- i = (((*rp >> padding) >> 4) & 0x03);
-
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
-
- i = (((*rp >> padding) >> 6) & 0x03);
-
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
-
- padding = 0;
- }
+ /* Must be 2bpp gray */
+ debug(channels == 1 && shift[0] == 1);
+ debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
+ while (dp < dp_end)
+ *dp++ = (*sp++ >> 1) & 0x55;
break;
- }
case 4:
- {
- for (; rp > png_ptr->row_buf; rp--)
+ /* Must be 4bpp gray */
+ debug(channels == 1);
+ debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
{
- int i = ((*rp >> padding) & 0x0f);
+ unsigned int gray_shift = shift[0];
+ unsigned int mask = 0xf >> gray_shift; /* <= 4 bits */
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
+ mask |= mask << 4; /* <= 8 bits */
- i = (((*rp >> padding) >> 4) & 0x0f);
-
- if (i > png_ptr->num_palette_max)
- png_ptr->num_palette_max = i;
-
- padding = 0;
+ while (dp < dp_end)
+ *dp++ = (png_byte)/*SAFE*/((*sp++ >> gray_shift) & mask);
}
-
break;
- }
case 8:
- {
- for (; rp > png_ptr->row_buf; rp--)
+ /* Single byte components, G, GA, RGB, RGBA */
{
- if (*rp > png_ptr->num_palette_max)
- png_ptr->num_palette_max = (int) *rp;
+ unsigned int channel = 0;
+
+ while (dp < dp_end)
+ {
+ *dp++ = (png_byte)/*SAFE*/(*sp++ >> shift[channel]);
+ if (++channel >= channels)
+ channel = 0;
+ }
}
-
break;
- }
- default:
+ case 16:
+ /* Double byte components, G, GA, RGB, RGBA */
+ {
+ unsigned int channel = 0;
+ unsigned int s0, s1;
+
+ if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
+ s0 = 0, s1 = 8; /* LSB */
+
+ else
+ s0 = 8, s1 = 0; /* MSB */
+
+ while (dp < dp_end)
+ {
+ unsigned int value = *sp++ << s0;
+
+ value += *sp++ << s1; /* <= 16 bits */
+
+ value >>= shift[channel];
+ if (++channel >= channels) channel = 0;
+ *dp++ = PNG_BYTE(value >> s0);
+ *dp++ = PNG_BYTE(value >> s1);
+ }
+ }
break;
}
}
+
+# undef png_ptr
}
-#endif /* PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED */
+#endif /* READ_SHIFT */
-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
- defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-void PNGAPI
-png_set_user_transform_info(png_structrp png_ptr, png_voidp
- user_transform_ptr, int user_transform_depth, int user_transform_channels)
+static void
+init_shift(png_transformp *transform, png_transform_controlp tc)
{
- png_debug(1, "in png_set_user_transform_info");
+ png_const_structp png_ptr = tc->png_ptr;
- if (png_ptr == NULL)
- return;
-
-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
- if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
- (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
+ /* These shifts apply to the component value, not the pixel index, so skip
+ * palette data. In addition there is no *write* shift for palette entries;
+ * only a read one, so skip the write/palette case too.
+ */
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
+ (png_ptr->read_struct || !tc->palette))
{
- png_app_error(png_ptr,
- "info change after png_start_read_image or png_read_update_info");
- return;
+ /* The only change to the format is to mark the data as having a non-PNG
+ * range.
+ */
+ tc->range++;
+ tc->format |= PNG_FORMAT_FLAG_RANGE;
+
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+# ifdef PNG_READ_SHIFT_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ (*transform)->fn = png_do_unshift;
+ return;
+ }
+# endif
+# ifdef PNG_WRITE_SHIFT_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ (*transform)->fn = png_do_shift;
+ return;
+ }
+# endif
+ }
}
-#endif
- png_ptr->user_transform_ptr = user_transform_ptr;
- png_ptr->user_transform_depth = (png_byte)user_transform_depth;
- png_ptr->user_transform_channels = (png_byte)user_transform_channels;
+ else /* transform not applicable */
+ (*transform)->fn = NULL;
}
-#endif
-/* This function returns a pointer to the user_transform_ptr associated with
- * the user transform functions. The application should free any memory
- * associated with this pointer before png_write_destroy and png_read_destroy
- * are called.
- */
-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-png_voidp PNGAPI
-png_get_user_transform_ptr(png_const_structrp png_ptr)
+void PNGAPI
+png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
{
- if (png_ptr == NULL)
- return (NULL);
+ if (png_ptr != NULL && true_bits != NULL)
+ {
+# ifndef PNG_READ_SHIFT_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_shift not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_SHIFT_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_shift not supported on write");
+ return;
+ }
+# endif
- return png_ptr->user_transform_ptr;
+ {
+ png_transform_shift *trs = png_transform_cast(png_transform_shift,
+ png_add_transform(png_ptr, sizeof (png_transform_shift),
+ init_shift, PNG_TR_SHIFT));
+ trs->true_bits = *true_bits;
+ }
+ }
}
-#endif
+#endif /* SHIFT */
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Turn on pixel packing */
+void PNGAPI
+png_set_packing(png_structrp png_ptr)
+{
+ /* The transforms aren't symmetric, so even though there is one API there are
+ * two internal init functions, one for read, the other write:
+ */
+ if (png_ptr != NULL)
+ {
+ if (png_ptr->read_struct)
+ {
+# ifdef PNG_READ_PACK_SUPPORTED
+ png_add_transform(png_ptr, 0/*size*/, png_init_read_pack,
+ PNG_TR_PACK);
+# else
+ png_app_error(png_ptr, "png_set_packing not supported on read");
+# endif
+ }
+
+ else
+ {
+# ifdef PNG_WRITE_PACK_SUPPORTED
+ png_add_transform(png_ptr, 0/*size*/, png_init_write_pack,
+ PNG_TR_PACK);
+# else
+ png_app_error(png_ptr, "png_set_packing not supported on write");
+# endif
+ }
+ }
+}
+#endif /* PACK */
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
+ defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Turn on pixel-swapping within a byte, this is symmetric - doing the swap
+ * twice produces the original value, so only one implementation is required for
+ * either read or write.
+ *
+ * Used to be refered to as "packswap", but pixel-swap seems more
+ * self-documenting.
+ */
+static void
+png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc)
+{
+ png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
+ png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
+ png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
+
+ tc->sp = dp;
+ tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
+
+ for (;;)
+ {
+ png_uint_32 s = *sp++;
+ s = ((s >> 1) & 0x55555555) | ((s & 0x55555555) << 1);
+ s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
+ s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
+ *dp++ = s;
+ if (rowbytes <= 4) break;
+ rowbytes -= 4;
+ }
+
+ PNG_UNUSED(transform)
+}
+
+static void
+png_do_swap_2bit(png_transformp *transform, png_transform_controlp tc)
+{
+ png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
+ png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
+ png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
+
+ tc->sp = dp;
+ tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
+
+ for (;;)
+ {
+ png_uint_32 s = *sp++;
+ s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
+ s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
+ *dp++ = s;
+ if (rowbytes <= 4) break;
+ rowbytes -= 4;
+ }
+
+ PNG_UNUSED(transform)
+}
+
+static void
+png_do_swap_4bit(png_transformp *transform, png_transform_controlp tc)
+{
+ png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
+ png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
+ png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
+
+ tc->sp = dp;
+ tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
+
+ for (;;)
+ {
+ png_uint_32 s = *sp++;
+ s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
+ *dp++ = s;
+ if (rowbytes <= 4) break;
+ rowbytes -= 4;
+ }
+
+ PNG_UNUSED(transform)
+}
+
+static void
+init_packswap(png_transformp *transform, png_transform_controlp tc)
+{
+ png_transform_fn fn;
+
+# define png_ptr tc->png_ptr
+ debug(tc->init);
+# undef png_ptr
+
+ switch (tc->bit_depth)
+ {
+ case 1: fn = png_do_swap_1bit; break;
+ case 2: fn = png_do_swap_2bit; break;
+ case 4: fn = png_do_swap_4bit; break;
+
+ default: /* transform not applicable */
+ (*transform)->fn = NULL;
+ return;
+ }
+
+ tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
+ if (tc->init == PNG_TC_INIT_FINAL)
+ (*transform)->fn = fn;
+}
+
+void PNGAPI
+png_set_packswap(png_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+# ifndef PNG_READ_PACKSWAP_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_packswap not supported on read");
+ return;
+ }
+# endif
+# ifndef PNG_WRITE_PACKSWAP_SUPPORTED
+ if (!png_ptr->read_struct)
+ {
+ png_app_error(png_ptr, "png_set_packswap not supported on write");
+ return;
+ }
+# endif
+
+ png_add_transform(png_ptr, 0/*size*/, init_packswap, PNG_TR_PIXEL_SWAP);
+ }
+}
+#endif /* PACKSWAP */
+
+/* User transform handling */
#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
png_uint_32 PNGAPI
png_get_current_row_number(png_const_structrp png_ptr)
{
- /* See the comments in png.h - this is the sub-image row when reading and
+ /* See the comments in png.h - this is the sub-image row when reading an
* interlaced image.
*/
if (png_ptr != NULL)
- return png_ptr->row_number;
+ {
+ /* In the read case png_struct::row_number is the row in the final image,
+ * not the pass, this will return the previous row number if the row isn't
+ * in the pass:
+ */
+ if (png_ptr->read_struct)
+ return PNG_PASS_ROWS(png_ptr->row_number+1, png_ptr->pass)-1U;
+
+ else
+ return png_ptr->row_number;
+ }
return PNG_UINT_32_MAX; /* help the app not to fail silently */
}
@@ -837,7 +3499,262 @@ png_get_current_pass_number(png_const_structrp png_ptr)
return png_ptr->pass;
return 8; /* invalid */
}
-#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */
-#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED ||
- PNG_WRITE_USER_TRANSFORM_SUPPORTED */
-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
+#endif /* USER_TRANSFORM_INFO */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) ||\
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+typedef struct
+{
+ png_transform tr;
+ png_user_transform_ptr user_fn;
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+ png_voidp user_ptr;
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ unsigned int user_depth;
+ unsigned int user_channels;
+#endif
+#endif
+} png_user_transform, *png_user_transformp;
+
+typedef const png_user_transform *png_const_user_transformp;
+
+static png_user_transformp
+get_user_transform(png_structrp png_ptr)
+{
+ /* Note that in an added transform the whole transform is memset to 0, so we
+ * don't need to initialize anything.
+ */
+ return png_transform_cast(png_user_transform, png_add_transform(png_ptr,
+ sizeof (png_user_transform), NULL/*function*/, PNG_TR_USER));
+}
+#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
+
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+png_voidp PNGAPI
+png_get_user_transform_ptr(png_const_structrp png_ptr)
+{
+ if (png_ptr != NULL)
+ {
+ png_transformp tr = png_find_transform(png_ptr, PNG_TR_USER);
+
+ if (tr != NULL)
+ {
+ png_user_transformp tru = png_transform_cast(png_user_transform, tr);
+ return tru->user_ptr;
+ }
+ }
+
+ return NULL;
+}
+#endif /* USER_TRANSFORM_PTR */
+
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+void PNGAPI
+png_set_user_transform_info(png_structrp png_ptr, png_voidp ptr, int depth,
+ int channels)
+{
+ if (png_ptr != NULL)
+ {
+ /* NOTE: this function only sets the user transform pointer on write, i.e.
+ * the depth and channels arguments are ignored.
+ */
+ png_user_transformp tr = get_user_transform(png_ptr);
+
+ tr->user_ptr = ptr;
+
+# ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ if (png_ptr->read_struct)
+ {
+ if (png_ptr->row_bit_depth == 0)
+ {
+ if (depth > 0 && depth <= 32 && channels > 0 && channels <= 4 &&
+ (-depth & depth) == depth /* power of 2 */)
+ {
+ tr->user_depth = png_check_bits(png_ptr, depth, 6);
+ tr->user_channels = png_check_bits(png_ptr, channels, 3);
+ }
+
+ else
+ png_app_error(png_ptr, "unsupported bit-depth or channels");
+ }
+ else
+ png_app_error(png_ptr,
+ "cannot change user info after image start");
+ }
+# else /* !READ_USER_TRANSFORM */
+ PNG_UNUSED(depth)
+ PNG_UNUSED(channels)
+# endif /* !READ_USER_TRANSFORM */
+ }
+}
+#endif /* USER_TRANSFORM_PTR */
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+static void
+png_do_read_user_transform(png_transformp *trIn, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
+
+ if (!tc->init && tr->user_fn != NULL)
+ {
+ png_row_info row_info;
+
+ row_info.width = tc->width;
+ row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
+ row_info.color_type = png_check_byte(png_ptr,
+ PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
+ row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
+ row_info.channels = png_check_byte(png_ptr,
+ PNG_FORMAT_CHANNELS(tc->format));
+ row_info.bit_depth = png_check_byte(png_ptr,
+ PNG_TC_PIXEL_DEPTH(*tc));
+
+ /* TODO: fix this API, but for the moment we have to copy the row data to
+ * the working buffer. This is an unnecessary perf overhead when a user
+ * transform is used to read information without a transform or when it is
+ * used on write.
+ */
+ if (tc->sp != tc->dp)
+ {
+ memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
+ tc->sp = tc->dp;
+ }
+
+ tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
+ }
+
+# ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+ if (tr->user_depth > 0)
+ {
+ /* The read transform can modify the bit depth and number of
+ * channels; the interface doesn't permit anything else to be
+ * changed. If the information isn't set the user callback has to
+ * produce pixels with the correct pixel depth (otherwise the
+ * de-interlace won't work) but there really is no other constraint.
+ */
+ tc->bit_depth = tr->user_depth;
+
+ /* The API is very restricted in functionality; the user_channels
+ * can be changed, but the color_type can't, so the format is simply
+ * fixed up to match the channels.
+ */
+ if (tr->user_channels != PNG_FORMAT_CHANNELS(tc->format))
+ switch (tr->user_channels)
+ {
+ case 1:
+ tc->format &=
+ PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
+ break;
+
+ case 2: /* has to be GA */
+ tc->format &=
+ PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR);
+ tc->format |= PNG_FORMAT_FLAG_ALPHA;
+ break;
+
+ case 3: /* has to be RGB */
+ tc->format &=
+ PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA);
+ tc->format |= PNG_FORMAT_FLAG_COLOR;
+ break;
+
+ case 4: /* has to be RGBA */
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP);
+ tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
+ break;
+
+ default: /* checked before */
+ impossible("user channels");
+ }
+
+ debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels);
+ }
+# endif /* USER_TRANSFORM_PTR */
+# undef png_ptr
+}
+
+void PNGAPI
+png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
+ read_user_transform_fn)
+{
+ /* There is no 'init' function, just the callback above to handle the
+ * transform.
+ */
+ if (png_ptr != NULL)
+ {
+ if (png_ptr->read_struct)
+ {
+ png_user_transformp tr = get_user_transform(png_ptr);
+
+ tr->user_fn = read_user_transform_fn;
+ tr->tr.fn = png_do_read_user_transform;
+ }
+
+ else
+ png_app_error(png_ptr, "cannot set a read transform on write");
+ }
+}
+#endif /* READ_USER_TRANSFORM */
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+static void
+png_do_write_user_transform(png_transformp *trIn, png_transform_controlp tc)
+{
+# define png_ptr (tc->png_ptr)
+ /* The write side is pretty simple; call the call-back, it will make the row
+ * data right.
+ *
+ * BUG: we need to copy the input row (it would be a non-quiet API change
+ * otherwise) and we don't know how big it is because the information passed
+ * to png_set_user_transform_info never was used on write, but that's fine
+ * because the write code never did get this right, so presumably all the
+ * apps have worked round it...
+ */
+ if (!tc->init)
+ {
+ png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
+ png_row_info row_info;
+
+ if (tc->sp != tc->dp) /* no interlace */
+ {
+ memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
+ tc->sp = tc->dp;
+ }
+
+ row_info.width = tc->width;
+ row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
+ row_info.color_type = png_check_byte(png_ptr,
+ PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
+ row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
+ row_info.channels = png_check_byte(png_ptr,
+ PNG_FORMAT_CHANNELS(tc->format));
+ row_info.bit_depth = png_check_byte(png_ptr,
+ PNG_TC_PIXEL_DEPTH(*tc));
+
+ /* The user function promises to give us this format: */
+ tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
+ }
+# undef png_ptr
+}
+
+void PNGAPI
+png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
+ write_user_transform_fn)
+{
+
+ if (png_ptr != NULL)
+ {
+ if (!png_ptr->read_struct)
+ {
+ png_user_transformp tr = get_user_transform(png_ptr);
+
+ tr->user_fn = write_user_transform_fn;
+ tr->tr.fn = png_do_write_user_transform;
+ }
+
+ else
+ png_app_error(png_ptr, "cannot set a write transform on read");
+ }
+}
+#endif /* WRITE_USER_TRANSFORM */
diff --git a/ext/libpng17/pngwio.c b/ext/libpng17/pngwio.c
index 1b525f9484..430cb29e56 100644
--- a/ext/libpng17/pngwio.c
+++ b/ext/libpng17/pngwio.c
@@ -1,8 +1,8 @@
/* pngwio.c - functions for data output
*
- * Last changed in libpng 1.6.9 [February 6, 2014]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -19,6 +19,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngwio
#ifdef PNG_WRITE_SUPPORTED
@@ -30,15 +31,24 @@
*/
void /* PRIVATE */
-png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length)
+png_write_data(png_structrp png_ptr, png_const_voidp data, png_size_t length)
{
- /* NOTE: write_data_fn must not change the buffer! */
- if (png_ptr->write_data_fn != NULL )
- (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
- length);
+ /* This was guaranteed by prior versions of libpng, so app callbacks may
+ * assume it even though it isn't documented to be the case.
+ */
+ debug(length > 0U);
+
+ /* NOTE: write_data_fn must not change the buffer!
+ * This cast is required because of the API; changing the type of the
+ * callback would require every app to change the callback and that change
+ * would have to be conditional on the libpng version.
+ */
+ if (png_ptr->rw_data_fn != NULL )
+ png_ptr->rw_data_fn(png_ptr,
+ png_constcast(png_bytep,png_voidcast(png_const_bytep,data)), length);
else
- png_error(png_ptr, "Call to NULL write function");
+ png_app_error(png_ptr, "No write function");
}
#ifdef PNG_STDIO_SUPPORTED
@@ -55,36 +65,22 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
if (png_ptr == NULL)
return;
- check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+ check = fwrite(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
if (check != length)
png_error(png_ptr, "Write Error");
}
#endif
-/* This function is called to output any data pending writing (normally
- * to disk). After png_flush is called, there should be no data pending
- * writing in any buffers.
- */
#ifdef PNG_WRITE_FLUSH_SUPPORTED
-void /* PRIVATE */
-png_flush(png_structrp png_ptr)
-{
- if (png_ptr->output_flush_fn != NULL)
- (*(png_ptr->output_flush_fn))(png_ptr);
-}
-
# ifdef PNG_STDIO_SUPPORTED
void PNGCBAPI
png_default_flush(png_structp png_ptr)
{
- png_FILE_p io_ptr;
-
if (png_ptr == NULL)
return;
- io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));
- fflush(io_ptr);
+ fflush(png_voidcast(png_FILE_p, (png_ptr->io_ptr)));
}
# endif
#endif
@@ -125,44 +121,26 @@ png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,
if (png_ptr == NULL)
return;
- png_ptr->io_ptr = io_ptr;
-
-#ifdef PNG_STDIO_SUPPORTED
- if (write_data_fn != NULL)
- png_ptr->write_data_fn = write_data_fn;
-
- else
- png_ptr->write_data_fn = png_default_write_data;
-#else
- png_ptr->write_data_fn = write_data_fn;
-#endif
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-# ifdef PNG_STDIO_SUPPORTED
-
- if (output_flush_fn != NULL)
- png_ptr->output_flush_fn = output_flush_fn;
-
- else
- png_ptr->output_flush_fn = png_default_flush;
-
-# else
- png_ptr->output_flush_fn = output_flush_fn;
-# endif
-#else
- PNG_UNUSED(output_flush_fn)
-#endif /* PNG_WRITE_FLUSH_SUPPORTED */
-
-#ifdef PNG_READ_SUPPORTED
- /* It is an error to read while writing a png file */
- if (png_ptr->read_data_fn != NULL)
+ if (png_ptr->read_struct)
{
- png_ptr->read_data_fn = NULL;
-
- png_warning(png_ptr,
- "Can't set both read_data_fn and write_data_fn in the"
- " same structure");
+ png_app_error(png_ptr, "cannot set a write function on a read struct");
+ return;
}
-#endif
+
+ if (write_data_fn == NULL)
+ {
+ png_app_error(png_ptr,
+ "API change: png_set_write_fn requires a function");
+ return;
+ }
+
+ png_ptr->io_ptr = io_ptr;
+ png_ptr->rw_data_fn = write_data_fn;
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ if (output_flush_fn != NULL)
+ png_ptr->output_flush_fn = output_flush_fn;
+# else
+ PNG_UNUSED(output_flush_fn)
+# endif /* WRITE_FLUSH */
}
-#endif /* PNG_WRITE_SUPPORTED */
+#endif /* WRITE */
diff --git a/ext/libpng17/pngwrite.c b/ext/libpng17/pngwrite.c
index b43a16fc07..77d1de69c0 100644
--- a/ext/libpng17/pngwrite.c
+++ b/ext/libpng17/pngwrite.c
@@ -2,7 +2,7 @@
/* pngwrite.c - general routines to write a PNG file
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -11,15 +11,12 @@
* and license in png.h
*/
-#ifdef _MSC_VER
-#pragma warning (disable:4996)
-#pragma warning (disable:4244)
-#endif
-
#include "pngpriv.h"
-#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
+#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
# include
-#endif
+#endif /* SIMPLIFIED_WRITE_STDIO */
+
+#define PNG_SRC_FILE PNG_SRC_FILE_pngwrite
#ifdef PNG_WRITE_SUPPORTED
@@ -27,9 +24,9 @@
/* Write out all the unknown chunks for the current given location */
static void
write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
- unsigned int where)
+ unsigned int where)
{
- if (info_ptr->unknown_chunks_num)
+ if (info_ptr->unknown_chunks_num != 0)
{
png_const_unknown_chunkp up;
@@ -38,7 +35,7 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
for (up = info_ptr->unknown_chunks;
up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
++up)
- if (up->location & where)
+ if ((up->location & where) != 0)
{
/* If per-chunk unknown chunk handling is enabled use it, otherwise
* just write the chunks the application has set.
@@ -64,17 +61,80 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
(keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&
png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))
#endif
- {
- /* TODO: review, what is wrong with a zero length unknown chunk? */
- if (up->size == 0)
- png_warning(png_ptr, "Writing zero-length unknown chunk");
-
png_write_chunk(png_ptr, up->name, up->data, up->size);
- }
}
}
}
-#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
+#endif /* WRITE_UNKNOWN_CHUNKS */
+
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+static void
+png_write_text(png_structrp png_ptr, png_const_inforp info_ptr, png_byte where)
+ /* Text chunk helper */
+{
+ int i;
+
+ /* Check to see if we need to write text chunks */
+ for (i = 0; i < info_ptr->num_text; i++)
+ {
+ png_debug2(2, "Writing text chunk %d, type %d", i,
+ info_ptr->text[i].compression);
+
+ /* Text chunks are written at info_ptr->text[i].location, skip the chunk
+ * if we are not writing at that location:
+ */
+ if ((info_ptr->text[i].location & where) == 0U)
+ continue;
+
+ switch (info_ptr->text[i].compression)
+ {
+ case PNG_ITXT_COMPRESSION_NONE:
+ case PNG_ITXT_COMPRESSION_zTXt:
+# ifdef PNG_WRITE_iTXt_SUPPORTED
+ /* Write international chunk */
+ png_write_iTXt(png_ptr, info_ptr->text[i].compression,
+ info_ptr->text[i].key, info_ptr->text[i].lang,
+ info_ptr->text[i].lang_key, info_ptr->text[i].text);
+# else /* !WRITE_iTXT */
+ png_app_error(png_ptr, "Unable to write international text");
+# endif /* !WRITE_iTXT */
+ break;
+
+ case PNG_TEXT_COMPRESSION_zTXt:
+# ifdef PNG_WRITE_zTXt_SUPPORTED
+ /* Write compressed chunk */
+ png_write_zTXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text, info_ptr->text[i].compression);
+# else /* !WRITE_zTXT */
+ png_app_error(png_ptr, "Unable to write compressed text");
+# endif /* !WRITE_zTXT */
+ break;
+
+ case PNG_TEXT_COMPRESSION_NONE:
+# ifdef PNG_WRITE_tEXt_SUPPORTED
+ /* Write uncompressed chunk */
+ png_write_tEXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text, 0);
+# else /* !WRITE_tEXt */
+ /* Can't get here TODO: why not? */
+ png_app_error(png_ptr, "Unable to write uncompressed text");
+# endif /* !WRITE_tEXt */
+ break;
+
+ default:
+ /* This is an internal error because the libpng checking should
+ * never manage to set any 'compression' except the above values.
+ */
+ impossible("invalid text compression");
+ }
+
+ /* The chunk was written, record where. This allows the location to have
+ * multiple bits set; the first successful write freezes the location.
+ */
+ info_ptr->text[i].location = where;
+ }
+}
+#endif /* WRITE_TEXT */
/* Writes all the PNG information. This is the suggested way to use the
* library. If you have a new chunk to add, make a function to write it,
@@ -93,257 +153,240 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
if (png_ptr == NULL || info_ptr == NULL)
return;
- if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+ if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
{
- /* Write PNG signature */
- png_write_sig(png_ptr);
+ int color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format);
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
- (png_ptr->mng_features_permitted))
- {
- png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
- png_ptr->mng_features_permitted = 0;
- }
-#endif
+ /* Write PNG signature; doesn't set PNG_HAVE_PNG_SIGNATURE if it has
+ * already been written (or rather, if at least 3 bytes have already been
+ * written; undocumented wackiness, it means the 'PNG' at the start can be
+ * replace by, e.g. "FOO" or "BAR" or "MNG").
+ */
+ png_write_sig(png_ptr);
- /* Write IHDR information. */
- png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
- info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
- info_ptr->filter_type,
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- info_ptr->interlace_type
-#else
- 0
-#endif
- );
+# ifdef PNG_MNG_FEATURES_SUPPORTED
+ if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 &&
+ png_ptr->mng_features_permitted != 0)
+ {
+ png_app_error(png_ptr,
+ "MNG features are not allowed in a PNG datastream");
+ /* Recovery: disable MNG features: */
+ png_ptr->mng_features_permitted = 0;
+ }
+# endif /* MNG_FEATURES */
- /* The rest of these check to see if the valid field has the appropriate
- * flag set, and if it does, writes the chunk.
- *
- * 1.6.0: COLORSPACE support controls the writing of these chunks too, and
- * the chunks will be written if the WRITE routine is there and information
- * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c
- * for where the valid flags get set.)
- *
- * Under certain circumstances the colorspace can be invalidated without
- * syncing the info_struct 'valid' flags; this happens if libpng detects and
- * error and calls png_error while the color space is being set, yet the
- * application continues writing the PNG. So check the 'invalid' flag here
- * too.
- */
-#ifdef PNG_GAMMA_SUPPORTED
-# ifdef PNG_WRITE_gAMA_SUPPORTED
- if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) &&
- (info_ptr->valid & PNG_INFO_gAMA))
- png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
-# endif
-#endif
+ /* Write IHDR information. */
+ png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+ info_ptr->bit_depth, color_type, info_ptr->compression_type,
+ info_ptr->filter_type, info_ptr->interlace_type);
-#ifdef PNG_COLORSPACE_SUPPORTED
- /* Write only one of sRGB or an ICC profile. If a profile was supplied
- * and it matches one of the known sRGB ones issue a warning.
- */
-# ifdef PNG_WRITE_iCCP_SUPPORTED
- if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
- (info_ptr->valid & PNG_INFO_iCCP))
- {
+# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ /* This are used for checking later on: */
+ png_ptr->info_format = info_ptr->format;
+# endif /* WRITE_TRANSFORMS */
+
+ /* This sets the flag that prevents re-entry to the 'before PLTE' case: */
+ affirm((png_ptr->mode & PNG_HAVE_IHDR) != 0);
+
+ /* The rest of these check to see if the valid field has the appropriate
+ * flag set, and if it does, writes the chunk.
+ *
+ * 1.6.0: COLORSPACE support controls the writing of these chunks too, and
+ * the chunks will be written if the WRITE routine is there and
+ * information is available in the COLORSPACE. (See
+ * png_colorspace_sync_info in png.c for where the valid flags get set.)
+ *
+ * Under certain circumstances the colorspace can be invalidated without
+ * syncing the info_struct 'valid' flags; this happens if libpng detects
+ * an error and calls png_error while the color space is being set, yet
+ * the application continues writing the PNG. So check the 'invalid'
+ * flag here too.
+ */
+# ifdef PNG_WRITE_tIME_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
+ (info_ptr->time_location & PNG_HAVE_IHDR) != 0)
+ png_write_tIME(png_ptr, &(info_ptr->mod_time));
+# endif /* WRITE_tIME */
+
+# ifdef PNG_WRITE_gAMA_SUPPORTED /* enables GAMMA */
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
+ (info_ptr->valid & PNG_INFO_gAMA) != 0)
+ {
+ /* This is the inverse of the test in png.c: */
+ affirm(info_ptr->colorspace.gamma >= 16 &&
+ info_ptr->colorspace.gamma <= 625000000);
+ png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
+ }
+# endif /* WRITE_gAMA */
+
+ /* Write only one of sRGB or an ICC profile. If a profile was supplied
+ * and it matches one of the known sRGB ones issue a warning.
+ */
+# ifdef PNG_WRITE_iCCP_SUPPORTED /* enables COLORSPACE, GAMMA */
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+ (info_ptr->valid & PNG_INFO_iCCP) != 0)
+ {
+# ifdef PNG_WRITE_sRGB_SUPPORTED
+ /* The app must have supplied an sRGB iCCP profile (and one that
+ * is recognized and therefore known to be correct) so we write
+ * that profile, even though it increases the size of the PNG
+ * significantly. A warning is reasonable:
+ */
+ if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
+ png_app_warning(png_ptr,
+ "profile matches sRGB but writing iCCP instead");
+# endif /* WRITE_sRGB */
+
+ png_write_iCCP(png_ptr, info_ptr->iccp_name,
+ info_ptr->iccp_profile);
+ }
# ifdef PNG_WRITE_sRGB_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sRGB)
- png_app_warning(png_ptr,
- "profile matches sRGB but writing iCCP instead");
-# endif
+ else /* iCCP not written */
+# endif /* WRITE_sRGB */
+# endif /* WRITE_iCCP */
- png_write_iCCP(png_ptr, info_ptr->iccp_name,
- info_ptr->iccp_profile);
- }
-# ifdef PNG_WRITE_sRGB_SUPPORTED
- else
+# ifdef PNG_WRITE_sRGB_SUPPORTED /* enables COLORSPACE, GAMMA */
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+ (info_ptr->valid & PNG_INFO_sRGB) != 0)
+ png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
+# endif /* WRITE_sRGB */
+
+# ifdef PNG_WRITE_sBIT_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
+ png_write_sBIT(png_ptr, &(info_ptr->sig_bit), color_type);
+# endif /* WRITE_sBIT */
+
+# ifdef PNG_WRITE_cHRM_SUPPORTED /* enables COLORSPACE */
+ if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+ (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 &&
+ (info_ptr->valid & PNG_INFO_cHRM) != 0)
+ png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
+# endif /* WRITE_cHRM */
+
+# ifdef PNG_WRITE_TEXT_SUPPORTED
+ if (info_ptr->num_text > 0)
+ png_write_text(png_ptr, info_ptr, PNG_HAVE_IHDR);
+# endif /* WRITE_TEXT */
+
+# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ /* The third arugment must encode only one bit, otherwise chunks will
+ * be written twice because the test in write_unknown_chunks is
+ * 'location & where'.
+ */
+ write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
# endif
-# endif
-
-# ifdef PNG_WRITE_sRGB_SUPPORTED
- if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
- (info_ptr->valid & PNG_INFO_sRGB))
- png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
-# endif /* WRITE_sRGB */
-#endif /* COLORSPACE */
-
-#ifdef PNG_WRITE_sBIT_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sBIT)
- png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
-#endif
-
-#ifdef PNG_COLORSPACE_SUPPORTED
-# ifdef PNG_WRITE_cHRM_SUPPORTED
- if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
- (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) &&
- (info_ptr->valid & PNG_INFO_cHRM))
- png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
-# endif
-#endif
-
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
- write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
-#endif
-
- png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
}
+
+ else /* 1.7.0: flag multiple calls; previously ignored */
+ png_app_error(png_ptr,
+ "png_write_info_before_PLTE called more than once");
}
void PNGAPI
png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
{
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
- int i;
-#endif
-
png_debug(1, "in png_write_info");
if (png_ptr == NULL || info_ptr == NULL)
return;
- png_write_info_before_PLTE(png_ptr, info_ptr);
+ if ((png_ptr->mode & (PNG_HAVE_PLTE+PNG_HAVE_IDAT)) != 0)
+ {
+ png_app_error(png_ptr, "late call to png_write_info");
+ return;
+ }
- if (info_ptr->valid & PNG_INFO_PLTE)
- png_write_PLTE(png_ptr, info_ptr->palette,
- (png_uint_32)info_ptr->num_palette);
+ /* The app may do this for us, and in 1.7.0 multiple calls are flagged as an
+ * application error, so this code must check:
+ */
+ if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+ png_write_info_before_PLTE(png_ptr, info_ptr);
- else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ if ((info_ptr->valid & PNG_INFO_PLTE) != 0)
+ png_write_PLTE(png_ptr, info_ptr->palette, info_ptr->num_palette);
+
+ /* Validate the consistency of the PNG being produced; a palette must have
+ * been written if a palette mapped PNG is to be valid:
+ */
+ if ((png_ptr->mode & PNG_HAVE_PLTE) == 0 &&
+ png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
png_error(png_ptr, "Valid palette required for paletted images");
-#ifdef PNG_WRITE_tRNS_SUPPORTED
- if (info_ptr->valid & PNG_INFO_tRNS)
- {
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
- /* Invert the alpha channel (in tRNS) */
- if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
- info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ /* But always set the mode flag because without this we don't know when to
+ * write the post-palette text or unknown chunks.
+ */
+ png_ptr->mode |= PNG_HAVE_PLTE;
+
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_tRNS) !=0)
{
- int j, jend;
-
- jend = info_ptr->num_trans;
- if (jend > PNG_MAX_PALETTE_LENGTH)
- jend = PNG_MAX_PALETTE_LENGTH;
-
- for (j = 0; jtrans_alpha[j] =
- (png_byte)(255 - info_ptr->trans_alpha[j]);
+ png_write_tRNS(png_ptr, info_ptr->trans_alpha,
+ &(info_ptr->trans_color), info_ptr->num_trans,
+ PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format));
}
-#endif
- png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
- info_ptr->num_trans, info_ptr->color_type);
- }
-#endif
-#ifdef PNG_WRITE_bKGD_SUPPORTED
- if (info_ptr->valid & PNG_INFO_bKGD)
- png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
-#endif
+# endif /* WRITE_tRNS */
-#ifdef PNG_WRITE_hIST_SUPPORTED
- if (info_ptr->valid & PNG_INFO_hIST)
- png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
-#endif
+# ifdef PNG_WRITE_bKGD_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_bKGD) != 0)
+ png_write_bKGD(png_ptr, &(info_ptr->background),
+ PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format));
+# endif /* WRITE_bKGD */
-#ifdef PNG_WRITE_oFFs_SUPPORTED
- if (info_ptr->valid & PNG_INFO_oFFs)
- png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
- info_ptr->offset_unit_type);
-#endif
+# ifdef PNG_WRITE_hIST_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_hIST) != 0)
+ png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+# endif /* WRITE_hIST */
-#ifdef PNG_WRITE_pCAL_SUPPORTED
- if (info_ptr->valid & PNG_INFO_pCAL)
- png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
- info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
- info_ptr->pcal_units, info_ptr->pcal_params);
-#endif
+# ifdef PNG_WRITE_oFFs_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_oFFs) != 0)
+ png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+ info_ptr->offset_unit_type);
+# endif /* WRITE_oFFs */
-#ifdef PNG_WRITE_sCAL_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sCAL)
- png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
- info_ptr->scal_s_width, info_ptr->scal_s_height);
-#endif /* sCAL */
+# ifdef PNG_WRITE_pCAL_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_pCAL) != 0)
+ png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+ info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+ info_ptr->pcal_units, info_ptr->pcal_params);
+# endif /* WRITE_pCAL */
-#ifdef PNG_WRITE_pHYs_SUPPORTED
- if (info_ptr->valid & PNG_INFO_pHYs)
- png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
- info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
-#endif /* pHYs */
+# ifdef PNG_WRITE_sCAL_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_sCAL) != 0)
+ png_write_sCAL_s(png_ptr, info_ptr->scal_unit, info_ptr->scal_s_width,
+ info_ptr->scal_s_height);
+# endif /* WRITE_sCAL */
-#ifdef PNG_WRITE_tIME_SUPPORTED
- if (info_ptr->valid & PNG_INFO_tIME)
- {
- png_write_tIME(png_ptr, &(info_ptr->mod_time));
- png_ptr->mode |= PNG_WROTE_tIME;
- }
-#endif /* tIME */
+# ifdef PNG_WRITE_pHYs_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_pHYs) != 0)
+ png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+ info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+# endif /* WRITE_pHYs */
-#ifdef PNG_WRITE_sPLT_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sPLT)
- for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
- png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
-#endif /* sPLT */
+# ifdef PNG_WRITE_tIME_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
+ (info_ptr->time_location & PNG_HAVE_PLTE) != 0)
+ png_write_tIME(png_ptr, &(info_ptr->mod_time));
+# endif /* WRITE_tIME */
-#ifdef PNG_WRITE_TEXT_SUPPORTED
- /* Check to see if we need to write text chunks */
- for (i = 0; i < info_ptr->num_text; i++)
- {
- png_debug2(2, "Writing header text chunk %d, type %d", i,
- info_ptr->text[i].compression);
- /* An internationalized chunk? */
- if (info_ptr->text[i].compression > 0)
+# ifdef PNG_WRITE_sPLT_SUPPORTED
+ if ((info_ptr->valid & PNG_INFO_sPLT) != 0)
{
-#ifdef PNG_WRITE_iTXt_SUPPORTED
- /* Write international chunk */
- png_write_iTXt(png_ptr,
- info_ptr->text[i].compression,
- info_ptr->text[i].key,
- info_ptr->text[i].lang,
- info_ptr->text[i].lang_key,
- info_ptr->text[i].text);
-#else
- png_warning(png_ptr, "Unable to write international text");
-#endif
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
- }
+ int i;
- /* If we want a compressed text chunk */
- else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
- {
-#ifdef PNG_WRITE_zTXt_SUPPORTED
- /* Write compressed chunk */
- png_write_zTXt(png_ptr, info_ptr->text[i].key,
- info_ptr->text[i].text, 0,
- info_ptr->text[i].compression);
-#else
- png_warning(png_ptr, "Unable to write compressed text");
-#endif
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+ for (i = 0; i < info_ptr->splt_palettes_num; i++)
+ png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
}
+# endif /* WRITE_sPLT */
- else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
- {
-#ifdef PNG_WRITE_tEXt_SUPPORTED
- /* Write uncompressed chunk */
- png_write_tEXt(png_ptr, info_ptr->text[i].key,
- info_ptr->text[i].text,
- 0);
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
-#else
- /* Can't get here */
- png_warning(png_ptr, "Unable to write uncompressed text");
-#endif
- }
- }
-#endif /* tEXt */
+# ifdef PNG_WRITE_TEXT_SUPPORTED
+ if (info_ptr->num_text > 0)
+ png_write_text(png_ptr, info_ptr, PNG_HAVE_PLTE);
+# endif /* WRITE_TEXT */
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
- write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
-#endif
+# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
+# endif /* WRITE_UNKNOWN_CHUNKS */
}
/* Writes the end of the PNG file. If you don't want to write comments or
@@ -359,89 +402,76 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr)
if (png_ptr == NULL)
return;
- if (!(png_ptr->mode & PNG_HAVE_IDAT))
- png_error(png_ptr, "No IDATs written into file");
+ if ((png_ptr->mode &
+ (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT+PNG_HAVE_IEND)) !=
+ (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT))
+ {
+ /* Out of place png_write_end: */
+ if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+ png_error(png_ptr, "Missing call to png_write_info");
-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
- if (png_ptr->num_palette_max > png_ptr->num_palette)
- png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
-#endif
+ else if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 && png_ptr->zowner == 0)
+ {
+ /* TODO: write unknown IDAT here, for the moment allow the app to write
+ * IDAT then call write_end:
+ */
+ png_app_error(png_ptr, "No IDATs written into file");
+ png_ptr->mode |= PNG_HAVE_IDAT+PNG_AFTER_IDAT;
+ }
+
+ else if ((png_ptr->mode & PNG_AFTER_IDAT) == 0)
+ {
+ affirm(png_ptr->zowner == png_IDAT);
+ png_error(png_ptr, "incomplete PNG image"); /* unrecoverable */
+ }
+
+ else if ((png_ptr->mode & PNG_HAVE_IEND) != 0)
+ {
+ png_app_error(png_ptr, "multiple calls to png_write_end");
+ return;
+ }
+
+ else
+ impossible("not reached");
+ }
+
+ /* And double check that the image rows were all written; this is actually
+ * a harmless error on an interlaced image because the image rows with
+ * data were all passed in or the above check would not work.
+ *
+ * Don't do this if the IDAT came from unknowns (TBD) or the app, above.
+ *
+ * The check depends on the precise logic in png_write_row.
+ */
+ else if (png_ptr->pass != 7U)
+ png_app_error(png_ptr, "png_write_row not called to last row");
+
+ else
+ debug(png_ptr->row_number == 0U);
/* See if user wants us to write information chunks */
if (info_ptr != NULL)
{
-#ifdef PNG_WRITE_TEXT_SUPPORTED
- int i; /* local index variable */
-#endif
-#ifdef PNG_WRITE_tIME_SUPPORTED
- /* Check to see if user has supplied a time chunk */
- if ((info_ptr->valid & PNG_INFO_tIME) &&
- !(png_ptr->mode & PNG_WROTE_tIME))
- png_write_tIME(png_ptr, &(info_ptr->mod_time));
+# ifdef PNG_WRITE_tIME_SUPPORTED
+ /* Check to see if user has supplied a time chunk */
+ if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
+ (info_ptr->time_location & PNG_AFTER_IDAT) != 0)
+ png_write_tIME(png_ptr, &(info_ptr->mod_time));
+# endif
-#endif
-#ifdef PNG_WRITE_TEXT_SUPPORTED
- /* Loop through comment chunks */
- for (i = 0; i < info_ptr->num_text; i++)
- {
- png_debug2(2, "Writing trailer text chunk %d, type %d", i,
- info_ptr->text[i].compression);
- /* An internationalized chunk? */
- if (info_ptr->text[i].compression > 0)
- {
-#ifdef PNG_WRITE_iTXt_SUPPORTED
- /* Write international chunk */
- png_write_iTXt(png_ptr,
- info_ptr->text[i].compression,
- info_ptr->text[i].key,
- info_ptr->text[i].lang,
- info_ptr->text[i].lang_key,
- info_ptr->text[i].text);
-#else
- png_warning(png_ptr, "Unable to write international text");
-#endif
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
- }
+# ifdef PNG_WRITE_TEXT_SUPPORTED
+ if (info_ptr->num_text > 0)
+ png_write_text(png_ptr, info_ptr, PNG_AFTER_IDAT);
+# endif /* WRITE_TEXT */
- else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
- {
-#ifdef PNG_WRITE_zTXt_SUPPORTED
- /* Write compressed chunk */
- png_write_zTXt(png_ptr, info_ptr->text[i].key,
- info_ptr->text[i].text, 0,
- info_ptr->text[i].compression);
-#else
- png_warning(png_ptr, "Unable to write compressed text");
-#endif
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
- }
-
- else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
- {
-#ifdef PNG_WRITE_tEXt_SUPPORTED
- /* Write uncompressed chunk */
- png_write_tEXt(png_ptr, info_ptr->text[i].key,
- info_ptr->text[i].text, 0);
-#else
- png_warning(png_ptr, "Unable to write uncompressed text");
-#endif
-
- /* Mark this chunk as written */
- info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
- }
- }
-#endif
-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
- write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
-#endif
+# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
+# endif
}
- png_ptr->mode |= PNG_AFTER_IDAT;
-
/* Write end of PNG file */
png_write_IEND(png_ptr);
+
/* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
* and restored again in libpng-1.2.30, may cause some applications that
* do not set png_ptr->output_flush_fn to crash. If your application
@@ -449,11 +479,12 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr)
* PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
* png-mng-implement at lists.sf.net .
*/
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
- png_flush(png_ptr);
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
+ if (png_ptr->output_flush_fn != NULL)
+ png_ptr->output_flush_fn(png_ptr);
+# endif
# endif
-#endif
}
#ifdef PNG_CONVERT_tIME_SUPPORTED
@@ -462,12 +493,12 @@ png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
{
png_debug(1, "in png_convert_from_struct_tm");
- ptime->year = (png_uint_16)(1900 + ttime->tm_year);
- ptime->month = (png_byte)(ttime->tm_mon + 1);
- ptime->day = (png_byte)ttime->tm_mday;
- ptime->hour = (png_byte)ttime->tm_hour;
- ptime->minute = (png_byte)ttime->tm_min;
- ptime->second = (png_byte)ttime->tm_sec;
+ ptime->year = png_check_u16(0/*TODO: fixme*/, 1900 + ttime->tm_year);
+ ptime->month = png_check_byte(0/*TODO: fixme*/, ttime->tm_mon + 1);
+ ptime->day = png_check_byte(0/*TODO: fixme*/, ttime->tm_mday);
+ ptime->hour = png_check_byte(0/*TODO: fixme*/, ttime->tm_hour);
+ ptime->minute = png_check_byte(0/*TODO: fixme*/, ttime->tm_min);
+ ptime->second = png_check_byte(0/*TODO: fixme*/, ttime->tm_sec);
}
void PNGAPI
@@ -489,7 +520,7 @@ png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
{
#ifndef PNG_USER_MEM_SUPPORTED
png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
- error_fn, warn_fn, NULL, NULL, NULL);
+ error_fn, warn_fn, NULL, NULL, NULL);
#else
return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
warn_fn, NULL, NULL, NULL);
@@ -502,86 +533,516 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
{
png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
- error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
-#endif /* PNG_USER_MEM_SUPPORTED */
+ error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
+#endif /* USER_MEM */
if (png_ptr != NULL)
{
- /* Set the zlib control values to defaults; they can be overridden by the
- * application after the struct has been created.
- */
- png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
-
- /* The 'zlib_strategy' setting is irrelevant because png_default_claim in
- * pngwutil.c defaults it according to whether or not filters will be
- * used, and ignores this setting.
- */
- png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;
- png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;
- png_ptr->zlib_mem_level = 8;
- png_ptr->zlib_window_bits = 15;
- png_ptr->zlib_method = 8;
-
-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
- png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;
- png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;
- png_ptr->zlib_text_mem_level = 8;
- png_ptr->zlib_text_window_bits = 15;
- png_ptr->zlib_text_method = 8;
-#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
-
- /* This is a highly dubious configuration option; by default it is off,
- * but it may be appropriate for private builds that are testing
- * extensions not conformant to the current specification, or of
- * applications that must not fail to write at all costs!
- */
-#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
- png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
- /* In stable builds only warn if an application error can be completely
- * handled.
- */
-#endif
-
- /* App warnings are warnings in release (or release candidate) builds but
- * are errors during development.
- */
-#if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
- png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
-#endif
-
- /* TODO: delay this, it can be done in png_init_io() (if the app doesn't
- * do it itself) avoiding setting the default function if it is not
- * required.
- */
- png_set_write_fn(png_ptr, NULL, NULL, NULL);
+# ifdef PNG_BENIGN_ERRORS_SUPPORTED
+# if !PNG_RELEASE_BUILD
+ /* Always quit on error prior to release */
+ png_ptr->benign_error_action = PNG_ERROR;
+ png_ptr->app_warning_action = PNG_WARN;
+ png_ptr->app_error_action = PNG_ERROR;
+# else /* RELEASE_BUILD */
+ /* Allow benign errors on write, subject to app control. */
+# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
+ png_ptr->benign_error_action = PNG_WARN;
+ png_ptr->app_error_action = PNG_WARN;
+ png_ptr->app_warning_action = PNG_WARN;
+# else /* !BENIGN_WRITE_ERRORS */
+ /* libpng build without benign error support; the application
+ * author has to be assumed to be correct, so:
+ */
+ png_ptr->benign_error_action = PNG_ERROR;
+ png_ptr->app_warning_action = PNG_WARN;
+ png_ptr->app_error_action = PNG_ERROR;
+# endif /* !BENIGN_WRITE_ERRORS */
+# endif /* RELEASE_BUILD */
+# endif /* BENIGN_ERRORS */
}
return png_ptr;
}
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED) ||\
+ defined(PNG_WRITE_TRANSFORMS_SUPPORTED)
+static void
+write_row_buffered(png_structrp png_ptr,
+ png_const_bytep row, unsigned int row_info_flags,
+ void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer,
+ png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p),
+ unsigned int copy_parameter)
+{
+ unsigned int max_pixels = png_max_pixel_block(png_ptr);
+ const unsigned int pass = png_ptr->pass;
+ const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ?
+ png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass);
+ png_uint_32 x;
+ png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */
+
+ memset(prev_pixels, 0U, sizeof prev_pixels);
+
+ for (x = 0U; x < width; x += max_pixels)
+ {
+ union
+ {
+ PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
+ png_byte buffer[PNG_ROW_BUFFER_SIZE];
+ } pixel_buffer;
+
+ if (max_pixels > width - x)
+ max_pixels = (unsigned int)/*SAFE*/(width - x);
+
+ debug((row_info_flags & png_row_end) == 0U); /* must be set here at end */
+ if (x + max_pixels >= width)
+ row_info_flags |= png_row_end;
+
+ /* Copy a block of input pixels into the buffer, effecting the interlace
+ * on the way if required. The argument is the number of pixels in the
+ * buffer, not the number handled from the input which will be larger in
+ * the interlaced case.
+ */
+ copy_fn(png_ptr, pixel_buffer.buffer, row, x, max_pixels, copy_parameter);
+
+ /* Now pixel_buffer[0..max_pixels-1] contains max_pixels pixels which may
+ * need to be transformed (the interlace has already been handled).
+ */
+# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ if (png_ptr->transform_list != NULL)
+ {
+ png_transform_control tc;
+
+ /* The initial values are the memory format; this was worked out in
+ * png_init_row_info below.
+ */
+ memset(&tc, 0, sizeof tc);
+ tc.png_ptr = png_ptr;
+ tc.sp = tc.dp = pixel_buffer.buffer;
+
+ tc.width = max_pixels; /* width of block that we have */
+ tc.format = png_ptr->row_format;
+ tc.range = png_ptr->row_range;
+ tc.bit_depth = png_ptr->row_bit_depth;
+ /* tc.init == 0 */
+ /* tc.caching: not used */
+ /* tc.palette: not used */
+ debug(PNG_TC_PIXEL_DEPTH(tc) == png_ptr->row_input_pixel_depth);
+
+ /* Run the list. */
+ png_run_transform_list_backwards(png_ptr, &tc);
+
+ /* Make sure the format that resulted is compatible with PNG: */
+ affirm((tc.format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA +
+ PNG_FORMAT_FLAG_COLOR + PNG_FORMAT_FLAG_LINEAR +
+ PNG_FORMAT_FLAG_COLORMAP)) == 0);
+
+ /* Now we must have the PNG format from the IHDR: */
+ affirm(png_ptr->bit_depth == tc.bit_depth &&
+ png_ptr->color_type == PNG_COLOR_TYPE_FROM_FORMAT(tc.format));
+ }
+# endif /* WRITE_TRANSFORMS */
+
+ /* Call png_write_png_data to write this block of data, the test on
+ * maxpixels says if this is the final block in the row.
+ */
+ png_write_png_data(png_ptr, prev_pixels, pixel_buffer.buffer, x,
+ max_pixels, row_info_flags);
+ }
+}
+#endif /* WRITE { INTERLACING || TRANSFORMS } */
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+static void
+copy_row(png_const_structrp png_ptr, png_bytep row_buffer,
+ png_const_bytep row, png_uint_32 x, unsigned int count,
+ unsigned int pixel_depth)
+{
+ /* Copy row[x..x+count] pixels to row_buffer. */
+ png_copy_row(png_ptr, row_buffer, row, x, count, pixel_depth, 1/*clear*/,
+ 0/* x_in_dest; row[x]->row_buffer */);
+}
+#endif /* WRITE_TRANSFORMS */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+static void
+interlace_row_lbd(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp,
+ png_uint_32 x, unsigned int count, const unsigned int B)
+{
+ /* Pick out the correct pixels for the interlace pass. The basic idea here
+ * is to go through the row with a source pointer and a destination pointer
+ * (sp and dp), and copy the correct pixels for the pass. As the row gets
+ * compacted, sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+ const unsigned int pass = png_ptr->pass;
+ png_uint_32 i = PNG_COL_FROM_PASS_COL(x, pass);
+ const unsigned int inc = PNG_PASS_COL_OFFSET(pass);
+
+ /* For pixels less than one byte wide the correct pixels have to be
+ * extracted from the input bytes. Because we are reading data in
+ * the application memory format we cannot rely on the PNG big
+ * endian order. Notice that this was apparently broken before
+ * 1.7.0.
+ *
+ * In libpng 1.7.0 libpng uses a classic bit-pump to optimize the
+ * extraction. In all passes before the last (6/7) no two pixels
+ * are adjacent in the input, so we are always extracting 1 bit.
+ * At present the code uses an 8-bit buffer to avoid coding for
+ * different byte sexes, but this could easily be changed.
+ *
+ * 'i' is the bit-index of bit in the input (sp[]), so,
+ * considering the 1-bit per pixel case, sp[i>>3] is the byte
+ * and the bit is bit (i&7) (0 lowest) on swapped (little endian)
+ * data or 7-(i&7) on PNG default (big-endian) data.
+ *
+ * Define these macros, where:
+ *
+ * B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of
+ * the data; this should be a constant.
+ * sp: the source pointer (sp) (a png_const_bytep)
+ * i: the pixel index in the input (png_uint_32)
+ * j: the bit index in the output (unsigned int)
+ *
+ * Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts
+ * up, for MSB it counts down.
+ *
+ * NOTE: this could all be expanded to eliminate the code below by
+ * the time honoured copy'n'paste into three separate functions. This
+ * might be worth doing in the future.
+ */
+# define PIXEL_MASK ((1U << (1<>(3-(B))]) /* byte to use */
+# define SP_OFFSET_LSB ((BIT_MASK & i) << (B))
+# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B))
+# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK)
+ {
+ unsigned int j;
+ unsigned int d;
+
+ /* The data is always in the PNG, big-endian, format: */
+ for (j = 8U, d = 0U; count > 0U; --count, i += inc)
+ { /* big-endian */
+ j -= 1U<pass;
+ const unsigned int inc = PNG_PASS_COL_OFFSET(pass);
+
+ /* Loop through the input copying each pixel to the correct place
+ * in the output. Note that the loop may be executed 0 times if
+ * this is called on a narrow image that does not contain this
+ * pass.
+ */
+ for (sp += PNG_COL_FROM_PASS_COL(x, pass) * cbytes; count > 0;
+ --count, sp += inc * cbytes, dp += cbytes)
+ memcpy(dp, sp, cbytes);
+}
+#endif /* WRITE_INTERLACING */
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+static void
+write_row_core(png_structrp png_ptr, png_const_bytep row,
+ unsigned int row_info_flags)
+{
+# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ if (png_ptr->transform_list != NULL)
+ write_row_buffered(png_ptr, row, row_info_flags,
+ copy_row, png_ptr->row_input_pixel_depth);
+
+ else
+# endif /* WRITE_TRANSFORMS */
+
+ /* If control reaches this point the intermediate buffer is not required and
+ * the input data can be used unmodified.
+ */
+ png_write_png_rows(png_ptr, &row, 1U);
+ PNG_UNUSED(row_info_flags)
+}
+
+/* Write a single non-interlaced row. */
+static void
+write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row)
+{
+ const png_uint_32 row_number = png_ptr->row_number+1U;
+ /* There is only one pass, so this is the last pass: */
+ const unsigned int row_info_flags =
+ (row_number == 1U ? png_pass_first_row : 0) |
+ (row_number >= png_ptr->height ? png_pass_last_row : 0) |
+ png_pass_last;
+
+ debug(png_ptr->interlaced == PNG_INTERLACE_NONE);
+
+ write_row_core(png_ptr, row, row_info_flags);
+}
+
+/* Write a single interlaced row. */
+static void
+write_row_interlaced(png_structrp png_ptr, png_const_bytep row)
+{
+ const png_uint_32 row_number = png_ptr->row_number+1U;
+ const png_uint_32 height = png_ptr->height;
+ const unsigned int pass = png_ptr->pass;
+ const unsigned int row_info_flags =
+ (row_number == 1U ? png_pass_first_row : 0) |
+ (row_number == PNG_PASS_ROWS(height, pass) ? png_pass_last_row : 0) |
+ (pass == PNG_LAST_PASS(png_ptr->width, height) ? png_pass_last : 0);
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Check that libpng is not doing the interlace: */
+ debug(png_ptr->interlaced != PNG_INTERLACE_NONE &&
+ !png_ptr->do_interlace);
+# endif /* WRITE_INTERLACING */
+
+ write_row_core(png_ptr, row, row_info_flags);
+}
+#endif /* WRITE_TRANSFORMS */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+/* Interlace a row then write it out. */
+static void
+interlace_row(png_structrp png_ptr, png_const_bytep row)
+{
+ /* The row may not exist in the image (for this pass). */
+ const png_uint_32 row_number = png_ptr->row_number; /* in image */
+ const unsigned int pass = png_ptr->pass;
+
+ if (png_ptr->width > PNG_PASS_START_COL(pass) &&
+ PNG_ROW_IN_INTERLACE_PASS(row_number, pass))
+ {
+ const unsigned int row_info_flags =
+ (row_number == PNG_PASS_START_ROW(pass) ? png_pass_first_row : 0) |
+ (PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height) ?
+ png_pass_last_row : 0) |
+ (pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ?
+ png_pass_last : 0);
+
+ if (pass < 6)
+ {
+ /* Libpng is doing the interlacing and pixels need to be selected
+ * from the input row for this pass.
+ */
+ /* row interlacing uses either the log bit depth for low bit
+ * depth input or the byte count for 8bpp or bigger pixels.
+ */
+ const unsigned int input_depth = png_ptr->row_input_pixel_depth;
+ unsigned int B = 0; /* log2(input_depth) */
+
+ switch (input_depth)
+ {
+ case 4U: /* B will be 2 */
+ ++B;
+ /*FALL THROUGH*/
+ case 2U: /* B will be 1 */
+ ++B;
+ /*FALL THROUGH*/
+ case 1U: /* B will be 0 */
+ write_row_buffered(png_ptr, row, row_info_flags,
+ interlace_row_lbd, B);
+ break;
+
+ default: /* Parameter is the pixel size in bytes */
+ write_row_buffered(png_ptr, row, row_info_flags,
+ interlace_row_byte, input_depth >> 3);
+ break;
+ }
+ } /* pass < 6 */
+
+ else /* pass 6: no interlacing required */
+ write_row_core(png_ptr, row, row_info_flags);
+ }
+
+ else
+ {
+ /* This code must advance row_number/pass itself; the row has been
+ * skipped.
+ */
+ if (row_number+1U < png_ptr->height)
+ png_ptr->row_number = row_number+1U;
+
+ else
+ {
+ png_ptr->row_number = 0U;
+ png_ptr->pass = 0x7U & (pass+1U);
+ }
+ }
+}
+#endif /* WRITE_INTERLACING */
+
+/* Bottleneck API to actually write a number of rows, only exists because the
+ * rows parameter to png_write_rows is wrong.
+ */
+static void
+png_write_rows_internal(png_structrp png_ptr, png_const_bytep *rows,
+ png_uint_32 num_rows)
+{
+ if (png_ptr != NULL && num_rows > 0U && rows != NULL)
+ {
+ /* Unlike the read code initialization happens automatically: */
+ if (png_ptr->row_number == 0U && png_ptr->pass == 0U)
+ {
+ png_init_row_info(png_ptr);
+
+# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ /* If the app takes a png_info from a read operation and if the app has
+ * performed transforms on the data the png_info can contain IHDR
+ * information that cannot be represented in PNG. The code that writes
+ * the IHDR takes the color type from the png_info::format. The app
+ * adds transforms, before or after writing the IHDR, then the IHDR
+ * color_type stored in png_struct::color_type is used in
+ * png_init_row_info above to work out the actual row format.
+ *
+ * Prior to 1.7.0 this was not verified (there was no easy way to do
+ * so). Now we can check it here, however this is an:
+ *
+ * API CHANGE: in 1.7.0 an error may be flagged against bogus
+ * info_struct formats even though the app had removed them itself.
+ * It's just a warning at present.
+ *
+ * The test is that either the row_format produced by the write
+ * transforms exactly matches that in the original info_struct::format
+ * or that the info_struct::format was a simple mapping of the
+ * color_type that ended up in the IHDR:
+ */
+ if (png_ptr->row_format != png_ptr->info_format &&
+ PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) !=
+ png_ptr->info_format)
+ png_app_warning(png_ptr, "info_struct format does not match IHDR");
+# endif /* WRITE_TRANSFORMS */
+
+ /* Perform initialization required before IDATs are written. */
+ png_write_start_IDAT(png_ptr);
+ }
+
+ else if (png_ptr->pass >= 7U) /* too many calls; write already ended */
+ {
+ debug(png_ptr->row_number == 0U);
+ png_app_error(png_ptr, "Too many calls to png_write_row");
+ return;
+ }
+
+ /* The remainder of these tests detect internal errors in libpng */
+ else if (png_ptr->interlaced == PNG_INTERLACE_NONE)
+ affirm(png_ptr->row_number < png_ptr->height && png_ptr->pass == 0U);
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ else if (png_ptr->do_interlace)
+ affirm(png_ptr->row_number < png_ptr->height);
+# endif /* WRITE_INTERLACING */
+
+ else /* app does interlace */
+ affirm(
+ PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, png_ptr->pass) &&
+ png_ptr->row_number < PNG_PASS_ROWS(png_ptr->height, png_ptr->pass)
+ );
+
+ /* First handle rows that require buffering because of the need to
+ * interlace them or the need to perform write transforms.
+ */
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* libpng is doing the interlacing, but this only makes a difference to
+ * the first six passes (numbered, in libpng, 0..5); the seventh pass
+ * (numbered 6 by libpng) consists of complete image rows.
+ */
+ if (png_ptr->do_interlace) while (num_rows > 0U && png_ptr->pass < 6)
+ interlace_row(png_ptr, *rows++), --num_rows;
+# endif /* WRITE_INTERLACING */
+
+# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+ /* Transforms required however the row interlacing has already been
+ * handled and we have a complete (PNG) row.
+ */
+ if (png_ptr->transform_list != NULL)
+ {
+ if (png_ptr->interlaced == PNG_INTERLACE_NONE)
+ while (num_rows > 0U)
+ write_row_non_interlaced(png_ptr, *rows++), --num_rows;
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ else if (png_ptr->do_interlace)
+ while (num_rows > 0U)
+ interlace_row(png_ptr, *rows++), --num_rows;
+# endif /* WRITE_INTERLACING */
+
+ else /* app does the interlacing */
+ while (num_rows > 0U)
+ write_row_interlaced(png_ptr, *rows++), --num_rows;
+ }
+# endif /* WRITE_TRANSFORMS */
+
+ /* Finally handle any remaining rows that require no (libpng) interlace
+ * and no transforms.
+ */
+ if (num_rows > 0U)
+ png_write_png_rows(png_ptr, rows, num_rows);
+
+ /* Repeat the checks above, but allow for end-of-image. */
+ if (png_ptr->pass < 7U)
+ {
+ if (png_ptr->interlaced == PNG_INTERLACE_NONE)
+ affirm(png_ptr->row_number < png_ptr->height &&
+ png_ptr->pass == 0U);
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ else if (png_ptr->do_interlace)
+ affirm(png_ptr->row_number < png_ptr->height);
+# endif /* WRITE_INTERLACING */
+
+ else /* app does interlace */
+ affirm(PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height,
+ png_ptr->pass) &&
+ png_ptr->row_number <
+ PNG_PASS_ROWS(png_ptr->height, png_ptr->pass));
+ }
+ } /* png_ptr, rows, num_rows all valid */
+
+ else if (png_ptr != NULL)
+ png_app_warning(png_ptr, "Missing rows to row write API");
+}
+
+/* ROW WRITE APIs */
+/* Called by user to write a single row of image data */
+void PNGAPI
+png_write_row(png_structrp png_ptr, png_const_bytep row)
+{
+ png_debug(1, "in png_write_row");
+ png_write_rows_internal(png_ptr, &row, 1U);
+}
+
/* Write a few rows of image data. If the image is interlaced,
* either you will have to write the 7 sub images, or, if you
* have called png_set_interlace_handling(), you will have to
* "write" the image seven times.
*/
void PNGAPI
-png_write_rows(png_structrp png_ptr, png_bytepp row,
- png_uint_32 num_rows)
+png_write_rows(png_structrp png_ptr, png_bytepp rows, png_uint_32 num_rows)
{
- png_uint_32 i; /* row counter */
- png_bytepp rp; /* row pointer */
-
png_debug(1, "in png_write_rows");
- if (png_ptr == NULL)
- return;
-
- /* Loop through the rows */
- for (i = 0, rp = row; i < num_rows; i++, rp++)
- {
- png_write_row(png_ptr, *rp);
- }
+ if (png_ptr != NULL)
+ png_write_rows_internal(png_ptr, png_constcast(png_const_bytep*,rows),
+ num_rows);
}
/* Write the image. You only need to call this function once, even
@@ -590,342 +1051,31 @@ png_write_rows(png_structrp png_ptr, png_bytepp row,
void PNGAPI
png_write_image(png_structrp png_ptr, png_bytepp image)
{
- png_uint_32 i; /* row index */
- int pass, num_pass; /* pass variables */
- png_bytepp rp; /* points to current row */
-
- if (png_ptr == NULL)
- return;
-
png_debug(1, "in png_write_image");
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* Initialize interlace handling. If image is not interlaced,
- * this will set pass to 1
- */
- num_pass = png_set_interlace_handling(png_ptr);
-#else
- num_pass = 1;
-#endif
- /* Loop through passes */
- for (pass = 0; pass < num_pass; pass++)
+ if (png_ptr != NULL)
{
- /* Loop through image */
- for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+ int num_pass = 1;
+
+ /* The image is always an non-interlaced image. To write it as interlaced
+ * interlace handling must be present:
+ */
+ if (png_ptr->interlaced)
{
- png_write_row(png_ptr, *rp);
- }
- }
-}
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
-/* Performs intrapixel differencing */
-static void
-png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_write_intrapixel");
-
- if ((row_info->color_type & PNG_COLOR_MASK_COLOR))
- {
- int bytes_per_pixel;
- png_uint_32 row_width = row_info->width;
- if (row_info->bit_depth == 8)
- {
- png_bytep rp;
- png_uint_32 i;
-
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
- bytes_per_pixel = 3;
-
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- bytes_per_pixel = 4;
-
- else
- return;
-
- for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
- {
- *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff);
- *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff);
- }
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ num_pass = png_set_interlace_handling(png_ptr);
+# else /* !WRITE_INTERLACING */
+ /* There is no recovery because the IHDR has already been written.
+ */
+ png_error(png_ptr, "No interlace support");
+# endif /* !WRITE_INTERLACING */
}
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- else if (row_info->bit_depth == 16)
- {
- png_bytep rp;
- png_uint_32 i;
-
- if (row_info->color_type == PNG_COLOR_TYPE_RGB)
- bytes_per_pixel = 6;
-
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- bytes_per_pixel = 8;
-
- else
- return;
-
- for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
- {
- png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1);
- png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3);
- png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5);
- png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL);
- png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
- *(rp ) = (png_byte)((red >> 8) & 0xff);
- *(rp + 1) = (png_byte)(red & 0xff);
- *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
- *(rp + 5) = (png_byte)(blue & 0xff);
- }
- }
-#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ /* And write the whole thing, 7 times if interlacing it: */
+ for (; num_pass > 0; --num_pass)
+ png_write_rows(png_ptr, image, png_ptr->height);
}
}
-#endif /* PNG_MNG_FEATURES_SUPPORTED */
-
-/* Called by user to write a row of image data */
-void PNGAPI
-png_write_row(png_structrp png_ptr, png_const_bytep row)
-{
- /* 1.5.6: moved from png_struct to be a local structure: */
- png_row_info row_info;
-
- if (png_ptr == NULL)
- return;
-
- png_debug2(1, "in png_write_row (row %u, pass %d)",
- png_ptr->row_number, png_ptr->pass);
-
- /* Initialize transformations and other stuff if first time */
- if (png_ptr->row_number == 0 && png_ptr->pass == 0)
- {
- /* Make sure we wrote the header info */
- if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
- png_error(png_ptr,
- "png_write_info was never called before png_write_row");
-
- /* Check for transforms that have been set but were defined out */
-#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
- if (png_ptr->transformations & PNG_INVERT_MONO)
- png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
- if (png_ptr->transformations & PNG_FILLER)
- png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
-#endif
-#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
- defined(PNG_READ_PACKSWAP_SUPPORTED)
- if (png_ptr->transformations & PNG_PACKSWAP)
- png_warning(png_ptr,
- "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
- if (png_ptr->transformations & PNG_PACK)
- png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
- if (png_ptr->transformations & PNG_SHIFT)
- png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
- if (png_ptr->transformations & PNG_BGR)
- png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
-#endif
-
-#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
- if (png_ptr->transformations & PNG_SWAP_BYTES)
- png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
-#endif
-
- png_write_start_row(png_ptr);
- }
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* If interlaced and not interested in row, return */
- if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
- {
- switch (png_ptr->pass)
- {
- case 0:
- if (png_ptr->row_number & 0x07)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 1:
- if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 2:
- if ((png_ptr->row_number & 0x07) != 4)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 3:
- if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 4:
- if ((png_ptr->row_number & 0x03) != 2)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 5:
- if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- case 6:
- if (!(png_ptr->row_number & 0x01))
- {
- png_write_finish_row(png_ptr);
- return;
- }
- break;
-
- default: /* error: ignore it */
- break;
- }
- }
-#endif
-
- /* Set up row info for transformations */
- row_info.color_type = png_ptr->color_type;
- row_info.width = png_ptr->usr_width;
- row_info.channels = png_ptr->usr_channels;
- row_info.bit_depth = png_ptr->usr_bit_depth;
- row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
- row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
-
- png_debug1(3, "row_info->color_type = %d", row_info.color_type);
- png_debug1(3, "row_info->width = %u", row_info.width);
- png_debug1(3, "row_info->channels = %d", row_info.channels);
- png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);
- png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);
- png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
-
- /* Copy user's row into buffer, leaving room for filter byte. */
- memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* Handle interlacing */
- if (png_ptr->interlaced && png_ptr->pass < 6 &&
- (png_ptr->transformations & PNG_INTERLACE))
- {
- png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
- /* This should always get caught above, but still ... */
- if (row_info.width == 0)
- {
- png_write_finish_row(png_ptr);
- return;
- }
- }
-#endif
-
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
- /* Handle other transformations */
- if (png_ptr->transformations)
- png_do_write_transformations(png_ptr, &row_info);
-#endif
-
- /* At this point the row_info pixel depth must match the 'transformed' depth,
- * which is also the output depth.
- */
- if (row_info.pixel_depth != png_ptr->pixel_depth ||
- row_info.pixel_depth != png_ptr->transformed_pixel_depth)
- png_error(png_ptr, "internal write transform logic error");
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- /* Write filter_method 64 (intrapixel differencing) only if
- * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
- * 2. Libpng did not write a PNG signature (this filter_method is only
- * used in PNG datastreams that are embedded in MNG datastreams) and
- * 3. The application called png_permit_mng_features with a mask that
- * included PNG_FLAG_MNG_FILTER_64 and
- * 4. The filter_method is 64 and
- * 5. The color_type is RGB or RGBA
- */
- if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
- (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
- {
- /* Intrapixel differencing */
- png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
- }
-#endif
-
-/* Added at libpng-1.5.10 */
-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
- /* Check for out-of-range palette index */
- if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
- png_ptr->num_palette_max >= 0)
- png_do_check_palette_indexes(png_ptr, &row_info);
-#endif
-
- /* Find a filter if necessary, filter the row and write it out. */
- png_write_find_filter(png_ptr, &row_info);
-
- if (png_ptr->write_row_fn != NULL)
- (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
-}
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
-/* Set the automatic flush interval or 0 to turn flushing off */
-void PNGAPI
-png_set_flush(png_structrp png_ptr, int nrows)
-{
- png_debug(1, "in png_set_flush");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
-}
-
-/* Flush the current output buffers now */
-void PNGAPI
-png_write_flush(png_structrp png_ptr)
-{
- png_debug(1, "in png_write_flush");
-
- if (png_ptr == NULL)
- return;
-
- /* We have already written out all of the data */
- if (png_ptr->row_number >= png_ptr->num_rows)
- return;
-
- png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
- png_ptr->flush_rows = 0;
- png_flush(png_ptr);
-}
-#endif /* PNG_WRITE_FLUSH_SUPPORTED */
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
-static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */
-#endif
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
@@ -933,30 +1083,15 @@ png_write_destroy(png_structrp png_ptr)
{
png_debug(1, "in png_write_destroy");
- /* Free any memory zlib uses */
- if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
- deflateEnd(&png_ptr->zstream);
+ png_deflate_destroy(png_ptr);
- /* Free our memory. png_free checks NULL for us. */
- png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
- png_free(png_ptr, png_ptr->row_buf);
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- png_free(png_ptr, png_ptr->prev_row);
- png_free(png_ptr, png_ptr->sub_row);
- png_free(png_ptr, png_ptr->up_row);
- png_free(png_ptr, png_ptr->avg_row);
- png_free(png_ptr, png_ptr->paeth_row);
-#endif
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- /* Use this to save a little code space, it doesn't free the filter_costs */
- png_reset_filter_heuristics(png_ptr);
- png_free(png_ptr, png_ptr->filter_costs);
- png_free(png_ptr, png_ptr->inv_filter_costs);
+#ifdef PNG_TRANSFORM_MECH_SUPPORTED
+ png_transform_free(png_ptr, &png_ptr->transform_list);
#endif
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
png_free(png_ptr, png_ptr->chunk_list);
+ png_ptr->chunk_list = NULL;
#endif
/* The error handling and memory handling information is left intact at this
@@ -992,518 +1127,6 @@ png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
}
}
-/* Allow the application to select one or more row filters to use. */
-void PNGAPI
-png_set_filter(png_structrp png_ptr, int method, int filters)
-{
- png_debug(1, "in png_set_filter");
-
- if (png_ptr == NULL)
- return;
-
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
- (method == PNG_INTRAPIXEL_DIFFERENCING))
- method = PNG_FILTER_TYPE_BASE;
-#endif
-
- /* The only supported method, except for the check above, is
- * PNG_FILTER_TYPE_BASE. The code below does not use 'method' other than
- * for the check, so just keep going if png_app_error returns.
- */
- if (method != PNG_FILTER_TYPE_BASE)
- png_app_error(png_ptr, "Unknown custom filter method");
-
- /* If filter writing is not supported the 'filters' value must be zero,
- * otherwise the value must be a single, valid, filter value or a set of the
- * mask values. The defines in png.h are such that the filter masks used in
- * this API and internally are 1<<(3+value), value is in the range 0..4, so
- * this fits in a byte.
- */
-# ifdef PNG_WRITE_FILTER_SUPPORTED
- /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK
- * because filters then gets set to PNG_FILTER_NONE, as is required.
- */
- if (filters < PNG_FILTER_VALUE_LAST)
- filters = 0x08 << filters;
-
- else if ((filters & ~PNG_ALL_FILTERS) != 0)
- {
- png_app_error(png_ptr, "png_set_filter: invalid filters mask/value");
-
- /* For compatibility with the previous behavior assume a mask value was
- * passed and ignore the non-mask bits.
- */
- filters &= PNG_ALL_FILTERS;
-
- /* For a possibly foolish consistency (it shouldn't matter) set
- * PNG_FILTER_NONE rather than 0.
- */
- if (filters == 0)
- filters = PNG_FILTER_NONE;
- }
-# else
- /* PNG_FILTER_VALUE_NONE and PNG_NO_FILTERS are both 0. */
- if (filters != 0 && filters != PNG_FILTER_NONE)
- png_app_error(png_ptr, "png_set_filter: no filters supported");
-
- filters = PNG_FILTER_NONE;
-# endif
-
-# ifdef PNG_WRITE_FILTER_SUPPORTED
- /* If we have allocated the row_buf, this means we have already started
- * with the image and we should have allocated all of the filter buffers
- * that have been selected. If prev_row isn't already allocated, then
- * it is too late to start using the filters that need it, since we
- * will be missing the data in the previous row. If an application
- * wants to start and stop using particular filters during compression,
- * it should start out with all of the filters, and then add and
- * remove them after the start of compression.
- *
- * NOTE: this is a nasty constraint on the code, because it means that the
- * prev_row buffer must be maintained even if there are currently no
- * 'prev_row' requiring filters active.
- */
- if (png_ptr->row_buf != NULL)
- {
- /* Repeat the checks in png_write_start_row; 1 pixel high or wide
- * images cannot benefit from certain filters. If this isn't done here
- * the check below will fire on 1 pixel high images.
- */
- if (png_ptr->height == 1)
- filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
- if (png_ptr->width == 1)
- filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
- if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
- && png_ptr->prev_row == NULL)
- {
- /* This is the error case, however it is benign - the previous row
- * is not available so the filter can't be used. Just warn here.
- */
- png_app_warning(png_ptr,
- "png_set_filter: UP/AVG/PAETH cannot be added after start");
- filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
- }
-
- /* Allocate any required buffers that have not already been allocated.
- */
- png_write_alloc_filter_row_buffers(png_ptr, filters);
- }
-# endif /* PNG_WRITE_FILTER_SUPPORTED */
-
- /* Finally store the value.
- * TODO: this field could probably be removed if neither READ nor
- * WRITE_FILTER are supported.
- */
- png_ptr->do_filter = (png_byte)filters; /* SAFE: checked above */
-}
-
-/* This allows us to influence the way in which libpng chooses the "best"
- * filter for the current scanline. While the "minimum-sum-of-absolute-
- * differences metric is relatively fast and effective, there is some
- * question as to whether it can be improved upon by trying to keep the
- * filtered data going to zlib more consistent, hopefully resulting in
- * better compression.
- */
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
-/* Convenience reset API. */
-static void
-png_reset_filter_heuristics(png_structrp png_ptr)
-{
- /* Clear out any old values in the 'weights' - this must be done because if
- * the app calls set_filter_heuristics multiple times with different
- * 'num_weights' values we would otherwise potentially have wrong sized
- * arrays.
- */
- png_ptr->num_prev_filters = 0;
- png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
- if (png_ptr->prev_filters != NULL)
- {
- png_bytep old = png_ptr->prev_filters;
- png_ptr->prev_filters = NULL;
- png_free(png_ptr, old);
- }
- if (png_ptr->filter_weights != NULL)
- {
- png_uint_16p old = png_ptr->filter_weights;
- png_ptr->filter_weights = NULL;
- png_free(png_ptr, old);
- }
-
- if (png_ptr->inv_filter_weights != NULL)
- {
- png_uint_16p old = png_ptr->inv_filter_weights;
- png_ptr->inv_filter_weights = NULL;
- png_free(png_ptr, old);
- }
-
- /* Leave the filter_costs - this array is fixed size. */
-}
-
-static int
-png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,
- int num_weights)
-{
- if (png_ptr == NULL)
- return 0;
-
- /* Clear out the arrays */
- png_reset_filter_heuristics(png_ptr);
-
- /* Check arguments; the 'reset' function makes the correct settings for the
- * unweighted case, but we must handle the weight case by initializing the
- * arrays for the caller.
- */
- if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int i;
-
- if (num_weights > 0)
- {
- png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
- (png_uint_32)((sizeof (png_byte)) * num_weights));
-
- /* To make sure that the weighting starts out fairly */
- for (i = 0; i < num_weights; i++)
- {
- png_ptr->prev_filters[i] = 255;
- }
-
- png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
- (png_uint_32)((sizeof (png_uint_16)) * num_weights));
-
- png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
- (png_uint_32)((sizeof (png_uint_16)) * num_weights));
-
- for (i = 0; i < num_weights; i++)
- {
- png_ptr->inv_filter_weights[i] =
- png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
- }
-
- /* Safe to set this now */
- png_ptr->num_prev_filters = (png_byte)num_weights;
- }
-
- /* If, in the future, there are other filter methods, this would
- * need to be based on png_ptr->filter.
- */
- if (png_ptr->filter_costs == NULL)
- {
- png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
- (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));
-
- png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
- (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));
- }
-
- for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
- {
- png_ptr->inv_filter_costs[i] =
- png_ptr->filter_costs[i] = PNG_COST_FACTOR;
- }
-
- /* All the arrays are inited, safe to set this: */
- png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;
-
- /* Return the 'ok' code. */
- return 1;
- }
- else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||
- heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
- {
- return 1;
- }
- else
- {
- png_warning(png_ptr, "Unknown filter heuristic method");
- return 0;
- }
-}
-
-/* Provide floating and fixed point APIs */
-#ifdef PNG_FLOATING_POINT_SUPPORTED
-void PNGAPI
-png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
- int num_weights, png_const_doublep filter_weights,
- png_const_doublep filter_costs)
-{
- png_debug(1, "in png_set_filter_heuristics");
-
- /* The internal API allocates all the arrays and ensures that the elements of
- * those arrays are set to the default value.
- */
- if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
- return;
-
- /* If using the weighted method copy in the weights. */
- if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int i;
- for (i = 0; i < num_weights; i++)
- {
- if (filter_weights[i] <= 0.0)
- {
- png_ptr->inv_filter_weights[i] =
- png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
- }
-
- else
- {
- png_ptr->inv_filter_weights[i] =
- (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);
-
- png_ptr->filter_weights[i] =
- (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);
- }
- }
-
- /* Here is where we set the relative costs of the different filters. We
- * should take the desired compression level into account when setting
- * the costs, so that Paeth, for instance, has a high relative cost at low
- * compression levels, while it has a lower relative cost at higher
- * compression settings. The filter types are in order of increasing
- * relative cost, so it would be possible to do this with an algorithm.
- */
- for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)
- {
- png_ptr->inv_filter_costs[i] =
- (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
-
- png_ptr->filter_costs[i] =
- (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
- }
- }
-}
-#endif /* FLOATING_POINT */
-
-#ifdef PNG_FIXED_POINT_SUPPORTED
-void PNGAPI
-png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
- int num_weights, png_const_fixed_point_p filter_weights,
- png_const_fixed_point_p filter_costs)
-{
- png_debug(1, "in png_set_filter_heuristics_fixed");
-
- /* The internal API allocates all the arrays and ensures that the elements of
- * those arrays are set to the default value.
- */
- if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
- return;
-
- /* If using the weighted method copy in the weights. */
- if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int i;
- for (i = 0; i < num_weights; i++)
- {
- if (filter_weights[i] <= 0)
- {
- png_ptr->inv_filter_weights[i] =
- png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
- }
-
- else
- {
- png_ptr->inv_filter_weights[i] = (png_uint_16)
- ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);
-
- png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*
- PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);
- }
- }
-
- /* Here is where we set the relative costs of the different filters. We
- * should take the desired compression level into account when setting
- * the costs, so that Paeth, for instance, has a high relative cost at low
- * compression levels, while it has a lower relative cost at higher
- * compression settings. The filter types are in order of increasing
- * relative cost, so it would be possible to do this with an algorithm.
- */
- for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
- if (filter_costs[i] >= PNG_FP_1)
- {
- png_uint_32 tmp;
-
- /* Use a 32 bit unsigned temporary here because otherwise the
- * intermediate value will be a 32 bit *signed* integer (ANSI rules)
- * and this will get the wrong answer on division.
- */
- tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);
- tmp /= filter_costs[i];
-
- png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;
-
- tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;
- tmp /= PNG_FP_1;
-
- png_ptr->filter_costs[i] = (png_uint_16)tmp;
- }
- }
-}
-#endif /* FIXED_POINT */
-#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
-
-void PNGAPI
-png_set_compression_level(png_structrp png_ptr, int level)
-{
- png_debug(1, "in png_set_compression_level");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->zlib_level = level;
-}
-
-void PNGAPI
-png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
-{
- png_debug(1, "in png_set_compression_mem_level");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->zlib_mem_level = mem_level;
-}
-
-void PNGAPI
-png_set_compression_strategy(png_structrp png_ptr, int strategy)
-{
- png_debug(1, "in png_set_compression_strategy");
-
- if (png_ptr == NULL)
- return;
-
- /* The flag setting here prevents the libpng dynamic selection of strategy.
- */
- png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
- png_ptr->zlib_strategy = strategy;
-}
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-void PNGAPI
-png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
-{
- if (png_ptr == NULL)
- return;
-
- /* Prior to 1.6.0 this would warn but then set the window_bits value, this
- * meant that negative window bits values could be selected which would cause
- * libpng to write a non-standard PNG file with raw deflate or gzip
- * compressed IDAT or ancillary chunks. Such files can be read and there is
- * no warning on read, so this seems like a very bad idea.
- */
- if (window_bits > 15)
- {
- png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
- window_bits = 15;
- }
-
- else if (window_bits < 8)
- {
- png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
- window_bits = 8;
- }
-
- png_ptr->zlib_window_bits = window_bits;
-}
-
-void PNGAPI
-png_set_compression_method(png_structrp png_ptr, int method)
-{
- png_debug(1, "in png_set_compression_method");
-
- if (png_ptr == NULL)
- return;
-
- /* This would produce an invalid PNG file if it worked, but it doesn't and
- * deflate will fault it, so it is harmless to just warn here.
- */
- if (method != 8)
- png_warning(png_ptr, "Only compression method 8 is supported by PNG");
-
- png_ptr->zlib_method = method;
-}
-
-/* The following were added to libpng-1.5.4 */
-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
-void PNGAPI
-png_set_text_compression_level(png_structrp png_ptr, int level)
-{
- png_debug(1, "in png_set_text_compression_level");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->zlib_text_level = level;
-}
-
-void PNGAPI
-png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
-{
- png_debug(1, "in png_set_text_compression_mem_level");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->zlib_text_mem_level = mem_level;
-}
-
-void PNGAPI
-png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
-{
- png_debug(1, "in png_set_text_compression_strategy");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->zlib_text_strategy = strategy;
-}
-
-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
- * smaller value of window_bits if it can do so safely.
- */
-void PNGAPI
-png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
-{
- if (png_ptr == NULL)
- return;
-
- if (window_bits > 15)
- {
- png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
- window_bits = 15;
- }
-
- else if (window_bits < 8)
- {
- png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
- window_bits = 8;
- }
-
- png_ptr->zlib_text_window_bits = window_bits;
-}
-
-void PNGAPI
-png_set_text_compression_method(png_structrp png_ptr, int method)
-{
- png_debug(1, "in png_set_text_compression_method");
-
- if (png_ptr == NULL)
- return;
-
- if (method != 8)
- png_warning(png_ptr, "Only compression method 8 is supported by PNG");
-
- png_ptr->zlib_text_method = method;
-}
-#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
-/* end of API added to libpng-1.5.4 */
-
void PNGAPI
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
{
@@ -1513,23 +1136,7 @@ png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
png_ptr->write_row_fn = write_row_fn;
}
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-void PNGAPI
-png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
- write_user_transform_fn)
-{
- png_debug(1, "in png_set_write_user_transform_fn");
-
- if (png_ptr == NULL)
- return;
-
- png_ptr->transformations |= PNG_USER_TRANSFORM;
- png_ptr->write_user_transform_fn = write_user_transform_fn;
-}
-#endif
-
-
-#ifdef PNG_INFO_IMAGE_SUPPORTED
+#ifdef PNG_WRITE_PNG_SUPPORTED
void PNGAPI
png_write_png(png_structrp png_ptr, png_inforp info_ptr,
int transforms, voidp params)
@@ -1549,7 +1156,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
/* ------ these transformations don't touch the info structure ------- */
/* Invert monochrome pixels */
- if (transforms & PNG_TRANSFORM_INVERT_MONO)
+ if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
#ifdef PNG_WRITE_INVERT_SUPPORTED
png_set_invert_mono(png_ptr);
#else
@@ -1559,16 +1166,16 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
/* Shift the pixels up to a legal bit depth and fill in
* as appropriate to correctly scale the image.
*/
- if (transforms & PNG_TRANSFORM_SHIFT)
+ if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
#ifdef PNG_WRITE_SHIFT_SUPPORTED
- if (info_ptr->valid & PNG_INFO_sBIT)
+ if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
png_set_shift(png_ptr, &info_ptr->sig_bit);
#else
png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
#endif
/* Pack pixels into bytes */
- if (transforms & PNG_TRANSFORM_PACKING)
+ if ((transforms & PNG_TRANSFORM_PACKING) != 0)
#ifdef PNG_WRITE_PACK_SUPPORTED
png_set_packing(png_ptr);
#else
@@ -1576,7 +1183,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Swap location of alpha bytes from ARGB to RGBA */
- if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+ if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
png_set_swap_alpha(png_ptr);
#else
@@ -1587,21 +1194,21 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
* RGB, note that the code expects the input color type to be G or RGB; no
* alpha channel.
*/
- if (transforms &
- (PNG_TRANSFORM_STRIP_FILLER_AFTER|PNG_TRANSFORM_STRIP_FILLER_BEFORE))
+ if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER|
+ PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0)
{
#ifdef PNG_WRITE_FILLER_SUPPORTED
- if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
+ if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0)
{
- if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
+ if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
png_app_error(png_ptr,
- "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
+ "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
/* Continue if ignored - this is the pre-1.6.10 behavior */
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
}
- else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
+ else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
#else
png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported");
@@ -1609,7 +1216,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
}
/* Flip BGR pixels to RGB */
- if (transforms & PNG_TRANSFORM_BGR)
+ if ((transforms & PNG_TRANSFORM_BGR) != 0)
#ifdef PNG_WRITE_BGR_SUPPORTED
png_set_bgr(png_ptr);
#else
@@ -1617,7 +1224,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Swap bytes of 16-bit files to most significant byte first */
- if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+ if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
#ifdef PNG_WRITE_SWAP_SUPPORTED
png_set_swap(png_ptr);
#else
@@ -1625,7 +1232,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Swap bits of 1, 2, 4 bit packed pixel formats */
- if (transforms & PNG_TRANSFORM_PACKSWAP)
+ if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
png_set_packswap(png_ptr);
#else
@@ -1633,7 +1240,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
#endif
/* Invert the alpha channel from opacity to transparency */
- if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+ if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
png_set_invert_alpha(png_ptr);
#else
@@ -1650,17 +1257,16 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr,
PNG_UNUSED(params)
}
-#endif
+#endif /* WRITE_PNG */
#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
-#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */
/* Initialize the write structure - general purpose utility. */
static int
png_image_write_init(png_imagep image)
{
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,
- png_safe_error, png_safe_warning);
+ png_safe_error, png_safe_warning);
if (png_ptr != NULL)
{
@@ -1669,7 +1275,7 @@ png_image_write_init(png_imagep image)
if (info_ptr != NULL)
{
png_controlp control = png_voidcast(png_controlp,
- png_malloc_warn(png_ptr, (sizeof *control)));
+ png_malloc_warn(png_ptr, (sizeof *control)));
if (control != NULL)
{
@@ -1699,13 +1305,17 @@ typedef struct
/* Arguments: */
png_imagep image;
png_const_voidp buffer;
- png_int_32 row_stride;
+ ptrdiff_t row_stride;
png_const_voidp colormap;
int convert_to_8bit;
/* Local variables: */
png_const_voidp first_row;
ptrdiff_t row_bytes;
png_voidp local_row;
+ /* Byte count for memory writing */
+ png_bytep memory;
+ png_alloc_size_t memory_bytes; /* not used for STDIO */
+ png_alloc_size_t output_bytes; /* running total */
} png_image_write_control;
/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to
@@ -1716,22 +1326,22 @@ static int
png_write_image_16bit(png_voidp argument)
{
png_image_write_control *display = png_voidcast(png_image_write_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
- display->first_row);
+ display->first_row);
png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);
png_uint_16p row_end;
- const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
+ const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
int aindex = 0;
png_uint_32 y = image->height;
- if (image->format & PNG_FORMAT_FLAG_ALPHA)
+ if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
- if (image->format & PNG_FORMAT_FLAG_AFIRST)
+ if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
{
aindex = -1;
++input_row; /* To point to the first component */
@@ -1795,7 +1405,7 @@ png_write_image_16bit(png_voidp argument)
{
png_uint_32 calc = component * reciprocal;
calc += 16384; /* round to nearest */
- component = (png_uint_16)(calc >> 15);
+ component = png_check_u16(png_ptr, calc >> 15);
}
*out_ptr++ = component;
@@ -1825,8 +1435,8 @@ png_write_image_16bit(png_voidp argument)
#define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)
static png_byte
-png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
- png_uint_32 reciprocal/*from the above macro*/)
+png_unpremultiply(png_const_structrp png_ptr, png_uint_32 component,
+ png_uint_32 alpha, png_uint_32 reciprocal/*from the above macro*/)
{
/* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0
* is represented as some other value there is more likely to be a
@@ -1862,34 +1472,36 @@ png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
component *= 255;
/* Convert the component to sRGB. */
- return (png_byte)PNG_sRGB_FROM_LINEAR(component);
+ return PNG_sRGB_FROM_LINEAR(png_ptr, component);
}
else
return 0;
+
+ PNG_UNUSEDRC(png_ptr)
}
static int
png_write_image_8bit(png_voidp argument)
{
png_image_write_control *display = png_voidcast(png_image_write_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
- display->first_row);
+ display->first_row);
png_bytep output_row = png_voidcast(png_bytep, display->local_row);
png_uint_32 y = image->height;
- const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
+ const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
- if (image->format & PNG_FORMAT_FLAG_ALPHA)
+ if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
png_bytep row_end;
int aindex;
# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
- if (image->format & PNG_FORMAT_FLAG_AFIRST)
+ if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
{
aindex = -1;
++input_row; /* To point to the first component */
@@ -1911,7 +1523,7 @@ png_write_image_8bit(png_voidp argument)
while (out_ptr < row_end)
{
png_uint_16 alpha = in_ptr[aindex];
- png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
+ png_byte alphabyte = png_check_byte(png_ptr, PNG_DIV257(alpha));
png_uint_32 reciprocal = 0;
int c;
@@ -1923,7 +1535,8 @@ png_write_image_8bit(png_voidp argument)
c = channels;
do /* always at least one channel */
- *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
+ *out_ptr++ = png_unpremultiply(png_ptr, *in_ptr++, alpha,
+ reciprocal);
while (--c > 0);
/* Skip to next component (skip the intervening alpha channel) */
@@ -1932,7 +1545,7 @@ png_write_image_8bit(png_voidp argument)
} /* while out_ptr < row_end */
png_write_row(png_ptr, png_voidcast(png_const_bytep,
- display->local_row));
+ display->local_row));
input_row += display->row_bytes/(sizeof (png_uint_16));
} /* while y */
}
@@ -1954,7 +1567,7 @@ png_write_image_8bit(png_voidp argument)
png_uint_32 component = *in_ptr++;
component *= 255;
- *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);
+ *out_ptr++ = PNG_sRGB_FROM_LINEAR(png_ptr, component);
}
png_write_row(png_ptr, output_row);
@@ -1986,7 +1599,7 @@ png_image_set_PLTE(png_image_write_control *display)
# endif
# ifdef PNG_FORMAT_BGR_SUPPORTED
- const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;
+ const int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
# else
# define bgr 0
# endif
@@ -2003,33 +1616,35 @@ png_image_set_PLTE(png_image_write_control *display)
/* This gets automatically converted to sRGB with reversal of the
* pre-multiplication if the color-map has an alpha channel.
*/
- if (format & PNG_FORMAT_FLAG_LINEAR)
+ if ((format & PNG_FORMAT_FLAG_LINEAR) != 0)
{
png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
entry += i * channels;
- if (channels & 1) /* no alpha */
+ if ((channels & 1) != 0) /* no alpha */
{
if (channels >= 3) /* RGB */
{
- palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
- entry[(2 ^ bgr)]);
- palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
- entry[1]);
- palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
- entry[bgr]);
+ palette[i].blue = PNG_sRGB_FROM_LINEAR(
+ display->image->opaque->png_ptr, 255 * entry[(2 ^ bgr)]);
+ palette[i].green = PNG_sRGB_FROM_LINEAR(
+ display->image->opaque->png_ptr, 255 * entry[1]);
+ palette[i].red = PNG_sRGB_FROM_LINEAR(
+ display->image->opaque->png_ptr, 255 * entry[bgr]);
}
else /* Gray */
palette[i].blue = palette[i].red = palette[i].green =
- (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);
+ PNG_sRGB_FROM_LINEAR(display->image->opaque->png_ptr,
+ 255 * *entry);
}
else /* alpha */
{
png_uint_16 alpha = entry[afirst ? 0 : channels-1];
- png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
+ png_byte alphabyte = png_check_byte(
+ display->image->opaque->png_ptr, PNG_DIV257(alpha));
png_uint_32 reciprocal = 0;
/* Calculate a reciprocal, as in the png_write_image_8bit code above
@@ -2045,17 +1660,21 @@ png_image_set_PLTE(png_image_write_control *display)
if (channels >= 3) /* RGB */
{
- palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],
- alpha, reciprocal);
- palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,
- reciprocal);
- palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,
- reciprocal);
+ palette[i].blue = png_unpremultiply(
+ display->image->opaque->png_ptr, entry[afirst + (2 ^ bgr)],
+ alpha, reciprocal);
+ palette[i].green = png_unpremultiply(
+ display->image->opaque->png_ptr, entry[afirst + 1], alpha,
+ reciprocal);
+ palette[i].red = png_unpremultiply(
+ display->image->opaque->png_ptr, entry[afirst + bgr], alpha,
+ reciprocal);
}
else /* gray */
palette[i].blue = palette[i].red = palette[i].green =
- png_unpremultiply(entry[afirst], alpha, reciprocal);
+ png_unpremultiply(display->image->opaque->png_ptr,
+ entry[afirst], alpha, reciprocal);
}
}
@@ -2102,11 +1721,11 @@ png_image_set_PLTE(png_image_write_control *display)
# endif
png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,
- entries);
+ entries);
if (num_trans > 0)
png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,
- num_trans, NULL);
+ num_trans, NULL);
image->colormap_entries = entries;
}
@@ -2115,52 +1734,92 @@ static int
png_image_write_main(png_voidp argument)
{
png_image_write_control *display = png_voidcast(png_image_write_control*,
- argument);
+ argument);
png_imagep image = display->image;
png_structrp png_ptr = image->opaque->png_ptr;
png_inforp info_ptr = image->opaque->info_ptr;
png_uint_32 format = image->format;
- int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0;
- int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */
- int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0;
- int write_16bit = linear && !colormap && !display->convert_to_8bit;
+ /* The following four ints are actually booleans */
+ int colormap = (format & PNG_FORMAT_FLAG_COLORMAP);
+ int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */
+ int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA);
+ int write_16bit = linear && !colormap && (display->convert_to_8bit == 0);
# ifdef PNG_BENIGN_ERRORS_SUPPORTED
/* Make sure we error out on any bad situation */
png_set_benign_errors(png_ptr, 0/*error*/);
# endif
- /* Default the 'row_stride' parameter if required. */
- if (display->row_stride == 0)
- display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);
+ /* Default the 'row_stride' parameter if required, also check the row stride
+ * and total image size to ensure that they are within the system limits.
+ */
+ {
+ const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);
+
+ /* The test is slightly evil: it assumes that a signed pointer difference
+ * (ptrdiff_t) can hold a maximum value of half, rounded down, of the
+ * maximum of a (size_t). This is almost certain to be true.
+ */
+ if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */
+ {
+ png_alloc_size_t check;
+ const png_alloc_size_t png_row_stride =
+ (png_alloc_size_t)/*SAFE*/image->width * channels;
+
+ if (display->row_stride == 0)
+ display->row_stride = (ptrdiff_t)png_row_stride;
+
+ if (display->row_stride < 0)
+ check = -display->row_stride;
+
+ else
+ check = display->row_stride;
+
+ if (check >= png_row_stride)
+ {
+ /* Now check for overflow of the image buffer calculation; check for
+ * (size_t) overflow here. This detects issues with the
+ * PNG_IMAGE_BUFFER_SIZE macro.
+ */
+ if (image->height > PNG_SIZE_MAX/png_row_stride)
+ png_error(image->opaque->png_ptr, "memory image too large");
+ }
+
+ else
+ png_error(image->opaque->png_ptr, "supplied row stride too small");
+ }
+
+ else
+ png_error(image->opaque->png_ptr, "image row stride too large");
+ }
/* Set the required transforms then write the rows in the correct order. */
- if (format & PNG_FORMAT_FLAG_COLORMAP)
+ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0)
{
if (display->colormap != NULL && image->colormap_entries > 0)
{
png_uint_32 entries = image->colormap_entries;
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
- entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
- PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
+ PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_image_set_PLTE(display);
}
else
png_error(image->opaque->png_ptr,
- "no color-map for color-mapped image");
+ "no color-map for color-mapped image");
}
else
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
- write_16bit ? 16 : 8,
- ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
- ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ write_16bit ? 16 : 8,
+ ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
+ ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
/* Counter-intuitively the data transformations must be called *after*
* png_write_info, not before as in the read code, but the 'set' functions
@@ -2173,17 +1832,17 @@ png_image_write_main(png_voidp argument)
/* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);
- if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
+ if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
png_set_cHRM_fixed(png_ptr, info_ptr,
- /* color x y */
- /* white */ 31270, 32900,
- /* red */ 64000, 33000,
- /* green */ 30000, 60000,
- /* blue */ 15000, 6000
+ /* color x y */
+ /* white */ 31270, 32900,
+ /* red */ 64000, 33000,
+ /* green */ 30000, 60000,
+ /* blue */ 15000, 6000
);
}
- else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
+ else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
/* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit
@@ -2204,36 +1863,36 @@ png_image_write_main(png_voidp argument)
{
PNG_CONST png_uint_16 le = 0x0001;
- if (*(png_const_bytep)&le)
+ if ((*(png_const_bytep) & le) != 0)
png_set_swap(png_ptr);
}
# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
- if (format & PNG_FORMAT_FLAG_BGR)
+ if ((format & PNG_FORMAT_FLAG_BGR) != 0)
{
- if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)
+ if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0)
png_set_bgr(png_ptr);
- format &= ~PNG_FORMAT_FLAG_BGR;
+ format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR);
}
# endif
# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
- if (format & PNG_FORMAT_FLAG_AFIRST)
+ if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
{
- if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
png_set_swap_alpha(png_ptr);
- format &= ~PNG_FORMAT_FLAG_AFIRST;
+ format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST);
}
# endif
/* If there are 16 or fewer color-map entries we wrote a lower bit depth
* above, but the application data is still byte packed.
*/
- if (colormap && image->colormap_entries <= 16)
+ if (colormap != 0 && image->colormap_entries <= 16)
png_set_packing(png_ptr);
/* That should have handled all (both) the transforms. */
- if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
+ if ((format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
png_error(png_ptr, "png_write_image: unsupported transformation");
@@ -2251,26 +1910,22 @@ png_image_write_main(png_voidp argument)
display->row_bytes = row_bytes;
}
- /* Apply 'fast' options if the flag is set. */
- if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)
- {
- png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);
- /* NOTE: determined by experiment using pngstest, this reflects some
- * balance between the time to write the image once and the time to read
- * it about 50 times. The speed-up in pngstest was about 10-20% of the
- * total (user) time on a heavily loaded system.
- */
- png_set_compression_level(png_ptr, 3);
- }
+ /* Select the right compression mode based on the presence or absence of the
+ * 'fast' flag. This will use whatever options are available in the libpng
+ * build. It is always supported.
+ */
+ png_set_compression(png_ptr, (image->flags & PNG_IMAGE_FLAG_FAST) != 0 ?
+ PNG_COMPRESSION_HIGH_SPEED : PNG_COMPRESSION_HIGH);
/* Check for the cases that currently require a pre-transform on the row
* before it is written. This only applies when the input is 16-bit and
* either there is an alpha channel or it is converted to 8-bit.
*/
- if ((linear && alpha) || (!colormap && display->convert_to_8bit))
+ if ((linear != 0 && alpha != 0 ) ||
+ (colormap == 0 && display->convert_to_8bit != 0))
{
png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
- png_get_rowbytes(png_ptr, info_ptr)));
+ png_get_rowbytes(png_ptr, info_ptr)));
int result;
display->local_row = row;
@@ -2307,25 +1962,136 @@ png_image_write_main(png_voidp argument)
return 1;
}
-int PNGAPI
-png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
- const void *buffer, png_int_32 row_stride, const void *colormap)
+
+static void (PNGCBAPI
+image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data,
+ png_size_t size)
{
- /* Write the image to the given (FILE*). */
+ png_image_write_control *display = png_voidcast(png_image_write_control*,
+ png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/);
+ const png_alloc_size_t ob = display->output_bytes;
+
+ /* Check for overflow; this should never happen: */
+ if (size <= ((png_alloc_size_t)-1) - ob)
+ {
+ /* I don't think libpng ever does this, but just in case: */
+ if (size > 0)
+ {
+ if (display->memory_bytes >= ob+size) /* writing */
+ memcpy(display->memory+ob, data, size);
+
+ /* Always update the size: */
+ display->output_bytes = ob+size;
+ }
+ }
+
+ else
+ png_error(png_ptr, "png_image_write_to_memory: PNG too big");
+}
+
+static void (PNGCBAPI
+image_memory_flush)(png_structp png_ptr)
+{
+ PNG_UNUSED(png_ptr)
+}
+
+static int
+png_image_write_memory(png_voidp argument)
+{
+ png_image_write_control *display = png_voidcast(png_image_write_control*,
+ argument);
+
+ /* The rest of the memory-specific init and write_main in an error protected
+ * environment. This case needs to use callbacks for the write operations
+ * since libpng has no built in support for writing to memory.
+ */
+ png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/,
+ image_memory_write, image_memory_flush);
+
+ return png_image_write_main(display);
+}
+
+int PNGAPI
+png_image_write_to_memory(png_imagep image, void *memory,
+ png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
+ const void *buffer, ptrdiff_t row_stride, const void *colormap)
+{
+ /* Write the image to the given buffer, or count the bytes if it is NULL */
if (image != NULL && image->version == PNG_IMAGE_VERSION)
{
- if (file != NULL)
+ if (memory_bytes != NULL && buffer != NULL)
{
- if (png_image_write_init(image))
+ /* This is to give the caller an easier error detection in the NULL
+ * case and guard against uninitialized variable problems:
+ */
+ if (memory == NULL)
+ *memory_bytes = 0;
+
+ if (png_image_write_init(image) != 0)
{
png_image_write_control display;
int result;
- /* This is slightly evil, but png_init_io doesn't do anything other
- * than this and we haven't changed the standard IO functions so
- * this saves a 'safe' function.
- */
- image->opaque->png_ptr->io_ptr = file;
+ memset(&display, 0, (sizeof display));
+ display.image = image;
+ display.buffer = buffer;
+ display.row_stride = row_stride;
+ display.colormap = colormap;
+ display.convert_to_8bit = convert_to_8bit;
+ display.memory = png_voidcast(png_bytep, memory);
+ display.memory_bytes = *memory_bytes;
+ display.output_bytes = 0;
+
+ result = png_safe_execute(image, png_image_write_memory, &display);
+ png_image_free(image);
+
+ /* write_memory returns true even if we ran out of buffer. */
+ if (result)
+ {
+ /* On out-of-buffer this function returns '0' but still updates
+ * memory_bytes:
+ */
+ if (memory != NULL && display.output_bytes > *memory_bytes)
+ result = 0;
+
+ *memory_bytes = display.output_bytes;
+ }
+
+ return result;
+ }
+
+ else
+ return 0;
+ }
+
+ else
+ return png_image_error(image,
+ "png_image_write_to_memory: invalid argument");
+ }
+
+ else if (image != NULL)
+ return png_image_error(image,
+ "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION");
+
+ else
+ return 0;
+}
+
+#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
+int PNGAPI
+png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
+ const void *buffer, ptrdiff_t row_stride, const void *colormap)
+{
+ /* Write the image to the given (FILE*). */
+ if (image != NULL && image->version == PNG_IMAGE_VERSION)
+ {
+ if (file != NULL && buffer != NULL)
+ {
+ if (png_image_write_init(image) != 0 &&
+ png_image_init_io(image, file) != 0)
+ {
+ png_image_write_control display;
+ int result;
memset(&display, 0, (sizeof display));
display.image = image;
@@ -2345,12 +2111,12 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
else
return png_image_error(image,
- "png_image_write_to_stdio: invalid argument");
+ "png_image_write_to_stdio: invalid argument");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
+ "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
else
return 0;
@@ -2358,20 +2124,20 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
int PNGAPI
png_image_write_to_file(png_imagep image, const char *file_name,
- int convert_to_8bit, const void *buffer, png_int_32 row_stride,
- const void *colormap)
+ int convert_to_8bit, const void *buffer, ptrdiff_t row_stride,
+ const void *colormap)
{
/* Write the image to the named file. */
if (image != NULL && image->version == PNG_IMAGE_VERSION)
{
- if (file_name != NULL)
+ if (file_name != NULL && buffer != NULL)
{
FILE *fp = fopen(file_name, "wb");
if (fp != NULL)
{
if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
- row_stride, colormap))
+ row_stride, colormap) != 0)
{
int error; /* from fflush/fclose */
@@ -2412,16 +2178,16 @@ png_image_write_to_file(png_imagep image, const char *file_name,
else
return png_image_error(image,
- "png_image_write_to_file: invalid argument");
+ "png_image_write_to_file: invalid argument");
}
else if (image != NULL)
return png_image_error(image,
- "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
+ "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
else
return 0;
}
-#endif /* PNG_STDIO_SUPPORTED */
+#endif /* SIMPLIFIED_WRITE_STDIO */
#endif /* SIMPLIFIED_WRITE */
-#endif /* PNG_WRITE_SUPPORTED */
+#endif /* WRITE */
diff --git a/ext/libpng17/pngwtran.c b/ext/libpng17/pngwtran.c
index 215bd68a9d..3aaf2205e0 100644
--- a/ext/libpng17/pngwtran.c
+++ b/ext/libpng17/pngwtran.c
@@ -1,8 +1,8 @@
/* pngwtran.c - transforms the data in a row for PNG writers
*
- * Last changed in libpng 1.6.9 [February 6, 2014]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -12,561 +12,137 @@
*/
#include "pngpriv.h"
-
-#ifdef PNG_WRITE_SUPPORTED
-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_SRC_FILE PNG_SRC_FILE_pngwtran
#ifdef PNG_WRITE_PACK_SUPPORTED
-/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The
- * row_info bit depth should be 8 (one pixel per byte). The channels
- * should be 1 (this only happens on grayscale and paletted images).
- */
+/* Pack pixels into bytes. */
static void
-png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+png_do_write_pack(png_transformp *transform, png_transform_controlp tc)
{
+ png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
+ png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
+ png_const_bytep ep = png_upcast(png_const_bytep, tc->sp) + rowbytes;
+ png_bytep dp = png_voidcast(png_bytep, tc->dp);
+
png_debug(1, "in png_do_pack");
- if (row_info->bit_depth == 8 &&
- row_info->channels == 1)
+# define png_ptr tc->png_ptr
+
+ switch ((*transform)->args)
{
- switch ((int)bit_depth)
+ case 1:
{
- case 1:
+ unsigned int mask = 0x80, v = 0;
+
+ while (sp < ep)
{
- png_bytep sp, dp;
- int mask, v;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
+ if (*sp++ != 0)
+ v |= mask;
- sp = row;
- dp = row;
- mask = 0x80;
- v = 0;
+ mask >>= 1;
- for (i = 0; i < row_width; i++)
+ if (mask == 0)
{
- if (*sp != 0)
- v |= mask;
-
- sp++;
-
- if (mask > 1)
- mask >>= 1;
-
- else
- {
- mask = 0x80;
- *dp = (png_byte)v;
- dp++;
- v = 0;
- }
+ mask = 0x80;
+ *dp++ = PNG_BYTE(v);
+ v = 0;
}
-
- if (mask != 0x80)
- *dp = (png_byte)v;
-
- break;
}
- case 2:
- {
- png_bytep sp, dp;
- int shift, v;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- sp = row;
- dp = row;
- shift = 6;
- v = 0;
-
- for (i = 0; i < row_width; i++)
- {
- png_byte value;
-
- value = (png_byte)(*sp & 0x03);
- v |= (value << shift);
-
- if (shift == 0)
- {
- shift = 6;
- *dp = (png_byte)v;
- dp++;
- v = 0;
- }
-
- else
- shift -= 2;
-
- sp++;
- }
-
- if (shift != 6)
- *dp = (png_byte)v;
-
- break;
- }
-
- case 4:
- {
- png_bytep sp, dp;
- int shift, v;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- sp = row;
- dp = row;
- shift = 4;
- v = 0;
-
- for (i = 0; i < row_width; i++)
- {
- png_byte value;
-
- value = (png_byte)(*sp & 0x0f);
- v |= (value << shift);
-
- if (shift == 0)
- {
- shift = 4;
- *dp = (png_byte)v;
- dp++;
- v = 0;
- }
-
- else
- shift -= 4;
-
- sp++;
- }
-
- if (shift != 4)
- *dp = (png_byte)v;
-
- break;
- }
-
- default:
- break;
+ if (mask != 0x80)
+ *dp++ = PNG_BYTE(v);
+ break;
}
- row_info->bit_depth = (png_byte)bit_depth;
- row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
- row_info->width);
+ case 2:
+ {
+ unsigned int shift = 8, v = 0;
+
+ while (sp < ep)
+ {
+ shift -= 2;
+ v |= (*sp++ & 0x3) << shift;
+
+ if (shift == 0)
+ {
+ shift = 8;
+ *dp++ = PNG_BYTE(v);
+ v = 0;
+ }
+ }
+
+ if (shift != 8)
+ *dp++ = PNG_BYTE(v);
+ break;
+ }
+
+ case 4:
+ {
+ unsigned int shift = 8, v = 0;
+
+ while (sp < ep)
+ {
+ shift -= 4;
+ v |= ((*sp++ & 0xf) << shift);
+
+ if (shift == 0)
+ {
+ shift = 8;
+ *dp++ = PNG_BYTE(v);
+ v = 0;
+ }
+ }
+
+ if (shift != 8)
+ *dp++ = PNG_BYTE(v);
+ break;
+ }
+
+ default:
+ impossible("bit depth");
}
+
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
+ --(tc->range) == 0)
+ tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
+
+ tc->bit_depth = (*transform)->args;
+ tc->sp = tc->dp;
+# undef png_ptr
}
-#endif
-#ifdef PNG_WRITE_SHIFT_SUPPORTED
-/* Shift pixel values to take advantage of whole range. Pass the
- * true number of bits in bit_depth. The row should be packed
- * according to row_info->bit_depth. Thus, if you had a row of
- * bit depth 4, but the pixels only had values from 0 to 7, you
- * would pass 3 as bit_depth, and this routine would translate the
- * data to 0 to 15.
- */
-static void
-png_do_shift(png_row_infop row_info, png_bytep row,
- png_const_color_8p bit_depth)
-{
- png_debug(1, "in png_do_shift");
-
- if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
- {
- int shift_start[4], shift_dec[4];
- int channels = 0;
-
- if (row_info->color_type & PNG_COLOR_MASK_COLOR)
- {
- shift_start[channels] = row_info->bit_depth - bit_depth->red;
- shift_dec[channels] = bit_depth->red;
- channels++;
-
- shift_start[channels] = row_info->bit_depth - bit_depth->green;
- shift_dec[channels] = bit_depth->green;
- channels++;
-
- shift_start[channels] = row_info->bit_depth - bit_depth->blue;
- shift_dec[channels] = bit_depth->blue;
- channels++;
- }
-
- else
- {
- shift_start[channels] = row_info->bit_depth - bit_depth->gray;
- shift_dec[channels] = bit_depth->gray;
- channels++;
- }
-
- if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
- {
- shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
- shift_dec[channels] = bit_depth->alpha;
- channels++;
- }
-
- /* With low row depths, could only be grayscale, so one channel */
- if (row_info->bit_depth < 8)
- {
- png_bytep bp = row;
- png_size_t i;
- unsigned int mask;
- png_size_t row_bytes = row_info->rowbytes;
-
- if (bit_depth->gray == 1 && row_info->bit_depth == 2)
- mask = 0x55;
-
- else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
- mask = 0x11;
-
- else
- mask = 0xff;
-
- for (i = 0; i < row_bytes; i++, bp++)
- {
- int j;
- unsigned int v, out;
-
- v = *bp;
- out = 0;
-
- for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
- {
- if (j > 0)
- out |= v << j;
-
- else
- out |= (v >> (-j)) & mask;
- }
-
- *bp = (png_byte)(out & 0xff);
- }
- }
-
- else if (row_info->bit_depth == 8)
- {
- png_bytep bp = row;
- png_uint_32 i;
- png_uint_32 istop = channels * row_info->width;
-
- for (i = 0; i < istop; i++, bp++)
- {
-
- const unsigned int c = i%channels;
- int j;
- unsigned int v, out;
-
- v = *bp;
- out = 0;
-
- for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
- {
- if (j > 0)
- out |= v << j;
-
- else
- out |= v >> (-j);
- }
-
- *bp = (png_byte)(out & 0xff);
- }
- }
-
- else
- {
- png_bytep bp;
- png_uint_32 i;
- png_uint_32 istop = channels * row_info->width;
-
- for (bp = row, i = 0; i < istop; i++)
- {
- const unsigned int c = i%channels;
- int j;
- unsigned int value, v;
-
- v = png_get_uint_16(bp);
- value = 0;
-
- for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
- {
- if (j > 0)
- value |= v << j;
-
- else
- value |= v >> (-j);
- }
- *bp++ = (png_byte)((value >> 8) & 0xff);
- *bp++ = (png_byte)(value & 0xff);
- }
- }
- }
-}
-#endif
-
-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
-static void
-png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_write_swap_alpha");
-
- {
- if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This converts from ARGB to RGBA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- png_byte save = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = save;
- }
- }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- else
- {
- /* This converts from AARRGGBB to RRGGBBAA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- png_byte save[2];
- save[0] = *(sp++);
- save[1] = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = save[0];
- *(dp++) = save[1];
- }
- }
-#endif /* PNG_WRITE_16BIT_SUPPORTED */
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This converts from AG to GA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- png_byte save = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = save;
- }
- }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- else
- {
- /* This converts from AAGG to GGAA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- png_byte save[2];
- save[0] = *(sp++);
- save[1] = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = save[0];
- *(dp++) = save[1];
- }
- }
-#endif /* PNG_WRITE_16BIT_SUPPORTED */
- }
- }
-}
-#endif
-
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
-static void
-png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
-{
- png_debug(1, "in png_do_write_invert_alpha");
-
- {
- if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This inverts the alpha channel in RGBA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- /* Does nothing
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- */
- sp+=3; dp = sp;
- *(dp++) = (png_byte)(255 - *(sp++));
- }
- }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- else
- {
- /* This inverts the alpha channel in RRGGBBAA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- /* Does nothing
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- */
- sp+=6; dp = sp;
- *(dp++) = (png_byte)(255 - *(sp++));
- *(dp++) = (png_byte)(255 - *(sp++));
- }
- }
-#endif /* PNG_WRITE_16BIT_SUPPORTED */
- }
-
- else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- {
- if (row_info->bit_depth == 8)
- {
- /* This inverts the alpha channel in GA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- *(dp++) = *(sp++);
- *(dp++) = (png_byte)(255 - *(sp++));
- }
- }
-
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- else
- {
- /* This inverts the alpha channel in GGAA */
- png_bytep sp, dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- for (i = 0, sp = dp = row; i < row_width; i++)
- {
- /* Does nothing
- *(dp++) = *(sp++);
- *(dp++) = *(sp++);
- */
- sp+=2; dp = sp;
- *(dp++) = (png_byte)(255 - *(sp++));
- *(dp++) = (png_byte)(255 - *(sp++));
- }
- }
-#endif /* PNG_WRITE_16BIT_SUPPORTED */
- }
- }
-}
-#endif
-
-/* Transform the data according to the user's wishes. The order of
- * transformations is significant.
- */
void /* PRIVATE */
-png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)
+png_init_write_pack(png_transformp *transform, png_transform_controlp tc)
{
- png_debug(1, "in png_do_write_transformations");
+# define png_ptr tc->png_ptr
+ debug(tc->init);
+# undef png_ptr
- if (png_ptr == NULL)
- return;
+ /* The init routine is called *forward* so the transform control we get has
+ * the required bit depth and the transform routine will increase it to 8
+ * bits per channel. The code doesn't really care how many channels there
+ * are, but the only way to get a channel depth of less than 8 is to have
+ * just one channel.
+ */
+ if (tc->bit_depth < 8) /* else no packing/unpacking */
+ {
+ if (tc->init == PNG_TC_INIT_FINAL)
+ {
+ (*transform)->fn = png_do_write_pack;
+ /* Record this for the backwards run: */
+ (*transform)->args = tc->bit_depth & 0xf;
+ }
-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
- if (png_ptr->transformations & PNG_USER_TRANSFORM)
- if (png_ptr->write_user_transform_fn != NULL)
- (*(png_ptr->write_user_transform_fn)) /* User write transform
- function */
- (png_ptr, /* png_ptr */
- row_info, /* row_info: */
- /* png_uint_32 width; width of row */
- /* png_size_t rowbytes; number of bytes in row */
- /* png_byte color_type; color type of pixels */
- /* png_byte bit_depth; bit depth of samples */
- /* png_byte channels; number of channels (1-4) */
- /* png_byte pixel_depth; bits per pixel (depth*channels) */
- png_ptr->row_buf + 1); /* start of pixel data for row */
-#endif
+ if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+ {
+ tc->range++;
+ tc->format |= PNG_FORMAT_FLAG_RANGE; /* forwards: backwards cancels */
+ }
-#ifdef PNG_WRITE_FILLER_SUPPORTED
- if (png_ptr->transformations & PNG_FILLER)
- png_do_strip_channel(row_info, png_ptr->row_buf + 1,
- !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
-#endif
+ tc->bit_depth = 8;
+ }
-#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
- if (png_ptr->transformations & PNG_PACKSWAP)
- png_do_packswap(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_PACK_SUPPORTED
- if (png_ptr->transformations & PNG_PACK)
- png_do_pack(row_info, png_ptr->row_buf + 1,
- (png_uint_32)png_ptr->bit_depth);
-#endif
-
-#ifdef PNG_WRITE_SWAP_SUPPORTED
- if (png_ptr->transformations & PNG_SWAP_BYTES)
- png_do_swap(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_SHIFT_SUPPORTED
- if (png_ptr->transformations & PNG_SHIFT)
- png_do_shift(row_info, png_ptr->row_buf + 1,
- &(png_ptr->shift));
-#endif
-
-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_SWAP_ALPHA)
- png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_INVERT_ALPHA)
- png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_BGR_SUPPORTED
- if (png_ptr->transformations & PNG_BGR)
- png_do_bgr(row_info, png_ptr->row_buf + 1);
-#endif
-
-#ifdef PNG_WRITE_INVERT_SUPPORTED
- if (png_ptr->transformations & PNG_INVERT_MONO)
- png_do_invert(row_info, png_ptr->row_buf + 1);
-#endif
+ else /* the transform is not applicable */
+ (*transform)->fn = NULL;
}
-#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */
-#endif /* PNG_WRITE_SUPPORTED */
+#endif /* WRITE_PACK */
diff --git a/ext/libpng17/pngwutil.c b/ext/libpng17/pngwutil.c
index df2ffdb9df..19052bfbaa 100644
--- a/ext/libpng17/pngwutil.c
+++ b/ext/libpng17/pngwutil.c
@@ -1,8 +1,14 @@
+#ifdef _MSC_VER
+#pragma warning (disable:4018)
+#pragma warning (disable:4028)
+#pragma warning (disable:4146)
+#pragma warning (disable:4334)
+#endif
/* pngwutil.c - utilities to write a PNG file
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
- * Copyright (c) 1998-2014 Glenn Randers-Pehrson
+ * Copyright (c) 1998-2002,2004,2006-2016 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.)
*
@@ -12,6 +18,7 @@
*/
#include "pngpriv.h"
+#define PNG_SRC_FILE PNG_SRC_FILE_pngwutil
#ifdef PNG_WRITE_SUPPORTED
@@ -23,10 +30,10 @@
void PNGAPI
png_save_uint_32(png_bytep buf, png_uint_32 i)
{
- buf[0] = (png_byte)((i >> 24) & 0xff);
- buf[1] = (png_byte)((i >> 16) & 0xff);
- buf[2] = (png_byte)((i >> 8) & 0xff);
- buf[3] = (png_byte)(i & 0xff);
+ buf[0] = PNG_BYTE(i >> 24);
+ buf[1] = PNG_BYTE(i >> 16);
+ buf[2] = PNG_BYTE(i >> 8);
+ buf[3] = PNG_BYTE(i);
}
/* Place a 16-bit number into a buffer in PNG byte order.
@@ -36,10 +43,10 @@ png_save_uint_32(png_bytep buf, png_uint_32 i)
void PNGAPI
png_save_uint_16(png_bytep buf, unsigned int i)
{
- buf[0] = (png_byte)((i >> 8) & 0xff);
- buf[1] = (png_byte)(i & 0xff);
+ buf[0] = PNG_BYTE(i >> 8);
+ buf[1] = PNG_BYTE(i);
}
-#endif
+#endif /* WRITE_INT_FUNCTIONS */
/* Simple function to write the signature. If we have already written
* the magic bytes of the signature, or more likely, the PNG stream is
@@ -59,7 +66,7 @@ png_write_sig(png_structrp png_ptr)
/* Write the rest of the 8 byte signature */
png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
- (png_size_t)(8 - png_ptr->sig_bytes));
+ (png_size_t)(8 - png_ptr->sig_bytes));
if (png_ptr->sig_bytes < 3)
png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
@@ -99,9 +106,7 @@ png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name,
png_ptr->chunk_name = chunk_name;
/* Reset the crc and run it over the chunk name */
- png_reset_crc(png_ptr);
-
- png_calculate_crc(png_ptr, buf + 4, 4);
+ png_reset_crc(png_ptr, buf+4);
#ifdef PNG_IO_STATE_SUPPORTED
/* Inform the I/O callback that chunk data will (possibly) be written.
@@ -124,7 +129,7 @@ png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string,
* given to png_write_chunk_header().
*/
void PNGAPI
-png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,
+png_write_chunk_data(png_structrp png_ptr, png_const_voidp data,
png_size_t length)
{
/* Write the data, and run the CRC over it */
@@ -136,7 +141,7 @@ png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,
png_write_data(png_ptr, data, length);
/* Update the CRC after writing the data,
- * in case that the user I/O routine alters it.
+ * in case the user I/O routine alters it.
*/
png_calculate_crc(png_ptr, data, length);
}
@@ -174,16 +179,16 @@ png_write_chunk_end(png_structrp png_ptr)
*/
static void
png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
- png_const_bytep data, png_size_t length)
+ png_const_voidp data, png_size_t length)
{
if (png_ptr == NULL)
return;
/* On 64 bit architectures 'length' may not fit in a png_uint_32. */
if (length > PNG_UINT_31_MAX)
- png_error(png_ptr, "length exceeds PNG maxima");
+ png_error(png_ptr, "length exceeds PNG maximum");
- png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
+ png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)/*SAFE*/length);
png_write_chunk_data(png_ptr, data, length);
png_write_chunk_end(png_ptr);
}
@@ -191,253 +196,132 @@ png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
/* This is the API that calls the internal function above. */
void PNGAPI
png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
- png_const_bytep data, png_size_t length)
+ png_const_voidp data, png_size_t length)
{
png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,
- length);
+ length);
}
-/* This is used below to find the size of an image to pass to png_deflate_claim,
- * so it only needs to be accurate if the size is less than 16384 bytes (the
- * point at which a lower LZ window size can be used.)
- */
static png_alloc_size_t
-png_image_size(png_structrp png_ptr)
-{
- /* Only return sizes up to the maximum of a png_uint_32, do this by limiting
- * the width and height used to 15 bits.
+png_write_row_buffer_size(png_const_structrp png_ptr)
+ /* Returns the width of the widest pass in the first row of an interlaced
+ * image. Passes in the first row are: 0.5.3.5.1.5.3.5, so the widest row is
+ * normally the one from pass 5. The only exception is if the image is only
+ * one pixel wide, so:
*/
- png_uint_32 h = png_ptr->height;
+#define PNG_FIRST_ROW_MAX_WIDTH(w) (w > 1U ? PNG_PASS_COLS(w, 5U) : 1U)
- if (png_ptr->rowbytes < 32768 && h < 32768)
+ /* For interlaced images the count of pixels is rounded up to a the number of
+ * pixels in the first pass (numbered 0). This ensures that passes before
+ * the last can be packed in the buffer without overflow.
+ */
+{
+ png_alloc_size_t w;
+
+ /* If the image is interlaced adjust 'w' for the interlacing: */
+ if (png_ptr->interlaced != PNG_INTERLACE_NONE)
{
- if (png_ptr->interlaced)
- {
- /* Interlacing makes the image larger because of the replication of
- * both the filter byte and the padding to a byte boundary.
- */
- png_uint_32 w = png_ptr->width;
- unsigned int pd = png_ptr->pixel_depth;
- png_alloc_size_t cb_base;
- int pass;
-
- for (cb_base=0, pass=0; pass<=6; ++pass)
- {
- png_uint_32 pw = PNG_PASS_COLS(w, pass);
-
- if (pw > 0)
- cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass);
- }
-
- return cb_base;
- }
+ /* Take advantage of the fact that 1-row interlaced PNGs require half the
+ * normal row width:
+ */
+ if (png_ptr->height == 1U) /* no pass 6 */
+ w = PNG_FIRST_ROW_MAX_WIDTH(png_ptr->width);
+ /* Otherwise round up to a multiple of 8. This may waste a few (less
+ * than 8) bytes for PNGs with a height less than 57 but this hardly
+ * matters.
+ */
else
- return (png_ptr->rowbytes+1) * h;
+ w = (png_ptr->width + 7U) & ~7U;
}
else
- return 0xffffffffU;
+ w = png_ptr->width;
+
+ /* The rounding above may leave 'w' exactly 2^31 */
+ debug(w <= 0x80000000U);
+
+ switch (png_ptr->row_output_pixel_depth)
+ {
+ /* This would happen if the function is called before png_write_IHDR. */
+ default: NOT_REACHED; return 0;
+
+ case 1: w = (w+7) >> 3; break;
+ case 2: w = (w+3) >> 2; break;
+ case 4: w = (w+1) >> 1; break;
+ case 8: break;
+ case 16: w <<= 1; break; /* overflow: w is set to 0, which is OK */
+
+ /* For the remaining cases the answer is w*bytes; where bytes is 3,4,6
+ * or 8. This may overflow 32 bits. There is no way to compute the
+ * result on an arbitrary platform, so test the maximum of a (size_t)
+ * against w for each possible byte depth:
+ */
+# define CASE(b)\
+ case b*8:\
+ if (w <= (PNG_SIZE_MAX/b)/*compile-time constant*/)\
+ return w * b;\
+ return 0;
+
+ CASE(3)
+ CASE(4)
+ CASE(6)
+ CASE(8)
+
+# undef CASE
+ }
+
+ /* This is the low bit depth case. The following can never be false on
+ * systems with a 32-bit or greater size_t:
+ */
+ if (w <= PNG_SIZE_MAX)
+ return w;
+
+ return 0U;
}
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
- /* This is the code to hack the first two bytes of the deflate stream (the
- * deflate header) to correct the windowBits value to match the actual data
- * size. Note that the second argument is the *uncompressed* size but the
- * first argument is the *compressed* data (and it must be deflate
- * compressed.)
- */
+/* Release memory used by the deflate mechanism */
static void
-optimize_cmf(png_bytep data, png_alloc_size_t data_size)
+png_deflateEnd(png_const_structrp png_ptr, z_stream *zs, int check)
{
- /* Optimize the CMF field in the zlib stream. The resultant zlib stream is
- * still compliant to the stream specification.
- */
- if (data_size <= 16384) /* else windowBits must be 15 */
+ if (zs->state != NULL)
{
- unsigned int z_cmf = data[0]; /* zlib compression method and flags */
+ int ret = deflateEnd(zs);
- if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+ /* Z_DATA_ERROR means there was pending output. */
+ if ((ret != Z_OK && (check || ret != Z_DATA_ERROR)) || zs->state != NULL)
{
- unsigned int z_cinfo;
- unsigned int half_z_window_size;
+ png_zstream_error(zs, ret);
- z_cinfo = z_cmf >> 4;
- half_z_window_size = 1U << (z_cinfo + 7);
-
- if (data_size <= half_z_window_size) /* else no change */
- {
- unsigned int tmp;
-
- do
- {
- half_z_window_size >>= 1;
- --z_cinfo;
- }
- while (z_cinfo > 0 && data_size <= half_z_window_size);
-
- z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
- data[0] = (png_byte)z_cmf;
- tmp = data[1] & 0xe0;
- tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
- data[1] = (png_byte)tmp;
- }
- }
- }
-}
-#else
-# define optimize_cmf(dp,dl) ((void)0)
-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
-
-/* Initialize the compressor for the appropriate type of compression. */
-static int
-png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
- png_alloc_size_t data_size)
-{
- if (png_ptr->zowner != 0)
- {
- char msg[64];
-
- PNG_STRING_FROM_CHUNK(msg, owner);
- msg[4] = ':';
- msg[5] = ' ';
- PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
- /* So the message that results is " using zstream"; this is an
- * internal error, but is very useful for debugging. i18n requirements
- * are minimal.
- */
- (void)png_safecat(msg, (sizeof msg), 10, " using zstream");
-# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
- png_warning(png_ptr, msg);
-
- /* Attempt sane error recovery */
- if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
- {
- png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
- return Z_STREAM_ERROR;
- }
-
- png_ptr->zowner = 0;
-# else
- png_error(png_ptr, msg);
-# endif
- }
-
- {
- int level = png_ptr->zlib_level;
- int method = png_ptr->zlib_method;
- int windowBits = png_ptr->zlib_window_bits;
- int memLevel = png_ptr->zlib_mem_level;
- int strategy; /* set below */
- int ret; /* zlib return code */
-
- if (owner == png_IDAT)
- {
- if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)
- strategy = png_ptr->zlib_strategy;
-
- else if (png_ptr->do_filter != PNG_FILTER_NONE)
- strategy = PNG_Z_DEFAULT_STRATEGY;
+ if (check)
+ png_error(png_ptr, zs->msg);
else
- strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY;
+ png_warning(png_ptr, zs->msg);
+
+ zs->state = NULL;
}
-
- else
- {
-# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
- level = png_ptr->zlib_text_level;
- method = png_ptr->zlib_text_method;
- windowBits = png_ptr->zlib_text_window_bits;
- memLevel = png_ptr->zlib_text_mem_level;
- strategy = png_ptr->zlib_text_strategy;
-# else
- /* If customization is not supported the values all come from the
- * IDAT values except for the strategy, which is fixed to the
- * default. (This is the pre-1.6.0 behavior too, although it was
- * implemented in a very different way.)
- */
- strategy = Z_DEFAULT_STRATEGY;
-# endif
- }
-
- /* Adjust 'windowBits' down if larger than 'data_size'; to stop this
- * happening just pass 32768 as the data_size parameter. Notice that zlib
- * requires an extra 262 bytes in the window in addition to the data to be
- * able to see the whole of the data, so if data_size+262 takes us to the
- * next windowBits size we need to fix up the value later. (Because even
- * though deflate needs the extra window, inflate does not!)
- */
- if (data_size <= 16384)
- {
- /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
- * work round a Microsoft Visual C misbehavior which, contrary to C-90,
- * widens the result of the following shift to 64-bits if (and,
- * apparently, only if) it is used in a test.
- */
- unsigned int half_window_size = 1U << (windowBits-1);
-
- while (data_size + 262 <= half_window_size)
- {
- half_window_size >>= 1;
- --windowBits;
- }
- }
-
- /* Check against the previous initialized values, if any. */
- if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) &&
- (png_ptr->zlib_set_level != level ||
- png_ptr->zlib_set_method != method ||
- png_ptr->zlib_set_window_bits != windowBits ||
- png_ptr->zlib_set_mem_level != memLevel ||
- png_ptr->zlib_set_strategy != strategy))
- {
- if (deflateEnd(&png_ptr->zstream) != Z_OK)
- png_warning(png_ptr, "deflateEnd failed (ignored)");
-
- png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
- }
-
- /* For safety clear out the input and output pointers (currently zlib
- * doesn't use them on Init, but it might in the future).
- */
- png_ptr->zstream.next_in = NULL;
- png_ptr->zstream.avail_in = 0;
- png_ptr->zstream.next_out = NULL;
- png_ptr->zstream.avail_out = 0;
-
- /* Now initialize if required, setting the new parameters, otherwise just
- * to a simple reset to the previous parameters.
- */
- if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
- ret = deflateReset(&png_ptr->zstream);
-
- else
- {
- ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,
- memLevel, strategy);
-
- if (ret == Z_OK)
- png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
- }
-
- /* The return code is from either deflateReset or deflateInit2; they have
- * pretty much the same set of error codes.
- */
- if (ret == Z_OK)
- png_ptr->zowner = owner;
-
- else
- png_zstream_error(png_ptr, ret);
-
- return ret;
}
}
-/* Clean up (or trim) a linked list of compression buffers. */
-void /* PRIVATE */
-png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
+/* compression_buffer (new in 1.6.0) is just a linked list of temporary buffers. * From 1.6.0 it is retained in png_struct so that it will be correctly freed in
+ * the event of a write error (previous implementations just leaked memory.)
+ *
+ * From 1.7.0 the size is fixed to the same as the (uncompressed) row buffer
+ * size. This avoids allocating a large chunk of memory when compressing small
+ * images. This type is also opaque outside this file.
+ */
+typedef struct png_compression_buffer
+{
+ struct png_compression_buffer *next;
+ png_byte output[PNG_ROW_BUFFER_SIZE];
+} png_compression_buffer, *png_compression_bufferp;
+
+/* png_compression_buffer methods */
+/* Deleting a compression buffer deletes the whole list: */
+static void
+png_free_compression_buffer(png_const_structrp png_ptr,
+ png_compression_bufferp *listp)
{
png_compression_bufferp list = *listp;
@@ -456,43 +340,1501 @@ png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
}
}
-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
-/* This pair of functions encapsulates the operation of (a) compressing a
- * text string, and (b) issuing it later as a series of chunk data writes.
- * The compression_state structure is shared context for these functions
- * set up by the caller to allow access to the relevant local variables.
+/* Return the next compression buffer in the list, allocating it if necessary.
+ * The caller must update 'end' if required; this just moves down the list.
+ */
+static png_compression_bufferp
+png_get_compression_buffer(png_const_structrp png_ptr,
+ png_compression_bufferp *end)
+{
+ png_compression_bufferp next = *end;
+
+ if (next == NULL)
+ {
+ next = png_voidcast(png_compression_bufferp, png_malloc_base(png_ptr,
+ sizeof *next));
+
+ /* Check for OOM: this is a recoverable error for non-critical chunks, let
+ * the caller decide what to do rather than issuing a png_error here.
+ */
+ if (next != NULL)
+ {
+ next->next = NULL; /* initialize the buffer */
+ *end = next;
+ }
+ }
+
+ return next; /* may still be NULL on OOM */
+}
+
+/* This structure is used to hold all the data for zlib compression of a single
+ * stream of data. It may be re-used, it stores the compressed data internally
+ * and can handle arbitrary input and output.
*
- * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
- * temporary buffers. From 1.6.0 it is retained in png_struct so that it will
- * be correctly freed in the event of a write error (previous implementations
- * just leaked memory.)
+ * 'list' is the output data contained in compression buffers, 'end' points to
+ * list at the start and is advanced down the compression buffer list (extending
+ * it as required) as the data is written. If 'end' points into a compression
+ * buffer (does not point to 'list') that is the buffer in use in
+ * z_stream::{next,avail}_out.
+ *
+ * Compression may be performed in multiple steps, '*end' always points to the
+ * compression buffer *after* the one that is in use, so 'end' is pointing
+ * *into* the one in use.
+ *
+ * end(on entry) .... end ....... end(on exit)
+ * | | |
+ * | | |
+ * V +----V-----+ +-----V----+ +----------+
+ * list ---> | next --+--> | next --+--> | next |
+ * | output[] | | output[] | | output[] |
+ * +----------+ +----------+ +----------+
+ * [in use] [unused]
+ *
+ * These invariants should always hold:
+ *
+ * 1) If zs.state is NULL decompression is not in progress, list may be non-NULL
+ * but end could be anything;
+ *
+ * 2) Otherwise if zs.next_out is NULL list will be NULL and end will point at
+ * list, len, overflow and start will be 0;
+ *
+ * 3) Otherwise list is non-NULL and end points at the 'next' element of an
+ * in-use compression buffer. zs.next_out points into the 'output' element
+ * of the same buffer. {overflow, len} is the amount of compressed data, len
+ * being the low 31 bits, overflow being the higher bits. start is used for
+ * writing and is the index of the first byte in list->output to write,
+ * {overflow, len} does not include start.
*/
typedef struct
{
- png_const_bytep input; /* The uncompressed input data */
- png_alloc_size_t input_len; /* Its length */
- png_uint_32 output_len; /* Final compressed length */
- png_byte output[1024]; /* First block of output */
-} compression_state;
+ z_stream zs; /* zlib compression data */
+ png_compression_bufferp list; /* Head of the buffer list */
+ png_compression_bufferp *end; /* Pointer to last 'next' pointer */
+ png_uint_32 len; /* Bottom 31 bits of data length */
+ unsigned int overflow; /* Top bits of data length */
+ unsigned int start; /* Start of data in first block */
+} png_zlib_compress, *png_zlib_compressp;
+/* png_zlib_compress methods */
+/* Initialize the compress structure. The z_stream itself is not initialized,
+ * however the the 'user' fields are set, including {next,avail}_{in,out}. The
+ * initialization does not change 'list', however it does set 'end' to point to
+ * it, effectively truncating the list.
+ */
static void
-png_text_compress_init(compression_state *comp, png_const_bytep input,
- png_alloc_size_t input_len)
+png_zlib_compress_init(png_structrp png_ptr, png_zlib_compressp pz)
{
- comp->input = input;
- comp->input_len = input_len;
- comp->output_len = 0;
+ /* png_zlib_compress z_stream: */
+ pz->zs.zalloc = png_zalloc;
+ pz->zs.zfree = png_zfree;
+ /* NOTE: this does not destroy 'restrict' because in all the functions herein
+ * *png_ptr is only ever accessed via *either* pz->zs.opaque *or* a passed in
+ * png_ptr.
+ */
+ pz->zs.opaque = png_ptr;
+
+ pz->zs.next_in = NULL;
+ pz->zs.avail_in = 0U;
+ pz->zs.total_in = 0U;
+
+ pz->zs.next_out = NULL;
+ pz->zs.avail_out = 0U;
+ pz->zs.total_out = 0U;
+
+ pz->zs.msg = PNGZ_MSG_CAST("zlib success"); /* safety */
+
+ /* pz->list preserved */
+ pz->end = &pz->list;
+ pz->len = 0U;
+ pz->overflow = 0U;
+ pz->start = 0U;
}
-/* Compress the data in the compression state input */
-static int
-png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
- compression_state *comp, png_uint_32 prefix_len)
-{
- int ret;
+/* Return the png_ptr: this is defined here for all the remaining
+ * png_zlib_compress methods because they are only ever called with zs
+ * initialized.
+ */
+#define png_ptr png_voidcast(png_const_structrp, pz->zs.opaque)
+#if PNG_RELEASE_BUILD
+# define png_zlib_compress_validate(pz, in_use) ((void)0)
+#else /* !RELEASE_BUILD */
+static void
+png_zlib_compress_validate(png_zlib_compressp pz, int in_use)
+{
+ const uInt o_size = sizeof pz->list->output;
+
+ affirm(pz->end != NULL && (in_use || (pz->zs.next_in == NULL &&
+ pz->zs.avail_in == 0U && *pz->end == NULL)));
+
+ if (pz->overflow == 0U && pz->len == 0U && pz->start == 0U) /* empty */
+ {
+ affirm((pz->end == &pz->list && pz->zs.next_out == NULL
+ && pz->zs.avail_out == 0U) ||
+ (pz->list != NULL && pz->end == &pz->list->next &&
+ pz->zs.next_out == pz->list->output &&
+ pz->zs.avail_out == o_size));
+ }
+
+ else /* not empty */
+ {
+ png_compression_bufferp *ep = &pz->list, list;
+ png_uint_32 o, l;
+
+ affirm(*ep != NULL && pz->zs.next_out != NULL);
+
+ /* Check the list length: */
+ o = pz->overflow;
+ l = pz->len;
+ affirm((l & 0x80000000U) == 0U && (o & 0x80000000U) == 0U);
+
+ do
+ {
+ list = *ep;
+ l -= o_size;
+ if (l & 0x80000000U) --o, l &= 0x7FFFFFFFU;
+ ep = &list->next;
+ }
+ while (ep != pz->end);
+
+ l += pz->start;
+ l += pz->zs.avail_out;
+ if (l & 0x80000000U) ++o, l &= 0x7FFFFFFFU;
+
+ affirm(o == 0U && l == 0U && pz->zs.next_out >= list->output &&
+ pz->zs.next_out + pz->zs.avail_out == list->output + o_size);
+ }
+}
+#endif /* !RELEASE_BUILD */
+
+/* Destroy one zlib compress structure. */
+static void
+png_zlib_compress_destroy(png_zlib_compressp pz, int check)
+{
+ /* If the 'opaque' pointer is NULL this png_zlib_compress was never
+ * initialized, so do nothing.
+ */
+ if (png_ptr != NULL)
+ {
+ if (pz->zs.state != NULL)
+ {
+ if (check)
+ png_zlib_compress_validate(pz, 0/*in_use*/);
+
+ png_deflateEnd(png_ptr, &pz->zs, check);
+ }
+
+ pz->end = &pz->list; /* safety */
+ png_free_compression_buffer(png_ptr, &pz->list);
+ }
+}
+
+/* Ensure that space is available for output, returns the amount of space
+ * available, 0 on OOM. This updates pz->zs.avail_out (etc) as required.
+ */
+static uInt
+png_zlib_compress_avail_out(png_zlib_compressp pz)
+{
+ uInt avail_out = pz->zs.avail_out;
+
+ png_zlib_compress_validate(pz, 1/*in_use*/);
+
+ if (avail_out == 0U)
+ {
+ png_compression_bufferp next;
+
+ affirm(pz->end == &pz->list || (pz->end != NULL && pz->list != NULL));
+ next = png_get_compression_buffer(png_ptr, pz->end);
+
+ if (next != NULL)
+ {
+ pz->zs.next_out = next->output;
+ pz->zs.avail_out = avail_out = sizeof next->output;
+ pz->end = &next->next;
+ }
+
+ /* else return 0: OOM */
+ }
+
+ else
+ affirm(pz->end != NULL && pz->list != NULL);
+
+ return avail_out;
+}
+
+/* Compress the given data given an initialized png_zlib_compress structure.
+ * This may be called multiple times, interleaved with writes as required.
+ *
+ * The input data is passed in in pz->zs.next_in, however the length of the data
+ * is in 'input_len' (to avoid the zlib uInt limit) and pz->zs.avail_in is
+ * overwritten (and left at 0).
+ *
+ * The output information is used and the amount of compressed data is added on
+ * to pz->{overflow,len}.
+ *
+ * If 'limit' is a limit on the amount of data to add to the output (not the
+ * total amount). The function will retun Z_BUF_ERROR if the limit is reached
+ * and the function will never produce more (additional) compressed data than
+ * the limit.
+ *
+ * All of zstream::next_in[input] is consumed if a success code is returned
+ * (Z_OK or Z_STREAM_END if flush is Z_FINISH), otherwise next_in may be used to
+ * determine how much was compressed.
+ *
+ * pz->overflow is not checked for overflow, so if 'limit' is not set overflow
+ * is possible. The caller must guard against this when supplying a limit of 0.
+ */
+static int
+png_compress(
+ png_zlib_compressp pz,
+ png_alloc_size_t input_len, /* Length of data to be compressed */
+ png_uint_32 limit, /* Limit on amount of compressed data made */
+ int flush) /* Flush parameter at end of input */
+{
+ const int unlimited = (limit == 0U);
+
+ /* Sanity checking: */
+ affirm(pz->zs.state != NULL &&
+ (pz->zs.next_out == NULL
+ ? pz->end == &pz->list && pz->len == 0U && pz->overflow == 0U
+ : pz->list != NULL && pz->end != NULL));
+ implies(pz->zs.next_out == NULL, pz->zs.avail_out == 0);
+
+ for (;;)
+ {
+ uInt extra;
+
+ /* OUTPUT: make sure some space is available: */
+ if (png_zlib_compress_avail_out(pz) == 0U)
+ return Z_MEM_ERROR;
+
+ /* INPUT: limit the deflate call input to ZLIB_IO_MAX: */
+ /* Adjust the input counters: */
+ {
+ uInt avail_in = ZLIB_IO_MAX;
+
+ if (avail_in > input_len)
+ avail_in = (uInt)/*SAFE*/input_len;
+
+ input_len -= avail_in;
+ pz->zs.avail_in = avail_in;
+ }
+
+ if (!unlimited && pz->zs.avail_out > limit)
+ {
+ extra = (uInt)/*SAFE*/(pz->zs.avail_out - limit); /* unused bytes */
+ pz->zs.avail_out = (uInt)/*SAFE*/limit;
+ limit = 0U;
+ }
+
+ else
+ {
+ extra = 0U;
+ limit -= pz->zs.avail_out; /* limit >= 0U */
+ }
+
+ pz->len += pz->zs.avail_out; /* maximum that can be produced */
+
+ /* Compress the data */
+ {
+ int ret = deflate(&pz->zs, input_len > 0U ? Z_NO_FLUSH : flush);
+
+ /* Claw back input data that was not consumed (because avail_in is
+ * reset above every time round the loop) and correct the output
+ * length.
+ */
+ input_len += pz->zs.avail_in;
+ pz->zs.avail_in = 0; /* safety */
+ pz->len -= pz->zs.avail_out;
+
+ if (pz->len & 0x80000000U)
+ ++pz->overflow, pz->len &= 0x7FFFFFFFU;
+
+ limit += pz->zs.avail_out;
+ pz->zs.avail_out += extra;
+
+ /* Check the error code: */
+ switch (ret)
+ {
+ case Z_OK:
+ if (pz->zs.avail_out > extra)
+ {
+ /* zlib had output space, so all the input should have been
+ * consumed:
+ */
+ affirm(input_len == 0U /* else unexpected stop */ &&
+ flush != Z_FINISH/* ret != Z_STREAM_END */);
+ return Z_OK;
+ }
+
+ else
+ {
+ /* zlib ran out of output space, produce some more. If the
+ * limit is 0 at this point, however, no more space is
+ * available.
+ */
+ if (unlimited || limit > 0U)
+ break; /* Allocate more output */
+
+ /* No more output space available, but the input may have all
+ * been consumed.
+ */
+ if (input_len == 0U && flush != Z_FINISH)
+ return Z_OK;
+
+ /* Input all consumed, but insufficient space to flush the
+ * output; this is the Z_BUF_ERROR case.
+ */
+ return Z_BUF_ERROR;
+ }
+
+ case Z_STREAM_END:
+ affirm(input_len == 0U && flush == Z_FINISH);
+ return Z_STREAM_END;
+
+ case Z_BUF_ERROR:
+ /* This means that we are flushing all the output; expect
+ * avail_out and input_len to be 0.
+ *
+ * NOTE: if png_compress is called with input_len 0 and flush set
+ * to Z_NO_FLUSH this affirm will fire because zlib will have no
+ * work to do.
+ */
+ affirm(input_len == 0U && pz->zs.avail_out == extra);
+ /* Allocate another buffer */
+ break;
+
+ default:
+ /* An error */
+ return ret;
+ }
+ }
+ }
+}
+
+#undef png_ptr /* remove definition using a png_zlib_compressp */
+
+/* All the compression state is held here, it is allocated when required. This
+ * ensures that the read code doesn't carry the overhead of the much less
+ * frequently used write stuff.
+ *
+ * TODO: make png_create_write_struct allocate this stuff after the main
+ * png_struct.
+ */
+struct filter_selector; /* Used only for filter selection */
+
+typedef struct png_zlib_state
+{
+ png_zlib_compress s; /* Primary compression state */
+ png_compression_bufferp stash; /* Unused compression buffers */
+
+# define ps_png_ptr(ps) png_upcast(png_const_structrp, (ps)->s.zs.opaque)
+ /* A png_ptr, used below in functions that only have a png_zlib_state.
+ * NOTE: the png_zlib_compress must have been initialized!
+ */
+
+ png_uint_32 zlib_max_pixels;
+ /* Maximum number of pixels that zlib can handle at once; the lesser of
+ * the PNG maximum and the maximum that will fit in (uInt)-1 bytes. This
+ * number of pixels may not be byte aligned.
+ */
+ png_uint_32 zlib_max_aligned_pixels;
+ /* The maximum number of pixels that zlib can handle while maintaining a
+ * buffer byte alignment of PNG_ROW_BUFFER_BYTE_ALIGN; <= the previous
+ * value.
+ */
+
+ png_alloc_size_t write_row_size;
+ /* Size of the PNG row (without the filter byte) in bytes or 0 if it is
+ * too large to be cached.
+ */
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* During write libpng needs the previous row when writing a new row with
+ * up, avg or paeth and one or more image rows when performing filter
+ * selection. So if performing filter selection typically two or more
+ * rows are required while if no filter selection is to be done only the
+ * previous row pointer is required.
+ */
+ png_bytep previous_write_row; /* Last row written, if any */
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ png_bytep current_write_row; /* Row being written */
+ struct filter_selector *selector; /* Data for filter selection */
+ png_uint_32 filter_select_window;
+ /* The number of bytes of uncompressed PNG data which are assumed to
+ * be relevant when doing filter selection. Limited to 8453377
+ * (about 2^23); the maximum number of bytes that can be encoded in
+ * the largest deflate window.
+ */
+# define PNG_FILTER_SELECT_WINDOW_MAX 8453377U
+ png_byte filter_select_threshold;
+ /* If the number of distinct codes seen in the PNG data are below
+ * this threshold the PNG data will not be filtered (if the 'none'
+ * filter is allowed). If this is still true and a particular
+ * filter does not add new codes that filter will be used.
+ */
+ png_byte filter_select_threshold2;
+ /* If the number of distinct codes that result by using a particular
+ * filter is below this second threshold that filter will be used.
+ * (When multiple filters pass this criterion the lowest numbered
+ * one producing the lowest number of new codes will be
+ * chosen.)
+ */
+# endif /* SELECT_FILTER */
+
+ unsigned int row_buffer_max_pixels;
+ /* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE
+ * bytes; not necessary a whole number of bytes.
+ */
+ unsigned int row_buffer_max_aligned_pixels;
+ /* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE
+ * bytes while maintaining PNG_ROW_BUFFER_BYTE_ALIGN alignment.
+ */
+
+ unsigned int filter_mask :8; /* mask of filters to consider on NEXT row */
+# define PREVIOUS_ROW_FILTERS\
+ (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)
+ unsigned int filters :8; /* Filters for current row */
+ unsigned int save_row :2; /* As below: */
+# define SAVE_ROW_UNSET 0U
+# define SAVE_ROW_OFF 1U /* Previous-row filters will be ignored */
+# define SAVE_ROW_DEFAULT 2U /* Default to save rows set by libpng */
+# define SAVE_ROW_ON 3U /* Force rows to be saved */
+# define SAVE_ROW(ps) ((ps)->save_row >= SAVE_ROW_DEFAULT)
+# endif /* WRITE_FILTER */
+
+ /* Compression settings: see below for how these are encoded. */
+ png_uint_32 pz_IDAT; /* Settings for the image */
+ png_uint_32 pz_iCCP; /* Settings for iCCP chunks */
+ png_uint_32 pz_text; /* Settings for text chunks */
+ png_uint_32 pz_current; /* Last set settings */
+
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
+ png_uint_32 flush_rows; /* number of rows written since last flush */
+# endif /* WRITE_FLUSH */
+} png_zlib_state;
+
+/* Create the zlib state: */
+static void
+png_create_zlib_state(png_structrp png_ptr)
+{
+ png_zlib_statep ps = png_voidcast(png_zlib_state*,
+ png_malloc(png_ptr, sizeof *ps));
+
+ /* Clear to NULL/0: */
+ memset(ps, 0, sizeof *ps);
+
+ debug(png_ptr->zlib_state == NULL);
+ png_ptr->zlib_state = ps;
+ png_zlib_compress_init(png_ptr, &ps->s);
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ ps->previous_write_row = NULL;
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ ps->current_write_row = NULL;
+ ps->selector = NULL;
+# endif /* SELECT_FILTER */
+# endif /* WRITE_FILTER */
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ /* Set this to prevent flushing by making it larger than the number
+ * of rows in the largest interlaced PNG; PNG_UINT_31_MAX times
+ * (1/8+1/8+1/8+1/4+1/4+1/2+1/2); 1.875, or 15/8
+ */
+ ps->flush_dist = 0xEFFFFFFFU;
+# endif /* WRITE_FLUSH */
+}
+
+static void
+png_zlib_state_set_buffer_limits(png_const_structrp png_ptr, png_zlib_statep ps)
+ /* Delayed initialization of the zlib state maxima; this is not done above in
+ * case the zlib_state is created before the IHDR has been written, which
+ * would lead to the various png_struct fields used below being
+ * uninitialized.
+ */
+{
+ /* Initialization of the buffer size constants. */
+ const unsigned int bpp = PNG_PIXEL_DEPTH(*png_ptr);
+ const unsigned int byte_pp = bpp >> 3; /* May be 0 */
+ const unsigned int pixel_block =
+ /* Number of pixels required to maintain PNG_ROW_BUFFER_BYTE_ALIGN
+ * alignment. For multi-byte pixels use the first set bit to determine
+ * if the pixels have a greater alignment already.
+ */
+ bpp < 8U ?
+ PNG_ROW_BUFFER_BYTE_ALIGN * (8U/bpp) :
+ PNG_ROW_BUFFER_BYTE_ALIGN <= (byte_pp & -byte_pp) ?
+ 1U :
+ PNG_ROW_BUFFER_BYTE_ALIGN / (byte_pp & -byte_pp);
+
+ /* pixel_block must always be a power of two: */
+ debug(bpp > 0 && pixel_block > 0 &&
+ (pixel_block & -pixel_block) == pixel_block &&
+ ((8U*PNG_ROW_BUFFER_BYTE_ALIGN-1U) & (pixel_block*bpp)) == 0U);
+
+ /* Zlib maxima */
+ {
+ png_uint_32 max = (uInt)-1; /* max bytes */
+
+ if (bpp <= 8U)
+ {
+ /* Maximum number of bytes PNG can generate in the lower bit depth
+ * cases:
+ */
+ png_uint_32 png_max =
+ (0x7FFFFFFF + PNG_ADDOF(bpp)) >> PNG_SHIFTOF(bpp);
+
+ if (png_max < max)
+ max = 0x7FFFFFFF;
+ }
+
+ else /* bpp > 8U */
+ {
+ max /= byte_pp;
+ if (max > 0x7FFFFFFF)
+ max = 0x7FFFFFFF;
+ }
+
+ /* So this is the maximum number of pixels regardless of alignment: */
+ ps->zlib_max_pixels = max;
+
+ /* For byte alignment the value has to be a multiple of pixel_block and
+ * that is a power of 2, so:
+ */
+ ps->zlib_max_aligned_pixels = max & ~(pixel_block-1U);
+ }
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* PNG_ROW_BUFFER maxima; this is easier because PNG_ROW_BUFFER_SIZE is
+ * limited so that the number of bits fits in any ANSI-C (unsigned int).
+ */
+ {
+ const unsigned int max = (8U * PNG_ROW_BUFFER_SIZE) / bpp;
+
+ ps->row_buffer_max_pixels = max;
+ ps->row_buffer_max_aligned_pixels = max & ~(pixel_block-1U);
+ }
+# endif /* WRITE_FILTER */
+
+ /* NOTE: this will be 0 for very long rows on 32-bit or less systems */
+ ps->write_row_size = png_write_row_buffer_size(png_ptr);
+}
+
+static png_zlib_statep
+get_zlib_state(png_structrp png_ptr)
+{
+ if (png_ptr->zlib_state == NULL)
+ png_create_zlib_state(png_ptr);
+
+ return png_ptr->zlib_state;
+}
+
+/* Internal API to clean up all the deflate related stuff, including the buffer
+ * lists.
+ */
+static void /* PRIVATE */
+png_deflate_release(png_structrp png_ptr, png_zlib_statep ps, int check)
+{
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* Free any mode-specific data that is owned here: */
+ if (ps->previous_write_row != NULL)
+ {
+ png_bytep p = ps->previous_write_row;
+ ps->previous_write_row = NULL;
+ png_free(png_ptr, p);
+ }
+
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ if (ps->current_write_row != NULL)
+ {
+ png_bytep p = ps->current_write_row;
+ ps->current_write_row = NULL;
+ png_free(png_ptr, p);
+ }
+
+ if (ps->selector != NULL)
+ {
+ struct filter_selector *s = ps->selector;
+ ps->selector = NULL;
+ png_free(png_ptr, s);
+ }
+# endif /* SELECT_FILTER */
+# endif /* WRITE_FILTER */
+
+ /* The main z_stream opaque pointer needs to remain set to png_ptr; it is
+ * only set once.
+ */
+ png_zlib_compress_destroy(&ps->s, check);
+ png_free_compression_buffer(png_ptr, &ps->stash);
+}
+
+void /* PRIVATE */
+png_deflate_destroy(png_structrp png_ptr)
+{
+ png_zlib_statep ps = png_ptr->zlib_state;
+
+ if (ps != NULL)
+ {
+ png_deflate_release(png_ptr, ps, 0/*check*/);
+ png_ptr->zlib_state = NULL;
+ png_free(png_ptr, ps);
+ }
+}
+
+/* Compression settings.
+ *
+ * These are stored packed into a png_uint_32 to make comparison with the
+ * current setting quick. The packing method uses four bits for each setting
+ * and reserves '0' for unset.
+ *
+ * ps__base: The lowest valid value (encoded as 1).
+ * ps__max: The highest valid value.
+ * ps__pos: The position in the range 0..3 (shift of 0..12).
+ *
+ * The low 16 bits are the zlib compression parameters:
+ */
+#define pz_level_base (-1)
+#define pz_level_max 9
+#define pz_level_pos 0
+#define pz_windowBits_base 8
+#define pz_windowBits_max 15
+#define pz_windowBits_pos 1
+#define pz_memLevel_base 1
+#define pz_memLevel_max 9
+#define pz_memLevel_pos 2
+#define pz_strategy_base 0
+#define pz_strategy_max 4
+#define pz_strategy_pos 3
+#define pz_zlib_bits 0xFFFFU
+/* Anything below this is not used directly by zlib: */
+#define pz_png_level_base 0
+#define pz_png_level_max 6
+#define pz_png_level_pos 4
+
+#define pz_offset(name) (pz_ ## name ## _base - 1)
+ /* setting_value == pz_offset(setting)+encoded_value */
+#define pz_min(name) pz_ ## name ## _base
+#define pz_max(name) pz_ ## name ## _max
+#define pz_shift(name) (4 * pz_ ## name ## _pos)
+
+#define pz_bits(name,x) ((int)(((x)>>pz_shift(name))&0xF))
+ /* the encoded value, or 0 if unset */
+
+/* Enquiries: */
+#define pz_isset(name,x) (pz_bits(name,x) != 0)
+#define pz_value(name,x) (pz_bits(name,x)+pz_offset(name))
+
+/* Assignments: */
+#define pz_clear(name,x) ((x)&~((png_uint_32)0xFU<pz_ ## type)
+#define pz_get(ps, type, name, def)\
+ (pz_isset(name, pz_var(ps, type)) ? pz_value(name, pz_var(ps, type)) : (def))
+/* pz_assign checks for out-of-range values and clears the setting if these are
+ * given. No warning or error is generated.
+ */
+#define pz_assign(ps, type, name, value)\
+ (pz_var(ps, type) = pz_clear(name, pz_var(ps, type)) |\
+ ((value) >= pz_min(name) && (value) <= pz_max(name) ?\
+ pz_encode(name, value) : 0))
+
+static png_int_32
+pz_compression_setting(png_structrp png_ptr, png_uint_32 owner,
+ int min, int max, int shift, png_int_32 value, int only_get, int unset)
+ /* This is a support function for png_write_setting below. */
+{
+ png_zlib_statep ps;
+ png_uint_32p psettings;
+
+ /* The value is only required for a 'set', eliminate out-of-range values
+ * first:
+ */
+ if (!only_get && (value < min || value > max))
+ return PNG_EDOM;
+
+ /* If setting a value make sure the state exists: */
+ if (!only_get)
+ ps = get_zlib_state(png_ptr);
+
+ else if (owner != 0U) /* ps may be NULL */
+ ps = png_ptr->zlib_state;
+
+ else /* get and owner is 0U */
+ return 0; /* supported */
+
+ psettings = NULL;
+ switch (owner)
+ {
+ png_int_32 res;
+
+ case png_IDAT:
+ if (ps != NULL) psettings = &ps->pz_IDAT;
+ break;
+
+ case png_iCCP:
+ if (ps != NULL) psettings = &ps->pz_iCCP;
+ break;
+
+ case 0U:
+ /* All the settings. At this point the 'get' case has returned 0
+ * above, the value has been checked and the paramter is 0, therefore
+ * valid. Each of the following calls should succeed and it would be
+ * reasonable to eliminate the PNG_FAILED tests in a world where
+ * software engineers never made mistakes.
+ */
+ res = pz_compression_setting(png_ptr, png_IDAT, min, max, shift,
+ value, 0/*set*/, 1/*iff unset*/);
+
+ if (PNG_FAILED(res))
+ return res;
+
+ res = pz_compression_setting(png_ptr, png_iCCP, min, max, shift,
+ value, 0/*set*/, 1/*iff unset*/);
+
+ if (PNG_FAILED(res))
+ return res;
+
+ /* The text settings are changed regardless of the customize support
+ * because if WRITE_CUSTOMIZE_ZTXT_COMPRESSION is not supported the old
+ * behavior was to use the WRITE_CUSTOMIZE_COMPRESSION setting.
+ *
+ * However, when we get png_zTXt directly (from png_write_setting) and
+ * the support is not compiled in return PNG_ENOSYS.
+ */
+ unset = 1; /* i.e. only if not already set */
+
+# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+ case png_zTXt:
+ case png_iTXt:
+# endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+ if (ps != NULL) psettings = &ps->pz_text;
+ break;
+
+ default:
+ /* Return PNG_ENOSYS, not PNG_EINVAL, to support future addition of new
+ * compressed chunks and the fact that zTXt and iTXt customization can
+ * be disabled.
+ */
+ return PNG_ENOSYS;
+ }
+
+ if (psettings == NULL)
+ return PNG_UNSET; /* valid setting that is not set */
+
+ {
+ png_uint_32 settings = *psettings;
+ png_uint_32 mask = 0xFU << shift;
+
+ /* Do not set it if 'only_get' was passed in or if 'unset' is true and the
+ * setting is not currently set:
+ */
+ if (!only_get && ((settings & mask) == 0U || !unset))
+ *psettings = (settings & ~mask) +
+ ((png_uint_32)/*SAFE*/(value-min+1) << shift);
+
+ settings &= mask;
+
+ if (settings == 0U)
+ return PNG_UNSET;
+
+ else
+ return (int)/*SAFE*/((settings >> shift)-1U) + min;
+ }
+}
+
+#define compression_setting(pp, owner, setting, value, get)\
+ pz_compression_setting(pp, owner, pz_min(setting), pz_max(setting),\
+ pz_shift(setting), value, get, 0/*always*/)
+
+/* There is (as of zlib 1.2.8) a bug in the implementation of compression with a
+ * window size of 256 which zlib works round by resetting windowBits from 8 to 9
+ * whenever deflateInit2 is called with that value. Fix this up here.
+ */
+static void
+fix_cinfo(png_zlib_statep ps, png_bytep data, png_alloc_size_t data_size)
+{
+ /* Do this if the CINFO field is '1', meaning windowBits of 9. The first
+ * byte of the stream is the CMF value, CINFO is in the upper four bits.
+ *
+ * If zlib didn't futz with the value then it should match the value in
+ * pz_current; check this is debug. (See below for why this works in the
+ * pz_default_settings call.)
+ */
+# define png_ptr png_voidcast(png_const_structrp, ps->s.zs.opaque)
+ if (data[0] == 0x18U &&
+ pz_get(ps, current, windowBits, 0) == 8 /* i.e. it was requested */)
+ {
+ /* Double check this here; the fixup only works if the data was 256 bytes
+ * or shorter *or* the window is never used. For safety repeat the checks
+ * done in pz_default_settings; technically we should be able to just skip
+ * this test.
+ *
+ * TODO: set a 'fixup' flag in zlib_state to make this quicker?
+ */
+ if (data_size <= 256U ||
+ pz_get(ps, current, strategy, Z_RLE) == Z_HUFFMAN_ONLY ||
+ pz_get(ps, current, level, 1) == Z_NO_COMPRESSION)
+ {
+ unsigned int d1;
+
+ data[0] = 0x08U;
+ /* The header checksum must be fixed too. The FCHECK (low 5 bits) make
+ * CMF.FLG a multiple of 31:
+ */
+ d1 = data[1] & 0xE0U; /* top three bits */
+ d1 += 31U - (0x0800U + d1) % 31U;
+ data[1] = PNG_BYTE(d1);
+ }
+
+ else /* pz_default_settings is expected to guarantee the above */
+ NOT_REACHED;
+ }
+
+ else if (data_size > 0U)
+ {
+ /* Prior to 1.7.0 libpng would shrink the windowBits even if the
+ * application requested a particular value, so:
+ */
+ unsigned int z_cinfo = data[0] >> 4;
+ unsigned int half_z_window_size = 1U << (z_cinfo + 7);
+
+ if (data_size <= half_z_window_size && z_cinfo > 0)
+ {
+ unsigned int tmp;
+
+ do
+ {
+ half_z_window_size >>= 1;
+ --z_cinfo;
+ }
+ while (z_cinfo > 0 && data_size <= half_z_window_size);
+
+ data[0] = PNG_BYTE((z_cinfo << 4) + 0x8U);
+ tmp = data[1] & 0xE0U; /* top three bits */
+ tmp += 31U - ((data[0] << 8) + tmp) % 31U;
+ data[1] = PNG_BYTE(tmp);
+ }
+ }
+
+ else
+ NOT_REACHED; /* invalid data size (0) */
+# undef png_ptr
+}
+
+static png_uint_32
+pz_default_settings(png_uint_32 settings, const png_uint_32 owner,
+ const png_alloc_size_t data_size, const unsigned int filters/*for IDAT*/)
+{
+ int png_level, strategy, zlib_level, windowBits;
+
+ /* The png 'level' parameter controls the defaults below. It uses the same
+ * numbering scheme as the Zlib compression level except that -1 invokes the
+ * set of options and, in some cases, libpng behavior of libpng 1.6 and
+ * earlier.
+ *
+ * In the comments below reference is made to the differences beteen the
+ * legacy compression sizes from libpng 1.6 and earlier and the result of
+ * using the various options. These are quoted as an overall size change in
+ * the compression of 147323 PNG test files. The set of test files is
+ * slightly restricted because pre-1.7 versions of png_read_png leave random
+ * bits into the final byte of a row which ends with a partial byte. This
+ * affects the compression unpredictably so such files were omitted from the
+ * measurements.
+ */
+ if (!pz_isset(png_level, settings))
+ {
+ png_level = PNG_DEFAULT_COMPRESSION_LEVEL;
+ settings |= pz_encode(png_level, png_level);
+ }
+
+ else
+ png_level = pz_value(png_level, settings);
+
+ /* First default the strategy. At lower data sizes other strategies do as
+ * well as the zlib default compression strategy but they never seem to
+ * improve on it with the 1.7 filtering.
+ */
+ if (!pz_isset(strategy, settings))
+ {
+ switch (png_level)
+ {
+ case PNG_COMPRESSION_COMPAT: /* Legacy setting */
+ /* The pre-1.7 code used Z_FILTERED normally but uses
+ * Z_DEFAULT_STRATEGY for palette or low-bit-depth images.
+ *
+ * In fact Z_DEFAULT_STRATEGY works best for filtered images as
+ * well, however the change in results is small:
+ *
+ * Z_DEFAULT_STRATEGY: -0.1%
+ * Z_FILTERED: +0.1%
+ *
+ * NOTE: this happened even if WRITE_FILTER was *not* supported.
+ */
+ if (owner != png_IDAT || filters == PNG_FILTER_NONE)
+ strategy = Z_DEFAULT_STRATEGY;
+
+ else
+ strategy = Z_FILTERED;
+ break;
+
+ case PNG_COMPRESSION_HIGH_SPEED:
+ /* RLE is as fast as HUFFMAN_ONLY and can reduce size a lot in a few
+ * cases.
+ */
+ strategy = Z_RLE;
+ break;
+
+ default: /* For GCC */
+ case PNG_COMPRESSION_LOW:
+ case PNG_COMPRESSION_MEDIUM:
+ /* Z_FILTERED is almost as good as the default and can be
+ * significantly faster. It biases the algorithm towards smaller
+ * byte values.
+ *
+ * Using Z_DEFAULT_STRATEGY here, rather than Z_FILTERED, benefits
+ * smaller 8 and 16-bit gray and larger 8 and 16-bit RGB images,
+ * however the overall gain is only 0.1% because it is offset by
+ * losses in larger 8-bit gray and alpha images. It is extremely
+ * difficult to deduce a pattern other than biases in the test set
+ * of images.
+ *
+ * Looking at the pattern of behavior with the 1.6 filter selection
+ * algorithm (none of palette or low-bit-depth, else all) produces
+ * results as follows:
+ */
+ if (owner == png_IDAT)
+ {
+ if (filters == PNG_FILTER_NONE)
+ strategy = Z_DEFAULT_STRATEGY;
+
+ else
+ strategy = Z_FILTERED;
+ }
+
+ else if (owner == png_iCCP)
+ strategy = Z_DEFAULT_STRATEGY;
+
+ /* TODO: investigate this, the observed behavior is suspicious: */
+ else /* text chunk */
+ strategy = Z_FILTERED; /* Always better for some reason */
+ break;
+
+ case PNG_COMPRESSION_LOW_MEMORY:
+ /* Reduce memory at all costs, speed doesn't matter. */
+ case PNG_COMPRESSION_HIGH_READ_SPEED:
+ case PNG_COMPRESSION_HIGH:
+ if (owner == png_IDAT || owner == png_iCCP)
+ strategy = Z_DEFAULT_STRATEGY;
+
+ else
+ strategy = Z_FILTERED;
+ break;
+ }
+
+ settings |= pz_encode(strategy, strategy);
+ }
+
+ else
+ strategy = pz_value(strategy, settings);
+
+ /* Next the zlib level; this just defaults to the png level, except that for
+ * Huffman or RLE encoding the level setting for Zlib doesn't matter.
+ */
+ if (!pz_isset(level, settings))
+ {
+ switch (strategy)
+ {
+ case Z_HUFFMAN_ONLY:
+ case Z_RLE:
+ /* The 'level' doesn't make any significant difference to the
+ * compression with these strategies; in a test set of about 3GByte
+ * of PNG files the total compressed size changed under 20 bytes
+ * with libpng 1.6!
+ */
+ zlib_level = 1;
+ break;
+
+ default: /* Z_FIXED, Z_FILTERED, Z_DEFAULT_STRATEGY */
+ /* Everything that uses the window seems to show rapidly diminishing
+ * returns above level 6 (at least with libpng 1.6).
+ * Z_DEFAULT_COMPRESSION is, in fact, level 6 so Mark seems to
+ * concur. With libpng 1.6 the following results were obtained
+ * using the full test set of files (including those with a partial
+ * byte at the end of the row) and just varying the zlib level:
+ *
+ * LEVEL SIZE(bytes) CHANGE TIME(s) CHANGE METRIC
+ * 9 2550246600 -1.19% 1972 +227% -77%
+ * 8 2556675866 -0.94% 1215 +101% -59%
+ * 7 2572685552 -0.32% 679 +12% -15%
+ * 6 2581196708 0% 604 0% 0%
+ * 5 2602831249 +0.84% 414 -30% +87%
+ * 4 2625206800 +1.71% 358 -40% +153%
+ * 3 2674752349 +3.62% 298 -50% +303%
+ * 2 2716261483 +5.23% 262 -56% +537%
+ * 1 2749875805 +6.53% 251 -57% +662%
+ * 0 7174488347 202 -66%
+ *
+ * The CHANGE columns express the change in compressed size
+ * (positive is an increase; a decrease in compression) and time
+ * (positive is an increase; an increase in time) relative to level
+ * 6. The METRIC column is a measure of the compression-per-second
+ * relative to level 6; positive is an increase in
+ * compression-per-second.
+ *
+ * The metric is derived by assuming the difference in time between
+ * level 0 (which does no compression) and the level being
+ * considered is spent doing the compression. (Reasonable, since
+ * only the level changed). Just the inverse of the product of the
+ * size and the time difference is a measure of compression per
+ * second. It can be seen that time dominates the metric;
+ * compression only varies slightly (under 8%) across the level
+ * range.
+ */
+ switch (png_level)
+ {
+ case PNG_COMPRESSION_COMPAT:
+ zlib_level = Z_DEFAULT_COMPRESSION; /* NOTE: -1 */
+ break;
+
+ case PNG_COMPRESSION_HIGH_SPEED:
+ zlib_level = 1;
+ break;
+
+ default: /* For GCC */
+ case PNG_COMPRESSION_LOW:
+ zlib_level = 3;
+ break;
+
+ case PNG_COMPRESSION_MEDIUM:
+ zlib_level = 6; /* Old default! */
+ break;
+
+ case PNG_COMPRESSION_LOW_MEMORY:
+ case PNG_COMPRESSION_HIGH_READ_SPEED:
+ case PNG_COMPRESSION_HIGH:
+ zlib_level = 9;
+ break;
+ }
+ break;
+ }
+
+ settings |= pz_encode(level, zlib_level);
+ }
+
+ else
+ zlib_level = pz_value(level, settings);
+
+ /* Now default windowBits. This is probably the most important of the
+ * settings because it is pretty much the only one that affects decode
+ * performance. The smaller the better:
+ */
+ if (!pz_isset(windowBits, settings))
+ {
+ if (png_level == PNG_COMPRESSION_COMPAT/* Legacy */)
+ {
+ /* This is the libpng16 calculation (it is wrong; a misunderstanding of
+ * what zlib actually requires!)
+ *
+ * Using the code below with the legacy choice of Z_FILTERED or
+ * Z_DEFAULT_STRATEGY increases the size of the test files by only
+ * 0.04%, however the settings below considerably reduce the windowBits
+ * used potentially benefitting read code a lot.
+ *
+ * NOTE: the algorithm below was determined by experiment and
+ * observation with the same set of test files; there is some
+ * considerable possibility that a different set might show different
+ * results. Obtaining large, representative, test sets is both a
+ * considerable amount of work and very error prone. [JB 20160518]
+ */
+ windowBits = 15;
+
+ if (data_size <= 16384U)
+ {
+ unsigned int half_window_size = 1U << (windowBits-1);
+
+ while (data_size + 262U <= half_window_size)
+ {
+ half_window_size >>= 1;
+ --windowBits;
+ }
+ }
+ }
+
+ /* The window size affects the memory used on both read and write but also
+ * the time on write (but not normally read). Handle the low memory
+ * requirement first:
+ */
+ else if (zlib_level == Z_NO_COMPRESSION ||
+ png_level == PNG_COMPRESSION_LOW_MEMORY)
+ windowBits = 8;
+
+ /* If the strategy has been set to something that doesn't benefit from
+ * higher windowBits values take advantage of this. Note that pz_value
+ * returns an invalid value if pz_isset is false.
+ *
+ * The only png_level that affects this decision is HIGH_SPEED, because
+ * a smaller windowBits should speed up the search, however the code above
+ * chose zlib_level based on this so ignore that consideration and just
+ * use zlib_level below.
+ */
+ else switch (strategy)
+ {
+ png_alloc_size_t test_size;
+
+ case Z_HUFFMAN_ONLY:
+ /* Use the minimum; the window doesn't get used */
+ windowBits = 8;
+ break;
+
+ case Z_RLE:
+ /* The longest length code is 258 bytes, the shortest string that
+ * can achieve this is 259 bytes long; 259 copies of the same byte
+ * which can be encoded as a code for the byte value then a string
+ * of length 258 starting at the first byte. So if the data is
+ * longer than 256 bytes use '9' for the windowBits, otherwise use
+ * 8:
+ */
+ if (data_size <= 256U)
+ windowBits = 8;
+
+ else
+ windowBits = 9;
+ break;
+
+ /* By experiment using about 150,000 files the optimal windowBits
+ * value across a range of files is somewhat less than implied by
+ * the data size and depends on the zlib level and the strategy
+ * used, the following values were determined by experiment using
+ * those files:
+ */
+ case Z_FILTERED:
+ /* The Z_FILTERED case changes suddenly at (zlib) level 4 to
+ * benefit from looking at all the data:
+ */
+ if (zlib_level < 4 && zlib_level != Z_DEFAULT_COMPRESSION/*-1: 6*/)
+ test_size = data_size / 8U;
+
+ else
+ test_size = data_size;
+
+ goto check_test_size;
+
+ case Z_FIXED:
+ /* With the fixed Huffman tables better compression only ever comes
+ * from looking for matches, so, logically:
+ */
+ test_size = data_size;
+ goto check_test_size;
+
+ default:
+ /* The default algorithm always does better with a window smaller
+ * than all the data and shows jumps at level 4 and level 8. The
+ * net effect with the test set of images is a very minor overall
+ * improvement compared to the pre-1.7 calculation (data size +
+ * 262). The benefit is less than 0.01%, however smaller window
+ * sizes reduce the memory zlib has to allocate in the decoder.
+ */
+ switch (zlib_level)
+ {
+ case 1: case 2: case 3:
+ test_size = data_size / 8U;
+ break;
+
+ default: /* -1(Z_DEFAULT_COMPRESSION) == 6, 4..7 */
+ /* This includes, implicitly, ZLIB_NO_COMPRESSION, but that
+ * was eliminated in the 'if' above.
+ */
+ test_size = data_size / 4U;
+ break;
+
+ case 8: case 9:
+ test_size = data_size / 3U;
+ break;
+ }
+
+ goto check_test_size;
+
+ check_test_size:
+ /* Find the smallest window that covers 'test_size' bytes, subject
+ * to the constraint that if the actual data size is more than 256
+ * bytes the minimum windowBits that can be supported is 9:
+ */
+ if (data_size <= 256U)
+ windowBits = 8;
+
+ else
+ windowBits = 9;
+
+ while (windowBits < 15 && (1U << windowBits) < test_size)
+ ++windowBits;
+
+ break;
+ }
+
+ settings |= pz_encode(windowBits, windowBits);
+ }
+
+ else
+ windowBits = pz_value(windowBits, settings);
+
+ /* zlib has a problem with 256 byte windows; 512 is used instead.
+ * We can't work round this if the data size is more than 256 bytes and
+ * the strategy actually uses the window (everything except huffman-only)
+ * so fix the problem here.
+ */
+ if (windowBits == 8 && data_size > 256U && strategy != Z_HUFFMAN_ONLY &&
+ zlib_level != Z_NO_COMPRESSION)
+ settings = pz_change(windowBits, settings, 9);
+
+ /* For memLevel this just increases the memory used but can help with the
+ * Huffman code generation even to level 9 (the maximum), so just set the
+ * max. This affects memory used, not (apparently) compression speed so
+ * the only relevant png_level is LOW_MEMORY.
+ *
+ * The legacy setting is '8'; this is the level that Zlib defaults to because
+ * 16-bit iAPX86 systems could not handle '9'. Because MAX_MEM_LEVEL is used
+ * below this does not matter; zconf.h selects 8 or 9 as appropriate.
+ *
+ * In fact using '9' with the legacy settings increases the size of the test
+ * set minutely; +0.007%. This is hardly significant; 0.007% of the test
+ * images equals 10 images. (Nevertheless it is interesting, just as the
+ * observation that decreasing windowBits can result in smaller compressed
+ * sizes is interesting.)
+ */
+ if (!pz_isset(memLevel, settings))
+ {
+ int memLevel;
+
+ switch (png_level)
+ {
+ case PNG_COMPRESSION_COMPAT:
+ memLevel = 8;
+ break;
+
+ case PNG_COMPRESSION_LOW_MEMORY:
+ memLevel = 1;
+ break;
+
+ default:
+ memLevel = MAX_MEM_LEVEL/*from zconf.h*/;
+ break;
+ }
+
+ settings |= pz_encode(memLevel, memLevel);
+ }
+
+ return settings;
+}
+
+/* This is used below to find the size of an image to pass to png_deflate_claim.
+ * It returns 0 for images whose size would overflow a 32-bit integer or have
+ * rows which cannot be allocated.
+ */
+static png_alloc_size_t
+png_image_size(png_const_structrp png_ptr)
+{
+ /* The size returned here is limited to PNG_SIZE_MAX, if the size would
+ * exceed that (or is close to exceeding that) 0 is returned. See below for
+ * a variant that limits the size of 0xFFFFFFFFU.
+ */
+ const png_alloc_size_t rowbytes = png_ptr->zlib_state->write_row_size;
+
+ /* NON-INTERLACED: (1+rowbytes) * h
+ * INTERLACED: Each pixel is transmitted exactly once, so the size is
+ * (rowbytes * h) + the count of filter bytes. Each complete
+ * block of 8 image rows generates at most 15 output rows
+ * (less for narrow images), so the filter byte count is
+ * at most (15*h/8)+14. Because the original rows are split
+ * extra byte passing may be introduced. Account for this by
+ * allowing an extra 1 byte per output row; that's two bytes
+ * including the filer byte.
+ *
+ * So:
+ * NON-INTERLACED: (rowbytes * h) + h
+ * INTERLACED: < (rowbytes * h) + 2*(15 * h/8) + 2*15
+ *
+ * Hence:
+ */
+ if (rowbytes != 0)
+ {
+ const png_uint_32 h = png_ptr->height;
+
+ if (png_ptr->interlaced == PNG_INTERLACE_NONE)
+ {
+ const png_alloc_size_t limit = PNG_SIZE_MAX / h;
+
+ /* On 16-bit systems the above might be 0, so: */
+ if (rowbytes *allow 1 for filter byte*/ limit)
+ return (rowbytes+1U) * h;
+ }
+
+ else /* INTERLACED */
+ {
+ const png_uint_32 w = png_ptr->width;
+
+ /* Interlacing makes the image larger because of the replication of
+ * both the filter byte and the padding to a byte boundary.
+ */
+ png_alloc_size_t cb_base;
+ int pass;
+
+ for (cb_base=0, pass=0; pass 0)
+ {
+ const png_uint_32 pass_h = PNG_PASS_ROWS(h, pass);
+
+ if (pass_h > 0)
+ {
+ /* This is the number of bytes available for each row of this
+ * pass:
+ */
+ const png_alloc_size_t limit = (PNG_SIZE_MAX - cb_base)/pass_h;
+ /* This cannot overflow because if it did rowbytes would
+ * have been 0 above.
+ */
+ const png_alloc_size_t pass_bytes =
+ PNG_ROWBYTES(png_ptr->row_output_pixel_depth, pass_w);
+
+ if (pass_bytes *allow 1 for filter byte*/ limit)
+ cb_base += (pass_bytes+1U) * pass_h;
+
+ else
+ return 0U; /* insufficient address space left */
+ }
+ }
+ }
+
+ return cb_base;
+ }
+ }
+
+ /* Failure case: */
+ return 0U;
+}
+
+/* Initialize the compressor for the appropriate type of compression. */
+static png_zlib_statep
+png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
+ png_alloc_size_t data_size)
+{
+ png_zlib_statep ps = get_zlib_state(png_ptr);
+
+ affirm(png_ptr->zowner == 0);
+
+ {
+ int ret; /* zlib return code */
+ unsigned int filters = 0U;
+ png_uint_32 settings;
+
+ switch (owner)
+ {
+ case png_IDAT:
+ debug(data_size == 0U);
+ data_size = png_image_size(png_ptr);
+
+ if (data_size == 0U)
+ data_size = PNG_SIZE_MAX;
+
+ settings = ps->pz_IDAT;
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ filters = ps->filter_mask;
+ debug(filters != 0U);
+# else /* !WRITE_FILTER */
+ filters = PNG_FILTER_NONE;
+# endif /* !WRITE_FILTER */
+ break;
+
+ case png_iCCP:
+ settings = ps->pz_iCCP;
+ break;
+
+ default: /* text chunk */
+ settings = ps->pz_text;
+ break;
+ }
+
+ settings = pz_default_settings(settings, owner, data_size, filters);
+
+ /* Check against the previous initialized values, if any. The relevant
+ * settings are in the low 16 bits.
+ */
+ if (ps->s.zs.state != NULL &&
+ ((settings ^ ps->pz_current) & pz_zlib_bits) != 0U)
+ png_deflateEnd(png_ptr, &ps->s.zs, 0/*check*/);
+
+ /* For safety clear out the input and output pointers (currently zlib
+ * doesn't use them on Init, but it might in the future).
+ */
+ ps->s.zs.next_in = NULL;
+ ps->s.zs.avail_in = 0;
+ ps->s.zs.next_out = NULL;
+ ps->s.zs.avail_out = 0;
+
+ /* The length fields must be cleared too and the lists reset: */
+ ps->s.overflow = ps->s.len = ps->s.start = 0U;
+
+ if (ps->s.list != NULL) /* error in prior chunk writing */
+ {
+ debug(ps->stash == NULL);
+ ps->stash = ps->s.list;
+ ps->s.list = NULL;
+ }
+
+ ps->s.end = &ps->s.list;
+
+ /* Now initialize if required, setting the new parameters, otherwise just
+ * do a simple reset to the previous parameters.
+ */
+ if (ps->s.zs.state != NULL)
+ ret = deflateReset(&ps->s.zs);
+
+ else
+ ret = deflateInit2(&ps->s.zs, pz_value(level, settings), Z_DEFLATED,
+ pz_value(windowBits, settings), pz_value(memLevel, settings),
+ pz_value(strategy, settings));
+
+ ps->pz_current = settings;
+
+ /* The return code is from either deflateReset or deflateInit2; they have
+ * pretty much the same set of error codes.
+ */
+ if (ret == Z_OK && ps->s.zs.state != NULL)
+ png_ptr->zowner = owner;
+
+ else
+ {
+ png_zstream_error(&ps->s.zs, ret);
+ png_error(png_ptr, ps->s.zs.msg);
+ }
+ }
+
+ return ps;
+}
+
+#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED /* includes iCCP */
+/* Compress the block of data at the end of a chunk. This claims and releases
+ * png_struct::z_stream. It returns the amount of data in the chunk list or
+ * zero on error (a zlib stream always contains some bytes!)
+ *
+ * prefix_len is the amount of (uncompressed) data before the start of the
+ * compressed data. The routine will return 0 if the total of the compressed
+ * data and the prefix exceeds PNG_UINT_MAX_31.
+ *
+ * NOTE: this function may not return; it only returns 0 if
+ * png_chunk_report(PNG_CHUNK_WRITE_ERROR) returns (not the default).
+ */
+static int /* success */
+png_compress_chunk_data(png_structrp png_ptr, png_uint_32 chunk_name,
+ png_uint_32 prefix_len, png_const_voidp input, png_alloc_size_t input_len)
+{
/* To find the length of the output it is necessary to first compress the
- * input, the result is buffered rather than using the two-pass algorithm
+ * input. The result is buffered rather than using the two-pass algorithm
* that is used on the inflate side; deflate is assumed to be slower and a
* PNG writer is assumed to have more memory available than a PNG reader.
*
@@ -501,168 +1843,115 @@ png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
* size so it is likely to be more efficient to use this linked-list
* approach.
*/
- ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
+ png_zlib_statep ps = png_deflate_claim(png_ptr, chunk_name, input_len);
- if (ret != Z_OK)
- return ret;
+ affirm(ps != NULL);
+
+ /* The data compression function always returns so that we can clean up. */
+ ps->s.zs.next_in = PNGZ_INPUT_CAST(png_voidcast(const Bytef*, input));
+
+ /* Use the stash, if available: */
+ debug(ps->s.list == NULL);
+ ps->s.list = ps->stash;
+ ps->stash = NULL;
- /* Set up the compression buffers, we need a loop here to avoid overflowing a
- * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited
- * by the output buffer size, so there is no need to check that. Since this
- * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
- * in size.
- */
{
- png_compression_bufferp *end = &png_ptr->zbuffer_list;
- png_alloc_size_t input_len = comp->input_len; /* may be zero! */
- png_uint_32 output_len;
+ int ret = png_compress(&ps->s, input_len, PNG_UINT_31_MAX-prefix_len,
+ Z_FINISH);
- /* zlib updates these for us: */
- png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
- png_ptr->zstream.avail_in = 0; /* Set below */
- png_ptr->zstream.next_out = comp->output;
- png_ptr->zstream.avail_out = (sizeof comp->output);
+ ps->s.zs.next_out = NULL; /* safety */
+ ps->s.zs.avail_out = 0;
+ ps->s.zs.next_in = NULL;
+ ps->s.zs.avail_in = 0;
+ png_ptr->zowner = 0; /* release png_ptr::zstream */
- output_len = png_ptr->zstream.avail_out;
-
- do
+ /* Since Z_FINISH was passed as the flush parameter any result other than
+ * Z_STREAM_END is an error. In any case in the event of an error free
+ * the whole compression state; the only expected error is Z_MEM_ERROR.
+ */
+ if (ret != Z_STREAM_END)
{
- uInt avail_in = ZLIB_IO_MAX;
+ png_zlib_compress_destroy(&ps->s, 0/*check*/);
- if (avail_in > input_len)
- avail_in = (uInt)input_len;
-
- input_len -= avail_in;
-
- png_ptr->zstream.avail_in = avail_in;
-
- if (png_ptr->zstream.avail_out == 0)
- {
- png_compression_buffer *next;
-
- /* Chunk data is limited to 2^31 bytes in length, so the prefix
- * length must be counted here.
- */
- if (output_len + prefix_len > PNG_UINT_31_MAX)
- {
- ret = Z_MEM_ERROR;
- break;
- }
-
- /* Need a new (malloc'ed) buffer, but there may be one present
- * already.
- */
- next = *end;
- if (next == NULL)
- {
- next = png_voidcast(png_compression_bufferp, png_malloc_base
- (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
-
- if (next == NULL)
- {
- ret = Z_MEM_ERROR;
- break;
- }
-
- /* Link in this buffer (so that it will be freed later) */
- next->next = NULL;
- *end = next;
- }
-
- png_ptr->zstream.next_out = next->output;
- png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
- output_len += png_ptr->zstream.avail_out;
-
- /* Move 'end' to the next buffer pointer. */
- end = &next->next;
- }
-
- /* Compress the data */
- ret = deflate(&png_ptr->zstream,
- input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
-
- /* Claw back input data that was not consumed (because avail_in is
- * reset above every time round the loop).
+ /* This is not very likely given the PNG_UINT_31_MAX limit above, but
+ * if code is added to limit the size of the chunks produced it can
+ * start to happen.
*/
- input_len += png_ptr->zstream.avail_in;
- png_ptr->zstream.avail_in = 0; /* safety */
+ if (ret == Z_BUF_ERROR)
+ ps->s.zs.msg = PNGZ_MSG_CAST("compressed chunk too long");
+
+ else
+ png_zstream_error(&ps->s.zs, ret);
+
+ png_chunk_report(png_ptr, ps->s.zs.msg, PNG_CHUNK_WRITE_ERROR);
+ return 0;
}
- while (ret == Z_OK);
-
- /* There may be some space left in the last output buffer, this needs to
- * be subtracted from output_len.
- */
- output_len -= png_ptr->zstream.avail_out;
- png_ptr->zstream.avail_out = 0; /* safety */
- comp->output_len = output_len;
-
- /* Now double check the output length, put in a custom message if it is
- * too long. Otherwise ensure the z_stream::msg pointer is set to
- * something.
- */
- if (output_len + prefix_len >= PNG_UINT_31_MAX)
- {
- png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
- ret = Z_MEM_ERROR;
- }
-
- else
- png_zstream_error(png_ptr, ret);
-
- /* Reset zlib for another zTXt/iTXt or image data */
- png_ptr->zowner = 0;
-
- /* The only success case is Z_STREAM_END, input_len must be 0, if not this
- * is an internal error.
- */
- if (ret == Z_STREAM_END && input_len == 0)
- {
- /* Fix up the deflate header, if required */
- optimize_cmf(comp->output, comp->input_len);
-
- /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
- * function above to return Z_STREAM_END on an error (though it never
- * does in the current versions of zlib.)
- */
- return Z_OK;
- }
-
- else
- return ret;
}
+
+ /* png_compress is meant to guarantee this on a successful return: */
+ affirm(ps->s.overflow == 0U && ps->s.len <= PNG_UINT_31_MAX - prefix_len);
+
+ /* Correct the zlib CINFO field: */
+ if (ps->s.len >= 2U)
+ fix_cinfo(ps, ps->s.list->output, input_len);
+
+ return 1;
}
-/* Ship the compressed text out via chunk writes */
-static void
-png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
+/* Return the length of the compressed data; this is effectively a debug
+ * function to catch inconsistencies caused by internal errors. It will
+ * disappear in a release build.
+ */
+#if PNG_RELEASE_BUILD
+# define png_length_compressed_chunk_data(pp, p) ((pp)->zlib_state->s.len)
+#else /* !RELEASE_BUILD */
+static png_uint_32
+png_length_compressed_chunk_data(png_structrp png_ptr, png_uint_32 p)
{
- png_uint_32 output_len = comp->output_len;
- png_const_bytep output = comp->output;
- png_uint_32 avail = (sizeof comp->output);
- png_compression_buffer *next = png_ptr->zbuffer_list;
+ png_zlib_statep ps = png_ptr->zlib_state;
- for (;;)
+ debug(ps != NULL && ps->s.overflow == 0U && ps->s.len <= PNG_UINT_31_MAX-p);
+ return ps->s.len;
+}
+#endif /* !RELEASE_BUILD */
+
+/* Write all the data produced by the above function; the caller must write the
+ * prefix and chunk header.
+ */
+static void
+png_write_compressed_chunk_data(png_structrp png_ptr)
+{
+ png_zlib_statep ps = png_ptr->zlib_state;
+ png_compression_bufferp next;
+ png_uint_32 output_len;
+
+ affirm(ps != NULL && ps->s.overflow == 0U);
+ next = ps->s.list;
+
+ for (output_len = ps->s.len; output_len > 0U; next = next->next)
{
- if (avail > output_len)
- avail = output_len;
+ png_uint_32 size = PNG_ROW_BUFFER_SIZE;
- png_write_chunk_data(png_ptr, output, avail);
+ /* If this affirm fails there is a bug in the calculation of
+ * output_length above, or in the buffer_limit code in png_compress.
+ */
+ affirm(next != NULL && output_len > 0U);
- output_len -= avail;
+ if (size > output_len)
+ size = output_len;
- if (output_len == 0 || next == NULL)
- break;
+ png_write_chunk_data(png_ptr, next->output, size);
- avail = png_ptr->zbuffer_size;
- output = next->output;
- next = next->next;
+ output_len -= size;
}
- /* This is an internal error; 'next' must have been NULL! */
- if (output_len > 0)
- png_error(png_ptr, "error writing ancillary chunked compressed data");
+ /* Release the list back to the stash. */
+ debug(ps->stash == NULL);
+ ps->stash = ps->s.list;
+ ps->s.list = NULL;
+ ps->s.end = &ps->s.list;
}
-#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
+#endif /* WRITE_COMPRESSED_TEXT */
#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
@@ -672,15 +1961,15 @@ png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
* length, forbids leading or trailing whitespace, multiple internal spaces,
* and the non-break space (0x80) from ISO 8859-1. Returns keyword length.
*
- * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
- * trailing '\0'). If this routine returns 0 then there was no keyword, or a
- * valid one could not be generated, and the caller must png_error.
+ * The 'new_key' buffer must be at least 80 characters in size (for the keyword
+ * plus a trailing '\0'). If this routine returns 0 then there was no keyword,
+ * or a valid one could not be generated, and the caller must CHUNK_WRITE_ERROR.
*/
-static png_uint_32
+static unsigned int
png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
{
png_const_charp orig_key = key;
- png_uint_32 key_len = 0;
+ unsigned int key_len = 0;
int bad_character = 0;
int space = 1;
@@ -715,7 +2004,7 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
bad_character = ch; /* just skip it, record the first error */
}
- if (key_len > 0 && space) /* trailing space */
+ if (key_len > 0 && space != 0) /* trailing space */
{
--key_len, --new_key;
if (bad_character == 0)
@@ -728,9 +2017,10 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
if (key_len == 0)
return 0;
+#ifdef PNG_WARNINGS_SUPPORTED
/* Try to only output one warning per keyword: */
- if (*key) /* keyword too long */
- png_warning(png_ptr, "keyword truncated");
+ if (*key != 0) /* keyword too long */
+ png_app_warning(png_ptr, "keyword truncated");
else if (bad_character != 0)
{
@@ -741,10 +2031,11 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
}
+#endif /* WARNINGS */
return key_len;
}
-#endif
+#endif /* WRITE_TEXT || WRITE_pCAL || WRITE_iCCP || WRITE_sPLT */
/* Write the IHDR chunk, and update the png_struct with the necessary
* information. Note that the rest of this code depends upon this
@@ -752,7 +2043,7 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
*/
void /* PRIVATE */
png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
- int bit_depth, int color_type, int compression_type, int filter_type,
+ int bit_depth, int color_type, int compression_type, int filter_method,
int interlace_type)
{
png_byte buf[13]; /* Buffer to store the IHDR info */
@@ -772,11 +2063,10 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
#ifdef PNG_WRITE_16BIT_SUPPORTED
case 16:
#endif
- png_ptr->channels = 1; break;
+ break;
default:
- png_error(png_ptr,
- "Invalid bit depth for grayscale image");
+ png_error(png_ptr, "Invalid bit depth for grayscale image");
}
break;
@@ -788,7 +2078,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
#endif
png_error(png_ptr, "Invalid bit depth for RGB image");
- png_ptr->channels = 3;
break;
case PNG_COLOR_TYPE_PALETTE:
@@ -798,7 +2087,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
case 2:
case 4:
case 8:
- png_ptr->channels = 1;
break;
default:
@@ -810,7 +2098,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
if (bit_depth != 8 && bit_depth != 16)
png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
- png_ptr->channels = 2;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
@@ -821,7 +2108,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
#endif
png_error(png_ptr, "Invalid bit depth for RGBA image");
- png_ptr->channels = 4;
break;
default:
@@ -830,7 +2116,7 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
if (compression_type != PNG_COMPRESSION_TYPE_BASE)
{
- png_warning(png_ptr, "Invalid compression type specified");
+ png_app_error(png_ptr, "Invalid compression type specified");
compression_type = PNG_COMPRESSION_TYPE_BASE;
}
@@ -844,71 +2130,46 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
* 5. The color_type is RGB or RGBA
*/
if (
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
- ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
- (color_type == PNG_COLOR_TYPE_RGB ||
- color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
- (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
-#endif
- filter_type != PNG_FILTER_TYPE_BASE)
+# ifdef PNG_MNG_FEATURES_SUPPORTED
+ !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+ ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
+ (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+ (filter_method == PNG_INTRAPIXEL_DIFFERENCING)) &&
+# endif /* MNG_FEATURES */
+ filter_method != PNG_FILTER_TYPE_BASE)
{
- png_warning(png_ptr, "Invalid filter type specified");
- filter_type = PNG_FILTER_TYPE_BASE;
+ png_app_error(png_ptr, "Invalid filter type specified");
+ filter_method = PNG_FILTER_TYPE_BASE;
}
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
if (interlace_type != PNG_INTERLACE_NONE &&
interlace_type != PNG_INTERLACE_ADAM7)
{
- png_warning(png_ptr, "Invalid interlace type specified");
+ png_app_error(png_ptr, "Invalid interlace type specified");
interlace_type = PNG_INTERLACE_ADAM7;
}
-#else
- interlace_type=PNG_INTERLACE_NONE;
-#endif
- /* Save the relevent information */
- png_ptr->bit_depth = (png_byte)bit_depth;
- png_ptr->color_type = (png_byte)color_type;
- png_ptr->interlaced = (png_byte)interlace_type;
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- png_ptr->filter_type = (png_byte)filter_type;
-#endif
- png_ptr->compression_type = (png_byte)compression_type;
+ /* Save the relevant information */
+ png_ptr->bit_depth = png_check_byte(png_ptr, bit_depth);
+ png_ptr->color_type = png_check_byte(png_ptr, color_type);
+ png_ptr->interlaced = png_check_byte(png_ptr, interlace_type);
+ png_ptr->filter_method = png_check_byte(png_ptr, filter_method);
png_ptr->width = width;
png_ptr->height = height;
- png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
- png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
- /* Set the usr info, so any transformations can modify it */
- png_ptr->usr_width = png_ptr->width;
- png_ptr->usr_bit_depth = png_ptr->bit_depth;
- png_ptr->usr_channels = png_ptr->channels;
-
/* Pack the header information into the buffer */
png_save_uint_32(buf, width);
png_save_uint_32(buf + 4, height);
- buf[8] = (png_byte)bit_depth;
- buf[9] = (png_byte)color_type;
- buf[10] = (png_byte)compression_type;
- buf[11] = (png_byte)filter_type;
- buf[12] = (png_byte)interlace_type;
+ buf[8] = png_check_byte(png_ptr, bit_depth);
+ buf[9] = png_check_byte(png_ptr, color_type);
+ buf[10] = png_check_byte(png_ptr, compression_type);
+ buf[11] = png_check_byte(png_ptr, filter_method);
+ buf[12] = png_check_byte(png_ptr, interlace_type);
/* Write the chunk */
png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
-
- if ((png_ptr->do_filter) == PNG_NO_FILTERS)
- {
- if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
- png_ptr->bit_depth < 8)
- png_ptr->do_filter = PNG_FILTER_NONE;
-
- else
- png_ptr->do_filter = PNG_ALL_FILTERS;
- }
-
- png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */
+ png_ptr->mode |= PNG_HAVE_IHDR;
}
/* Write the palette. We are careful not to trust png_color to be in the
@@ -917,19 +2178,22 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
*/
void /* PRIVATE */
png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
- png_uint_32 num_pal)
+ unsigned int num_pal)
{
- png_uint_32 i;
+ png_uint_32 max_palette_length, i;
png_const_colorp pal_ptr;
png_byte buf[3];
png_debug(1, "in png_write_PLTE");
+ max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
+ (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
+
if ((
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
-#endif
- num_pal == 0) || num_pal > 256)
+# ifdef PNG_MNG_FEATURES_SUPPORTED
+ (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 &&
+# endif /* MNG_FEATURES */
+ num_pal == 0) || num_pal > max_palette_length)
{
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
@@ -943,7 +2207,7 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
}
}
- if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+ if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
{
png_warning(png_ptr,
"Ignoring request to write a PLTE chunk in grayscale PNG");
@@ -951,195 +2215,23 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
return;
}
- png_ptr->num_palette = (png_uint_16)num_pal;
+ png_ptr->num_palette = png_check_bits(png_ptr, num_pal, 9);
png_debug1(3, "num_palette = %d", png_ptr->num_palette);
- png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
+ png_write_chunk_header(png_ptr, png_PLTE, num_pal * 3U);
for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
{
buf[0] = pal_ptr->red;
buf[1] = pal_ptr->green;
buf[2] = pal_ptr->blue;
- png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+ png_write_chunk_data(png_ptr, buf, 3U);
}
-#else
- /* This is a little slower but some buggy compilers need to do this
- * instead
- */
- pal_ptr=palette;
-
- for (i = 0; i < num_pal; i++)
- {
- buf[0] = pal_ptr[i].red;
- buf[1] = pal_ptr[i].green;
- buf[2] = pal_ptr[i].blue;
- png_write_chunk_data(png_ptr, buf, (png_size_t)3);
- }
-
-#endif
png_write_chunk_end(png_ptr);
png_ptr->mode |= PNG_HAVE_PLTE;
}
-/* This is similar to png_text_compress, above, except that it does not require
- * all of the data at once and, instead of buffering the compressed result,
- * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
- * because it calls the write interface. As a result it does its own error
- * reporting and does not return an error code. In the event of error it will
- * just call png_error. The input data length may exceed 32-bits. The 'flush'
- * parameter is exactly the same as that to deflate, with the following
- * meanings:
- *
- * Z_NO_FLUSH: normal incremental output of compressed data
- * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
- * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
- *
- * The routine manages the acquire and release of the png_ptr->zstream by
- * checking and (at the end) clearing png_ptr->zowner, it does some sanity
- * checks on the 'mode' flags while doing this.
- */
-void /* PRIVATE */
-png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
- png_alloc_size_t input_len, int flush)
-{
- if (png_ptr->zowner != png_IDAT)
- {
- /* First time. Ensure we have a temporary buffer for compression and
- * trim the buffer list if it has more than one entry to free memory.
- * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
- * created at this point, but the check here is quick and safe.
- */
- if (png_ptr->zbuffer_list == NULL)
- {
- png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
- png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
- png_ptr->zbuffer_list->next = NULL;
- }
-
- else
- png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
-
- /* It is a terminal error if we can't claim the zstream. */
- if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- /* The output state is maintained in png_ptr->zstream, so it must be
- * initialized here after the claim.
- */
- png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
- png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
- }
-
- /* Now loop reading and writing until all the input is consumed or an error
- * terminates the operation. The _out values are maintained across calls to
- * this function, but the input must be reset each time.
- */
- png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
- png_ptr->zstream.avail_in = 0; /* set below */
- for (;;)
- {
- int ret;
-
- /* INPUT: from the row data */
- uInt avail = ZLIB_IO_MAX;
-
- if (avail > input_len)
- avail = (uInt)input_len; /* safe because of the check */
-
- png_ptr->zstream.avail_in = avail;
- input_len -= avail;
-
- ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
-
- /* Include as-yet unconsumed input */
- input_len += png_ptr->zstream.avail_in;
- png_ptr->zstream.avail_in = 0;
-
- /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note
- * that these two zstream fields are preserved across the calls, therefore
- * there is no need to set these up on entry to the loop.
- */
- if (png_ptr->zstream.avail_out == 0)
- {
- png_bytep data = png_ptr->zbuffer_list->output;
- uInt size = png_ptr->zbuffer_size;
-
- /* Write an IDAT containing the data then reset the buffer. The
- * first IDAT may need deflate header optimization.
- */
-# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
- if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
- png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
- optimize_cmf(data, png_image_size(png_ptr));
-# endif
-
- png_write_complete_chunk(png_ptr, png_IDAT, data, size);
- png_ptr->mode |= PNG_HAVE_IDAT;
-
- png_ptr->zstream.next_out = data;
- png_ptr->zstream.avail_out = size;
-
- /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
- * the same flush parameter until it has finished output, for NO_FLUSH
- * it doesn't matter.
- */
- if (ret == Z_OK && flush != Z_NO_FLUSH)
- continue;
- }
-
- /* The order of these checks doesn't matter much; it just effect which
- * possible error might be detected if multiple things go wrong at once.
- */
- if (ret == Z_OK) /* most likely return code! */
- {
- /* If all the input has been consumed then just return. If Z_FINISH
- * was used as the flush parameter something has gone wrong if we get
- * here.
- */
- if (input_len == 0)
- {
- if (flush == Z_FINISH)
- png_error(png_ptr, "Z_OK on Z_FINISH with output space");
-
- return;
- }
- }
-
- else if (ret == Z_STREAM_END && flush == Z_FINISH)
- {
- /* This is the end of the IDAT data; any pending output must be
- * flushed. For small PNG files we may still be at the beginning.
- */
- png_bytep data = png_ptr->zbuffer_list->output;
- uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
-
-# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
- if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
- png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
- optimize_cmf(data, png_image_size(png_ptr));
-# endif
-
- png_write_complete_chunk(png_ptr, png_IDAT, data, size);
- png_ptr->zstream.avail_out = 0;
- png_ptr->zstream.next_out = NULL;
- png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
-
- png_ptr->zowner = 0; /* Release the stream */
- return;
- }
-
- else
- {
- /* This is an error condition. */
- png_zstream_error(png_ptr, ret);
- png_error(png_ptr, png_ptr->zstream.msg);
- }
- }
-}
-
/* Write an IEND chunk */
void /* PRIVATE */
png_write_IEND(png_structrp png_ptr)
@@ -1150,6 +2242,40 @@ png_write_IEND(png_structrp png_ptr)
png_ptr->mode |= PNG_HAVE_IEND;
}
+#if defined(PNG_WRITE_gAMA_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
+static int
+png_save_int_31(png_structrp png_ptr, png_bytep buf, png_int_32 i)
+ /* Save a signed value as a PNG unsigned value; the argument is required to
+ * be in the range 0..0x7FFFFFFFU. If not a *warning* is produced and false
+ * is returned. Because this is only called from png_write_cHRM_fixed and
+ * png_write_gAMA_fixed below this is safe (we don't need either chunk,
+ * particularly if the value is bogus.)
+ *
+ * The warning is png_app_error; it may return if the app tells it to but the
+ * app can have it error out. JB 20150821: I believe the checking in png.c
+ * actually makes this error impossible, but this is safe.
+ */
+{
+#ifndef __COVERITY__
+ if (i >= 0 && i <= 0x7FFFFFFF)
+#else
+ /* Supress bogus Coverity complaint */
+ if (i >= 0)
+#endif
+ {
+ png_save_uint_32(buf, (png_uint_32)/*SAFE*/i);
+ return 1;
+ }
+
+ else
+ {
+ png_chunk_report(png_ptr, "negative value in cHRM or gAMA",
+ PNG_CHUNK_WRITE_ERROR);
+ return 0;
+ }
+}
+#endif /* WRITE_gAMA || WRITE_cHRM */
+
#ifdef PNG_WRITE_gAMA_SUPPORTED
/* Write a gAMA chunk */
void /* PRIVATE */
@@ -1160,8 +2286,8 @@ png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma)
png_debug(1, "in png_write_gAMA");
/* file_gamma is saved in 1/100,000ths */
- png_save_uint_32(buf, (png_uint_32)file_gamma);
- png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
+ if (png_save_int_31(png_ptr, buf, file_gamma))
+ png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
}
#endif
@@ -1175,10 +2301,10 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent)
png_debug(1, "in png_write_sRGB");
if (srgb_intent >= PNG_sRGB_INTENT_LAST)
- png_warning(png_ptr,
- "Invalid sRGB rendering intent specified");
+ png_chunk_report(png_ptr, "Invalid sRGB rendering intent specified",
+ PNG_CHUNK_WRITE_ERROR);
- buf[0]=(png_byte)srgb_intent;
+ buf[0] = png_check_byte(png_ptr, srgb_intent);
png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
}
#endif
@@ -1187,59 +2313,37 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent)
/* Write an iCCP chunk */
void /* PRIVATE */
png_write_iCCP(png_structrp png_ptr, png_const_charp name,
- png_const_bytep profile)
+ png_const_voidp profile)
{
png_uint_32 name_len;
png_uint_32 profile_len;
png_byte new_name[81]; /* 1 byte for the compression byte */
- compression_state comp;
png_debug(1, "in png_write_iCCP");
- /* These are all internal problems: the profile should have been checked
- * before when it was stored.
- */
- if (profile == NULL)
- png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */
+ affirm(profile != NULL);
profile_len = png_get_uint_32(profile);
-
- if (profile_len < 132)
- png_error(png_ptr, "ICC profile too short");
-
- if (profile_len & 0x03)
- png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
-
- {
- png_uint_32 embedded_profile_len = png_get_uint_32(profile);
-
- if (profile_len != embedded_profile_len)
- png_error(png_ptr, "Profile length does not match profile");
- }
-
name_len = png_check_keyword(png_ptr, name, new_name);
if (name_len == 0)
- png_error(png_ptr, "iCCP: invalid keyword");
+ {
+ png_chunk_report(png_ptr, "iCCP: invalid keyword", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
- new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
+ ++name_len; /* trailing '\0' */
+ new_name[name_len++] = PNG_COMPRESSION_TYPE_BASE;
- /* Make sure we include the NULL after the name and the compression type */
- ++name_len;
-
- png_text_compress_init(&comp, profile, profile_len);
-
- /* Allow for keyword terminator and compression byte */
- if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
-
- png_write_chunk_data(png_ptr, new_name, name_len);
-
- png_write_compressed_data_out(png_ptr, &comp);
-
- png_write_chunk_end(png_ptr);
+ if (png_compress_chunk_data(png_ptr, png_iCCP, name_len, profile,
+ profile_len))
+ {
+ png_write_chunk_header(png_ptr, png_iCCP,
+ name_len+png_length_compressed_chunk_data(png_ptr, name_len));
+ png_write_chunk_data(png_ptr, new_name, name_len);
+ png_write_compressed_chunk_data(png_ptr);
+ png_write_chunk_end(png_ptr);
+ }
}
#endif
@@ -1254,9 +2358,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
png_size_t palette_size = entry_size * spalette->nentries;
png_sPLT_entryp ep;
-#ifndef PNG_POINTER_INDEXING_SUPPORTED
- int i;
-#endif
png_debug(1, "in png_write_sPLT");
@@ -1269,21 +2370,19 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
png_write_chunk_header(png_ptr, png_sPLT,
(png_uint_32)(name_len + 2 + palette_size));
- png_write_chunk_data(png_ptr, (png_bytep)new_name,
- (png_size_t)(name_len + 1));
+ png_write_chunk_data(png_ptr, new_name, name_len + 1);
- png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
+ png_write_chunk_data(png_ptr, &spalette->depth, 1);
/* Loop through each palette entry, writing appropriately */
-#ifdef PNG_POINTER_INDEXING_SUPPORTED
for (ep = spalette->entries; epentries + spalette->nentries; ep++)
{
if (spalette->depth == 8)
{
- entrybuf[0] = (png_byte)ep->red;
- entrybuf[1] = (png_byte)ep->green;
- entrybuf[2] = (png_byte)ep->blue;
- entrybuf[3] = (png_byte)ep->alpha;
+ entrybuf[0] = png_check_byte(png_ptr, ep->red);
+ entrybuf[1] = png_check_byte(png_ptr, ep->green);
+ entrybuf[2] = png_check_byte(png_ptr, ep->blue);
+ entrybuf[3] = png_check_byte(png_ptr, ep->alpha);
png_save_uint_16(entrybuf + 4, ep->frequency);
}
@@ -1298,31 +2397,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
png_write_chunk_data(png_ptr, entrybuf, entry_size);
}
-#else
- ep=spalette->entries;
- for (i = 0; i>spalette->nentries; i++)
- {
- if (spalette->depth == 8)
- {
- entrybuf[0] = (png_byte)ep[i].red;
- entrybuf[1] = (png_byte)ep[i].green;
- entrybuf[2] = (png_byte)ep[i].blue;
- entrybuf[3] = (png_byte)ep[i].alpha;
- png_save_uint_16(entrybuf + 4, ep[i].frequency);
- }
-
- else
- {
- png_save_uint_16(entrybuf + 0, ep[i].red);
- png_save_uint_16(entrybuf + 2, ep[i].green);
- png_save_uint_16(entrybuf + 4, ep[i].blue);
- png_save_uint_16(entrybuf + 6, ep[i].alpha);
- png_save_uint_16(entrybuf + 8, ep[i].frequency);
- }
-
- png_write_chunk_data(png_ptr, entrybuf, entry_size);
- }
-#endif
png_write_chunk_end(png_ptr);
}
@@ -1339,18 +2413,17 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
png_debug(1, "in png_write_sBIT");
/* Make sure we don't depend upon the order of PNG_COLOR_8 */
- if (color_type & PNG_COLOR_MASK_COLOR)
+ if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
{
- png_byte maxbits;
+ unsigned int maxbits;
- maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
- png_ptr->usr_bit_depth);
+ maxbits = color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->bit_depth;
if (sbit->red == 0 || sbit->red > maxbits ||
sbit->green == 0 || sbit->green > maxbits ||
sbit->blue == 0 || sbit->blue > maxbits)
{
- png_warning(png_ptr, "Invalid sBIT depth specified");
+ png_app_error(png_ptr, "Invalid sBIT depth specified");
return;
}
@@ -1362,9 +2435,9 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
else
{
- if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+ if (sbit->gray == 0 || sbit->gray > png_ptr->bit_depth)
{
- png_warning(png_ptr, "Invalid sBIT depth specified");
+ png_app_error(png_ptr, "Invalid sBIT depth specified");
return;
}
@@ -1372,11 +2445,11 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
size = 1;
}
- if (color_type & PNG_COLOR_MASK_ALPHA)
+ if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
{
- if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+ if (sbit->alpha == 0 || sbit->alpha > png_ptr->bit_depth)
{
- png_warning(png_ptr, "Invalid sBIT depth specified");
+ png_app_error(png_ptr, "Invalid sBIT depth specified");
return;
}
@@ -1397,19 +2470,15 @@ png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy)
png_debug(1, "in png_write_cHRM");
/* Each value is saved in 1/100,000ths */
- png_save_int_32(buf, xy->whitex);
- png_save_int_32(buf + 4, xy->whitey);
-
- png_save_int_32(buf + 8, xy->redx);
- png_save_int_32(buf + 12, xy->redy);
-
- png_save_int_32(buf + 16, xy->greenx);
- png_save_int_32(buf + 20, xy->greeny);
-
- png_save_int_32(buf + 24, xy->bluex);
- png_save_int_32(buf + 28, xy->bluey);
-
- png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);
+ if (png_save_int_31(png_ptr, buf, xy->whitex) &&
+ png_save_int_31(png_ptr, buf + 4, xy->whitey) &&
+ png_save_int_31(png_ptr, buf + 8, xy->redx) &&
+ png_save_int_31(png_ptr, buf + 12, xy->redy) &&
+ png_save_int_31(png_ptr, buf + 16, xy->greenx) &&
+ png_save_int_31(png_ptr, buf + 20, xy->greeny) &&
+ png_save_int_31(png_ptr, buf + 24, xy->bluex) &&
+ png_save_int_31(png_ptr, buf + 28, xy->bluey))
+ png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);
}
#endif
@@ -1425,31 +2494,37 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
- if (num_trans <= 0 || num_trans > png_ptr->num_palette)
+ affirm(num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH);
{
- /* This is an error which can only be reliably detected late. */
- png_app_error(png_ptr,
- "Invalid number of transparent colors specified");
- return;
- }
+# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ union
+ {
+ png_uint_32 u32[1];
+ png_byte b8[PNG_MAX_PALETTE_LENGTH];
+ } inverted_alpha;
- /* Write the chunk out as it is */
- png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha,
- (png_size_t)num_trans);
+ /* Invert the alpha channel (in tRNS) if required */
+ if (png_ptr->write_invert_alpha)
+ {
+ int i;
+
+ memcpy(inverted_alpha.b8, trans_alpha, num_trans);
+
+ for (i=0; 4*igray >= (1 << png_ptr->bit_depth))
- {
- /* This can no longer happen because it is checked in png_set_tRNS */
- png_app_error(png_ptr,
- "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
-
- return;
- }
-
+ affirm(tran->gray < (1 << png_ptr->bit_depth));
png_save_uint_16(buf, tran->gray);
png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
}
@@ -1460,26 +2535,12 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,
png_save_uint_16(buf, tran->red);
png_save_uint_16(buf + 2, tran->green);
png_save_uint_16(buf + 4, tran->blue);
-#ifdef PNG_WRITE_16BIT_SUPPORTED
- if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
-#else
- if (buf[0] | buf[2] | buf[4])
-#endif
- {
- /* Also checked in png_set_tRNS */
- png_app_error(png_ptr,
- "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
- return;
- }
-
+ affirm(png_ptr->bit_depth == 8 || (buf[0] | buf[2] | buf[4]) == 0);
png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
}
- else
- {
- /* Checked in png_set_tRNS */
- png_app_error(png_ptr, "Can't write tRNS with an alpha channel");
- }
+ else /* Already checked in png_set_tRNS */
+ impossible("invalid tRNS");
}
#endif
@@ -1495,13 +2556,13 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
if (
-#ifdef PNG_MNG_FEATURES_SUPPORTED
- (png_ptr->num_palette ||
- (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
-#endif
+# ifdef PNG_MNG_FEATURES_SUPPORTED
+ (png_ptr->num_palette != 0 ||
+ (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) &&
+# endif /* MNG_FEATURES */
back->index >= png_ptr->num_palette)
{
- png_warning(png_ptr, "Invalid background palette index");
+ png_app_error(png_ptr, "Invalid background palette index");
return;
}
@@ -1509,18 +2570,18 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
}
- else if (color_type & PNG_COLOR_MASK_COLOR)
+ else if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
{
png_save_uint_16(buf, back->red);
png_save_uint_16(buf + 2, back->green);
png_save_uint_16(buf + 4, back->blue);
#ifdef PNG_WRITE_16BIT_SUPPORTED
- if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+ if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
#else
- if (buf[0] | buf[2] | buf[4])
+ if ((buf[0] | buf[2] | buf[4]) != 0)
#endif
{
- png_warning(png_ptr,
+ png_app_error(png_ptr,
"Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
return;
@@ -1533,7 +2594,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
{
if (back->gray >= (1 << png_ptr->bit_depth))
{
- png_warning(png_ptr,
+ png_app_error(png_ptr,
"Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
return;
@@ -1582,7 +2643,7 @@ void /* PRIVATE */
png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
png_size_t text_len)
{
- png_uint_32 key_len;
+ unsigned int key_len;
png_byte new_key[80];
png_debug(1, "in png_write_tEXt");
@@ -1590,7 +2651,10 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
key_len = png_check_keyword(png_ptr, key, new_key);
if (key_len == 0)
- png_error(png_ptr, "tEXt: invalid keyword");
+ {
+ png_chunk_report(png_ptr, "tEXt: invalid keyword", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
if (text == NULL || *text == '\0')
text_len = 0;
@@ -1599,7 +2663,10 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
text_len = strlen(text);
if (text_len > PNG_UINT_31_MAX - (key_len+1))
- png_error(png_ptr, "tEXt: text too long");
+ {
+ png_chunk_report(png_ptr, "tEXt: text too long", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
/* Make sure we include the 0 after the key */
png_write_chunk_header(png_ptr, png_tEXt,
@@ -1623,51 +2690,38 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
/* Write a compressed text chunk */
void /* PRIVATE */
png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
- png_size_t text_len, int compression)
+ int compression)
{
- png_uint_32 key_len;
+ unsigned int key_len;
png_byte new_key[81];
- compression_state comp;
png_debug(1, "in png_write_zTXt");
- PNG_UNUSED(text_len) /* Always use strlen */
-
- if (compression == PNG_TEXT_COMPRESSION_NONE)
- {
- png_write_tEXt(png_ptr, key, text, 0);
- return;
- }
if (compression != PNG_TEXT_COMPRESSION_zTXt)
- png_error(png_ptr, "zTXt: invalid compression type");
+ png_app_warning(png_ptr, "zTXt: invalid compression type ignored");
key_len = png_check_keyword(png_ptr, key, new_key);
if (key_len == 0)
- png_error(png_ptr, "zTXt: invalid keyword");
+ {
+ png_chunk_report(png_ptr, "zTXt: invalid keyword", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
/* Add the compression method and 1 for the keyword separator. */
- new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
++key_len;
+ new_key[key_len++] = PNG_COMPRESSION_TYPE_BASE;
- /* Compute the compressed data; do it now for the length */
- png_text_compress_init(&comp, (png_const_bytep)text,
- text == NULL ? 0 : strlen(text));
+ if (png_compress_chunk_data(png_ptr, png_zTXt, key_len, text, strlen(text)))
+ {
+ png_write_chunk_header(png_ptr, png_zTXt,
+ key_len+png_length_compressed_chunk_data(png_ptr, key_len));
+ png_write_chunk_data(png_ptr, new_key, key_len);
+ png_write_compressed_chunk_data(png_ptr);
+ png_write_chunk_end(png_ptr);
+ }
- if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg);
-
- /* Write start of chunk */
- png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
-
- /* Write key */
- png_write_chunk_data(png_ptr, new_key, key_len);
-
- /* Write the compressed data */
- png_write_compressed_data_out(png_ptr, &comp);
-
- /* Close the chunk */
- png_write_chunk_end(png_ptr);
+ /* else chunk report already issued and ignored */
}
#endif
@@ -1677,99 +2731,139 @@ void /* PRIVATE */
png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
png_const_charp lang, png_const_charp lang_key, png_const_charp text)
{
- png_uint_32 key_len, prefix_len;
- png_size_t lang_len, lang_key_len;
- png_byte new_key[82];
- compression_state comp;
+ png_uint_32 key_len, prefix_len, data_len;
+ png_size_t lang_len, lang_key_len, text_len;
+ png_byte new_key[82]; /* 80 bytes for the key, 2 byte compression info */
png_debug(1, "in png_write_iTXt");
key_len = png_check_keyword(png_ptr, key, new_key);
if (key_len == 0)
- png_error(png_ptr, "iTXt: invalid keyword");
+ {
+ png_chunk_report(png_ptr, "iTXt: invalid keyword", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
+
+ debug(new_key[key_len] == 0);
+ ++key_len; /* terminating 0 added by png_check_keyword */
/* Set the compression flag */
switch (compression)
{
case PNG_ITXT_COMPRESSION_NONE:
case PNG_TEXT_COMPRESSION_NONE:
- compression = new_key[++key_len] = 0; /* no compression */
+ compression = new_key[key_len++] = 0; /* no compression */
break;
case PNG_TEXT_COMPRESSION_zTXt:
case PNG_ITXT_COMPRESSION_zTXt:
- compression = new_key[++key_len] = 1; /* compressed */
+ compression = new_key[key_len++] = 1; /* compressed */
break;
default:
- png_error(png_ptr, "iTXt: invalid compression");
+ png_chunk_report(png_ptr, "iTXt: invalid compression",
+ PNG_CHUNK_WRITE_ERROR);
+ return;
}
- new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
- ++key_len; /* for the keywod separator */
+ new_key[key_len++] = PNG_COMPRESSION_TYPE_BASE;
/* We leave it to the application to meet PNG-1.0 requirements on the
* contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
- * any non-Latin-1 characters except for NEWLINE. ISO PNG, however,
- * specifies that the text is UTF-8 and this really doesn't require any
- * checking.
+ * any non-Latin-1 characters except for NEWLINE (yes, this is really weird
+ * in an 'international' text string. ISO PNG, however, specifies that the
+ * text is UTF-8 and this *IS NOT YET CHECKED*, so invalid sequences may be
+ * present.
*
* The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
*
* TODO: validate the language tag correctly (see the spec.)
*/
if (lang == NULL) lang = ""; /* empty language is valid */
- lang_len = strlen(lang)+1;
+ lang_len = strlen(lang)+1U;
if (lang_key == NULL) lang_key = ""; /* may be empty */
- lang_key_len = strlen(lang_key)+1;
+ lang_key_len = strlen(lang_key)+1U;
if (text == NULL) text = ""; /* may be empty */
- prefix_len = key_len;
- if (lang_len > PNG_UINT_31_MAX-prefix_len)
- prefix_len = PNG_UINT_31_MAX;
- else
- prefix_len = (png_uint_32)(prefix_len + lang_len);
+ if (lang_len > PNG_UINT_31_MAX-key_len ||
+ lang_key_len > PNG_UINT_31_MAX-key_len-lang_len)
+ {
+ png_chunk_report(png_ptr, "iTXt: prefix too long", PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
- if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
- prefix_len = PNG_UINT_31_MAX;
- else
- prefix_len = (png_uint_32)(prefix_len + lang_key_len);
-
- png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
+ prefix_len = (png_uint_32)/*SAFE*/(key_len+lang_len+lang_key_len);
+ text_len = strlen(text); /* no trailing '\0' */
if (compression != 0)
{
- if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
- png_error(png_ptr, png_ptr->zstream.msg);
+ if (png_compress_chunk_data(png_ptr, png_iTXt, prefix_len, text,
+ text_len))
+ data_len = png_length_compressed_chunk_data(png_ptr, prefix_len);
+
+ else
+ return; /* chunk report already issued and ignored */
}
else
{
- if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
- png_error(png_ptr, "iTXt: uncompressed text too long");
+ if (text_len > PNG_UINT_31_MAX-prefix_len)
+ {
+ png_chunk_report(png_ptr, "iTXt: text too long",
+ PNG_CHUNK_WRITE_ERROR);
+ return;
+ }
- /* So the string will fit in a chunk: */
- comp.output_len = (png_uint_32)/*SAFE*/comp.input_len;
+ data_len = (png_uint_32)/*SAFE*/text_len;
}
- png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
-
+ png_write_chunk_header(png_ptr, png_iTXt, prefix_len+data_len);
png_write_chunk_data(png_ptr, new_key, key_len);
-
- png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
-
- png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
+ png_write_chunk_data(png_ptr, lang, lang_len);
+ png_write_chunk_data(png_ptr, lang_key, lang_key_len);
if (compression != 0)
- png_write_compressed_data_out(png_ptr, &comp);
+ png_write_compressed_chunk_data(png_ptr);
else
- png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.output_len);
+ png_write_chunk_data(png_ptr, text, data_len);
png_write_chunk_end(png_ptr);
}
-#endif
+#endif /* WRITE_iTXt */
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED) ||\
+ defined(PNG_WRITE_pCAL_SUPPORTED)
+/* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90
+ * defines a cast of a signed integer to an unsigned integer either to preserve
+ * the value, if it is positive, or to calculate:
+ *
+ * (UNSIGNED_MAX+1) + integer
+ *
+ * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the
+ * negative integral value is added the result will be an unsigned value
+ * correspnding to the 2's complement representation.
+ */
+static int
+save_int_32(png_structrp png_ptr, png_bytep buf, png_int_32 j)
+{
+ png_uint_32 i = 0xFFFFFFFFU & (png_uint_32)/*SAFE & CORRECT*/j;
+
+ if (i != 0x80000000U/*value not permitted*/)
+ {
+ png_save_uint_32(buf, i);
+ return 1;
+ }
+
+ else
+ {
+ png_chunk_report(png_ptr, "invalid value in oFFS or pCAL",
+ PNG_CHUNK_WRITE_ERROR);
+ return 0;
+ }
+}
+#endif /* WRITE_oFFs || WRITE_pCAL */
#ifdef PNG_WRITE_oFFs_SUPPORTED
/* Write the oFFs chunk */
@@ -1784,13 +2878,18 @@ png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
if (unit_type >= PNG_OFFSET_LAST)
png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
- png_save_int_32(buf, x_offset);
- png_save_int_32(buf + 4, y_offset);
- buf[8] = (png_byte)unit_type;
-
- png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+ if (save_int_32(png_ptr, buf, x_offset) &&
+ save_int_32(png_ptr, buf + 4, y_offset))
+ {
+ /* unit type is 0 or 1, this has been checked already so the following
+ * is safe:
+ */
+ buf[8] = unit_type != 0;
+ png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+ }
}
-#endif
+#endif /* WRITE_oFFs */
+
#ifdef PNG_WRITE_pCAL_SUPPORTED
/* Write the pCAL chunk (described in the PNG extensions document) */
void /* PRIVATE */
@@ -1799,11 +2898,9 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,
png_charpp params)
{
png_uint_32 purpose_len;
- png_size_t units_len, total_len;
- png_size_tp params_len;
+ size_t units_len;
png_byte buf[10];
png_byte new_purpose[80];
- int i;
png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
@@ -1820,41 +2917,44 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,
png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
units_len = strlen(units) + (nparams == 0 ? 0 : 1);
png_debug1(3, "pCAL units length = %d", (int)units_len);
- total_len = purpose_len + units_len + 10;
- params_len = (png_size_tp)png_malloc(png_ptr,
- (png_alloc_size_t)(nparams * (sizeof (png_size_t))));
-
- /* Find the length of each parameter, making sure we don't count the
- * null terminator for the last parameter.
- */
- for (i = 0; i < nparams; i++)
+ if (save_int_32(png_ptr, buf, X0) &&
+ save_int_32(png_ptr, buf + 4, X1))
{
- params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
- png_debug2(3, "pCAL parameter %d length = %lu", i,
- (unsigned long)params_len[i]);
- total_len += params_len[i];
+ png_size_tp params_len = png_voidcast(png_size_tp,
+ png_malloc(png_ptr, nparams * sizeof (png_size_t)));
+ int i;
+ size_t total_len = purpose_len + units_len + 10;
+
+ /* Find the length of each parameter, making sure we don't count the
+ * null terminator for the last parameter.
+ */
+ for (i = 0; i < nparams; i++)
+ {
+ params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+ png_debug2(3, "pCAL parameter %d length = %lu", i,
+ (unsigned long)params_len[i]);
+ total_len += params_len[i];
+ }
+
+ png_debug1(3, "pCAL total length = %d", (int)total_len);
+ png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
+ png_write_chunk_data(png_ptr, new_purpose, purpose_len);
+ buf[8] = png_check_byte(png_ptr, type);
+ buf[9] = png_check_byte(png_ptr, nparams);
+ png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+ png_write_chunk_data(png_ptr, (png_const_bytep)units,
+ (png_size_t)units_len);
+
+ for (i = 0; i < nparams; i++)
+ png_write_chunk_data(png_ptr, (png_const_bytep)params[i],
+ params_len[i]);
+
+ png_free(png_ptr, params_len);
+ png_write_chunk_end(png_ptr);
}
-
- png_debug1(3, "pCAL total length = %d", (int)total_len);
- png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
- png_write_chunk_data(png_ptr, new_purpose, purpose_len);
- png_save_int_32(buf, X0);
- png_save_int_32(buf + 4, X1);
- buf[8] = (png_byte)type;
- buf[9] = (png_byte)nparams;
- png_write_chunk_data(png_ptr, buf, (png_size_t)10);
- png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
-
- for (i = 0; i < nparams; i++)
- {
- png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
- }
-
- png_free(png_ptr, params_len);
- png_write_chunk_end(png_ptr);
}
-#endif
+#endif /* WRITE_pCAL */
#ifdef PNG_WRITE_sCAL_SUPPORTED
/* Write the sCAL chunk */
@@ -1877,7 +2977,7 @@ png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width,
return;
}
- buf[0] = (png_byte)unit;
+ buf[0] = png_check_byte(png_ptr, unit);
memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */
memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */
@@ -1902,7 +3002,7 @@ png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit,
png_save_uint_32(buf, x_pixels_per_unit);
png_save_uint_32(buf + 4, y_pixels_per_unit);
- buf[8] = (png_byte)unit_type;
+ buf[8] = png_check_byte(png_ptr, unit_type);
png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
}
@@ -1938,1125 +3038,2017 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
}
#endif
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-void /* PRIVATE */
-png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters)
- /* Allocate row buffers for any filters that need them, this is also called
- * from png_set_filter if the filters are changed during write to ensure that
- * the required buffers exist. png_set_filter ensures that up/avg/paeth are
- * only set if png_ptr->prev_row is allocated.
- */
+static void
+png_end_IDAT(png_structrp png_ptr)
{
- /* The buffer size is determined just by the output row size, not any
- * processing requirements.
- */
- png_alloc_size_t buf_size = png_ptr->rowbytes + 1;
+ png_zlib_statep ps = png_ptr->zlib_state;
- if ((filters & PNG_FILTER_SUB) != 0 && png_ptr->sub_row == NULL)
- {
- png_ptr->sub_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
- png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
- }
+ png_ptr->zowner = 0U; /* release the stream */
- if ((filters & PNG_FILTER_UP) != 0 && png_ptr->up_row == NULL)
- {
- png_ptr->up_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
- png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
- }
-
- if ((filters & PNG_FILTER_AVG) != 0 && png_ptr->avg_row == NULL)
- {
- png_ptr->avg_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
- png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
- }
-
- if ((filters & PNG_FILTER_PAETH) != 0 && png_ptr->paeth_row == NULL)
- {
- png_ptr->paeth_row = png_voidcast(png_bytep, png_malloc(png_ptr,
- buf_size));
- png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
- }
+ if (ps != NULL)
+ png_deflate_release(png_ptr, ps, 1/*check*/);
}
-#endif /* PNG_WRITE_FILTER_SUPPORTED */
-/* Initializes the row writing capability of libpng */
-void /* PRIVATE */
-png_write_start_row(png_structrp png_ptr)
+static void
+png_write_IDAT(png_structrp png_ptr, int flush)
{
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+ png_zlib_statep ps = png_ptr->zlib_state;
+ png_uint_32 IDAT_size;
- /* 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};
-#endif
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- int filters;
-#endif
-
- png_alloc_size_t buf_size;
- int usr_pixel_depth;
-
- png_debug(1, "in png_write_start_row");
-
- if (png_ptr == NULL)
- return;
-
- usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;
- buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1;
-
- /* 1.5.6: added to allow checking in the row write code. */
- png_ptr->transformed_pixel_depth = png_ptr->pixel_depth;
- png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;
-
- /* Set up row buffer */
- png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
-
- png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- filters = png_ptr->do_filter;
-
- if (png_ptr->height == 1)
- filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
- if (png_ptr->width == 1)
- filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
-
- if (filters == 0)
- filters = PNG_FILTER_NONE;
-
- /* We only need to keep the previous row if we are using one of the following
- * filters.
+ /* Check for a correctly initialized list, the requirement that the end
+ * pointer is NULL means that the end of the list can be easily detected.
*/
- if (filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
- png_ptr->prev_row = png_voidcast(png_bytep, png_calloc(png_ptr,
- buf_size));
+ affirm(ps != NULL && ps->s.end != NULL && *ps->s.end == NULL);
+ png_zlib_compress_validate(&png_ptr->zlib_state->s, 0/*in_use*/);
- png_write_alloc_filter_row_buffers(png_ptr, filters);
-
- png_ptr->do_filter = (png_byte)filters; /* in case it was changed above */
-#else
- png_ptr->do_filter = PNG_FILTER_NONE;
-#endif /* PNG_WRITE_FILTER_SUPPORTED */
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* If interlaced, we need to set up width and height of pass */
- if (png_ptr->interlaced)
+ IDAT_size = png_ptr->IDAT_size;
+ if (IDAT_size == 0U)
{
- if (!(png_ptr->transformations & PNG_INTERLACE))
+ switch (pz_get(ps, IDAT, png_level, PNG_DEFAULT_COMPRESSION_LEVEL))
{
- png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
- png_pass_ystart[0]) / png_pass_yinc[0];
+ case PNG_COMPRESSION_COMPAT: /* Legacy */
+ IDAT_size = 8192U;
+ break;
- png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
- png_pass_start[0]) / png_pass_inc[0];
+ case PNG_COMPRESSION_LOW_MEMORY:
+ case PNG_COMPRESSION_HIGH_SPEED:
+ case PNG_COMPRESSION_LOW:
+ /* png_compress uses PNG_ROW_BUFFER_SIZE buffers for the compressed
+ * data. Optimize to allocate only one of these:
+ */
+ IDAT_size = PNG_ROW_BUFFER_SIZE;
+ break;
+
+ default:
+ case PNG_COMPRESSION_MEDIUM:
+ IDAT_size = PNG_ZBUF_SIZE;
+ break;
+
+ case PNG_COMPRESSION_HIGH_READ_SPEED:
+ /* Assume the reader reads partial IDAT chunks (pretty much a
+ * requirement given that some PNG encoders produce just one IDAT)
+ */
+ case PNG_COMPRESSION_HIGH:
+ /* This doesn't control the amount of memory allocated unless the
+ * PNG IDAT data really is this big.
+ *
+ * TODO: review handling out-of-memory from png_compress() by
+ * flushing an IDAT.
+ */
+ IDAT_size = PNG_UINT_31_MAX;
+ break;
+ }
+ }
+
+ /* Write IDAT chunks while either 'flush' is true or there are at
+ * least png_ptr->IDAT_size bytes available to be written.
+ */
+ for (;;)
+ {
+ png_uint_32 len = IDAT_size;
+
+ if (ps->s.overflow == 0U)
+ {
+ png_uint_32 avail = ps->s.len;
+
+ if (avail < len)
+ {
+ /* When end_of_image is true everything gets written, otherwise
+ * there must be at least IDAT_size bytes available.
+ */
+ if (!flush)
+ return;
+
+ if (avail == 0U)
+ break;
+
+ len = avail;
+ }
}
- else
+ png_write_chunk_header(png_ptr, png_IDAT, len);
+
+ /* Write bytes from the buffer list, adjusting {overflow,len} as they are
+ * written.
+ */
+ do
{
- png_ptr->num_rows = png_ptr->height;
- png_ptr->usr_width = png_ptr->width;
+ png_compression_bufferp next = ps->s.list;
+ unsigned int avail = sizeof next->output;
+ unsigned int start = ps->s.start;
+ unsigned int written;
+
+ affirm(next != NULL);
+
+ if (next->next == NULL) /* end of list */
+ {
+ /* The z_stream should always be pointing into this output buffer,
+ * the buffer may not be full:
+ */
+ debug(ps->s.zs.next_out + ps->s.zs.avail_out ==
+ next->output + sizeof next->output);
+ avail -= ps->s.zs.avail_out;
+ }
+
+ else /* not end of list */
+ debug((ps->s.zs.next_out < next->output ||
+ ps->s.zs.next_out > next->output + sizeof next->output) &&
+ (ps->s.overflow > 0 ||
+ ps->s.start + ps->s.len >= sizeof next->output));
+
+ /* First, if this is the very first IDAT (PNG_HAVE_IDAT not set)
+ * fix the Zlib CINFO field if required:
+ */
+ if ((png_ptr->mode & PNG_HAVE_IDAT) == 0U &&
+ avail >= start+2U /* enough for the zlib header */)
+ {
+ debug(start == 0U);
+ fix_cinfo(ps, next->output+start, png_image_size(png_ptr));
+ }
+
+ else /* always expect to see at least 2 bytes: */
+ debug((png_ptr->mode & PNG_HAVE_IDAT) != 0U);
+
+ /* Set this now to prevent the above happening again second time round
+ * the loop:
+ */
+ png_ptr->mode |= PNG_HAVE_IDAT;
+
+ if (avail <= start+len)
+ {
+ /* Write all of this buffer: */
+ affirm(avail > start); /* else overflow on the subtract */
+ written = avail-start;
+ png_write_chunk_data(png_ptr, next->output+start, written);
+
+ /* At the end there are no buffers in the list but the z_stream
+ * still points into the old (just released) buffer. This can
+ * happen when the old buffer is not full if the compressed bytes
+ * exactly match the IDAT length; it should always happen when
+ * end_of_image is set.
+ */
+ ps->s.list = next->next;
+
+ if (next->next == NULL)
+ {
+ debug(avail == start+len);
+ ps->s.end = &ps->s.list;
+ ps->s.zs.next_out = NULL;
+ ps->s.zs.avail_out = 0U;
+ }
+
+ next->next = ps->stash;
+ ps->stash = next;
+ ps->s.start = 0U;
+ }
+
+ else /* write only part of this buffer */
+ {
+ written = len;
+ png_write_chunk_data(png_ptr, next->output+start, written);
+ ps->s.start = (unsigned int)/*SAFE*/(start + written);
+ }
+
+ /* 'written' bytes were written: */
+ len -= written;
+
+ if (written <= ps->s.len)
+ ps->s.len -= written;
+
+ else
+ {
+ affirm(ps->s.overflow > 0U);
+ --ps->s.overflow;
+ ps->s.len += 0x80000000U - written;
+ UNTESTED
+ }
+ }
+ while (len > 0U);
+
+ png_write_chunk_end(png_ptr);
+ }
+
+ /* avail == 0 && flush */
+ png_end_IDAT(png_ptr);
+ png_ptr->mode |= PNG_AFTER_IDAT;
+}
+
+/* This is is a convenience wrapper to handle IDAT compression; it takes a
+ * pointer to the input data and places no limit on the size of the output but
+ * is otherwise the same as png_compress(). It also handles the use of the
+ * stash (only used for IDAT compression.)
+ */
+static int
+png_compress_IDAT_data(png_structrp png_ptr, png_zlib_statep ps,
+ png_zlib_compressp pz, png_const_voidp input, uInt input_len, int flush)
+{
+ /* Delay initialize the z_stream. */
+ if (png_ptr->zowner != png_IDAT)
+ png_deflate_claim(png_ptr, png_IDAT, 0U);
+
+ affirm(png_ptr->zowner == png_IDAT && pz->end != NULL && *pz->end == NULL);
+
+ /* z_stream::{next,avail}_out are set by png_compress to point into the
+ * buffer list. next_in must be set here, avail_in comes from the input_len
+ * parameter:
+ */
+ pz->zs.next_in = PNGZ_INPUT_CAST(png_voidcast(const Bytef*, input));
+ *pz->end = ps->stash; /* May be NULL */
+ ps->stash = NULL;
+
+ /* zlib buffers the output, the maximum amount of compressed data that can be
+ * produced here is governed by the amount of buffering.
+ */
+ {
+ int ret = png_compress(pz, input_len, 0U/*unlimited*/, flush);
+
+ affirm(pz->end != NULL && ps->stash == NULL);
+ ps->stash = *pz->end; /* May be NULL */
+ *pz->end = NULL;
+
+ /* Z_FINISH should give Z_STREAM_END, everything else should give Z_OK, in
+ * either case all the input should have been consumed:
+ */
+ implies(ret == Z_OK || ret == Z_FINISH, pz->zs.avail_in == 0U &&
+ (ret == Z_STREAM_END) == (flush == Z_FINISH));
+ pz->zs.next_in = NULL;
+ pz->zs.avail_in = 0U; /* safety */
+ png_zlib_compress_validate(pz, 0/*in_use*/);
+
+ return ret;
+ }
+}
+
+/* Compress some image data using the main png_zlib_compress. Write the result
+ * out if there is sufficient data.
+ */
+static void
+png_compress_IDAT(png_structrp png_ptr, png_const_voidp input, uInt input_len,
+ int flush)
+{
+ png_zlib_statep ps = png_ptr->zlib_state;
+ int ret = png_compress_IDAT_data(png_ptr, ps, &ps->s, input, input_len,
+ flush);
+
+ /* Check the return code. */
+ if (ret == Z_OK || ret == Z_STREAM_END)
+ png_write_IDAT(png_ptr, flush == Z_FINISH);
+
+ else /* ret != Z_OK && ret != Z_STREAM_END */
+ {
+ /* This is an error condition. It is fatal. */
+ png_end_IDAT(png_ptr);
+ png_zstream_error(&ps->s.zs, ret);
+ png_error(png_ptr, ps->s.zs.msg);
+ }
+}
+
+/* This is called at the end of every row to handle the required callbacks and
+ * advance png_struct::row_number and png_struct::pass.
+ */
+static void
+png_write_end_row(png_structrp png_ptr, int flush)
+{
+ png_uint_32 row_number = png_ptr->row_number;
+ unsigned int pass = png_ptr->pass;
+
+ debug(pass < 7U);
+ implies(flush == Z_FINISH, png_ptr->zowner == 0U);
+
+ /* API NOTE: the write callback is made before any changes to the row number
+ * or pass however, in 1.7.0, the zlib stream can be closed before the
+ * callback is made (this is new). The application flush function happens
+ * afterward as was the case before. In 1.7.0 this is solely determined by
+ * the order of the code that follows.
+ */
+ if (png_ptr->write_row_fn != NULL)
+ png_ptr->write_row_fn(png_ptr, row_number, pass);
+
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ if (flush == Z_SYNC_FLUSH)
+ {
+ if (png_ptr->output_flush_fn != NULL)
+ png_ptr->output_flush_fn(png_ptr);
+ png_ptr->zlib_state->flush_rows = 0U;
+ }
+# else /* !WRITE_FLUSH */
+ PNG_UNUSED(flush)
+# endif /* !WRITE_FLUSH */
+
+ /* Finally advance to the next row/pass: */
+ if (png_ptr->interlaced == PNG_INTERLACE_NONE)
+ {
+ debug(row_number < png_ptr->height);
+
+ if (++row_number == png_ptr->height) /* last row */
+ {
+ row_number = 0U;
+ debug(flush == Z_FINISH);
+ png_ptr->pass = 7U;
+ }
+ }
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ else /* interlaced */ if (png_ptr->do_interlace)
+ {
+ /* This gets called only for rows that are processed; i.e. rows that
+ * are in the pass of a pass which is itself in the output.
+ */
+ debug(row_number < png_ptr->height &&
+ PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass) &&
+ pass <= PNG_LAST_PASS(png_ptr->width, png_ptr->height) &&
+ PNG_ROW_IN_INTERLACE_PASS(row_number, pass));
+
+ /* NOTE: the last row of the original image may not be in the pass, in
+ * this case the code which skipped the row must do the increment
+ * below! See 'interlace_row' in pngwrite.c and the code in
+ * png_write_png_rows below.
+ *
+ * In that case an earlier row will be the last one in the pass (if the
+ * pass is in the output), check this here:
+ */
+ implies(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) &&
+ PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height),
+ flush == Z_FINISH);
+
+ if (++row_number == png_ptr->height) /* last row */
+ {
+ row_number = 0U;
+ png_ptr->pass = 0x7U & ++pass;
+ }
+ }
+# endif /* WRITE_INTERLACING */
+
+ else /* application does interlace */
+ {
+ implies(png_ptr->height == 1U, pass != 6U);
+ debug(PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass) &&
+ row_number < PNG_PASS_ROWS(png_ptr->height, pass));
+
+ if (++row_number == PNG_PASS_ROWS(png_ptr->height, pass))
+ {
+ /* last row in this pass, next one may be empty. */
+ row_number = 0U;
+
+ do
+ ++pass;
+ while (pass < 7U &&
+ !PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass));
+
+ implies(png_ptr->height == 1U, pass != 6U);
+ implies(pass == 7U, flush == Z_FINISH);
+ png_ptr->pass = 0x7U & pass;
+ }
+ }
+
+ png_ptr->row_number = row_number;
+}
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+/* Flush the current output buffers now */
+void PNGAPI
+png_write_flush(png_structrp png_ptr)
+{
+ png_debug(1, "in png_write_flush");
+
+ /* Force a flush at the end of the current row by setting 'flush_rows' to the
+ * maximum:
+ */
+ if (png_ptr != NULL && png_ptr->zlib_state != NULL)
+ png_ptr->zlib_state->flush_rows = 0xEFFFFFFF;
+}
+
+/* Return the correct flush to use */
+static int
+row_flush(png_zlib_statep ps, unsigned int row_info_flags)
+{
+ if (PNG_IDAT_END(row_info_flags))
+ return Z_FINISH;
+
+ else if ((row_info_flags & png_row_end) != 0 &&
+ ++ps->flush_rows >= ps->flush_dist)
+ return Z_SYNC_FLUSH;
+
+ else
+ return Z_NO_FLUSH;
+}
+#else /* !WRITE_FLUSH */
+# define row_flush(ps, ri) (PNG_IDAT_END(ri) ? Z_FINISH : Z_NO_FLUSH)
+#endif /* !WRITE_FLUSH */
+
+static void
+write_filtered_row(png_structrp png_ptr, png_const_voidp filtered_row,
+ unsigned int row_bytes, unsigned int filter /*if at start of row*/,
+ int flush)
+{
+ /* This handles writing a row that has been filtered, or did not need to be
+ * filtered. If the data row has a partial pixel it must have been handled
+ * correctly in the caller; filters generate a full 8 bits even if the pixel
+ * only has one significant bit!
+ */
+ debug(row_bytes > 0);
+ affirm(row_bytes <= ZLIB_IO_MAX); /* I.e. it fits in a uInt */
+
+ if (filter < PNG_FILTER_VALUE_LAST) /* start of row */
+ {
+ png_byte buffer[1];
+
+ buffer[0] = PNG_BYTE(filter);
+ png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH);
+ }
+
+ png_compress_IDAT(png_ptr, filtered_row, row_bytes, flush);
+}
+
+static void
+write_unfiltered_rowbits(png_structrp png_ptr, png_const_bytep filtered_row,
+ unsigned int row_bits, png_byte filter /*if at start of row*/,
+ int flush)
+{
+ /* Same as above, but it correctly clears the unused bits in a partial
+ * byte.
+ */
+ const png_uint_32 row_bytes = row_bits >> 3;
+
+ debug(filter == PNG_FILTER_VALUE_NONE || filter == PNG_FILTER_VALUE_LAST);
+
+ if (row_bytes > 0U)
+ {
+ row_bits -= row_bytes << 3;
+ write_filtered_row(png_ptr, filtered_row, row_bytes, filter,
+ row_bits == 0U ? flush : Z_NO_FLUSH);
+ filter = PNG_FILTER_VALUE_LAST; /* written */
+ }
+
+ /* Handle a partial byte. */
+ if (row_bits > 0U)
+ {
+ png_byte buffer[1];
+
+ buffer[0] = PNG_BYTE(filtered_row[row_bytes] & ~(0xFFU >> row_bits));
+ write_filtered_row(png_ptr, buffer, 1U, filter, flush);
+ }
+}
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+static void
+filter_block_singlebyte(unsigned int row_bytes, png_bytep sub_row,
+ png_bytep up_row, png_bytep avg_row, png_bytep paeth_row,
+ png_const_bytep row, png_const_bytep prev_row, png_bytep prev_pixels)
+{
+ /* Calculate rows for all four filters where the input has one byte per pixel
+ * (more accurately per filter-unit).
+ */
+ png_byte a = prev_pixels[0];
+ png_byte c = prev_pixels[1];
+
+ while (row_bytes-- > 0U)
+ {
+ const png_byte x = *row++;
+ const png_byte b = prev_row == NULL ? 0U : *prev_row++;
+
+ /* Calculate each filtered byte in turn: */
+ if (sub_row != NULL) *sub_row++ = 0xFFU & (x - a);
+ if (up_row != NULL) *up_row++ = 0xFFU & (x - b);
+ if (avg_row != NULL) *avg_row++ = 0xFFU & (x - (a+b)/2U);
+
+ /* Paeth is a little more difficult: */
+ if (paeth_row != NULL)
+ {
+ int pa = b-c; /* a+b-c - a */
+ int pb = a-c; /* a+b-c - b */
+ int pc = pa+pb; /* a+b-c - c = b-c + a-c */
+ png_byte p = a;
+
+ pa = abs(pa);
+ pb = abs(pb);
+ if (pa > pb) pa = pb, p = b;
+ if (pa > abs(pc)) p = c;
+
+ *paeth_row++ = 0xFFU & (x - p);
+ }
+
+ /* And set a and c for the next pixel: */
+ a = x;
+ c = b;
+ }
+
+ /* Store a and c for the next block: */
+ prev_pixels[0] = a;
+ prev_pixels[1] = c;
+}
+
+static void
+filter_block_multibyte(unsigned int row_bytes,
+ const unsigned int bpp, png_bytep sub_row, png_bytep up_row,
+ png_bytep avg_row, png_bytep paeth_row, png_const_bytep row,
+ png_const_bytep prev_row, png_bytep prev_pixels)
+{
+ /* Calculate rows for all four filters, the input is a block of bytes such
+ * that row_bytes is a multiple of bpp. bpp can be 2, 3, 4, 6 or 8.
+ * prev_pixels will be updated to the last pixels processed.
+ */
+ while (row_bytes >= bpp)
+ {
+ unsigned int i;
+
+ for (i=0; i pb) pa = pb, p = b;
+ if (pa > abs(pc)) p = c;
+
+ *paeth_row++ = 0xFFU & (x - p);
+ }
+ }
+
+ row_bytes -= i;
+ }
+}
+
+static void
+filter_block(png_const_bytep prev_row, png_bytep prev_pixels,
+ png_const_bytep unfiltered_row, unsigned int row_bits,
+ const unsigned int bpp, png_bytep sub_row, png_bytep up_row,
+ png_bytep avg_row, png_bytep paeth_row)
+{
+ const unsigned int row_bytes = row_bits >> 3; /* complete bytes */
+
+ if (bpp <= 8U)
+ {
+ /* There may be a partial byte at the end. */
+ if (row_bytes > 0)
+ filter_block_singlebyte(row_bytes, sub_row, up_row, avg_row, paeth_row,
+ unfiltered_row, prev_row, prev_pixels);
+
+ /* The partial byte must be handled correctly here; both the previous row
+ * value and the current value need to have non-present bits cleared.
+ */
+ if ((row_bits & 7U) != 0)
+ {
+ const png_byte mask = PNG_BYTE(~(0xFFU >> (row_bits & 7U)));
+ png_byte buffer[2];
+
+ buffer[0] = unfiltered_row[row_bytes] & mask;
+
+ if (prev_row != NULL)
+ buffer[1U] = prev_row[row_bytes] & mask;
+
+ else
+ buffer[1U] = 0U;
+
+ filter_block_singlebyte(1U,
+ sub_row == NULL ? NULL : sub_row+row_bytes,
+ up_row == NULL ? NULL : up_row+row_bytes,
+ avg_row == NULL ? NULL : avg_row+row_bytes,
+ paeth_row == NULL ? NULL : paeth_row+row_bytes,
+ buffer, buffer+1U, prev_pixels);
}
}
else
-#endif
+ filter_block_multibyte(row_bytes, bpp >> 3,
+ sub_row, up_row, avg_row, paeth_row,
+ unfiltered_row, prev_row, prev_pixels);
+}
+
+static void
+filter_row(png_structrp png_ptr, png_const_bytep prev_row,
+ png_bytep prev_pixels, png_const_bytep unfiltered_row,
+ unsigned int row_bits, unsigned int bpp, unsigned int filter,
+ int start_of_row, int flush)
+{
+ /* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE.
+ */
+ png_byte filtered_row[PNG_ROW_BUFFER_SIZE];
+
+ affirm((row_bits+7U) >> 3 <= PNG_ROW_BUFFER_SIZE &&
+ filter >= PNG_FILTER_VALUE_SUB && filter <= PNG_FILTER_VALUE_PAETH);
+ debug((row_bits % bpp) == 0U);
+
+ filter_block(prev_row, prev_pixels, unfiltered_row, row_bits, bpp,
+ filter == PNG_FILTER_VALUE_SUB ? filtered_row : NULL,
+ filter == PNG_FILTER_VALUE_UP ? filtered_row : NULL,
+ filter == PNG_FILTER_VALUE_AVG ? filtered_row : NULL,
+ filter == PNG_FILTER_VALUE_PAETH ? filtered_row : NULL);
+
+ write_filtered_row(png_ptr, filtered_row, (row_bits+7U)>>3,
+ start_of_row ? filter : PNG_FILTER_VALUE_LAST, flush);
+}
+
+/* Allow the application to select one or more row filters to use. */
+static png_int_32
+set_filter(png_zlib_statep ps, unsigned int filtersIn)
+{
+ /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK because
+ * filters then gets set to PNG_FILTER_NONE, as is required.
+ *
+ * The argument to this routine is actually an (int), but conversion to
+ * (unsigned int) is safe because it leaves the top bits set which results in
+ * PNG_EDOM below.
+ */
+ if (filtersIn < PNG_FILTER_NONE)
+ filtersIn = PNG_FILTER_MASK(filtersIn);
+
+ /* PNG_ALL_FILTERS is a constant, unfortunately it is nominally signed, for
+ * historical reasons, hence the PNG_BIC_MASK here.
+ */
+ if ((filtersIn & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0U)
{
- png_ptr->num_rows = png_ptr->height;
- png_ptr->usr_width = png_ptr->width;
+# ifndef PNG_SELECT_FILTER_SUPPORTED
+ filtersIn &= -filtersIn; /* Use lowest set bit */
+# endif /* !SELECT_FILTER */
+
+ return ps->filter_mask = filtersIn & PNG_ALL_FILTERS;
+ }
+
+ else /* Out-of-range filtersIn: */
+ return PNG_EDOM;
+}
+#endif /* WRITE_FILTER */
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+void /* PRIVATE */
+png_write_start_IDAT(png_structrp png_ptr)
+{
+ png_zlib_statep ps = get_zlib_state(png_ptr);
+
+ /* Set up the IDAT compression state. Expect the state to have been released
+ * by the previous owner, but it doesn't much matter if there was an error.
+ * Note that the stream is not claimed yet.
+ */
+ debug(png_ptr->zowner == 0U);
+
+ /* This sets the buffer limits and write_row_size, which is used below. */
+ png_zlib_state_set_buffer_limits(png_ptr, ps);
+
+ if (ps->filter_mask == 0)
+ {
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ /* Now default the filter mask if it hasn't been set already: */
+ int png_level =
+ pz_get(ps, IDAT, png_level, PNG_DEFAULT_COMPRESSION_LEVEL);
+
+ /* If the bit depth is less than 8, so pixels are not byte aligned, PNG
+ * filtering hardly ever helps because there is no correlation between
+ * the bytes on which the filter works and the actual pixel values.
+ * Note that GIF is a whole lot better at this because it uses LZW to
+ * compress a bit-stream, not a byte stream as in the deflate
+ * implementation of LZ77.
+ *
+ * If the row size is less than 256 bytes filter selection algorithms
+ * are flakey because the restricted range of codes in each row can
+ * lead to poor selection of filters, particularly if the bytes in the
+ * image are themselves limited. (This happens when a low bit-depth
+ * image is encoded with 8-bit channels.)
+ *
+ * By experiment with the test set of images the breakpoint between
+ * not filtering and filtering based on which gives best compression by
+ * row size is as follows:
+ *
+ * NONE FAST ALL
+ * PAL <=anything [even 8-bit palette images larger if filtered]
+ * G<8 <=anything [low bit depth gray images]
+ * G8 <=16 [+~1%] >16
+ * G16 <=128 [+~1%] >128
+ * GA8 <=64 [+~1%] >64
+ * GA16 <=anything [always better without filtering!]
+ * RGB8 <=32 [+0-2%(1)] >32
+ * RGB16 <=1024 [+~1%] >1024
+ * RGBA8 <=64 [+~~1%] >64
+ * RGBA16 <=128 {+~0.5%] >128
+ *
+ * (1) The largest 24-bit RGB image (RGB8) faired better, by 1.3%,
+ * with 'fast' filters. This is assumed to be random.
+ *
+ * Aggregated across all color types and bit depths the breakpoint for
+ * filtering is >16 bytes, but the size increase only exceeds 0.5% for
+ * images with rows between 64 and 128 bytes, hence the choices below.
+ *
+ * Across all the test images that change (not including selecting just
+ * the 'fast' filters by default) does not change the compressed size
+ * significantly (+0.06% across the whole test set), however it does
+ * substantially increase the number of images without filtering.
+ *
+ * Using just none and sub filters results in overall compressed sizes
+ * somewhere around the geometric mean of no filtering and 'fast'.
+ *
+ * The image size also plays a part. Filtering is not an advantage for
+ * images of size <= 512 bytes. This is also reflected below.
+ *
+ * NOTE: the libpng 1.6 (and earlier) algorithm seems to work
+ * because it biases the byte codes in the output towards 0 and 255.
+ * Zlib doesn't care what the codes are, but Huffman encoding always
+ * benefits from a biased distribution and the filters themselves were
+ * designed to produce values in this range.
+ *
+ * In a raw comparison with the legacy code selection of specific sets
+ * of filters always increased the compressed size of the test set, as
+ * follows:
+ *
+ * PNG_ALL_FILTERS: +0.26%
+ * PNG_FAST_FILTERS: +1.9%
+ * NONE+SUB: +5.8%
+ * PNG_NO_FILTERS: +14%
+ *
+ * This mainly proves that a static selection of filters (without
+ * considering the PNG format) is always worse than the legacy
+ * algorithm below.
+ *
+ * NOTE: ps->filter_mask must be set to a mask value, not a simple
+ * PNG_FILTER_VALUE_ number.
+ */
+ if (ps->write_row_size == 0U /* row cannot be buffered */)
+ ps->filter_mask = PNG_FILTER_NONE;
+
+ else if (png_level == PNG_COMPRESSION_COMPAT/* Legacy */)
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+ png_ptr->bit_depth < 8U)
+ ps->filter_mask = PNG_FILTER_NONE;
+
+ else
+ ps->filter_mask = PNG_ALL_FILTERS;
+ }
+
+ /* NOTE: overall with the following size tests (row and image size) the
+ * test set of images end up 0.06% larger, however some color types are
+ * smaller and some larger; the differences are minute. If the test is
+ * <=128 (which means <=129 bytes per row with the filter byte) the
+ * resultant inclusion of 32x32 RGBA images results in significantly
+ * increased compressed size.
+ *
+ * The test on png_level captures the following settings:
+ *
+ * PNG_COMPRESSION_LOW_MEMORY
+ * PNG_COMPRESSION_HIGH_SPEED
+ * PNG_COMPRESSION_HIGH_READ_SPEED
+ *
+ * NOTE: this relies on the exact values in png.h!
+ */
+ else if (png_level <= PNG_COMPRESSION_HIGH_READ_SPEED
+ || png_ptr->color_type == PNG_COLOR_TYPE_PALETTE
+ || png_ptr->bit_depth < 8U
+ || ps->write_row_size/*does not include filter*/ < 128U
+ || png_image_size(png_ptr) <= 512U)
+ ps->filter_mask = PNG_FILTER_NONE;
+
+ /* ELSE: there are at least 128 bytes in every row and the pixels
+ * are multiples of a byte.
+ */
+ else switch (png_level)
+ {
+ default: /* For GCC */
+ case PNG_COMPRESSION_LOW:
+ ps->filter_mask = PNG_FILTER_NONE+PNG_FILTER_SUB;
+ break;
+
+ case PNG_COMPRESSION_MEDIUM:
+ ps->filter_mask = PNG_FAST_FILTERS;
+ break;
+
+ case PNG_COMPRESSION_HIGH:
+ ps->filter_mask = PNG_ALL_FILTERS;
+ break;
+ }
+# else /* !SELECT_FILTER */
+ ps->filter_mask = PNG_FILTER_NONE;
+# endif /* !SELECT_FILTER */
}
}
-/* Internal use only. Called when finished processing a row of data. */
-void /* PRIVATE */
-png_write_finish_row(png_structrp png_ptr)
+static png_byte
+png_write_start_row(png_zlib_statep ps, int start_of_pass, int no_previous_row)
+ /* Called at the start of a row to set up anything required for filter
+ * handling in the row. Sets png_zlib_state::filters to a single filter.
+ */
{
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+ unsigned int mask = ps->filter_mask;
- /* Start of interlace block */
- static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+ /* If we see a previous-row filter in mask and png_zlib_state::save_row is
+ * still unset set it. This means that the first time a previous-row filter
+ * is seen row-saving gets turned on.
+ */
+ if (ps->save_row == SAVE_ROW_UNSET && (mask & PREVIOUS_ROW_FILTERS) != 0U)
+ ps->save_row = SAVE_ROW_DEFAULT;
- /* Offset to next interlace block */
- static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+ if ((no_previous_row /* row not stored */ && !start_of_pass) ||
+ ps->save_row == SAVE_ROW_OFF /* disabled by app */ ||
+ ps->write_row_size == 0U /* row too large to buffer */)
+ mask &= PNG_BIC_MASK(PREVIOUS_ROW_FILTERS);
- /* Start of interlace block in the y direction */
- static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+ /* On the first row of a pass Paeth is equivalent to sub and up is equivalent
+ * to none, so try to simplify the mask in in this case.
+ */
+ else if (start_of_pass) {
+# define MATCH(flags) ((mask & (flags)) == (flags))
+ if (MATCH(PNG_FILTER_NONE|PNG_FILTER_UP))
+ mask &= PNG_BIC_MASK(PNG_FILTER_UP);
- /* 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};
-#endif
+ if (MATCH(PNG_FILTER_SUB|PNG_FILTER_PAETH))
+ mask &= PNG_BIC_MASK(PNG_FILTER_PAETH);
+# undef MATCH
+ }
- png_debug(1, "in png_write_finish_row");
-
- /* Next row */
- png_ptr->row_number++;
-
- /* See if we are done */
- if (png_ptr->row_number < png_ptr->num_rows)
- return;
-
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
- /* If interlaced, go to next pass */
- if (png_ptr->interlaced)
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ if ((mask & (mask-1U)) == 0U /* single bit set */ ||
+ ps->write_row_size == 0U /* row cannot be buffered */)
+# endif /* SELECT_FILTER */
+ /* Convert the lowest set bit into the corresponding value. If no bits
+ * are set select NONE. After this switch statement the value of
+ * ps->filters is guaranteed to just be a single filter.
+ */
+ switch (mask & -mask)
{
- png_ptr->row_number = 0;
- if (png_ptr->transformations & PNG_INTERLACE)
+ default: mask = PNG_FILTER_VALUE_NONE; break;
+ case PNG_FILTER_SUB: mask = PNG_FILTER_VALUE_SUB; break;
+ case PNG_FILTER_UP: mask = PNG_FILTER_VALUE_UP; break;
+ case PNG_FILTER_AVG: mask = PNG_FILTER_VALUE_AVG; break;
+ case PNG_FILTER_PAETH: mask = PNG_FILTER_VALUE_PAETH; break;
+ }
+
+ return ps->filters = PNG_BYTE(mask);
+}
+
+static png_bytep
+allocate_row(png_structrp png_ptr, png_const_bytep data, png_alloc_size_t size)
+ /* Utility to allocate and save some row bytes. If the result is NULL the
+ * allocation failed and the png_zlib_struct will have been updated to
+ * prevent further allocation attempts.
+ */
+{
+ const png_zlib_statep ps = png_ptr->zlib_state;
+ png_bytep buffer;
+
+ debug(ps->write_row_size > 0U);
+
+ /* OOM is handled silently, as is the case where the row is too large to
+ * buffer.
+ */
+ buffer = png_voidcast(png_bytep,
+ png_malloc_base(png_ptr, ps->write_row_size));
+
+ /* Setting write_row_size to 0 switches on the code for handling a row that
+ * is too large to buffer. This will kick in next time round, i.e. on the
+ * next row.
+ */
+ if (buffer == NULL)
+ ps->write_row_size = 0U;
+
+ else
+ memcpy(buffer, data, size);
+
+ return buffer;
+}
+#endif /* WRITE_FILTER */
+
+#ifdef PNG_SELECT_FILTER_SUPPORTED
+/* Bit set operations. Not in ANSI C-90 but commonly available in highly
+ * optimized versions, hence the ifndef. These operations just work on bitsets
+ * of size 256. The second argument (the code index) may be evaluated multiple
+ * times.
+ */
+#ifndef PNG_CODE_SET /* Can be set in pngpriv.h */
+typedef png_uint_32 png_codeset[8];
+# define PNG_CODE_MASK(i) (((png_uint_32)1U) << ((i) & 0x1FU))
+# define PNG_CODE_IS_SET(c,i) (((c)[(i) >> 5] & PNG_CODE_MASK(i)))
+# define PNG_CODE_SET(c,i) (((c)[(i) >> 5] |= PNG_CODE_MASK(i)))
+# define PNG_CODE_CLEAR(c,i) (((c)[(i) >> 5] &= ~PNG_CODE_MASK(i)))
+#endif /* !PNG_CODE_SET */
+
+typedef struct filter_selector
+{
+ /* Persistent filter selection information (stored across row boundaries).
+ * A code is not considered if it last occured more than 'window' bytes ago.
+ * The deflate algorithm means that 'window' cannot exceed 8453377, however
+ * practical versions may be far less. When 'distance' reaches 'window' any
+ * code where:
+ *
+ * distance - code_distance[code] > window
+ *
+ * at the end of a row 'code' is removed from codeset. Otherwise
+ * (rearranging the above):
+ *
+ * distance - window <= code_distance[code]
+ *
+ * and so the distances of the still active codes can be reduced:
+ *
+ * code_distance[code] -= distance-window
+ * distance = window
+ *
+ * This prevents any wrap of 'distance' on a row which is shorter than
+ * 2^32-window.
+ *
+ * However when then row is 2^32-window or more bytes long (the row can be up
+ * to just under 2^34 bytes long) this algorithm doesn't work; 'distance'
+ * will overflow in the middle of the row and all codes are relevant. This
+ * is handled below simply by reseting the set of present codes at the start
+ * of the row and ignoring the overflow.
+ */
+ unsigned int code_count; /* Number of distinct codes seen */
+ int png_level; /* Cached compression level */
+ png_uint_32 filter_select_max_width;
+ /* The maximum number of pixels which can be fitted in the window without
+ * filling the entire window (i.e. the maximum number that can be fitted
+ * in (window-1) bytes).
+ */
+ png_uint_32 sum_bias[PNG_FILTER_VALUE_LAST];
+ /* For each filter a measure of its cost in the filter sum calculation.
+ * This allows filter selection based on the sum-of-absolute-dfferences
+ * method to be biased to favour particular filters. There was no such
+ * bias before 1.7 and the filter byte was ignored.
+ */
+ png_uint_32 distance; /* Distance from beginning */
+ png_codeset codeset; /* Set of seen codes */
+ png_uint_32 code_distance[256]; /* Distance at last occurence */
+} filter_selector;
+
+static const filter_selector *
+png_start_filter_select(png_zlib_statep ps, unsigned int bpp)
+{
+# define png_ptr ps_png_ptr(ps)
+ filter_selector *fs = ps->selector;
+
+ if (fs == NULL)
+ {
+ fs = png_voidcast(filter_selector*, png_malloc_base(png_ptr, sizeof *fs));
+
+ if (fs != NULL)
{
- png_ptr->pass++;
+ png_uint_32 window = ps->filter_select_window;
+ fs->png_level = pz_get(ps, IDAT, png_level,
+ PNG_DEFAULT_COMPRESSION_LEVEL);
+
+ /* Delay initialize this here: */
+ if (window < 3U || window > PNG_FILTER_SELECT_WINDOW_MAX)
+ ps->filter_select_window = window = PNG_FILTER_SELECT_WINDOW_MAX;
+
+ fs->code_count = 0;
+
+ switch (fs->png_level)
+ {
+ default:
+ /* TODO: investigate other settings */
+ {
+ unsigned int f;
+
+ for (f=0; fsum_bias[f] = f;
+ }
+ ps->filter_select_threshold = 64U; /* 6bit RGB */
+ ps->filter_select_threshold2 = 50U; /* TODO: experiment! */
+ break;
+
+ case PNG_COMPRESSION_COMPAT: /* Legacy */
+ memset(fs->sum_bias, 0U, sizeof fs->sum_bias);
+ ps->filter_select_threshold = 1U; /* disabled */
+ ps->filter_select_threshold2 = 1U;
+ break;
+ }
+
+ /* This is the maximum row width, in pixels, of a row which fits and
+ * leaves 1 byte free in the window. For any bigger row filter
+ * selection ignores the previous rows.
+ */
+ fs->filter_select_max_width = ((window-2U/*filter+last byte*/)*8U)/bpp;
+ fs->distance = 0U;
+ memset(fs->codeset, 0U, sizeof fs->codeset);
+ /* fs->code_distance is left uninitialized because fs->codeset says
+ * whether or not each entry has been initialized.
+ */
+ ps->selector = fs;
}
else
- {
- /* Loop until we find a non-zero width or height pass */
- do
- {
- png_ptr->pass++;
-
- if (png_ptr->pass >= 7)
- break;
-
- png_ptr->usr_width = (png_ptr->width +
- png_pass_inc[png_ptr->pass] - 1 -
- png_pass_start[png_ptr->pass]) /
- png_pass_inc[png_ptr->pass];
-
- 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];
-
- if (png_ptr->transformations & PNG_INTERLACE)
- break;
-
- } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
-
- }
-
- /* Reset the row above the image for the next pass */
- if (png_ptr->pass < 7)
- {
- if (png_ptr->prev_row != NULL)
- memset(png_ptr->prev_row, 0,
- (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
- png_ptr->usr_bit_depth, png_ptr->width)) + 1);
-
- return;
- }
+ ps->write_row_size = 0U; /* OOM */
}
-#endif
+# undef png_ptr
- /* If we get here, we've just written the last row, so we need
- to flush the compressor */
- png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
+ return fs;
}
-#ifdef PNG_WRITE_INTERLACING_SUPPORTED
-/* Pick out the correct pixels for the interlace pass.
- * The basic idea here is to go through the row with a source
- * pointer and a destination pointer (sp and dp), and copy the
- * correct pixels for the pass. As the row gets compacted,
- * sp will always be >= dp, so we should never overwrite anything.
- * See the default: case for the easiest code to understand.
+typedef struct
+{
+ /* Per-filter data. This remains separate from the above until the filter
+ * selection has been made. It reflects the above however the codeset only
+ * records codes present in this row.
+ *
+ * The 'sum' fields are the sum of the absolute deviation of each code from
+ * 0, the algorithm from 1.6 and earlier. In other words:
+ *
+ * if (code >= 128)
+ * sum += code;
+ * else
+ * sum += 256-code;
+ */
+ unsigned int code_count; /* Number of distinct codes seen in row */
+ unsigned int new_code_count; /* Number of new codes seen in row */
+ png_uint_32 sum_low; /* Low 31 bits of code sum */
+ png_uint_32 sum_high; /* High 32 bits of code sum */
+ png_codeset codeset; /* Set of codes seen in this row */
+ png_uint_32 code_distance[256]; /* Distance at last occurence in this row */
+} filter_data;
+
+static void
+filter_data_init(filter_data *fd, png_uint_32 distance, unsigned int filter,
+ unsigned int code_is_set, png_uint_32 bias)
+{
+ fd->code_count = 1U;
+ fd->new_code_count = !code_is_set;
+ fd->sum_low = bias;
+ fd->sum_high = 0U;
+ memset(&fd->codeset, 0U, sizeof fd->codeset);
+ PNG_CODE_SET(fd->codeset, filter);
+ fd->code_distance[filter] = distance;
+}
+
+static void
+add_code(const filter_selector *fs, filter_data *fd, png_uint_32 distance,
+ unsigned int code)
+{
+ if (!PNG_CODE_IS_SET(fd->codeset, code))
+ {
+ PNG_CODE_SET(fd->codeset, code);
+ ++(fd->code_count);
+ fd->code_distance[code] = distance;
+ if (!PNG_CODE_IS_SET(fs->codeset, code))
+ ++(fd->new_code_count);
+ }
+
+ {
+ png_uint_32 low = fd->sum_low;
+
+ if (code <= 128U)
+ low += code;
+
+ else
+ low += 256U-code;
+
+ /* Handle overflow into the top bit: */
+ if (low & 0x80000000U)
+ fd->sum_low = low & 0x7FFFFFFFU, ++fd->sum_high;
+
+ else
+ fd->sum_low = low;
+ }
+}
+
+static png_byte
+filter_data_select(png_zlib_statep ps, filter_data fd[PNG_FILTER_VALUE_LAST],
+ unsigned int filter, png_uint_32 distance, png_uint_32 w)
+{
+# define png_ptr ps_png_ptr(ps)
+ /* Choose how to do this depending on the row and window size. */
+ filter_selector *fs = ps->selector;
+ png_uint_32 window = ps->filter_select_window;
+
+ affirm(fs != NULL);
+
+ /* Check the width against the maximum number of pixels that can fit in a
+ * window without filling it:
+ */
+ if (w > fs->filter_select_max_width)
+ {
+ /* The cache is not used */
+ fs->distance = 0U; /* for next row */
+ fs->code_count = 0U;
+ memset(fs->codeset, 0U, sizeof fs->codeset);
+ }
+
+ else
+ {
+ /* Merge the two code sets, discounting codes that last occurred before
+ * the start of the window.
+ */
+ png_uint_32 adjust, code_count;
+ unsigned int code;
+
+ /* filter_selector::distance is the distance of the first byte in the row
+ * (the filter byte), but 'distance' can wrap on long rows. The above
+ * test is meant to exclude the wrap case by excluding any case where the
+ * row has as many bytes as the window, so:
+ */
+ affirm(distance > fs->distance && distance - fs->distance < window);
+
+ /* Set 'adjust' to the current distance of the start of the window. I.e:
+ *
+ * +---------------+--------+
+ * | before window | window | future data
+ * +---------------+--------+
+ * A A
+ * | |
+ * adjust + + distance
+ *
+ * If the window isn't full yet 'adjust' will be zero, otherwise all the
+ * distances will be reduced by 'adjust' so that the first byte of the
+ * window has distance 0.
+ */
+ if (distance > window)
+ adjust = distance-window;
+
+ else
+ adjust = 0;
+
+
+ /* This may be decreased below if some old codes only occured before the
+ * start of the window.
+ */
+ code_count = fs->code_count + fd->new_code_count;
+
+ for (code=0U; code<256U; ++code)
+ {
+ if (PNG_CODE_IS_SET(fd[filter].codeset, code))
+ {
+ PNG_CODE_SET(fs->codeset, code);
+ debug(fd[filter].code_distance[code] >= adjust);
+ fs->code_distance[code] = fd[filter].code_distance[code] - adjust;
+ }
+
+ else if (PNG_CODE_IS_SET(fs->codeset, code) && adjust > 0)
+ {
+ /* The code did not occur in this row, the old distance may now be
+ * outside the window (because adjust is non-zero).
+ */
+ const png_uint_32 d = fs->code_distance[code];
+
+ if (d >= adjust)
+ fs->code_distance[code] = d-adjust;
+
+ else
+ PNG_CODE_CLEAR(fs->codeset, code), --code_count;
+ }
+ }
+
+ fs->code_count = code_count;
+ fs->distance = distance - adjust; /* I.e. either distance or window! */
+ }
+
+ return ps->filters = PNG_BYTE(filter);
+# undef png_ptr
+}
+
+static png_byte
+select_filter(png_zlib_statep ps, png_const_bytep row,
+ png_const_bytep prev, unsigned int bpp, png_uint_32 width, int start_of_pass)
+ /* Select a filter from the list provided by png_write_start_row. */
+{
+ png_byte filters = png_write_start_row(ps, start_of_pass, prev == NULL);
+
+# define png_ptr ps_png_ptr(ps)
+ if (filters >= PNG_FILTER_NONE) /* multiple filters to test */
+ {
+ const png_uint_32 max_pixels = ps->row_buffer_max_pixels;
+ const png_uint_32 block_pixels = ps->row_buffer_max_aligned_pixels;
+ const filter_selector *fs = ps->selector;
+ png_uint_32 pixels_to_go = width;
+ png_uint_32 distance;
+ unsigned int bits_at_end = 0U;
+ png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4x2-bytes each */
+ filter_data fd[PNG_FILTER_VALUE_LAST];
+
+ debug((filters & (filters-1)) != 0U); /* Expect more than one bit! */
+
+ if (fs == NULL)
+ {
+ /* Delay initialize with a quiet OOM handler */
+ fs = png_start_filter_select(ps, bpp);
+ if (fs == NULL)
+ {
+ ps->filters = PNG_FILTER_VALUE_NONE;
+ return PNG_FILTER_VALUE_NONE;
+ }
+ }
+
+ /* If PNG_FILTER_NONE is in the list check it first. */
+ if (filters & PNG_FILTER_NONE)
+ {
+ png_const_bytep rp = row;
+ png_uint_32 w = width;
+
+ distance = fs->distance;
+ filter_data_init(fd+PNG_FILTER_VALUE_NONE, distance++,
+ PNG_FILTER_VALUE_NONE,
+ PNG_CODE_IS_SET(fs->codeset, PNG_FILTER_VALUE_NONE),
+ fs->sum_bias[PNG_FILTER_VALUE_NONE]);
+
+ if (bpp >= 8) /* complete bytes */
+ {
+ const unsigned int bytes = bpp/8U;
+
+ while (w > 0)
+ {
+ unsigned int b;
+ for (b=0; b= ppb)
+ {
+ add_code(fs, fd+PNG_FILTER_VALUE_NONE, distance++, *rp++);
+ w -= ppb;
+ }
+
+ if (w > 0) /* partial byte at end */
+ add_code(fs, fd+PNG_FILTER_VALUE_NONE, distance++,
+ *rp & (0xFFU >> (w*bpp) /* zero unused bits */));
+ }
+
+ /* For PNG data with a small number of codes it is worth skipping the
+ * filtering because it almost always increases the code count
+ * significantly. This is controlled by
+ * png_zlib_state::filter_select_threshold and causes an early return
+ * here.
+ */
+ if (fd[PNG_FILTER_VALUE_NONE].new_code_count +
+ fs->code_count < ps->filter_select_threshold)
+ return filter_data_select(ps, fd, PNG_FILTER_VALUE_NONE, distance,
+ width);
+ } /* PNG_FILTER_NONE */
+
+ memset(prev_pixels, 0U, sizeof prev_pixels);
+ distance = fs->distance;
+
+ {
+ unsigned int i;
+
+ for (i=PNG_FILTER_VALUE_NONE+1U; icodeset, i), fs->sum_bias[i]);
+ }
+
+ ++distance;
+
+ while (pixels_to_go || bits_at_end)
+ {
+ unsigned int bits, i;
+ union
+ {
+ PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
+ png_byte row[4][PNG_ROW_BUFFER_SIZE];
+ } filtered;
+ union
+ {
+ PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment;
+ png_byte byte;
+ } last;
+
+ if (pixels_to_go)
+ {
+ if (pixels_to_go > max_pixels)
+ {
+ /* Maintain alignment by consuming on block_pixels at once */
+ bits = block_pixels * bpp;
+ pixels_to_go -= block_pixels; /* May be 0 */
+ }
+
+ else
+ {
+ bits = pixels_to_go * bpp;
+ bits_at_end = bits & 0x7U;
+ bits -= bits_at_end;
+ pixels_to_go = 0U; /* +bits_at_end */
+ }
+ }
+
+ else /* incomplete byte at the end of the pixel */
+ {
+ /* Make sure the unused bits are cleared (to zero, although this is
+ * an arbitrary choice):
+ */
+ last.byte = PNG_BYTE(*row & ~(0xFFU >> bits_at_end));
+ row = &last.byte;
+ bits = bits_at_end;
+ bits_at_end = 0U;
+ }
+
+ filter_block(prev, prev_pixels, row, bits, bpp,
+ filtered.row[0/*sub*/], filtered.row[1/*up*/],
+ filtered.row[2/*avg*/], filtered.row[3/*Paeth*/]);
+
+ /* A block of (bits+7)/8 bytes is now available to process. */
+ for (i=0; 8U*i < bits; ++i, ++distance)
+ {
+ unsigned int f;
+
+ for (f=PNG_FILTER_VALUE_NONE+1U; f> 3;
+
+ row += bits >> 3;
+ }
+
+ /* Now look at the candidate filters, including 'none' and select the
+ * best. We know that 'none' increases the code count beyond the
+ * threshold, so if the old code count is below the threshold and there is
+ * a filter which does not increase the code count select it; doing so
+ * should do no harm to the overall compression.
+ */
+ if (fs->code_count < ps->filter_select_threshold)
+ {
+ unsigned int f, min_new_count = 257U, min_f = PNG_FILTER_VALUE_NONE;
+
+ for (f=PNG_FILTER_VALUE_NONE+1U; fcode_count + min_new_count < ps->filter_select_threshold2)
+ return filter_data_select(ps, fd, min_f, distance, width);
+ }
+
+ /* Now fall back to the libpng 1.6 and earlier algorithm. This favours
+ * the filter which produces least deviation in the codes from 0. When
+ * this works it does so by reducing the distribution of code values. The
+ * filters implicitly encode the difference between a predictor based on
+ * adjacent values, the assumption is that this will result in values
+ * close to 0.
+ */
+ {
+ png_uint_32 high = -1;
+ png_uint_32 low = -1;
+ unsigned int min_f = 0 /*unset, but safe*/;
+ unsigned int f;
+
+ for (f=PNG_FILTER_VALUE_NONE; ffilters = filters;
+# undef png_ptr
+}
+#else /* !SELECT_FILTER */
+ /* Filter selection not being done, just call png_write_start_row: */
+# define select_filter(ps, rp, pp, bpp, width, start_of_pass)\
+ png_write_start_row((ps), (start_of_pass), (pp) == NULL)
+#endif /* !SELECT_FILTER */
+
+/* This is the common function to write multiple rows of PNG data. The data is
+ * in the relevant PNG format but has had no filtering done.
*/
void /* PRIVATE */
-png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows,
+ png_uint_32 num_rows)
{
- /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+ const png_zlib_statep ps = png_ptr->zlib_state;
+ const unsigned int bpp = png_ptr->row_output_pixel_depth;
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ png_const_bytep previous_row = ps->previous_write_row;
+# else /* !WRITE_FILTER */
+ /* These are constant in the no-filer case: */
+ const png_byte filter = PNG_FILTER_VALUE_NONE;
+ const png_uint_32 max_pixels = ps->zlib_max_pixels;
+ const png_uint_32 block_pixels = ps->zlib_max_aligned_pixels;
+# endif /* !WRITE_FILTER */
+ /* Write the given rows handling the png_compress_IDAT argument limitations
+ * (uInt) and any valid row width.
+ */
+ png_uint_32 last_row_in_pass = 0U; /* Actual last, not last+1! */
+ png_uint_32 pixels_in_pass = 0U;
+ unsigned int first_row_in_pass = 0U; /* For do_interlace */
+ unsigned int pixels_at_end = 0U; /* for a partial byte at the end */
+ unsigned int base_info_flags = png_row_end;
+ int pass = -1; /* Invalid: force calculation first time round */
- /* Start of interlace block */
- static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+ debug(png_ptr->row_output_pixel_depth == PNG_PIXEL_DEPTH(*png_ptr));
- /* Offset to next interlace block */
- static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
-
- png_debug(1, "in png_do_write_interlace");
-
- /* We don't have to do anything on the last pass (6) */
- if (pass < 6)
+ while (num_rows-- > 0U)
{
- /* Each pixel depth is handled separately */
- switch (row_info->pixel_depth)
+ if (png_ptr->pass != pass)
{
- case 1:
+ /* Recalcuate the row bytes and partial bits */
+ pass = png_ptr->pass;
+ pixels_in_pass = png_ptr->width;
+
+ if (png_ptr->interlaced == PNG_INTERLACE_NONE)
{
- png_bytep sp;
- png_bytep dp;
- int shift;
- int d;
- int value;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
+ debug(pass == 0);
+ last_row_in_pass = png_ptr->height - 1U;
+ base_info_flags |= png_pass_last; /* there is only one */
+ }
- dp = row;
- d = 0;
- shift = 7;
+ else
+ {
+ const png_uint_32 height = png_ptr->height;
- for (i = png_pass_start[pass]; i < row_width;
- i += png_pass_inc[pass])
- {
- sp = row + (png_size_t)(i >> 3);
- value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
- d |= (value << shift);
+ last_row_in_pass = PNG_PASS_ROWS(height, pass);
+ debug(pass >= 0 && pass < 7);
- if (shift == 0)
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ if (png_ptr->do_interlace)
{
- shift = 7;
- *dp++ = (png_byte)d;
- d = 0;
+ /* libpng is doing the interlace handling, the row number is
+ * actually the row in the image.
+ *
+ * This overflows when the PNG height is such that the are no
+ * rows in this pass. This does not matter; because there are
+ * no rows the value doesn't get used.
+ */
+ last_row_in_pass =
+ PNG_ROW_FROM_PASS_ROW(last_row_in_pass-1U, pass);
+ first_row_in_pass = PNG_PASS_START_ROW(pass);
}
- else
- shift--;
-
- }
- if (shift != 7)
- *dp = (png_byte)d;
-
- break;
- }
-
- case 2:
- {
- png_bytep sp;
- png_bytep dp;
- int shift;
- int d;
- int value;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
-
- dp = row;
- shift = 6;
- d = 0;
-
- for (i = png_pass_start[pass]; i < row_width;
- i += png_pass_inc[pass])
+ else /* Application handles the interlace */
+# endif /* WRITE_INTERLACING */
{
- sp = row + (png_size_t)(i >> 2);
- value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
- d |= (value << shift);
-
- if (shift == 0)
- {
- shift = 6;
- *dp++ = (png_byte)d;
- d = 0;
- }
-
- else
- shift -= 2;
+ /* The row does exist, so this works without checking the column
+ * count.
+ */
+ debug(last_row_in_pass > 0U);
+ last_row_in_pass -= 1U;
}
- if (shift != 6)
- *dp = (png_byte)d;
- break;
+ if (pass == PNG_LAST_PASS(pixels_in_pass/*PNG width*/, height))
+ base_info_flags |= png_pass_last;
+
+ /* Finally, adjust pixels_in_pass for the interlacing (skip the
+ * final pass; it is full width).
+ */
+ if (pass < 6)
+ pixels_in_pass = PNG_PASS_COLS(pixels_in_pass, pass);
}
- case 4:
- {
- png_bytep sp;
- png_bytep dp;
- int shift;
- int d;
- int value;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
+ /* Mask out the bits in a partial byte. */
+ pixels_at_end = pixels_in_pass & PNG_ADDOF(bpp);
- dp = row;
- shift = 4;
- d = 0;
- for (i = png_pass_start[pass]; i < row_width;
- i += png_pass_inc[pass])
- {
- sp = row + (png_size_t)(i >> 1);
- value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
- d |= (value << shift);
-
- if (shift == 0)
- {
- shift = 4;
- *dp++ = (png_byte)d;
- d = 0;
- }
-
- else
- shift -= 4;
- }
- if (shift != 4)
- *dp = (png_byte)d;
-
- break;
- }
-
- default:
- {
- png_bytep sp;
- png_bytep dp;
- png_uint_32 i;
- png_uint_32 row_width = row_info->width;
- png_size_t pixel_bytes;
-
- /* Start at the beginning */
- dp = row;
-
- /* Find out how many bytes each pixel takes up */
- pixel_bytes = (row_info->pixel_depth >> 3);
-
- /* Loop through the row, only looking at the pixels that matter */
- for (i = png_pass_start[pass]; i < row_width;
- i += png_pass_inc[pass])
- {
- /* Find out where the original pixel is */
- sp = row + (png_size_t)i * pixel_bytes;
-
- /* Move the pixel */
- if (dp != sp)
- memcpy(dp, sp, pixel_bytes);
-
- /* Next pixel */
- dp += pixel_bytes;
- }
- break;
- }
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* Reset the previous_row pointer correctly; NULL at the start of
+ * the pass. If row_number is not 0 then a previous write_rows was
+ * interrupted in mid-pass and any required buffer should be in
+ * previous_write_row (set in the initializer).
+ */
+ if (png_ptr->row_number == first_row_in_pass)
+ previous_row = NULL;
+# endif /* WRITE_FILTER */
}
- /* Set new row width */
- row_info->width = (row_info->width +
- png_pass_inc[pass] - 1 -
- png_pass_start[pass]) /
- png_pass_inc[pass];
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
- row_info->width);
- }
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* When libpng is handling the interlace we see rows that must be
+ * skipped.
+ */
+ if (!png_ptr->do_interlace ||
+ PNG_ROW_IN_INTERLACE_PASS(png_ptr->row_number, pass))
+# endif /* WRITE_INTERLACING */
+ {
+ const unsigned int row_info_flags = base_info_flags |
+ (png_ptr->row_number ==
+ first_row_in_pass ? png_pass_first_row : 0) |
+ (png_ptr->row_number == last_row_in_pass ? png_pass_last_row : 0);
+ const int flush = row_flush(ps, row_info_flags);
+ png_const_bytep row = *rows;
+ png_uint_32 pixels_to_go = pixels_in_pass;
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* The filter can change each time round. Call png_write_start_row
+ * to resolve any changes. Note that when this function is used to
+ * do filter selection from png_write_png_data on the first row
+ * png_write_start_row will get called twice.
+ */
+ const png_byte filter = select_filter(ps, row, previous_row, bpp,
+ pixels_in_pass, png_ptr->row_number == first_row_in_pass);
+ const png_uint_32 max_pixels = filter == PNG_FILTER_VALUE_NONE ?
+ ps->zlib_max_pixels : ps->row_buffer_max_pixels;
+ const png_uint_32 block_pixels = filter == PNG_FILTER_VALUE_NONE ?
+ ps->zlib_max_aligned_pixels : ps->row_buffer_max_aligned_pixels;
+
+ /* The row handling uses png_compress_IDAT directly if there is no
+ * filter to be applied, otherwise it uses filter_row.
+ */
+ if (filter != PNG_FILTER_VALUE_NONE)
+ {
+ int start_of_row = 1;
+ png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4x2-bytes each */
+
+ memset(prev_pixels, 0U, sizeof prev_pixels);
+
+ while (pixels_to_go > max_pixels)
+ {
+ /* Write a block at once to maintain alignment */
+ filter_row(png_ptr, previous_row, prev_pixels, row,
+ bpp * block_pixels, bpp, filter, start_of_row,
+ Z_NO_FLUSH);
+
+ if (previous_row != NULL)
+ previous_row += (block_pixels * bpp) >> 3;
+
+ row += (block_pixels * bpp) >> 3;
+ pixels_to_go -= block_pixels;
+ start_of_row = 0;
+ }
+
+ /* The filter code handles the partial byte at the end correctly,
+ * so this is all that is required:
+ */
+ if (pixels_to_go > 0)
+ filter_row(png_ptr, previous_row, prev_pixels, row,
+ bpp * pixels_to_go, bpp, filter, start_of_row, flush);
+ }
+
+ else
+# endif /* WRITE_FILTER */
+
+ {
+ /* The no-filter case. */
+ const uInt block_bytes = (uInt)/*SAFE*/(
+ bpp <= 8U ?
+ block_pixels >> PNG_SHIFTOF(bpp) :
+ block_pixels * (bpp >> 3));
+
+ /* png_write_start_IDAT guarantees this, but double check for
+ * overflow above in debug:
+ */
+ debug((block_bytes & (PNG_ROW_BUFFER_BYTE_ALIGN-1U)) == 0U);
+
+ /* The filter has to be written here: */
+ png_compress_IDAT(png_ptr, &filter, 1U/*len*/, Z_NO_FLUSH);
+
+ /* Process blocks of pixels up to the limit. */
+ while (pixels_to_go > max_pixels)
+ {
+ png_compress_IDAT(png_ptr, row, block_bytes, Z_NO_FLUSH);
+ row += block_bytes;
+ pixels_to_go -= block_pixels;
+ }
+
+ /* Now compress the remainder; pixels_to_go <= max_pixels so it will
+ * fit in a uInt.
+ */
+ {
+ const png_uint_32 remainder =
+ bpp <= 8U
+ ? (pixels_to_go-pixels_at_end) >> PNG_SHIFTOF(bpp)
+ : (pixels_to_go-pixels_at_end) * (bpp >> 3);
+
+ if (remainder > 0U)
+ png_compress_IDAT(png_ptr, row, remainder,
+ pixels_at_end > 0U ? Z_NO_FLUSH : flush);
+
+ else
+ debug(pixels_at_end > 0U);
+
+ if (pixels_at_end > 0U)
+ {
+ /* There is a final partial byte. This is PNG format so the
+ * left-most bits are the most significant.
+ */
+ const png_byte last = PNG_BYTE(row[remainder] &
+ ~(0xFFU >> (pixels_at_end * bpp)));
+
+ png_compress_IDAT(png_ptr, &last, 1U, flush);
+ }
+ }
+ }
+
+ png_write_end_row(png_ptr, flush);
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ previous_row = *rows;
+# endif /* WRITE_FILTER */
+# undef HANDLE
+ } /* row in pass */
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ else /* row not in pass; just skip it */
+ {
+ if (++png_ptr->row_number >= png_ptr->height)
+ {
+ debug(png_ptr->row_number == png_ptr->height);
+
+ png_ptr->row_number = 0U;
+ png_ptr->pass = 0x7U & (pass+1U);
+ }
+ }
+# endif /* WRITE_INTERLACING */
+
+ ++rows;
+ } /* while num_rows */
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* previous_row must be copied back unless we don't need it because the
+ * next row is the first one in the pass (this relies on png_write_end_row
+ * setting row_number to 0 at the end!)
+ */
+ if (png_ptr->row_number != 0U && previous_row != NULL && SAVE_ROW(ps) &&
+ ps->previous_write_row != previous_row/*all rows skipped*/)
+ {
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ /* We might be able to avoid any copy. */
+ if (ps->current_write_row == previous_row)
+ {
+ png_bytep old = ps->previous_write_row;
+ ps->previous_write_row = ps->current_write_row;
+ ps->current_write_row = old; /* may be NULL */
+ }
+
+ else
+# endif /* SELECT_FILTER */
+
+ if (ps->previous_write_row != NULL)
+ memcpy(ps->previous_write_row, previous_row,
+ png_calc_rowbytes(png_ptr, bpp, pixels_in_pass));
+
+ else
+ ps->previous_write_row = allocate_row(png_ptr, previous_row,
+ png_calc_rowbytes(png_ptr, bpp, pixels_in_pass));
+ }
+# endif /* WRITE_FILTER */
}
-#endif
+#ifdef PNG_WRITE_FILTER_SUPPORTED
/* This filters the row, chooses which filter to use, if it has not already
* been specified by the application, and then writes the row out with the
* chosen filter.
*/
-static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
- png_size_t row_bytes);
-
-#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
-#define PNG_HISHIFT 10
-#define PNG_LOMASK ((png_uint_32)0xffffL)
-#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
-void /* PRIVATE */
-png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
-{
- png_bytep best_row;
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- png_bytep prev_row, row_buf;
- png_uint_32 mins, bpp;
- png_byte filter_to_do = png_ptr->do_filter;
- png_size_t row_bytes = row_info->rowbytes;
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- int num_p_filters = png_ptr->num_prev_filters;
-#endif
-
- png_debug(1, "in png_write_find_filter");
-
-#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS)
- {
- /* These will never be selected so we need not test them. */
- filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH);
- }
-#endif
-
- /* Find out how many bytes offset each pixel is */
- bpp = (row_info->pixel_depth + 7) >> 3;
-
- prev_row = png_ptr->prev_row;
-#endif
- best_row = png_ptr->row_buf;
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- row_buf = best_row;
- mins = PNG_MAXSUM;
-
- /* The prediction method we use is to find which method provides the
- * smallest value when summing the absolute values of the distances
- * from zero, using anything >= 128 as negative numbers. This is known
- * as the "minimum sum of absolute differences" heuristic. Other
- * heuristics are the "weighted minimum sum of absolute differences"
- * (experimental and can in theory improve compression), and the "zlib
- * predictive" method (not implemented yet), which does test compressions
- * of lines using different filter methods, and then chooses the
- * (series of) filter(s) that give minimum compressed data size (VERY
- * computationally expensive).
- *
- * GRR 980525: consider also
- *
- * (1) minimum sum of absolute differences from running average (i.e.,
- * keep running sum of non-absolute differences & count of bytes)
- * [track dispersion, too? restart average if dispersion too large?]
- *
- * (1b) minimum sum of absolute differences from sliding average, probably
- * with window size <= deflate window (usually 32K)
- *
- * (2) minimum sum of squared differences from zero or running average
- * (i.e., ~ root-mean-square approach)
- */
-
-
- /* We don't need to test the 'no filter' case if this is the only filter
- * that has been chosen, as it doesn't actually do anything to the data.
- */
- if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE)
- {
- png_bytep rp;
- png_uint_32 sum = 0;
- png_size_t i;
- int v;
-
- for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
- {
- v = *rp;
- sum += (v < 128) ? v : 256 - v;
- }
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- png_uint_32 sumhi, sumlo;
- int j;
- sumlo = sum & PNG_LOMASK;
- sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
-
- /* Reduce the sum if we match any of the previous rows */
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
- {
- sumlo = (sumlo * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- /* Factor in the cost of this filter (this is here for completeness,
- * but it makes no sense to have a "cost" for the NONE filter, as
- * it has the minimum possible computational cost - none).
- */
- sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
- PNG_COST_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
- PNG_COST_SHIFT;
-
- if (sumhi > PNG_HIMASK)
- sum = PNG_MAXSUM;
-
- else
- sum = (sumhi << PNG_HISHIFT) + sumlo;
- }
-#endif
- mins = sum;
- }
-
- /* Sub filter */
- if (filter_to_do == PNG_FILTER_SUB)
- /* It's the only filter so no testing is needed */
- {
- png_bytep rp, lp, dp;
- png_size_t i;
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
- i++, rp++, dp++)
- {
- *dp = *rp;
- }
-
- for (lp = row_buf + 1; i < row_bytes;
- i++, rp++, lp++, dp++)
- {
- *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
- }
-
- best_row = png_ptr->sub_row;
- }
-
- else if (filter_to_do & PNG_FILTER_SUB)
- {
- png_bytep rp, dp, lp;
- png_uint_32 sum = 0, lmins = mins;
- png_size_t i;
- int v;
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- /* We temporarily increase the "minimum sum" by the factor we
- * would reduce the sum of this filter, so that we can do the
- * early exit comparison without scaling the sum each time.
- */
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 lmhi, lmlo;
- lmlo = lmins & PNG_LOMASK;
- lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
- {
- lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
- PNG_COST_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
- PNG_COST_SHIFT;
-
- if (lmhi > PNG_HIMASK)
- lmins = PNG_MAXSUM;
-
- else
- lmins = (lmhi << PNG_HISHIFT) + lmlo;
- }
-#endif
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
- i++, rp++, dp++)
- {
- v = *dp = *rp;
-
- sum += (v < 128) ? v : 256 - v;
- }
-
- for (lp = row_buf + 1; i < row_bytes;
- i++, rp++, lp++, dp++)
- {
- v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
-
- if (sum > lmins) /* We are already worse, don't continue. */
- break;
- }
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 sumhi, sumlo;
- sumlo = sum & PNG_LOMASK;
- sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
- {
- sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
- PNG_COST_SHIFT;
-
- sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
- PNG_COST_SHIFT;
-
- if (sumhi > PNG_HIMASK)
- sum = PNG_MAXSUM;
-
- else
- sum = (sumhi << PNG_HISHIFT) + sumlo;
- }
-#endif
-
- if (sum < mins)
- {
- mins = sum;
- best_row = png_ptr->sub_row;
- }
- }
-
- /* Up filter */
- if (filter_to_do == PNG_FILTER_UP)
- {
- png_bytep rp, dp, pp;
- png_size_t i;
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
- pp = prev_row + 1; i < row_bytes;
- i++, rp++, pp++, dp++)
- {
- *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
- }
-
- best_row = png_ptr->up_row;
- }
-
- else if (filter_to_do & PNG_FILTER_UP)
- {
- png_bytep rp, dp, pp;
- png_uint_32 sum = 0, lmins = mins;
- png_size_t i;
- int v;
-
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 lmhi, lmlo;
- lmlo = lmins & PNG_LOMASK;
- lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
- {
- lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
- PNG_COST_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
- PNG_COST_SHIFT;
-
- if (lmhi > PNG_HIMASK)
- lmins = PNG_MAXSUM;
-
- else
- lmins = (lmhi << PNG_HISHIFT) + lmlo;
- }
-#endif
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
- pp = prev_row + 1; i < row_bytes; i++)
- {
- v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
-
- if (sum > lmins) /* We are already worse, don't continue. */
- break;
- }
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 sumhi, sumlo;
- sumlo = sum & PNG_LOMASK;
- sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
- {
- sumlo = (sumlo * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
- PNG_COST_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
- PNG_COST_SHIFT;
-
- if (sumhi > PNG_HIMASK)
- sum = PNG_MAXSUM;
-
- else
- sum = (sumhi << PNG_HISHIFT) + sumlo;
- }
-#endif
-
- if (sum < mins)
- {
- mins = sum;
- best_row = png_ptr->up_row;
- }
- }
-
- /* Avg filter */
- if (filter_to_do == PNG_FILTER_AVG)
- {
- png_bytep rp, dp, pp, lp;
- png_uint_32 i;
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
- pp = prev_row + 1; i < bpp; i++)
- {
- *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
- }
-
- for (lp = row_buf + 1; i < row_bytes; i++)
- {
- *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
- & 0xff);
- }
- best_row = png_ptr->avg_row;
- }
-
- else if (filter_to_do & PNG_FILTER_AVG)
- {
- png_bytep rp, dp, pp, lp;
- png_uint_32 sum = 0, lmins = mins;
- png_size_t i;
- int v;
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 lmhi, lmlo;
- lmlo = lmins & PNG_LOMASK;
- lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
- {
- lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
- PNG_COST_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
- PNG_COST_SHIFT;
-
- if (lmhi > PNG_HIMASK)
- lmins = PNG_MAXSUM;
-
- else
- lmins = (lmhi << PNG_HISHIFT) + lmlo;
- }
-#endif
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
- pp = prev_row + 1; i < bpp; i++)
- {
- v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
- }
-
- for (lp = row_buf + 1; i < row_bytes; i++)
- {
- v = *dp++ =
- (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
-
- if (sum > lmins) /* We are already worse, don't continue. */
- break;
- }
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 sumhi, sumlo;
- sumlo = sum & PNG_LOMASK;
- sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
- {
- sumlo = (sumlo * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
- PNG_COST_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
- PNG_COST_SHIFT;
-
- if (sumhi > PNG_HIMASK)
- sum = PNG_MAXSUM;
-
- else
- sum = (sumhi << PNG_HISHIFT) + sumlo;
- }
-#endif
-
- if (sum < mins)
- {
- mins = sum;
- best_row = png_ptr->avg_row;
- }
- }
-
- /* Paeth filter */
- if (filter_to_do == PNG_FILTER_PAETH)
- {
- png_bytep rp, dp, pp, cp, lp;
- png_size_t i;
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
- pp = prev_row + 1; i < bpp; i++)
- {
- *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
- }
-
- for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
- {
- int a, b, c, pa, pb, pc, p;
-
- b = *pp++;
- c = *cp++;
- a = *lp++;
-
- 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
-
- p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
-
- *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
- }
- best_row = png_ptr->paeth_row;
- }
-
- else if (filter_to_do & PNG_FILTER_PAETH)
- {
- png_bytep rp, dp, pp, cp, lp;
- png_uint_32 sum = 0, lmins = mins;
- png_size_t i;
- int v;
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 lmhi, lmlo;
- lmlo = lmins & PNG_LOMASK;
- lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
- {
- lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
- PNG_COST_SHIFT;
-
- lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
- PNG_COST_SHIFT;
-
- if (lmhi > PNG_HIMASK)
- lmins = PNG_MAXSUM;
-
- else
- lmins = (lmhi << PNG_HISHIFT) + lmlo;
- }
-#endif
-
- for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
- pp = prev_row + 1; i < bpp; i++)
- {
- v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
- }
-
- for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
- {
- int a, b, c, pa, pb, pc, p;
-
- b = *pp++;
- c = *cp++;
- a = *lp++;
-
-#ifndef PNG_SLOW_PAETH
- 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
- p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
-#else /* PNG_SLOW_PAETH */
- p = a + b - c;
- pa = abs(p - a);
- pb = abs(p - b);
- pc = abs(p - c);
-
- if (pa <= pb && pa <= pc)
- p = a;
-
- else if (pb <= pc)
- p = b;
-
- else
- p = c;
-#endif /* PNG_SLOW_PAETH */
-
- v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
-
- sum += (v < 128) ? v : 256 - v;
-
- if (sum > lmins) /* We are already worse, don't continue. */
- break;
- }
-
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
- {
- int j;
- png_uint_32 sumhi, sumlo;
- sumlo = sum & PNG_LOMASK;
- sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
-
- for (j = 0; j < num_p_filters; j++)
- {
- if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
- {
- sumlo = (sumlo * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_weights[j]) >>
- PNG_WEIGHT_SHIFT;
- }
- }
-
- sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
- PNG_COST_SHIFT;
-
- sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
- PNG_COST_SHIFT;
-
- if (sumhi > PNG_HIMASK)
- sum = PNG_MAXSUM;
-
- else
- sum = (sumhi << PNG_HISHIFT) + sumlo;
- }
-#endif
-
- if (sum < mins)
- {
- best_row = png_ptr->paeth_row;
- }
- }
-#endif /* PNG_WRITE_FILTER_SUPPORTED */
-
- /* Do the actual writing of the filtered row data from the chosen filter. */
- png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);
-
-#ifdef PNG_WRITE_FILTER_SUPPORTED
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
- /* Save the type of filter we picked this time for future calculations */
- if (png_ptr->num_prev_filters > 0)
- {
- int j;
-
- for (j = 1; j < num_p_filters; j++)
- {
- png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
- }
-
- png_ptr->prev_filters[j] = best_row[0];
- }
-#endif
-#endif /* PNG_WRITE_FILTER_SUPPORTED */
-}
-
-
-/* Do the actual writing of a previously filtered row. */
static void
-png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
- png_size_t full_row_length/*includes filter byte*/)
+write_png_data(png_structrp png_ptr, png_const_bytep prev_row,
+ png_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x,
+ unsigned int row_bits, unsigned int row_info_flags)
+ /* This filters the row appropriately and returns an updated prev_row
+ * (updated for 'x').
+ */
{
- png_debug(1, "in png_write_filtered_row");
+ const png_zlib_statep ps = png_ptr->zlib_state;
+ const unsigned int bpp = png_ptr->row_output_pixel_depth;
+ const int flush = row_flush(ps, row_info_flags);
+ const png_byte filter = ps->filters; /* just one */
- png_debug1(2, "filter = %d", filtered_row[0]);
+ /* These invariants are expected from the caller: */
+ affirm(row_bits <= 8U*PNG_ROW_BUFFER_SIZE);
+ debug(filter < PNG_FILTER_VALUE_LAST/*sic: last+1*/);
- png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
+ /* Now choose the correct filter implementation according to the number of
+ * filters in the filters_to_try list. The prev_row parameter is made
+ * NULL on the first row because it is uninitialized at that point.
+ */
+ if (filter == PNG_FILTER_VALUE_NONE)
+ write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits,
+ x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, flush);
-#ifdef PNG_WRITE_FILTER_SUPPORTED
- /* Swap the current and previous rows */
- if (png_ptr->prev_row != NULL)
- {
- png_bytep tptr;
+ else
+ filter_row(png_ptr,
+ (row_info_flags & png_pass_first_row) ? NULL : prev_row,
+ prev_pixels, unfiltered_row, row_bits, bpp, filter, x == 0, flush);
- tptr = png_ptr->prev_row;
- png_ptr->prev_row = png_ptr->row_buf;
- png_ptr->row_buf = tptr;
- }
-#endif /* PNG_WRITE_FILTER_SUPPORTED */
-
- /* Finish row - updates counters and flushes zlib if last row */
- png_write_finish_row(png_ptr);
-
-#ifdef PNG_WRITE_FLUSH_SUPPORTED
- png_ptr->flush_rows++;
-
- if (png_ptr->flush_dist > 0 &&
- png_ptr->flush_rows >= png_ptr->flush_dist)
- {
- png_write_flush(png_ptr);
- }
-#endif
+ /* Handle end of row: */
+ if ((row_info_flags & png_row_end) != 0)
+ png_write_end_row(png_ptr, flush);
}
-#endif /* PNG_WRITE_SUPPORTED */
+
+void /* PRIVATE */
+png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels,
+ png_const_bytep unfiltered_row, png_uint_32 x,
+ unsigned int width/*pixels*/, unsigned int row_info_flags)
+{
+ const png_zlib_statep ps = png_ptr->zlib_state;
+
+ affirm(ps != NULL);
+
+ {
+ const unsigned int bpp = png_ptr->row_output_pixel_depth;
+ const unsigned int row_bits = width * bpp;
+ png_bytep prev_row = ps->previous_write_row;
+
+ debug(bpp <= 64U && width <= 65535U &&
+ width < 65535U/bpp); /* Expensive: only matters on 16-bit */
+
+ /* This is called once before starting a new row here, but below it is
+ * only called once between starting a new list of rows.
+ */
+ if (x == 0)
+ png_write_start_row(ps, (row_info_flags & png_pass_first_row) != 0,
+ prev_row == NULL);
+
+ /* If filter selection is required the filter will have at least one mask
+ * bit set.
+ */
+# ifdef PNG_SELECT_FILTER_SUPPORTED
+ if (ps->filters >= PNG_FILTER_NONE/*lowest mask bit*/)
+ {
+ /* If the entire row is passed in the input process it via
+ * immediately, otherwise the row must be buffered for later
+ * analysis.
+ */
+ png_const_bytep row;
+
+ if (x > 0 || (row_info_flags & png_row_end) == 0)
+ {
+ /* The row must be saved for later. */
+ png_bytep buffer = ps->current_write_row;
+
+ /* png_write_start row should always check this: */
+ debug(ps->write_row_size > 0U);
+
+ if (buffer != NULL)
+ memcpy(buffer + png_calc_rowbytes(png_ptr, bpp, x),
+ unfiltered_row, (row_bits + 7U) >> 3);
+
+
+ else if (x == 0U)
+ ps->current_write_row = buffer = allocate_row(png_ptr,
+ unfiltered_row, (row_bits + 7U) >> 3);
+
+ row = buffer;
+ }
+
+ else
+ row = unfiltered_row;
+
+ if (row != NULL) /* else out of memory */
+ {
+ /* At row end, process the save buffer. */
+ if ((row_info_flags & png_row_end) != 0)
+ png_write_png_rows(png_ptr, &row, 1U);
+
+ /* Early return to skip the single-filter code */
+ return;
+ }
+
+ /* Caching the row failed, so process the row using the lowest set
+ * filter. The allocation error should only ever happen at the
+ * start of the row. If this goes wrong the output will have been
+ * damaged.
+ */
+ affirm(x == 0U);
+ }
+# endif /* SELECT_FILTER */
+
+ /* prev_row is either NULL or the position in the previous row buffer */
+ if (prev_row != NULL && x > 0)
+ prev_row += png_calc_rowbytes(png_ptr, bpp, x);
+
+ /* This is the single filter case (no selection): */
+ write_png_data(png_ptr, prev_row, prev_pixels, unfiltered_row, x,
+ row_bits, row_info_flags);
+
+ /* Copy the current row into the previous row buffer, if available, unless
+ * this is the last row in the pass, when there is no point. Note that
+ * write_previous_row may have garbage in a partial byte at the end as a
+ * result of this memcpy.
+ */
+ if (!(row_info_flags & png_pass_last_row) && SAVE_ROW(ps)) {
+ if (prev_row != NULL)
+ memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3);
+
+ /* NOTE: if the application sets png_zlib_state::save_row in a callback
+ * it isn't possible to do the save until the next row. allocate_row
+ * handles OOM silently by turning off the save.
+ */
+ else if (x == 0) /* can allocate the save buffer */
+ ps->previous_write_row =
+ allocate_row(png_ptr, unfiltered_row, (row_bits + 7U) >> 3);
+ }
+ }
+}
+#else /* !WRITE_FILTER */
+void /* PRIVATE */
+png_write_start_IDAT(png_structrp png_ptr)
+{
+ png_zlib_statep ps = get_zlib_state(png_ptr);
+
+ /* Set up the IDAT compression state. Expect the state to have been released
+ * by the previous owner, but it doesn't much matter if there was an error.
+ * Note that the stream is not claimed yet.
+ */
+ debug(png_ptr->zowner == 0U);
+
+ /* This sets the buffer limits and write_row_size, which is used below. */
+ png_zlib_state_set_buffer_limits(png_ptr, ps);
+}
+
+void /* PRIVATE */
+png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels,
+ png_const_bytep unfiltered_row, png_uint_32 x,
+ unsigned int width/*pixels*/, unsigned int row_info_flags)
+{
+ const unsigned int bpp = png_ptr->row_output_pixel_depth;
+ int flush;
+ png_uint_32 row_bits;
+
+ row_bits = width;
+ row_bits *= bpp;
+ /* These invariants are expected from the caller: */
+ affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp &&
+ row_bits <= 8U*PNG_ROW_BUFFER_SIZE);
+
+ affirm(png_ptr->zlib_state != NULL);
+ flush = row_flush(png_ptr->zlib_state, row_info_flags);
+
+ write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits,
+ x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, flush);
+
+ PNG_UNUSED(prev_pixels)
+
+ /* Handle end of row: */
+ if ((row_info_flags & png_row_end) != 0)
+ png_write_end_row(png_ptr, flush);
+}
+#endif /* !WRITE_FILTER */
+
+png_int_32 /* PRIVATE */
+png_write_setting(png_structrp png_ptr, png_uint_32 setting,
+ png_uint_32 parameter, png_int_32 value)
+{
+ /* Caller checks the arguments for basic validity */
+ int only_get = (setting & PNG_SF_GET) != 0U;
+
+ setting &= ~PNG_SF_GET;
+
+ switch (setting)
+ {
+ /* Settings in png_struct: */
+ case PNG_SW_IDAT_size:
+ if (parameter > 0 && parameter <= PNG_UINT_31_MAX)
+ {
+ if (!only_get)
+ png_ptr->IDAT_size = parameter;
+
+ return 0; /* set ok */
+ }
+
+ else
+ return PNG_EINVAL;
+
+ /* Settings in zlib_state: */
+ case PNG_SW_COMPRESS_png_level:
+ return compression_setting(png_ptr, parameter, png_level, value,
+ only_get);
+
+# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+ case PNG_SW_COMPRESS_zlib_level:
+ return compression_setting(png_ptr, parameter, level, value,
+ only_get);
+
+ case PNG_SW_COMPRESS_windowBits:
+ return compression_setting(png_ptr, parameter, windowBits, value,
+ only_get);
+
+ case PNG_SW_COMPRESS_memLevel:
+ return compression_setting(png_ptr, parameter, memLevel, value,
+ only_get);
+
+ case PNG_SW_COMPRESS_strategy:
+ return compression_setting(png_ptr, parameter, strategy, value,
+ only_get);
+
+ case PNG_SW_COMPRESS_method:
+ if (value != 8) /* Only supported method */
+ return PNG_EINVAL;
+ return 8; /* old method */
+# endif /* WRITE_CUSTOMIZE_COMPRESSION */
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ case PNG_SW_COMPRESS_filters:
+ /* The method must match that in the IHDR: */
+ if (parameter == png_ptr->filter_method)
+ {
+ if (!only_get)
+ return set_filter(get_zlib_state(png_ptr), value);
+
+ else if (png_ptr->zlib_state != NULL &&
+ png_ptr->zlib_state->filter_mask != 0U/*unset*/)
+ return png_ptr->zlib_state->filter_mask;
+
+ else
+ return PNG_UNSET;
+ }
+
+ else /* Invalid filter method */
+ return PNG_EINVAL;
+
+ case PNG_SW_COMPRESS_row_buffers:
+ /* New in 1.7.0: direct control of the buffering. */
+ switch (parameter)
+ {
+ case 0:
+ if (!only_get)
+ get_zlib_state(png_ptr)->save_row = SAVE_ROW_OFF;
+ return 0;
+
+ case 1:
+ if (!only_get)
+ get_zlib_state(png_ptr)->save_row = SAVE_ROW_ON;
+ return 1;
+
+ default:
+ return PNG_ENOSYS; /* no support for bigger values */
+ }
+# endif /* WRITE_FILTER */
+
+# ifdef PNG_WRITE_FLUSH_SUPPORTED
+ case PNG_SW_FLUSH:
+ /* Set the automatic flush interval or 0 to turn flushing off */
+ if (!only_get)
+ get_zlib_state(png_ptr)->flush_dist =
+ value <= 0 ? 0xEFFFFFFFU : (png_uint_32)/*SAFE*/value;
+
+ return 0;
+# endif /* WRITE_FLUSH */
+
+# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+ case PNG_SRW_CHECK_FOR_INVALID_INDEX:
+ /* The 'enabled' value is a FORTRAN style three-state: */
+ if (value > 0)
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON;
+
+ else if (value < 0)
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF;
+
+ else
+ png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT;
+
+ return 0;
+# endif /* WRITE_CHECK_FOR_INVALID_INDEX */
+
+# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
+ case PNG_SRW_ERROR_HANDLING:
+ /* The parameter is a bit mask of what to set, the value is what to
+ * set it to. PNG_IDAT_ERRORS is ignored on write.
+ */
+ if (value >= PNG_IGNORE && value <= PNG_ERROR &&
+ parameter <= PNG_ALL_ERRORS)
+ {
+ if ((parameter & PNG_BENIGN_ERRORS) != 0U)
+ png_ptr->benign_error_action = value & 0x3U;
+
+ if ((parameter & PNG_APP_WARNINGS) != 0U)
+ png_ptr->app_warning_action = value & 0x3U;
+
+ if ((parameter & PNG_APP_ERRORS) != 0U)
+ png_ptr->app_error_action = value & 0x3U;
+
+ return 0;
+ }
+
+ return PNG_EINVAL;
+# endif /* BENIGN_WRITE_ERRORS */
+
+ default:
+ return PNG_ENOSYS; /* not supported (whatever it is) */
+ }
+}
+#endif /* WRITE */
diff --git a/ext/libzstd.vcxproj b/ext/libzstd.vcxproj
index 268bfde84a..7cd642f51c 100644
--- a/ext/libzstd.vcxproj
+++ b/ext/libzstd.vcxproj
@@ -60,9 +60,6 @@
-
-
-
@@ -80,12 +77,11 @@
-
+
-
diff --git a/ext/zstd b/ext/zstd
index 56421f34e4..a488ba114e 160000
--- a/ext/zstd
+++ b/ext/zstd
@@ -1 +1 @@
-Subproject commit 56421f34e4ab5c17cbac0832bd12413e4f27ecf9
+Subproject commit a488ba114ec17ea1054b9057c26a046fc122b3b6
diff --git a/libretro/Makefile.common b/libretro/Makefile.common
index 64c99664c6..704993a645 100644
--- a/libretro/Makefile.common
+++ b/libretro/Makefile.common
@@ -786,7 +786,6 @@ SOURCES_C += $(ZSTDDIR)/common/debug.c \
SOURCES_C += \
$(ZSTDDIR)/common/zstd_common.c \
- $(ZSTDDIR)/common/zstd_trace.c \
$(ZSTDDIR)/compress/fse_compress.c \
$(ZSTDDIR)/compress/hist.c \
$(ZSTDDIR)/compress/huf_compress.c \