diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b3a542688d..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 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 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 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 */