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