Files
libpng/pngcrush.c
Glenn Randers-Pehrson c1adcfaf63 Eliminate "shadowed declaration" warnings from "gcc -Wshadow"
Removed some duplicate declarations ("int i;") and changed name
of some instances of "buffer" to "buff" when the shadowing declaration
was different (*char vs. png_bytep).  Changed "gamma" to "png_gamma"
in pngset.c.
2009-04-19 07:01:06 -05:00

5964 lines
227 KiB
C

/* pngcrush.c - recompresses png files
* Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson (glennrp@users.sf.net)
* Copyright (C) 2005 Greg Roelofs
*
* 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.6.16"
/*
#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-2002,2006-2009 Glenn Randers-Pehrson (glennrp@users.sf.net)
* Copyright (C) 2005 Greg Roelofs
*
* 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, or executable programs
* compiled from it, 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.
*/
/* 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.
*
* Allow in-place file replacement or as a filter, as in
* "pngcrush -overwrite file.png"
* "pngcreator | pngcrush > output.png"
*
* 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.
*
* 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.
*
* GRR: More generally (superset of previous 3 items): split into separate
* "edit" and "crush" programs (or functions). Former is fully libpng-
* aware, much like current pngcrush; latter makes little or no use of
* libpng (maybe IDAT-compression parts only?), instead handling virtually
* all chunks as opaque binary blocks that are copied to output file _once_,
* with IDATs alone replaced (either by best in-memory result or by original
* _data_ resplit into bigger IDATs, if pngcrush can't match/beat). "edit"
* version should be similar to current code but more efficient: make
* _one_ pass through args list, creating table of PNG_UINTs for removal;
* then make initial pass through PNG image, creating (in-order) table of
* all chunks (and byte offsets?) and marking each as "keep" or "remove"
* according to args table. Can start with static table of ~20-30 slots,
* then double size & copy if run out of room: still O(n) algorithm.
*/
#include "png.h"
/* internal libpng macros */
#ifdef PNG_LIBPNG_VER
#define PNGCRUSH_LIBPNG_VER PNG_LIBPNG_VER
#else
/* This must agree with PNG_LIBPNG_VER; you have to define it manually
here if you are using libpng-1.0.6h or earlier */
#define PNGCRUSH_LIBPNG_VER 10007
#endif
/* Changed in version 0.99 */
#if PNGCRUSH_LIBPNG_VER < 99
#undef PNG_CONST
#ifndef PNG_NO_CONST
# define PNG_CONST const
#else
# define PNG_CONST
#endif
#endif
#define PNG_IDAT const png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'}
#define PNG_IHDR const png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'}
#define PNG_dSIG const png_byte png_dSIG[5] = {100, 83, 73, 71, '\0'}
#define PNG_iCCP const png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'}
#define PNG_IEND const png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'}
/* GRR 20050220: added these, which apparently aren't defined anywhere else */
#ifndef PNG_UINT_IHDR
# define PNG_UINT_IHDR (((png_uint_32) 73<<24) | \
((png_uint_32) 72<<16) | \
((png_uint_32) 68<< 8) | \
((png_uint_32) 82 ))
#endif
#ifndef PNG_UINT_IDAT
# define PNG_UINT_IDAT (((png_uint_32) 73<<24) | \
((png_uint_32) 68<<16) | \
((png_uint_32) 65<< 8) | \
((png_uint_32) 84 ))
#endif
#ifndef PNG_UINT_IEND
# define PNG_UINT_IEND (((png_uint_32) 73<<24) | \
((png_uint_32) 69<<16) | \
((png_uint_32) 78<< 8) | \
((png_uint_32) 68 ))
#endif
#ifndef PNG_UINT_PLTE
# define PNG_UINT_PLTE (((png_uint_32) 80<<24) | \
((png_uint_32) 76<<16) | \
((png_uint_32) 84<< 8) | \
((png_uint_32) 69 ))
#endif
#ifndef PNG_UINT_bKGD
# define PNG_UINT_bKGD (((png_uint_32) 98<<24) | \
((png_uint_32) 75<<16) | \
((png_uint_32) 71<< 8) | \
((png_uint_32) 68 ))
#endif
#ifndef PNG_UINT_cHRM
# define PNG_UINT_cHRM (((png_uint_32) 99<<24) | \
((png_uint_32) 72<<16) | \
((png_uint_32) 82<< 8) | \
((png_uint_32) 77 ))
#endif
#ifndef PNG_UINT_dSIG
# define PNG_UINT_dSIG (((png_uint_32) 100<<24) | \
((png_uint_32) 83<<16) | \
((png_uint_32) 73<< 8) | \
((png_uint_32) 71 ))
#endif
#ifndef PNG_UINT_gAMA
# define PNG_UINT_gAMA (((png_uint_32) 103<<24) | \
((png_uint_32) 65<<16) | \
((png_uint_32) 77<< 8) | \
((png_uint_32) 65 ))
#endif
#ifndef PNG_UINT_hIST
# define PNG_UINT_hIST (((png_uint_32) 104<<24) | \
((png_uint_32) 73<<16) | \
((png_uint_32) 83<< 8) | \
((png_uint_32) 84 ))
#endif
#ifndef PNG_UINT_iCCP
# define PNG_UINT_iCCP (((png_uint_32) 105<<24) | \
((png_uint_32) 67<<16) | \
((png_uint_32) 67<< 8) | \
((png_uint_32) 80 ))
#endif
#ifndef PNG_UINT_iTXt
# define PNG_UINT_iTXt (((png_uint_32) 105<<24) | \
((png_uint_32) 84<<16) | \
((png_uint_32) 88<< 8) | \
((png_uint_32) 116 ))
#endif
#ifndef PNG_UINT_oFFs
# define PNG_UINT_oFFs (((png_uint_32) 111<<24) | \
((png_uint_32) 70<<16) | \
((png_uint_32) 70<< 8) | \
((png_uint_32) 115 ))
#endif
#ifndef PNG_UINT_pCAL
# define PNG_UINT_pCAL (((png_uint_32) 112<<24) | \
((png_uint_32) 67<<16) | \
((png_uint_32) 65<< 8) | \
((png_uint_32) 76 ))
#endif
#ifndef PNG_UINT_pHYs
# define PNG_UINT_pHYs (((png_uint_32) 112<<24) | \
((png_uint_32) 72<<16) | \
((png_uint_32) 89<< 8) | \
((png_uint_32) 115 ))
#endif
#ifndef PNG_UINT_sBIT
# define PNG_UINT_sBIT (((png_uint_32) 115<<24) | \
((png_uint_32) 66<<16) | \
((png_uint_32) 73<< 8) | \
((png_uint_32) 84 ))
#endif
#ifndef PNG_UINT_sCAL
# define PNG_UINT_sCAL (((png_uint_32) 115<<24) | \
((png_uint_32) 67<<16) | \
((png_uint_32) 65<< 8) | \
((png_uint_32) 76 ))
#endif
#ifndef PNG_UINT_sPLT
# define PNG_UINT_sPLT (((png_uint_32) 115<<24) | \
((png_uint_32) 80<<16) | \
((png_uint_32) 76<< 8) | \
((png_uint_32) 84 ))
#endif
#ifndef PNG_UINT_sTER
# define PNG_UINT_sTER (((png_uint_32) 115<<24) | \
((png_uint_32) 84<<16) | \
((png_uint_32) 69<< 8) | \
((png_uint_32) 82 ))
#endif
#ifndef PNG_UINT_sRGB
# define PNG_UINT_sRGB (((png_uint_32) 115<<24) | \
((png_uint_32) 82<<16) | \
((png_uint_32) 71<< 8) | \
((png_uint_32) 66 ))
#endif
#ifndef PNG_UINT_tEXt
# define PNG_UINT_tEXt (((png_uint_32) 116<<24) | \
((png_uint_32) 69<<16) | \
((png_uint_32) 88<< 8) | \
((png_uint_32) 116 ))
#endif
#ifndef PNG_UINT_tIME
# define PNG_UINT_tIME (((png_uint_32) 116<<24) | \
((png_uint_32) 73<<16) | \
((png_uint_32) 77<< 8) | \
((png_uint_32) 69 ))
#endif
#ifndef PNG_UINT_tRNS
# define PNG_UINT_tRNS (((png_uint_32) 116<<24) | \
((png_uint_32) 82<<16) | \
((png_uint_32) 78<< 8) | \
((png_uint_32) 83 ))
#endif
#ifndef PNG_UINT_zTXt
# define PNG_UINT_zTXt (((png_uint_32) 122<<24) | \
((png_uint_32) 84<<16) | \
((png_uint_32) 88<< 8) | \
((png_uint_32) 116 ))
#endif
#define PNG_FLAG_CRC_CRITICAL_USE 0x0400
#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800
#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100
#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200
#define PNG_FLAG_CRC_CRITICAL_USE 0x0400
#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800
#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
PNG_FLAG_CRC_ANCILLARY_NOWARN)
#define PNG_PACK 0x0004
#define PNG_DITHER 0x0040
#define PNG_BACKGROUND 0x0080
#define PNG_16_TO_8 0x0400
#define PNG_RGBA 0x0800
#define PNG_EXPAND 0x1000
#define PNG_GAMMA 0x2000
#define PNG_GRAY_TO_RGB 0x4000
#define PNG_FILLER 0x8000L
#define PNG_USER_TRANSFORM 0x100000L
#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */
/* we don't need some of the extra libpng transformations
* so they are ifdef'ed out in pngcrush.h, which is included by
* libpng's pngconf.h which 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
#ifdef PNG_MNG_FEATURES_SUPPORTED
# define PNGCRUSH_LOCO
#endif
#ifndef PNG_UINT_31_MAX
#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
#endif
/* These macros were renamed in libpng-1.2.6 */
#ifndef PNG_HANDLE_CHUNK_ALWAYS
#define PNG_HANDLE_CHUNK_ALWAYS HANDLE_CHUNK_ALWAYS
#define PNG_HANDLE_CHUNK_NEVER HANDLE_CHUNK_NEVER
#define PNG_HANDLE_CHUNK_IF_SAFE HANDLE_CHUNK_IF_SAFE
#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
#ifndef GAS_VERSION
# define GAS_VERSION "2.9.5(?)" /* used only in help/usage screen */
#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>
# include <utime.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
#define STRNGIFY_STAGE1(x) #x
#define STRNGIFY(x) STRNGIFY_STAGE1(x)
#define STR_BUF_SIZE 256
#define MAX_IDAT_SIZE 524288L
#define MAX_METHODS 200
#define MAX_METHODSP1 (MAX_METHODS+1)
#define DEFAULT_METHODS 10
#define FAKE_PAUSE_STRING "P"
#ifdef Z_RLE
# define NUM_STRATEGIES 4
#else
# define NUM_STRATEGIES 3
#endif
#ifdef __TURBOC__
# include <mem.h>
#endif
#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC 1000
#endif
#if CLOCKS_PER_SEC <= 100
# define TIME_T long
#else
# define TIME_T float
#endif
struct options_help {
int verbosity; /* if verbose >= this value, then print line */
const char *textline; /* static string with newline chopped off */
};
/* input and output filenames */
static PNG_CONST char *progname;
static PNG_CONST char *inname = "pngtest" DOT "png";
static PNG_CONST char *outname = "pngout" DOT "png";
static PNG_CONST char *mngname = "mngout" DOT "mng";
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 found_gAMA = 0;
static int found_cHRM = 0;
static int found_any_chunk = 0;
static int image_is_immutable = 0;
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[STR_BUF_SIZE];
char prog_string[STR_BUF_SIZE];
char out_string[STR_BUF_SIZE];
char in_extension[STR_BUF_SIZE];
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];
/* PNG_iTXt_SUPPORTED */
char text_lang[800];
char text_lang_key[800];
/* PNG_iCCP_SUPPORTED */
int iccp_length = 0;
char *iccp_text;
char *iccp_file;
char iccp_name[80];
int best;
char buffer[256];
/* 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];
png_const_charp msg;
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 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 };
#ifdef Z_RLE
static int brute_force_strategies[NUM_STRATEGIES] = { 1, 1, 1, 1 };
#else
static int brute_force_strategies[NUM_STRATEGIES] = { 1, 1, 1 };
#endif
static int method = 10;
static int pauses = 0;
static int nosave = 0;
static int nofilecheck = 0;
#ifdef PNGCRUSH_LOCO
static int new_mng = 0;
#endif
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 ster_mode = -1;
static int new_time_stamp = 1;
static int plte_len = -1;
#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;
static int names;
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;
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, mng_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, *mng_out;
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);
static png_uint_32 idat_length[MAX_METHODSP1];
static int filter_type, zlib_level;
static png_bytep png_row_filters = NULL;
static float t_start, t_stop, t_decode, t_encode, t_misc;
static png_uint_32 max_idat_size = MAX_IDAT_SIZE; /* increases the IDAT size */
static png_uint_32 crushed_idat_size = 0x3ffffffL;
static int already_crushed = 0;
int ia;
/* prototypes */
static void png_cexcept_error(png_structp png_ptr, png_const_charp msg);
void PNGAPI png_default_read_data(png_structp png_ptr, png_bytep data,
png_size_t length);
void png_read_transform_info(png_structp png_ptr, png_infop info_ptr);
void PNGAPI png_default_write_data(png_structp png_ptr, png_bytep data,
png_size_t length);
void png_reset_crc(png_structp png_ptr);
void png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length);
void png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length);
int png_crc_error(png_structp png_ptr);
int png_crc_finish(png_structp png_ptr, png_uint_32 skip);
void png_save_uint_32(png_bytep buf, png_uint_32 i);
#ifdef PNG_USER_MEM_SUPPORTED
png_voidp png_debug_malloc(png_structp png_ptr, png_uint_32 size);
void png_debug_free(png_structp png_ptr, png_voidp ptr);
#endif
void png_crush_pause(void);
#ifdef __riscos
static int fileexists(const char *name)
static int filesize(const char *name)
static int mkdir(const char *name, int ignored)
static void setfiletype(const char *name)
#endif
int keep_unknown_chunk(png_const_charp name, char *argv[]);
int keep_chunk(png_const_charp name, char *argv[]);
void show_result(void);
png_uint_32 measure_idats(FILE * fpin);
png_uint_32 png_measure_idat(png_structp png_ptr);
#ifdef PNGCRUSH_COUNT_COLORS
int count_colors(FILE * fpin);
#endif
void print_version_info(void);
void print_usage(int retval);
#if (!defined(PNGCRUSH_H))
/* ============================================================
* We aren't using the bundled libpng functions, so we must
* reproduce the libpng routines that aren't exported by libpng
* ============================================================
*/
#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
png_uint_32 /* PRIVATE */
png_get_uint_32(png_bytep buf)
{
png_uint_32 i = ((png_uint_32)(*buf) << 24) +
((png_uint_32)(*(buf + 1)) << 16) +
((png_uint_32)(*(buf + 2)) << 8) +
(png_uint_32)(*(buf + 3));
return (i);
}
#else
# define png_get_uint_32(buf) ( *((png_uint_32p) (buf)))
#endif
png_uint_32 /* PRIVATE */
png_get_uint_31(png_structp png_ptr, png_bytep buf)
{
png_uint_32 i = png_get_uint_32(buf);
if (i > PNG_UINT_31_MAX)
png_error(png_ptr, "PNG unsigned integer out of range.\n");
return (i);
}
void /* PRIVATE */
png_save_uint_32(png_bytep buf, png_uint_32 i)
{
buf[0] = (png_byte)((i >> 24) & 0xff);
buf[1] = (png_byte)((i >> 16) & 0xff);
buf[2] = (png_byte)((i >> 8) & 0xff);
buf[3] = (png_byte)(i & 0xff);
}
/* Reset the CRC variable to 32 bits of 1's. Care must be taken
* in case CRC is > 32 bits to leave the top bits 0.
*/
void /* PRIVATE */
png_reset_crc(png_structp png_ptr)
{
png_ptr->crc = crc32(0, Z_NULL, 0);
}
/* Calculate the CRC over a section of data. We can only pass as
* much data to this routine as the largest single buffer size. We
* also check that this data will actually be used before going to the
* trouble of calculating it.
*/
void /* PRIVATE */
png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length)
{
int need_crc = 1;
if (png_ptr->chunk_name[0] & 0x20) /* ancillary */
{
if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
(PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
need_crc = 0;
}
else /* critical */
{
if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
need_crc = 0;
}
if (need_crc)
png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
}
/* Read data, and (optionally) run it through the CRC. */
void /* PRIVATE */
png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
{
png_default_read_data(png_ptr, buf, length);
png_calculate_crc(png_ptr, buf, length);
}
/* Compare the CRC stored in the PNG file with that calculated by libpng from
the data it has read thus far. */
int /* PRIVATE */
png_crc_error(png_structp png_ptr)
{
png_byte crc_bytes[4];
png_uint_32 crc;
int need_crc = 1;
if (png_ptr->chunk_name[0] & 0x20) /* ancillary */
{
if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
(PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
need_crc = 0;
}
else /* critical */
{
if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
need_crc = 0;
}
png_default_read_data(png_ptr, crc_bytes, 4);
if (need_crc)
{
crc = png_get_uint_32(crc_bytes);
return ((int)(crc != png_ptr->crc));
}
else
return (0);
}
/* Optionally skip data and then check the CRC. Depending on whether we
are reading a ancillary or critical chunk, and how the program has set
things up, we may calculate the CRC on the data and print a message.
Returns '1' if there was a CRC error, '0' otherwise. */
int /* PRIVATE */
png_crc_finish(png_structp png_ptr, png_uint_32 skip)
{
png_size_t i;
png_size_t istop = png_ptr->zbuf_size;
for (i = (png_size_t)skip; i > istop; i -= istop)
{
png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
}
if (i)
{
png_crc_read(png_ptr, png_ptr->zbuf, i);
}
if (png_crc_error(png_ptr))
{
if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */
!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) ||
(!(png_ptr->chunk_name[0] & 0x20) && /* Critical */
(png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)))
{
png_chunk_warning(png_ptr, "CRC error");
}
else
{
png_chunk_error(png_ptr, "CRC error");
}
return (1);
}
return (0);
}
/* Modify the info structure to reflect the transformations. The
* info should be updated so a PNG file could be written with it,
* assuming the transformations result in valid PNG data.
*/
void /* PRIVATE */
png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
{
png_debug(1, "in png_read_transform_info\n");
#if defined(PNG_READ_EXPAND_SUPPORTED)
if (png_ptr->transformations & PNG_EXPAND)
{
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
if (png_ptr->num_trans)
info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
else
info_ptr->color_type = PNG_COLOR_TYPE_RGB;
info_ptr->bit_depth = 8;
info_ptr->num_trans = 0;
}
else
{
if (png_ptr->num_trans)
info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
if (info_ptr->bit_depth < 8)
info_ptr->bit_depth = 8;
info_ptr->num_trans = 0;
}
}
#endif
#if defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->transformations & PNG_BACKGROUND)
{
info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
info_ptr->num_trans = 0;
info_ptr->background = png_ptr->background;
}
#endif
#if defined(PNG_READ_GAMMA_SUPPORTED)
if (png_ptr->transformations & PNG_GAMMA)
{
#ifdef PNG_FLOATING_POINT_SUPPORTED
info_ptr->gamma = png_ptr->gamma;
#endif
#ifdef PNG_FIXED_POINT_SUPPORTED
info_ptr->int_gamma = png_ptr->int_gamma;
#endif
}
#endif
#if defined(PNG_READ_16_TO_8_SUPPORTED)
if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16))
info_ptr->bit_depth = 8;
#endif
#if defined(PNG_READ_DITHER_SUPPORTED)
if (png_ptr->transformations & PNG_DITHER)
{
if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
(info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
png_ptr->palette_lookup && info_ptr->bit_depth == 8)
{
info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
}
}
#endif
#if defined(PNG_READ_PACK_SUPPORTED)
if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
info_ptr->bit_depth = 8;
#endif
#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
if (png_ptr->transformations & PNG_GRAY_TO_RGB)
info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
#endif
#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
if (png_ptr->transformations & PNG_RGB_TO_GRAY)
info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR;
#endif
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
info_ptr->channels = 1;
else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
info_ptr->channels = 3;
else
info_ptr->channels = 1;
#ifndef PNG_FLAG_ADD_ALPHA
#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */
#endif
#ifndef PNG_FLAG_STRIP_ALPHA
#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */
#endif
#ifndef PNG_ADD_ALPHA
#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */
#endif
#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)
info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
#endif
if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
info_ptr->channels++;
#if defined(PNG_READ_FILLER_SUPPORTED)
/* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */
if ((png_ptr->transformations & PNG_FILLER) &&
((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
(info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))
{
info_ptr->channels++;
/* if adding a true alpha channel not just filler */
#if !defined(PNG_1_0_X)
if (png_ptr->transformations & PNG_ADD_ALPHA)
info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
#endif
}
#endif
#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
if(png_ptr->transformations & PNG_USER_TRANSFORM)
{
if(info_ptr->bit_depth < png_ptr->user_transform_depth)
info_ptr->bit_depth = png_ptr->user_transform_depth;
if(info_ptr->channels < png_ptr->user_transform_channels)
info_ptr->channels = png_ptr->user_transform_channels;
}
#endif
info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
info_ptr->bit_depth);
#ifndef PNG_ROWBYTES
/* Added to libpng-1.2.6 JB */
#define PNG_ROWBYTES(pixel_bits, width) \
((pixel_bits) >= 8 ? \
((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \
(( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) )
#endif
info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width);
#if !defined(PNG_READ_EXPAND_SUPPORTED)
if(png_ptr)
return;
#endif
}
#if !defined(PNG_NO_STDIO)
/* This is the function that does the actual reading of data. If you are
not reading from a standard C stream, you should create a replacement
read_data function and use it at run time with png_set_read_fn(), rather
than changing the library. */
#ifndef USE_FAR_KEYWORD
void PNGAPI
png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
png_size_t check;
/* fread() returns 0 on error, so it is OK to store this in a png_size_t
* instead of an int, which is what fread() actually returns.
*/
#if defined(_WIN32_WCE)
if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
check = 0;
#else
check = (png_size_t)fread(data, (png_size_t)1, length,
(png_FILE_p)png_ptr->io_ptr);
#endif
if (check != length)
png_error(png_ptr, "Read Error");
}
#else
/* this is the model-independent version. Since the standard I/O library
can't handle far buffers in the medium and small models, we have to copy
the data.
*/
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
static void /* PRIVATE */
png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
int check;
png_byte *n_data;
png_FILE_p io_ptr;
/* Check if data really is near. If so, use usual code. */
n_data = (png_byte *)CVT_PTR_NOCHECK(data);
io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
if ((png_bytep)n_data == data)
{
#if defined(_WIN32_WCE)
if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
check = 0;
#else
check = fread(n_data, 1, length, io_ptr);
#endif
}
else
{
png_byte buf[NEAR_BUF_SIZE];
png_size_t read, remaining, err;
check = 0;
remaining = length;
do
{
read = MIN(NEAR_BUF_SIZE, remaining);
#if defined(_WIN32_WCE)
if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) )
err = 0;
#else
err = fread(buf, (png_size_t)1, read, io_ptr);
#endif
png_memcpy(data, buf, read); /* copy far buffer to near buffer */
if(err != read)
break;
else
check += err;
data += read;
remaining -= read;
}
while (remaining != 0);
}
if ((png_uint_32)check != (png_uint_32)length)
png_error(png_ptr, "read Error");
}
#endif
#endif
#if !defined(PNG_NO_STDIO)
/* This is the function that does the actual writing of data. If you are
not writing to a standard C stream, you should create a replacement
write_data function and use it at run time with png_set_write_fn(), rather
than changing the library. */
#ifndef USE_FAR_KEYWORD
void PNGAPI
png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
png_uint_32 check;
#if defined(_WIN32_WCE)
if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
check = 0;
#else
check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
#endif
if (check != length)
png_error(png_ptr, "Write Error");
}
#else
/* this is the model-independent version. Since the standard I/O library
can't handle far buffers in the medium and small models, we have to copy
the data.
*/
#define NEAR_BUF_SIZE 1024
#define MIN(a,b) (a <= b ? a : b)
void PNGAPI
png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
png_uint_32 check;
png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */
png_FILE_p io_ptr;
/* Check if data really is near. If so, use usual code. */
near_data = (png_byte *)CVT_PTR_NOCHECK(data);
io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
if ((png_bytep)near_data == data)
{
#if defined(_WIN32_WCE)
if ( !WriteFile(io_ptr, near_data, length, &check, NULL) )
check = 0;
#else
check = fwrite(near_data, 1, length, io_ptr);
#endif
}
else
{
png_byte buf[NEAR_BUF_SIZE];
png_size_t written, remaining, err;
check = 0;
remaining = length;
do
{
written = MIN(NEAR_BUF_SIZE, remaining);
png_memcpy(buf, data, written); /* copy far buffer to near buffer */
#if defined(_WIN32_WCE)
if ( !WriteFile(io_ptr, buf, written, &err, NULL) )
err = 0;
#else
err = fwrite(buf, 1, written, io_ptr);
#endif
if (err != written)
break;
else
check += err;
data += written;
remaining -= written;
}
while (remaining != 0);
}
if (check != length)
png_error(png_ptr, "Write Error");
}
#endif
#endif
#endif /* !defined(PNGCRUSH_H) */
/* cexcept interface */
static void png_cexcept_error(png_structp png_ptr, png_const_charp err_msg)
{
if (png_ptr);
#if (defined(PNGCRUSH_H))
if (!strcmp(err_msg, "Too many IDAT's found")) {
#ifndef PNG_NO_CONSOLE_IO
fprintf(stderr, "\nIn %s, correcting ", inname);
#else
png_warning(png_ptr, err_msg);
#endif
} else
#endif /* defined(PNGCRUSH_H) */
{
Throw err_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;
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 = (memory_infop)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 %lux allocated %lu bytes\n",
(unsigned long) pinfo->pointer, (unsigned long)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 %lux freed %lu bytes\n",
(unsigned long) ptr, (unsigned long)pinfo->size);
png_free_default(png_ptr, pinfo);
break;
}
if (pinfo->next == NULL) {
fprintf(STDERR, "Pointer %lux not found\n",
(unsigned long) 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)
{
if (pauses > 0) {
char keystroke;
fprintf(STDERR, "Press [ENTER] key to continue.\n");
keystroke = (char) getc(stdin);
keystroke = keystroke; /* stifle compiler warning */
}
}
#ifndef __riscos
# define setfiletype(x)
#else /* defined(__riscos) */
# include <kernel.h>
/* The riscos/acorn support was contributed by Darren Salt. */
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
}
#endif /* ?defined(__riscos) */
/* GRR: basically boolean; first arg is chunk name-string (e.g., "tIME" or
* "alla"); second is always full argv[] command line
* - remove_chunks is argv index of *last* -rem arg on command line
* (would be more efficient to build table at time of cmdline processing!)
* (i.e., build removal_list with names or unique IDs or whatever--skip
* excessive string-processing on every single one)
* - reprocesses command line _every_ time called, looking for -rem opts...
* - just like keep_chunk() except that latter sets things_have_changed
* variable and debug stmts say "Removed chunk" (but caller actually does
* so, by choosing not to copy chunk to new file)
* - for any given chunk name, "name" must either match exact command-line
* arg (e.g., -rem fOOb), OR it must match one of the official PNG chunk
* names explicitly listed below AND command-line arg either used all-
* lowercase form or one of "all[ab]" options
*/
int keep_unknown_chunk(png_const_charp name, char *argv[])
{
int i;
if (remove_chunks == 0)
return 1; /* no -rem options, so always keeping */
for (i = 1; i <= remove_chunks; i++) {
if (!strncmp(argv[i], "-rem", 4)) {
int allb = 0;
i++;
if (!strncmp(argv[i], "all", 3)) {
allb++; /* all but gamma, but not doing gamma here */
}
if (!strncmp(argv[i], name, 4) /* exact chunk-name match in args */
/* ...or exact match for one of known set, plus args included
* either "alla", "allb", or all-lowercase form of "name" */
|| (!strncmp(name, "cHRM", 4)
&& (!strncmp(argv[i], "chrm", 4) || allb))
|| (!strncmp(name, "dSIG", 4)
&& (!strncmp(argv[i], "dsig", 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 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;
int allt = 0;
i++;
if (!strncmp(argv[i], "all", 3)) {
allt++; /* all forms of text chunk are ancillary */
allb++; /* all ancillaries but gamma... */
if (!strncmp(argv[i], "alla", 4))
alla++; /* ...no, all ancillaries, period */
} else if (!strncmp(argv[i], "text", 4))
allt++; /* all forms of text chunk */
if (!strncmp(argv[i], name, 4) /* exact chunk-name match in args
* ...or exact match for one of known set, plus args included
* either "alla", "allb", or all-lowercase form of "name": */
|| (!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, "dSIG", 4)
&& (!strncmp(argv[i], "dsig", 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) || allt))
|| (!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, "sTER", 4)
&& (!strncmp(argv[i], "ster", 4) || allb))
|| (!strncmp(name, "sPLT", 4)
&& (!strncmp(argv[i], "splt", 4) || allb))
|| (!strncmp(name, "tEXt", 4)
&& ( allt))
|| (!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) || allt)) )
{
things_have_changed = 1;
/* (caller actually does the removal--by failing to create
* copy) */
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, %lu bytes\n",
(100.0 -
(100.0 * total_output_length) / total_input_length),
(unsigned long)(total_input_length-total_output_length));
else
fprintf(STDERR,
" Overall result: %4.2f%% increase, %lu bytes\n",
-(100.0 -
(100.0 * total_output_length) / total_input_length),
(unsigned long)(total_output_length - total_input_length));
}
t_stop = (TIME_T) clock();
t_misc += (t_stop - t_start);
if (t_stop < t_start) {
t_misc += PNG_UINT_31_MAX;
if (t_stop < 0)
t_misc += PNG_UINT_31_MAX;
}
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 %lux\n", (unsigned long)pinfo->size,
(unsigned long) 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 num_methods;
int try_method[MAX_METHODSP1];
int fm[MAX_METHODSP1];
int lv[MAX_METHODSP1];
int zs[MAX_METHODSP1];
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();
strncpy(prog_string, argv[0], STR_BUF_SIZE);
prog_string[STR_BUF_SIZE-1] = '\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
}
/*
* Definition of methods ("canonical list" is methods 11 and up)
*/
for (i = 0; i < MAX_METHODS; i++) {
try_method[i] = 1;
fm[i] = 5; lv[i] = 9; zs[i] = 1; /* default: method 124 */
}
fm[1] = 0; lv[1] = 4; zs[1] = 0; /* method 1 == method 53 */
fm[2] = 1; lv[2] = 4; zs[2] = 0; /* method 2 == method 54 */
lv[3] = 4; /* method 3 == method 64 */
fm[4] = 0; /* method 4 == method 119 */
fm[5] = 1; zs[5] = 0; /* method 5 == method 114 */
zs[6] = 0; /* method 6 == method 118 */
fm[7] = 0; zs[7] = 0; /* method 7 == method 113 */
fm[8] = 1; /* method 8 == method 120 */
lv[9] = 2; zs[9] = 2; /* method 9 == method 16 */
/* method 10 == method 124 */
/* methods 11 through 16 */
/* [strategy 2 (Z_HUFFMAN_ONLY) is independent of zlib compression level] */
method = 11;
for (filt = 0; filt <= 5; filt++) {
fm[method] = filt;
lv[method] = 2;
zs[method] = 2;
method++;
}
/* methods 17 through 124 (9*2*6 = 108) */
for (lev = 1; lev <= 9; lev++) {
for (strat = 0; strat <= 1; strat++) {
for (filt = 0; filt <= 5; filt++) {
fm[method] = filt;
lv[method] = lev;
zs[method] = strat;
method++;
}
}
}
#ifdef Z_RLE
/* methods 125 through 136 */
/* [strategy 3 (Z_RLE) is mostly independent of level; 1-3 and 4-9 are
* same] */
for (filt = 0; filt <= 5; filt++) {
fm[method] = filt;
lv[method] = 1;
zs[method] = 3;
method++;
}
for (filt = 0; filt <= 5; filt++) {
fm[method] = filt;
lv[method] = 4;
zs[method] = 3;
method++;
}
#endif /* Z_RLE */
num_methods = method; /* GRR */
#define BUMP_I i++;if(i >= argc) {printf("insufficient parameters\n");exit(1);}
names = 1;
/* ===================================================================== */
/* FIXME: move args-processing block into separate function (470 lines) */
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "-", 1))
names++;
/* GRR: start of giant else-if block */
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;
}
#ifdef Z_RLE
} else if (!strncmp(argv[i], "-rle", 4)) {
/* try all filters with RLE */
methods_specified = 1;
for (method = 125; method <= 136; method++) {
try_method[method] = 0;
}
#endif
}
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 */
{
methods_specified = 1;
brute_force++;
for (method = 11; method < num_methods; 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 < NUM_STRATEGIES; 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++;
found_gAMA=1;
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]);
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 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;
found_gAMA=1;
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 /* PNG_gAMA_SUPPORTED */
else if (!strncmp(argv[i], "-h", 2)) {
++verbose;
print_version_info();
print_usage(0); /* this exits */
}
#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 = (char*)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], "-keep", 5)) {
names++;
BUMP_I;
if (!strncmp(argv[i], "dSIG", 4)
&& (!strncmp(argv[i], "dsig", 4) ))
found_any_chunk=1;
}
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_UINT_31_MAX)
max_idat_size = PNG_ZBUF_SIZE;
#ifdef PNGCRUSH_LOCO
} else if (!strncmp(argv[i], "-mng", 4)) {
names++;
BUMP_I;
mngname = argv[i];
new_mng++;
#endif
} 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], "-oldtimestamp", 5)) {
new_time_stamp=0;
} 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;
found_gAMA=1;
{
#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;
if (!strncmp(argv[i], "dSIG", 4)
&& (!strncmp(argv[i], "dsig", 4)))
image_is_immutable=0;
} 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]);
global_things_have_changed = 1;
} else
i--;
} else if (!strncmp(argv[i], "-ster", 5) ||
!strncmp(argv[i], "-sTER", 5)) {
BUMP_I;
ster_mode = -1;
if (!strncmp(argv[i], "0", 1) ||
!strncmp(argv[i], "1", 1)) {
names++;
ster_mode = (int) atoi(argv[i]);
global_things_have_changed = 1;
} 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]) < 180 && 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 {
i += 2;
BUMP_I;
i -= 3;
names += 2;
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
}
}
else if (!strncmp(argv[i], "-time_stamp", 5) || /* legacy */
!strncmp(argv[i], "-newtimestamp", 5))
new_time_stamp=1;
#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]);
trns_index=num_trans_in-1;
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");
verbose = 0;
} 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 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++;
} /* GRR: end of giant if-else block */
} /* end of loop over args ============================================ */
if (verbose > 0)
print_version_info();
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) {
if (argc - names == 2) {
inname = argv[names];
outname = argv[names + 1];
} else {
if ((argc - names == 1 || nosave)) {
inname = argv[names];
}
if (verbose && !nosave) {
print_usage(1); /* this exits */
}
}
}
for (ia = 0; ia < 256; 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();
break;
}
if (pngcrush_mode == DIRECTORY_MODE || pngcrush_mode == DIREX_MODE) {
int inlen, outlen;
#ifndef __riscos
struct stat stat_buf;
if (stat(directory_name, &stat_buf))
#else
if (fileexists(directory_name) & 2)
#endif
{
#if defined(_MBCS) || defined(WIN32) || defined(__WIN32__)
if (_mkdir(directory_name))
#else
if (mkdir(directory_name, 0755))
#endif
{
fprintf(STDERR, "could not create directory %s\n",
directory_name);
exit(1);
}
nofilecheck = 1;
}
outlen = strlen(directory_name);
if (outlen >= STR_BUF_SIZE-1) {
fprintf(STDERR, "directory %s is too long for buffer\n",
directory_name);
exit(1);
}
strcpy(out_string, directory_name);
/*strcpy(out_string+outlen, SLASH); */
out_string[outlen++] = SLASH[0]; /* (assuming SLASH is 1 byte) */
out_string[outlen] = '\0';
inlen = strlen(inname);
if (inlen >= STR_BUF_SIZE) {
fprintf(STDERR, "filename %s is too long for buffer\n", inname);
exit(1);
}
strcpy(in_string, inname);
in_string[inlen] = '\0';
#ifdef __riscos
op = strrchr(in_string, '.');
if (!op)
op = in_string;
else
op++;
#else
op = in_string;
ip = in_string + inlen - 1; /* start at last char in string */
while (ip > in_string) {
if (*ip == '\\' || *ip == '/') {
op = ip + 1;
break;
}
--ip;
}
#endif
if (outlen + (inlen - (op - in_string)) >= STR_BUF_SIZE) {
fprintf(STDERR, "full path is too long for buffer\n");
exit(1);
}
strcpy(out_string+outlen, op);
/*outlen += inlen - (op - in_string); */
outname = out_string;
}
/* FIXME: need same input-validation fixes (as above) here, too
* FIXME: what was the point of setting in_string and out_string in
* DIREX_MODE above if going to do all over again here? */
if (pngcrush_mode == EXTENSION_MODE || pngcrush_mode == DIREX_MODE) {
ip = in_string;
in_string[0] = '\0';
if (pngcrush_mode == EXTENSION_MODE)
strcat(in_string, inname);
else
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) {
strcat(in_extension, ++dot);
}
strcat(out_string, extension);
outname = out_string;
}
if (nosave < 2) {
P1( "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;
#ifdef PNGCRUSH_LOCO
if (new_mng) {
#ifdef PNG_USER_MEM_SUPPORTED
mng_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
mng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
(png_voidp) NULL, (png_error_ptr) png_cexcept_error,
(png_error_ptr) NULL);
#endif
if (mng_ptr == NULL)
fprintf(STDERR, "pngcrush could not create mng_ptr");
if ((mng_out = FOPEN(mngname, "wb")) == NULL) {
fprintf(STDERR, "Could not open output file %s\n",
mngname);
FCLOSE(fpin);
exit(1);
}
number_of_open_files++;
png_init_io(mng_ptr, mng_out);
png_set_write_fn(mng_ptr, (png_voidp) mng_out,
(png_rw_ptr) NULL,
NULL);
#endif
}
idat_length[0] = measure_idats(fpin);
#ifdef PNGCRUSH_LOCO
if (new_mng) {
png_destroy_write_struct(&mng_ptr, NULL);
FCLOSE(mng_out);
}
#endif
FCLOSE(fpin);
if (verbose > 0) {
fprintf(STDERR, " Recompressing %s\n", inname);
fprintf(STDERR,
" Total length of data found in IDAT chunks = %8lu\n",
(unsigned long)idat_length[0]);
fflush(STDERR);
}
if (idat_length[0] == 0)
continue;
} else
idat_length[0] = 1;
if (already_crushed) {
fprintf(STDERR, " File %s has already been crushed.\n", inname);
}
if (image_is_immutable) {
fprintf(STDERR,
" Image %s has a dSIG chunk and is immutable.\n", inname);
}
if (!already_crushed && !image_is_immutable) {
#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;
P1( "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 */
if (force_output_bit_depth != 0 &&
force_output_bit_depth != 1 &&
force_output_bit_depth != 2 &&
force_output_bit_depth != 4 &&
force_output_bit_depth != 8 &&
force_output_bit_depth != 16)
{
fprintf(STDERR, "\n Ignoring invalid bit_depth: %d\n",
force_output_bit_depth);
force_output_bit_depth=0;
}
if (force_output_color_type != 8 &&
force_output_color_type != 0 &&
force_output_color_type != 2 &&
force_output_color_type != 3 &&
force_output_color_type != 4 &&
force_output_color_type != 6)
{
fprintf(STDERR, "\n Ignoring invalid color_type: %d\n",
force_output_color_type);
force_output_color_type=8;
}
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;
/* ////////////////////////////////////////////////////////////////////
//////////////// ////////////////////
//////////////// START OF MAIN LOOP OVER METHODS ////////////////////
//////////////// ////////////////////
//////////////////////////////////////////////////////////////////// */
/* MAX_METHODS is 200 */
P1("\n\nENTERING MAIN LOOP OVER %d METHODS\n", MAX_METHODS);
for (trial = 1; trial <= MAX_METHODS; trial++) {
idat_length[trial] = (png_uint_32) 0xffffffff;
/* this part of if-block is for final write-the-best-file
iteration */
if (trial == MAX_METHODS) {
png_uint_32 best_length;
int j;
/* check lengths */
best = 0; /* i.e., input file */
best_length = (png_uint_32) 0xffffffff;
for (j = things_have_changed; j < MAX_METHODS; j++) {
if (best_length > idat_length[j]) {
best_length = idat_length[j];
best = j;
}
}
if (image_is_immutable
|| (idat_length[best] == idat_length[0]
&& things_have_changed == 0
&& idat_length[best] != idat_length[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 (idat_length[best] == idat_length[final_method]) {
break;
} else {
filter_type = fm[best];
zlib_level = lv[best];
if (zs[best] == 1)
z_strategy = Z_FILTERED;
else if (zs[best] == 2)
z_strategy = Z_HUFFMAN_ONLY;
#ifdef Z_RLE
else if (zs[best] == 3)
z_strategy = Z_RLE;
#endif
else /* if (zs[best] == 0) */
z_strategy = Z_DEFAULT_STRATEGY;
}
} else {
if (trial > 2 && trial < 5 && idat_length[trial - 1]
< idat_length[best_of_three])
best_of_three = trial - 1;
if (try_method[trial]) {
P2("skipping \"late\" trial %d\n", trial);
continue;
}
if (!methods_specified && try_method[0]) {
if ((trial == 4 || trial == 7) && best_of_three != 1) {
P2("skipping \"early\" trial %d\n", trial);
continue;
}
if ((trial == 5 || trial == 8) && best_of_three != 2) {
P2("skipping \"early\" trial %d\n", trial);
continue;
}
if ((trial == 6 || trial == 9 || trial == 10)
&& best_of_three != 3) {
P2("skipping \"early\" trial %d\n", trial);
continue;
}
}
filter_type = fm[trial];
zlib_level = lv[trial];
if (zs[trial] == 1)
z_strategy = Z_FILTERED;
else if (zs[trial] == 2)
z_strategy = Z_HUFFMAN_ONLY;
#ifdef Z_RLE
else if (zs[trial] == 3)
z_strategy = Z_RLE;
#endif
else /* if (zs[trial] == 0) */
z_strategy = Z_DEFAULT_STRATEGY;
final_method = trial;
if (!nosave) {
P2("\n\n------------------------------------------------\n"
"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();
/* OK to ignore any warning about the address of exception__prev in "Try" */
Try {
png_uint_32 row_length;
P1( "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";
}
P1("Allocating read_info, write_info, 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();
P1( "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_rw_ptr) NULL);
if (nosave == 0)
png_set_write_fn(write_ptr, (png_voidp) fpout,
(png_rw_ptr) NULL,
#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, PNG_HANDLE_CHUNK_ALWAYS,
(png_bytep) NULL, 0);
#endif
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
if (nosave == 0) {
if (found_any_chunk == 1)
png_set_keep_unknown_chunks(write_ptr,
PNG_HANDLE_CHUNK_ALWAYS,
(png_bytep) "dSIG", 1);
if (all_chunks_are_safe)
png_set_keep_unknown_chunks(write_ptr,
PNG_HANDLE_CHUNK_ALWAYS,
(png_bytep) NULL, 0);
else {
#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_sTER_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,
PNG_HANDLE_CHUNK_IF_SAFE,
(png_bytep) NULL,
0);
else
png_set_keep_unknown_chunks(write_ptr,
PNG_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,
PNG_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,
PNG_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,
PNG_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,
PNG_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,
PNG_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,
PNG_HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
#if !defined(PNG_sTER_SUPPORTED)
if (keep_unknown_chunk("sTER", argv)) {
png_save_uint_32(chunk_name, PNG_UINT_sTER);
png_set_keep_unknown_chunks(write_ptr,
PNG_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,
PNG_HANDLE_CHUNK_ALWAYS,
chunk_name, 1);
}
#endif
}
}
#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
P1( "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_default_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 buff[40];
unsigned long length;
/* Skip the MHDR */
png_permit_mng_features(read_ptr,
PNG_FLAG_MNG_FILTER_64);
png_default_read_data(read_ptr, buff, 4);
length=buff[3]+(buff[2]<<8)+(buff[1]<<16)+(buff[0]<<24);
png_default_read_data(read_ptr, buff, 4);
printf("Skipping %c%c%c%c chunk.\n",buff[0],buff[1],
buff[2],buff[3]);
png_default_read_data(read_ptr, buff, length);
png_default_read_data(read_ptr, buff, 4);
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);
/* { GRR added for quick %-navigation (1) */
/* Start of chunk-copying/removal code, in order:
* - IHDR
* - bKGD
* - cHRM
* - gAMA
* - sRGB
* - iCCP
* - oFFs
* - pCAL
* - pHYs
* - hIST
* - tRNS
* - PLTE
* - sBIT
* - sCAL
* - sPLT
* - sTER
* - tEXt/zTXt/iTXt
* - tIME
* - unknown chunks
*/
{
int interlace_method, compression_method,
filter_method;
P1( "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=%lu, height=%lu\n",
(unsigned long)width,
(unsigned long)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 backgrnd = &backgd;
backgrnd->red = bkgd_red;
backgrnd->green = bkgd_green;
backgrnd->blue = bkgd_blue;
backgrnd->gray = backgrnd->green;
png_set_bKGD(write_ptr, write_info_ptr,
backgrnd);
}
}
#endif /* defined(PNG_READ_bKGD_SUPPORTED)&&defined(PNG_WRITE_bKGD_SUPPORTED) */
#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 (found_cHRM && 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)) {
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)) {
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 /* ?PNG_FIXED_POINT_SUPPORTED */
#endif /* PNG_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_cHRM_SUPPORTED */
#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, " Inserting gAMA chunk with "
#ifdef PNG_FIXED_POINT_SUPPORTED
"gamma=(%d/100000)\n",
#else
"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 (found_gAMA && png_get_gAMA_fixed
(read_ptr, read_info_ptr, &file_gamma))
#else
else if (found_gAMA && 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, " Inserting gAMA chunk with "
#ifdef PNG_FIXED_POINT_SUPPORTED
"gamma=(%d/100000)\n",
#else
"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 /* defined(PNG_READ_gAMA_SUPPORTED)&&defined(PNG_WRITE_gAMA_SUPPORTED) */
#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) {
if (first_trial) {
fprintf(STDERR, " Ignoring sRGB request; "
#ifdef PNG_FIXED_POINT_SUPPORTED
"gamma=(%lu/100000)"
#else
"gamma=%f"
#endif
" is not approx. 0.455\n",
(unsigned long)file_gamma);
}
}
#endif /* PNG_gAMA_SUPPORTED */
}
}
#endif /* defined(PNG_READ_sRGB_SUPPORTED)&&defined(PNG_WRITE_sRGB_SUPPORTED) */
#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",
(unsigned long)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 /* defined(PNG_READ_iCCP_SUPPORTED)&&defined(PNG_WRITE_iCCP_SUPPORTED) */
#if defined(PNG_READ_oFFs_SUPPORTED) && defined(PNG_WRITE_oFFs_SUPPORTED)
{
png_int_32 offset_x, offset_y;
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",
(unsigned long)res_x,
(unsigned long)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 /* defined(PNG_READ_tRNS_SUPPORTED)&&defined(PNG_WRITE_tRNS_SUPPORTED) */
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) {
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 /* defined(PNG_READ_sBIT_SUPPORTED)&&defined(PNG_WRITE_sBIT_SUPPORTED) */
#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 /* PNG_FLOATING_POINT_SUPPORTED */
#endif /* ?PNG_sCAL_SUPPORTED */
#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;
P1( "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
printf(" Added a%scompressed iTXt chunk"
".\n", (added_text[0].compression == 1)?
"n un" : " ");
#endif
#if 0
printf(" key=%s.\n",(added_text[0].key));
#endif
png_free(write_ptr, added_text);
added_text = (png_textp) NULL;
}
}
}
}
#endif /* defined(PNG_TEXT_SUPPORTED) */
#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
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
/* This section handles pCAL and tIME (at least, in default
* build), gIFx/gIFg/gIFt, private Fireworks chunks, etc. */
{
png_unknown_chunkp unknowns; /* allocated by libpng */
int num_unknowns;
if (nosave == 0 && ster_mode >= 0) {
/* Add sTER chunk */
png_unknown_chunkp ster;
P1("Handling sTER as unknown chunk %d\n", i);
ster = (png_unknown_chunk*)png_malloc(read_ptr,
(png_uint_32) sizeof(png_unknown_chunk));
png_memcpy((char *)ster[0].name, "sTER",5);
ster[0].size = 1;
ster[0].data = (png_byte*)png_malloc(read_ptr, 1);
ster[0].data[0] = (png_byte)ster_mode;
png_set_unknown_chunks(read_ptr, read_info_ptr,
ster, 1);
png_free(read_ptr,ster[0].data);
png_free(read_ptr,ster);
}
num_unknowns = (int)png_get_unknown_chunks(read_ptr,
read_info_ptr, &unknowns);
#ifndef PNG_HAVE_IHDR
#define PNG_HAVE_IHDR 0x01
#endif
if (ster_mode >= 0)
png_set_unknown_chunk_location(read_ptr, read_info_ptr,
num_unknowns - 1, (int)PNG_HAVE_IHDR);
P1("Found %d unknown chunks\n", num_unknowns);
if (nosave == 0 && num_unknowns) {
png_unknown_chunkp unknowns_keep; /* allocated by us */
int num_unknowns_keep;
unknowns_keep = (png_unknown_chunk*)png_malloc(write_ptr,
(png_uint_32) num_unknowns
*sizeof(png_unknown_chunk));
P1("malloc for %d unknown chunks\n", num_unknowns);
num_unknowns_keep = 0;
/* make an array of only those chunks we want to keep */
for (i = 0; i < num_unknowns; i++) {
P1("Handling unknown chunk %d %s\n", i,
(char *)unknowns[i].name);
/* not EBCDIC-safe, but neither is keep_chunks(): */
P2(" unknown[%d] = %s (%lu bytes, location %d)\n",
i, unknowns[i].name,
(unsigned long)unknowns[i].size,
unknowns[i].location);
if (keep_chunk((char *)unknowns[i].name, argv)) {
png_memcpy(&unknowns_keep[num_unknowns_keep],
&unknowns[i], sizeof(png_unknown_chunk));
++num_unknowns_keep;
}
}
P1("Keeping %d unknown chunks\n", num_unknowns_keep);
png_set_unknown_chunks(write_ptr, write_info_ptr,
unknowns_keep, num_unknowns_keep);
/* relevant location bits:
* (1) !PNG_HAVE_PLTE && !PNG_HAVE_IDAT (before PLTE)
* (2) PNG_HAVE_PLTE && !PNG_HAVE_IDAT (between)
* (3) PNG_AFTER_IDAT (after IDAT)
* PNG_HAVE_PLTE = 0x02
* PNG_HAVE_IDAT = 0x04
* PNG_AFTER_IDAT = 0x08
*/
for (i = 0; i < num_unknowns_keep; i++) {
png_set_unknown_chunk_location(write_ptr,
write_info_ptr, i,
(int)unknowns_keep[i].location);
}
/* png_set_unknown_chunks() makes own copy, so nuke
* ours */
png_free(write_ptr, unknowns_keep);
}
}
P0("unknown chunk handling done.\n");
#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
/* } GRR added for quick %-navigation (1) */
png_read_transform_info(read_ptr, read_info_ptr);
/* this is the default case (nosave == 1 -> perf-testing
only) */
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);
/* GRR 20050220: not clear why unknowns treated differently from other chunks */
/* (i.e., inside nosave==0 block)... Moved up 50 lines now. */
#if 0 /* #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
{
png_unknown_chunkp unknowns;
int num_unknowns = (int) png_get_unknown_chunks(
read_ptr, read_info_ptr, &unknowns);
P1("Keeping %d unknown chunks\n", num_unknowns);
if (num_unknowns) {
png_set_unknown_chunks(write_ptr, write_info_ptr,
unknowns, num_unknowns);
for (i = 0; i < num_unknowns; i++) {
P2(" unknown[%d] = %s\n", i, unknowns[i].name);
png_set_unknown_chunk_location(write_ptr,
write_info_ptr, i, (int)unknowns[i].location);
}
}
}
#endif /* 0, was PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */
#ifdef PNGCRUSH_LOCO
if (do_loco) {
png_byte buff[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_default_write_data(write_ptr, &mng_signature[0],
(png_size_t) 8);
png_set_sig_bytes(write_ptr, 8);
/* Write a MHDR chunk */
buff[0] = (png_byte) ((width >> 24) & 0xff);
buff[1] = (png_byte) ((width >> 16) & 0xff);
buff[2] = (png_byte) ((width >> 8) & 0xff);
buff[3] = (png_byte) ((width) & 0xff);
buff[4] = (png_byte) ((height >> 24) & 0xff);
buff[5] = (png_byte) ((height >> 16) & 0xff);
buff[6] = (png_byte) ((height >> 8) & 0xff);
buff[7] = (png_byte) ((height) & 0xff);
for (i = 8; i < 27; i++)
buff[i] = 0x00;
buff[15] = 2; /* layer count */
buff[19] = 1; /* frame count */
if (output_color_type == 6)
buff[27] = 0x09; /* profile: MNG-VLC with trans. */
else
buff[27] = 0x01; /* profile: MNG-VLC */
png_write_chunk(write_ptr, (png_bytep) png_MHDR,
buff, (png_size_t) 28);
}
#endif /* PNGCRUSH_LOCO */
png_crush_pause();
P1( "\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;
png_uint_32 zbuf_size;
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);
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",
(unsigned long)max_possible_size);
png_set_compression_buffer_size(write_ptr,
max_possible_size);
}
#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);
P1( "\nWrote info struct\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);
if (t_stop < t_start) {
t_misc += PNG_UINT_31_MAX;
if (t_stop < 0)
t_misc += PNG_UINT_31_MAX;
}
t_start = t_stop;
for (pass = 0; pass < num_pass; pass++) {
#ifdef PNGCRUSH_MULTIPLE_ROWS
png_uint_32 num_rows;
#endif
P1( "\nBegin interlace pass %d\n", pass);
#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);
if (t_stop < t_start) {
t_decode += PNG_UINT_31_MAX;
if (t_stop < 0)
t_decode += PNG_UINT_31_MAX;
}
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);
if (t_stop < t_start) {
t_encode += PNG_UINT_31_MAX;
if (t_stop < 0)
t_encode += PNG_UINT_31_MAX;
}
t_start = t_stop;
}
}
P2( "End interlace pass %d\n\n", pass);
}
if (nosave) {
t_stop = (TIME_T) clock();
t_decode += (t_stop - t_start);
if (t_stop < t_start) {
t_decode += PNG_UINT_31_MAX;
if (t_stop < 0)
t_decode += PNG_UINT_31_MAX;
}
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
P1( "Reading and writing end_info data\n");
png_read_end(read_ptr, end_info_ptr);
/* { GRR: added for %-navigation (2) */
#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;
P1( "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 /* (PNG_READ_tEXt_SUPPORTED and PNG_WRITE_tEXt_SUPPORTED) or */
/* (PNG_READ_zTXt_SUPPORTED and PNG_WRITE_zTXt_SUPPORTED) */
#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)
/* GRR FIXME? this block may need same fix as above */
{
png_unknown_chunkp unknowns;
int num_unknowns =
(int) png_get_unknown_chunks(read_ptr,
end_info_ptr,
&unknowns);
if (num_unknowns && nosave == 0) {
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
/* } GRR: added for %-navigation (2) */
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);
}
P1( "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) {
P1( "Opening file for length measurement\n");
if ((fpin = FOPEN(outname, "rb")) == NULL) {
fprintf(STDERR, "Could not find output 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,
(unsigned long)idat_length[trial]);
fflush(STDERR);
}
} /* end of trial-loop */
P1("\n\nFINISHED MAIN LOOP OVER %d METHODS\n\n\n", MAX_METHODS);
/* ////////////////////////////////////////////////////////////////////
////////////////// ////////////////////
////////////////// END OF MAIN LOOP OVER METHODS ////////////////////
////////////////// ////////////////////
//////////////////////////////////////////////////////////////////// */
}
if (fpin) {
FCLOSE(fpin);
}
if (nosave == 0 && fpout) {
FCLOSE(fpout);
setfiletype(outname);
}
if (nosave == 0) {
png_uint_32 input_length, output_length;
#ifndef __riscos
struct stat stat_buf;
struct utimbuf utim;
stat(inname, &stat_buf);
input_length = (unsigned long) stat_buf.st_size;
utim.actime = stat_buf.st_atime;
utim.modtime = stat_buf.st_mtime;
stat(outname, &stat_buf);
output_length = (unsigned long) stat_buf.st_size;
if (new_time_stamp == 0) {
/* set file timestamp (no big deal if fails) */
utime(outname, &utim);
}
#else
input_length = (unsigned long) filesize(inname);
output_length = (unsigned long) filesize(outname);
#endif
if (verbose > 0) {
total_input_length += input_length + output_length;
if (!already_crushed && !image_is_immutable) {
fprintf(STDERR, " Best pngcrush method = %d (fm %d zl %d zs %d) "
"for %s\n", best, fm[best], lv[best], zs[best], outname);
}
if (idat_length[0] == idat_length[best])
fprintf(STDERR, " (no IDAT change)\n");
else if (idat_length[0] > idat_length[best])
fprintf(STDERR, " (%4.2f%% IDAT reduction)\n",
(100.0 - (100.0 * idat_length[best]) / idat_length[0]));
else
fprintf(STDERR, " (%4.2f%% IDAT increase)\n",
-(100.0 - (100.0 * idat_length[best]) / idat_length[0]));
if (input_length == output_length)
fprintf(STDERR, " (no filesize change)\n\n");
else if (input_length > output_length)
fprintf(STDERR, " (%4.2f%% filesize reduction)\n\n",
(100.0 - (100.0 * output_length) / input_length));
else
fprintf(STDERR, " (%4.2f%% filesize increase)\n\n",
-(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 */
return 0; /* just in case */
} /* end of main() */
png_uint_32 measure_idats(FILE * fp_in)
{
/* Copyright (C) 1999-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net)
See notice in pngcrush.c for conditions of use and distribution */
P2("\nmeasure_idats:\n");
P1( "Allocating read structure\n");
/* OK to ignore any warning about the address of exception__prev in "Try" */
Try {
read_ptr =
png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL,
(png_error_ptr) png_cexcept_error,
(png_error_ptr) NULL);
P1( "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, fp_in);
#else
png_set_read_fn(read_ptr, (png_voidp) fp_in, (png_rw_ptr) NULL);
#endif
png_set_sig_bytes(read_ptr, 0);
measured_idat_length = png_measure_idat(read_ptr);
P2("measure_idats: IDAT length=%lu\n",
(unsigned long)measured_idat_length);
P1( "Destroying data structs\n");
png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr);
}
Catch(msg) {
fprintf(STDERR, "\nWhile measuring IDATs in %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);
P1( "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-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net)
See notice in pngcrush.c for conditions of use and distribution */
png_uint_32 sum_idat_length = 0;
png_byte *bb = NULL;
png_uint_32 malloced_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_default_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))) {
const png_byte png_MHDR[5] = { 77, 72, 68, 82, '\0' };
int b;
png_byte buff[40];
unsigned long length;
/* read the MHDR */
png_default_read_data(read_ptr, buff, 4);
length=buff[3]+(buff[2]<<8)+(buff[1]<<16)+(buff[0]<<24);
png_default_read_data(read_ptr, buff, 4);
printf("Reading %c%c%c%c chunk.\n",buff[0],buff[1],
buff[2],buff[3]);
for (b=0; b<40; b++)
buff[b]='\0';
png_default_read_data(read_ptr, buff, length);
if (verbose) {
printf(" width=%lu\n",(unsigned long)(buff[3]+(buff[2]<<8)
+(buff[1]<<16)+(buff[0]<<24)));
printf(" height=%lu\n",(unsigned long)(buff[7]+(buff[6]<<8)
+(buff[5]<<16)+(buff[4]<<24)));
printf(" ticksps=%lu\n",(unsigned long)(buff[11]+
(buff[10]<<8)+(buff[9]<<16)+(buff[8]<<24)));
printf(" nomlayc=%lu\n",(unsigned long)(buff[15]+
(buff[14]<<8)+(buff[13]<<16)+(buff[12]<<24)));
printf(" nomfram=%lu\n",(unsigned long)(buff[19]+
(buff[18]<<8)+(buff[17]<<16)+(buff[16]<<24)));
printf(" nomplay=%lu\n",(unsigned long)(buff[23]+
(buff[22]<<8)+(buff[21]<<16)+(buff[20]<<24)));
printf(" profile=%lu\n",(unsigned long)(buff[27]+
(buff[26]<<8)+(buff[25]<<16)+(buff[24]<<24)));
}
if (new_mng) {
/* write the MNG 8-byte signature */
png_default_write_data(mng_ptr, &mng_signature[0],
(png_size_t) 8);
/* Write a MHDR chunk */
png_write_chunk(mng_ptr, (png_bytep) png_MHDR,
buff, (png_size_t) 28);
}
png_default_read_data(read_ptr, buff, 4);
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");
}
}
if (fix)
{
#ifdef PNG_CRC_WARN_USE
png_set_crc_action(png_ptr, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
#endif
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
inflateUndermine(&png_ptr->zstream, 1);
#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 buff[32];
png_uint_32 length;
png_default_read_data(png_ptr, chunk_length, 4);
length = png_get_uint_31(png_ptr,chunk_length);
png_reset_crc(png_ptr);
png_crc_read(png_ptr, chunk_name, 4);
if (new_mng) {
const png_byte png_DHDR[5] = { 68, 72, 68, 82, '\0' };
const png_byte png_DEFI[5] = { 68, 69, 70, 73, '\0' };
const png_byte png_FRAM[5] = { 70, 82, 65, 77, '\0' };
const png_byte png_nEED[5] = { 110, 69, 69, 68, '\0' };
if (!png_memcmp(chunk_name, png_nEED, 4)) {
/* Skip the nEED chunk */
printf (" skipping MNG %c%c%c%c chunk, %lu bytes\n",chunk_name[0],
chunk_name[1],chunk_name[2],chunk_name[3],(unsigned long)length);
}
else {
/* copy the chunk. */
printf (" reading MNG %c%c%c%c chunk, %lu bytes\n",chunk_name[0],
chunk_name[1],chunk_name[2],chunk_name[3],(unsigned long)length);
if (length > malloced_length) {
png_free(mng_ptr,bb);
printf (" png_malloc %lu bytes.\n",(unsigned long)length);
bb=(png_byte*)png_malloc(mng_ptr, length);
malloced_length=length;
}
png_crc_read(png_ptr, bb, length);
png_write_chunk(mng_ptr, chunk_name,
bb, (png_size_t) length);
if (!png_memcmp(chunk_name, png_DHDR, 4)) {
if (verbose > 1) {
printf(" objid=%lu\n",(unsigned long)(bb[1]+(bb[0]<<8)));
printf(" itype=%lu\n",(unsigned long)(bb[2]));
printf(" dtype=%lu\n",(unsigned long)(bb[3]));
printf(" width=%lu\n",(unsigned long)(bb[7]+(bb[6]<<8)
+(bb[5]<<16)+(bb[4]<<24)));
printf(" height=%lu\n",(unsigned long)(bb[11]+(bb[10]<<8)
+(bb[9]<<16)+(bb[8]<<24)));
printf(" xloc=%lu\n",(unsigned long)(bb[15]+(bb[14]<<8)
+(bb[13]<<16)+(bb[12]<<24)));
printf(" yloc=%lu\n",(unsigned long)(bb[19]+(bb[18]<<8)
+(bb[17]<<16)+(bb[16]<<24)));
}
}
if (!png_memcmp(chunk_name, png_DEFI, 4)) {
if (verbose > 1) {
printf(" objid=%lu\n",(unsigned long)(bb[1]+(bb[0]<<8)));
printf(" do_not_show=%lu\n",(unsigned long)(bb[2]));
printf(" concrete=%lu\n",(unsigned long)(bb[3]));
if (length > 4) {
printf(" xloc=%lu\n",(unsigned long)(bb[15]+(bb[14]<<8)
+(bb[13]<<16)+(bb[12]<<24)));
printf(" yloc=%lu\n",(unsigned long)(bb[19]+(bb[18]<<8)
+(bb[17]<<16)+(bb[16]<<24)));
if (length > 12) {
printf(" l_cb=%lu\n",(unsigned long)(bb[20]+(bb[19]<<8)
+(bb[18]<<16)+(bb[17]<<24)));
printf(" r_cb=%lu\n",(unsigned long)(bb[24]+(bb[23]<<8)
+(bb[22]<<16)+(bb[21]<<24)));
}
}
}
}
if (!png_memcmp(chunk_name, png_FRAM, 4)) {
if (verbose > 1) {
printf(" mode=%lu\n",(unsigned long)bb[0]);
if (length > 1) {
int ib;
printf(" name = ");
for (ib=0; bb[ib]; ib++)
{
printf ("%c", bb[ib]);
}
printf ("\n");
}
}
}
length=0;
}
}
else {
#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 = %lu.\n", chunk_name,
(unsigned long)length);
}
#ifdef PNG_UINT_IHDR
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, buff, 13);
length -= 13;
input_color_type = buff[9];
}
else {
if (png_get_uint_32(chunk_name) == PNG_UINT_dSIG)
{
if (found_any_chunk == 0 && !all_chunks_are_safe)
{
image_is_immutable=1;
}
}
else
found_any_chunk=1;
}
#ifdef PNG_gAMA_SUPPORTED
if (png_get_uint_32(chunk_name) == PNG_UINT_gAMA)
found_gAMA=1;
#endif
#ifdef PNG_cHRM_SUPPORTED
if (png_get_uint_32(chunk_name) == PNG_UINT_cHRM)
found_cHRM=1;
#endif
#ifdef PNG_iCCP_SUPPORTED
/* check for bad Photoshop iCCP chunk */
#ifdef PNG_UINT_iCCP
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, buff, 22);
length -= 22;
buff[23] = 0;
if (!strncmp((png_const_charp) buff, "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 PNGCRUSH_LOCO
#ifdef PNG_UINT_MEND
if (png_get_uint_32(chunk_name) == PNG_UINT_MEND)
return sum_idat_length;
#else
{
const png_byte png_MEND[5] =
{ 77, 69, 78, 68, '\0' };
if (!png_memcmp(chunk_name, png_MEND, 4)) {
if (new_mng) {
png_free(mng_ptr,bb);
return (0);
}
return sum_idat_length;
}
}
#endif
#endif
if (input_format == 0) {
#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 * fp_in)
{
/* Copyright (C) 2000-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net)
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");
P1( "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) {
P1( "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, fp_in);
#else
png_set_read_fn(read_ptr, (png_voidp) fp_in, (png_rw_ptr) NULL);
#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_default_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];
unsigned long length;
/* skip the MHDR */
png_default_read_data(read_ptr, buffer, 4);
length=buffer[3]+(buffer[2]<<8)+(buffer[1]<<16)+(buffer[0]<<24);
png_default_read_data(read_ptr, buffer, 4);
printf("Skipping %c%c%c%c chunk.\n",buffer[0],buffer[1],
buffer[2],buffer[3]);
png_default_read_data(read_ptr, buffer, length);
png_default_read_data(read_ptr, buffer, 4);
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;
P2( "\nBegin count_colors() interlace pass %d\n", pass);
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;
}
}
}
P2( "End count_colors() interlace pass %d\n\n", pass);
}
} else /* (bit_depth != 8) */ {
/* 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;
P1( "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);
P1( "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 */
void print_version_info(void)
{
fprintf(STDERR,
"\n"
" | pngcrush %s\n"
/* If you have modified this source, you may insert additional notices
* immediately after this sentence: */
" | Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson\n"
" | Copyright (C) 2005 Greg Roelofs\n"
" | This is a free, open-source program. Permission is irrevocably\n"
" | granted to everyone to use this version of pngcrush without\n"
" | payment of any fee.\n"
" | Executable name is %s\n"
" | It was built with libpng version %s, and is\n"
" | running with %s"
" | Copyright (C) 1998-2004,2006-2009 Glenn Randers-Pehrson,\n"
" | Copyright (C) 1996, 1997 Andreas Dilger,\n"
" | Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,\n"
" | and zlib version %s, Copyright (C) 1998-2002 (or later),\n"
" | Jean-loup Gailly and Mark Adler.\n",
PNGCRUSH_VERSION, progname, PNG_LIBPNG_VER_STRING,
png_get_header_version(NULL), ZLIB_VERSION);
#if defined(__GNUC__)
fprintf(STDERR,
" | It was compiled with gcc version %s", __VERSION__);
# if defined(PNG_USE_PNGGCCRD)
fprintf(STDERR,
" and gas version %s", GAS_VERSION);
# endif
# if defined(__DJGPP__)
fprintf(STDERR,
"\n"
" | under DJGPP %d.%d, Copyright (C) 1995, D. J. Delorie\n"
" | and loaded with PMODE/DJ, by Thomas Pytel and Matthias Grimrath\n"
" | Copyright (C) 1996, Matthias Grimrath.\n",
__DJGPP__, __DJGPP_MINOR__);
# else
fprintf(STDERR, ".\n");
# endif
#endif
fprintf(STDERR, "\n");
}
static const char *pngcrush_legal[] = {
"",
/* If you have modified this source, you may insert additional notices
* immediately after this sentence: */
"Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson (glennrp@users.sf.net)",
"Copyright (C) 2005 Greg Roelofs",
"",
"DISCLAIMER: 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.",
"",
"LICENSE: Permission is hereby irrevocably granted to everyone to use,",
"copy, modify, and distribute this computer program, or portions hereof,",
"purpose, without payment of any fee, subject to the following",
"restrictions:",
"",
"1. The origin of this binary or source code must not be misrepresented.",
"",
"2. Altered versions must be plainly marked as such and must not be",
"misrepresented as being the original binary or source.",
"",
"3. The Copyright notice, disclaimer, and license may not be removed",
"or altered from any source, binary, or altered source distribution.",
""
};
static const char *pngcrush_usage[] = {
"\nusage: %s [options] infile.png outfile.png\n",
" %s -e ext [other options] files.png ...\n",
" %s -d dir [other options] files.png ...\n"
};
struct options_help pngcrush_options[] = {
{0, " -already already_crushed_size [e.g., 8192]"},
{2, ""}, /* blank */
{2, " If file has an IDAT greater than this size, it"},
{2, " will be considered to be already crushed and will"},
{2, " not be processed, unless you are making other changes"},
{2, " or the \"-force\" option is present."},
{2, ""},
{0, " -bit_depth depth (bit_depth to use in output file)"},
{2, ""},
{2, " Default output depth is same as input depth."},
{2, ""},
#ifdef Z_RLE
{0, " -brute (use brute-force: try 126 different methods [11-136])"},
#else
{0, " -brute (use brute-force: try 114 different methods [11-124])"},
#endif
{2, ""},
{2, " Very time-consuming and generally not worthwhile."},
{2, " You can restrict this option to certain filter types,"},
{2, " compression levels, or strategies by following it"},
{2, " with \"-f filter\", \"-l level\", or \"-z strategy\"."},
{2, ""},
{0, " -c color_type of output file [0, 2, 4, or 6]"},
{2, ""},
{2, " Color type for the output file. Future versions"},
{2, " will also allow color_type 3, if there are 256 or"},
{2, " fewer colors present in the input file. Color types"},
{2, " 4 and 6 are padded with an opaque alpha channel if"},
{2, " the input file does not have alpha information."},
{2, " You can use 0 or 4 to convert color to grayscale."},
{2, " Use 0 or 2 to delete an unwanted alpha channel."},
{2, " Default is to use same color type as the input file."},
{2, ""},
#ifdef PNGCRUSH_COUNT_COLORS
{0, " -cc (do color counting)"},
{2, ""},
#endif
{0, " -d directory_name (where output files will go)"},
{2, ""},
{2, " If a directory name is given, then the output"},
{2, " files are placed in it, with the same filenames as"},
{2, " those of the original files. For example,"},
{2, " you would type 'pngcrush -directory CRUSHED *.png'"},
{2, " to get *.png => CRUSHED/*.png"},
{2, ""},
{0, FAKE_PAUSE_STRING},
{0, " -double_gamma (used for fixing gamma in PhotoShop 5.0/5.02 files)"},
{2, ""},
{2, " It has been claimed that the PS5 bug is actually"},
{2, " more complex than that, in some unspecified way."},
{2, ""},
{0, " -e extension (used for creating output filename)"},
{2, ""},
{2, " e.g., -ext .new means *.png => *.new"},
{2, " and -e _C.png means *.png => *_C.png"},
{2, ""},
{0, " -f user_filter [0-5]"},
{2, ""},
{2, " filter to use with the method specified in the"},
{2, " preceding '-m method' or '-brute_force' argument."},
{2, " 0: none; 1-4: use specified filter; 5: adaptive."},
{2, ""},
{0, " -fix (fix otherwise fatal conditions such as bad CRCs)"},
{2, ""},
{0, " -force (write a new output file even if larger than input)"},
{2, ""},
{2, " Otherwise the input file will be copied to output"},
{2, " if it is smaller than any generated file and no chunk"},
{2, " additions, removals, or changes were requested."},
{2, ""},
#ifdef PNG_FIXED_POINT_SUPPORTED
{0, " -g gamma (float or fixed*100000, e.g., 0.45455 or 45455)"},
#else
{0, " -g gamma (float, e.g., 0.45455)"},
#endif
{2, ""},
{2, " Value to insert in gAMA chunk, only if the input"},
{2, " file has no gAMA chunk. To replace an existing"},
{2, " gAMA chunk, use the '-replace_gamma' option."},
{2, ""},
{0, FAKE_PAUSE_STRING},
{0, " -huffman (use only zlib strategy 2, Huffman-only)"},
{2, ""},
{2, " Fast, but almost never very effective except for"},
{2, " certain rare image types."},
{2, ""},
#ifdef PNG_iCCP_SUPPORTED
{0, " -iccp length \"Profile Name\" iccp_file"},
{2, ""},
{2, " file with ICC profile to insert in an iCCP chunk."},
{2, ""},
#endif
#ifdef PNG_iTXt_SUPPORTED
{0, " -itxt b[efore_IDAT]|a[fter_IDAT] \"keyword\""},
{2, " \"language_code\" \"translated_keyword\" \"text\""},
{2, ""},
{2, " Uncompressed iTXt chunk to insert (see -text)."},
{2, ""},
#endif
{0, " -keep chunk_name"},
{2, ""},
{2, " keep named chunk even when pngcrush makes"},
{2, " changes to the PNG datastream that cause it"},
{2, " to become invalid. Currently only dSIG is"},
{2, " recognized as a chunk to be kept."},
{2, ""},
{0, " -l zlib_compression_level [0-9]"},
{2, ""},
{2, " zlib compression level to use with method specified"},
{2, " with the preceding '-m method' or '-brute_force'"},
{2, " argument."},
{2, ""},
#ifdef PNGCRUSH_LOCO
{0, " -loco (\"loco crush\" truecolor PNGs)"},
{2, ""},
{2, " Make the file more compressible by performing a"},
{2, " lossless, reversible, color transformation."},
{2, " The resulting file is a MNG, not a PNG, and should"},
{2, " be given the \".mng\" file extension. The"},
{2, " \"loco\" option has no effect on grayscale or"},
{2, " indexed-color PNG files."},
{2, ""},
#endif
{0, " -m method [0 through " STRNGIFY(MAX_METHODS) "]"},
{2, ""},
{2, " pngcrush method to try (0 means try all of 1-10)."},
{2, " Can be repeated as in '-m 1 -m 4 -m 7'."},
{2, " This can be useful if pngcrush runs out of memory"},
{2, " when it tries methods 2, 3, 5, 6, 8, 9, or 10 which"},
{2, " use filtering and are memory-intensive. Methods"},
{2, " 1, 4, and 7 use no filtering; methods 11 and up use"},
{2, " specified filter, compression level, and strategy."},
{2, ""},
{2, FAKE_PAUSE_STRING},
{0, " -max maximum_IDAT_size [default "STRNGIFY(PNG_ZBUF_SIZE)"]"},
{2, ""},
#ifdef PNGCRUSH_LOCO
{0, " -mng (write a new MNG, do not crush embedded PNGs)"},
{2, ""},
#endif
{0, " -newtimestamp"},
{2, ""},
{2, " Reset file modification time [default]."},
{2, ""},
#ifdef PNGCRUSH_COUNT_COLORS
{0, " -no_cc (no color counting)"},
{2, ""},
#endif
{0, " -nofilecheck (do not check for infile.png == outfile.png)"},
{2, ""},
{2, " To avoid false hits from MSVC-compiled code. Note"},
{2, " that if you use this option, you are responsible for"},
{2, " ensuring that the input file is not the output file."},
{2, ""},
{0, " -oldtimestamp"},
{2, ""},
{2, " Don't reset file modification time."},
{2, ""},
{0, " -n (no save; does not do compression or write output PNG)"},
{2, ""},
{2, " Useful in conjunction with -v option to get info."},
{2, ""},
{0, " -plte_len n (truncate PLTE)"},
{2, ""},
{2, " Truncates the PLTE. Be sure not to truncate it to"},
{2, " less than the greatest index present in IDAT."},
{2, ""},
{0, " -q (quiet)"},
{2, ""},
{0, " -reduce (do lossless color-type or bit-depth reduction)"},
{2, ""},
{2, " (if possible)"},
{2, ""},
{0, " -rem chunkname (or \"alla\" or \"allb\")"},
{2, ""},
{2, " Name of an ancillary chunk or optional PLTE to be"},
{2, " removed. Be careful with this. Please don't use "},
{2, " this feature to remove transparency, gamma, copyright,"},
{2, " or other valuable information. To remove several"},
{2, " different chunks, repeat: -rem tEXt -rem pHYs."},
{2, " Known chunks (those in the PNG 1.1 spec or extensions"},
{2, " document) can be named with all lower-case letters,"},
{2, " so \"-rem bkgd\" is equivalent to \"-rem bKGD\". But"},
{2, " note: \"-rem text\" removes all forms of text chunks;"},
{2, " Exact case is required to remove unknown chunks."},
{2, " To do surgery with a chain-saw, \"-rem alla\" removes"},
{2, " all known ancillary chunks except for tRNS, and"},
{2, " \"-rem allb\" removes all but tRNS and gAMA."},
{2, ""},
{0, FAKE_PAUSE_STRING},
#ifdef PNG_FIXED_POINT_SUPPORTED
{0, "-replace_gamma gamma (float or fixed*100000) even if gAMA is present."},
#else
{0, "-replace_gamma gamma (float, e.g. 0.45455) even if gAMA is present."},
#endif
{2, ""},
{0, " -res dpi"},
{2, ""},
{2, " Write a pHYs chunk with the given resolution."},
{2, ""},
#ifdef Z_RLE
{0, " -rle (use only zlib strategy 3, RLE-only)"},
{2, ""},
{2, " A relatively fast subset of the \"-brute\" methods,"},
{2, " generally more effective than \"-huffman\" on PNG images"},
{2, " (and quite effective on black-and-white images),"},
{2, " but not necessarily worth the bother otherwise."},
{2, ""},
#endif
{0, " -save (keep all copy-unsafe chunks)"},
{2, ""},
{2, " Save otherwise unknown ancillary chunks that would"},
{2, " be considered copy-unsafe. This option makes"},
{2, " chunks 'known' to pngcrush, so they can be copied."},
{2, " It also causes the dSIG chunk to be saved, even when"},
{2, " it becomes invalid due to datastream changes."},
{2, ""},
{0, FAKE_PAUSE_STRING},
{0, " -srgb [0, 1, 2, or 3]"},
{2, ""},
{2, " Value of 'rendering intent' for sRGB chunk."},
{2, ""},
{0, " -ster [0 or 1]"},
{2, ""},
{2, " Value of 'stereo mode' for sTER chunk."},
{2, " 0: cross-fused; 1: divergent-fused"},
{2, ""},
{0, " -text b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\""},
{2, ""},
{2, " tEXt chunk to insert. keyword < 80 chars,"},
{2, " text < 2048 chars. For now, you can add no more than"},
{2, " ten tEXt, iTXt, or zTXt chunks per pngcrush run."},
{2, ""},
#ifdef PNG_tRNS_SUPPORTED
{0, " -trns_array n trns[0] trns[1] .. trns[n-1]"},
{2, ""},
{2, " Insert a tRNS chunk, if no tRNS chunk found in file."},
{2, " Values are for the tRNS array in indexed-color PNG."},
{2, ""},
{0, " -trns index red green blue gray"},
{2, ""},
{2, " Insert a tRNS chunk, if no tRNS chunk found in file."},
{2, " You must give all five parameters regardless of the"},
{2, " color type, scaled to the output bit depth."},
{2, ""},
#endif
{0, " -v (display more detailed information)"},
{2, ""},
{2, " Repeat the option (use \"-v -v\") for even more."},
{2, ""},
{0, " -version (display the pngcrush version)"},
{2, ""},
{2, " Look for the most recent version of pngcrush at"},
{2, " http://pmt.sf.net"},
{2, ""},
{0, " -w compression_window_size [32, 16, 8, 4, 2, 1, 512]"},
{2, ""},
{2, " Size of the sliding compression window, in kbytes"},
{2, " (or bytes, in case of 512). It's best to"},
{2, " use the default (32) unless you run out of memory."},
{2, " The program will use a smaller window anyway when"},
{2, " the uncompressed file is smaller than 16k."},
{2, ""},
#ifdef Z_RLE
{0, " -z zlib_strategy [0, 1, 2, or 3]"},
#else
{0, " -z zlib_strategy [0, 1, or 2]"},
#endif
{2, ""},
{2, " zlib compression strategy to use with the preceding"},
{2, " '-m method' argument."},
{2, ""},
{0, " -zmem zlib_compression_mem_level [1-9, default 9]"},
{2, ""},
#ifdef PNG_iTXt_SUPPORTED
{0, " -zitxt b[efore_IDAT]|a[fter_IDAT] \"keyword\""},
{2, " \"language_code\" \"translated_keyword\" \"text\""},
{2, ""},
{2, " Compressed iTXt chunk to insert (see -text)."},
{2, ""},
#endif
{0, " -ztxt b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\""},
{2, ""},
{2, " zTXt chunk to insert (see -text)."},
{2, ""},
{2, FAKE_PAUSE_STRING},
{0, " -h (help and legal notices)"},
{2, ""},
{2, " Display this information."},
{2, ""},
{0, " -p (pause)"}
};
void print_usage(int retval)
{
int j, jmax;
if (verbose) {
jmax = sizeof(pngcrush_legal) / sizeof(char *);
for (j = 0; j < jmax; ++j)
fprintf(STDERR, "%s\n", pngcrush_legal[j]);
jmax = sizeof(pngcrush_usage) / sizeof(char *);
for (j = 0; j < jmax; ++j)
fprintf(STDERR, pngcrush_usage[j], progname); /* special case */
}
/* this block is also handled specially due to the "else" clause... */
if (verbose > 1) {
png_crush_pause();
fprintf(STDERR,
"\n"
"options (Note: any option can be spelled out for clarity, e.g.,\n"
" \"pngcrush -dir New -method 7 -remove bkgd *.png\"\n"
" is the same as \"pngcrush -d New -m 7 -rem bkgd *.png\"):"
"\n\n");
} else
fprintf(STDERR, "options:\n");
/* this is the main part of the help screen; it is more complex than the
* other blocks due to the mix of verbose and non-verbose lines */
jmax = sizeof(pngcrush_options) / sizeof(struct options_help);
for (j = 0; j < jmax; ++j) {
if (verbose >= pngcrush_options[j].verbosity) {
if (pngcrush_options[j].textline[0] == FAKE_PAUSE_STRING[0])
png_crush_pause();
else
fprintf(STDERR, "%s\n", pngcrush_options[j].textline);
}
}
/* due to progname, the verbose part of the -p option is handled explicitly
* (fortunately, it's the very last option anyway) */
if (verbose > 1) {
fprintf(STDERR, "\n"
" Wait for [enter] key before continuing display.\n"
" e.g., type '%s -pause -help', if the help\n"
" screen scrolls out of sight.\n\n", progname);
}
exit(retval);
}