blob: 22c87c43a7c4febf70f4e9229cf5395a3ab4c1ab [file] [log] [blame]
/* pngcrush.c - recompresses png files
* Copyright (C) 1998-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)
*
* The most recent version of pngcrush can be found at SourceForge in
* http://pmt.sf.net/pngcrush/
*
* This program reads in a PNG image, and writes it out again, with the
* optimum filter_method and zlib_level. It uses brute force (trying
* filter_method none, and libpng adaptive filtering, with compression
* levels 3 and 9). It does the most time-consuming method last in case
* it turns out to be the best.
*
* Optionally, it can remove unwanted chunks or add gAMA, sRGB, bKGD,
* tEXt/zTXt, and tRNS chunks. It will remove some chunks such as gAMA,
* cHRM, pHYs, and oFFs when their data fields contain all zero, which is a
* mistake.
*
* Uses libpng and zlib. This program was based upon libpng's pngtest.c.
*
* Thanks to Greg Roelofs for various bug fixes, suggestions, and
* occasionally creating Linux executables.
*
* Thanks to Stephan Levavej for some helpful suggestions about gcc compiler
* options and for a suggestion to increase the Z_MEM_LEVEL from default.
*
*/
#define PNGCRUSH_VERSION "1.5.8"
/*
#define PNGCRUSH_COUNT_COLORS
*/
/*
* COPYRIGHT NOTICE, DISCLAIMER, AND LICENSE:
*
* If you have modified this source, you may insert additional notices
* immediately after this sentence.
*
* Copyright (C) 1998-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)
*
* The pngcrush computer program is supplied "AS IS". The Author disclaims all
* warranties, expressed or implied, including, without limitation, the
* warranties of merchantability and of fitness for any purpose. The
* Author assumes no liability for direct, indirect, incidental, special,
* exemplary, or consequential damages, which may result from the use of
* the computer program, even if advised of the possibility of such damage.
* There is no warranty against interference with your enjoyment of the
* computer program or against infringement. There is no warranty that my
* efforts or the computer program will fulfill any of your particular purposes
* or needs. This computer program is provided with all faults, and the entire
* risk of satisfactory quality, performance, accuracy, and effort is with
* the user.
*
* Permission is hereby irrevocably granted to everyone to use, copy, modify,
* and distribute this source code, or portions hereof, for any purpose,
* without payment of any 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, disclaimer, and license may not be removed
* or altered from any source or altered source distribution.
*/
/* Change log:
*
* Version 1.5.8 (built with libpng-1.2.1)
*
* Added -trns_a option for entering a tRNS array.
*
* Version 1.5.7 (built with libpng-1.2.0)
*
* Added setargv.obj to Makefile.msc to expand wildcards, e.g., *.png
*
* Use constant string "pngcrush" instead of argv[0] when appropriate.
*
* Only check stats for infile==outfile once per input file, or not at all
* if "-nofilecheck" option is present or if a directory was created.
*
* Fixed bugs with changing bit_depth of grayscale images.
*
* Version 1.5.6 (built with libpng-1.0.12)
*
* Eliminated extra "Removed the cHNK chunk" messages generated by version
* 1.5.5 when "-rem alla" or "-rem allb" is used.
*
* All unknown chunks including safe-to-copy chunks are now removed in
* response to the "-rem alla" or "-rem allb" options.
*
* Issue a warning if the user tries "-cc" option when it is not supported.
*
* Version 1.5.5 (built with libpng-1.0.12)
*
* Reset reduce_to_gray and it_is_opaque flags prior to processing each
* image.
*
* Enable removal of safe-to-copy chunks that are being handled as unknown
* e.g., "-rem time".
*
* Version 1.5.4 (built with libpng-1.0.11)
*
* Added 262 to the length of uncompressed data when calculating
* required_window_size, to account for zlib/deflate implementation.
*
* Added "-bit_depth n" to the help screen.
*
* Call png_set_packing() when increasing bit_depth to 2 or 4.
*
* Added warning about not overwriting an existing tRNS chunk.
*
* Reduced the memory usage
*
* Write 500K IDAT chunks even when system libpng is being used.
*
* Ignore all-zero cHRM chunks, with a warning.
*
* Version 1.5.3 (built with libpng-1.0.9beta5)
*
* Added "-loco" option (writes MNG files with filter_method 64)
*
* "-dir" and "-ext" options are no longer mutually exclusive, e.g.:
* pngcrush -loco -dir Crushed -ext .mng *.png
*
* Version 1.5.2 (built with libpng-1.0.9beta1)
*
* Added "-iccp" option.
*
* Increased the zlib memory level, which improves compression (typically
* about 1.3 percent for photos) at the expense of increased memory usage.
*
* Enabled the "-max max_idat_size" option, even when max_idat_size
* exceeds the default 1/2 megabyte size.
*
* Added missing "png_ptr" argument to png_error() call
*
* Added "-loco" option, to enable the LOCO color transformation
* (R->R-G, G, B->B-G) while writing a MNG with filter_method 64. Undo
* the transformation and write the regular PNG filter_method (0) if the
* MNG filter_method 64 is detected.
*
* Revised the "-help" output slightly and improved the "-version" output.
*
* The "-already[_crushed]" option is now ignored if the "-force" option
* is present or if chunks are being added, deleted, or modified.
*
* Improved "things_have_changed" behavior (now, when set in a particular
* file, it is not set for all remaining files)
*
* Version 1.5.1 (built with libpng-1.0.8)
*
* Disabled color counting by default and made it controllable with new
* -cc and -no_cc commandline arguments.
*
* Added some #ifdef PNGCRUSH_COUNT_COLORS around code that needs it.
*
* Revised count_colors() attempting to avoid stack corruption that has
* been observed on RedHat 6.2
*
* Added the word "irrevocably" to the license and changed "without fee"
* to "without payment of any fee".
*
* Version 1.5.0 (built with libpng-1.0.8)
*
* After encountering an image with a bad Photoshop iCCP chunk, pngcrush
* 1.4.5 through 1.4.8 write sRGB and gAMA=45455 chunks in all
* remaining PNG files on the command line. This has been fixed so the
* correction is only applied to the particular bad input file.
*
* Version 1.4.8 (built with libpng-1.0.8rc1)
*
* Detect and remove all-opaque alpha channel.
* Detect and reduce all-gray truecolor images to grayscale.
*
* Version 1.4.7 (built with libpng-1.0.8rc1)
*
* Restored the "-ext" option that was inadvertently overridden with
* a new "-exit" option in version 1.4.6 ("-exit" is used to force an
* "exit" instead of a "return" from the main program).
*
* Version 1.4.6 (built with libpng-1.0.8rc1)
*
* Fixed bug in color-counting of noninterlaced images.
*
* Added capability of processing multiple rows at a time (disabled by
* default because it turns out to be no faster).
*
* Replaced "return" statements in main() with "exit" statements.
* Force exit instead of return with "-exit" argument.
*
* Added the UCITA disclaimers to the help output.
*
* Version 1.4.5 (built with libpng-1.0.7rc2 and cexcept-1.0.0)
*
* Added color-counting and palette-building capability (enable by
* defining PNGCRUSH_COUNT_COLORS). In a future version, this will
* give pngcrush the ability to reduce RGBA images to indexed-color
* or grayscale when fewer than 257 RGBA combinations are present,
* and no color is present that requires 16-bit precision. For now,
* it only reports the frequencies.
*
* Added "-fix" option, for fixing bad CRC's and other correctable
* conditions.
*
* Write sBIT.alpha=1 when adding an opaque alpha channel and sBIT
* is present.
*
* Identify the erroneous 2615-byte sRGB monitor profile being written
* by Photoshop 5.5, which causes many apps to crash, and replace it with
* an sRGB chunk.
*
* Added a check for input and output on different devices before rejecting
* the output file as being the same as the input file based on inode.
*
* Added some UCITA language to the disclaimer.
*
* Version 1.4.4 (built with libpng-1.0.6i and cexcept-0.6.3)
*
* Can be built on RISC OS platforms, thanks to Darren Salt.
*
* Version 1.4.3 (built with libpng-1.0.6h and cexcept-0.6.3)
*
* Reduced scope of Try/Catch blocks to avoid nesting them, and
* removed returns from within the Try blocks, where they are not
* allowed.
*
* Removed direct access to the png structure when possible, and isolated
* the remaining direct accesses to the png structure into new
* png_get_compression_buffer_size(), png_set_compression_buffer_size(),
* and png_set_unknown_chunk_location() functions that were installed
* in libpng version 1.0.6g.
*
* Version 1.4.2 (built with libpng-1.0.6f and cexcept-0.6.0)
*
* Removes extra IDAT chunks (such as found in some POV-ray PNGs) with
* a warning instead of bailing out (this feature requires libpng-1.0.6f
* or later, compiled with "#define PNG_ABORT()").
*
* Removed old setjmp interface entirely.
*
* Version 1.4.1 (built with libpng-1.0.6e and cexcept-0.6.0)
*
* Uses cexcept.h for error handling instead of libpng's built-in
* setjmp/longjmp mechanism. See http://cexcept.sf.net/
*
* Pngcrush.c will now run when compiled with old versions of libpng back
* to version 0.96, although some features will not be available.
*
* Version 1.4.0 (built with libpng-1.0.6 + libpng-1.0.6-patch-a)
*
* Version 1.3.6 (built with libpng-1.0.5v)
*
* RGB to Grayscale conversion is more accurate (15-bit instead of 8-bit)
* and now uses only integer arithmetic.
*
* #ifdef'ed out PNG_READ_DITHER
*
* Changed "Compressed" to "Uncompressed" in help for -itxt.
*
* Stifled some compiler warnings
*
* Version 1.3.5 (built with libpng-1.0.5s)
*
* Add test on stat_buf.st_size to verify fpin==fpout, because stat in
* MSVC++6.0 standard version returns stat_buf.st_ino=0 for all files.
*
* Revised pngcrush.h to make it easier to control PNG_ZBUF_SIZE and
* PNG_NO_FLOATING_POINT_SUPPORTED from a makefile.
*
* Restored ability to enter "replace_gamma" value as a float even when
* floating point arithmetic is not enabled.
*
* Enabled removing tEXt, zTXt, or iTXt chunks by chunk type, i.e.,
* "-rem tEXt" only removes tEXt chunks, while "-rem text" removes all
* three types of text chunk.
*
* Removed definition of TOO_FAR from pngcrush.h
*
* Uses new libpng error handler; if a file has errors, pngcrush now will
* continue on and compress the remaining files instead of bailing out.
*
* Version 1.3.4 (built with libpng-1.0.5m)
*
* Do not allow pngcrush to overwrite the input file.
*
* Version 1.3.3 (built with libpng-1.0.5m)
*
* Restored ability to enter gamma as a float even when floating point
* arithmetic is not enabled.
*
* Version 1.3.2 (built with libpng-1.0.5k)
*
* Renamed "dirname" to "directory_name" to avoid conflict with "dirname"
* that appears in string.h on some platforms.
*
* Fixed "PNG_NO_FLOAING_POINT" typo in pngcrush.h
*
* #ifdef'ed out parts of the help screen for options that are unsupported.
*
* Version 1.3.1 (built with libpng-1.0.5k): Eliminated some spurious warnings
* that were being issued by libpng-1.0.5j. Added -itxt, -ztxt, and
* -zitxt descriptions to the help screen.
*
* Dropped explicit support for pCAL, hIST, sCAL, sPLT, iCCP, tIME, and
* cHRM chunks and handle them as unknown but safe-to-copy instead, using
* new png_handle_as_unknown function available in libpng-1.0.5k.
*
* Version 1.3.0 (built with libpng-1.0.5j): Added support for handling
* unknown chunks.
*
* pngcrush is now fixed-point only, unless PNG_NO_FLOATING_POINT_SUPPORTED
* is undefined in pngcrush.h.
*
* Added support for the iCCP, iTXt, sCAL, and sPLT chunks, which
* are now supported by libpng (since libpng-1.0.5j). None of these have
* been adequately tested.
*
* #ifdef'ed out more unused code (weighted filters and progressive read;
* this saves about 15k in the size of the executable).
*
* Moved the special definitions from pngconf.h into a new pngcrush.h
*
* Disallow 256-byte compression window size when writing, to work around
* an apparent zlib bug. Either deflate was producing incorrect results in a
* 21x21 4-bit image or inflate was decoding it incorrectly; the uncompressed
* stream is 252 bytes, which is uncomfortably close to the resulting
* 256-byte compression window. This workaround can be removed when zlib
* is fixed.
*
* The "-m method" can be used any of the 124 methods, without having to
* specify the filter, level, and strategy, instead of just the first 10.
*
* Version 1.2.1 (built with libpng-1.0.5f): Fixed -srgb parameter so it
* really does take an argument, and so it continues to use "0" if an
* integer does not follow the -srgb.
*
* Added "-plte_len n" argument for truncating the PLTE. Be sure not to
* truncate it to less than the greatest index actually appearing in IDAT.
*
* Version 1.2.0: Removed registration requirement. Added open source
* license. Redefined TOO_FAR=32k in deflate.c.
*
* Changes prior to going "open source":
*
* Version 1.1.8: built with libpng-1.0.5a. Runs OK with pngvcrd.c.
*
* Version 1.1.7: added ability to add tEXt/zTXt chunks. Fixed bug with
* closing a file that wasn't opened when using "pngcrush -n". Fixed
* bug with tEXt/zTXt chunks after IDAT not being copied.
* Added alpha to the displayed palette table. Rebuilt with libpng-1.0.5.
*
* Version 1.1.6: fixed bug with one file left open after each image is
* processed
*
* Version 1.1.5: Shorten or remove tRNS chunks that are all opaque or have
* opaque entries at the end. Added timing report.
*
* Version 1.1.4: added ability to restrict brute_force to one or more filter
* types, compression levels, or compression strategies.
*/
/* To do:
*
* Reset CINFO to reflect decoder's required window size (instead of
* libz-1.1.3 encoder's required window size, which is 262 bytes larger).
* See discussion about zlib in png-list archives for April 2001.
*
* Add a "pcRu" ancillary chunk that keeps track of the best method,
* methods already tried, and whether "loco crushing" was effective.
*
* Try both transformed and untransformed colors when "-loco" is used.
*
* Check for unused alpha channel and ok-to-reduce-depth.
* Take care that sBIT and bKGD data aren't lost when reducing images
* from truecolor to grayscale.
*
* Rearrange palette to put most-used color first and transparent color
* second (see ImageMagick 5.1.1 and later).
*
* Finish pplt (partial palette) feature.
*
* Use an alternate write function for the trial passes, that
* simply counts bytes rather than actually writing to a file, to save wear
* and tear on disk drives.
*
* Allow in-place file replacement or as a filter, as in
* "pngcrush -overwrite file.png"
* "pngcreator | pngcrush > output.png"
*
* Remove text-handling and color-handling features and put
* those in a separate program or programs, to avoid unnecessary
* recompressing.
*
* Move the Photoshop-fixing stuff into a separate program.
*
* add "-time" directive
*/
#define PNG_INTERNAL
#include "png.h"
/* we don't need the some of the extra libpng transformations
* so they are ifdef'ed out in a special version of pngconf.h, which
* includes pngcrush.h and is included by png.h */
/* defined so I can write to a file on gui/windowing platforms */
/* #define STDERR stderr */
#define STDERR stdout /* for DOS */
#ifndef PNGCRUSH_LIBPNG_VER
# define PNGCRUSH_LIBPNG_VER PNG_LIBPNG_VER
#endif
#if PNGCRUSH_LIBPNG_VER != PNG_LIBPNG_VER
int
main()
{
printf("Version numbers in pngcrush.h (%d) and png.h (%d) do not match\n",
PNGCRUSH_LIBPNG_VER, PNG_LIBPNG_VER);
}
#else
#if PNG_LIBPNG_VER < 96
int
main()
{
printf("Sorry, but pngcrush needs libpng version 0.96 or later\n");
printf("You have built pngcrush with libpng version %s\n",
PNG_LIBPNG_VER_STRING);
}
#else
#ifdef PNG_MNG_FEATURES_SUPPORTED
# define PNGCRUSH_LOCO
#endif
#if defined(__DJGPP__)
# if ((__DJGPP__ == 2) && (__DJGPP_MINOR__ == 0))
# include <libc/dosio.h> /* for _USE_LFN, djgpp 2.0 only */
# endif
# define SLASH "\\"
# define DOT "."
#else
# ifdef __riscos
# define SLASH "."
# define DOT "/"
# else
# define SLASH "/"
# define DOT "."
# endif
#endif
#if !defined(__TURBOC__) && !defined(_MSC_VER) && !defined(_MBCS) && \
!defined(__riscos)
# include <unistd.h>
#endif
#ifndef __riscos
# include <sys/types.h>
# include <sys/stat.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#if defined(_MBCS) || defined(WIN32) || defined(__WIN32__)
# include <direct.h>
#endif
#define DEFAULT_MODE 0
#define DIRECTORY_MODE 1
#define EXTENSION_MODE 2
#define DIREX_MODE 3
#define FOPEN(file, how) fopen(file, how)
#define FCLOSE(file) {fclose(file); file=NULL;--number_of_open_files;};
#define P0 if(first_trial && verbose > 0)printf
#define P1 if(verbose > 1)printf
#define P2 if(verbose > 2)printf
#if (PNG_LIBPNG_VER > 95)
/* so we can load pngcrush with pre-1.0.6 versions of libpng */
/* for version 1.0.2 and earlier */
#ifndef PNG_MAX_UINT
#define PNG_MAX_UINT 2147483647L
#endif
/* for version 0.89c and earlier */
#ifndef PNG_TEXT_COMPRESSION_NONE
#define PNG_TEXT_COMPRESSION_NONE -1
#define PNG_TEXT_COMPRESSION_zTXt 0
#endif
#ifndef png_debug
#define png_debug(l, m)
#endif
#ifndef png_debug1
#define png_debug1(l, m, p1)
#endif
#ifndef png_debug2
#define png_debug2(l, m, p1, p2)
#endif
#if (PNG_LIBPNG_VER < 10006)
/* These shorter macros weren't defined until version 1.0.6 */
#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_WRITE_gAMA_SUPPORTED)
#define PNG_gAMA_SUPPORTED
#endif
#if defined(PNG_READ_pHYs_SUPPORTED) || defined(PNG_WRITE_pHYs_SUPPORTED)
#define PNG_pHYs_SUPPORTED
#endif
#if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED)
#define PNG_sRGB_SUPPORTED
#endif
#if defined(PNG_READ_cHRM_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED)
#define PNG_cHRM_SUPPORTED
#endif
#if defined(PNG_READ_hIST_SUPPORTED) || defined(PNG_WRITE_hIST_SUPPORTED)
#define PNG_hIST_SUPPORTED
#endif
#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED)
#define PNG_pCAL_SUPPORTED
#endif
#if defined(PNG_READ_tIME_SUPPORTED) || defined(PNG_WRITE_tIME_SUPPORTED)
#define PNG_tIME_SUPPORTED
#endif
#if defined(PNG_READ_tRNS_SUPPORTED) || defined(PNG_WRITE_tRNS_SUPPORTED)
#define PNG_tRNS_SUPPORTED
#endif
#endif
#endif
#ifdef __TURBOC__
#include <mem.h>
#endif
#if CLOCKS_PER_SEC <= 100
# define TIME_T long
#else
# define TIME_T float
#endif
/* input and output filenames */
static PNG_CONST char *progname = "pngtest" DOT "exe";
static PNG_CONST char *inname = "pngtest" DOT "png";
static PNG_CONST char *outname = "pngout" DOT "png";
static PNG_CONST char *directory_name = "pngcrush" DOT "bak";
static PNG_CONST char *extension = "_C" DOT "png";
static png_uint_32 width, height;
static png_uint_32 measured_idat_length;
static int pngcrush_must_exit=0;
static int all_chunks_are_safe=0;
static int number_of_open_files;
static int do_pplt = 0;
#ifdef PNGCRUSH_MULTIPLE_ROWS
static png_uint_32 max_rows_at_a_time=1;
static png_uint_32 rows_at_a_time;
#endif
char pplt_string[1024];
char *ip, *op, *dot;
char in_string[256];
char prog_string[256];
char out_string[256];
char in_extension[256];
static int text_inputs=0;
int text_where[10]; /* 0: no text; 1: before PLTE; 2: after PLTE */
int text_compression[10]; /* -1: uncompressed tEXt; 0: compressed zTXt
1: uncompressed iTXt; 2: compressed iTXt */
char text_text[20480]; /* It would be nice to png_malloc this, but we don't
* have a png_ptr yet when we need it. */
char text_keyword[800];
#ifdef PNG_iTXt_SUPPORTED
char text_lang[800];
char text_lang_key[800];
#endif
#ifdef PNG_iCCP_SUPPORTED
int iccp_length = 0;
char *iccp_text;
char *iccp_file;
char iccp_name[80];
#endif
int best;
char buffer[256];
char *str_return;
/* Set up the "cexcept" Try/Throw/Catch exception handler. */
#include "cexcept.h"
define_exception_type(const char *);
extern struct exception_context the_exception_context[1];
struct exception_context the_exception_context[1];
#if (PNG_LIBPNG_VER > 96)
png_const_charp msg;
#else
const char *msg;
#endif
static png_uint_32 total_input_length = 0;
static png_uint_32 total_output_length = 0;
static int pngcrush_mode = DEFAULT_MODE;
static int resolution = 0;
static int remove_chunks = 0;
static int output_color_type;
static int output_bit_depth;
static int force_output_color_type=8;
static int force_output_bit_depth=0;
static int input_color_type;
static int input_bit_depth;
static int trial;
static int first_trial=0;
static int verbose=1;
static int help=0;
static int fix=0;
static int things_have_changed=0;
static int global_things_have_changed=0;
static int default_compression_window=15;
static int force_compression_window=0;
static int compression_mem_level=9;
static int final_method=0;
static int brute_force=0;
static int brute_force_level=0;
static int brute_force_filter=0;
static int brute_force_strategy=0;
static int brute_force_levels[10]={1,1,1,1,1,1,1,1,1,1};
static int brute_force_filters[6]={1,1,1,1,1,1};
static int brute_force_strategies[3]={1,1,1};
static int method=10;
static int pauses=0;
static int nosave=0;
static int nofilecheck=0;
static png_bytep row_buf;
#ifdef PNGCRUSH_MULTIPLE_ROWS
static png_bytepp row_pointers;
#endif
static int z_strategy;
static int best_of_three;
static int methods_specified=0;
static int intent=-1;
static int plte_len=-1;
#ifdef PNG_gAMA_SUPPORTED
# ifdef PNG_FIXED_POINT_SUPPORTED
static int specified_gamma=0;
static int image_specified_gamma=0;
static int force_specified_gamma=0;
# else
static double specified_gamma=0.0;
static double image_specified_gamma=0;
static double force_specified_gamma=0.0;
# endif
static int double_gamma=0;
#endif
static int names;
#ifdef PNG_tRNS_SUPPORTED
static int have_trns=0;
static png_uint_16 trns_index=0;
static png_uint_16 trns_red=0;
static png_uint_16 trns_green=0;
static png_uint_16 trns_blue=0;
static png_uint_16 trns_gray=0;
#endif
static png_byte trns_array[256];
static png_byte trans_in[256];
static png_uint_16 num_trans_in;
static int have_bkgd=0;
static png_uint_16 bkgd_red=0;
static png_uint_16 bkgd_green=0;
static png_uint_16 bkgd_blue=0;
static png_colorp palette;
static int num_palette;
#ifdef REORDER_PALETTE
static png_byte palette_reorder[256];
#endif
static png_structp read_ptr, write_ptr;
static png_infop read_info_ptr, write_info_ptr;
static png_infop end_info_ptr;
static png_infop write_end_info_ptr;
static FILE *fpin, *fpout;
png_uint_32 measure_idats(FILE *fpin);
#ifdef PNGCRUSH_LOCO
static int do_loco=0;
static int input_format=0; /* 0: PNG 1: MNG */
static int output_format=0;
#endif
static int do_color_count;
static int reduction_ok=0;
#ifdef PNGCRUSH_COUNT_COLORS
int count_colors(FILE *fpin);
static int num_rgba, reduce_to_gray, it_is_opaque;
#endif
png_uint_32 png_measure_idat(png_structp png_ptr);
# define MAX_METHODS 200
# define MAX_METHODSP1 201
# define DEFAULT_METHODS 10
static png_uint_32 idat_length[MAX_METHODSP1];
static int filter_type, zlib_level;
static png_bytep png_row_filters=NULL;
static TIME_T t_start, t_stop, t_decode, t_encode, t_misc;
static png_uint_32 max_idat_size = 524288L; /* increases the IDAT size */
static png_uint_32 crushed_idat_size = 0x3ffffffL;
static int already_crushed = 0;
int ia;
/********* Functions to make direct access to the png_ptr. ***************
*
* Making direct access to the png_ptr or info_ptr is frowned upon because
* it incurs a risk of binary incompatibility with otherwise compatible
* versions of libpng, so all such accesses are collected here. These
* functions all exist in later versions of libpng.
*/
#if (PNG_LIBPNG_VER < 95)
png_uint_32
png_get_rowbytes(png_structp png_ptr, png_infop info_ptr)
{
return ((png_ptr && info_ptr) ? info_ptr->rowbytes : 0);
}
#endif /* (PNG_LIBPNG_VER < 95) */
#if (PNG_LIBPNG_VER < 10007)
/* This is binary incompatible with versions earlier than 0.99h because of the
* introduction of png_user_transform stuff ahead of the zbuf_size member
* of png_ptr in libpng version 0.99h. Not needed after libpng-1.0.6g
* because the functions became available in libpng.
*/
static png_uint_32
png_get_compression_buffer_size(png_structp png_ptr)
{
if(png_ptr->zbuf_size != PNG_ZBUF_SIZE)
{
fprintf(STDERR, " png_ptr->zbuf_size = %d but PNG_ZBUF_SIZE is %d\n",
png_ptr->zbuf_size, PNG_ZBUF_SIZE);
fprintf(STDERR, " You may be using pngcrush with an incompatible version");
fprintf(STDERR, " of libpng. pngcrush was compiled with version %s\n",
PNG_LIBPNG_VER_STRING);
}
return(png_ptr->zbuf_size);
}
static void
png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
{
if(png_ptr->zbuf)
png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf=NULL;
png_ptr->zbuf_size = (png_size_t)size;
png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
if(!png_ptr->zbuf)
png_error(png_ptr,"Unable to malloc zbuf");
}
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static void
png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr,
int chunk, int location)
{
if(png_ptr && info_ptr && chunk >= 0 && chunk < info_ptr->unknown_chunks_num)
info_ptr->unknown_chunks[chunk].location = (png_byte)location;
}
#endif
#endif /* (PNG_LIBPNG_VER < 10007) */
/************* end of direct access functions *****************************/
/* cexcept interface */
static void
png_cexcept_error(png_structp png_ptr, png_const_charp msg)
{
if(png_ptr)
;
#if (PNG_LIBPNG_VER > 10006 && defined(PNGCRUSH_H))
if (!strcmp(msg, "Too many IDAT's found"))
{
#ifndef PNG_NO_CONSOLE_IO
fprintf(stderr, "\nIn %s, correcting ",inname);
#else
png_warning(png_ptr, msg);
#endif
}
else
#endif
{
Throw msg;
}
}
/* START of code to validate memory allocation and deallocation */
#ifdef PNG_USER_MEM_SUPPORTED
/* Allocate memory. For reasonable files, size should never exceed
64K. However, zlib may allocate more then 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.
This piece of code can be compiled to validate max 64K allocations
by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */
typedef struct memory_information {
png_uint_32 size;
png_voidp pointer;
struct memory_information FAR *next;
} memory_information;
typedef memory_information FAR *memory_infop;
static memory_infop pinformation = NULL;
static int current_allocation = 0;
static int maximum_allocation = 0;
extern PNG_EXPORT(png_voidp,png_debug_malloc) PNGARG((png_structp png_ptr,
png_uint_32 size));
extern PNG_EXPORT(void,png_debug_free) PNGARG((png_structp png_ptr,
png_voidp ptr));
png_voidp
png_debug_malloc(png_structp png_ptr, png_uint_32 size) {
/* png_malloc has already tested for NULL; png_create_struct calls
png_debug_malloc directly (with png_ptr == NULL prior to libpng-1.2.0
which is OK since we are not using a user mem_ptr) */
if (size == 0)
return (png_voidp)(NULL);
/* This calls the library allocator twice, once to get the requested
buffer and once to get a new free list entry. */
{
memory_infop pinfo = png_malloc_default(png_ptr, sizeof *pinfo);
pinfo->size = size;
current_allocation += size;
if (current_allocation > maximum_allocation)
maximum_allocation = current_allocation;
pinfo->pointer = png_malloc_default(png_ptr, size);
pinfo->next = pinformation;
pinformation = pinfo;
/* Make sure the caller isn't assuming zeroed memory. */
png_memset(pinfo->pointer, 0xdd, pinfo->size);
if(verbose > 2)
fprintf(STDERR, "Pointer %x allocated %lu bytes\n",
(int)pinfo->pointer, size);
return (png_voidp)(pinfo->pointer);
}
}
/* Free a pointer. It is removed from the list at the same time. */
void
png_debug_free(png_structp png_ptr, png_voidp ptr)
{
if (png_ptr == NULL)
fprintf(STDERR, "NULL pointer to png_debug_free.\n");
if (ptr == 0) {
#if 0 /* This happens all the time. */
fprintf(STDERR, "WARNING: freeing NULL pointer\n");
#endif
return;
}
/* Unlink the element from the list. */
{
memory_infop FAR *ppinfo = &pinformation;
for (;;) {
memory_infop pinfo = *ppinfo;
if (pinfo->pointer == ptr) {
*ppinfo = pinfo->next;
current_allocation -= pinfo->size;
if (current_allocation < 0)
fprintf(STDERR, "Duplicate free of memory\n");
/* We must free the list element too, but first kill
the memory that is to be freed. */
memset(ptr, 0x55, pinfo->size);
if(verbose > 2)
fprintf(STDERR, "Pointer %x freed %lu bytes\n", (int)ptr,
pinfo->size);
png_free_default(png_ptr, pinfo);
break;
}
if (pinfo->next == NULL) {
fprintf(STDERR, "Pointer %x not found\n", (int)ptr);
break;
}
ppinfo = &pinfo->next;
}
}
/* Finally free the data. */
png_free_default(png_ptr, ptr);
}
#endif /* PNG_USER_MEM_SUPPORTED */
/* END of code to test memory allocation/deallocation */
void png_crush_pause(void);
void png_crush_pause(void)
{
if(pauses > 0)
{
char keystroke;
fprintf(STDERR, "Press [ENTER] key to continue.\n");
keystroke=(char)getc(stdin);
keystroke = keystroke; /* stifle compiler warning */
}
}
#ifdef __riscos
/* The riscos/acorn support was contributed by Darren Salt. */
#include <kernel.h>
static int fileexists(const char *name)
{
# ifdef __acorn
int ret;
return _swix (8, 3 | 1<<31, 17, name, &ret) ? 0 : ret;
# else
_kernel_swi_regs r;
r.r[0] = 17; r.r[1] = (int) name;
return _kernel_swi(8, &r, &r) ? 0 : r.r[0];
# endif
}
static int filesize(const char *name)
{
# ifdef __acorn
int ret;
return _swix (8, 3 | 1<<27, 17, name, &ret) ? 0 : ret;
# else
_kernel_swi_regs r;
r.r[0] = 17; r.r[1] = (int) name;
return _kernel_swi(8, &r, &r) ? 0 : r.r[4];
# endif
}
static int mkdir(const char *name, int ignored)
{
# ifdef __acorn
_swi (8, 0x13, 8, name, 0);
return 0;
# else
_kernel_swi_regs r;
r.r[0] = 8; r.r[1] = (int) name;
r.r[4] = r.r[3] = r.r[2] = 0;
return (int)_kernel_swi(8 | 1<<31, &r, &r);
# endif
}
static void setfiletype(const char *name)
{
# ifdef __acorn
_swi (8, 7, 18, name, 0xB60);
# else
_kernel_swi_regs r;
r.r[0] = 18; r.r[1] = (int) name; r.r[2] = 0xB60;
_kernel_swi(8 | 1<<31, &r, &r);
# endif
}
#else /* !defined(__riscos) */
# define setfiletype(x)
#endif
int keep_unknown_chunk(png_const_charp name, char *argv[]);
int keep_unknown_chunk(png_const_charp name, char *argv[])
{
int i;
if(remove_chunks == 0) return 1;
for (i=1; i<=remove_chunks; i++)
{
if(!strncmp(argv[i],"-rem",4))
{
int alla = 0;
int allb = 0;
i++;
if(!strncmp(argv[i],"alla",4)) alla++; /* all ancillaries */
if(!strncmp(argv[i],"all",3)) allb++; /* all but gamma */
if(!strncmp(argv[i],name,4) ||
(!strncmp(name,"cHRM",4) && (!strncmp(argv[i],"chrm",4) || allb)) ||
(!strncmp(name,"gIFg",4) && (!strncmp(argv[i],"gifg",4) || allb)) ||
(!strncmp(name,"gIFt",4) && (!strncmp(argv[i],"gift",4) || allb)) ||
(!strncmp(name,"gIFx",4) && (!strncmp(argv[i],"gifx",4) || allb)) ||
(!strncmp(name,"hIST",4) && (!strncmp(argv[i],"hist",4) || allb)) ||
(!strncmp(name,"iCCP",4) && (!strncmp(argv[i],"iccp",4) || allb)) ||
(!strncmp(name,"pCAL",4) && (!strncmp(argv[i],"pcal",4) || allb)) ||
(!strncmp(name,"sCAL",4) && (!strncmp(argv[i],"scal",4) || allb)) ||
(!strncmp(name,"sPLT",4) && (!strncmp(argv[i],"splt",4) || allb)) ||
(!strncmp(name,"tIME",4) && (!strncmp(argv[i],"time",4) || allb)))
{
return 0;
}
}
}
return 1;
}
int keep_chunk(png_const_charp name, char *argv[]);
int keep_chunk(png_const_charp name, char *argv[])
{
int i;
if(verbose > 2 && first_trial)
fprintf(STDERR, " Read the %s chunk.\n", name);
if(remove_chunks == 0) return 1;
if(verbose > 1 && first_trial)
fprintf(STDERR, " Check for removal of the %s chunk.\n", name);
for (i=1; i<=remove_chunks; i++)
{
if(!strncmp(argv[i],"-rem",4))
{
int alla = 0;
int allb = 0;
i++;
if(!strncmp(argv[i],"alla",4)) alla++; /* all ancillaries */
if(!strncmp(argv[i],"all",3)) allb++; /* all but gamma */
if(!strncmp(argv[i],name,4) ||
(!strncmp(name,"PLTE",4) && (!strncmp(argv[i],"plte",4))) ||
(!strncmp(name,"bKGD",4) && (!strncmp(argv[i],"bkgd",4) || allb)) ||
(!strncmp(name,"cHRM",4) && (!strncmp(argv[i],"chrm",4) || allb)) ||
(!strncmp(name,"gAMA",4) && (!strncmp(argv[i],"gama",4) || alla)) ||
(!strncmp(name,"gIFg",4) && (!strncmp(argv[i],"gifg",4) || allb)) ||
(!strncmp(name,"gIFt",4) && (!strncmp(argv[i],"gift",4) || allb)) ||
(!strncmp(name,"gIFx",4) && (!strncmp(argv[i],"gifx",4) || allb)) ||
(!strncmp(name,"hIST",4) && (!strncmp(argv[i],"hist",4) || allb)) ||
(!strncmp(name,"iCCP",4) && (!strncmp(argv[i],"iccp",4) || allb)) ||
(!strncmp(name,"iTXt",4) && (!strncmp(argv[i],"itxt",4) || allb)) ||
(!strncmp(name,"iTXt",4) && (!strncmp(argv[i],"text",4) )) ||
(!strncmp(name,"oFFs",4) && (!strncmp(argv[i],"offs",4) || allb)) ||
(!strncmp(name,"pHYs",4) && (!strncmp(argv[i],"phys",4) || allb)) ||
(!strncmp(name,"pCAL",4) && (!strncmp(argv[i],"pcal",4) || allb)) ||
(!strncmp(name,"sBIT",4) && (!strncmp(argv[i],"sbit",4) || allb)) ||
(!strncmp(name,"sCAL",4) && (!strncmp(argv[i],"scal",4) || allb)) ||
(!strncmp(name,"sRGB",4) && (!strncmp(argv[i],"srgb",4) || allb)) ||
(!strncmp(name,"sPLT",4) && (!strncmp(argv[i],"splt",4) || allb)) ||
(!strncmp(name,"tEXt",4) && (!strncmp(argv[i],"text",4) || allb)) ||
(!strncmp(name,"tIME",4) && (!strncmp(argv[i],"time",4) || allb)) ||
(!strncmp(name,"tRNS",4) && (!strncmp(argv[i],"trns",4) )) ||
(!strncmp(name,"zTXt",4) && (!strncmp(argv[i],"ztxt",4) || allb)) ||
(!strncmp(name,"zTXt",4) && (!strncmp(argv[i],"text",4) )))
{
things_have_changed=1;
if(verbose > 0 && first_trial)
fprintf(STDERR, " Removed the %s chunk.\n", name);
return 0;
}
}
}
if(verbose > 1 && first_trial)
fprintf(STDERR, " Preserving the %s chunk.\n", name);
return 1;
}
void
show_result(void)
{
if(total_output_length)
{
if(total_input_length == total_output_length)
fprintf(STDERR,
" Overall result: no change\n");
else if(total_input_length > total_output_length)
fprintf(STDERR,
" Overall result: %4.2f%% reduction, %ld bytes\n",
(100.0 - (100.0*total_output_length)/total_input_length),
total_input_length - total_output_length);
else
fprintf(STDERR,
" Overall result: %4.2f%% increase, %ld bytes\n",
-(100.0 - (100.0*total_output_length)/total_input_length),
total_output_length - total_input_length);
}
t_stop = (TIME_T)clock();
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);
fprintf(STDERR," (decoding %.3f,\n",
t_decode/(float)CLOCKS_PER_SEC);
fprintf(STDERR," encoding %.3f,",
t_encode/(float)CLOCKS_PER_SEC);
fprintf(STDERR," other %.3f seconds)\n\n",
t_misc/(float)CLOCKS_PER_SEC);
#ifdef PNG_USER_MEM_SUPPORTED
if (current_allocation) {
memory_infop pinfo = pinformation;
fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n",
current_allocation);
while (pinfo != NULL) {
fprintf(STDERR, " %8lu bytes at %x\n", pinfo->size,
(int)pinfo->pointer);
free(pinfo->pointer);
pinfo = pinfo->next;
}
}
#endif
}
int
main(int argc, char *argv[])
{
png_uint_32 y;
int bit_depth, color_type;
int num_pass, pass;
int try_method[MAX_METHODSP1];
int fm[MAX_METHODSP1];
int lv[MAX_METHODSP1];
int zs[MAX_METHODSP1];
int ntrial;
int lev, strat, filt;
#ifdef PNG_gAMA_SUPPORTED
#ifdef PNG_FIXED_POINT_SUPPORTED
png_fixed_point file_gamma=0;
#else
double file_gamma=0.;
#endif
#endif
char *cp;
int i;
row_buf = (png_bytep)NULL;
number_of_open_files=0;
#ifdef PNGCRUSH_COUNT_COLORS
reduce_to_gray=0;
it_is_opaque=0;
#else
do_color_count = 0;
do_color_count = do_color_count; /* silence compiler warning */
#endif
if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING))
{
fprintf(STDERR,
"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);
}
t_start = (TIME_T)clock();
prog_string[0] = '\0';
str_return = strcat(prog_string,argv[0]);
progname = prog_string;
for(i=0, cp=prog_string; *cp!='\0'; i++, cp++)
{
#ifdef __riscos
if(*cp == '.' || *cp == ':') progname = ++cp;
#else
if(*cp == '\\' || *cp == '/') progname = ++cp;
if(*cp == '.') *cp='\0';
#endif
}
for(i=0; i<MAX_METHODS; i++)
{
try_method[i]=1;
fm[i]=5;
zs[i]=1;
lv[i]=9;
}
fm[1]=0; fm[2]=1; fm[4]=0; fm[5]=1; fm[7]=0; fm[8]=1;
lv[1]=4; lv[2]=4; lv[3]=4; lv[9]=2;
zs[1]=0; zs[2]=0; zs[5]= 0; zs[6]=0; zs[7]=0; zs[9]=2;
method=11;
for(filt=0; filt<6; filt++)
{
zs[method]=2;
lv[method]=2;
fm[method]=filt;
method++;
}
for(lev=1; lev<10; lev++)
{
for(strat=0; strat<2; strat++)
{
for(filt=0; filt<6; filt++)
{
zs[method]=strat;
lv[method]=lev;
fm[method]=filt;
method++;
}
}
}
#define BUMP_I i++;if(i >= argc) {printf("insufficient parameters\n");exit(1);}
names=1;
for (i=1; i<argc; i++)
{
if(!strncmp(argv[i],"-",1))
names++;
if(!strncmp(argv[i],"-fast",5))
/* try two fast filters */
{
methods_specified=1;
try_method[16]=0;
try_method[53]=0;
}
else if(!strncmp(argv[i],"-huffman",8))
/* try all filters with huffman */
{
methods_specified=1;
for(method=11; method<16; method++)
{
try_method[method]=0;
}
}
else if( !strncmp(argv[i],"-already",8))
{
names++;
BUMP_I;
crushed_idat_size = (png_uint_32)atoi(argv[i]);
}
else if( !strncmp(argv[i],"-bkgd",5) ||
!strncmp(argv[i],"-bKGD",5))
{
names+=3;
have_bkgd=1;
bkgd_red=(png_uint_16)atoi(argv[++i]);
bkgd_green=(png_uint_16)atoi(argv[++i]);
bkgd_blue=(png_uint_16)atoi(argv[++i]);
}
else if(!strncmp(argv[i],"-brute",6))
/* brute force try everything */
{
int lev, strat, filt;
methods_specified=1;
brute_force++;
for(method=11; method < 125; method++)
try_method[method]=0;
if(brute_force_filter==0)
for (filt=0; filt<6; filt++)
brute_force_filters[filt]=0;
if(brute_force_level==0)
for (lev=0; lev<10; lev++)
brute_force_levels[lev]=0;
if(brute_force_strategy == 0)
for (strat=0; strat<3; strat++)
brute_force_strategies[strat]=0;
}
else if(!strncmp(argv[i],"-bit_depth",10))
{
names++;
BUMP_I;
force_output_bit_depth=atoi(argv[i]);
}
else if(!strncmp(argv[i],"-cc",3))
{
do_color_count=1;
}
else if(!strncmp(argv[i],"-no_cc",6))
do_color_count=0;
else if(!strncmp(argv[i],"-c",2))
{
names++;
BUMP_I;
force_output_color_type=atoi(argv[i]);
}
#ifdef PNG_gAMA_SUPPORTED
else if(!strncmp(argv[i],"-dou",4))
{
double_gamma++;
global_things_have_changed=1;
}
#endif
else if(!strncmp(argv[i],"-d",2))
{
BUMP_I;
if(pngcrush_mode==EXTENSION_MODE)
pngcrush_mode=DIREX_MODE;
else
pngcrush_mode=DIRECTORY_MODE;
directory_name= argv[names++];
}
else if(!strncmp(argv[i],"-exit",5))
pngcrush_must_exit=1;
else if(!strncmp(argv[i],"-e",2))
{
BUMP_I;
if(pngcrush_mode==DIRECTORY_MODE)
pngcrush_mode=DIREX_MODE;
else
pngcrush_mode=EXTENSION_MODE;
extension= argv[names++];
}
else if(!strncmp(argv[i],"-force",6))
{
global_things_have_changed=1;
}
else if(!strncmp(argv[i],"-fix",4))
fix++;
else if(!strncmp(argv[i],"-f",2))
{
int specified_filter=atoi(argv[++i]);
int lev, strat, filt;
if(specified_filter > 5 || specified_filter < 0)
specified_filter = 5;
names++;
if(brute_force == 0)
fm[method]=specified_filter;
else
{
for (filt=0; filt<6; filt++)
brute_force_filters[filt]=1;
brute_force_filters[specified_filter]=0;
method=11;
for(filt=0; filt<6; filt++)
{
try_method[method]= brute_force_filters[filt] |
brute_force_strategies[2];
method++;
}
for(lev=1; lev<10; lev++)
{
for(strat=0; strat<2; strat++)
{
for(filt=0; filt<6; filt++)
{
try_method[method]=brute_force_levels[lev] |
brute_force_filters[filt] |
brute_force_strategies[strat];
method++;
}
}
}
brute_force_filter++;
}
}
else if(!strncmp(argv[i],"-loco",5))
{
#ifdef PNGCRUSH_LOCO
do_loco=1;
#else
printf(
"Cannot do -loco because libpng was compiled without MNG features");
#endif
}
else if(!strncmp(argv[i],"-l",2))
{
int lev, strat, filt;
int specified_level=atoi(argv[++i]);
if(specified_level > 9 || specified_level < 0)
specified_level = 9;
names++;
if(brute_force == 0)
lv[method]=specified_level;
else
{
if(brute_force_level == 0)
for (lev=0; lev<10; lev++)
brute_force_levels[lev]=1;
brute_force_levels[specified_level]=0;
method=11;
for(filt=0; filt<6; filt++)
{
lv[method]=specified_level;
method++;
}
for(lev=1; lev<10; lev++)
{
for(strat=0; strat<2; strat++)
{
for(filt=0; filt<6; filt++)
{
try_method[method]=brute_force_levels[lev] |
brute_force_filters[filt] |
brute_force_strategies[strat];
method++;
}
}
}
brute_force_level++;
}
}
#ifdef PNG_gAMA_SUPPORTED
else if(!strncmp(argv[i],"-g",2))
{
names++;
BUMP_I;
if (intent < 0)
{
#ifdef PNG_FIXED_POINT_SUPPORTED
int c;
char number[16];
char *n=number;
int nzeroes=-1;
int length=strlen(argv[i]);
for (c=0; c<length; c++)
{
if( *(argv[i]+c) == '.')
{
nzeroes=5;
}
else if (nzeroes)
{
*n++=*(argv[i]+c);
nzeroes--;
}
}
for (c=0; c<nzeroes; c++)
*n++='0';
*n='\0';
specified_gamma=atoi(number);
#else
specified_gamma=atof(argv[i]);
#endif
}
}
#endif
else if(!strncmp(argv[i],"-h",2))
{
help++;
verbose++;
}
#ifdef PNG_iCCP_SUPPORTED
else if(!strncmp(argv[i],"-iccp",5))
{
FILE *iccp_fn;
if(iccp_length)
free(iccp_text);
iccp_length=atoi(argv[++i]);
names+=3;
strcpy(iccp_name,argv[++i]);
iccp_file=argv[++i];
if ((iccp_fn = FOPEN(iccp_file, "rb")) == NULL)
{
fprintf(STDERR, "Could not find file: %s\n", iccp_file);
iccp_length=0;
}
else
{
int ic;
iccp_text=malloc(iccp_length);
for (ic=0; ic<iccp_length; ic++)
{
png_size_t num_in;
num_in = fread(buffer, 1, 1, iccp_fn);
if (!num_in)
break;
iccp_text[ic]=buffer[0];
}
}
}
#endif
else if(!strncmp(argv[i],"-max",4))
{
names++;
BUMP_I;
max_idat_size = (png_uint_32)atoi(argv[i]);
if (max_idat_size == 0 || max_idat_size > PNG_MAX_UINT)
max_idat_size=PNG_ZBUF_SIZE;
}
else if(!strncmp(argv[i],"-m",2))
{
names++;
BUMP_I;
method=atoi(argv[i]);
methods_specified=1;
brute_force=0;
try_method[method]=0;
}
else if(!strncmp(argv[i],"-nofilecheck",5))
nofilecheck++;
else if(!strncmp(argv[i],"-nosave",2))
{
/* no save; I just use this for testing decode speed */
nosave++;
pngcrush_mode=EXTENSION_MODE;
}
else if(!strncmp(argv[i],"-plte_len",9))
{
names++;
BUMP_I;
plte_len=atoi(argv[i]);
}
else if(!strncmp(argv[i],"-pplt",3))
{
names++;
do_pplt++;
BUMP_I;
strcpy(pplt_string,argv[i]);
global_things_have_changed=1;
}
else if(!strncmp(argv[i],"-p",2))
{
pauses++;
}
else if(!strncmp(argv[i],"-q",2))
verbose=0;
else if(!strncmp(argv[i],"-reduce",7))
{
reduction_ok++;
do_color_count=1;
}
#ifdef PNG_gAMA_SUPPORTED
else if(!strncmp(argv[i],"-rep",4))
{
names++;
BUMP_I;
{
#ifdef PNG_FIXED_POINT_SUPPORTED
int c;
char number[16];
char *n=number;
int nzeroes=-1;
int length=strlen(argv[i]);
for (c=0; c<length; c++)
{
if( *(argv[i]+c) == '.')
{
nzeroes=5;
}
else if (nzeroes)
{
*n++=*(argv[i]+c);
nzeroes--;
}
}
for (c=0; c<nzeroes; c++)
*n++='0';
*n='\0';
force_specified_gamma=atoi(number);
#else
force_specified_gamma=atof(argv[i]);
#endif
}
global_things_have_changed=1;
}
#endif
#ifdef PNG_pHYs_SUPPORTED
else if(!strncmp(argv[i],"-res",4))
{
names++;
BUMP_I;
resolution=atoi(argv[i]);
global_things_have_changed=1;
}
#endif
#ifdef PNGCRUSH_MULTIPLE_ROWS
else if(!strncmp(argv[i],"-rows",5))
{
names++;
BUMP_I;
max_rows_at_a_time=atoi(argv[i]);
}
#endif
else if(!strncmp(argv[i],"-r",2))
{
remove_chunks=i;
names++;
BUMP_I;
}
else if( !strncmp(argv[i],"-save",5))
all_chunks_are_safe++;
else if( !strncmp(argv[i],"-srgb",5) ||
!strncmp(argv[i],"-sRGB",5))
{
#ifdef PNG_gAMA_SUPPORTED
#ifdef PNG_FIXED_POINT_SUPPORTED
specified_gamma=45455L;
#else
specified_gamma=0.45455;
#endif
#endif
intent=0;
BUMP_I;
if(!strncmp(argv[i],"0",1) ||
!strncmp(argv[i],"1",1) ||
!strncmp(argv[i],"2",1) ||
!strncmp(argv[i],"3",1))
{
names++;
intent=(int)atoi(argv[i]);
}
else
i--;
}
else if(!strncmp(argv[i],"-s",2))
verbose=0;
else if( !strncmp(argv[i],"-text",5) || !strncmp(argv[i],"-tEXt",5) ||
#ifdef PNG_iTXt_SUPPORTED
!strncmp(argv[i],"-itxt",5) || !strncmp(argv[i],"-iTXt",5) ||
!strncmp(argv[i],"-zitxt",6) || !strncmp(argv[i],"-ziTXt",6) ||
#endif
!strncmp(argv[i],"-ztxt",5) || !strncmp(argv[i],"-zTXt",5))
{
i+=2; BUMP_I; i-=3;
if(strlen(argv[i+2]) < 80 && strlen(argv[i+3]) < 2048 &&
text_inputs < 10)
{
#ifdef PNG_iTXt_SUPPORTED
if( !strncmp(argv[i],"-zi",3))
{
text_compression[text_inputs] = PNG_ITXT_COMPRESSION_zTXt;
names+=2;
}
else
#endif
if( !strncmp(argv[i],"-z",2))
text_compression[text_inputs] = PNG_TEXT_COMPRESSION_zTXt;
else if( !strncmp(argv[i],"-t",2))
text_compression[text_inputs] = PNG_TEXT_COMPRESSION_NONE;
#ifdef PNG_iTXt_SUPPORTED
else
{
text_compression[text_inputs] = PNG_ITXT_COMPRESSION_NONE;
names+=2;
}
#endif
names+=3;
if( !strncmp(argv[++i],"b",1))
text_where[text_inputs]=1;
if( !strncmp(argv[i],"a",1))
text_where[text_inputs]=2;
strcpy(&text_keyword[text_inputs*80],argv[++i]);
#ifdef PNG_iTXt_SUPPORTED
if(text_compression[text_inputs] <= 0)
{
text_lang[text_inputs*80] = '\0';
text_lang_key[text_inputs*80] = '\0';
}
else
{
strcpy(&text_lang[text_inputs*80],argv[++i]);
/* libpng-1.0.5j and later */
strcpy(&text_lang_key[text_inputs*80],argv[++i]);
}
#endif
strcpy(&text_text[text_inputs*2048],argv[++i]);
text_inputs++;
}
else
{
if(text_inputs > 9)
fprintf(STDERR,
"too many text/zTXt inputs; only 10 allowed\n");
else
fprintf(STDERR,
"keyword exceeds 79 characters or text exceeds 2047 characters\n");
i+=3;
names+=3;
#ifdef PNG_iTXt_SUPPORTED
if( !strncmp(argv[i],"-i",2) || !strncmp(argv[i],"-zi",3))
{
i++;
BUMP_I;
names+=2;
}
#endif
}
}
#ifdef PNG_tRNS_SUPPORTED
else if( !strncmp(argv[i],"-trns_a",7) ||
!strncmp(argv[i],"-tRNS_a",7))
{
num_trans_in=(png_uint_16)atoi(argv[++i]);
have_trns=1;
for (ia=0; ia<num_trans_in; ia++)
trans_in[ia]=(png_byte)atoi(argv[++i]);
names+=1+num_trans_in;
}
else if( !strncmp(argv[i],"-trns",5) ||
!strncmp(argv[i],"-tRNS",5))
{
names+=5;
have_trns=1;
trns_index=(png_uint_16)atoi(argv[++i]);
trns_red=(png_uint_16)atoi(argv[++i]);
trns_green=(png_uint_16)atoi(argv[++i]);
trns_blue=(png_uint_16)atoi(argv[++i]);
trns_gray=(png_uint_16)atoi(argv[++i]);
}
#endif
else if(!strncmp(argv[i],"-version",8))
{
fprintf(STDERR, " pngcrush ");
fprintf(STDERR, PNGCRUSH_VERSION );
fprintf(STDERR,", uses libpng ");
fprintf(STDERR, PNG_LIBPNG_VER_STRING );
fprintf(STDERR,"and zlib ");
fprintf(STDERR, ZLIB_VERSION );
fprintf(STDERR, "\n Check http://pmt.sf.net\n");
fprintf(STDERR, " for the most recent version.\n");
}
else if(!strncmp(argv[i],"-v",2))
{
verbose++;
}
else if(!strncmp(argv[i],"-w",2))
{
default_compression_window=atoi(argv[++i]);
force_compression_window++;
names++;
}
else if(!strncmp(argv[i],"-zm",3))
{
compression_mem_level=atoi(argv[++i]);
names++;
}
else if(!strncmp(argv[i],"-z",2))
{
int lev, strat, filt;
int specified_strategy=atoi(argv[++i]);
if(specified_strategy > 2 || specified_strategy < 0)
specified_strategy = 0;
names++;
if(brute_force == 0)
zs[method]=specified_strategy;
else
{
if(brute_force_strategy == 0)
for (strat=0; strat<2; strat++)
brute_force_strategies[strat]=1;
brute_force_strategies[specified_strategy]=0;
method=11;
for(filt=0; filt<6; filt++)
{
if(specified_strategy != 2)
try_method[method]=1;
method++;
}
for(lev=1; lev<10; lev++)
{
for(strat=0; strat<2; strat++)
{
for(filt=0; filt<6; filt++)
{
try_method[method]=brute_force_levels[lev] |
brute_force_filters[filt] |
brute_force_strategies[strat];
method++;
}
}
}
}
brute_force_strategy++;
}
}
if(verbose > 0)
{
/* If you have modified this source, you may insert additional notices
* immediately after this sentence. */
fprintf(STDERR,
"\n | pngcrush %s, Copyright (C) 1998-2001 Glenn Randers-Pehrson\n",
PNGCRUSH_VERSION);
fprintf(STDERR,
" | This is a free, open-source program. Permission is irrevocably\n");
fprintf(STDERR,
" | granted to everyone to use this version of pngcrush without\n");
fprintf(STDERR,
" | payment of any fee.\n");
fprintf(STDERR,
" | Executable name is %s\n",progname);
fprintf(STDERR,
" | It was built with libpng version %s, and is\n",
PNG_LIBPNG_VER_STRING);
#if PNG_LIBPNG_VER > 10001
fprintf(STDERR,
" | running with %s", png_get_header_version(NULL));
#endif
#if PNG_LIBPNG_VER > 96
fprintf(STDERR,
" | Copyright (C) 1998-2001 Glenn Randers-Pehrson,\n");
#endif
#if PNG_LIBPNG_VER > 89
fprintf(STDERR,
" | Copyright (C) 1996, 1997 Andreas Dilger,\n");
#endif
fprintf(STDERR,
" | Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,\n");
fprintf(STDERR,
" | and zlib version %s, Copyright (C) 1998,\n",
ZLIB_VERSION);
fprintf(STDERR,
" | Jean-loup Gailly and Mark Adler.\n");
#if defined(__DJGPP__)
fprintf(STDERR,
" | It was compiled with gcc version %s", __VERSION__);
# if defined(PNG_USE_PNGGCCRD)
/* is there a macro for "as" versions? */
fprintf(STDERR, "\n | and as version %s", "2.9.5");
# endif
fprintf(STDERR,
"\n | under DJGPP %d.%d, Copyright (C) 1995, D. J. Delorie\n",
__DJGPP__,__DJGPP_MINOR__);
fprintf(STDERR,
" | and loaded with PMODE/DJ, by Thomas Pytel and Matthias Grimrath\n");
fprintf(STDERR,
" | Copyright (C) 1996, Matthias Grimrath.\n");
#endif
fprintf(STDERR,"\n");
}
if (default_compression_window == 32) default_compression_window=15;
else if(default_compression_window == 16) default_compression_window=14;
else if(default_compression_window == 8) default_compression_window=13;
else if(default_compression_window == 4) default_compression_window=12;
else if(default_compression_window == 2) default_compression_window=11;
else if(default_compression_window == 1) default_compression_window=10;
else if(default_compression_window == 512) default_compression_window= 9;
/* Use of compression window size 256 is not recommended. */
else if(default_compression_window == 256) default_compression_window= 8;
else if(default_compression_window != 15)
{
fprintf(STDERR,"Invalid window size (%d); using window size=4\n",
default_compression_window);
default_compression_window=12;
}
if(pngcrush_mode == DEFAULT_MODE && argc - names == 2)
{
inname= argv[names];
outname=argv[names+1];
}
if(pngcrush_mode == DEFAULT_MODE && (argc - names == 1 || nosave))
{
inname= argv[names];
}
if((nosave == 0 && pngcrush_mode == DEFAULT_MODE && argc - names != 2) ||
help > 0)
{
fprintf(STDERR,
"\nusage: %s [options] infile.png outfile.png\n",progname);
fprintf(STDERR,
" %s -e ext [other options] files.png ...\n",progname);
fprintf(STDERR,
" %s -d dir [other options] files.png ...\n",progname);
if(verbose > 1)
{
png_crush_pause();
fprintf(STDERR,
"\noptions (Note: any option can be spelled out for clarity, e.g.,\n");
fprintf(STDERR,
" \"pngcrush -dir New -method 7 -remove bkgd *.png\"\n");
fprintf(STDERR,
" is the same as \"pngcrush -d New -m 7 -rem bkgd *.png\"):\n\n");
}
else
fprintf(STDERR, "options:\n");
fprintf(STDERR,
" -already already_crushed_size [e.g., 8192]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n If file has an IDAT greater than this size, it\n");
fprintf(STDERR,
" will be considered to be already crushed and will\n");
fprintf(STDERR,
" not be processed, unless you are making other changes\n");
fprintf(STDERR,
" or the \"-force\" option is present.\n\n");
}
fprintf(STDERR,
" -bit_depth depth (bit_depth to use in output file)\n");
if(verbose > 1)
fprintf(STDERR,
"\n Default output depth is same as input depth.\n\n");
fprintf(STDERR,
" -brute (Use brute-force, try 114 different methods [11-124])\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Very time-consuming and generally not worthwhile.\n");
fprintf(STDERR,
" You can restrict this option to certain filter types,\n");
fprintf(STDERR,
" compression levels, or strategies by following it with\n");
fprintf(STDERR,
" \"-f filter\", \"-l level\", or \"-z strategy\".\n\n");
}
fprintf(STDERR,
" -c color_type of output file [0, 2, 4, or 6]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Color type for the output file. Future versions\n");
fprintf(STDERR,
" will also allow color_type 3, if there are 256 or\n");
fprintf(STDERR,
" fewer colors present in the input file. Color types\n");
fprintf(STDERR,
" 4 and 6 are padded with an opaque alpha channel if\n");
fprintf(STDERR,
" the input file does not have alpha information.\n");
fprintf(STDERR,
" You can use 0 or 4 to convert color to grayscale.\n");
fprintf(STDERR,
" Use 0 or 2 to delete an unwanted alpha channel.\n");
fprintf(STDERR,
" Default is to use same color type as the input file.\n\n");
}
#ifdef PNGCRUSH_COUNT_COLORS
fprintf(STDERR,
" -cc (do color counting)\n");
if(verbose > 1)
fprintf(STDERR,"\n");
#endif
fprintf(STDERR,
" -d directory_name (where output files will go)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n If a directory name is given, then the output\n");
fprintf(STDERR,
" files are placed in it, with the same filenames as\n");
fprintf(STDERR,
" those of the original files. For example,\n");
fprintf(STDERR,
" you would type 'pngcrush -directory CRUSHED *.png'\n");
fprintf(STDERR,
" to get *.png => CRUSHED/*.png\n\n");
}
png_crush_pause();
fprintf(STDERR,
" -double_gamma (used for fixing gamma in PhotoShop 5.0/5.02 files)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n It has been claimed that the PS5 bug is actually\n");
fprintf(STDERR,
" more complex than that, in some unspecified way.\n\n");
}
fprintf(STDERR,
" -e extension (used for creating output filename)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n e.g., -ext .new means *.png => *.new\n");
fprintf(STDERR,
" and -e _C.png means *.png => *_C.png\n\n");
}
fprintf(STDERR,
" -f user_filter [0-5]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n filter to use with the method specified in the\n");
fprintf(STDERR,
" preceding '-m method' or '-brute_force' argument.\n");
fprintf(STDERR,
" 0: none; 1-4: use specified filter; 5: adaptive.\n\n");
}
fprintf(STDERR,
" -fix (fix otherwise fatal conditions such as bad CRCs)\n");
if(verbose > 1)
fprintf(STDERR, "\n");
fprintf(STDERR,
" -force (Write a new output file even if larger than input)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Otherwise the input file will be copied to output\n");
fprintf(STDERR,
" if it is smaller than any generated file and no chunk\n");
fprintf(STDERR,
" additions, removals, or changes were requested.\n\n");
}
fprintf(STDERR,
#ifdef PNG_FIXED_POINT_SUPPORTED
" -g gamma (float or fixed*100000, e.g., 0.45455 or 45455)\n");
#else
" -g gamma (float, e.g., 0.45455)\n");
#endif
if(verbose > 1)
fprintf(STDERR,
"\n Value to insert in gAMA chunk, only if the input\n");
if(verbose > 1)
fprintf(STDERR,
" file has no gAMA chunk. To replace an existing\n");
if(verbose > 1)
fprintf(STDERR,
" gAMA chunk, use the '-replace_gamma' option.\n\n");
png_crush_pause();
#ifdef PNG_iCCP_SUPPORTED
fprintf(STDERR,
" -iccp length \"Profile Name\" iccp_file\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n file with ICC profile to insert in an iCCP chunk.");
fprintf(STDERR, "\n\n");
}
#endif
#ifdef PNG_iTXt_SUPPORTED
fprintf(STDERR,
" -itxt b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\"\n");
if(verbose > 1)
fprintf(STDERR,
"\n Uncompressed iTXt chunk to insert (see -text).\n\n");
#endif
fprintf(STDERR,
" -l zlib_compression_level [0-9]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n zlib compression level to use with method specified\n");
fprintf(STDERR,
" with the preceding '-m method' or '-brute_force'\n");
fprintf(STDERR,
" argument.\n\n");
}
#ifdef PNGCRUSH_LOCO
fprintf(STDERR,
" -loco (\"loco crush\" truecolor PNGs)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Make the file more compressible by performing a\n");
fprintf(STDERR,
" lossless reversible color transformation.\n");
fprintf(STDERR,
" The resulting file is a MNG, not a PNG, and should\n");
fprintf(STDERR,
" be given the \".mng\" file extension. The\n");
fprintf(STDERR,
" \"loco\" option has no effect on grayscale or\n");
fprintf(STDERR,
" indexed-color PNG files.\n\n");
}
#endif
fprintf(STDERR,
" -m method [0 through %d]\n",MAX_METHODS);
if(verbose > 1)
{
fprintf(STDERR,
"\n pngcrush method to try (0 means try all of 1-10).\n");
fprintf(STDERR,
" Can be repeated as in '-m 1 -m 4 -m 7'.\n");
fprintf(STDERR,
" This can be useful if pngcrush runs out of memory\n");
fprintf(STDERR,
" when it tries methods 2, 3, 5, 6, 8, 9, or 10 which\n");
fprintf(STDERR,
" use filtering and are memory intensive. Methods\n");
fprintf(STDERR,
" 1, 4, and 7 use no filtering; methods 11 and up use \n");
fprintf(STDERR,
" specified filter, compression level, and strategy.\n\n");
png_crush_pause();
}
fprintf(STDERR,
" -max maximum_IDAT_size [default %d]\n",PNG_ZBUF_SIZE);
if(verbose > 1)
fprintf(STDERR,"\n");
#ifdef PNGCRUSH_COUNT_COLORS
fprintf(STDERR,
" -no_cc (no color counting)\n");
if(verbose > 1)
fprintf(STDERR,"\n");
#endif
fprintf(STDERR,
" -nofilecheck (do not check for infile.png == outfile.png)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n To avoid false hits from MSVC-compiled code. Note\n");
fprintf(STDERR,
" that if you use this option, you are responsible for\n");
fprintf(STDERR,
" ensuring that the input file is not the output file.\n\n");
}
fprintf(STDERR,
" -n (no save; does not do compression or write output PNG)\n");
if(verbose > 1)
fprintf(STDERR,
"\n Useful in conjunction with -v option to get info.\n\n");
fprintf(STDERR,
" -plte_len n (truncate PLTE)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Truncates the PLTE. Be sure not to truncate it to\n");
fprintf(STDERR,
" less than the greatest index present in IDAT.\n\n");
}
fprintf(STDERR,
" -q (quiet)\n");
if(verbose > 1)
fprintf(STDERR,"\n");
fprintf(STDERR,
" -reduce (do lossless color type or bit depth reduction)\n");
if(verbose > 1)
fprintf(STDERR,
"\n (if possible)\n\n");
fprintf(STDERR,
" -rem chunkname (or \"alla\" or \"allb\")\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Name of an ancillary chunk or optional PLTE to be\n");
fprintf(STDERR,
" removed. Be careful with this. Please don't use \n");
fprintf(STDERR,
" this feature to remove transparency, gamma, copyright,\n");
fprintf(STDERR,
" or other valuable information. To remove several\n");
fprintf(STDERR,
" different chunks, repeat: -rem tEXt -rem pHYs.\n");
fprintf(STDERR,
" Known chunks (those in the PNG 1.1 spec or extensions\n");
fprintf(STDERR,
" document) can be named with all lower-case letters,\n");
fprintf(STDERR,
" so \"-rem bkgd\" is equivalent to \"-rem bKGD\". But\n");
fprintf(STDERR,
" note: \"-rem text\" removes all forms of text chunks;\n");
fprintf(STDERR,
" Exact case is required to remove unknown chunks.\n");
fprintf(STDERR,
" To do surgery with a chain-saw, \"-rem alla\" removes\n");
fprintf(STDERR,
" all known ancillary chunks except for tRNS, and\n");
fprintf(STDERR,
" \"-rem allb\" removes all but tRNS and gAMA.\n\n");
}
png_crush_pause();
fprintf(STDERR,
#ifdef PNG_FIXED_POINT_SUPPORTED
"-replace_gamma gamma (float or fixed*100000) even if gAMA is present.\n");
#else
"-replace_gamma gamma (float, e.g. 0.45455) even if gAMA is present.\n");
#endif
if(verbose > 1)
fprintf(STDERR,"\n");
fprintf(STDERR,
" -res dpi\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Write a pHYs chunk with the given resolution.\n\n");
}
fprintf(STDERR,
" -save (keep all copy-unsafe chunks)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Save otherwise unknown ancillary chunks that would\n");
fprintf(STDERR,
" be considered copy-unsafe. This option makes\n");
fprintf(STDERR,
" chunks 'known' to pngcrush, so they can be copied.\n\n");
}
png_crush_pause();
fprintf(STDERR,
" -srgb [0, 1, 2, or 3]\n");
if(verbose > 1)
fprintf(STDERR,
"\n Value of 'rendering intent' for sRGB chunk.\n\n");
fprintf(STDERR,
" -text b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\"\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n tEXt chunk to insert. keyword < 80 chars,\n");
fprintf(STDERR,
"\n text < 2048 chars. For now, you can only add ten\n");
fprintf(STDERR,
" tEXt, iTXt, or zTXt chunks per pngcrush run.\n\n");
}
#ifdef PNG_tRNS_SUPPORTED
fprintf(STDERR,
" -trns_array n trns[0] trns[1] .. trns[n-1]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Insert a tRNS chunk, if no tRNS chunk found in file.\n");
fprintf(STDERR,
" Values are for the tRNS array in indexed-color PNG.\n\n");
}
fprintf(STDERR,
" -trns index red green blue gray\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Insert a tRNS chunk, if no tRNS chunk found in file.\n");
fprintf(STDERR,
" You must give all five parameters regardless of the\n");
fprintf(STDERR,
" color type, scaled to the output bit depth.\n\n");
}
#endif
fprintf(STDERR,
" -v (display more detailed information)\n");
if(verbose > 1)
fprintf(STDERR,
"\n Repeat the option (use \"-v -v\") for even more.\n\n");
fprintf(STDERR,
" -version (display the pngcrush version)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Look for the most recent version of pngcrush at\n");
fprintf(STDERR,
" http://pmt.sf.net\n\n");
}
fprintf(STDERR,
" -w compression_window_size [32, 16, 8, 4, 2, 1, 512]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Size of the sliding compression window, in kbytes\n");
fprintf(STDERR,
" (or bytes, in case of 512). It's best to\n");
fprintf(STDERR,
" use the default (32) unless you run out of memory.\n");
fprintf(STDERR,
" The program will use a smaller window anyway when\n");
fprintf(STDERR,
" the uncompressed file is smaller than 16k.\n\n");
}
fprintf(STDERR,
" -z zlib_strategy [0, 1, or 2]\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n zlib compression strategy to use with the preceding\n");
fprintf(STDERR,
" '-m method' argument.\n\n");
}
fprintf(STDERR,
" -zmem zlib_compression_mem_level [1-9, default 9]\n");
if(verbose > 1)
fprintf(STDERR,"\n");
#ifdef PNG_iTXt_SUPPORTED
fprintf(STDERR,
" -zitxt b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\"\n");
if(verbose > 1)
fprintf(STDERR,
"\n Compressed iTXt chunk to insert (see -text).\n\n");
#endif
fprintf(STDERR,
" -ztxt b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\"\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n zTXt chunk to insert (see -text).\n\n");
png_crush_pause();
}
fprintf(STDERR,
" -h (help and legal notices)\n");
if(verbose > 1)
fprintf(STDERR,
"\n Display this information.\n\n");
fprintf(STDERR,
" -p (pause)\n");
if(verbose > 1)
{
fprintf(STDERR,
"\n Wait for [enter] key before continuing display.\n");
fprintf(STDERR,
" e.g., type '%s -pause -help', if the help\n",progname);
fprintf(STDERR,
" screen scrolls out of sight.\n\n");
}
/* If you have modified this source, you may insert additional notices
* immediately after this sentence. */
fprintf (STDERR,
"\nCopyright (C) 1998-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)\n\n");
fprintf(STDERR,
"\nDISCLAIMER: The pngcrush computer program is supplied \"AS IS\".\n");
fprintf(STDERR,
"The Author disclaims all warranties, expressed or implied, including,\n");
fprintf(STDERR,
"without limitation, the warranties of merchantability and of fitness\n");
fprintf(STDERR,
"for any purpose. The Author assumes no liability for direct, indirect,\n");
fprintf(STDERR,
"incidental, special, exemplary, or consequential damages, which may\n");
fprintf(STDERR,
"result from the use of the computer program, even if advised of the\n");
fprintf(STDERR,
"possibility of such damage. There is no warranty against interference\n");
fprintf(STDERR,
"with your enjoyment of the computer program or against infringement.\n");
fprintf(STDERR,
"There is no warranty that my efforts or the computer program will\n");
fprintf(STDERR,
"fulfill any of your particular purposes or needs. This computer\n");
fprintf(STDERR,
"program is provided with all faults, and the entire risk of satisfactory\n");
fprintf(STDERR,
"quality, performance, accuracy, and effort is with the user.\n");
fprintf(STDERR,
"\nLICENSE: Permission is hereby irrevocably granted to everyone to use,\n");
fprintf(STDERR,
"copy, modify, and distribute this computer program, or portions hereof,\n");
fprintf(STDERR,
"purpose, without payment of any fee, subject to the following\n");
fprintf(STDERR,
"restrictions:\n\n");
fprintf(STDERR,
"1. The origin of this binary or source code must not be misrepresented.\n\n");
fprintf(STDERR,
"2. Altered versions must be plainly marked as such and must not be\n");
fprintf(STDERR,
"misrepresented as being the original binary or source.\n\n");
fprintf(STDERR,
"3. The Copyright notice, disclaimer, and license may not be removed\n");
fprintf(STDERR,
"or altered from any source, binary, or altered source distribution.\n");
if(pngcrush_mode == DEFAULT_MODE && argc - names != 2 && nosave == 0)
exit(1);
}
for (ia=0; ia<256; ia++)
trans_in[ia]=trns_array[ia]=255;
for(;;) /* loop on input files */
{
first_trial = 1;
things_have_changed=global_things_have_changed;
if(png_row_filters != NULL)
{
free(png_row_filters); png_row_filters=NULL;
}
image_specified_gamma=0;
inname=argv[names++];
if(inname == NULL)
{
if(verbose > 0) show_result();
exit(0);
}
if(pngcrush_mode == DIRECTORY_MODE || pngcrush_mode == DIREX_MODE)
{
#ifdef __riscos
if(fileexists(directory_name) & 2)
#else
struct stat stat_buf;
if(stat(directory_name, &stat_buf))
#endif
{
#if defined(_MBCS) || defined(WIN32) || defined(__WIN32__)
if(_mkdir(directory_name))
#else
if(mkdir(directory_name, 0x1ed))
#endif
{
fprintf(STDERR,"could not create directory %s\n",directory_name);
exit(1);
}
nofilecheck=1;
}
out_string[0] = '\0';
str_return = strcat(out_string,directory_name);
str_return = strcat(out_string,SLASH);
in_string[0] = '\0';
str_return = strcat(in_string,inname);
ip = op = in_string;
#ifdef __riscos
op = strrchr(in_string, '.');
if (!op) op = in_string; else op++;
#else
while(*ip != '\0')
{
if(*ip == '\\' || *ip == '/')op=ip+1;
ip++;
}
#endif
str_return = strcat(out_string,op);
outname=out_string;
}
if(pngcrush_mode == EXTENSION_MODE || pngcrush_mode == DIREX_MODE)
{
ip=in_string;
in_string[0]='\0';
if(pngcrush_mode == EXTENSION_MODE)
str_return = strcat(in_string,inname);
else
str_return = strcat(in_string,outname);
ip = in_string;
op = dot = out_string;
while(*ip != '\0')
{
*op++ = *ip++;
#ifdef __riscos
if(*ip == '/')dot=op;
#else
if(*ip == '.')dot=op;
#endif
}
*op = '\0';
if(dot != out_string)
*dot = '\0';
in_extension[0]='\0';
if(dot != out_string)
{
str_return = strcat(in_extension,++dot);
}
str_return = strcat(out_string,extension);
outname=out_string;
}
if(nosave < 2)
{
png_debug1(0, "Opening file %s for length measurement\n",inname);
if ((fpin = FOPEN(inname, "rb")) == NULL)
{
fprintf(STDERR, "Could not find file: %s\n", inname);
continue;
}
number_of_open_files++;
already_crushed = 0;
idat_length[0]=measure_idats(fpin);
FCLOSE(fpin);
if(already_crushed)
{
fprintf(STDERR, "File has already been crushed: %s\n", inname);
if(!things_have_changed) continue;
}
if(verbose > 0)
{
fprintf(STDERR," Recompressing %s\n",inname);
fprintf(STDERR,
" Total length of data found in IDAT chunks = %8lu\n",
idat_length[0]);
fflush(STDERR);
}
if(idat_length[0] == 0) continue;
}
else
idat_length[0]=1;
#ifdef PNGCRUSH_COUNT_COLORS
reduce_to_gray=0;
it_is_opaque=0;
output_color_type = input_color_type;
if (do_color_count)
{
if (force_output_color_type == 8 && (input_color_type == 2 ||
(input_color_type == 3) ||
input_color_type == 4 || input_color_type == 6))
/* check for unused alpha channel or single transparent color */
{
int alpha_status;
png_debug1(0, "Opening file %s for alpha check\n",inname);
if ((fpin = FOPEN(inname, "rb")) == NULL)
{
fprintf(STDERR, "Could not find file: %s\n", inname);
continue;
}
number_of_open_files++;
alpha_status=count_colors(fpin);
if(num_rgba < 257)
{
P1 ("Finished counting colors. num_rgba=%d\n", num_rgba);
}
else
{
P1 ("Finished counting colors. num_rgba is more than 256\n");
}
alpha_status = alpha_status; /* silence compiler warning. */
FCLOSE(fpin);
if (it_is_opaque)
{
if (output_color_type == 4)
output_color_type=0;
else if (output_color_type == 6)
output_color_type=2;
}
if (reduce_to_gray)
{
if (output_color_type == 2)
output_color_type=0;
else if (output_color_type == 6)
output_color_type=4;
}
}
#if 0 /* TO DO */
if (output_color_type == 0)
/* see if bit depth can be reduced */
{
}
if (input_color_type == 2)
/* check for 256 or fewer colors */
{
/* TO DO */
}
if (input_color_type == 3)
/* check for unused palette entries */
{
/* TO DO */
}
#endif
if(force_output_color_type == 8 && input_color_type != output_color_type)
{
P1 ("setting output color type to %d\n",output_color_type);
force_output_color_type = output_color_type;
}
}
#else
if (do_color_count)
printf(" color counting (-cc option) is disabled.\n");
#endif /* PNGCRUSH_COUNT_COLORS */
output_color_type=force_output_color_type;
output_bit_depth=force_output_bit_depth;
if(!methods_specified || try_method[0] == 0)
{
for (i=1; i<=DEFAULT_METHODS; i++) try_method[i]=0;
try_method[6]=try_method[0];
}
best_of_three=1;
for(trial=1; trial<=MAX_METHODS; trial++)
{
idat_length[trial]=(png_uint_32)0xffffffff;
if(trial == MAX_METHODS)
{
png_uint_32 best_length;
/* check lengths */
best=0;
best_length=(png_uint_32)0xffffffff;
for (ntrial=things_have_changed; ntrial<MAX_METHODS; ntrial++)
if(idat_length[ntrial]<=best_length)
{
best_length=idat_length[ntrial];
best=ntrial;
}
if(idat_length[best] == idat_length[0] && things_have_changed == 0
&& best != final_method && nosave == 0)
{
/* just copy input to output */
P2("prepare to copy input to output\n");
png_crush_pause();
if ((fpin = FOPEN(inname, "rb")) == NULL)
{
fprintf(STDERR, "Could not find input file %s\n", inname);
continue;
}
number_of_open_files++;
if ((fpout = FOPEN(outname, "wb")) == NULL)
{
fprintf(STDERR, "Could not open output file %s\n", outname);
FCLOSE(fpin);
exit(1);
}
number_of_open_files++;
P2("copying input to output... tc=%d ...",things_have_changed);
for(;;)
{
png_size_t num_in;
num_in = fread(buffer, 1, 1, fpin);
if (!num_in)
break;
fwrite(buffer, 1, 1, fpout);
}
P2("copy complete.\n");
png_crush_pause();
FCLOSE(fpin);
FCLOSE(fpout);
setfiletype(outname);
break;
}
if(best == final_method)
{
break;
}
else
{
filter_type=fm[best];
zlib_level=lv[best];
if(zs[best] == 0)z_strategy=Z_DEFAULT_STRATEGY;
if(zs[best] == 1)z_strategy=Z_FILTERED;
if(zs[best] == 2)z_strategy=Z_HUFFMAN_ONLY;
}
}
else
{
if(trial > 2 && trial < 5 && idat_length[trial-1]
< idat_length[best_of_three])best_of_three = trial-1;
if(try_method[trial])continue;
if(!methods_specified && try_method[0])
{
if((trial == 4 || trial == 7) && best_of_three != 1) continue;
if((trial == 5 || trial == 8) && best_of_three != 2) continue;
if((trial == 6 || trial == 9 || trial == 10) && best_of_three != 3)
continue;
}
filter_type=fm[trial];
zlib_level=lv[trial];
if(zs[trial] == 0)z_strategy=Z_DEFAULT_STRATEGY;
if(zs[trial] == 1)z_strategy=Z_FILTERED;
if(zs[trial] == 2)z_strategy=Z_HUFFMAN_ONLY;
final_method=trial;
if(nosave == 0)
P2(" Begin trial %d, filter %d, strategy %d, level %d\n",
trial, filter_type, z_strategy, zlib_level);
}
P2("prepare to open files.\n");
png_crush_pause();
if ((fpin = FOPEN(inname, "rb")) == NULL)
{
fprintf(STDERR, "Could not find input file %s\n", inname);
continue;
}
number_of_open_files++;
if(nosave == 0)
{
#ifndef __riscos
/* Can't sensibly check this on RISC OS without opening a file for
update or output
*/
struct stat stat_in, stat_out;
if (first_trial && !nofilecheck && (stat(inname, &stat_in) == 0) &&
(stat(outname, &stat_out) == 0) &&
#if defined(_MSC_VER) || defined(__MINGW32__) /* maybe others? */
/* MSVC++6.0 will erroneously return 0 for both files, so we
simply check the size instead. It is possible that we will
erroneously reject the attempt when inputsize and outputsize
are equal, for different files
*/
(stat_in.st_size == stat_out.st_size) &&
#else
(stat_in.st_ino == stat_out.st_ino) &&
#endif
(stat_in.st_dev == stat_out.st_dev))
{
fprintf(STDERR, "\n Cannot overwrite input file %s\n", outname);
P1(" st_ino=%d, st_size=%d\n\n", (int)stat_in.st_ino,
(int)stat_in.st_size);
FCLOSE(fpin);
exit(1);
}
#endif
if ((fpout = FOPEN(outname, "wb")) == NULL)
{
fprintf(STDERR, "Could not open output file %s\n", outname);
FCLOSE(fpin);
exit(1);
}
number_of_open_files++;
}
P2("files are opened.\n");
png_crush_pause();
Try
{
png_uint_32 row_length;
png_debug(0, "Allocating read and write structures\n");
#ifdef PNG_USER_MEM_SUPPORTED
read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL, (png_voidp)NULL,
(png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
#else
read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
#endif
if (read_ptr == NULL)
Throw "pngcrush could not create read_ptr";
if(nosave == 0)
{
#ifdef PNG_USER_MEM_SUPPORTED
write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL, (png_voidp)NULL,
(png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free);
#else
write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
#endif
if (write_ptr == NULL)
Throw "pngcrush could not create write_ptr";
}
png_debug(0, "Allocating read_info, write_info and end_info structures\n");
read_info_ptr = png_create_info_struct(read_ptr);
if (read_info_ptr == NULL)
Throw "pngcrush could not create read_info_ptr";
end_info_ptr = png_create_info_struct(read_ptr);
if (end_info_ptr == NULL)
Throw "pngcrush could not create end_info_ptr";
if(nosave == 0)
{
write_info_ptr = png_create_info_struct(write_ptr);
if (write_info_ptr == NULL)
Throw "pngcrush could not create write_info_ptr";
write_end_info_ptr = png_create_info_struct(write_ptr);
if (write_end_info_ptr == NULL)
Throw "pngcrush could not create write_end_info_ptr";
}
P2("structures created.\n");
png_crush_pause();
png_debug(0, "Initializing input and output streams\n");
#if !defined(PNG_NO_STDIO)
png_init_io(read_ptr, fpin);
if(nosave == 0)
png_init_io(write_ptr, fpout);
#else
png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data);
if(nosave == 0)
png_set_write_fn(write_ptr, (png_voidp)fpout, png_default_write_data,
#if defined(PNG_WRITE_FLUSH_SUPPORTED)
png_default_flush);
#else
NULL);
#endif
#endif
P2("io has been initialized.\n");
png_crush_pause();
/* We don't need to check CRC's because they were already checked
in the png_measure_idat function */
#ifdef PNG_CRC_QUIET_USE
png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
#endif
#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
png_set_keep_unknown_chunks(read_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)NULL, 0);
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
if(nosave == 0)
{
if(all_chunks_are_safe)
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)NULL, 0);
else
{
#ifndef PNG_UINT_IHDR
/* We are using libpng-1.0.6 or earlier */
#ifdef PNG_USE_LOCAL_ARRAYS
#if !defined(PNG_cHRM_SUPPORTED)
PNG_cHRM;
#endif
#if !defined(PNG_hIST_SUPPORTED)
PNG_hIST;
#endif
#if !defined(PNG_iCCP_SUPPORTED)
PNG_iCCP;
#endif
#if !defined(PNG_pCAL_SUPPORTED)
PNG_pCAL;
#endif
#if !defined(PNG_sCAL_SUPPORTED)
PNG_sCAL;
#endif
#if !defined(PNG_sPLT_SUPPORTED)
PNG_sPLT;
#endif
#if !defined(PNG_tIME_SUPPORTED)
PNG_tIME;
#endif
#endif /* PNG_USE_LOCAL_ARRAYS */
if(keep_unknown_chunk("alla",argv) &&
keep_unknown_chunk("allb",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_IF_SAFE,
(png_bytep)NULL, 0);
else
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_NEVER,
(png_bytep)NULL, 0);
/* Process the following chunks as if safe-to-copy since it is known that
recompressing the IDAT chunks has no effect on them */
#if !defined(PNG_cHRM_SUPPORTED)
if(keep_unknown_chunk("cHRM",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_cHRM, 1);
#endif
#if !defined(PNG_hIST_SUPPORTED)
if(keep_unknown_chunk("hIST",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_hIST, 1);
#endif
#if !defined(PNG_iCCP_SUPPORTED)
if(keep_unknown_chunk("iCCP",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_iCCP, 1);
#endif
#if !defined(PNG_sCAL_SUPPORTED)
if(keep_unknown_chunk("sCAL",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_sCAL, 1);
#endif
#if !defined(PNG_pCAL_SUPPORTED)
if(keep_unknown_chunk("pCAL",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_pCAL, 1);
#endif
#if !defined(PNG_sPLT_SUPPORTED)
if(keep_unknown_chunk("sPLT",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_sPLT, 1);
#endif
#if !defined(PNG_tIME_SUPPORTED)
if(keep_unknown_chunk("tIME",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
(png_bytep)png_tIME, 1);
#endif
#else /* PNG_UINT_IHDR is defined; we are using libpng newer than 1.0.6 */
#if !defined(PNG_cHRM_SUPPORTED) || !defined(PNG_hIST_SUPPORTED) || \
!defined(PNG_iCCP_SUPPORTED) || !defined(PNG_sCAL_SUPPORTED) || \
!defined(PNG_pCAL_SUPPORTED) || !defined(PNG_sPLT_SUPPORTED) || \
!defined(PNG_tIME_SUPPORTED)
png_byte chunk_name[5];
chunk_name[4]='\0';
#endif
if(keep_unknown_chunk("alla",argv) &&
keep_unknown_chunk("allb",argv))
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_IF_SAFE,
(png_bytep)NULL, 0);
else
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_NEVER,
(png_bytep)NULL, 0);
#if !defined(PNG_cHRM_SUPPORTED)
if(keep_unknown_chunk("cHRM",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_cHRM);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_hIST_SUPPORTED)
if(keep_unknown_chunk("hIST",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_hIST);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_iCCP_SUPPORTED)
if(keep_unknown_chunk("iCCP",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_iCCP);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_sCAL_SUPPORTED)
if(keep_unknown_chunk("sCAL",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_sCAL);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_pCAL_SUPPORTED)
if(keep_unknown_chunk("pCAL",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_pCAL);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_sPLT_SUPPORTED)
if(keep_unknown_chunk("sPLT",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_sPLT);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_tIME_SUPPORTED)
if(keep_unknown_chunk("tIME",argv))
{
png_save_uint_32(chunk_name, PNG_UINT_tIME);
png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#endif /* PNG_UINT_IHDR */
}
}
#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
png_debug(0, "Reading info struct\n");
{
#if defined(PNGCRUSH_LOCO)
png_byte mng_signature[8] = {138, 77, 78, 71, 13, 10, 26, 10};
#endif
png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
png_read_data(read_ptr, png_signature, 8);
png_set_sig_bytes(read_ptr, 8);
#if defined(PNGCRUSH_LOCO)
if (!(int)(png_memcmp(mng_signature, png_signature, 8)))
{
png_byte buffer[40];
/* Skip the MHDR */
png_permit_mng_features (read_ptr, PNG_FLAG_MNG_FILTER_64);
png_read_data(read_ptr, buffer, 40);
input_format=1;
}
else
#endif
if (png_sig_cmp(png_signature, 0, 8))
{
if (png_sig_cmp(png_signature, 0, 4))
png_error(read_ptr, "Not a PNG file!");
else
png_error(read_ptr, "PNG file corrupted by ASCII conversion");
}
}
png_read_info(read_ptr, read_info_ptr);
#if (PNG_LIBPNG_VER > 90)
{
int interlace_method, compression_method, filter_method;
png_debug(0, "Transferring info struct\n");
if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth,
&color_type, &interlace_method, &compression_method, &filter_method))
{
int need_expand = 0;
input_color_type=color_type;
input_bit_depth=bit_depth;
if(output_color_type > 7)
{
output_color_type=input_color_type;
}
if(verbose > 1 && first_trial)
{
fprintf(STDERR, " IHDR chunk data:\n");
fprintf(STDERR, " Width=%ld, height=%ld\n", width, height);
fprintf(STDERR, " Bit depth =%d\n", bit_depth);
fprintf(STDERR, " Color type=%d\n", color_type);
if (output_color_type != color_type)
fprintf(STDERR, " Output color type=%d\n",
output_color_type);
fprintf(STDERR, " Interlace =%d\n", interlace_method);
}
#ifndef PNG_WRITE_PACK_SUPPORTED
if(output_bit_depth == 0)
#else
if(force_output_bit_depth == 0)
#endif
{
output_bit_depth=input_bit_depth;
}
if((output_color_type != 3 || output_bit_depth > 8) &&
output_bit_depth >= 8 && output_bit_depth != input_bit_depth)
need_expand = 1;
#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
if((color_type == 2 || color_type == 6 || color_type == 3) &&
(output_color_type == 0 || output_color_type == 4))
{
if(verbose > 0 && first_trial)
{
#ifdef PNGCRUSH_COUNT_COLORS
if(reduce_to_gray)
fprintf(STDERR,
" Reducing all-gray truecolor image to grayscale.\n");
else
#endif
fprintf(STDERR,
" Reducing truecolor image to grayscale.\n");
}
#ifdef PNG_FIXED_POINT_SUPPORTED
png_set_rgb_to_gray_fixed(read_ptr, 1, -1, -1);
#else
png_set_rgb_to_gray(read_ptr, 1, 0., 0.);
#endif
if(output_bit_depth < 8)output_bit_depth=8;
if(color_type == 3) need_expand = 1;
}
#endif
if(color_type != 3 && output_color_type == 3)
{
printf(" Cannot change to indexed color (color_type 3)\n");
output_color_type=input_color_type;
}
if((color_type == 0 || color_type == 4) &&
(output_color_type == 2 || output_color_type == 6))
{
png_set_gray_to_rgb(read_ptr);
}
if((color_type == 4 || color_type == 6) &&
(output_color_type != 4 && output_color_type != 6))
{
if(verbose > 0 && first_trial)
{
#ifdef PNGCRUSH_COUNT_COLORS
if(it_is_opaque)
fprintf(STDERR, " Stripping opaque alpha channel.\n");
else
#endif
fprintf(STDERR, " Stripping existing alpha channel.\n");
}
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
png_set_strip_alpha(read_ptr);
#endif
}
if((output_color_type == 4 || output_color_type == 6) &&
(color_type != 4 && color_type != 6))
{
if(verbose > 0 && first_trial)
fprintf(STDERR, " Adding an opaque alpha channel.\n");
#ifdef PNG_READ_FILLER_SUPPORTED
png_set_filler(read_ptr, (png_uint_32)65535L, PNG_FILLER_AFTER);
#endif
need_expand = 1;
}
if(output_color_type && output_color_type != 3 &&
output_bit_depth < 8) output_bit_depth = 8;
if((output_color_type == 2 || output_color_type == 6) &&
color_type == 3)
{
if(verbose > 0 && first_trial)
fprintf(STDERR, " Expanding indexed color file.\n");
need_expand = 1;
}
#ifdef PNG_READ_EXPAND_SUPPORTED
if (need_expand == 1)
png_set_expand(read_ptr);
#endif
#ifdef PNG_READ_PACK_SUPPORTED
if(input_bit_depth < 8)
{
png_set_packing(read_ptr);
}
if(output_color_type == 0 && output_bit_depth < 8)
{
png_color_8 true_bits;
true_bits.gray = (png_byte)(output_bit_depth);
png_set_shift(read_ptr, &true_bits);
}
#endif
if(verbose > 1)
fprintf(STDERR, " Setting IHDR\n");
#if defined(PNGCRUSH_LOCO)
output_format=0;
if(do_loco)
{
if(output_color_type == 2 || output_color_type == 6)
{
output_format=1;
filter_method = 64;
png_permit_mng_features (write_ptr, PNG_FLAG_MNG_FILTER_64);
}
}
else
filter_method=0;
if(input_format != output_format)
things_have_changed = 1;
#endif
png_set_IHDR(write_ptr, write_info_ptr, width, height,
output_bit_depth, output_color_type, interlace_method,
compression_method, filter_method);
if(output_color_type != input_color_type) things_have_changed=1;
}
}
#if defined(PNG_READ_bKGD_SUPPORTED) && defined(PNG_WRITE_bKGD_SUPPORTED)
{
png_color_16p background;
if (!have_bkgd && png_get_bKGD(read_ptr, read_info_ptr, &background))
{
if(keep_chunk("bKGD",argv))
{
if((input_color_type == 2 || input_color_type == 6 ) &&
(output_color_type == 0 || output_color_type == 4))
background->gray = background->green;
png_set_bKGD(write_ptr, write_info_ptr, background);
}
}
if (have_bkgd)
{
/* If we are reducing an RGB image to grayscale, but the
background color isn't gray, the green channel is written.
That's not spec-compliant. We should really check for
a non-gray bKGD and refuse to do the reduction if one is
present. */
png_color_16 backgd;
png_color_16p background = &backgd;
background->red=bkgd_red;
background->green=bkgd_green;
background->blue=bkgd_blue;
background->gray = background->green;
png_set_bKGD(write_ptr, write_info_ptr, background);
}
}
#endif
#if defined(PNG_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_cHRM_SUPPORTED)
#ifdef PNG_FIXED_POINT_SUPPORTED
{
png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y,
blue_x, 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))
{
if(keep_chunk("cHRM",argv))
{
if (white_x == 0 && white_y == 0 && red_x == 0 && red_y == 0 &&
green_x == 0 && green_y == 0 && blue_x == 0 && blue_y == 0)
png_warning (write_ptr, "Deleting all-zero cHRM chunk");
else
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);
}
}
}
#else
{
double white_x, white_y, red_x, red_y, green_x, green_y,
blue_x, 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))
{
if(keep_chunk("cHRM",argv))
{
if (white_x == 0 && white_y == 0 && red_x == 0 && red_y == 0 &&
green_x == 0 && green_y == 0 && blue_x == 0 && blue_y == 0)
png_warning (write_ptr, "Deleting all-zero cHRM chunk");
else
png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y,
red_x, red_y, green_x, green_y, blue_x, blue_y);
}
}
}
#endif
#endif
#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_WRITE_gAMA_SUPPORTED)
{
if(force_specified_gamma)
{
if(first_trial)
{
things_have_changed=1;
if(verbose > 0)
fprintf(STDERR,
#ifdef PNG_FIXED_POINT_SUPPORTED
" Inserting gAMA chunk with gamma=(%d/100000)\n",
#else
" Inserting gAMA chunk with gamma=%f\n",
#endif
force_specified_gamma);
}
#ifdef PNG_FIXED_POINT_SUPPORTED
png_set_gAMA_fixed(write_ptr, write_info_ptr,
(png_fixed_point)force_specified_gamma);
file_gamma=(png_fixed_point)force_specified_gamma;
#else
png_set_gAMA(write_ptr, write_info_ptr,
force_specified_gamma);
file_gamma=force_specified_gamma;
#endif
}
#ifdef PNG_FIXED_POINT_SUPPORTED
else if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &file_gamma))
#else
else if (png_get_gAMA(read_ptr, read_info_ptr, &file_gamma))
#endif
{
if(keep_chunk("gAMA",argv))
{
if(image_specified_gamma)
file_gamma=image_specified_gamma;
if(verbose > 1 && first_trial)
#ifdef PNG_FIXED_POINT_SUPPORTED
fprintf(STDERR, " gamma=(%d/100000)\n", (int)file_gamma);
if(double_gamma)
file_gamma+=file_gamma;
png_set_gAMA_fixed(write_ptr, write_info_ptr, file_gamma);
#else
fprintf(STDERR, " gamma=%f\n", file_gamma);
if(double_gamma)
file_gamma+=file_gamma;
png_set_gAMA(write_ptr, write_info_ptr, file_gamma);
#endif
}
}
else if(specified_gamma)
{
if(first_trial)
{
things_have_changed=1;
if(verbose > 0)
fprintf(STDERR,
#ifdef PNG_FIXED_POINT_SUPPORTED
" Inserting gAMA chunk with gamma=(%d/100000)\n",
#else
" Inserting gAMA chunk with gamma=%f\n",
#endif
specified_gamma);
}
#ifdef PNG_FIXED_POINT_SUPPORTED
png_set_gAMA_fixed(write_ptr, write_info_ptr, specified_gamma);
file_gamma=(png_fixed_point)specified_gamma;
#else
png_set_gAMA(write_ptr, write_info_ptr, specified_gamma);
file_gamma=specified_gamma;
#endif
}
}
#endif
#if defined(PNG_READ_sRGB_SUPPORTED) && defined(PNG_WRITE_sRGB_SUPPORTED)
{
int file_intent;
if (png_get_sRGB(read_ptr, read_info_ptr, &file_intent))
{
if(keep_chunk("sRGB",argv))
{
png_set_sRGB(write_ptr, write_info_ptr, file_intent);
intent=file_intent;
}
}
else if(intent >= 0)
{
#ifdef PNG_gAMA_SUPPORTED
#ifdef PNG_FIXED_POINT_SUPPORTED
if(file_gamma >= 45000L && file_gamma <= 46000L)
#else
if(file_gamma >= 0.45000 && file_gamma <= 0.46000)
#endif
{
things_have_changed=1;
if(first_trial)
fprintf(STDERR, " Inserting sRGB chunk with intent=%d\n",intent);
png_set_sRGB(write_ptr, write_info_ptr, intent);
}
else if(file_gamma == 0)
{
things_have_changed=1;
png_set_sRGB_gAMA_and_cHRM(write_ptr, write_info_ptr, intent);
}
else
{
if(first_trial)
{
fprintf(STDERR,
#ifdef PNG_FIXED_POINT_SUPPORTED
" Ignoring sRGB request; gamma=(%lu/100000) is not approx. 0.455\n",
#else
" Ignoring sRGB request; gamma=%f is not approx. 0.455\n",
#endif
file_gamma);
}
}
#endif
}
}
#endif
#if defined(PNG_READ_iCCP_SUPPORTED) && defined(PNG_WRITE_iCCP_SUPPORTED)
if(intent < 0) /* ignore iCCP if sRGB is being written */
{
png_charp name;
png_charp profile;
png_uint_32 proflen;
int compression_method;
if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_method,
&profile, &proflen))
{
P1 ("Got iccp chunk, proflen=%lu\n",proflen);
if(iccp_length)
P0 ("Will not replace existing iccp chunk.\n");
if(keep_chunk("iCCP",argv))
png_set_iCCP(write_ptr, write_info_ptr, name, compression_method,
profile, proflen);
}
#ifdef PNG_iCCP_SUPPORTED
else if (iccp_length)
{
png_set_iCCP(write_ptr, write_info_ptr, iccp_name, 0,
iccp_text, iccp_length);
P1 ("Wrote iccp chunk, proflen=%d\n",iccp_length);
}
#endif
}
#endif
#if defined(PNG_READ_oFFs_SUPPORTED) && defined(PNG_WRITE_oFFs_SUPPORTED)
{
#if PNG_LIBPNG_VER < 10006
png_uint_32 offset_x, offset_y;
#else
png_int_32 offset_x, offset_y;
#endif
int unit_type;
if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type))
{
if(offset_x == 0 && offset_y == 0)
{
if(verbose > 0 && first_trial)
fprintf(STDERR, " Deleting useless oFFs 0 0 chunk\n");
}
else
{
if(keep_chunk("oFFs",argv))
png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y,
unit_type);
}
}
}
#endif
#if defined(PNG_READ_pCAL_SUPPORTED) && defined(PNG_WRITE_pCAL_SUPPORTED)
{
png_charp purpose, units;
png_charpp params;
png_int_32 X0, X1;
int type, nparams;
if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type,
&nparams, &units, &params))
{
if(keep_chunk("pCAL",argv))
png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type,
nparams, units, params);
}
}
#endif
#if defined(PNG_READ_pHYs_SUPPORTED) && defined(PNG_WRITE_pHYs_SUPPORTED)
{
png_uint_32 res_x, res_y;
int unit_type;
if(resolution == 0)
{
if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y,
&unit_type))
{
if(res_x == 0 && res_y == 0)
{
if(verbose > 0 && first_trial)
fprintf(STDERR, " Deleting useless pHYs 0 0 chunk\n");
}
else
{
if(keep_chunk("pHYs",argv))
png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type);
}
}
}
else
{
unit_type=1;
res_x = res_y = (png_uint_32)((resolution/.0254 + 0.5));
png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type);
if(verbose > 0 && first_trial)
fprintf(STDERR, " Added pHYs %lu %lu 1 chunk\n",res_x,res_y);
}
}
#endif
#if defined(PNG_READ_hIST_SUPPORTED) && defined(PNG_WRITE_hIST_SUPPORTED)
{
png_uint_16p hist;
if (png_get_hIST(read_ptr, read_info_ptr, &hist))
{
if(keep_chunk("hIST",argv))
png_set_hIST(write_ptr, write_info_ptr, hist);
}
}
#endif
#if defined(PNG_READ_tRNS_SUPPORTED) && defined(PNG_WRITE_tRNS_SUPPORTED)
{
png_bytep trans;
int num_trans;
png_color_16p trans_values;
if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans,
&trans_values))
{
if(verbose > 1)
fprintf(STDERR," Found tRNS chunk in input file.\n");
if (have_trns == 1)
P0 (" Will not overwrite existing tRNS chunk.\n");
if(keep_chunk("tRNS",argv))
{
int last_nonmax = -1;
trns_red = trans_values->red;
trns_green = trans_values->green;
trns_blue = trans_values->blue;
trns_gray = trans_values->gray;
if(output_color_type == 3)
{
for (ia=0;ia<num_trans;ia++)
trns_array[ia]=trans[ia];
for ( ; ia<256; ia++)
trns_array[ia]=255;
for (ia=0; ia<256; ia++)
{
if(trns_array[ia] != 255)
last_nonmax=ia;
}
if(first_trial && verbose > 0)
{
if(last_nonmax < 0)
fprintf(STDERR," Deleting all-opaque tRNS chunk.\n");
else if(last_nonmax+1 < num_trans)
fprintf(STDERR,
" Truncating trailing opaque entries from tRNS chunk.\n");
}
num_trans = last_nonmax+1;
}
if(verbose > 1)
fprintf(STDERR," png_set_tRNS, num_trans=%d\n",num_trans);
if (output_color_type != 3 || num_trans)
png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans,
trans_values);
}
}
else if (have_trns == 1) /* will not overwrite existing trns data */
{
png_color_16 trans_data;
png_byte index_data = (png_byte)trns_index;
num_trans = index_data+1;
if(verbose > 1)
fprintf(STDERR,"Have_tRNS, num_trans=%d\n",num_trans);
if (output_color_type == 3)
{
trans_values=NULL;
for (ia=0;ia<num_trans;ia++)
trns_array[ia]=trans_in[ia];
for ( ; ia<256; ia++)
trns_array[ia]=255;
}
else
{
for (ia=0;ia<256;ia++)
trns_array[ia]=255;
trns_array[index_data]=0;
trans_data.index = index_data;
trans_data.red = trns_red;
trans_data.green = trns_green;
trans_data.blue = trns_blue;
trans_data.gray = trns_gray;
trans_values = &trans_data;
}
P0 (" Adding a tRNS chunk\n");
png_set_tRNS(write_ptr, write_info_ptr, trns_array, num_trans,
trans_values);
things_have_changed=1;
}
else
{
for (ia=0 ; ia<256; ia++)
trns_array[ia]=255;
}
if (verbose > 1 && first_trial)
{
int last=-1;
for (i=0 ; ia<num_palette; ia++)
if(trns_array[ia] != 255) last = ia;
if(last >= 0)
{
fprintf(STDERR, " Transparency:\n");
if(output_color_type == 3)
for (i=0 ; ia<num_palette; ia++)
fprintf(STDERR, " %4d %4d\n",ia,trns_array[ia]);
else if(output_color_type == 0)
fprintf(STDERR, " %d\n",trns_gray);
else if(output_color_type == 2)
fprintf(STDERR, " %d %d %d\n",
trns_red, trns_green, trns_blue);
}
}
}
#endif
if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette))
{
if (plte_len > 0)
num_palette=plte_len;
if (do_pplt)
{
printf("PPLT: %s\n",pplt_string);
printf("Sorry, PPLT is not implemented yet.\n");
}
if(output_color_type == 3)
png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
else if(keep_chunk("PLTE",argv))
png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
if(verbose > 1 && first_trial)
{
int i;
png_colorp p = palette;
fprintf(STDERR, " Palette:\n");
fprintf(STDERR, " I R G B ( color ) A\n");
for (i=0; i<num_palette; i++)
{
fprintf(STDERR, " %4d %4d %4d %4d (#%2.2x%2.2x%2.2x) %4d\n",
i, p->red, p->green, p->blue,
p->red, p->green, p->blue,
trns_array[i]);
p++;
}
}
}
#if defined(PNG_READ_sBIT_SUPPORTED) && defined(PNG_WRITE_sBIT_SUPPORTED)
{
png_color_8p sig_bit;
/* If we are reducing a truecolor PNG to grayscale, and the
RGB sBIT values aren't identical, we'll lose sBIT info. */
if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit))
{
if(keep_chunk("sBIT",argv))
{
if((input_color_type == 0 || input_color_type == 4) &&
(output_color_type == 2 || output_color_type == 6 ||
output_color_type == 3))
sig_bit->red = sig_bit->green = sig_bit->blue
= sig_bit->gray;
if((input_color_type == 2 || input_color_type == 6 ||
output_color_type == 3) &&
(output_color_type == 0 || output_color_type == 4))
sig_bit->gray = sig_bit->green;
if((input_color_type == 0 || input_color_type == 2) &&
(output_color_type == 4 || output_color_type == 6))
sig_bit->alpha=1;
png_set_sBIT(write_ptr, write_info_ptr, sig_bit);
}
}
}
#endif
#if defined(PNG_sCAL_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
{
int unit;
double scal_width, scal_height;
if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, &scal_height))
{
png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height);
}
}
#else
#ifdef PNG_FIXED_POINT_SUPPORTED
{
int unit;
png_charp scal_width, scal_height;
if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, &scal_height))
{
if(keep_chunk("sCAL",argv))
png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height);
}
}
#endif
#endif
#endif
#if defined(PNG_sPLT_SUPPORTED)
{
png_sPLT_tp entries;
int num_entries;
num_entries = (int)png_get_sPLT(read_ptr, read_info_ptr, &entries);
if (num_entries)
{
if(keep_chunk("sPLT",argv))
png_set_sPLT(write_ptr, write_info_ptr, entries, num_entries);
png_free_data(read_ptr, read_info_ptr, PNG_FREE_SPLT, num_entries);
}
}
#endif
#if defined(PNG_TEXT_SUPPORTED)
{
png_textp text_ptr;
int num_text=0;
if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0 ||
text_inputs)
{
int ntext;
png_debug1(0, "Handling %d tEXt/zTXt chunks\n", num_text);
if (verbose > 1 && first_trial && num_text > 0)
{
for (ntext = 0; ntext < num_text; ntext++)
{
fprintf(STDERR,"%d %s",ntext,text_ptr[ntext].key);
if(text_ptr[ntext].text_length)
fprintf(STDERR,": %s\n",text_ptr[ntext].text);
#ifdef PNG_iTXt_SUPPORTED
else if (text_ptr[ntext].itxt_length)
{
fprintf(STDERR," (%s: %s): \n",
text_ptr[ntext].lang,
text_ptr[ntext].lang_key);
fprintf(STDERR,"%s\n",text_ptr[ntext].text);
}
#endif
else
fprintf(STDERR,"\n");
}
}
if(num_text > 0)
{
if(keep_chunk("text",argv))
{
int num_to_write=num_text;
for (ntext = 0; ntext < num_text; ntext++)
{
if (first_trial)
P2("Text chunk before IDAT, compression=%d\n",
text_ptr[ntext].compression);
if(text_ptr[ntext].compression==PNG_TEXT_COMPRESSION_NONE)
{
if(!keep_chunk("tEXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
if(text_ptr[ntext].compression==PNG_TEXT_COMPRESSION_zTXt)
{
if(!keep_chunk("zTXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
#ifdef PNG_iTXt_SUPPORTED
if(text_ptr[ntext].compression==PNG_ITXT_COMPRESSION_NONE
||text_ptr[ntext].compression==PNG_ITXT_COMPRESSION_zTXt)
{
if(!keep_chunk("iTXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
#endif
}
if (num_to_write > 0)
png_set_text(write_ptr, write_info_ptr, text_ptr,
num_text);
}
}
for (ntext=0; ntext<text_inputs; ntext++)
{
if(text_where[ntext] == 1)
{
png_textp added_text;
added_text = (png_textp)
png_malloc(write_ptr, (png_uint_32)sizeof(png_text));
added_text[0].key = &text_keyword[ntext*80];
#ifdef PNG_iTXt_SUPPORTED
added_text[0].lang = &text_lang[ntext*80];
added_text[0].lang_key = &text_lang_key[ntext*80];
#endif
added_text[0].text = &text_text[ntext*2048];
added_text[0].compression = text_compression[ntext];
png_set_text(write_ptr, write_info_ptr, added_text, 1);
if(added_text[0].compression < 0)
printf(" Added a tEXt chunk.\n");
else if(added_text[0].compression == 0)
printf(" Added a zTXt chunk.\n");
#ifdef PNG_iTXt_SUPPORTED
else if(added_text[0].compression == 1)
printf(" Added an uncompressed iTXt chunk.\n");
else
printf(" Added a compressed iTXt chunk.\n");
#endif
png_free(write_ptr,added_text); added_text=(png_textp)NULL;
}
}
}
}
#endif
#if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED)
{
png_timep mod_time;
if (png_get_tIME(read_ptr, read_info_ptr, &mod_time))
{
if(keep_chunk("tIME",argv))
png_set_tIME(write_ptr, write_info_ptr, mod_time);
}
}
#endif
#endif /* PNG_LIBPNG_VER > 90 */
png_read_transform_info(read_ptr, read_info_ptr);
if(nosave == 0)
{
if (filter_type == 0)png_set_filter(write_ptr,0,PNG_FILTER_NONE);
else if(filter_type == 1)png_set_filter(write_ptr,0,PNG_FILTER_SUB);
else if(filter_type == 2)png_set_filter(write_ptr,0,PNG_FILTER_UP);
else if(filter_type == 3)png_set_filter(write_ptr,0,PNG_FILTER_AVG);
else if(filter_type == 4)png_set_filter(write_ptr,0,PNG_FILTER_PAETH);
else if(filter_type == 5)png_set_filter(write_ptr,0,PNG_ALL_FILTERS);
else png_set_filter(write_ptr,0,PNG_FILTER_NONE);
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
{
png_unknown_chunkp unknowns;
int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr,
&unknowns);
if (num_unknowns)
{
int i;
png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns,
num_unknowns);
for (i = 0; i < num_unknowns; i++)
png_set_unknown_chunk_location(write_ptr, write_info_ptr,
i, (int)unknowns[i].location);
}
}
#endif
#ifdef PNGCRUSH_LOCO
if(do_loco)
{
png_byte buffer[30];
const png_byte png_MHDR[5] = { 77, 72, 68, 82, '\0'};
png_byte mng_signature[8] = {138, 77, 78, 71, 13, 10, 26, 10};
/* write the MNG 8 byte signature */
if(outname[strlen(outname)-3] == 'p')
png_warning(read_ptr, " Writing a MNG file with a .png extension");
png_write_data(write_ptr, &mng_signature[0], (png_size_t)8);
png_set_sig_bytes(write_ptr, 8);
/* Write a MHDR chunk */
buffer[0]=(png_byte)((width>>24) & 0xff);
buffer[1]=(png_byte)((width>>16) & 0xff);
buffer[2]=(png_byte)((width>> 8) & 0xff);
buffer[3]=(png_byte)((width ) & 0xff);
buffer[4]=(png_byte)((height>>24) & 0xff);
buffer[5]=(png_byte)((height>>16) & 0xff);
buffer[6]=(png_byte)((height>> 8) & 0xff);
buffer[7]=(png_byte)((height ) & 0xff);
for (i=8; i<27; i++)
buffer[i]=0x00;
buffer[15]=2; /* layer count */
buffer[19]=1; /* frame count */
if(output_color_type == 6)
buffer[27]=0x09; /* simplicity profile: MNG-VLC with transparency */
else
buffer[27]=0x01; /* simplicity profile: MNG-VLC */
png_write_chunk(write_ptr, (png_bytep)png_MHDR, buffer, (png_size_t)28);
}
#endif
P2("writing info structure.\n");
png_crush_pause();
png_debug(0, "\nWriting info struct\n");
#if 0 /* doesn't work; compression level has to be the same as in IDAT */
/* if zTXt other compressed chunk */
png_set_compression_level(write_ptr, 9);
png_set_compression_window_bits(write_ptr, 15);
#endif
png_crush_pause();
{
int compression_window;
#if (PNG_LIBPNG_VER >= 10000)
png_uint_32 zbuf_size;
#endif
png_uint_32 required_window;
int channels=0;
png_set_compression_strategy(write_ptr, z_strategy);
png_set_compression_mem_level(write_ptr, compression_mem_level);
if (output_color_type == 0)channels=1;
if (output_color_type == 2)channels=3;
if (output_color_type == 3)channels=1;
if (output_color_type == 4)channels=2;
if (output_color_type == 6)channels=4;
required_window=
(png_uint_32)(height*((width*channels*bit_depth+15)>>3)+262);
#if (PNG_LIBPNG_VER >= 10000)
zbuf_size = png_get_compression_buffer_size(write_ptr);
/* reinitialize zbuf - compression buffer */
if (zbuf_size != max_idat_size)
{
png_uint_32 max_possible_size=required_window;
if(max_possible_size > max_idat_size)
max_possible_size=max_idat_size;
P2("reinitializing write zbuf to %lu.\n",max_possible_size);
png_set_compression_buffer_size(write_ptr, max_possible_size);
}
#endif
#ifdef WBITS_8_OK
if (required_window <= 256)compression_window = 8;
else if(required_window <= 512)compression_window = 9;
#else
if (required_window <= 512)compression_window = 9;
#endif
else if(required_window <= 1024)compression_window = 10;
else if(required_window <= 2048)compression_window = 11;
else if(required_window <= 4096)compression_window = 12;
else if(required_window <= 8192)compression_window = 13;
else if(required_window <= 16384)compression_window = 14;
else compression_window = 15;
if(compression_window > default_compression_window ||
force_compression_window)
compression_window = default_compression_window;
if(verbose > 1 && first_trial && (compression_window != 15 ||
force_compression_window))
fprintf(STDERR, " Compression window for output= %d\n",
1 << compression_window);
png_set_compression_window_bits(write_ptr, compression_window);
}
png_set_compression_level(write_ptr, zlib_level);
png_write_info(write_ptr, write_info_ptr);
png_debug(0, "\nWrote info struct\n");
P2("wrote info structure.\n");
#ifdef PNG_WRITE_PACK_SUPPORTED
if(output_bit_depth < 8)
{
if(output_color_type == 0)
{
png_color_8 true_bits;
true_bits.gray = (png_byte)(output_bit_depth);
png_set_shift(write_ptr, &true_bits);
}
png_set_packing(write_ptr);
}
#endif
} /* no save */
#define LARGE_PNGCRUSH
#ifdef PNGCRUSH_MULTIPLE_ROWS
rows_at_a_time=max_rows_at_a_time;
if(rows_at_a_time == 0 || rows_at_a_time < height)
rows_at_a_time=height;
#endif
#ifndef LARGE_PNGCRUSH
{
png_uint_32 rowbytes_s;
png_uint_32 rowbytes;
rowbytes = png_get_rowbytes(read_ptr, read_info_ptr);
rowbytes_s = (png_size_t)rowbytes;
if(rowbytes == (png_uint_32)rowbytes_s)
#ifdef PNGCRUSH_MULTIPLE_ROWS
row_buf = png_malloc(read_ptr, rows_at_a_time*rowbytes+16);
#else
row_buf = png_malloc(read_ptr, rowbytes+16);
#endif
else
row_buf = NULL;
}
#else
{
png_uint_32 read_row_length, write_row_length;
read_row_length=
(png_uint_32)(png_get_rowbytes(read_ptr, read_info_ptr));
write_row_length=
(png_uint_32)(png_get_rowbytes(write_ptr, write_info_ptr));
row_length = read_row_length > write_row_length ?
read_row_length : write_row_length;
#ifdef PNGCRUSH_MULTIPLE_ROWS
row_buf = (png_bytep)png_malloc(read_ptr,rows_at_a_time*row_length+16);
#else
row_buf = (png_bytep)png_malloc(read_ptr,row_length+16);
#endif
}
#endif
if (row_buf == NULL)
png_error(read_ptr, "Insufficient memory to allocate row buffer");
{
/* check for sufficient memory: we need 2*zlib_window
and, if filter_type == 5, 4*rowbytes in separate allocations.
If it's not enough we can drop the "average" filter and
we can reduce the zlib_window for writing. We can't change
the input zlib_window because the input file might have
used the full 32K sliding window.
*/
}
#ifdef PNGCRUSH_MULTIPLE_ROWS
row_pointers = (png_bytepp)png_malloc(read_ptr,
rows_at_a_time*sizeof(png_bytepp));
for (i=0; i<rows_at_a_time; i++)
row_pointers[i]=row_buf + i*row_length;
#endif
P2("allocated rowbuf.\n");
png_crush_pause();
num_pass = png_set_interlace_handling(read_ptr);
if(nosave == 0)
png_set_interlace_handling(write_ptr);
t_stop = (TIME_T)clock();
t_misc += (t_stop - t_start);
t_start = t_stop;
for (pass = 0; pass < num_pass; pass++)
{
#ifdef PNGCRUSH_MULTIPLE_ROWS
png_uint_32 num_rows;
#endif
png_debug(0, "\nBegin Pass\n");
#ifdef PNGCRUSH_MULTIPLE_ROWS
num_rows=rows_at_a_time;
for (y = 0; y < height; y+=rows_at_a_time)
#else
for (y = 0; y < height; y++)
#endif
{
#ifdef PNGCRUSH_MULTIPLE_ROWS
if (y+num_rows > height) num_rows=height-y;
png_read_rows(read_ptr, row_pointers, (png_bytepp)NULL, num_rows);
#else
png_read_row(read_ptr, row_buf, (png_bytep)NULL);
#endif
if(nosave == 0)
{
t_stop = (TIME_T)clock();
t_decode += (t_stop - t_start);
t_start = t_stop;
#ifdef PNGCRUSH_MULTIPLE_ROWS
png_write_rows(write_ptr, row_pointers, num_rows);
#else
png_write_row(write_ptr, row_buf);
#endif
t_stop = (TIME_T)clock();
t_encode += (t_stop - t_start);
t_start = t_stop;
}
}
png_debug(0, "\nEnd Pass\n");
}
if(nosave)
{
t_stop = (TIME_T)clock();
t_decode += (t_stop - t_start);
t_start = t_stop;
}
#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) && \
defined(PNG_FLOATING_POINT_SUPPORTED)
if((color_type == 2 || color_type == 6 || color_type == 3) &&
(output_color_type == 0 || output_color_type == 4))
{
png_byte rgb_error = png_get_rgb_to_gray_status(read_ptr);
if((first_trial) && rgb_error)
printf(" **** Converted non-gray image to gray. **** \n");
}
#endif
#ifdef PNG_FREE_UNKN
# if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1);
# endif
# if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1);
# endif
#else
# if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
png_free_unknown_chunks(read_ptr, read_info_ptr, -1);
# endif
# if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
png_free_unknown_chunks(write_ptr, write_info_ptr, -1);
# endif
#endif
png_debug(0, "Reading and writing end_info data\n");
png_read_end(read_ptr, end_info_ptr);
#if (PNG_LIBPNG_VER > 90)
#if (defined(PNG_READ_tEXt_SUPPORTED) && defined(PNG_WRITE_tEXt_SUPPORTED)) || \
(defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED))
{
png_textp text_ptr;
int num_text=0;
if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0 ||
text_inputs)
{
int ntext;
png_debug1(0, "Handling %d tEXt/zTXt chunks\n", num_text);
if (verbose > 1 && first_trial && num_text > 0)
{
for (ntext = 0; ntext < num_text; ntext++)
{
fprintf(STDERR,"%d %s",ntext,text_ptr[ntext].key);
if(text_ptr[ntext].text_length)
fprintf(STDERR,": %s\n",text_ptr[ntext].text);
#ifdef PNG_iTXt_SUPPORTED
else if (text_ptr[ntext].itxt_length)
{
fprintf(STDERR," (%s: %s): \n",
text_ptr[ntext].lang,
text_ptr[ntext].lang_key);
fprintf(STDERR,"%s\n",text_ptr[ntext].text);
}
#endif
else
fprintf(STDERR,"\n");
}
}
if(num_text > 0)
{
if(keep_chunk("text",argv))
{
int num_to_write=num_text;
for (ntext = 0; ntext < num_text; ntext++)
{
if (first_trial)
P2("Text chunk after IDAT, compression=%d\n",
text_ptr[ntext].compression);
if(text_ptr[ntext].compression==PNG_TEXT_COMPRESSION_NONE)
{
if(!keep_chunk("tEXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
if(text_ptr[ntext].compression==PNG_TEXT_COMPRESSION_zTXt)
{
if(!keep_chunk("zTXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
#ifdef PNG_iTXt_SUPPORTED
if(text_ptr[ntext].compression==PNG_ITXT_COMPRESSION_NONE
||text_ptr[ntext].compression==PNG_ITXT_COMPRESSION_zTXt)
{
if(!keep_chunk("iTXt",argv))
{
text_ptr[ntext].key[0]='\0';
num_to_write--;
}
}
#endif
}
if (num_to_write > 0)
png_set_text(write_ptr, write_end_info_ptr, text_ptr,
num_text);
}
}
for (ntext=0; ntext<text_inputs; ntext++)
{
if(text_where[ntext] == 2)
{
png_textp added_text;
added_text = (png_textp)
png_malloc(write_ptr, (png_uint_32)sizeof(png_text));
added_text[0].key = &text_keyword[ntext*80];
#ifdef PNG_iTXt_SUPPORTED
added_text[0].lang = &text_lang[ntext*80];
added_text[0].lang_key = &text_lang_key[ntext*80];
#endif
added_text[0].text = &text_text[ntext*2048];
added_text[0].compression = text_compression[ntext];
png_set_text(write_ptr, write_end_info_ptr, added_text, 1);
if(added_text[0].compression < 0)
printf(" Added a tEXt chunk.\n");
else if(added_text[0].compression == 0)
printf(" Added a zTXt chunk.\n");
#ifdef PNG_iTXt_SUPPORTED
else if(added_text[0].compression == 1)
printf(" Added an uncompressed iTXt chunk.\n");
else
printf(" Added a compressed iTXt chunk.\n");
#endif
png_free(write_ptr,added_text); added_text=(png_textp)NULL;
}
}
}
}
#endif
#if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED)
{
png_timep mod_time;
if (png_get_tIME(read_ptr, end_info_ptr, &mod_time))
{
if(keep_chunk("tIME",argv))
png_set_tIME(write_ptr, write_end_info_ptr, mod_time);
}
}
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
{
png_unknown_chunkp unknowns;
int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr,
&unknowns);
if (num_unknowns && nosave == 0)
{
int i;
printf("setting %d unknown chunks after IDAT\n",num_unknowns);
png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns,
num_unknowns);
for (i = 0; i < num_unknowns; i++)
png_set_unknown_chunk_location(write_ptr, write_end_info_ptr,
i, (int)unknowns[i].location);
}
}
#endif
#endif /* PNG_LIBPNG_VER > 90 */
if(nosave == 0)
{
#if 0 /* doesn't work; compression level has to be the same as in IDAT */
/* if zTXt other compressed chunk */
png_set_compression_level(write_ptr, 9);
png_set_compression_window_bits(write_ptr, 15);
png_set_compression_buffer_size(write_ptr, PNG_ZBUF_SIZE);
png_set_compression_strategy(write_ptr, 0);
#endif
png_write_end(write_ptr, write_end_info_ptr);
}
png_debug(0, "Destroying data structs\n");
if(row_buf != (png_bytep)NULL)
{
png_free(read_ptr, row_buf); row_buf = (png_bytep)NULL;
}
#ifdef PNGCRUSH_MULTIPLE_ROWS
if(row_pointers != (png_bytepp)NULL)
{
png_free(read_ptr, row_pointers); row_pointers = (png_bytepp)NULL;
}
#endif
png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
if(nosave == 0)
{
#ifdef PNGCRUSH_LOCO
if(do_loco)
{
const png_byte png_MEND[5] = { 77, 69, 78, 68, '\0'};
/* write the MNG MEND chunk */
png_write_chunk(write_ptr, (png_bytep)png_MEND, NULL, (png_size_t)0);
}
#endif
png_destroy_info_struct(write_ptr, &write_end_info_ptr);
png_destroy_write_struct(&write_ptr, &write_info_ptr);
}
}
Catch (msg)
{
if(nosave == 0)
fprintf(stderr, "While converting %s to %s:\n", inname, outname);
else
fprintf(stderr, "While reading %s:\n", inname);
fprintf(stderr, " pngcrush caught libpng error:\n %s\n\n",msg);
if(row_buf)
{
png_free(read_ptr, row_buf); row_buf = (png_bytep)NULL;
}
#ifdef PNGCRUSH_MULTIPLE_ROWS
if(row_pointers != (png_bytepp)NULL)
{
png_free(read_ptr, row_pointers); row_pointers = (png_bytepp)NULL;
}
#endif
if(nosave == 0)
{
png_destroy_info_struct(write_ptr, &write_end_info_ptr);
png_destroy_write_struct(&write_ptr, &write_info_ptr);
FCLOSE(fpout);
setfiletype(outname);
}
png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
FCLOSE(fpin);
if(verbose > 1)
fprintf(stderr, "returning after cleanup\n");
trial = MAX_METHODS+1;
}
read_ptr=NULL;
write_ptr=NULL;
FCLOSE(fpin);
if(nosave == 0)
{
FCLOSE(fpout);
setfiletype(outname);
}
if(nosave)
break;
first_trial=0;
if (nosave == 0)
{
png_debug(0, "Opening file for length measurement\n");
if ((fpin = FOPEN(outname, "rb")) == NULL)
{
fprintf(STDERR, "Could not find file %s\n", outname);
if(png_row_filters != NULL)
{
free(png_row_filters); png_row_filters=NULL;
}
exit(1);
}
number_of_open_files++;
idat_length[trial]=measure_idats(fpin);
FCLOSE(fpin);
}
if(verbose > 0 && trial != MAX_METHODS)
{
fprintf(STDERR,
" IDAT length with method %3d (fm %d zl %d zs %d)= %8lu\n",
trial,filter_type,zlib_level,z_strategy,idat_length[trial]);
fflush(STDERR);
}
} /* end of trial-loop */
if (fpin)
{
FCLOSE(fpin);
}
if (nosave == 0 && fpout)
{
FCLOSE(fpout);
setfiletype(outname);
}
if(verbose > 0 && nosave == 0)
{
png_uint_32 input_length, output_length;
#ifdef __riscos
input_length = (unsigned long)filesize(inname);
output_length = (unsigned long)filesize(outname);
#else
struct stat stat_buf;
stat(inname, &stat_buf);
input_length = (unsigned long)stat_buf.st_size;
stat(outname, &stat_buf);
output_length = (unsigned long)stat_buf.st_size;
#endif
total_input_length += input_length + output_length;
if(input_length == output_length)
fprintf(STDERR,
" Best pngcrush method = %d for %s (no change)\n\n",
best, outname);
else if(input_length > output_length)
fprintf(STDERR,
" Best pngcrush method = %d for %s (%4.2f%% reduction)\n\n",
best, outname,
(100.0 - (100.0*output_length)/input_length));
else
fprintf(STDERR,
" Best pngcrush method = %d for %s (%4.2f%% increase)\n\n",
best, outname,
-(100.0 - (100.0*output_length)/input_length));
if(verbose > 2)
fprintf(STDERR, " Number of open files=%d\n",number_of_open_files);
}
if(pngcrush_mode == DEFAULT_MODE)
{
if(png_row_filters != NULL)
{
free(png_row_filters); png_row_filters=NULL;
}
if(verbose > 0) show_result();
#ifdef PNG_iCCP_SUPPORTED
if(iccp_length)
free(iccp_text);
#endif
if(pngcrush_must_exit)
exit(0);
return(0);
}
} /* end of loop on input files */
}
png_uint_32
measure_idats(FILE *fpin)
{
/* Copyright (C) 1999-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)
See notice in pngcrush.c for conditions of use and distribution */
P2("measure_idats:\n");
png_debug(0, "Allocating read structure\n");
Try
{
read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
png_debug(0, "Allocating read_info, end_info structures\n");
read_info_ptr = png_create_info_struct(read_ptr);
end_info_ptr = png_create_info_struct(read_ptr);
#if !defined(PNG_NO_STDIO)
png_init_io(read_ptr, fpin);
#else
png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data);
#endif
png_set_sig_bytes(read_ptr, 0);
measured_idat_length=png_measure_idat(read_ptr);
P2("measure_idats: IDAT length=%lu\n",measured_idat_length);
png_debug(0, "Destroying data structs\n");
png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
}
Catch (msg)
{
fprintf(STDERR, "\nWhile reading %s ", inname);
fprintf(STDERR,"pngcrush caught libpng error:\n %s\n\n",msg);
png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
png_debug(0, "Destroyed data structs\n");
measured_idat_length=0;
}
return measured_idat_length;
}
png_uint_32
png_measure_idat(png_structp png_ptr)
{
/* Copyright (C) 1999-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)
See notice in pngcrush.c for conditions of use and distribution */
png_uint_32 sum_idat_length=0;
{
png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
#if defined(PNGCRUSH_LOCO)
png_byte mng_signature[8] = {138, 77, 78, 71, 13, 10, 26, 10};
#endif
png_read_data(png_ptr, png_signature, 8);
png_set_sig_bytes(png_ptr, 8);
#if defined(PNGCRUSH_LOCO)
if (!(int)(png_memcmp(mng_signature, png_signature, 8)))
{
png_byte buffer[40];
/* skip the MHDR */
png_read_data(read_ptr, buffer, 40);
input_format=1;
}
else
#endif
if (png_sig_cmp(png_signature, 0, 8))
{
if (png_sig_cmp(png_signature, 0, 4))
png_error(png_ptr, "Not a PNG file..");
else
png_error(png_ptr, "PNG file corrupted by ASCII conversion");
}
}
#ifdef PNG_CRC_WARN_USE
if(fix)
png_set_crc_action(png_ptr, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
#endif
for(;;)
{
#ifndef PNG_UINT_IDAT
#ifdef PNG_USE_LOCAL_ARRAYS
PNG_IDAT;
PNG_IEND;
PNG_IHDR;
#ifdef PNG_iCCP_SUPPORTED
PNG_iCCP;
#else
const png_byte png_iCCP[5]={105, 67, 67, 80, '\0'};
#endif
#endif
#endif
png_byte chunk_name[5];
png_byte chunk_length[4];
png_byte buffer[32];
png_uint_32 length;
png_read_data(png_ptr, chunk_length, 4);
length = png_get_uint_32(chunk_length);
png_reset_crc(png_ptr);
png_crc_read(png_ptr, chunk_name, 4);
#ifdef PNG_UINT_IDAT
if (png_get_uint_32(chunk_name) == PNG_UINT_IDAT)
#else
if (!png_memcmp(chunk_name, png_IDAT, 4))
#endif
{
sum_idat_length += length;
if(length > crushed_idat_size)
already_crushed++;
}
if(verbose > 1)
{
chunk_name[4]='\0';
printf( "Reading %s chunk, length = %ld.\n", chunk_name, length);
}
#ifdef PNG_UINT_IDAT
if (png_get_uint_32(chunk_name) == PNG_UINT_IHDR)
#else
if (!png_memcmp(chunk_name, png_IHDR, 4))
#endif
{
/* get the color type */
png_crc_read(png_ptr, buffer, 13);
length-=13;
input_color_type=buffer[9];
}
#ifdef PNG_iCCP_SUPPORTED
/* check for bad photoshop iccp chunk */
#ifdef PNG_UINT_IDAT
if (png_get_uint_32(chunk_name) == PNG_UINT_iCCP)
#else
if (!png_memcmp(chunk_name, png_iCCP, 4))
#endif
{
/* Check for bad Photoshop iCCP chunk. Libpng will reject the
* bad chunk because the Adler-32 bytes are missing, but we check
* here to see if it's really the sRGB profile, and if so, set the
* "intent" flag and gamma so pngcrush will write an sRGB chunk
* and a gamma chunk.
*/
if (length == 2615)
{
png_crc_read(png_ptr, buffer, 22);
length-=22;
buffer[23]=0;
if(!strncmp((png_const_charp)buffer, "Photoshop ICC profile",21))
{
printf(
" Replacing bad Photoshop ICCP chunk with an sRGB chunk\n");
#ifdef PNG_gAMA_SUPPORTED
#ifdef PNG_FIXED_POINT_SUPPORTED
image_specified_gamma=45455L;
#else
image_specified_gamma=0.45455;
#endif
#endif
intent=0;
}
}
}
#endif
png_crc_finish(png_ptr, length);
#ifdef PNG_UINT_IEND
if (png_get_uint_32(chunk_name) == PNG_UINT_IEND)
#else
if (!png_memcmp(chunk_name, png_IEND, 4))
#endif
return sum_idat_length;
}
}
#ifdef PNGCRUSH_COUNT_COLORS
#define USE_HASHCODE
int
count_colors(FILE *fpin)
{
/* Copyright (C) 2000-2001 Glenn Randers-Pehrson (randeg@alum.rpi.edu)
See notice in pngcrush.c for conditions of use and distribution */
int bit_depth, color_type, interlace_method, filter_method, compression_method;
png_uint_32 rowbytes;
volatile png_uint_32 channels;
int i;
int pass, num_pass;
int ret;
volatile int result, hashmiss, hashinserts;
png_uint_32 rgba_frequency[257];
png_uint_32 rgba_hi[257]; /* Actually contains ARGB not RGBA */
#if 0
png_uint_32 rgba_lo[257]; /* Low bytes of ARGB in 16-bit PNGs */
#endif
/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
/* start of interlace block */
int png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
/* offset to next interlace block */
int png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
/* start of interlace block in the y direction */
int png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
/* offset to next interlace block in the y direction */
int png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
result=0;
reduce_to_gray=1;
it_is_opaque=1;
hashmiss=0;
hashinserts=0;
row_buf = (png_bytep)NULL;
num_rgba=0;
for (i=0; i<257; i++)
{
rgba_frequency[i]=0;
}
P2("Checking alphas:\n");
png_debug(0, "Allocating read structure\n");
Try
{
read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
if (read_ptr)
{
png_debug(0, "Allocating read_info structure\n");
read_info_ptr = png_create_info_struct(read_ptr);
if (read_info_ptr == NULL)
png_destroy_read_struct(&read_ptr, (png_infopp)NULL, (png_infopp)NULL);
}
else
read_info_ptr = NULL;
if(read_info_ptr)
{
#ifdef USE_HASHCODE
int hash[16385];
#endif
#ifdef USE_HASHCODE
for (i=0; i<16385; i++)
hash[i]=-1;
#endif
end_info_ptr = NULL;
#if !defined(PNG_NO_STDIO)
png_init_io(read_ptr, fpin);
#else
png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data);
#endif
{
#if defined(PNGCRUSH_LOCO)
png_byte mng_signature[8] = {138, 77, 78, 71, 13, 10, 26, 10};
#endif
png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
png_read_data(read_ptr, png_signature, 8);
png_set_sig_bytes(read_ptr, 8);
#if defined(PNGCRUSH_LOCO)
if (!(int)(png_memcmp(mng_signature, png_signature, 8)))
{
png_byte buffer[40];
/* skip the MHDR */
png_read_data(read_ptr, buffer, 40);
png_permit_mng_features (read_ptr, PNG_FLAG_MNG_FILTER_64);
input_format=1;
}
else
#endif
if (png_sig_cmp(png_signature, 0, 8))
{
if (png_sig_cmp(png_signature, 0, 4))
png_error(read_ptr, "Not a PNG file.");
else
png_error(read_ptr, "PNG file corrupted by ASCII conversion");
}
}
png_read_info(read_ptr, read_info_ptr);
#ifdef PNG_CRC_QUIET_USE
png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
#endif
png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth,
&color_type, &interlace_method, &compression_method, &filter_method);
if (color_type == 2)
channels = 3;
else if (color_type == 4)
channels = 2;
else if (color_type == 6)
channels = 4;
else
channels=1;
if(color_type == 0 || color_type == 3 || color_type == 4)
reduce_to_gray = 1;
if(bit_depth == 8)
{
if(interlace_method)
num_pass=7;
else
num_pass = 1;
rowbytes = png_get_rowbytes(read_ptr, read_info_ptr);
row_buf = png_malloc(read_ptr, rowbytes+16);
for (pass = 0; pass < num_pass; pass++)
{
png_byte *rp;
png_uint_32 pass_height, pass_width, y;
png_debug(0, "\nBegin Pass\n");
if (interlace_method)
{
pass_height = (height - png_pass_ystart[pass]
+ png_pass_yinc[pass] - 1) / png_pass_yinc[pass];
pass_width = (width - png_pass_start[pass]
+ png_pass_inc[pass] - 1) / png_pass_inc[pass];
}
else
{
pass_height=height;
pass_width=width;
}
for (y = 0; y < pass_height; y++)
{
png_uint_32 x;
png_read_row(read_ptr, row_buf, (png_bytep)NULL);
if(result < 2 || it_is_opaque ||
reduce_to_gray)
{
if(color_type==2)
{
for (rp=row_buf, x = 0; x < pass_width; x++, rp+=channels)
{
#ifdef USE_HASHCODE
int hashcode;
#endif
png_uint_32 rgba_high = (255<<24)|(*(rp)<<16)|(*(rp+1)<<8)|
*(rp+2);
assert(num_rgba < 258);
rgba_hi[num_rgba]=rgba_high;
if(reduce_to_gray &&
((*(rp)) != (*(rp+1)) || (*(rp)) != (*(rp+2))))
reduce_to_gray=0;
if (result > 1 || !it_is_opaque)
continue;
#ifdef USE_HASHCODE
/*
* R G B mask
* 11,111 0,0000, 0000 0x3e00
* 00,000 1,1111, 0000 0x01f0
* 00,000 0,0000, 1111 0x000f
*
*/
hashcode=(int)(((rgba_high>>10)&0x3e00)|
((rgba_high>> 7)&0x01f0)|
((rgba_high>> 4)&0x000f));
assert(hashcode < 16385);
if (hash[hashcode] < 0)
{
hash[hashcode] = i = num_rgba;
if (i > 256)
result=2;
else
num_rgba++;
}
else
{
int start=hash[hashcode];
for(i=start; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += (i-start);
if(i == num_rgba)
{
int j;
if (i > 256)
result=2;
else
{
for(j=num_rgba; j>start+1; j--)
{
rgba_hi[j]=rgba_hi[j-1];
rgba_frequency[j]=rgba_frequency[j-1];
}
assert(start+1 < 258);
rgba_hi[start+1]=rgba_high;
rgba_frequency[start+1]=0;
for(j=0; j<16384; j++)
if(hash[j]>start)
hash[j]++;
i=start+1;
hashinserts++;
num_rgba++;
}
}
}
#else
for(i=0; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += i;
if (i > 256)
result=2;
else if(i == num_rgba)
num_rgba++;
#endif
assert(i < 258);
++rgba_frequency[i];
}
}
else if(color_type==6)
{
for (rp=row_buf, x = 0; x < pass_width; x++, rp+=channels)
{
#ifdef USE_HASHCODE
int hashcode;
#endif
png_uint_32 rgba_high = (*(rp+3)<<24)|(*(rp)<<16)|
(*(rp+1)<<8)|*(rp+2);
assert(rp-row_buf+3 < rowbytes);
rgba_hi[num_rgba]=rgba_high;
if(reduce_to_gray &&
((*(rp)) != (*(rp+1)) || (*(rp)) != (*(rp+2))))
reduce_to_gray=0;
if(it_is_opaque && (*(rp+3)) != 255)
it_is_opaque=0;
if (result > 1)
continue;
#ifdef USE_HASHCODE
/*
* A R G B mask
* 11,1 000,0 000,0 000 0x3800
* 00,0 111,1 000,0 000 0x0780
* 00,0 000,0 111,1 000 0x0078
* 00,0 000,0 000,0 111 0x0007
*
*/
hashcode=(int)(((rgba_high>>18)&0x3800)|
((rgba_high>>12)&0x0780)|
((rgba_high>> 8)&0x0078)|
((rgba_high>> 4)&0x0007));
assert(hashcode < 16385);
if (hash[hashcode] < 0)
{
hash[hashcode] = i = num_rgba;
if (i > 256)
result=2;
else
num_rgba++;
}
else
{
int start=hash[hashcode];
for(i=start; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += (i-start);
if(i == num_rgba)
{
if (i > 256)
result=2;
else
{
int j;
for(j=num_rgba; j>start+1; j--)
{
rgba_hi[j]=rgba_hi[j-1];
rgba_frequency[j]=rgba_frequency[j-1];
}
rgba_hi[start+1]=rgba_high;
rgba_frequency[start+1]=0;
for(j=0; j<16384; j++)
if(hash[j]>start)
hash[j]++;
i=start+1;
hashinserts++;
num_rgba++;
}
}
}
#else
for(i=0; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += i;
if (i > 256)
result=2;
else if(i == num_rgba)
num_rgba++;
#endif
++rgba_frequency[i];
}
}
else if(color_type==4)
{
for (rp=row_buf, x = 0; x < pass_width; x++, rp+=channels)
{
#ifdef USE_HASHCODE
int hashcode;
#endif
png_uint_32 rgba_high = (*(rp+1)<<24)|(*(rp)<<16)|
(*(rp)<<8)|(*rp);
assert(rp-row_buf+1 < rowbytes);
rgba_hi[num_rgba]=rgba_high;
if(it_is_opaque && (*(rp+1)) != 255)
it_is_opaque=0;
#ifdef USE_HASHCODE
/*
* A G mask
* 11,1111, 0000,0000 0x3f00
* 00,0000, 1111,1111 0x00ff
*
*/
hashcode=(int)(((rgba_high>>18)&0x3f00)|
((rgba_high>> 4)&0x00ff));
if (hash[hashcode] < 0)
{
hash[hashcode] = i = num_rgba;
if (i > 256)
result=2;
else
num_rgba++;
}
else
{
int start=hash[hashcode];
for(i=start; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += (i-start);
if(i == num_rgba)
{
if (i > 256)
result=2;
else
{
int j;
for(j=num_rgba; j>start+1; j--)
{
rgba_hi[j]=rgba_hi[j-1];
rgba_frequency[j]=rgba_frequency[j-1];
}
rgba_hi[start+1]=rgba_high;
rgba_frequency[start+1]=0;
for(j=0; j<16384; j++)
if(hash[j]>start)
hash[j]++;
i=start+1;
hashinserts++;
num_rgba++;
}
}
}
#else
for(i=0; i<=num_rgba; i++)
if(rgba_high == rgba_hi[i])
break;
hashmiss += i;
if (i > 256)
result=2;
else if(i == num_rgba)
num_rgba++;
#endif
++rgba_frequency[i];
}
}
else /* other color type */
{
result=2;
}
}
}
png_debug(0, "\nEnd Pass\n");
}
}
else
{
/* TO DO: 16-bit support */
reduce_to_gray=0;
it_is_opaque=0;
result=0;
}
png_free (read_ptr, row_buf); row_buf = (png_bytep)NULL;
png_debug(0, "Destroying data structs\n");
png_destroy_read_struct(&read_ptr, &read_info_ptr, (png_infopp)NULL);
}
else
result=2;
}
Catch (msg)
{
fprintf(STDERR, "\nWhile checking alphas in %s ", inname);
fprintf(STDERR,"pngcrush caught libpng error:\n %s\n\n",msg);
png_free (read_ptr, row_buf); row_buf = (png_bytep)NULL;
png_destroy_read_struct(&read_ptr, &read_info_ptr, (png_infopp)NULL);
png_debug(0, "Destroyed data structs\n");
result=2;
}
if(verbose > 1)
{
int total=0;
if(num_rgba && num_rgba < 257)
{
for(i=0; i<num_rgba; i++)
{
printf("RGBA=(%3.3d,%3.3d,%3.3d,%3.3d), frequency=%d\n",
(int)(rgba_hi[i]>>16) & 0xff,
(int)(rgba_hi[i]>>8) & 0xff,
(int)(rgba_hi[i]) & 0xff,
(int)(rgba_hi[i]>>24) & 0xff,
(int)rgba_frequency[i]);
total+=rgba_frequency[i];
}
P2 ("num_rgba=%d, total pixels=%d\n",num_rgba, total);
P2 ("hashcode misses=%d, inserts=%d\n",hashmiss,
hashinserts);
}
if(color_type == 0 || color_type == 2)
it_is_opaque=0;
if(reduction_ok)
{
if(reduce_to_gray)
P1 ("The truecolor image is all gray and will be reduced.\n");
if(it_is_opaque)
P1 ("The image is opaque and the alpha channel will be removed.\n");
}
else
{
if(reduce_to_gray)
P1 ("The truecolor image is all gray and could be reduced.\n");
if(it_is_opaque)
P1 ("The image is opaque and the alpha channel could be removed.\n");
if (reduce_to_gray || it_is_opaque)
P1 ("Rerun pngcrush with the \"-reduce\" option to do so.\n");
reduce_to_gray = 0;
it_is_opaque = 0;
}
P2 ("Finished checking alphas, result=%d\n",result);
}
ret=result;
return (ret);
}
#endif /* PNGCRUSH_COUNT_COLORS */
#endif /* PNG_LIBPNG_VER < 96 */
#endif /* PNGCRUSH_LIBPNG_VER != PNG_LIBPNG_VER */