diff options
Diffstat (limited to 'Build/source/libs/libpng/libpng-src/contrib')
9 files changed, 2601 insertions, 39 deletions
diff --git a/Build/source/libs/libpng/libpng-src/contrib/conftest/pngcp.dfa b/Build/source/libs/libpng/libpng-src/contrib/conftest/pngcp.dfa new file mode 100644 index 00000000000..15a856e2fb9 --- /dev/null +++ b/Build/source/libs/libpng/libpng-src/contrib/conftest/pngcp.dfa @@ -0,0 +1,57 @@ +# pngcp.dfa +# Build time configuration of libpng +# +# Author: John Bowler +# Copyright: (c) John Bowler, 2016 +# Usage rights: +# To the extent possible under law, the author has waived all copyright and +# related or neighboring rights to this work. This work is published from: +# United States. +# +# Build libpng with support for pngcp. This means just png_read_png, +# png_write_png and small number of configuration settings. +# +everything = off + +# This option is specific to this configuration; it adds a #define to the +# generated pnglibconf.h which turns on the (not portable) timing option for +# pngcp. Note that any option is automatically preceded by PNG_; there is no +# way round this and this is deliberate. +option PNGCP_TIMING + +# Because of the everything off above the option must also be turned on. This +# may not be done in one step because it is safer and avoids mis-spelled options +# in user .dfa files to error out if an unrecognized option is turned on. +option PNGCP_TIMING on + +# Options to turn on png_read_png and png_write_png: +option INFO_IMAGE on +option SEQUENTIAL_READ on +option EASY_ACCESS on +option WRITE on +option WRITE_16BIT on +option WRITE_FILTER on + +# pngcp needs this to preserve unknown chunks, switching all these on means that +# pngcp can work without explicit known chunk reading suppport +option UNKNOWN_CHUNKS on +option SET_UNKNOWN_CHUNKS on +option HANDLE_AS_UNKNOWN on +option SAVE_UNKNOWN_CHUNKS on +option WRITE_UNKNOWN_CHUNKS on + +# pngcp needs this to handle palette files with invalid indices: +option CHECK_FOR_INVALID_INDEX on +option GET_PALETTE_MAX on + +# Pre-libpng 1.7 pngcp has to stash text chunks manually, post 1.7 without this +# text chunks should be handled as unknown ok. +option TEXT on + +# this is used to turn off limits: +option USER_LIMITS on +option SET_USER_LIMITS on + +# these are are just required for specific customizations +option WRITE_CUSTOMIZE_ZTXT_COMPRESSION on +option WRITE_CUSTOMIZE_COMPRESSION on diff --git a/Build/source/libs/libpng/libpng-src/contrib/intel/filter_sse2_intrinsics.c b/Build/source/libs/libpng/libpng-src/contrib/intel/filter_sse2_intrinsics.c index aea3f86af5a..b02840c5544 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/intel/filter_sse2_intrinsics.c +++ b/Build/source/libs/libpng/libpng-src/contrib/intel/filter_sse2_intrinsics.c @@ -6,7 +6,7 @@ * Derived from arm/filter_neon_intrinsics.c, which was * Copyright (c) 2014,2016 Glenn Randers-Pehrson * - * Last changed in libpng 1.6.22 [May 26, 2016] + * Last changed in libpng 1.6.24 [August 4, 2016] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -208,7 +208,7 @@ static __m128i abs_i16(__m128i x) { x = _mm_xor_si128(x, is_negative); /* +1 to negative lanes, else +0. */ - x = _mm_add_epi16(x, _mm_srli_epi16(is_negative, 15)); + x = _mm_sub_epi16(x, is_negative); return x; #endif } diff --git a/Build/source/libs/libpng/libpng-src/contrib/intel/intel_sse.patch b/Build/source/libs/libpng/libpng-src/contrib/intel/intel_sse.patch index f000c11a9d0..c5579e3abd8 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/intel/intel_sse.patch +++ b/Build/source/libs/libpng/libpng-src/contrib/intel/intel_sse.patch @@ -55,7 +55,7 @@ diff --git a/configure.ac b/configure.ac diff --git a/Makefile.am b/Makefile.am --- a/Makefile.am 2016-05-17 18:15:12.000000000 -0400 +++ b/Makefile.am 2016-05-25 19:48:10.631751170 -0400 -@@ -89,16 +89,20 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SO +@@ -92,16 +92,20 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SO pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c pngwutil.c\ png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h pngusr.dfa @@ -77,8 +77,8 @@ diff --git a/Makefile.am b/Makefile.am # Versioned symbols and restricted exports if HAVE_SOLARIS_LD diff --git a/pngpriv.h b/pngpriv.h ---- a/pngpriv.h 2016-05-31 09:20:34.442885047 -0500 -+++ b/pngpriv.h 2016-05-31 09:14:54.583492341 -0500 +--- a/pngpriv.h 2016-08-01 18:13:38.770128810 -0500 ++++ b/pngpriv.h 2016-08-01 18:50:19.130179017 -0500 @@ -177,16 +177,52 @@ # endif /* !PNG_ARM_NEON_IMPLEMENTATION */ @@ -132,7 +132,7 @@ diff --git a/pngpriv.h b/pngpriv.h * PNG_BUILD_DLL must not be set. To avoid the code below kicking in build a * static library of libpng then link the DLL against that. */ -@@ -1184,16 +1220,29 @@ PNG_INTERNAL_FUNCTION(void,png_read_filt +@@ -1185,16 +1221,31 @@ PNG_INTERNAL_FUNCTION(void,png_read_filt 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); @@ -140,7 +140,9 @@ diff --git a/pngpriv.h b/pngpriv.h 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); - + #endif ++ ++#if PNG_INTEL_SSE_IMPLEMENTATION > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop @@ -153,7 +155,8 @@ diff --git a/pngpriv.h b/pngpriv.h + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -+ ++#endif + /* 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); @@ -161,18 +164,19 @@ diff --git a/pngpriv.h b/pngpriv.h #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 -@@ -1910,16 +1959,18 @@ PNG_INTERNAL_FUNCTION(void, PNG_FILTER_O - /* Just declare the optimization that will be used */ - #else +@@ -1914,16 +1965,20 @@ PNG_INTERNAL_FUNCTION(void, PNG_FILTER_O /* List *all* the possible optimizations here - this branch is required if * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing. */ + # if PNG_ARM_NEON_OPT > 0 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); + # endif ++# if PNG_INTEL_SSE_IMPLEMENTATION > 0 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); ++# endif #endif PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr, diff --git a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c index 2ebe553f6d5..9ae402cc4bb 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c +++ b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngimage.c @@ -2,7 +2,7 @@ * * Copyright (c) 2015,2016 John Cunningham Bowler * - * Last changed in libpng 1.6.22 [May 26, 2016] + * Last changed in libpng 1.6.24 [August 4, 2016] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -45,8 +45,19 @@ # define SKIP 0 #endif -#if defined(PNG_INFO_IMAGE_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)\ - && (defined(PNG_READ_PNG_SUPPORTED) || PNG_LIBPNG_VER < 10700) +#if PNG_LIBPNG_VER < 10700 + /* READ_PNG and WRITE_PNG were not defined, so: */ +# ifdef PNG_INFO_IMAGE_SUPPORTED +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED +# define PNG_READ_PNG_SUPPORTED +# endif /* SEQUENTIAL_READ */ +# ifdef PNG_WRITE_SUPPORTED +# define PNG_WRITE_PNG_SUPPORTED +# endif /* WRITE */ +# endif /* INFO_IMAGE */ +#endif /* pre 1.7.0 */ + +#ifdef PNG_READ_PNG_SUPPORTED /* If a transform is valid on both read and write this implies that if the * transform is applied to read it must also be applied on write to produce * meaningful data. This is because these transforms when performed on read @@ -395,7 +406,7 @@ buffer_destroy(struct buffer *buffer) buffer_destroy_list(list); } -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED static void buffer_start_write(struct buffer *buffer) { @@ -565,7 +576,7 @@ struct display png_structp read_pp; png_infop read_ip; -# ifdef PNG_WRITE_SUPPORTED +# ifdef PNG_WRITE_PNG_SUPPORTED /* Used to write a new image (the original info_ptr is used) */ png_structp write_pp; struct buffer written_file; /* where the file gets written */ @@ -592,7 +603,7 @@ display_init(struct display *dp) dp->read_ip = NULL; buffer_init(&dp->original_file); -# ifdef PNG_WRITE_SUPPORTED +# ifdef PNG_WRITE_PNG_SUPPORTED dp->write_pp = NULL; buffer_init(&dp->written_file); # endif @@ -605,7 +616,7 @@ display_clean_read(struct display *dp) png_destroy_read_struct(&dp->read_pp, &dp->read_ip, NULL); } -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED static void display_clean_write(struct display *dp) { @@ -617,7 +628,7 @@ display_clean_write(struct display *dp) static void display_clean(struct display *dp) { -# ifdef PNG_WRITE_SUPPORTED +# ifdef PNG_WRITE_PNG_SUPPORTED display_clean_write(dp); # endif display_clean_read(dp); @@ -635,7 +646,7 @@ static void display_destroy(struct display *dp) { /* Release any memory held in the display. */ -# ifdef PNG_WRITE_SUPPORTED +# ifdef PNG_WRITE_PNG_SUPPORTED buffer_destroy(&dp->written_file); # endif @@ -1082,6 +1093,7 @@ compare_read(struct display *dp, int applied_transforms) } else +# ifdef PNG_sBIT_SUPPORTED { unsigned long y; int bpp; /* bits-per-pixel then bytes-per-pixel */ @@ -1243,12 +1255,16 @@ compare_read(struct display *dp, int applied_transforms) } } /* for y */ } +# else /* !sBIT */ + display_log(dp, INTERNAL_ERROR, + "active shift transform but no sBIT support"); +# endif /* !sBIT */ } return 1; /* compare succeeded */ } -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED static void buffer_write(struct display *dp, struct buffer *buffer, png_bytep data, png_size_t size) @@ -1347,7 +1363,7 @@ write_png(struct display *dp, png_infop ip, int transforms) */ display_clean_write(dp); } -#endif /* WRITE_SUPPORTED */ +#endif /* WRITE_PNG */ static int skip_transform(struct display *dp, int tr) @@ -1409,7 +1425,7 @@ test_one_file(struct display *dp, const char *filename) return; /* no point testing more */ } -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED /* Second test: write the original PNG data out to a new file (to test the * write side) then read the result back in and make sure that it hasn't * changed. @@ -1450,7 +1466,7 @@ test_one_file(struct display *dp, const char *filename) * out and read it back in again (without the reversible transforms) * we should get back to the place where we started. */ -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED if ((current & write_transforms) == current) { /* All transforms reversible: write the PNG with the transformations @@ -1686,7 +1702,7 @@ main(const int argc, const char * const * const argv) return errors != 0; } } -#else /* !INFO_IMAGE || !SEQUENTIAL_READ || !READ_PNG*/ +#else /* !READ_PNG */ int main(void) { diff --git a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngstest.c b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngstest.c index dffd7c12ae3..30f57a9666f 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngstest.c +++ b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngstest.c @@ -3,7 +3,7 @@ * * Copyright (c) 2013-2016 John Cunningham Bowler * - * Last changed in libpng 1.6.22 [May 26, 2016] + * Last changed in libpng 1.6.24 [August 4, 2016] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -26,15 +26,6 @@ # include <config.h> #endif -/* 1.6.1 added support for the configure test harness, which uses 77 to indicate - * a skipped test, in earlier versions we need to succeed on a skipped test, so: - */ -#if PNG_LIBPNG_VER >= 10601 && defined(HAVE_CONFIG_H) -# define SKIP 77 -#else -# define SKIP 0 -#endif - /* Define the following to use this test against your installed libpng, rather * than the one being built here: */ @@ -44,6 +35,15 @@ # include "../../png.h" #endif +/* 1.6.1 added support for the configure test harness, which uses 77 to indicate + * a skipped test, in earlier versions we need to succeed on a skipped test, so: + */ +#if PNG_LIBPNG_VER >= 10601 && defined(HAVE_CONFIG_H) +# define SKIP 77 +#else +# define SKIP 0 +#endif + #ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* Else nothing can be done */ #include "../tools/sRGB.h" diff --git a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngvalid.c b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngvalid.c index 7a42d1350ed..9d3beb5fa5b 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/libtests/pngvalid.c +++ b/Build/source/libs/libpng/libpng-src/contrib/libtests/pngvalid.c @@ -1,7 +1,7 @@ /* pngvalid.c - validate libpng by constructing then reading png files. * - * Last changed in libpng 1.6.23 [June 9, 2016] + * Last changed in libpng 1.6.24 [August 4, 2016] * Copyright (c) 2014-2016 Glenn Randers-Pehrson * Written by John Cunningham Bowler * @@ -1115,7 +1115,7 @@ store_warning(png_structp ppIn, png_const_charp message) if (!ps->expect_warning) store_log(ps, pp, message, 0 /* warning */); else - ps->saw_warning = 1; + ps->saw_warning = 1; } /* These somewhat odd functions are used when reading an image to ensure that @@ -1589,7 +1589,7 @@ store_read_chunk(png_store *ps, png_bytep pb, const png_size_t max, { if (chunkpos < chunklen-4U) { - uInt avail = -1; + uInt avail = (uInt)-1; if (avail > (IDAT_len-4U) - IDAT_pos) avail = (uInt)/*SAFE*/((IDAT_len-4U) - IDAT_pos); diff --git a/Build/source/libs/libpng/libpng-src/contrib/tools/chkfmt b/Build/source/libs/libpng/libpng-src/contrib/tools/chkfmt index 9da6475fd4e..a1b88ccbff5 100644 --- a/Build/source/libs/libpng/libpng-src/contrib/tools/chkfmt +++ b/Build/source/libs/libpng/libpng-src/contrib/tools/chkfmt @@ -1,4 +1,11 @@ #!/bin/sh + +# chkfmt +# +# COPYRIGHT: Written by John Cunningham Bowler, 2010. +# To the extent possible under law, the author has waived all copyright and +# related or neighboring rights to this work. This work is published from: +# United States. # # Check the format of the source files in the current directory - checks for a # line length of 80 characters max and no tab characters. diff --git a/Build/source/libs/libpng/libpng-src/contrib/tools/pngcp.c b/Build/source/libs/libpng/libpng-src/contrib/tools/pngcp.c new file mode 100644 index 00000000000..e82dddccc78 --- /dev/null +++ b/Build/source/libs/libpng/libpng-src/contrib/tools/pngcp.c @@ -0,0 +1,2453 @@ +/* pngcp.c + * + * Copyright (c) 2016 John Cunningham Bowler + * + * Last changed in libpng 1.6.24 [August 4, 2016] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This is an example of copying a PNG without changes using the png_read_png + * and png_write_png interfaces. A considerable number of options are provided + * to manipulate the compression of the PNG data and other compressed chunks. + * + * For a more extensive example that uses the transforms see + * contrib/libtests/pngimage.c in the libpng distribution. + */ +#include "pnglibconf.h" /* To find how libpng was configured. */ + +#ifdef PNG_PNGCP_TIMING_SUPPORTED + /* WARNING: + * + * This test is here to allow POSIX.1b extensions to be used if enabled in + * the compile; specifically the code requires_POSIX_C_SOURCE support of + * 199309L or later to enable clock_gettime use. + * + * IF this causes problems THEN compile with a strict ANSI C compiler and let + * this code turn on the POSIX features that it minimally requires. + * + * IF this does not work there is probably a bug in your ANSI C compiler or + * your POSIX implementation. + */ +# define _POSIX_C_SOURCE 199309L +#else /* No timing support required */ +# define _POSIX_SOURCE 1 +#endif + +#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> + +/* Define the following to use this test against your installed libpng, rather + * than the one being built here: + */ +#ifdef PNG_FREESTANDING_TESTS +# include <png.h> +#else +# include "../../png.h" +#endif + +#if PNG_LIBPNG_VER < 10700 + /* READ_PNG and WRITE_PNG were not defined, so: */ +# ifdef PNG_INFO_IMAGE_SUPPORTED +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED +# define PNG_READ_PNG_SUPPORTED +# endif /* SEQUENTIAL_READ */ +# ifdef PNG_WRITE_SUPPORTED +# define PNG_WRITE_PNG_SUPPORTED +# endif /* WRITE */ +# endif /* INFO_IMAGE */ +#endif /* pre 1.7.0 */ + +#if (defined(PNG_READ_PNG_SUPPORTED)) && (defined(PNG_WRITE_PNG_SUPPORTED)) +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <assert.h> + +#include <unistd.h> +#include <sys/stat.h> + +#include <zlib.h> + +#ifndef PNG_SETJMP_SUPPORTED +# include <setjmp.h> /* because png.h did *not* include this */ +#endif + +#ifdef __cplusplus +# define voidcast(type, value) static_cast<type>(value) +#else +# define voidcast(type, value) (value) +#endif /* __cplusplus */ + +#ifdef __GNUC__ + /* Many versions of GCC erroneously report that local variables unmodified + * within the scope of a setjmp may be clobbered. This hacks round the + * problem (sometimes) without harming other compilers. + */ +# define gv volatile +#else +# define gv +#endif + +/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It + * need not be supported even when clock_gettime is available. It returns the + * 'CPU' time the process has consumed. 'CPU' time is assumed to include time + * when the CPU is actually blocked by a pending cache fill but not time + * waiting for page faults. The attempt is to get a measure of the actual time + * the implementation takes to read a PNG ignoring the potentially very large IO + * overhead. + */ +#ifdef PNG_PNGCP_TIMING_SUPPORTED +# include <time.h> /* clock_gettime and associated definitions */ +# ifndef CLOCK_PROCESS_CPUTIME_ID + /* Prevent inclusion of the spurious code: */ +# undef PNG_PNGCP_TIMING_SUPPORTED +# endif +#endif /* PNGCP_TIMING */ + +/* So if the timing feature has been activated: */ + +/* This structure is used to control the test of a single file. */ +typedef enum +{ + VERBOSE, /* switches on all messages */ + INFORMATION, + WARNINGS, /* switches on warnings */ + LIBPNG_WARNING, + APP_WARNING, + ERRORS, /* just errors */ + APP_FAIL, /* continuable error - no need to longjmp */ + LIBPNG_ERROR, /* this and higher cause a longjmp */ + LIBPNG_BUG, /* erroneous behavior in libpng */ + APP_ERROR, /* such as out-of-memory in a callback */ + QUIET, /* no normal messages */ + USER_ERROR, /* such as file-not-found */ + INTERNAL_ERROR +} error_level; +#define LEVEL_MASK 0xf /* where the level is in 'options' */ + +#define STRICT 0x010 /* Fail on warnings as well as errors */ +#define LOG 0x020 /* Log pass/fail to stdout */ +#define CONTINUE 0x040 /* Continue on APP_FAIL errors */ +#define SIZES 0x080 /* Report input and output sizes */ +#define SEARCH 0x100 /* Search IDAT compression options */ +#define NOWRITE 0x200 /* Do not write an output file */ +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +# define IGNORE_INDEX 0x400 /* Ignore out of range palette indices (BAD!) */ +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +# define FIX_INDEX 0x800 /* 'Fix' out of range palette indices (OK) */ +# endif /* GET_PALETTE_MAX */ +#endif /* CHECK_FOR_INVALID_INDEX */ +#define OPTION 0x80000000 /* Used for handling options */ +#define LIST 0x80000001 /* Used for handling options */ + +/* Result masks apply to the result bits in the 'results' field below; these + * bits are simple 1U<<error_level. A pass requires either nothing worse than + * warnings (--relaxes) or nothing worse than information (--strict) + */ +#define RESULT_STRICT(r) (((r) & ~((1U<<WARNINGS)-1)) == 0) +#define RESULT_RELAXED(r) (((r) & ~((1U<<ERRORS)-1)) == 0) + +/* OPTION DEFINITIONS */ +static const char range_lo[] = "low"; +static const char range_hi[] = "high"; +static const char all[] = "all"; +#define RANGE(lo,hi) { range_lo, lo }, { range_hi, hi } +typedef struct value_list +{ + const char *name; /* the command line name of the value */ + int value; /* the actual value to use */ +} value_list; + +static const value_list +#ifdef PNG_SW_COMPRESS_png_level +vl_compression[] = +{ + /* Overall compression control. The order controls the search order for + * 'all'. Since the search is for the smallest the order used is low memory + * then high speed. + */ + { "low-memory", PNG_COMPRESSION_LOW_MEMORY }, + { "high-speed", PNG_COMPRESSION_HIGH_SPEED }, + { "high-read-speed", PNG_COMPRESSION_HIGH_READ_SPEED }, + { "low", PNG_COMPRESSION_LOW }, + { "medium", PNG_COMPRESSION_MEDIUM }, + { "old", PNG_COMPRESSION_COMPAT }, + { "high", PNG_COMPRESSION_HIGH }, + { all, 0 } +}, +#endif /* SW_COMPRESS_png_level */ + +#if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\ + defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED) +vl_strategy[] = +{ + /* This controls the order of search. */ + { "huffman", Z_HUFFMAN_ONLY }, + { "RLE", Z_RLE }, + { "fixed", Z_FIXED }, /* the remainder do window searchs */ + { "filtered", Z_FILTERED }, + { "default", Z_DEFAULT_STRATEGY }, + { all, 0 } +}, +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +vl_windowBits_text[] = +{ + { "default", MAX_WBITS/*from zlib*/ }, + { "minimum", 8 }, + RANGE(8, MAX_WBITS/*from zlib*/), + { all, 0 } +}, +#endif /* text compression */ +vl_level[] = +{ + { "default", Z_DEFAULT_COMPRESSION /* this is -1 */ }, + { "none", Z_NO_COMPRESSION }, + { "speed", Z_BEST_SPEED }, + { "best", Z_BEST_COMPRESSION }, + { "0", Z_NO_COMPRESSION }, + RANGE(1, 9), /* this deliberately excludes '0' */ + { all, 0 } +}, +vl_memLevel[] = +{ + { "max", MAX_MEM_LEVEL }, /* zlib maximum */ + { "1", 1 }, /* zlib minimum */ + { "default", 8 }, /* zlib default */ + { "2", 2 }, + { "3", 3 }, + { "4", 4 }, + { "5", 5 }, /* for explicit testing */ + RANGE(6, MAX_MEM_LEVEL/*zlib*/), /* exclude 5 and below: zlib bugs */ + { all, 0 } +}, +#endif /* WRITE_CUSTOMIZE_*COMPRESSION */ +#ifdef PNG_WRITE_FILTER_SUPPORTED +vl_filter[] = +{ + { all, PNG_ALL_FILTERS }, + { "off", PNG_NO_FILTERS }, + { "none", PNG_FILTER_NONE }, + { "sub", PNG_FILTER_SUB }, + { "up", PNG_FILTER_UP }, + { "avg", PNG_FILTER_AVG }, + { "paeth", PNG_FILTER_PAETH } +}, +#endif /* WRITE_FILTER */ +#ifdef PNG_PNGCP_TIMING_SUPPORTED +# define PNGCP_TIME_READ 1 +# define PNGCP_TIME_WRITE 2 +vl_time[] = +{ + { "both", PNGCP_TIME_READ+PNGCP_TIME_WRITE }, + { "off", 0 }, + { "read", PNGCP_TIME_READ }, + { "write", PNGCP_TIME_WRITE } +}, +#endif /* PNGCP_TIMING */ +vl_IDAT_size[] = /* for png_set_IDAT_size */ +{ + { "default", 0x7FFFFFFF }, + { "minimal", 1 }, + RANGE(1, 0x7FFFFFFF) +}, +#ifndef PNG_SW_IDAT_size + /* Pre 1.7 API: */ +# define png_set_IDAT_size(p,v) png_set_compression_buffer_size(p, v) +#endif /* !SW_IDAT_size */ +#define SL 8 /* stack limit in display, below */ +vl_log_depth[] = { { "on", 1 }, { "off", 0 }, RANGE(0, SL) }, +vl_on_off[] = { { "on", 1 }, { "off", 0 } }; + +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +static value_list +vl_windowBits_IDAT[] = +{ + { "default", MAX_WBITS }, + { "small", 9 }, + RANGE(8, MAX_WBITS), /* modified by set_windowBits_hi */ + { all, 0 } +}; +#endif /* IDAT compression */ + +typedef struct option +{ + const char *name; /* name of the option */ + png_uint_32 opt; /* an option, or OPTION or LIST */ + png_byte search; /* Search on --search */ + png_byte value_count; /* length of the list of values: */ + const value_list *values; /* values for OPTION or LIST */ +} option; + +static const option options[] = +{ + /* struct display options, these are set when the command line is read */ +# define S(n,v) { #n, v, 0, 2, vl_on_off }, + S(verbose, VERBOSE) + S(warnings, WARNINGS) + S(errors, ERRORS) + S(quiet, QUIET) + S(strict, STRICT) + S(log, LOG) + S(continue, CONTINUE) + S(sizes, SIZES) + S(search, SEARCH) + S(nowrite, NOWRITE) +# ifdef IGNORE_INDEX + S(ignore-palette-index, IGNORE_INDEX) +# endif /* IGNORE_INDEX */ +# ifdef FIX_INDEX + S(fix-palette-index, FIX_INDEX) +# endif /* FIX_INDEX */ +# undef S + + /* OPTION settings, these and LIST settings are read on demand */ +# define VLNAME(name) vl_ ## name +# define VLSIZE(name) voidcast(png_byte,\ + (sizeof VLNAME(name))/(sizeof VLNAME(name)[0])) +# define VL(oname, name, type, search)\ + { oname, type, search, VLSIZE(name), VLNAME(name) }, +# define VLO(oname, name, search) VL(oname, name, OPTION, search) + +# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +# define VLCIDAT(name) VLO(#name, name, 1/*search*/) +# ifdef PNG_SW_COMPRESS_level +# define VLCiCCP(name) VLO("ICC-profile-" #name, name, 0/*search*/) +# else +# define VLCiCCP(name) +# endif +# else +# define VLCIDAT(name) +# define VLCiCCP(name) +# endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +# define VLCzTXt(name) VLO("text-" #name, name, 0/*search*/) +# else +# define VLCzTXt(name) +# endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ + +# define VLC(name) VLCIDAT(name) VLCiCCP(name) VLCzTXt(name) + +# ifdef PNG_SW_COMPRESS_png_level + /* The libpng compression level isn't searched beause it justs sets the + * other things that are searched! + */ + VLO("compression", compression, 0) + VLO("text-compression", compression, 0) + VLO("ICC-profile-compression", compression, 0) +# endif /* SW_COMPRESS_png_level */ + VLC(strategy) + VLO("windowBits", windowBits_IDAT, 1) +# ifdef PNG_SW_COMPRESS_windowBits + VLO("ICC-profile-windowBits", windowBits_text/*sic*/, 0) +# endif + VLO("text-windowBits", windowBits_text, 0) + VLC(level) + VLC(memLevel) + VLO("IDAT-size", IDAT_size, 0) + VLO("log-depth", log_depth, 0) + +# undef VLO + + /* LIST settings */ +# define VLL(name, search) VL(#name, name, LIST, search) +#ifdef PNG_WRITE_FILTER_SUPPORTED + VLL(filter, 0) +#endif /* WRITE_FILTER */ +#ifdef PNG_PNGCP_TIMING_SUPPORTED + VLL(time, 0) +#endif /* PNGCP_TIMING */ +# undef VLL +# undef VL +}; + +#ifdef __cplusplus + static const size_t option_count((sizeof options)/(sizeof options[0])); +#else /* !__cplusplus */ +# define option_count ((sizeof options)/(sizeof options[0])) +#endif /* !__cplusplus */ + +static const char * +cts(int ct) +{ + switch (ct) + { + case PNG_COLOR_TYPE_PALETTE: return "P"; + case PNG_COLOR_TYPE_GRAY: return "G"; + case PNG_COLOR_TYPE_GRAY_ALPHA: return "GA"; + case PNG_COLOR_TYPE_RGB: return "RGB"; + case PNG_COLOR_TYPE_RGB_ALPHA: return "RGBA"; + default: return "INVALID"; + } +} + +struct display +{ + jmp_buf error_return; /* Where to go to on error */ + unsigned int errset; /* error_return is set */ + + const char *operation; /* What is happening */ + const char *filename; /* The name of the original file */ + const char *output_file; /* The name of the output file */ + + /* Used on both read and write: */ + FILE *fp; + + /* Used on a read, both the original read and when validating a written + * image. + */ + png_alloc_size_t read_size; + png_structp read_pp; + png_infop ip; +# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED + png_textp text_ptr; /* stash of text chunks */ + int num_text; + int text_stashed; +# endif /* pre 1.7 */ + +# ifdef PNG_PNGCP_TIMING_SUPPORTED + struct timespec read_time; + struct timespec read_time_total; + struct timespec write_time; + struct timespec write_time_total; +# endif /* PNGCP_TIMING */ + + /* Used to write a new image (the original info_ptr is used) */ +# define MAX_SIZE ((png_alloc_size_t)(-1)) + png_alloc_size_t write_size; + png_alloc_size_t best_size; + png_structp write_pp; + + /* Base file information */ + png_alloc_size_t size; + png_uint_32 w; + png_uint_32 h; + int bpp; + png_byte ct; + int no_warnings; /* Do not output libpng warnings */ + int min_windowBits; /* The windowBits range is 8..8 */ + + /* Options handling */ + png_uint_32 results; /* A mask of errors seen */ + png_uint_32 options; /* See display_log below */ + png_byte entry[option_count]; /* The selected entry+1 of an option + * that appears on the command line, or + * 0 if it was not given. */ + int value[option_count]; /* Corresponding value */ + + /* Compression exhaustive testing */ + /* Temporary variables used only while testing a single collection of + * settings: + */ + unsigned int csp; /* next stack entry to use */ + unsigned int nsp; /* highest active entry+1 found so far */ + + /* Values used while iterating through all the combinations of settings for a + * single file: + */ + unsigned int tsp; /* nsp from the last run; this is the + * index+1 of the highest active entry on + * this run; this entry will be advanced. + */ + int opt_string_start; /* Position in buffer for the first + * searched option; non-zero if earlier + * options were set on the command line. + */ + struct stack + { + png_alloc_size_t best_size; /* Best so far for this option */ + png_alloc_size_t lo_size; + png_alloc_size_t hi_size; + int lo, hi; /* For binary chop of a range */ + int best_val; /* Best value found so far */ + int opt_string_end; /* End of the option string in 'curr' */ + png_byte opt; /* The option being tested */ + png_byte entry; /* The next value entry to be tested */ + png_byte end; /* This is the last entry */ + } stack[SL]; /* Stack of entries being tested */ + char curr[32*SL]; /* current options being tested */ + char best[32*SL]; /* best options */ + + char namebuf[FILENAME_MAX]; /* output file name */ +}; + +static void +display_init(struct display *dp) + /* Call this only once right at the start to initialize the control + * structure, the (struct buffer) lists are maintained across calls - the + * memory is not freed. + */ +{ + memset(dp, 0, sizeof *dp); + dp->operation = "internal error"; + dp->filename = "command line"; + dp->output_file = "no output file"; + dp->options = WARNINGS; /* default to !verbose, !quiet */ + dp->fp = NULL; + dp->read_pp = NULL; + dp->ip = NULL; + dp->write_pp = NULL; + dp->min_windowBits = -1; /* this is an OPTIND, so -1 won't match anything */ +# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED + dp->text_ptr = NULL; + dp->num_text = 0; + dp->text_stashed = 0; +# endif /* pre 1.7 */ +} + +static void +display_clean_read(struct display *dp) +{ + if (dp->read_pp != NULL) + png_destroy_read_struct(&dp->read_pp, NULL, NULL); + + if (dp->fp != NULL) + { + FILE *fp = dp->fp; + dp->fp = NULL; + (void)fclose(fp); + } +} + +static void +display_clean_write(struct display *dp) +{ + if (dp->fp != NULL) + { + FILE *fp = dp->fp; + dp->fp = NULL; + (void)fclose(fp); + } + + if (dp->write_pp != NULL) + png_destroy_write_struct(&dp->write_pp, dp->tsp > 0 ? NULL : &dp->ip); +} + +static void +display_clean(struct display *dp) +{ + display_clean_read(dp); + display_clean_write(dp); + dp->output_file = NULL; + +# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED + /* This is actually created and used by the write code, but only + * once; it has to be retained for subsequent writes of the same file. + */ + if (dp->text_stashed) + { + dp->text_stashed = 0; + dp->num_text = 0; + free(dp->text_ptr); + dp->text_ptr = NULL; + } +# endif /* pre 1.7 */ + + /* leave the filename for error detection */ + dp->results = 0; /* reset for next time */ +} + +static void +display_destroy(struct display *dp) +{ + /* Release any memory held in the display. */ + display_clean(dp); +} + +static struct display * +get_dp(png_structp pp) + /* The display pointer is always stored in the png_struct error pointer */ +{ + struct display *dp = (struct display*)png_get_error_ptr(pp); + + if (dp == NULL) + { + fprintf(stderr, "pngcp: internal error (no display)\n"); + exit(99); /* prevents a crash */ + } + + return dp; +} + +/* error handling */ +#ifdef __GNUC__ +# define VGATTR __attribute__((__format__ (__printf__,3,4))) + /* Required to quiet GNUC warnings when the compiler sees a stdarg function + * that calls one of the stdio v APIs. + */ +#else +# define VGATTR +#endif +static void VGATTR +display_log(struct display *dp, error_level level, const char *fmt, ...) + /* 'level' is as above, fmt is a stdio style format string. This routine + * does not return if level is above LIBPNG_WARNING + */ +{ + dp->results |= 1U << level; + + if (level > (error_level)(dp->options & LEVEL_MASK)) + { + const char *lp; + va_list ap; + + switch (level) + { + case INFORMATION: lp = "information"; break; + case LIBPNG_WARNING: lp = "warning(libpng)"; break; + case APP_WARNING: lp = "warning(pngcp)"; break; + case APP_FAIL: lp = "error(continuable)"; break; + case LIBPNG_ERROR: lp = "error(libpng)"; break; + case LIBPNG_BUG: lp = "bug(libpng)"; break; + case APP_ERROR: lp = "error(pngcp)"; break; + case USER_ERROR: lp = "error(user)"; break; + + case INTERNAL_ERROR: /* anything unexpected is an internal error: */ + case VERBOSE: case WARNINGS: case ERRORS: case QUIET: + default: lp = "bug(pngcp)"; break; + } + + fprintf(stderr, "%s: %s: %s", + dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation); + + fprintf(stderr, ": "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + } + /* else do not output any message */ + + /* Errors cause this routine to exit to the fail code */ + if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE))) + { + if (dp->errset) + longjmp(dp->error_return, level); + + else + exit(99); + } +} + +#if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED +static void +text_stash(struct display *dp) +{ + /* libpng 1.6 and earlier fixed a bug whereby text chunks were written + * multiple times by png_write_png; the issue was that png_write_png passed + * the same png_info to both png_write_info and png_write_end. Rather than + * fixing it by recording the information in the png_struct, or by recording + * where to write the chunks, the fix made was to change the 'compression' + * field of the chunk to invalid values, rendering the png_info somewhat + * useless. + * + * The only fix for this given that we use the png_info more than once is to + * make a copy of the text chunks and png_set_text it each time. This adds a + * text chunks, so they get replicated, but only the new set gets written + * each time. This uses memory like crazy but there is no way to delete the + * useless chunks from the png_info. + * + * To make this slightly more efficient only the top level structure is + * copied; since the old strings are actually preserved (in 1.6 and earlier) + * this happens to work. + */ + png_textp chunks = NULL; + + dp->num_text = png_get_text(dp->write_pp, dp->ip, &chunks, NULL); + + if (dp->num_text > 0) + { + dp->text_ptr = voidcast(png_textp, malloc(dp->num_text * sizeof *chunks)); + + if (dp->text_ptr == NULL) + display_log(dp, APP_ERROR, "text chunks: stash malloc failed"); + + else + memcpy(dp->text_ptr, chunks, dp->num_text * sizeof *chunks); + } + + dp->text_stashed = 1; /* regardless of whether there are chunks or not */ +} + +#define text_stash(dp) if (!dp->text_stashed) text_stash(dp) + +static void +text_restore(struct display *dp) +{ + /* libpng makes a copy, so this is fine: */ + if (dp->text_ptr != NULL) + png_set_text(dp->write_pp, dp->ip, dp->text_ptr, dp->num_text); +} + +#define text_restore(dp) if (dp->text_stashed) text_restore(dp) + +#else +#define text_stash(dp) ((void)0) +#define text_restore(dp) ((void)0) +#endif /* pre 1.7 */ + +/* OPTIONS: + * + * The command handles options of the forms: + * + * --option + * Turn an option on (Option) + * --no-option + * Turn an option off (Option) + * --option=value + * Set an option to a value (Value) + * --option=val1,val2,val3 + * Set an option to a bitmask constructed from the values (List) + */ +static png_byte +option_index(struct display *dp, const char *opt, size_t len) + /* Return the index (in options[]) of the given option, outputs an error if + * it does not exist. Takes the name of the option and a length (number of + * characters in the name). + */ +{ + png_byte j; + + for (j=0; j<option_count; ++j) + if (strncmp(options[j].name, opt, len) == 0 && options[j].name[len] == 0) + return j; + + /* If the setjmp buffer is set the code is asking for an option index; this + * is bad. Otherwise this is the command line option parsing. + */ + display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR, + "%.*s: unknown option", (int)/*SAFE*/len, opt); + abort(); /* NOT REACHED */ +} + +/* This works for an option name (no quotes): */ +#define OPTIND(dp, name) option_index(dp, #name, (sizeof #name)-1) + +static int +get_option(struct display *dp, const char *opt, int *value) +{ + const png_byte i = option_index(dp, opt, strlen(opt)); + + if (dp->entry[i]) /* option was set on command line */ + { + *value = dp->value[i]; + return 1; + } + + else + return 0; +} + +static int +set_opt_string_(struct display *dp, unsigned int sp, png_byte opt, + const char *entry_name) + /* Add the appropriate option string to dp->curr. */ +{ + int offset, add; + + if (sp > 0) + offset = dp->stack[sp-1].opt_string_end; + + else + offset = dp->opt_string_start; + + if (entry_name == range_lo) + add = sprintf(dp->curr+offset, " --%s=%d", options[opt].name, + dp->value[opt]); + + else + add = sprintf(dp->curr+offset, " --%s=%s", options[opt].name, entry_name); + + if (add < 0) + display_log(dp, INTERNAL_ERROR, "sprintf failed"); + + assert(offset+add < (int)/*SAFE*/sizeof dp->curr); + return offset+add; +} + +static void +set_opt_string(struct display *dp, unsigned int sp) + /* Add the appropriate option string to dp->curr. */ +{ + dp->stack[sp].opt_string_end = set_opt_string_(dp, sp, dp->stack[sp].opt, + options[dp->stack[sp].opt].values[dp->stack[sp].entry].name); +} + +static void +record_opt(struct display *dp, png_byte opt, const char *entry_name) + /* Record this option in dp->curr; called for an option not being searched, + * the caller passes in the name of the value, or range_lo to use the + * numerical value. + */ +{ + const unsigned int sp = dp->csp; /* stack entry of next searched option */ + + if (sp >= dp->tsp) + { + /* At top of stack; add the opt string for this entry to the previous + * searched entry or the start of the dp->curr buffer if there is nothing + * on the stack yet (sp == 0). + */ + const int offset = set_opt_string_(dp, sp, opt, entry_name); + + if (sp > 0) + dp->stack[sp-1].opt_string_end = offset; + + else + dp->opt_string_start = offset; + } + + /* else do nothing: option already recorded */ +} + +static int +opt_list_end(struct display *dp, png_byte opt, png_byte entry) +{ + if (options[opt].values[entry].name == range_lo) + return entry+1U >= options[opt].value_count /* missing range_hi */ || + options[opt].values[entry+1U].name != range_hi /* likewise */ || + options[opt].values[entry+1U].value <= dp->value[opt] /* range end */; + + else + return entry+1U >= options[opt].value_count /* missing 'all' */ || + options[opt].values[entry+1U].name == all /* last entry */; +} + +static void +push_opt(struct display *dp, unsigned int sp, png_byte opt, int search) + /* Push a new option onto the stack, initializing the new stack entry + * appropriately; this does all the work of next_opt (setting end/nsp) for + * the first entry in the list. + */ +{ + png_byte entry; + const char *entry_name; + + assert(sp == dp->tsp && sp < SL); + + /* The starting entry is entry 0 unless there is a range in which case it is + * the entry corresponding to range_lo: + */ + entry = options[opt].value_count; + assert(entry > 0U); + + do + { + entry_name = options[opt].values[--entry].name; + if (entry_name == range_lo) + break; + } + while (entry > 0U); + + dp->tsp = sp+1U; + dp->stack[sp].best_size = + dp->stack[sp].lo_size = + dp->stack[sp].hi_size = MAX_SIZE; + + if (search && entry_name == range_lo) /* search this range */ + { + dp->stack[sp].lo = options[opt].values[entry].value; + /* check for a mal-formed RANGE above: */ + assert(entry+1 < options[opt].value_count && + options[opt].values[entry+1].name == range_hi); + dp->stack[sp].hi = options[opt].values[entry+1].value; + } + + else + { + /* next_opt will just iterate over the range. */ + dp->stack[sp].lo = INT_MAX; + dp->stack[sp].hi = INT_MIN; /* Prevent range chop */ + } + + dp->stack[sp].opt = opt; + dp->stack[sp].entry = entry; + dp->stack[sp].best_val = dp->value[opt] = options[opt].values[entry].value; + + set_opt_string(dp, sp); + + /* This works for the search case too; if the range has only one entry 'end' + * will be marked here. + */ + if (opt_list_end(dp, opt, entry)) + { + dp->stack[sp].end = 1; + /* Skip the warning if pngcp did this itself. See the code in + * set_windowBits_hi. + */ + if (opt != dp->min_windowBits) + display_log(dp, APP_WARNING, "%s: only testing one value", + options[opt].name); + } + + else + { + dp->stack[sp].end = 0; + dp->nsp = dp->tsp; + } + + /* Do a lazy cache of the text chunks for libpng 1.6 and earlier; this is + * because they can only be written once(!) so if we are going to re-use the + * png_info we need a copy. + */ + text_stash(dp); +} + +static void +next_opt(struct display *dp, unsigned int sp) + /* Return the next value for this option. When called 'sp' is expected to be + * the topmost stack entry - only the topmost entry changes each time round - + * and there must be a valid entry to return. next_opt will set dp->nsp to + * sp+1 if more entries are available, otherwise it will not change it and + * set dp->stack[s].end to true. + */ +{ + int search = 0; + png_byte entry, opt; + const char *entry_name; + + /* dp->stack[sp] must be the top stack entry and it must be active: */ + assert(sp+1U == dp->tsp && !dp->stack[sp].end); + + opt = dp->stack[sp].opt; + entry = dp->stack[sp].entry; + assert(entry+1U < options[opt].value_count); + entry_name = options[opt].values[entry].name; + assert(entry_name != NULL); + + /* For ranges increment the value but don't change the entry, for all other + * cases move to the next entry and load its value: + */ + if (entry_name == range_lo) /* a range */ + { + /* A range can be iterated over or searched. The default iteration option + * is indicated by hi < lo on the stack, otherwise the range being search + * is [lo..hi] (inclusive). + */ + if (dp->stack[sp].lo > dp->stack[sp].hi) + dp->value[opt]++; + + else + { + /* This is the best size found for this option value: */ + png_alloc_size_t best_size = dp->stack[sp].best_size; + int lo = dp->stack[sp].lo; + int hi = dp->stack[sp].hi; + int val = dp->value[opt]; + + search = 1; /* end is determined here */ + assert(best_size < MAX_SIZE); + + if (val == lo) + { + /* Finding the best for the low end of the range: */ + dp->stack[sp].lo_size = best_size; + assert(hi > val); + + if (hi == val+1) /* only 2 entries */ + dp->stack[sp].end = 1; + + val = hi; + } + + else if (val == hi) + { + dp->stack[sp].hi_size = best_size; + assert(val > lo+1); /* else 'end' set above */ + + if (val == lo+2) /* only three entries to test */ + dp->stack[sp].end = 1; + + val = (lo + val)/2; + } + + else + { + png_alloc_size_t lo_size = dp->stack[sp].lo_size; + png_alloc_size_t hi_size = dp->stack[sp].hi_size; + + /* lo and hi should have been tested. */ + assert(lo_size < MAX_SIZE && hi_size < MAX_SIZE); + + /* These cases arise with the 'probe' handling below when there is a + * dip or peak in the size curve. + */ + if (val < lo) /* probing a new lo */ + { + /* Swap lo and val: */ + dp->stack[sp].lo = val; + dp->stack[sp].lo_size = best_size; + val = lo; + best_size = lo_size; + lo = dp->stack[sp].lo; + lo_size = dp->stack[sp].lo_size; + } + + else if (val > hi) /* probing a new hi */ + { + /* Swap hi and val: */ + dp->stack[sp].hi = val; + dp->stack[sp].hi_size = best_size; + val = hi; + best_size = hi_size; + hi = dp->stack[sp].hi; + hi_size = dp->stack[sp].hi_size; + } + + /* The following should be true or something got messed up above. */ + assert(lo < val && val < hi); + + /* If there are only four entries (lo, val, hi plus one more) just + * test the remaining entry. + */ + if (hi == lo+3) + { + /* Because of the 'probe' code val can either be lo+1 or hi-1; we + * need to test the other. + */ + val = lo + ((val == lo+1) ? 2 : 1); + assert(lo < val && val < hi); + dp->stack[sp].end = 1; + } + + else + { + /* There are at least 2 entries still untested between lo and hi, + * i.e. hi >= lo+4. 'val' is the midpoint +/- 0.5 + * + * Separate out the four easy cases when lo..val..hi are + * monotonically decreased or (more weird) increasing: + */ + assert(hi > lo+3); + + if (lo_size <= best_size && best_size <= hi_size) + { + /* Select the low range; testing this first favours the low + * range over the high range when everything comes out equal. + * Because of the probing 'val' may be lo+1. In that case end + * the search and set 'val' to lo+2. + */ + if (val == lo+1) + { + ++val; + dp->stack[sp].end = 1; + } + + else + { + dp->stack[sp].hi = hi = val; + dp->stack[sp].hi_size = best_size; + val = (lo + val) / 2; + } + } + + else if (lo_size >= best_size && best_size >= hi_size) + { + /* Monotonically decreasing size; this is the expected case. + * Select the high end of the range. As above, val may be + * hi-1. + */ + if (val == hi-1) + { + --val; + dp->stack[sp].end = 1; + } + + else + { + dp->stack[sp].lo = lo = val; + dp->stack[sp].lo_size = best_size; + val = (val + hi) / 2; + } + } + + /* If both those tests failed 'best_size' is either greater than + * or less than both lo_size and hi_size. There is a peak or dip + * in the curve of sizes from lo to hi and val is on the peak or + * dip. + * + * Because the ranges being searched as so small (level is 1..9, + * windowBits 8..15, memLevel 1..9) there will only be at most + * three untested values between lo..val and val..hi, so solve + * the problem by probing down from hi or up from lo, whichever + * is the higher. + * + * This is the place where 'val' is set to outside the range + * lo..hi, described as 'probing', though maybe 'narrowing' would + * be more accurate. + */ + else if (lo_size <= hi_size) /* down from hi */ + { + dp->stack[sp].hi = val; + dp->stack[sp].hi_size = best_size; + val = --hi; + } + + else /* up from low */ + { + dp->stack[sp].lo = val; + dp->stack[sp].lo_size = best_size; + val = ++lo; + } + + /* lo and hi are still the true range limits, check for the end + * condition. + */ + assert(hi > lo+1); + if (hi <= lo+2) + dp->stack[sp].end = 1; + } + } + + assert(val != dp->stack[sp].best_val); /* should be a new value */ + dp->value[opt] = val; + dp->stack[sp].best_size = MAX_SIZE; + } + } + + else + { + /* Increment 'entry' */ + dp->value[opt] = options[opt].values[++entry].value; + dp->stack[sp].entry = entry; + } + + set_opt_string(dp, sp); + + if (!search && opt_list_end(dp, opt, entry)) /* end of list */ + dp->stack[sp].end = 1; + + else if (!dp->stack[sp].end) /* still active after all these tests */ + dp->nsp = dp->tsp; +} + +static int +compare_option(const struct display *dp, unsigned int sp) +{ + int opt = dp->stack[sp].opt; + + /* If the best so far is numerically less than the current value the + * current set of options is invariably worse. + */ + if (dp->stack[sp].best_val < dp->value[opt]) + return -1; + + /* Lists of options are searched out of numerical order (currently only + * strategy), so only return +1 here when a range is being searched. + */ + else if (dp->stack[sp].best_val > dp->value[opt]) + { + if (dp->stack[sp].lo <= dp->stack[sp].hi /*searching*/) + return 1; + + else + return -1; + } + + else + return 0; /* match; current value is the best one */ +} + +static int +advance_opt(struct display *dp, png_byte opt, int search) +{ + unsigned int sp = dp->csp++; /* my stack entry */ + + assert(sp >= dp->nsp); /* nsp starts off zero */ + + /* If the entry was active in the previous run dp->stack[sp] is already + * set up and dp->tsp will be greater than sp, otherwise a new entry + * needs to be created. + * + * dp->nsp is handled this way: + * + * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are + * both set (by push_opt) to the next stack entry *unless* there is + * only one entry in the new list, in which case dp->stack[sp].end + * is set. + * + * 2) For the top stack entry next_opt is called. The entry must be + * active (dp->stack[sp].end is not set) and either 'nsp' or 'end' + * will be updated as appropriate. + * + * 3) For lower stack entries nsp is set unless the stack entry is + * already at the end. This means that when all the higher entries + * are popped this entry will be too. + */ + if (sp >= dp->tsp) + { + push_opt(dp, sp, opt, search); /* This sets tsp to sp+1 */ + return 1; /* initialized */ + } + + else + { + int ret = 0; /* unchanged */ + + /* An option that is already on the stack; update best_size and best_val + * if appropriate. On the first run there are no previous values and + * dp->write_size will be MAX_SIZE, however on the first run dp->tsp + * starts off as 0. + */ + assert(dp->write_size > 0U && dp->write_size < MAX_SIZE); + + if (dp->stack[sp].best_size > dp->write_size || + (dp->stack[sp].best_size == dp->write_size && + compare_option(dp, sp) > 0)) + { + dp->stack[sp].best_size = dp->write_size; + dp->stack[sp].best_val = dp->value[opt]; + } + + if (sp+1U >= dp->tsp) + { + next_opt(dp, sp); + ret = 1; /* advanced */ + } + + else if (!dp->stack[sp].end) /* Active, not at top of stack */ + dp->nsp = sp+1U; + + return ret; /* advanced || unchanged */ + } +} + +static int +getallopts_(struct display *dp, const png_byte opt, int *value, int record) + /* Like getop but iterate over all the values if the option was set to "all". + */ +{ + if (dp->entry[opt]) /* option was set on command line */ + { + /* Simple, single value, entries don't have a stack frame and have a fixed + * value (it doesn't change once set on the command line). Otherwise the + * value (entry) selected from the command line is 'all': + */ + const char *entry_name = options[opt].values[dp->entry[opt]-1].name; + + if (entry_name == all) + (void)advance_opt(dp, opt, 0/*do not search; iterate*/); + + else if (record) + record_opt(dp, opt, entry_name); + + *value = dp->value[opt]; + return 1; /* set */ + } + + else + return 0; /* not set */ +} + +static int +getallopts(struct display *dp, const char *opt_str, int *value) +{ + return getallopts_(dp, option_index(dp, opt_str, strlen(opt_str)), value, 0); +} + +static int +getsearchopts(struct display *dp, const char *opt_str, int *value) + /* As above except that if the option was not set try a search */ +{ + png_byte istrat; + const png_byte opt = option_index(dp, opt_str, strlen(opt_str)); + int record = options[opt].search; + const char *entry_name; + + /* If it was set on the command line honour the setting, including 'all' + * which will override the built in search: + */ + if (getallopts_(dp, opt, value, record)) + return 1; + + else if (!record) /* not a search option */ + return 0; /* unset and not searched */ + + /* Otherwise decide what to do here. */ + istrat = OPTIND(dp, strategy); + entry_name = range_lo; /* record the value, not the name */ + + if (opt == istrat) /* search all strategies */ + (void)advance_opt(dp, opt, 0/*iterate*/), record=0; + + else if (opt == OPTIND(dp, level)) + { + /* Both RLE and HUFFMAN don't benefit from level increases */ + if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY) + dp->value[opt] = 1; + + else /* fixed, filtered or default */ + (void)advance_opt(dp, opt, 1/*search*/), record=0; + } + + else if (opt == OPTIND(dp, windowBits)) + { + /* Changing windowBits for strategies that do not search the window is + * pointless. Huffman-only does not search, RLE only searches backwards + * one byte, so given that the maximum string length is 258, a windowBits + * of 9 is always sufficient. + */ + if (dp->value[istrat] == Z_HUFFMAN_ONLY) + dp->value[opt] = 8; + + else if (dp->value[istrat] == Z_RLE) + dp->value[opt] = 9; + + else /* fixed, filtered or default */ + (void)advance_opt(dp, opt, 1/*search*/), record=0; + } + + else if (opt == OPTIND(dp, memLevel)) + { +# if 0 + (void)advance_opt(dp, opt, 0/*all*/), record=0; +# else + dp->value[opt] = MAX_MEM_LEVEL; +# endif + } + + else /* something else */ + assert(0=="reached"); + + if (record) + record_opt(dp, opt, entry_name); + + /* One of the above searched options: */ + *value = dp->value[opt]; + return 1; +} + +static int +find_val(struct display *dp, png_byte opt, const char *str, size_t len) + /* Like option_index but sets (index+i) of the entry in options[opt] that + * matches str[0..len-1] into dp->entry[opt] as well as returning the actual + * value. + */ +{ + int rlo = INT_MAX, rhi = INT_MIN; + png_byte j, irange = 0; + + for (j=1U; j<=options[opt].value_count; ++j) + { + if (strncmp(options[opt].values[j-1U].name, str, len) == 0 && + options[opt].values[j-1U].name[len] == 0) + { + dp->entry[opt] = j; + return options[opt].values[j-1U].value; + } + else if (options[opt].values[j-1U].name == range_lo) + rlo = options[opt].values[j-1U].value, irange = j; + else if (options[opt].values[j-1U].name == range_hi) + rhi = options[opt].values[j-1U].value; + } + + /* No match on the name, but there may be a range. */ + if (irange > 0) + { + char *ep = NULL; + long l = strtol(str, &ep, 0); + + if (ep == str+len && l >= rlo && l <= rhi) + { + dp->entry[opt] = irange; /* range_lo */ + return (int)/*SAFE*/l; + } + } + + display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR, + "%s: unknown value setting '%.*s'", options[opt].name, + (int)/*SAFE*/len, str); + abort(); /* NOT REACHED */ +} + +static int +opt_check(struct display *dp, const char *arg) +{ + assert(dp->errset == 0); + + if (arg != NULL && arg[0] == '-' && arg[1] == '-') + { + int i = 0, negate = (strncmp(arg+2, "no-", 3) == 0), val; + png_byte j; + + if (negate) + arg += 5; /* --no- */ + + else + arg += 2; /* -- */ + + /* Find the length (expect arg\0 or arg=) */ + while (arg[i] != 0 && arg[i] != '=') ++i; + + /* So arg[0..i-1] is the argument name, this does not return if this isn't + * a valid option name. + */ + j = option_index(dp, arg, i); + + /* It matcheth an option; check the remainder. */ + if (arg[i] == 0) /* no specified value, use the default */ + { + val = options[j].values[negate].value; + dp->entry[j] = (png_byte)/*SAFE*/(negate + 1U); + } + + else + { + const char *list = arg + (i+1); + + /* Expect a single value here unless this is a list, in which case + * multiple values are combined. + */ + if (options[j].opt != LIST) + { + /* find_val sets 'dp->entry[j]' to a non-zero value: */ + val = find_val(dp, j, list, strlen(list)); + + if (negate) + { + if (options[j].opt < OPTION) + val = !val; + + else + { + display_log(dp, USER_ERROR, + "%.*s: option=arg cannot be negated", i, arg); + abort(); /* NOT REACHED */ + } + } + } + + else /* multiple options separated by ',' characters */ + { + /* --no-option negates list values from the default, which should + * therefore be 'all'. Notice that if the option list is empty in + * this case nothing will be removed and therefore --no-option= is + * the same as --option. + */ + if (negate) + val = options[j].values[0].value; + + else + val = 0; + + while (*list != 0) /* allows option= which sets 0 */ + { + /* A value is terminated by the end of the list or a ',' + * character. + */ + int v, iv; + + iv = 0; /* an index into 'list' */ + while (list[++iv] != 0 && list[iv] != ',') {} + + v = find_val(dp, j, list, iv); + + if (negate) + val &= ~v; + + else + val |= v; + + list += iv; + if (*list != 0) + ++list; /* skip the ',' */ + } + } + } + + /* 'val' is the new value, store it for use later and debugging: */ + dp->value[j] = val; + + if (options[j].opt < LEVEL_MASK) + { + /* The handling for error levels is to set the level. */ + if (val) /* Set this level */ + dp->options = (dp->options & ~LEVEL_MASK) | options[j].opt; + + else + display_log(dp, USER_ERROR, + "%.*s: messages cannot be turned off individually; set a message level", + i, arg); + } + + else if (options[j].opt < OPTION) + { + if (val) + dp->options |= options[j].opt; + + else + dp->options &= ~options[j].opt; + } + + return 1; /* this is an option */ + } + + else + return 0; /* not an option */ +} + +#ifdef PNG_PNGCP_TIMING_SUPPORTED +static void +set_timer(struct display *dp, struct timespec *timer) +{ + /* Do the timing using clock_gettime and the per-process timer. */ + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, timer)) + { + display_log(dp, APP_ERROR, + "CLOCK_PROCESS_CPUTIME_ID: %s: timing disabled\n", strerror(errno)); + dp->value[OPTIND(dp,time)] = 0; /* i.e. off */ + } +} + +static void +start_timer(struct display *dp, int what) +{ + if ((dp->value[OPTIND(dp,time)] & what) != 0) + set_timer(dp, what == PNGCP_TIME_READ ? &dp->read_time : &dp->write_time); +} + +static void +end_timer(struct display *dp, int what) +{ + if ((dp->value[OPTIND(dp,time)] & what) != 0) + { + struct timespec t, tmp; + + set_timer(dp, &t); + + if (what == PNGCP_TIME_READ) + tmp = dp->read_time; + + else + tmp = dp->write_time; + + t.tv_sec -= tmp.tv_sec; + t.tv_nsec -= tmp.tv_nsec; + + if (t.tv_nsec < 0) + { + --(t.tv_sec); + t.tv_nsec += 1000000000L; + } + + if (what == PNGCP_TIME_READ) + dp->read_time = t, tmp = dp->read_time_total; + + else + dp->write_time = t, tmp = dp->write_time_total; + + tmp.tv_sec += t.tv_sec; + tmp.tv_nsec += t.tv_nsec; + + if (tmp.tv_nsec >= 1000000000L) + { + ++(tmp.tv_sec); + tmp.tv_nsec -= 1000000000L; + } + + if (what == PNGCP_TIME_READ) + dp->read_time_total = tmp; + + else + dp->write_time_total = tmp; + } +} + +static void +print_time(const char *what, struct timespec t) +{ + printf("%s %.2lu.%.9ld", what, (unsigned long)t.tv_sec, t.tv_nsec); +} +#else /* !PNGCP_TIMING */ +#define start_timer(dp, what) ((void)0) +#define end_timer(dp, what) ((void)0) +#endif /* !PNGCP_TIMING */ + +/* The following is used in main to verify that the final argument is a + * directory: + */ +static int +checkdir(const char *pathname) +{ + struct stat buf; + return stat(pathname, &buf) == 0 && S_ISDIR(buf.st_mode); +} + +/* Work out whether a path is valid (if not a display_log occurs), a directory + * (1 is returned) or a file *or* non-existent (0 is returned). + * + * Used for a write path. + */ +static int +isdir(struct display *dp, const char *pathname) +{ + if (pathname == NULL) + return 0; /* stdout */ + + else if (pathname[0] == 0) + return 1; /* empty string */ + + else + { + struct stat buf; + int ret = stat(pathname, &buf); + + if (ret == 0) /* the entry exists */ + { + if (S_ISDIR(buf.st_mode)) + return 1; + + /* Else expect an object that exists and can be written: */ + if (access(pathname, W_OK) != 0) + display_log(dp, USER_ERROR, "%s: cannot be written (%s)", pathname, + strerror(errno)); + + return 0; /* file (exists, can be written) */ + } + + else /* an error */ + { + /* Non-existence is fine, other errors are not: */ + if (errno != ENOENT) + display_log(dp, USER_ERROR, "%s: invalid output name (%s)", + pathname, strerror(errno)); + + return 0; /* file (does not exist) */ + } + } +} + +static void +makename(struct display *dp, const char *dir, const char *infile) +{ + /* Make a name for an output file (and check it). */ + dp->namebuf[0] = 0; + + if (dir == NULL || infile == NULL) + display_log(dp, INTERNAL_ERROR, "NULL name to makename"); + + else + { + size_t dsize = strlen(dir); + + if (dsize <= (sizeof dp->namebuf)-2) /* Allow for name + '/' + '\0' */ + { + size_t isize = strlen(infile); + size_t istart = isize-1; + + /* This should fail before here: */ + if (infile[istart] == '/') + display_log(dp, INTERNAL_ERROR, "infile with trailing /"); + + memcpy(dp->namebuf, dir, dsize); + if (dsize > 0 && dp->namebuf[dsize-1] != '/') + dp->namebuf[dsize++] = '/'; + + /* Find the rightmost non-/ character: */ + while (istart > 0 && infile[istart-1] != '/') + --istart; + + isize -= istart; + infile += istart; + + if (dsize+isize < (sizeof dp->namebuf)) /* dsize + infile + '\0' */ + { + memcpy(dp->namebuf+dsize, infile, isize+1); + + if (isdir(dp, dp->namebuf)) + display_log(dp, USER_ERROR, "%s: output file is a directory", + dp->namebuf); + } + + else + { + dp->namebuf[dsize] = 0; /* allowed for: -2 at start */ + display_log(dp, USER_ERROR, "%s%s: output file name too long", + dp->namebuf, infile); + } + } + + else + display_log(dp, USER_ERROR, "%s: output directory name too long", dir); + } +} + +/* error handler callbacks for libpng */ +static void PNGCBAPI +display_warning(png_structp pp, png_const_charp warning) +{ + struct display *dp = get_dp(pp); + + /* This is used to prevent repeated warnings while searching */ + if (!dp->no_warnings) + display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning); +} + +static void PNGCBAPI +display_error(png_structp pp, png_const_charp error) +{ + struct display *dp = get_dp(pp); + + display_log(dp, LIBPNG_ERROR, "%s", error); +} + +static void +display_start_read(struct display *dp, const char *filename) +{ + if (filename != NULL) + { + dp->filename = filename; + dp->fp = fopen(filename, "rb"); + } + + else + { + dp->filename = "<stdin>"; + dp->fp = stdin; + } + + dp->w = dp->h = 0U; + dp->bpp = 0U; + dp->size = 0U; + dp->read_size = 0U; + + if (dp->fp == NULL) + display_log(dp, USER_ERROR, "file open failed (%s)", strerror(errno)); +} + +static void PNGCBAPI +read_function(png_structp pp, png_bytep data, png_size_t size) +{ + struct display *dp = get_dp(pp); + + if (size == 0U || fread(data, size, 1U, dp->fp) == 1U) + dp->read_size += size; + + else + { + if (feof(dp->fp)) + display_log(dp, LIBPNG_ERROR, "PNG file truncated"); + else + display_log(dp, LIBPNG_ERROR, "PNG file read failed (%s)", + strerror(errno)); + } +} + +static void +read_png(struct display *dp, const char *filename) +{ + display_clean_read(dp); /* safety */ + display_start_read(dp, filename); + + dp->read_pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp, + display_error, display_warning); + if (dp->read_pp == NULL) + display_log(dp, LIBPNG_ERROR, "failed to create read struct"); + +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + png_set_benign_errors(dp->read_pp, 1/*allowed*/); +# endif /* BENIGN_ERRORS */ + +# ifdef FIX_INDEX + if ((dp->options & FIX_INDEX) != 0) + png_set_check_for_invalid_index(dp->read_pp, 1/*on, no warning*/); +# ifdef IGNORE_INDEX + else +# endif /* IGNORE_INDEX */ +# endif /* FIX_INDEX */ +# ifdef IGNORE_INDEX + if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */ + png_set_check_for_invalid_index(dp->read_pp, -1/*off completely*/); +# endif /* IGNORE_INDEX */ + + /* The png_read_png API requires us to make the info struct, but it does the + * call to png_read_info. + */ + dp->ip = png_create_info_struct(dp->read_pp); + if (dp->ip == NULL) + png_error(dp->read_pp, "failed to create info struct"); + + /* Set the IO handling */ + png_set_read_fn(dp->read_pp, dp, read_function); + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + png_set_keep_unknown_chunks(dp->read_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL, + 0); +# endif /* HANDLE_AS_UNKNOWN */ + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + /* Remove the user limits, if any */ + png_set_user_limits(dp->read_pp, 0x7fffffff, 0x7fffffff); +# endif /* SET_USER_LIMITS */ + + /* Now read the PNG. */ + start_timer(dp, PNGCP_TIME_READ); + png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/); + end_timer(dp, PNGCP_TIME_READ); + dp->w = png_get_image_width(dp->read_pp, dp->ip); + dp->h = png_get_image_height(dp->read_pp, dp->ip); + dp->ct = png_get_color_type(dp->read_pp, dp->ip); + dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) * + png_get_channels(dp->read_pp, dp->ip); + { + /* png_get_rowbytes should never return 0 because the value is set by the + * first call to png_set_IHDR, which should have happened by now, but just + * in case: + */ + png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip); + + if (rb == 0) + png_error(dp->read_pp, "invalid row byte count from libpng"); + + /* The size calc can overflow. */ + if ((MAX_SIZE-dp->h)/rb < dp->h) + png_error(dp->read_pp, "image too large"); + + dp->size = rb * dp->h + dp->h/*filter byte*/; + } + +#ifdef FIX_INDEX + if (dp->ct == PNG_COLOR_TYPE_PALETTE && (dp->options & FIX_INDEX) != 0) + { + int max = png_get_palette_max(dp->read_pp, dp->ip); + png_colorp palette = NULL; + int num = -1; + + if (png_get_PLTE(dp->read_pp, dp->ip, &palette, &num) != PNG_INFO_PLTE + || max < 0 || num <= 0 || palette == NULL) + display_log(dp, LIBPNG_ERROR, "invalid png_get_PLTE result"); + + if (max >= num) + { + /* 'Fix' the palette. */ + int i; + png_color newpal[256]; + + for (i=0; i<num; ++i) + newpal[i] = palette[i]; + + /* Fill in any remainder with a warning color: */ + for (; i<=max; ++i) + { + newpal[i].red = 0xbe; + newpal[i].green = 0xad; + newpal[i].blue = 0xed; + } + + png_set_PLTE(dp->read_pp, dp->ip, newpal, i); + } + } +#endif /* FIX_INDEX */ + + display_clean_read(dp); + dp->operation = "none"; +} + +static void +display_start_write(struct display *dp, const char *filename) +{ + assert(dp->fp == NULL); + + if ((dp->options & NOWRITE) != 0) + dp->output_file = "<no write>"; + + else + { + if (filename != NULL) + { + dp->output_file = filename; + dp->fp = fopen(filename, "wb"); + } + + else + { + dp->output_file = "<stdout>"; + dp->fp = stdout; + } + + if (dp->fp == NULL) + display_log(dp, USER_ERROR, "%s: file open failed (%s)", + dp->output_file, strerror(errno)); + } +} + +static void PNGCBAPI +write_function(png_structp pp, png_bytep data, png_size_t size) +{ + struct display *dp = get_dp(pp); + + /* The write fail is classed as a USER_ERROR, so --quiet does not turn it + * off, this seems more likely to be correct. + */ + if (dp->fp == NULL || fwrite(data, size, 1U, dp->fp) == 1U) + { + dp->write_size += size; + if (dp->write_size < size || dp->write_size == MAX_SIZE) + png_error(pp, "IDAT size overflow"); + } + + else + display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)", + dp->output_file, strerror(errno)); +} + +/* Compression option, 'method' is never set: there is no choice. + * + * IMPORTANT: the order of the entries in this macro determines the preference + * order when two different combos of two of these options produce an IDAT of + * the same size. The logic here is to put the things that affect the decoding + * of the PNG image ahead of those that are relevant only to the encoding. + */ +#define SET_COMPRESSION\ + SET(strategy, strategy);\ + SET(windowBits, window_bits);\ + SET(level, level);\ + SET(memLevel, mem_level); + +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +static void +search_compression(struct display *dp) +{ + /* Like set_compression below but use a more restricted search than 'all' */ + int val; + +# define SET(name, func) if (getsearchopts(dp, #name, &val))\ + png_set_compression_ ## func(dp->write_pp, val); + SET_COMPRESSION +# undef SET +} + +static void +set_compression(struct display *dp) +{ + int val; + +# define SET(name, func) if (getallopts(dp, #name, &val))\ + png_set_compression_ ## func(dp->write_pp, val); + SET_COMPRESSION +# undef SET +} + +#ifdef PNG_SW_COMPRESS_level /* 1.7.0+ */ +static void +set_ICC_profile_compression(struct display *dp) +{ + int val; + +# define SET(name, func) if (getallopts(dp, "ICC-profile-" #name, &val))\ + png_set_ICC_profile_compression_ ## func(dp->write_pp, val); + SET_COMPRESSION +# undef SET +} +#else +# define set_ICC_profile_compression(dp) ((void)0) +#endif +#else +# define search_compression(dp) ((void)0) +# define set_compression(dp) ((void)0) +# define set_ICC_profile_compression(dp) ((void)0) +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +static void +set_text_compression(struct display *dp) +{ + int val; + +# define SET(name, func) if (getallopts(dp, "text-" #name, &val))\ + png_set_text_compression_ ## func(dp->write_pp, val); + SET_COMPRESSION +# undef SET +} +#else +# define set_text_compression(dp) ((void)0) +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ + +static void +write_png(struct display *dp, const char *destname) +{ + display_clean_write(dp); /* safety */ + display_start_write(dp, destname); + + dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp, + display_error, display_warning); + + if (dp->write_pp == NULL) + display_log(dp, LIBPNG_ERROR, "failed to create write png_struct"); + +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + png_set_benign_errors(dp->write_pp, 1/*allowed*/); +# endif /* BENIGN_ERRORS */ + + png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/); + +#ifdef IGNORE_INDEX + if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */ + png_set_check_for_invalid_index(dp->write_pp, -1/*off completely*/); +#endif /* IGNORE_INDEX */ + + /* Restore the text chunks when using libpng 1.6 or less; this is a macro + * which expands to nothing in 1.7+ In earlier versions it tests + * dp->text_stashed, which is only set (below) *after* the first write. + */ + text_restore(dp); + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + png_set_keep_unknown_chunks(dp->write_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL, + 0); +# endif /* HANDLE_AS_UNKNOWN */ + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + /* Remove the user limits, if any */ + png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff); +# endif + + /* OPTION HANDLING */ + /* compression outputs, IDAT and zTXt/iTXt: */ + dp->tsp = dp->nsp; + dp->nsp = dp->csp = 0; +# ifdef PNG_SW_COMPRESS_png_level + { + int val; + + /* This sets everything, but then the following options just override + * the specific settings for ICC profiles and text. + */ + if (getallopts(dp, "compression", &val)) + png_set_compression(dp->write_pp, val); + + if (getallopts(dp, "ICC-profile-compression", &val)) + png_set_ICC_profile_compression(dp->write_pp, val); + + if (getallopts(dp, "text-compression", &val)) + png_set_text_compression(dp->write_pp, val); + } +# endif /* png_level support */ + if (dp->options & SEARCH) + search_compression(dp); + else + set_compression(dp); + set_ICC_profile_compression(dp); + set_text_compression(dp); + + { + int val; + + /* The permitted range is 1..0x7FFFFFFF, so the cast is safe */ + if (get_option(dp, "IDAT-size", &val)) + png_set_IDAT_size(dp->write_pp, val); + } + + /* filter handling */ +# ifdef PNG_WRITE_FILTER_SUPPORTED + { + int val; + + if (get_option(dp, "filter", &val)) + png_set_filter(dp->write_pp, PNG_FILTER_TYPE_BASE, val); + } +# endif /* WRITE_FILTER */ + + /* This just uses the 'read' info_struct directly, it contains the image. */ + dp->write_size = 0U; + start_timer(dp, PNGCP_TIME_WRITE); + png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/); + end_timer(dp, PNGCP_TIME_WRITE); + + /* Make sure the file was written ok: */ + if (dp->fp != NULL) + { + FILE *fp = dp->fp; + dp->fp = NULL; + if (fclose(fp)) + display_log(dp, APP_ERROR, "%s: write failed (%s)", + destname == NULL ? "stdout" : destname, strerror(errno)); + } + + /* Clean it on the way out - if control returns to the caller then the + * written_file contains the required data. + */ + display_clean_write(dp); + dp->operation = "none"; +} + +static void +set_windowBits_hi(struct display *dp) +{ + /* windowBits is in the range 8..15 but zlib maps '8' to '9' so it is only + * worth using if the data size is 256 byte or less. + */ + int wb = MAX_WBITS; /* for large images */ + int i = VLSIZE(windowBits_IDAT); + + while (wb > 8 && dp->size <= 1U<<(wb-1)) --wb; + + while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break; + + assert(i > 1); /* vl_windowBits_IDAT always has a RANGE() */ + VLNAME(windowBits_IDAT)[i].value = wb; + + assert(VLNAME(windowBits_IDAT)[--i].name == range_lo); + VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8; + + /* If wb == 8 then any search has been restricted to just one windowBits + * entry. Record that here to avoid producing a spurious app-level warning + * above. + */ + if (wb == 8) + dp->min_windowBits = OPTIND(dp, windowBits); +} + +static int +better_options(const struct display *dp) +{ + /* Are these options better than the best found so far? Normally the + * options are tested in preference order, best first, however when doing a + * search operation on a range the range values are tested out of order. In + * that case preferable options will get tested later. + * + * This function looks through the stack from the bottom up looking for an + * option that does not match the current best value. When it finds one it + * checks to see if it is more or less desireable and returns true or false + * as appropriate. + * + * Notice that this means that the order options are pushed onto the stack + * conveys a priority; lower/earlier options are more important than later + * ones. + */ + unsigned int sp; + + for (sp=0; sp<dp->csp; ++sp) + { + int c = compare_option(dp, sp); + + if (c < 0) + return 0; /* worse */ + + else if (c > 0) + return 1; /* better */ + } + + assert(0 && "unreached"); +} + +static void +print_search_results(struct display *dp) +{ + assert(dp->filename != NULL); + printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu with '%s'\n", + dp->filename, (unsigned long)dp->w, (unsigned long)dp->h, dp->bpp, + cts(dp->ct), (unsigned long)dp->size, (unsigned long)dp->read_size, + (unsigned long)dp->best_size, dp->best); + fflush(stdout); +} + +static void +log_search(struct display *dp, unsigned int log_depth) +{ + /* Log, and reset, the search so far: */ + if (dp->nsp/*next entry to change*/ <= log_depth) + { + print_search_results(dp); + /* Start again with this entry: */ + dp->best_size = MAX_SIZE; + } +} + +static void +cp_one_file(struct display *dp, const char *filename, const char *destname) +{ + unsigned int log_depth; + + dp->filename = filename; + dp->operation = "read"; + dp->no_warnings = 0; + + /* Read it then write it: */ + if (filename != NULL && access(filename, R_OK) != 0) + display_log(dp, USER_ERROR, "%s: invalid file name (%s)", + filename, strerror(errno)); + + read_png(dp, filename); + + /* But 'destname' may be a directory. */ + dp->operation = "write"; + + /* Limit the upper end of the windowBits range for this file */ + set_windowBits_hi(dp); + + /* For logging, depth to log: */ + { + int val; + + if (get_option(dp, "log-depth", &val) && val >= 0) + log_depth = (unsigned int)/*SAFE*/val; + + else + log_depth = 0U; + } + + if (destname != NULL) /* else stdout */ + { + if (isdir(dp, destname)) + { + makename(dp, destname, filename); + destname = dp->namebuf; + } + + else if (access(destname, W_OK) != 0 && errno != ENOENT) + display_log(dp, USER_ERROR, "%s: invalid output name (%s)", destname, + strerror(errno)); + } + + dp->nsp = 0; + dp->curr[0] = 0; /* acts as a flag for the caller */ + dp->opt_string_start = 0; + dp->best[0] = 0; /* safety */ + dp->best_size = MAX_SIZE; + write_png(dp, destname); + + /* Initialize the 'best' fields: */ + strcpy(dp->best, dp->curr); + dp->best_size = dp->write_size; + + if (dp->nsp > 0) /* interating over lists */ + { + char *tmpname, tmpbuf[(sizeof dp->namebuf) + 4]; + assert(dp->curr[0] == ' ' && dp->tsp > 0); + + /* Cancel warnings on subsequent writes */ + log_search(dp, log_depth); + dp->no_warnings = 1; + + /* Make a temporary name for the subsequent tests: */ + if (destname != NULL) + { + strcpy(tmpbuf, destname); + strcat(tmpbuf, ".tmp"); /* space for .tmp allocated above */ + tmpname = tmpbuf; + } + + else + tmpname = NULL; /* stdout */ + + /* Loop to find the best option. */ + do + { + write_png(dp, tmpname); + + /* And compare the sizes (the write function makes sure write_size + * doesn't overflow.) + */ + assert(dp->csp > 0); + + if (dp->write_size < dp->best_size || + (dp->write_size == dp->best_size && better_options(dp))) + { + if (destname != NULL && rename(tmpname, destname) != 0) + display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname, + destname, strerror(errno)); + + strcpy(dp->best, dp->curr); + dp->best_size = dp->write_size; + } + + else if (tmpname != NULL && unlink(tmpname) != 0) + display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname, + strerror(errno)); + + log_search(dp, log_depth); + } + while (dp->nsp > 0); + + /* Do this for the 'sizes' option so that it reports the correct size. */ + dp->write_size = dp->best_size; + } +} + +static int +cppng(struct display *dp, const char *file, const char *gv dest) + /* Exists solely to isolate the setjmp clobbers which some versions of GCC + * erroneously generate. + */ +{ + int ret = setjmp(dp->error_return); + + if (ret == 0) + { + dp->errset = 1; + cp_one_file(dp, file, dest); + dp->errset = 0; + return 0; + } + + else + { + dp->errset = 0; + + if (ret < ERRORS) /* shouldn't longjmp on warnings */ + display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret); + + return ret; + } +} + +int +main(const int argc, const char * const * const argv) +{ + /* For each file on the command line test it with a range of transforms */ + int option_end; + struct display d; + + display_init(&d); + + d.operation = "options"; + for (option_end = 1; + option_end < argc && opt_check(&d, argv[option_end]); + ++option_end) + { + } + + /* Do a quick check on the directory target case; when there are more than + * two arguments the last one must be a directory. + */ + if (!(d.options & NOWRITE) && option_end+2 < argc && !checkdir(argv[argc-1])) + { + fprintf(stderr, + "pngcp: %s: directory required with more than two arguments\n", + argv[argc-1]); + return 99; + } + + { + int errors = 0; + int i = option_end; + + /* Do this at least once; if there are no arguments stdin/stdout are used. + */ + d.operation = "files"; + do + { + const char *infile = NULL; + const char *outfile = NULL; + int ret; + + if (i < argc) + { + infile = argv[i++]; + if (!(d.options & NOWRITE) && i < argc) + outfile = argv[argc-1]; + } + + ret = cppng(&d, infile, outfile); + + if (ret) + { + if (ret > QUIET) /* abort on user or internal error */ + return 99; + + /* An error: the output is meaningless */ + } + + else if (d.best[0] != 0) + { + /* This result may already have been output, in which case best_size + * has been reset. + */ + if (d.best_size < MAX_SIZE) + print_search_results(&d); + } + + else if (d.options & SIZES) + { + printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu [0x%lx]\n", + infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp, + cts(d.ct), (unsigned long)d.size, (unsigned long)d.read_size, + (unsigned long)d.write_size, (unsigned long)d.results); + fflush(stdout); + } + + /* Here on any return, including failures, except user/internal issues + */ + { + const int pass = (d.options & STRICT) ? + RESULT_STRICT(d.results) : RESULT_RELAXED(d.results); + + if (!pass) + ++errors; + + if (d.options & LOG) + { + int j; + + printf("%s: pngcp", pass ? "PASS" : "FAIL"); + + for (j=1; j<option_end; ++j) + printf(" %s", argv[j]); + + if (infile != NULL) + printf(" %s", infile); + +# ifdef PNG_PNGCP_TIMING_SUPPORTED + /* When logging output the files for each file, if enabled. */ + if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0) + print_time(" read", d.read_time); + + if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0) + print_time(" write", d.write_time); +# endif /* PNGCP_TIMING */ + + printf("\n"); + fflush(stdout); + } + } + + display_clean(&d); + } + while (i+!(d.options & NOWRITE) < argc); + /* I.e. for write cases after the first time through the loop require + * there to be at least two arguments left and for the last one to be a + * directory (this was checked above). + */ + + /* Release allocated memory */ + display_destroy(&d); + +# ifdef PNG_PNGCP_TIMING_SUPPORTED + { + int output = 0; + + if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0) + print_time("read", d.read_time_total), output = 1; + + if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0) + { + if (output) putchar(' '); + print_time("write", d.write_time_total); + output = 1; + } + + if (output) putchar('\n'); + } +# endif /* PNGCP_TIMING */ + + return errors != 0; + } +} +#else /* !READ_PNG || !WRITE_PNG */ +int +main(void) +{ + fprintf(stderr, "pngcp: no support for png_read/write_image\n"); + return 77; +} +#endif /* !READ_PNG || !WRITE_PNG */ diff --git a/Build/source/libs/libpng/libpng-src/contrib/tools/reindent b/Build/source/libs/libpng/libpng-src/contrib/tools/reindent new file mode 100644 index 00000000000..f4df309b668 --- /dev/null +++ b/Build/source/libs/libpng/libpng-src/contrib/tools/reindent @@ -0,0 +1,25 @@ +#!/bin/sh + +# reindent a libpng C source + +# COPYRIGHT: Written by Glenn Randers-Pehrson, 2016. +# To the extent possible under law, the author has waived all copyright and +# related or neighboring rights to this work. This work is published from: +# United States. + +# Usage: +# reindent inputtabsize outputtabsize inputcontinuestring outputcontinuestring +# +# Assumes that continued lines begin with indentation plus one space, and +# that continued comments begin with indentation plus " *". +# +# eg, to change libpng coding style from 3-space indentation with 4-space +# continuations to 4-space indentation with 2-space continuations: +# +# reindent 3 4 "\t " " " < example.c > example.c_4_2 +# and to restore the file back to libpng coding style +# reindent 4 3 " " " " < example.c_4_2 > example.c_3_4 + +unexpand --first-only --t $1 | \ + sed -e "/^ *$3[^\*]/{s/$3/$4/}" | \ + expand -t $2 |