| /* |
| * pngcrush.c - recompresses png files |
| * Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson |
| * (glennrp at users.sf.net) |
| * Copyright (C) 2005 Greg Roelofs |
| * |
| * This software is released under a license derived from the libpng |
| * license (see LICENSE, below). |
| * |
| * 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. |
| * |
| * Caution: there is another version of pngcrush that has been distributed by |
| * Apple since mid-2008 as a part of the Xcode SDK. Although it claims |
| * to be pngcrush by Glenn Randers-Pehrson, it has additional options |
| * "-iPhone" * and "-speed". It writes files that have the PNG 8-byte signature |
| * but are not valid PNG files, due to at least |
| * |
| * o the presence of the CgBI chunk ahead of the IHDR chunk, |
| * o nonstandard deflate compression in IDAT, iCCP, and perhaps zTXt chunks, |
| * o the use of premultiplied alpha in color_type 6 files, and |
| * o the sample order is ARGB instead of RGBA in color_type 6 files. |
| * |
| * The original PNG file cannot be losslessly recovered from such files |
| * because of the use of premultiplied alpha. |
| * |
| * Most PNG decoders will recognize the fact that an unknown critical |
| * chunk "CgBI" is present and will immediately reject the file. |
| * |
| * It is said that the Xcode version of pngcrush is automatically applied |
| * when PNG files are prepared for downloading to the iPhone unless the |
| * user takes special measures to prevent it. |
| * |
| * I have not seen the source for the Xcode version of pngcrush. All I |
| * know, for now, is from running "strings -a" on a copy of the executable, |
| * looking at two Xcode-PNG files, and reading Apple's patent application |
| * <http://www.freepatentsonline.com/y2008/0177769.html>. |
| * |
| */ |
| |
| #define PNGCRUSH_VERSION "1.6.20" |
| |
| /* |
| #define PNGCRUSH_COUNT_COLORS |
| */ |
| |
| /* |
| * NOTICES |
| * |
| * If you have modified this source, you may insert additional notices |
| * immediately after this sentence. |
| * |
| * COPYRIGHT: |
| * |
| * Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson |
| * (glennrp at users.sf.net) |
| * Copyright (C) 2005 Greg Roelofs |
| * |
| * DISCLAIMERS: |
| * |
| * The pngcrush computer program is supplied "AS IS". The Author disclaims all |
| * warranties, expressed or implied, including, without limitation, the |
| * warranties of merchantability and of fitness for any purpose. The |
| * Author assumes no liability for direct, indirect, incidental, special, |
| * exemplary, or consequential damages, which may result from the use of |
| * the computer program, even if advised of the possibility of such damage. |
| * There is no warranty against interference with your enjoyment of the |
| * computer program or against infringement. There is no warranty that my |
| * efforts or the computer program will fulfill any of your particular purposes |
| * or needs. This computer program is provided with all faults, and the entire |
| * risk of satisfactory quality, performance, accuracy, and effort is with |
| * the user. |
| * |
| * LICENSE: |
| * |
| * Permission is hereby irrevocably granted to everyone to use, copy, modify, |
| * and distribute this source code, or portions hereof, or executable programs |
| * compiled from it, for any purpose, without payment of any fee, subject to |
| * the following restrictions: |
| * |
| * 1. The origin of this source code must not be misrepresented. |
| * |
| * 2. Altered versions must be plainly marked as such and must not be |
| * misrepresented as being the original source. |
| * |
| * 3. This Copyright notice, disclaimer, and license may not be removed |
| * or altered from any source or altered source distribution. |
| */ |
| |
| /* To do: |
| * |
| * Reset CINFO to reflect decoder's required window size (instead of |
| * libz-1.1.3 encoder's required window size, which is 262 bytes larger). |
| * See discussion about zlib in png-list archives for April 2001. |
| * |
| * Add a "pcRu" ancillary chunk that keeps track of the best method, |
| * methods already tried, and whether "loco crushing" was effective. |
| * |
| * Try both transformed and untransformed colors when "-loco" is used. |
| * |
| * Check for unused alpha channel and ok-to-reduce-depth. |
| * Take care that sBIT and bKGD data aren't lost when reducing images |
| * from truecolor to grayscale. |
| * |
| * Rearrange palette to put most-used color first and transparent color |
| * second (see ImageMagick 5.1.1 and later). |
| * |
| * Finish pplt (partial palette) feature. |
| * |
| * Allow in-place file replacement or as a filter, as in |
| * "pngcrush -overwrite file.png" |
| * "pngcreator | pngcrush > output.png" |
| * |
| * Use an alternate write function for the trial passes, that |
| * simply counts bytes rather than actually writing to a file, to save wear |
| * and tear on disk drives. |
| * |
| * Remove text-handling and color-handling features and put |
| * those in a separate program or programs, to avoid unnecessary |
| * recompressing. |
| * |
| * Move the Photoshop-fixing stuff into a separate program. |
| * |
| * GRR: More generally (superset of previous 3 items): split into separate |
| * "edit" and "crush" programs (or functions). Former is fully libpng- |
| * aware, much like current pngcrush; latter makes little or no use of |
| * libpng (maybe IDAT-compression parts only?), instead handling virtually |
| * all chunks as opaque binary blocks that are copied to output file _once_, |
| * with IDATs alone replaced (either by best in-memory result or by original |
| * _data_ resplit into bigger IDATs, if pngcrush can't match/beat). "edit" |
| * version should be similar to current code but more efficient: make |
| * _one_ pass through args list, creating table of PNG_UINTs for removal; |
| * then make initial pass through PNG image, creating (in-order) table of |
| * all chunks (and byte offsets?) and marking each as "keep" or "remove" |
| * according to args table. Can start with static table of ~20-30 slots, |
| * then double size & copy if run out of room: still O(n) algorithm. |
| */ |
| |
| #if 0 /* changelog */ |
| |
| Change log: |
| |
| Version 1.6.20 (built with libpng-1.2.38 and zlib-1.2.3.2) |
| Changed local variable "write" to "wwrite" in inffast.c (zlib) to avoid |
| shadowed declaration warning. |
| |
| Version 1.6.19 (built with libpng-1.2.37 and zlib-1.2.3.2) |
| Added missing braces that cause an incorrect png_error() to be issued. |
| |
| Version 1.6.18 (built with libpng-1.2.37 and zlib-1.2.3.2) |
| Removed extra FCLOSE(fpin) and FCLOSE(fpout) in the first Catch{} block, |
| since they get removed anyway right after that (hanno boeck). |
| Define PNG_NO_READ|WRITE_cHRM and PNG_NO_READ_|WRITEiCCP in pngcrush.h |
| and reordered pngcrush.h |
| |
| Version 1.6.17 (built with libpng-1.2.36 and zlib-1.2.3.2) |
| Defined TOO_FAR == 32767 in deflate.c (again). The definition |
| has continually been inadvertently omitted during zlib updates |
| since pngcrush version 1.6.4. |
| Revised handling of xcode files so at least we can get printout |
| of IHDR values with "pngcrush -fix -n -v xcode.png". |
| Moved ChangeLog.txt back into pngcrush.c so it does not get lost. |
| Removed single quotes from the ChangeLog. |
| |
| Version 1.6.16 (built with libpng-1.2.35 and zlib-1.2.3.2) |
| Added -newtimestamp and -oldtimestamp options and changed |
| default condition to timestamping the output file with |
| the current time (i.e., -newtimestamp is default) |
| If the -oldtimestamp option is used then the output file |
| has the same timestamp as the input file. |
| Added CgBI chunk detection. |
| |
| Version 1.6.15 (built with libpng-1.2.35 and zlib-1.2.3.2) |
| Fixes some missing typecasts on png_malloc() calls, patch from |
| an anonymous reporter to the SourceForge bug tracker. |
| Added -time_stamp option to change time stamping from default |
| condition. |
| |
| Version 1.6.14 (built with libpng-1.2.35 and zlib-1.2.3.2) |
| Avoids CVE-2009-0040. |
| |
| Version 1.6.12 (built with libpng-1.2.34 and zlib-1.2.3.2) |
| |
| Version 1.6.11 (built with libpng-1.2.33 and zlib-1.2.3.2) |
| Eliminated a memory leak in libpng with writing bad tEXt chunks. |
| |
| Version 1.6.10 (built with libpng-1.2.31 and zlib-1.2.3.2) |
| Add sTER chunk support. |
| |
| Version 1.6.9 (built with libpng-1.2.31 and zlib-1.2.3.2) |
| Updated cexcept.h to version 2.0.1 |
| Add missing curly brackets. |
| |
| Version 1.6.8 (built with libpng-1.2.29 and zlib-1.2.3.2) |
| Fixed bug with handling of -z and -zi options. |
| |
| Version 1.6.7 (built with libpng-1.2.29 and zlib-1.2.3.2) |
| Moved PNG_UINT_CHNK and some other defines from pngcrush.h to pngcrush.c |
| Reject invalid color_type or bit_depth. |
| |
| Version 1.6.6 (built with libpng-1.2.29 and zlib-1.2.3.2) |
| Added dSIG support. Pngcrush will not rewrite an image containing |
| a dSIG chunk immediately following the IHDR chunk, unless the |
| dSIG is explicitly removed with "-rem dSIG" or explicitly kept |
| with "-keep dSIG". In the latter case the saved dSIG chunks will |
| become invalid if any changes are made to the datastream. |
| |
| Fixed bug in writing unknown chunks from the end_info_ptr. |
| |
| Version 1.6.5 (built with libpng-1.2.29 and zlib-1.2.3.2) |
| Discontinued adding a new gAMA chunk when writing sRGB chunk. |
| |
| Version 1.6.4 (built with libpng-1.2.9rc1 and zlib-1.2.3) |
| Fixed bug in handling of undocumented -trns_a option (Michal Politowski). |
| Fixed bug with "nosave" handling of unknown chunks. |
| |
| Version 1.6.3 (built with libpng-1.2.9beta11 and zlib-1.2.3) |
| |
| Fixed documentation of iTXt input (Shlomi Tal). |
| Removed #define PNG_INTERNAL and provided prototypes for some |
| internal libpng functions that are duplicated in pngcrush.c |
| |
| Version 1.6.2 (built with libpng-1.2.8 and zlib-1.2.3) |
| |
| Fixed bug with "PNG_ROWBYTES" usage, introduced in version 1.6.0. |
| The bug could cause a crash and only affects the "nolib" builds. |
| |
| Converted C++ style (// ...) comments to C style (/* ... */). |
| |
| Defined TOO_FAR == 32767 in deflate.c (again). The definition was |
| omitted from version 1.6.0 when zlib was upgraded to version 1.2.3. |
| |
| Version 1.6.1 (distributed as 1.6.0, built with libpng-1.2.8 and zlib-1.2.3) |
| |
| Copied non-exported libpng functions from libpng into pngcrush, to make |
| pngcrush play more nicely with shared libpng. These are not compiled |
| when a static library is being built with the bundled libpng and |
| pngcrush.h is included. |
| |
| Version 1.6.0-grr (built with libpng-1.2.4 and zlib-1.1.4pc or zlib-1.2.2) |
| |
| Moved ChangeLog out of pngcrush.c comments and into a separate file. |
| |
| Filtered pngcrush.c through "indent -kr" and "expand" for readability. |
| |
| Moved 550 lines of usage/help/copyright/license/version info to separate |
| function(s) and cleaned up significantly. |
| |
| Added some comments for ease of navigation and readability. |
| |
| Stripped out a bunch of ancient-libpng compatibility stuff. |
| |
| Defined PNG_UINT_* macros (pngcrush.h for now). |
| |
| Fixed unknown-chunk handling ("-rem alla" and "-rem gifx" now work). |
| |
| Created modified version of makefile that supports external zlib. |
| |
| Added support for methods using Z_RLE zlib strategy (zlib 1.2.x only). |
| |
| Documented -huffman option in usage screen. |
| |
| Added IDAT statistics to final per-file summary. |
| |
| Added utime() support to give output files same timestamps as input files. |
| |
| Version 1.5.10 (built with libpng-1.2.4 and zlib-1.1.4pc) |
| |
| Fixed bug, introduced in 1.5.9, that caused defaults for method 0 to |
| be used instead of copying the original image, when the original was |
| already smallest. |
| |
| Version 1.5.9 (built with libpng-1.2.4beta3 and zlib-1.1.4pc) |
| |
| Work around CPU timer wraparound at 2G microseconds. |
| |
| Upgraded zlib from 1.1.3 to 1.1.4. Pngcrush is believed not to |
| be vulnerable to the zlib-1.1.3 buffer-overflow bug. |
| |
| Choose the first instance of smallest IDAT instead of the last, |
| for faster final recompression, suggested by TSamuel. |
| |
| 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 CRCs 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 libpngs 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. |
| |
| "#ifdefed" 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 |
| |
| "#ifdefed" 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. |
| |
| "#ifdefed" 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 was not 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. |
| |
| #endif /* end of changelog */ |
| |
| #include "png.h" |
| |
| /* internal libpng macros */ |
| |
| |
| #ifdef PNG_LIBPNG_VER |
| #define PNGCRUSH_LIBPNG_VER PNG_LIBPNG_VER |
| #else |
| /* |
| * This must agree with PNG_LIBPNG_VER; you have to define it manually |
| * here if you are using libpng-1.0.6h or earlier |
| */ |
| #define PNGCRUSH_LIBPNG_VER 10007 |
| #endif |
| |
| /* Changed in version 0.99 */ |
| #if PNGCRUSH_LIBPNG_VER < 99 |
| #undef PNG_CONST |
| #ifndef PNG_NO_CONST |
| # define PNG_CONST const |
| #else |
| # define PNG_CONST |
| #endif |
| #endif |
| |
| #define PNG_IDAT const png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} |
| #define PNG_IHDR const png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} |
| #define PNG_dSIG const png_byte png_dSIG[5] = {100, 83, 73, 71, '\0'} |
| #define PNG_iCCP const png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} |
| #define PNG_IEND const png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} |
| |
| /* GRR 20050220: added these, which apparently aren't defined anywhere else */ |
| #ifndef PNG_UINT_IHDR |
| # define PNG_UINT_IHDR (((png_uint_32) 73<<24) | \ |
| ((png_uint_32) 72<<16) | \ |
| ((png_uint_32) 68<< 8) | \ |
| ((png_uint_32) 82 )) |
| #endif |
| |
| #ifndef PNG_UINT_IDAT |
| # define PNG_UINT_IDAT (((png_uint_32) 73<<24) | \ |
| ((png_uint_32) 68<<16) | \ |
| ((png_uint_32) 65<< 8) | \ |
| ((png_uint_32) 84 )) |
| #endif |
| |
| #ifndef PNG_UINT_IEND |
| # define PNG_UINT_IEND (((png_uint_32) 73<<24) | \ |
| ((png_uint_32) 69<<16) | \ |
| ((png_uint_32) 78<< 8) | \ |
| ((png_uint_32) 68 )) |
| #endif |
| |
| #ifndef PNG_UINT_PLTE |
| # define PNG_UINT_PLTE (((png_uint_32) 80<<24) | \ |
| ((png_uint_32) 76<<16) | \ |
| ((png_uint_32) 84<< 8) | \ |
| ((png_uint_32) 69 )) |
| #endif |
| |
| #ifndef PNG_UINT_bKGD |
| # define PNG_UINT_bKGD (((png_uint_32) 98<<24) | \ |
| ((png_uint_32) 75<<16) | \ |
| ((png_uint_32) 71<< 8) | \ |
| ((png_uint_32) 68 )) |
| #endif |
| |
| /* glennrp added CgBI at pngcrush-1.6.16 */ |
| #ifndef PNG_UINT_CgBI |
| # define PNG_UINT_CgBI (((png_uint_32) 67<<24) | \ |
| ((png_uint_32) 103<<16) | \ |
| ((png_uint_32) 66<< 8) | \ |
| ((png_uint_32) 73 )) |
| #endif |
| |
| #ifndef PNG_UINT_cHRM |
| # define PNG_UINT_cHRM (((png_uint_32) 99<<24) | \ |
| ((png_uint_32) 72<<16) | \ |
| ((png_uint_32) 82<< 8) | \ |
| ((png_uint_32) 77 )) |
| #endif |
| |
| #ifndef PNG_UINT_dSIG |
| # define PNG_UINT_dSIG (((png_uint_32) 100<<24) | \ |
| ((png_uint_32) 83<<16) | \ |
| ((png_uint_32) 73<< 8) | \ |
| ((png_uint_32) 71 )) |
| #endif |
| |
| #ifndef PNG_UINT_gAMA |
| # define PNG_UINT_gAMA (((png_uint_32) 103<<24) | \ |
| ((png_uint_32) 65<<16) | \ |
| ((png_uint_32) 77<< 8) | \ |
| ((png_uint_32) 65 )) |
| #endif |
| |
| #ifndef PNG_UINT_hIST |
| # define PNG_UINT_hIST (((png_uint_32) 104<<24) | \ |
| ((png_uint_32) 73<<16) | \ |
| ((png_uint_32) 83<< 8) | \ |
| ((png_uint_32) 84 )) |
| #endif |
| |
| #ifndef PNG_UINT_iCCP |
| # define PNG_UINT_iCCP (((png_uint_32) 105<<24) | \ |
| ((png_uint_32) 67<<16) | \ |
| ((png_uint_32) 67<< 8) | \ |
| ((png_uint_32) 80 )) |
| #endif |
| |
| #ifndef PNG_UINT_iTXt |
| # define PNG_UINT_iTXt (((png_uint_32) 105<<24) | \ |
| ((png_uint_32) 84<<16) | \ |
| ((png_uint_32) 88<< 8) | \ |
| ((png_uint_32) 116 )) |
| #endif |
| |
| #ifndef PNG_UINT_oFFs |
| # define PNG_UINT_oFFs (((png_uint_32) 111<<24) | \ |
| ((png_uint_32) 70<<16) | \ |
| ((png_uint_32) 70<< 8) | \ |
| ((png_uint_32) 115 )) |
| #endif |
| |
| #ifndef PNG_UINT_pCAL |
| # define PNG_UINT_pCAL (((png_uint_32) 112<<24) | \ |
| ((png_uint_32) 67<<16) | \ |
| ((png_uint_32) 65<< 8) | \ |
| ((png_uint_32) 76 )) |
| #endif |
| |
| #ifndef PNG_UINT_pHYs |
| # define PNG_UINT_pHYs (((png_uint_32) 112<<24) | \ |
| ((png_uint_32) 72<<16) | \ |
| ((png_uint_32) 89<< 8) | \ |
| ((png_uint_32) 115 )) |
| #endif |
| |
| #ifndef PNG_UINT_sBIT |
| # define PNG_UINT_sBIT (((png_uint_32) 115<<24) | \ |
| ((png_uint_32) 66<<16) | \ |
| ((png_uint_32) 73<< 8) | \ |
| ((png_uint_32) 84 )) |
| #endif |
| |
| #ifndef PNG_UINT_sCAL |
| # define PNG_UINT_sCAL (((png_uint_32) 115<<24) | \ |
| ((png_uint_32) 67<<16) | \ |
| ((png_uint_32) 65<< 8) | \ |
| ((png_uint_32) 76 )) |
| #endif |
| |
| #ifndef PNG_UINT_sPLT |
| # define PNG_UINT_sPLT (((png_uint_32) 115<<24) | \ |
| ((png_uint_32) 80<<16) | \ |
| ((png_uint_32) 76<< 8) | \ |
| ((png_uint_32) 84 )) |
| #endif |
| |
| #ifndef PNG_UINT_sTER |
| # define PNG_UINT_sTER (((png_uint_32) 115<<24) | \ |
| ((png_uint_32) 84<<16) | \ |
| ((png_uint_32) 69<< 8) | \ |
| ((png_uint_32) 82 )) |
| #endif |
| |
| #ifndef PNG_UINT_sRGB |
| # define PNG_UINT_sRGB (((png_uint_32) 115<<24) | \ |
| ((png_uint_32) 82<<16) | \ |
| ((png_uint_32) 71<< 8) | \ |
| ((png_uint_32) 66 )) |
| #endif |
| |
| #ifndef PNG_UINT_tEXt |
| # define PNG_UINT_tEXt (((png_uint_32) 116<<24) | \ |
| ((png_uint_32) 69<<16) | \ |
| ((png_uint_32) 88<< 8) | \ |
| ((png_uint_32) 116 )) |
| #endif |
| |
| #ifndef PNG_UINT_tIME |
| # define PNG_UINT_tIME (((png_uint_32) 116<<24) | \ |
| ((png_uint_32) 73<<16) | \ |
| ((png_uint_32) 77<< 8) | \ |
| ((png_uint_32) 69 )) |
| #endif |
| |
| #ifndef PNG_UINT_tRNS |
| # define PNG_UINT_tRNS (((png_uint_32) 116<<24) | \ |
| ((png_uint_32) 82<<16) | \ |
| ((png_uint_32) 78<< 8) | \ |
| ((png_uint_32) 83 )) |
| #endif |
| |
| #ifndef PNG_UINT_zTXt |
| # define PNG_UINT_zTXt (((png_uint_32) 122<<24) | \ |
| ((png_uint_32) 84<<16) | \ |
| ((png_uint_32) 88<< 8) | \ |
| ((png_uint_32) 116 )) |
| #endif |
| |
| #define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 |
| #define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 |
| #define PNG_FLAG_CRC_CRITICAL_USE 0x0400 |
| #define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 |
| #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ |
| PNG_FLAG_CRC_ANCILLARY_NOWARN) |
| #define PNG_PACK 0x0004 |
| #define PNG_DITHER 0x0040 |
| #define PNG_BACKGROUND 0x0080 |
| #define PNG_16_TO_8 0x0400 |
| #define PNG_RGBA 0x0800 |
| #define PNG_EXPAND 0x1000 |
| #define PNG_GAMMA 0x2000 |
| #define PNG_GRAY_TO_RGB 0x4000 |
| #define PNG_FILLER 0x8000L |
| #define PNG_USER_TRANSFORM 0x100000L |
| #define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ |
| |
| /* |
| * We don't need some of the extra libpng transformations |
| * so they are ifdef'ed out in pngcrush.h, which is included by |
| * pngcrush's local copy of libpng's pngconf.h which is included |
| * by png.h |
| * |
| */ |
| |
| /* defined so I can write to a file on gui/windowing platforms */ |
| /* #define STDERR stderr */ |
| #define STDERR stdout /* for DOS */ |
| |
| #ifndef PNGCRUSH_LIBPNG_VER |
| # define PNGCRUSH_LIBPNG_VER PNG_LIBPNG_VER |
| #endif |
| |
| #ifdef PNG_MNG_FEATURES_SUPPORTED |
| # define PNGCRUSH_LOCO |
| #endif |
| |
| #ifndef PNG_UINT_31_MAX |
| #define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) |
| #endif |
| |
| /* These macros were renamed in libpng-1.2.6 */ |
| #ifndef PNG_HANDLE_CHUNK_ALWAYS |
| #define PNG_HANDLE_CHUNK_ALWAYS HANDLE_CHUNK_ALWAYS |
| #define PNG_HANDLE_CHUNK_NEVER HANDLE_CHUNK_NEVER |
| #define PNG_HANDLE_CHUNK_IF_SAFE HANDLE_CHUNK_IF_SAFE |
| #endif |
| |
| #if defined(__DJGPP__) |
| # if ((__DJGPP__ == 2) && (__DJGPP_MINOR__ == 0)) |
| # include <libc/dosio.h> /* for _USE_LFN, djgpp 2.0 only */ |
| # endif |
| # define SLASH "\\" |
| # define DOT "." |
| #else |
| # ifdef __riscos |
| # define SLASH "." |
| # define DOT "/" |
| # else |
| # define SLASH "/" |
| # define DOT "." |
| # endif |
| #endif |
| |
| #ifndef GAS_VERSION |
| # define GAS_VERSION "2.9.5(?)" /* used only in help/usage screen */ |
| #endif |
| |
| #if !defined(__TURBOC__) && !defined(_MSC_VER) && !defined(_MBCS) && \ |
| !defined(__riscos) |
| # include <unistd.h> |
| #endif |
| |
| #ifndef __riscos |
| # include <sys/types.h> |
| # include <sys/stat.h> |
| # include <utime.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <time.h> |
| #include <assert.h> |
| |
| #if defined(_MBCS) || defined(WIN32) || defined(__WIN32__) |
| # include <direct.h> |
| #endif |
| |
| #define DEFAULT_MODE 0 |
| #define DIRECTORY_MODE 1 |
| #define EXTENSION_MODE 2 |
| #define DIREX_MODE 3 |
| #define FOPEN(file, how) fopen(file, how) |
| #define FCLOSE(file) {fclose(file); file=NULL;--number_of_open_files;}; |
| |
| #define P0 if(first_trial && verbose > 0)printf |
| #define P1 if(verbose > 1)printf |
| #define P2 if(verbose > 2)printf |
| |
| #define STRNGIFY_STAGE1(x) #x |
| #define STRNGIFY(x) STRNGIFY_STAGE1(x) |
| |
| #define STR_BUF_SIZE 256 |
| #define MAX_IDAT_SIZE 524288L |
| #define MAX_METHODS 200 |
| #define MAX_METHODSP1 (MAX_METHODS+1) |
| #define DEFAULT_METHODS 10 |
| #define FAKE_PAUSE_STRING "P" |
| |
| #ifdef Z_RLE |
| # define NUM_STRATEGIES 4 |
| #else |
| # define NUM_STRATEGIES 3 |
| #endif |
| |
| #ifdef __TURBOC__ |
| # include <mem.h> |
| #endif |
| |
| #ifndef CLOCKS_PER_SEC |
| # define CLOCKS_PER_SEC 1000 |
| #endif |
| |
| #ifdef __STDC__ |
| # define TIME_T clock_t |
| #else |
| # if CLOCKS_PER_SEC <= 100 |
| # define TIME_T long |
| # else |
| # define TIME_T float |
| # endif |
| #endif |
| |
| struct options_help { |
| int verbosity; /* if verbose >= this value, then print line */ |
| const char *textline; /* static string with newline chopped off */ |
| }; |
| |
| /* input and output filenames */ |
| static PNG_CONST char *progname; |
| static PNG_CONST char *inname = "pngtest" DOT "png"; |
| static PNG_CONST char *outname = "pngout" DOT "png"; |
| static PNG_CONST char *mngname = "mngout" DOT "mng"; |
| static PNG_CONST char *directory_name = "pngcrush" DOT "bak"; |
| static PNG_CONST char *extension = "_C" DOT "png"; |
| |
| static png_uint_32 width, height; |
| static png_uint_32 measured_idat_length; |
| static int found_gAMA = 0; |
| #ifdef PNG_cHRM_SUPPORTED |
| static int found_cHRM = 0; |
| #endif |
| static int found_CgBI = 0; |
| static int found_any_chunk = 0; |
| static int image_is_immutable = 0; |
| static int pngcrush_must_exit = 0; |
| static int all_chunks_are_safe = 0; |
| static int number_of_open_files; |
| static int do_pplt = 0; |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| static png_uint_32 max_rows_at_a_time = 1; |
| static png_uint_32 rows_at_a_time; |
| #endif |
| char pplt_string[1024]; |
| char *ip, *op, *dot; |
| char in_string[STR_BUF_SIZE]; |
| char prog_string[STR_BUF_SIZE]; |
| char out_string[STR_BUF_SIZE]; |
| char in_extension[STR_BUF_SIZE]; |
| static int text_inputs = 0; |
| int text_where[10]; /* 0: no text; 1: before PLTE; 2: after PLTE */ |
| int text_compression[10]; /* -1: uncompressed tEXt; 0: compressed zTXt |
| 1: uncompressed iTXt; 2: compressed iTXt */ |
| char text_text[20480]; /* It would be nice to png_malloc this, but we |
| * don't have a png_ptr yet when we need it. */ |
| char text_keyword[800]; |
| |
| /* PNG_iTXt_SUPPORTED */ |
| char text_lang[800]; |
| char text_lang_key[800]; |
| |
| /* PNG_iCCP_SUPPORTED */ |
| int iccp_length = 0; |
| char *iccp_text; |
| char *iccp_file; |
| char iccp_name[80]; |
| |
| int best; |
| |
| char buffer[256]; |
| |
| /* Set up the "cexcept" Try/Throw/Catch exception handler. */ |
| #include "cexcept.h" |
| define_exception_type(const char *); |
| extern struct exception_context the_exception_context[1]; |
| struct exception_context the_exception_context[1]; |
| png_const_charp msg; |
| |
| static png_uint_32 total_input_length = 0; |
| static png_uint_32 total_output_length = 0; |
| static int pngcrush_mode = DEFAULT_MODE; |
| static int resolution = 0; |
| static int remove_chunks = 0; |
| static int output_color_type; |
| static int output_bit_depth; |
| static int force_output_color_type = 8; |
| static int force_output_bit_depth = 0; |
| static int input_color_type; |
| static int input_bit_depth; |
| static int trial; |
| static int first_trial = 0; |
| static int verbose = 1; |
| static int fix = 0; |
| static int things_have_changed = 0; |
| static int global_things_have_changed = 0; |
| static int default_compression_window = 15; |
| static int force_compression_window = 0; |
| static int compression_mem_level = 9; |
| static int final_method = 0; |
| static int brute_force = 0; |
| static int brute_force_level = 0; |
| static int brute_force_filter = 0; |
| static int brute_force_strategy = 0; |
| static int brute_force_levels[10] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; |
| static int brute_force_filters[6] = { 1, 1, 1, 1, 1, 1 }; |
| #ifdef Z_RLE |
| static int brute_force_strategies[NUM_STRATEGIES] = { 1, 1, 1, 1 }; |
| #else |
| static int brute_force_strategies[NUM_STRATEGIES] = { 1, 1, 1 }; |
| #endif |
| static int method = 10; |
| static int pauses = 0; |
| static int nosave = 0; |
| static int nofilecheck = 0; |
| #ifdef PNGCRUSH_LOCO |
| static int new_mng = 0; |
| #endif |
| static png_bytep row_buf; |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| static png_bytepp row_pointers; |
| #endif |
| static int z_strategy; |
| static int best_of_three; |
| static int methods_specified = 0; |
| static int intent = -1; |
| static int ster_mode = -1; |
| static int new_time_stamp = 1; |
| static int plte_len = -1; |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| static int specified_gamma = 0; |
| static int image_specified_gamma = 0; |
| static int force_specified_gamma = 0; |
| #else |
| static double specified_gamma = 0.0; |
| static double image_specified_gamma = 0; |
| static double force_specified_gamma = 0.0; |
| #endif |
| static int double_gamma = 0; |
| |
| static int names; |
| |
| static int have_trns = 0; |
| static png_uint_16 trns_index = 0; |
| static png_uint_16 trns_red = 0; |
| static png_uint_16 trns_green = 0; |
| static png_uint_16 trns_blue = 0; |
| static png_uint_16 trns_gray = 0; |
| |
| static png_byte trns_array[256]; |
| static png_byte trans_in[256]; |
| static png_uint_16 num_trans_in; |
| |
| static int have_bkgd = 0; |
| static png_uint_16 bkgd_red = 0; |
| static png_uint_16 bkgd_green = 0; |
| static png_uint_16 bkgd_blue = 0; |
| |
| static png_colorp palette; |
| static int num_palette; |
| |
| #ifdef REORDER_PALETTE |
| static png_byte palette_reorder[256]; |
| #endif |
| |
| static png_structp read_ptr, write_ptr, mng_ptr; |
| static png_infop read_info_ptr, write_info_ptr; |
| static png_infop end_info_ptr; |
| static png_infop write_end_info_ptr; |
| static FILE *fpin, *fpout, *mng_out; |
| png_uint_32 measure_idats(FILE * fpin); |
| #ifdef PNGCRUSH_LOCO |
| static int do_loco = 0; |
| static int input_format = 0; /* 0: PNG 1: MNG */ |
| static int output_format = 0; |
| #endif |
| static int do_color_count; |
| static int reduction_ok = 0; |
| #ifdef PNGCRUSH_COUNT_COLORS |
| int count_colors(FILE * fpin); |
| static int num_rgba, reduce_to_gray, it_is_opaque; |
| #endif |
| png_uint_32 png_measure_idat(png_structp png_ptr); |
| |
| static png_uint_32 idat_length[MAX_METHODSP1]; |
| static int filter_type, zlib_level; |
| static png_bytep png_row_filters = NULL; |
| static float t_start, t_stop, t_decode, t_encode, t_misc; |
| |
| static png_uint_32 max_idat_size = MAX_IDAT_SIZE; /* increases the IDAT size */ |
| static png_uint_32 crushed_idat_size = 0x3ffffffL; |
| static int already_crushed = 0; |
| int ia; |
| |
| |
| /* prototypes */ |
| static void png_cexcept_error(png_structp png_ptr, png_const_charp msg); |
| |
| void PNGAPI png_default_read_data(png_structp png_ptr, png_bytep data, |
| png_size_t length); |
| |
| void png_read_transform_info(png_structp png_ptr, png_infop info_ptr); |
| |
| void PNGAPI png_default_write_data(png_structp png_ptr, png_bytep data, |
| png_size_t length); |
| |
| void png_reset_crc(png_structp png_ptr); |
| void png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length); |
| void png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length); |
| int png_crc_error(png_structp png_ptr); |
| int png_crc_finish(png_structp png_ptr, png_uint_32 skip); |
| |
| void png_save_uint_32(png_bytep buf, png_uint_32 i); |
| |
| #ifdef PNG_USER_MEM_SUPPORTED |
| png_voidp png_debug_malloc(png_structp png_ptr, png_uint_32 size); |
| void png_debug_free(png_structp png_ptr, png_voidp ptr); |
| #endif |
| |
| void png_crush_pause(void); |
| |
| #ifdef __riscos |
| static int fileexists(const char *name) |
| static int filesize(const char *name) |
| static int mkdir(const char *name, int ignored) |
| static void setfiletype(const char *name) |
| #endif |
| |
| int keep_unknown_chunk(png_const_charp name, char *argv[]); |
| int keep_chunk(png_const_charp name, char *argv[]); |
| void show_result(void); |
| png_uint_32 measure_idats(FILE * fpin); |
| png_uint_32 png_measure_idat(png_structp png_ptr); |
| |
| #ifdef PNGCRUSH_COUNT_COLORS |
| int count_colors(FILE * fpin); |
| #endif |
| void print_version_info(void); |
| void print_usage(int retval); |
| |
| |
| #if (!defined(PNGCRUSH_H)) |
| /* |
| * ============================================================ |
| * We aren't using the bundled libpng functions, so we must |
| * reproduce the libpng routines that aren't exported by libpng |
| * ============================================================ |
| */ |
| |
| #ifndef PNG_READ_BIG_ENDIAN_SUPPORTED |
| /* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ |
| png_uint_32 /* PRIVATE */ |
| png_get_uint_32(png_bytep buf) |
| { |
| png_uint_32 i = ((png_uint_32)(*buf) << 24) + |
| ((png_uint_32)(*(buf + 1)) << 16) + |
| ((png_uint_32)(*(buf + 2)) << 8) + |
| (png_uint_32)(*(buf + 3)); |
| |
| return (i); |
| } |
| #else |
| # define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) |
| #endif |
| png_uint_32 /* PRIVATE */ |
| png_get_uint_31(png_structp png_ptr, png_bytep buf) |
| { |
| png_uint_32 i = png_get_uint_32(buf); |
| if (i > PNG_UINT_31_MAX) |
| png_error(png_ptr, "PNG unsigned integer out of range.\n"); |
| return (i); |
| } |
| void /* PRIVATE */ |
| png_save_uint_32(png_bytep buf, png_uint_32 i) |
| { |
| buf[0] = (png_byte)((i >> 24) & 0xff); |
| buf[1] = (png_byte)((i >> 16) & 0xff); |
| buf[2] = (png_byte)((i >> 8) & 0xff); |
| buf[3] = (png_byte)(i & 0xff); |
| } |
| |
| /* |
| * Reset the CRC variable to 32 bits of 1's. Care must be taken |
| * in case CRC is > 32 bits to leave the top bits 0. |
| */ |
| void /* PRIVATE */ |
| png_reset_crc(png_structp png_ptr) |
| { |
| png_ptr->crc = crc32(0, Z_NULL, 0); |
| } |
| /* |
| * Calculate the CRC over a section of data. We can only pass as |
| * much data to this routine as the largest single buffer size. We |
| * also check that this data will actually be used before going to the |
| * trouble of calculating it. |
| */ |
| void /* PRIVATE */ |
| png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) |
| { |
| int need_crc = 1; |
| |
| if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ |
| { |
| if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == |
| (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) |
| need_crc = 0; |
| } |
| else /* critical */ |
| { |
| if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) |
| need_crc = 0; |
| } |
| |
| if (need_crc) |
| png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); |
| } |
| |
| /* Read data, and (optionally) run it through the CRC. */ |
| void /* PRIVATE */ |
| png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) |
| { |
| png_default_read_data(png_ptr, buf, length); |
| png_calculate_crc(png_ptr, buf, length); |
| } |
| |
| /* Compare the CRC stored in the PNG file with that calculated by libpng from |
| the data it has read thus far. */ |
| int /* PRIVATE */ |
| png_crc_error(png_structp png_ptr) |
| { |
| png_byte crc_bytes[4]; |
| png_uint_32 crc; |
| int need_crc = 1; |
| |
| if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ |
| { |
| if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == |
| (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) |
| need_crc = 0; |
| } |
| else /* critical */ |
| { |
| if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) |
| need_crc = 0; |
| } |
| |
| png_default_read_data(png_ptr, crc_bytes, 4); |
| |
| if (need_crc) |
| { |
| crc = png_get_uint_32(crc_bytes); |
| return ((int)(crc != png_ptr->crc)); |
| } |
| else |
| return (0); |
| } |
| |
| /* |
| * Optionally skip data and then check the CRC. Depending on whether we |
| * are reading a ancillary or critical chunk, and how the program has set |
| * things up, we may calculate the CRC on the data and print a message. |
| * Returns '1' if there was a CRC error, '0' otherwise. |
| */ |
| int /* PRIVATE */ |
| png_crc_finish(png_structp png_ptr, png_uint_32 skip) |
| { |
| png_size_t i; |
| png_size_t istop = png_ptr->zbuf_size; |
| |
| for (i = (png_size_t)skip; i > istop; i -= istop) |
| { |
| png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); |
| } |
| if (i) |
| { |
| png_crc_read(png_ptr, png_ptr->zbuf, i); |
| } |
| |
| if (png_crc_error(png_ptr)) |
| { |
| if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ |
| !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || |
| (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ |
| (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) |
| { |
| png_chunk_warning(png_ptr, "CRC error"); |
| } |
| else |
| { |
| png_chunk_error(png_ptr, "CRC error"); |
| } |
| return (1); |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * Modify the info structure to reflect the transformations. The |
| * info should be updated so a PNG file could be written with it, |
| * assuming the transformations result in valid PNG data. |
| */ |
| void /* PRIVATE */ |
| png_read_transform_info(png_structp png_ptr, png_infop info_ptr) |
| { |
| png_debug(1, "in png_read_transform_info\n"); |
| #if defined(PNG_READ_EXPAND_SUPPORTED) |
| if (png_ptr->transformations & PNG_EXPAND) |
| { |
| if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) |
| { |
| if (png_ptr->num_trans) |
| info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
| else |
| info_ptr->color_type = PNG_COLOR_TYPE_RGB; |
| info_ptr->bit_depth = 8; |
| info_ptr->num_trans = 0; |
| } |
| else |
| { |
| if (png_ptr->num_trans) |
| info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; |
| if (info_ptr->bit_depth < 8) |
| info_ptr->bit_depth = 8; |
| info_ptr->num_trans = 0; |
| } |
| } |
| #endif |
| |
| #if defined(PNG_READ_BACKGROUND_SUPPORTED) |
| if (png_ptr->transformations & PNG_BACKGROUND) |
| { |
| info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; |
| info_ptr->num_trans = 0; |
| info_ptr->background = png_ptr->background; |
| } |
| #endif |
| |
| #if defined(PNG_READ_GAMMA_SUPPORTED) |
| if (png_ptr->transformations & PNG_GAMMA) |
| { |
| #ifdef PNG_FLOATING_POINT_SUPPORTED |
| info_ptr->gamma = png_ptr->gamma; |
| #endif |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| info_ptr->int_gamma = png_ptr->int_gamma; |
| #endif |
| } |
| #endif |
| |
| #if defined(PNG_READ_16_TO_8_SUPPORTED) |
| if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) |
| info_ptr->bit_depth = 8; |
| #endif |
| |
| #if defined(PNG_READ_DITHER_SUPPORTED) |
| if (png_ptr->transformations & PNG_DITHER) |
| { |
| if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || |
| (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && |
| png_ptr->palette_lookup && info_ptr->bit_depth == 8) |
| { |
| info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; |
| } |
| } |
| #endif |
| |
| #if defined(PNG_READ_PACK_SUPPORTED) |
| if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) |
| info_ptr->bit_depth = 8; |
| #endif |
| |
| #if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) |
| if (png_ptr->transformations & PNG_GRAY_TO_RGB) |
| info_ptr->color_type |= PNG_COLOR_MASK_COLOR; |
| #endif |
| |
| #if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) |
| if (png_ptr->transformations & PNG_RGB_TO_GRAY) |
| info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; |
| #endif |
| |
| if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) |
| info_ptr->channels = 1; |
| else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) |
| info_ptr->channels = 3; |
| else |
| info_ptr->channels = 1; |
| |
| #ifndef PNG_FLAG_ADD_ALPHA |
| #define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ |
| #endif |
| #ifndef PNG_FLAG_STRIP_ALPHA |
| #define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ |
| #endif |
| #ifndef PNG_ADD_ALPHA |
| #define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ |
| #endif |
| |
| #if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) |
| if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) |
| info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; |
| #endif |
| |
| if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) |
| info_ptr->channels++; |
| |
| #if defined(PNG_READ_FILLER_SUPPORTED) |
| /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ |
| if ((png_ptr->transformations & PNG_FILLER) && |
| ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || |
| (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) |
| { |
| info_ptr->channels++; |
| /* if adding a true alpha channel not just filler */ |
| #if !defined(PNG_1_0_X) |
| if (png_ptr->transformations & PNG_ADD_ALPHA) |
| info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; |
| #endif |
| } |
| #endif |
| |
| #if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ |
| defined(PNG_READ_USER_TRANSFORM_SUPPORTED) |
| if(png_ptr->transformations & PNG_USER_TRANSFORM) |
| { |
| if(info_ptr->bit_depth < png_ptr->user_transform_depth) |
| info_ptr->bit_depth = png_ptr->user_transform_depth; |
| if(info_ptr->channels < png_ptr->user_transform_channels) |
| info_ptr->channels = png_ptr->user_transform_channels; |
| } |
| #endif |
| |
| info_ptr->pixel_depth = (png_byte)(info_ptr->channels * |
| info_ptr->bit_depth); |
| |
| #ifndef PNG_ROWBYTES |
| /* Added to libpng-1.2.6 JB */ |
| #define PNG_ROWBYTES(pixel_bits, width) \ |
| ((pixel_bits) >= 8 ? \ |
| ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ |
| (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) |
| #endif |
| info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); |
| |
| #if !defined(PNG_READ_EXPAND_SUPPORTED) |
| if(png_ptr) |
| return; |
| #endif |
| } |
| |
| #if !defined(PNG_NO_STDIO) |
| /* |
| * This is the function that does the actual reading of data. If you are |
| * not reading from a standard C stream, you should create a replacement |
| * read_data function and use it at run time with png_set_read_fn(), rather |
| * than changing the library. |
| */ |
| #ifndef USE_FAR_KEYWORD |
| void PNGAPI |
| png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) |
| { |
| png_size_t check; |
| |
| /* |
| * fread() returns 0 on error, so it is OK to store this in a png_size_t |
| * instead of an int, which is what fread() actually returns. |
| */ |
| #if defined(_WIN32_WCE) |
| if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) |
| check = 0; |
| #else |
| check = (png_size_t)fread(data, (png_size_t)1, length, |
| (png_FILE_p)png_ptr->io_ptr); |
| #endif |
| |
| if (check != length) |
| png_error(png_ptr, "Read Error"); |
| } |
| #else |
| /* |
| * This is the model-independent version. Since the standard I/O library |
| * can't handle far buffers in the medium and small models, we have to copy |
| * the data. |
| */ |
| |
| #define NEAR_BUF_SIZE 1024 |
| #define MIN(a,b) (a <= b ? a : b) |
| |
| static void /* PRIVATE */ |
| png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) |
| { |
| int check; |
| png_byte *n_data; |
| png_FILE_p io_ptr; |
| |
| /* Check if data really is near. If so, use usual code. */ |
| n_data = (png_byte *)CVT_PTR_NOCHECK(data); |
| io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); |
| if ((png_bytep)n_data == data) |
| { |
| #if defined(_WIN32_WCE) |
| if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) |
| check = 0; |
| #else |
| check = fread(n_data, 1, length, io_ptr); |
| #endif |
| } |
| else |
| { |
| png_byte buf[NEAR_BUF_SIZE]; |
| png_size_t read, remaining, err; |
| check = 0; |
| remaining = length; |
| do |
| { |
| read = MIN(NEAR_BUF_SIZE, remaining); |
| #if defined(_WIN32_WCE) |
| if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) |
| err = 0; |
| #else |
| err = fread(buf, (png_size_t)1, read, io_ptr); |
| #endif |
| png_memcpy(data, buf, read); /* copy far buffer to near buffer */ |
| if(err != read) |
| break; |
| else |
| check += err; |
| data += read; |
| remaining -= read; |
| } |
| while (remaining != 0); |
| } |
| if ((png_uint_32)check != (png_uint_32)length) |
| png_error(png_ptr, "read Error"); |
| } |
| #endif |
| #endif |
| #if !defined(PNG_NO_STDIO) |
| /* |
| * This is the function that does the actual writing of data. If you are |
| * not writing to a standard C stream, you should create a replacement |
| * write_data function and use it at run time with png_set_write_fn(), rather |
| * than changing the library. |
| */ |
| #ifndef USE_FAR_KEYWORD |
| void PNGAPI |
| png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) |
| { |
| png_uint_32 check; |
| |
| #if defined(_WIN32_WCE) |
| if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) |
| check = 0; |
| #else |
| check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); |
| #endif |
| if (check != length) |
| png_error(png_ptr, "Write Error"); |
| } |
| #else |
| /* |
| * This is the model-independent version. Since the standard I/O library |
| * can't handle far buffers in the medium and small models, we have to copy |
| * the data. |
| */ |
| |
| #define NEAR_BUF_SIZE 1024 |
| #define MIN(a,b) (a <= b ? a : b) |
| |
| void PNGAPI |
| png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) |
| { |
| png_uint_32 check; |
| png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ |
| png_FILE_p io_ptr; |
| |
| /* Check if data really is near. If so, use usual code. */ |
| near_data = (png_byte *)CVT_PTR_NOCHECK(data); |
| io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); |
| if ((png_bytep)near_data == data) |
| { |
| #if defined(_WIN32_WCE) |
| if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) |
| check = 0; |
| #else |
| check = fwrite(near_data, 1, length, io_ptr); |
| #endif |
| } |
| else |
| { |
| png_byte buf[NEAR_BUF_SIZE]; |
| png_size_t written, remaining, err; |
| check = 0; |
| remaining = length; |
| do |
| { |
| written = MIN(NEAR_BUF_SIZE, remaining); |
| png_memcpy(buf, data, written); /* copy far buffer to near buffer */ |
| #if defined(_WIN32_WCE) |
| if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) |
| err = 0; |
| #else |
| err = fwrite(buf, 1, written, io_ptr); |
| #endif |
| if (err != written) |
| break; |
| else |
| check += err; |
| data += written; |
| remaining -= written; |
| } |
| while (remaining != 0); |
| } |
| if (check != length) |
| png_error(png_ptr, "Write Error"); |
| } |
| |
| #endif |
| #endif |
| |
| #endif /* !defined(PNGCRUSH_H) */ |
| |
| |
| |
| /* cexcept interface */ |
| |
| static void png_cexcept_error(png_structp png_ptr, png_const_charp err_msg) |
| { |
| if (png_ptr); |
| #if (defined(PNGCRUSH_H)) |
| if (!strcmp(err_msg, "Too many IDAT's found")) { |
| #ifndef PNG_NO_CONSOLE_IO |
| fprintf(stderr, "\nIn %s, correcting ", inname); |
| #else |
| png_warning(png_ptr, err_msg); |
| #endif |
| } else |
| #endif /* defined(PNGCRUSH_H) */ |
| { |
| Throw err_msg; |
| } |
| } |
| |
| |
| |
| |
| /* START of code to validate memory allocation and deallocation */ |
| #ifdef PNG_USER_MEM_SUPPORTED |
| |
| /* |
| * Allocate memory. For reasonable files, size should never exceed |
| * 64K. However, zlib may allocate more then 64K if you don't tell |
| * it not to. See zconf.h and png.h for more information. zlib does |
| * need to allocate exactly 64K, so whatever you call here must |
| * have the ability to do that. |
| * |
| * This piece of code can be compiled to validate max 64K allocations |
| * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. |
| */ |
| typedef struct memory_information { |
| png_uint_32 size; |
| png_voidp pointer; |
| struct memory_information FAR *next; |
| } memory_information; |
| typedef memory_information FAR *memory_infop; |
| |
| static memory_infop pinformation = NULL; |
| static int current_allocation = 0; |
| static int maximum_allocation = 0; |
| |
| |
| |
| |
| png_voidp png_debug_malloc(png_structp png_ptr, png_uint_32 size) |
| { |
| |
| /* |
| * png_malloc has already tested for NULL; png_create_struct calls |
| * png_debug_malloc directly (with png_ptr == NULL prior to libpng-1.2.0 |
| * which is OK since we are not using a user mem_ptr) |
| */ |
| |
| if (size == 0) |
| return (png_voidp) (NULL); |
| |
| /* |
| * This calls the library allocator twice, once to get the requested |
| * buffer and once to get a new free list entry. |
| */ |
| { |
| memory_infop pinfo = (memory_infop)png_malloc_default(png_ptr, |
| sizeof *pinfo); |
| pinfo->size = size; |
| current_allocation += size; |
| if (current_allocation > maximum_allocation) |
| maximum_allocation = current_allocation; |
| pinfo->pointer = png_malloc_default(png_ptr, size); |
| pinfo->next = pinformation; |
| pinformation = pinfo; |
| /* Make sure the caller isn't assuming zeroed memory. */ |
| png_memset(pinfo->pointer, 0xdd, pinfo->size); |
| if (verbose > 2) |
| fprintf(STDERR, "Pointer %lux allocated %lu bytes\n", |
| (unsigned long) pinfo->pointer, (unsigned long)size); |
| return (png_voidp) (pinfo->pointer); |
| } |
| } |
| |
| |
| |
| |
| /* Free a pointer. It is removed from the list at the same time. */ |
| void png_debug_free(png_structp png_ptr, png_voidp ptr) |
| { |
| if (png_ptr == NULL) |
| fprintf(STDERR, "NULL pointer to png_debug_free.\n"); |
| if (ptr == 0) { |
| #if 0 /* This happens all the time. */ |
| fprintf(STDERR, "WARNING: freeing NULL pointer\n"); |
| #endif |
| return; |
| } |
| |
| /* Unlink the element from the list. */ |
| { |
| memory_infop FAR *ppinfo = &pinformation; |
| for (;;) { |
| memory_infop pinfo = *ppinfo; |
| if (pinfo->pointer == ptr) { |
| *ppinfo = pinfo->next; |
| current_allocation -= pinfo->size; |
| if (current_allocation < 0) |
| fprintf(STDERR, "Duplicate free of memory\n"); |
| /* We must free the list element too, but first kill |
| the memory that is to be freed. */ |
| memset(ptr, 0x55, pinfo->size); |
| if (verbose > 2) |
| fprintf(STDERR, "Pointer %lux freed %lu bytes\n", |
| (unsigned long) ptr, (unsigned long)pinfo->size); |
| png_free_default(png_ptr, pinfo); |
| break; |
| } |
| if (pinfo->next == NULL) { |
| fprintf(STDERR, "Pointer %lux not found\n", |
| (unsigned long) ptr); |
| break; |
| } |
| ppinfo = &pinfo->next; |
| } |
| } |
| |
| /* Finally free the data. */ |
| png_free_default(png_ptr, ptr); |
| } |
| |
| #endif /* PNG_USER_MEM_SUPPORTED */ |
| /* END of code to test memory allocation/deallocation */ |
| |
| |
| |
| |
| void png_crush_pause(void) |
| { |
| if (pauses > 0) { |
| char keystroke; |
| fprintf(STDERR, "Press [ENTER] key to continue.\n"); |
| keystroke = (char) getc(stdin); |
| keystroke = keystroke; /* stifle compiler warning */ |
| } |
| } |
| |
| |
| void png_skip_chunk(png_structp png_ptr) |
| { |
| png_byte buff[4]; |
| int i; |
| unsigned long length; |
| |
| /* read the length field */ |
| png_default_read_data(png_ptr, buff, 4); |
| length=buff[3]+(buff[2]<<8)+(buff[1]<<16)+(buff[0]<<24); |
| /* read the chunk name */ |
| png_default_read_data(png_ptr, buff, 4); |
| printf("Skipping %c%c%c%c chunk.\n",buff[0],buff[1], |
| buff[2],buff[3]); |
| /* skip the data */ |
| for (i=0; i<length; i++) |
| png_default_read_data(png_ptr, buff, 1); |
| /* skip the CRC */ |
| png_default_read_data(png_ptr, buff, 4); |
| } |
| |
| #ifndef __riscos |
| # define setfiletype(x) |
| |
| #else /* defined(__riscos) */ |
| # include <kernel.h> |
| |
| /* The riscos/acorn support was contributed by Darren Salt. */ |
| static int fileexists(const char *name) |
| { |
| # ifdef __acorn |
| int ret; |
| return _swix(8, 3 | 1 << 31, 17, name, &ret) ? 0 : ret; |
| # else |
| _kernel_swi_regs r; |
| r.r[0] = 17; |
| r.r[1] = (int) name; |
| return _kernel_swi(8, &r, &r) ? 0 : r.r[0]; |
| # endif |
| } |
| |
| |
| static int filesize(const char *name) |
| { |
| # ifdef __acorn |
| int ret; |
| return _swix(8, 3 | 1 << 27, 17, name, &ret) ? 0 : ret; |
| # else |
| _kernel_swi_regs r; |
| r.r[0] = 17; |
| r.r[1] = (int) name; |
| return _kernel_swi(8, &r, &r) ? 0 : r.r[4]; |
| # endif |
| } |
| |
| |
| static int mkdir(const char *name, int ignored) |
| { |
| # ifdef __acorn |
| _swi(8, 0x13, 8, name, 0); |
| return 0; |
| # else |
| _kernel_swi_regs r; |
| r.r[0] = 8; |
| r.r[1] = (int) name; |
| r.r[4] = r.r[3] = r.r[2] = 0; |
| return (int) _kernel_swi(8 | 1 << 31, &r, &r); |
| # endif |
| } |
| |
| |
| static void setfiletype(const char *name) |
| { |
| # ifdef __acorn |
| _swi(8, 7, 18, name, 0xB60); |
| # else |
| _kernel_swi_regs r; |
| r.r[0] = 18; |
| r.r[1] = (int) name; |
| r.r[2] = 0xB60; |
| _kernel_swi(8 | 1 << 31, &r, &r); |
| # endif |
| } |
| |
| #endif /* ?defined(__riscos) */ |
| |
| |
| |
| |
| /* |
| * GRR: basically boolean; first arg is chunk name-string (e.g., "tIME" or |
| * "alla"); second is always full argv[] command line |
| * - remove_chunks is argv index of *last* -rem arg on command line |
| * (would be more efficient to build table at time of cmdline processing!) |
| * (i.e., build removal_list with names or unique IDs or whatever--skip |
| * excessive string-processing on every single one) |
| * - reprocesses command line _every_ time called, looking for -rem opts... |
| * - just like keep_chunk() except that latter sets things_have_changed |
| * variable and debug stmts say "Removed chunk" (but caller actually does |
| * so, by choosing not to copy chunk to new file) |
| * - for any given chunk name, "name" must either match exact command-line |
| * arg (e.g., -rem fOOb), OR it must match one of the official PNG chunk |
| * names explicitly listed below AND command-line arg either used all- |
| * lowercase form or one of "all[ab]" options |
| */ |
| int keep_unknown_chunk(png_const_charp name, char *argv[]) |
| { |
| int i; |
| if (remove_chunks == 0) |
| return 1; /* no -rem options, so always keeping */ |
| for (i = 1; i <= remove_chunks; i++) { |
| if (!strncmp(argv[i], "-rem", 4)) { |
| int allb = 0; |
| i++; |
| if (!strncmp(argv[i], "all", 3)) { |
| allb++; /* all but gamma, but not doing gamma here */ |
| } |
| if (!strncmp(argv[i], name, 4) /* exact chunk-name match in args */ |
| /* ...or exact match for one of known set, plus args included |
| * either "alla", "allb", or all-lowercase form of "name" */ |
| || (!strncmp(name, "cHRM", 4) |
| && (!strncmp(argv[i], "chrm", 4) || allb)) |
| || (!strncmp(name, "dSIG", 4) |
| && (!strncmp(argv[i], "dsig", 4) || allb)) |
| || (!strncmp(name, "gIFg", 4) |
| && (!strncmp(argv[i], "gifg", 4) || allb)) |
| || (!strncmp(name, "gIFt", 4) |
| && (!strncmp(argv[i], "gift", 4) || allb)) |
| || (!strncmp(name, "gIFx", 4) |
| && (!strncmp(argv[i], "gifx", 4) || allb)) |
| || (!strncmp(name, "hIST", 4) |
| && (!strncmp(argv[i], "hist", 4) || allb)) |
| || (!strncmp(name, "iCCP", 4) |
| && (!strncmp(argv[i], "iccp", 4) || allb)) |
| || (!strncmp(name, "pCAL", 4) |
| && (!strncmp(argv[i], "pcal", 4) || allb)) |
| || (!strncmp(name, "sCAL", 4) |
| && (!strncmp(argv[i], "scal", 4) || allb)) |
| || (!strncmp(name, "sPLT", 4) |
| && (!strncmp(argv[i], "splt", 4) || allb)) |
| || (!strncmp(name, "tIME", 4) |
| && (!strncmp(argv[i], "time", 4) || allb))) |
| { |
| return 0; |
| } |
| } |
| } |
| return 1; |
| } |
| |
| |
| |
| |
| int keep_chunk(png_const_charp name, char *argv[]) |
| { |
| int i; |
| if (verbose > 2 && first_trial) |
| fprintf(STDERR, " Read the %s chunk.\n", name); |
| if (remove_chunks == 0) |
| return 1; |
| if (verbose > 1 && first_trial) |
| fprintf(STDERR, " Check for removal of the %s chunk.\n", name); |
| for (i = 1; i <= remove_chunks; i++) { |
| if (!strncmp(argv[i], "-rem", 4)) { |
| int alla = 0; |
| int allb = 0; |
| int allt = 0; |
| i++; |
| if (!strncmp(argv[i], "all", 3)) { |
| allt++; /* all forms of text chunk are ancillary */ |
| allb++; /* all ancillaries but gamma... */ |
| if (!strncmp(argv[i], "alla", 4)) |
| alla++; /* ...no, all ancillaries, period */ |
| } else if (!strncmp(argv[i], "text", 4)) |
| allt++; /* all forms of text chunk */ |
| if (!strncmp(argv[i], name, 4) /* exact chunk-name match in args |
| * ...or exact match for one of known set, plus args included |
| * either "alla", "allb", or all-lowercase form of "name": */ |
| || (!strncmp(name, "PLTE", 4) |
| && (!strncmp(argv[i], "plte", 4) )) |
| || (!strncmp(name, "bKGD", 4) |
| && (!strncmp(argv[i], "bkgd", 4) || allb)) |
| || (!strncmp(name, "cHRM", 4) |
| && (!strncmp(argv[i], "chrm", 4) || allb)) |
| || (!strncmp(name, "dSIG", 4) |
| && (!strncmp(argv[i], "dsig", 4) || allb)) |
| || (!strncmp(name, "gAMA", 4) |
| && (!strncmp(argv[i], "gama", 4) || alla)) |
| || (!strncmp(name, "gIFg", 4) |
| && (!strncmp(argv[i], "gifg", 4) || allb)) |
| || (!strncmp(name, "gIFt", 4) |
| && (!strncmp(argv[i], "gift", 4) || allb)) |
| || (!strncmp(name, "gIFx", 4) |
| && (!strncmp(argv[i], "gifx", 4) || allb)) |
| || (!strncmp(name, "hIST", 4) |
| && (!strncmp(argv[i], "hist", 4) || allb)) |
| || (!strncmp(name, "iCCP", 4) |
| && (!strncmp(argv[i], "iccp", 4) || allb)) |
| || (!strncmp(name, "iTXt", 4) |
| && (!strncmp(argv[i], "itxt", 4) || allt)) |
| || (!strncmp(name, "oFFs", 4) |
| && (!strncmp(argv[i], "offs", 4) || allb)) |
| || (!strncmp(name, "pHYs", 4) |
| && (!strncmp(argv[i], "phys", 4) || allb)) |
| || (!strncmp(name, "pCAL", 4) |
| && (!strncmp(argv[i], "pcal", 4) || allb)) |
| || (!strncmp(name, "sBIT", 4) |
| && (!strncmp(argv[i], "sbit", 4) || allb)) |
| || (!strncmp(name, "sCAL", 4) |
| && (!strncmp(argv[i], "scal", 4) || allb)) |
| || (!strncmp(name, "sRGB", 4) |
| && (!strncmp(argv[i], "srgb", 4) || allb)) |
| || (!strncmp(name, "sTER", 4) |
| && (!strncmp(argv[i], "ster", 4) || allb)) |
| || (!strncmp(name, "sPLT", 4) |
| && (!strncmp(argv[i], "splt", 4) || allb)) |
| || (!strncmp(name, "tEXt", 4) |
| && ( allt)) |
| || (!strncmp(name, "tIME", 4) |
| && (!strncmp(argv[i], "time", 4) || allb)) |
| || (!strncmp(name, "tRNS", 4) |
| && (!strncmp(argv[i], "trns", 4) )) |
| || (!strncmp(name, "zTXt", 4) |
| && (!strncmp(argv[i], "ztxt", 4) || allt)) ) |
| { |
| things_have_changed = 1; |
| /* (caller actually does the removal--by failing to create |
| * copy) */ |
| if (verbose > 0 && first_trial) |
| fprintf(STDERR, " Removed the %s chunk.\n", name); |
| return 0; |
| } |
| } |
| } |
| if (verbose > 1 && first_trial) |
| fprintf(STDERR, " Preserving the %s chunk.\n", name); |
| return 1; |
| } |
| |
| |
| |
| |
| void show_result(void) |
| { |
| if (total_output_length) { |
| if (total_input_length == total_output_length) |
| fprintf(STDERR, " Overall result: no change\n"); |
| else if (total_input_length > total_output_length) |
| fprintf(STDERR, |
| " Overall result: %4.2f%% reduction, %lu bytes\n", |
| (100.0 - |
| (100.0 * total_output_length) / total_input_length), |
| (unsigned long)(total_input_length-total_output_length)); |
| else |
| fprintf(STDERR, |
| " Overall result: %4.2f%% increase, %lu bytes\n", |
| -(100.0 - |
| (100.0 * total_output_length) / total_input_length), |
| (unsigned long)(total_output_length - total_input_length)); |
| } |
| t_stop = (TIME_T) clock(); |
| t_misc += (t_stop - t_start); |
| if (t_stop < t_start) { |
| t_misc += PNG_UINT_31_MAX; |
| if (t_stop < 0) |
| t_misc += PNG_UINT_31_MAX; |
| } |
| t_start = t_stop; |
| fprintf(STDERR, " CPU time used = %.3f seconds", |
| (t_misc + t_decode + t_encode) / (float) CLOCKS_PER_SEC); |
| fprintf(STDERR, " (decoding %.3f,\n", |
| t_decode / (float) CLOCKS_PER_SEC); |
| fprintf(STDERR, " encoding %.3f,", |
| t_encode / (float) CLOCKS_PER_SEC); |
| fprintf(STDERR, " other %.3f seconds)\n\n", |
| t_misc / (float) CLOCKS_PER_SEC); |
| #ifdef PNG_USER_MEM_SUPPORTED |
| if (current_allocation) { |
| memory_infop pinfo = pinformation; |
| fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", |
| current_allocation); |
| while (pinfo != NULL) { |
| fprintf(STDERR, " %8lu bytes at %lux\n", (unsigned long)pinfo->size, |
| (unsigned long) pinfo->pointer); |
| free(pinfo->pointer); |
| pinfo = pinfo->next; |
| } |
| } |
| #endif |
| } |
| |
| |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| png_uint_32 y; |
| int bit_depth, color_type; |
| int num_pass, pass; |
| int num_methods; |
| int try_method[MAX_METHODSP1]; |
| int fm[MAX_METHODSP1]; |
| int lv[MAX_METHODSP1]; |
| int zs[MAX_METHODSP1]; |
| int lev, strat, filt; |
| #ifdef PNG_gAMA_SUPPORTED |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| png_fixed_point file_gamma = 0; |
| #else |
| double file_gamma = 0.; |
| #endif |
| #endif |
| char *cp; |
| int i; |
| row_buf = (png_bytep) NULL; |
| number_of_open_files = 0; |
| #ifdef PNGCRUSH_COUNT_COLORS |
| reduce_to_gray = 0; |
| it_is_opaque = 0; |
| #else |
| do_color_count = 0; |
| do_color_count = do_color_count; /* silence compiler warning */ |
| #endif |
| |
| if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) { |
| fprintf(STDERR, |
| "Warning: versions are different between png.h and png.c\n"); |
| fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); |
| fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); |
| } |
| |
| t_start = (TIME_T) clock(); |
| |
| strncpy(prog_string, argv[0], STR_BUF_SIZE); |
| prog_string[STR_BUF_SIZE-1] = '\0'; |
| progname = prog_string; |
| for (i = 0, cp = prog_string; *cp != '\0'; i++, cp++) { |
| #ifdef __riscos |
| if (*cp == '.' || *cp == ':') |
| progname = ++cp; |
| #else |
| if (*cp == '\\' || *cp == '/') |
| progname = ++cp; |
| if (*cp == '.') |
| *cp = '\0'; |
| #endif |
| } |
| |
| /* |
| * Definition of methods ("canonical list" is methods 11 and up) |
| */ |
| for (i = 0; i < MAX_METHODS; i++) { |
| try_method[i] = 1; |
| fm[i] = 5; lv[i] = 9; zs[i] = 1; /* default: method 124 */ |
| } |
| |
| fm[1] = 0; lv[1] = 4; zs[1] = 0; /* method 1 == method 53 */ |
| fm[2] = 1; lv[2] = 4; zs[2] = 0; /* method 2 == method 54 */ |
| lv[3] = 4; /* method 3 == method 64 */ |
| fm[4] = 0; /* method 4 == method 119 */ |
| fm[5] = 1; zs[5] = 0; /* method 5 == method 114 */ |
| zs[6] = 0; /* method 6 == method 118 */ |
| fm[7] = 0; zs[7] = 0; /* method 7 == method 113 */ |
| fm[8] = 1; /* method 8 == method 120 */ |
| lv[9] = 2; zs[9] = 2; /* method 9 == method 16 */ |
| /* method 10 == method 124 */ |
| |
| /* methods 11 through 16 |
| * |
| * [strategy 2 (Z_HUFFMAN_ONLY) is independent of zlib compression level] |
| */ |
| method = 11; |
| for (filt = 0; filt <= 5; filt++) { |
| fm[method] = filt; |
| lv[method] = 2; |
| zs[method] = 2; |
| method++; |
| } |
| |
| /* |
| * methods 17 through 124 (9*2*6 = 108) |
| */ |
| for (lev = 1; lev <= 9; lev++) { |
| for (strat = 0; strat <= 1; strat++) { |
| for (filt = 0; filt <= 5; filt++) { |
| fm[method] = filt; |
| lv[method] = lev; |
| zs[method] = strat; |
| method++; |
| } |
| } |
| } |
| |
| #ifdef Z_RLE |
| /* methods 125 through 136 |
| * |
| * [strategy 3 (Z_RLE) is mostly independent of level; 1-3 and 4-9 are |
| * same] |
| */ |
| for (filt = 0; filt <= 5; filt++) { |
| fm[method] = filt; |
| lv[method] = 1; |
| zs[method] = 3; |
| method++; |
| } |
| for (filt = 0; filt <= 5; filt++) { |
| fm[method] = filt; |
| lv[method] = 4; |
| zs[method] = 3; |
| method++; |
| } |
| #endif /* Z_RLE */ |
| |
| num_methods = method; /* GRR */ |
| |
| |
| #define BUMP_I i++;if(i >= argc) {printf("insufficient parameters\n");exit(1);} |
| names = 1; |
| |
| /* ===================================================================== */ |
| /* FIXME: move args-processing block into separate function (470 lines) */ |
| for (i = 1; i < argc; i++) { |
| if (!strncmp(argv[i], "-", 1)) |
| names++; |
| |
| |
| /* GRR: start of giant else-if block */ |
| if (!strncmp(argv[i], "-fast", 5)) { |
| /* try two fast filters */ |
| methods_specified = 1; |
| try_method[16] = 0; |
| try_method[53] = 0; |
| } else if (!strncmp(argv[i], "-huffman", 8)) { |
| /* try all filters with huffman */ |
| methods_specified = 1; |
| for (method = 11; method <= 16; method++) { |
| try_method[method] = 0; |
| } |
| #ifdef Z_RLE |
| } else if (!strncmp(argv[i], "-rle", 4)) { |
| /* try all filters with RLE */ |
| methods_specified = 1; |
| for (method = 125; method <= 136; method++) { |
| try_method[method] = 0; |
| } |
| #endif |
| } |
| |
| else if (!strncmp(argv[i], "-already", 8)) { |
| names++; |
| BUMP_I; |
| crushed_idat_size = (png_uint_32) atoi(argv[i]); |
| } |
| |
| else if (!strncmp(argv[i], "-bkgd", 5) || |
| !strncmp(argv[i], "-bKGD", 5)) |
| { |
| names += 3; |
| have_bkgd = 1; |
| bkgd_red = (png_uint_16) atoi(argv[++i]); |
| bkgd_green = (png_uint_16) atoi(argv[++i]); |
| bkgd_blue = (png_uint_16) atoi(argv[++i]); |
| } |
| |
| else if (!strncmp(argv[i], "-brute", 6)) |
| /* brute force: try everything */ |
| { |
| methods_specified = 1; |
| brute_force++; |
| for (method = 11; method < num_methods; method++) |
| try_method[method] = 0; |
| if (brute_force_filter == 0) |
| for (filt = 0; filt < 6; filt++) |
| brute_force_filters[filt] = 0; |
| if (brute_force_level == 0) |
| for (lev = 0; lev < 10; lev++) |
| brute_force_levels[lev] = 0; |
| if (brute_force_strategy == 0) |
| for (strat = 0; strat < NUM_STRATEGIES; strat++) |
| brute_force_strategies[strat] = 0; |
| } else if (!strncmp(argv[i], "-bit_depth", 10)) { |
| names++; |
| BUMP_I; |
| force_output_bit_depth = atoi(argv[i]); |
| } else if (!strncmp(argv[i], "-cc", 3)) { |
| do_color_count = 1; |
| } else if (!strncmp(argv[i], "-no_cc", 6)) { |
| do_color_count = 0; |
| } else if (!strncmp(argv[i], "-c", 2)) { |
| names++; |
| BUMP_I; |
| force_output_color_type = atoi(argv[i]); |
| } |
| #ifdef PNG_gAMA_SUPPORTED |
| else if (!strncmp(argv[i], "-dou", 4)) { |
| double_gamma++; |
| found_gAMA=1; |
| global_things_have_changed = 1; |
| } |
| #endif |
| else if (!strncmp(argv[i], "-d", 2)) { |
| BUMP_I; |
| if (pngcrush_mode == EXTENSION_MODE) |
| pngcrush_mode = DIREX_MODE; |
| else |
| pngcrush_mode = DIRECTORY_MODE; |
| directory_name = argv[names++]; |
| } else if (!strncmp(argv[i], "-exit", 5)) { |
| pngcrush_must_exit = 1; |
| } else if (!strncmp(argv[i], "-e", 2)) { |
| BUMP_I; |
| if (pngcrush_mode == DIRECTORY_MODE) |
| pngcrush_mode = DIREX_MODE; |
| else |
| pngcrush_mode = EXTENSION_MODE; |
| extension = argv[names++]; |
| } else if (!strncmp(argv[i], "-force", 6)) { |
| global_things_have_changed = 1; |
| } else if (!strncmp(argv[i], "-fix", 4)) { |
| fix++; |
| } else if (!strncmp(argv[i], "-f", 2)) { |
| int specified_filter = atoi(argv[++i]); |
| if (specified_filter > 5 || specified_filter < 0) |
| specified_filter = 5; |
| names++; |
| if (brute_force == 0) |
| fm[method] = specified_filter; |
| else { |
| for (filt = 0; filt < 6; filt++) |
| brute_force_filters[filt] = 1; |
| brute_force_filters[specified_filter] = 0; |
| method = 11; |
| for (filt = 0; filt < 6; filt++) { |
| try_method[method] = brute_force_filters[filt] | |
| brute_force_strategies[2]; |
| method++; |
| } |
| for (lev = 1; lev < 10; lev++) { |
| for (strat = 0; strat < 2; strat++) { |
| for (filt = 0; filt < 6; filt++) { |
| try_method[method] = brute_force_levels[lev] | |
| brute_force_filters[filt] | |
| brute_force_strategies[strat]; |
| method++; |
| } |
| } |
| } |
| brute_force_filter++; |
| } |
| } else if (!strncmp(argv[i], "-loco", 5)) { |
| #ifdef PNGCRUSH_LOCO |
| do_loco = 1; |
| #else |
| printf |
| ("Cannot do -loco because libpng was compiled" |
| " without MNG features"); |
| #endif |
| } else if (!strncmp(argv[i], "-l", 2)) { |
| int specified_level = atoi(argv[++i]); |
| if (specified_level > 9 || specified_level < 0) |
| specified_level = 9; |
| names++; |
| if (brute_force == 0) |
| lv[method] = specified_level; |
| else { |
| if (brute_force_level == 0) |
| for (lev = 0; lev < 10; lev++) |
| brute_force_levels[lev] = 1; |
| brute_force_levels[specified_level] = 0; |
| method = 11; |
| for (filt = 0; filt < 6; filt++) { |
| lv[method] = specified_level; |
| method++; |
| } |
| for (lev = 1; lev < 10; lev++) { |
| for (strat = 0; strat < 2; strat++) { |
| for (filt = 0; filt < 6; filt++) { |
| try_method[method] = brute_force_levels[lev] | |
| brute_force_filters[filt] | |
| brute_force_strategies[strat]; |
| method++; |
| } |
| } |
| } |
| brute_force_level++; |
| } |
| } |
| #ifdef PNG_gAMA_SUPPORTED |
| else if (!strncmp(argv[i], "-g", 2)) { |
| names++; |
| BUMP_I; |
| found_gAMA=1; |
| if (intent < 0) { |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| int c; |
| char number[16]; |
| char *n = number; |
| int nzeroes = -1; |
| int length = strlen(argv[i]); |
| for (c = 0; c < length; c++) { |
| if (*(argv[i] + c) == '.') { |
| nzeroes = 5; |
| } else if (nzeroes) { |
| *n++ = *(argv[i] + c); |
| nzeroes--; |
| } |
| } |
| for (c = 0; c < nzeroes; c++) |
| *n++ = '0'; |
| *n = '\0'; |
| specified_gamma = atoi(number); |
| #else |
| specified_gamma = atof(argv[i]); |
| #endif |
| } |
| } |
| #endif /* PNG_gAMA_SUPPORTED */ |
| else if (!strncmp(argv[i], "-h", 2)) { |
| ++verbose; |
| print_version_info(); |
| print_usage(0); /* this exits */ |
| } |
| #ifdef PNG_iCCP_SUPPORTED |
| else if (!strncmp(argv[i], "-iccp", 5)) { |
| FILE *iccp_fn; |
| if (iccp_length) |
| free(iccp_text); |
| iccp_length = atoi(argv[++i]); |
| names += 3; |
| strcpy(iccp_name, argv[++i]); |
| iccp_file = argv[++i]; |
| if ((iccp_fn = FOPEN(iccp_file, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find file: %s\n", iccp_file); |
| iccp_length = 0; |
| } else { |
| int ic; |
| iccp_text = (char*)malloc(iccp_length); |
| for (ic = 0; ic < iccp_length; ic++) { |
| png_size_t num_in; |
| num_in = fread(buffer, 1, 1, iccp_fn); |
| if (!num_in) |
| break; |
| iccp_text[ic] = buffer[0]; |
| } |
| } |
| } |
| #endif |
| else if (!strncmp(argv[i], "-keep", 5)) { |
| names++; |
| BUMP_I; |
| if (!strncmp(argv[i], "dSIG", 4) |
| && (!strncmp(argv[i], "dsig", 4) )) |
| found_any_chunk=1; |
| } |
| |
| else if (!strncmp(argv[i], "-max", 4)) { |
| names++; |
| BUMP_I; |
| max_idat_size = (png_uint_32) atoi(argv[i]); |
| if (max_idat_size == 0 || max_idat_size > PNG_UINT_31_MAX) |
| max_idat_size = PNG_ZBUF_SIZE; |
| #ifdef PNGCRUSH_LOCO |
| } else if (!strncmp(argv[i], "-mng", 4)) { |
| names++; |
| BUMP_I; |
| mngname = argv[i]; |
| new_mng++; |
| #endif |
| } else if (!strncmp(argv[i], "-m", 2)) { |
| names++; |
| BUMP_I; |
| method = atoi(argv[i]); |
| methods_specified = 1; |
| brute_force = 0; |
| try_method[method] = 0; |
| } else if (!strncmp(argv[i], "-nofilecheck", 5)) { |
| nofilecheck++; |
| } else if (!strncmp(argv[i], "-nosave", 2)) { |
| /* no save; I just use this for testing decode speed */ |
| /* also to avoid saving if a CgBI chunk was found */ |
| nosave++; |
| pngcrush_mode = EXTENSION_MODE; |
| } else if (!strncmp(argv[i], "-oldtimestamp", 5)) { |
| new_time_stamp=0; |
| } else if (!strncmp(argv[i], "-plte_len", 9)) { |
| names++; |
| BUMP_I; |
| plte_len = atoi(argv[i]); |
| } else if (!strncmp(argv[i], "-pplt", 3)) { |
| names++; |
| do_pplt++; |
| BUMP_I; |
| strcpy(pplt_string, argv[i]); |
| global_things_have_changed = 1; |
| } else if (!strncmp(argv[i], "-p", 2)) { |
| pauses++; |
| } else if (!strncmp(argv[i], "-q", 2)) { |
| verbose = 0; |
| } else if (!strncmp(argv[i], "-reduce", 7)) { |
| reduction_ok++; |
| do_color_count = 1; |
| } |
| #ifdef PNG_gAMA_SUPPORTED |
| else if (!strncmp(argv[i], "-replace_gamma", 4)) { |
| names++; |
| BUMP_I; |
| found_gAMA=1; |
| { |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| int c; |
| char number[16]; |
| char *n = number; |
| int nzeroes = -1; |
| int length = strlen(argv[i]); |
| for (c = 0; c < length; c++) { |
| if (*(argv[i] + c) == '.') { |
| nzeroes = 5; |
| } else if (nzeroes) { |
| *n++ = *(argv[i] + c); |
| nzeroes--; |
| } |
| } |
| for (c = 0; c < nzeroes; c++) |
| *n++ = '0'; |
| *n = '\0'; |
| force_specified_gamma = atoi(number); |
| #else |
| force_specified_gamma = atof(argv[i]); |
| #endif |
| } |
| global_things_have_changed = 1; |
| } |
| #endif |
| #ifdef PNG_pHYs_SUPPORTED |
| else if (!strncmp(argv[i], "-res", 4)) { |
| names++; |
| BUMP_I; |
| resolution = atoi(argv[i]); |
| global_things_have_changed = 1; |
| } |
| #endif |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| else if (!strncmp(argv[i], "-rows", 5)) { |
| names++; |
| BUMP_I; |
| max_rows_at_a_time = atoi(argv[i]); |
| } |
| #endif |
| else if (!strncmp(argv[i], "-r", 2)) { |
| remove_chunks = i; |
| names++; |
| BUMP_I; |
| if (!strncmp(argv[i], "dSIG", 4) |
| && (!strncmp(argv[i], "dsig", 4))) |
| image_is_immutable=0; |
| } else if (!strncmp(argv[i], "-save", 5)) { |
| all_chunks_are_safe++; |
| } else if (!strncmp(argv[i], "-srgb", 5) || |
| !strncmp(argv[i], "-sRGB", 5)) { |
| #ifdef PNG_gAMA_SUPPORTED |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| specified_gamma = 45455L; |
| #else |
| specified_gamma = 0.45455; |
| #endif |
| #endif |
| intent = 0; |
| BUMP_I; |
| if (!strncmp(argv[i], "0", 1) || |
| !strncmp(argv[i], "1", 1) || |
| !strncmp(argv[i], "2", 1) || !strncmp(argv[i], "3", 1)) { |
| names++; |
| intent = (int) atoi(argv[i]); |
| global_things_have_changed = 1; |
| } else |
| i--; |
| } else if (!strncmp(argv[i], "-ster", 5) || |
| !strncmp(argv[i], "-sTER", 5)) { |
| BUMP_I; |
| ster_mode = -1; |
| if (!strncmp(argv[i], "0", 1) || |
| !strncmp(argv[i], "1", 1)) { |
| names++; |
| ster_mode = (int) atoi(argv[i]); |
| global_things_have_changed = 1; |
| } else |
| i--; |
| } else if (!strncmp(argv[i], "-s", 2)) { |
| verbose = 0; |
| } else if (!strncmp(argv[i], "-text", 5) |
| || !strncmp(argv[i], "-tEXt", 5) || |
| #ifdef PNG_iTXt_SUPPORTED |
| !strncmp(argv[i], "-itxt", 5) |
| || !strncmp(argv[i], "-iTXt", 5) |
| || !strncmp(argv[i], "-zitxt", 6) |
| || !strncmp(argv[i], "-ziTXt", 6) || |
| #endif |
| !strncmp(argv[i], "-ztxt", 5) |
| || !strncmp(argv[i], "-zTXt", 5)) |
| { |
| i += 2; |
| BUMP_I; |
| i -= 3; |
| if (strlen(argv[i + 2]) < 180 && strlen(argv[i + 3]) < 2048 && |
| text_inputs < 10) { |
| #ifdef PNG_iTXt_SUPPORTED |
| if (!strncmp(argv[i], "-zi", 3)) { |
| text_compression[text_inputs] = |
| PNG_ITXT_COMPRESSION_zTXt; |
| /* names += 2; */ |
| } else |
| #endif |
| if (!strncmp(argv[i], "-z", 2)) |
| text_compression[text_inputs] = |
| PNG_TEXT_COMPRESSION_zTXt; |
| else if (!strncmp(argv[i], "-t", 2)) |
| text_compression[text_inputs] = |
| PNG_TEXT_COMPRESSION_NONE; |
| #ifdef PNG_iTXt_SUPPORTED |
| else { |
| text_compression[text_inputs] = |
| PNG_ITXT_COMPRESSION_NONE; |
| /* names += 2; */ |
| } |
| #endif |
| names += 3; |
| if (!strncmp(argv[++i], "b", 1)) |
| text_where[text_inputs] = 1; |
| if (!strncmp(argv[i], "a", 1)) |
| text_where[text_inputs] = 2; |
| strcpy(&text_keyword[text_inputs * 80], argv[++i]); |
| #ifdef PNG_iTXt_SUPPORTED |
| if (text_compression[text_inputs] <= 0) { |
| text_lang[text_inputs * 80] = '\0'; |
| text_lang_key[text_inputs * 80] = '\0'; |
| } else { |
| i += 2; |
| BUMP_I; |
| i -= 3; |
| names += 2; |
| strcpy(&text_lang[text_inputs * 80], argv[++i]); |
| /* libpng-1.0.5j and later */ |
| strcpy(&text_lang_key[text_inputs * 80], argv[++i]); |
| } |
| #endif |
| strcpy(&text_text[text_inputs * 2048], argv[++i]); |
| text_inputs++; |
| } else { |
| if (text_inputs > 9) |
| fprintf(STDERR, |
| "too many text/zTXt inputs; only 10 allowed\n"); |
| else |
| fprintf(STDERR, |
| "keyword exceeds 79 characters or text" |
| " exceeds 2047 characters\n"); |
| i += 3; |
| names += 3; |
| #ifdef PNG_iTXt_SUPPORTED |
| if (!strncmp(argv[i], "-i", 2) |
| || !strncmp(argv[i], "-zi", 3)) { |
| i++; |
| BUMP_I; |
| names += 2; |
| } |
| #endif |
| } |
| } |
| else if (!strncmp(argv[i], "-time_stamp", 5) || /* legacy */ |
| !strncmp(argv[i], "-newtimestamp", 5)) |
| new_time_stamp=1; |
| |
| #ifdef PNG_tRNS_SUPPORTED |
| else if (!strncmp(argv[i], "-trns_a", 7) || |
| !strncmp(argv[i], "-tRNS_a", 7)) { |
| num_trans_in = (png_uint_16) atoi(argv[++i]); |
| trns_index=num_trans_in-1; |
| have_trns = 1; |
| for (ia = 0; ia < num_trans_in; ia++) |
| trans_in[ia] = (png_byte) atoi(argv[++i]); |
| names += 1 + num_trans_in; |
| } else if (!strncmp(argv[i], "-trns", 5) || |
| !strncmp(argv[i], "-tRNS", 5)) { |
| names += 5; |
| have_trns = 1; |
| trns_index = (png_uint_16) atoi(argv[++i]); |
| trns_red = (png_uint_16) atoi(argv[++i]); |
| trns_green = (png_uint_16) atoi(argv[++i]); |
| trns_blue = (png_uint_16) atoi(argv[++i]); |
| trns_gray = (png_uint_16) atoi(argv[++i]); |
| } |
| #endif |
| else if (!strncmp(argv[i], "-version", 8)) { |
| fprintf(STDERR, " pngcrush "); |
| fprintf(STDERR, PNGCRUSH_VERSION); |
| fprintf(STDERR, ", uses libpng "); |
| fprintf(STDERR, PNG_LIBPNG_VER_STRING); |
| fprintf(STDERR, " and zlib "); |
| fprintf(STDERR, ZLIB_VERSION); |
| fprintf(STDERR, "\n Check http://pmt.sf.net/\n"); |
| fprintf(STDERR, " for the most recent version.\n"); |
| verbose = 0; |
| } else if (!strncmp(argv[i], "-v", 2)) { |
| verbose++; |
| } else if (!strncmp(argv[i], "-w", 2)) { |
| default_compression_window = atoi(argv[++i]); |
| force_compression_window++; |
| names++; |
| } else if (!strncmp(argv[i], "-zm", 3)) { |
| compression_mem_level = atoi(argv[++i]); |
| names++; |
| } else if (!strncmp(argv[i], "-z", 2)) { |
| int specified_strategy = atoi(argv[++i]); |
| if (specified_strategy > 2 || specified_strategy < 0) |
| specified_strategy = 0; |
| names++; |
| if (brute_force == 0) |
| zs[method] = specified_strategy; |
| else { |
| if (brute_force_strategy == 0) |
| for (strat = 0; strat < 2; strat++) |
| brute_force_strategies[strat] = 1; |
| brute_force_strategies[specified_strategy] = 0; |
| method = 11; |
| for (filt = 0; filt < 6; filt++) { |
| if (specified_strategy != 2) |
| try_method[method] = 1; |
| method++; |
| } |
| for (lev = 1; lev < 10; lev++) { |
| for (strat = 0; strat < 2; strat++) { |
| for (filt = 0; filt < 6; filt++) { |
| try_method[method] = brute_force_levels[lev] | |
| brute_force_filters[filt] | |
| brute_force_strategies[strat]; |
| method++; |
| } |
| } |
| } |
| } |
| brute_force_strategy++; |
| } /* GRR: end of giant if-else block */ |
| } /* end of loop over args ============================================ */ |
| |
| |
| if (verbose > 0) |
| print_version_info(); |
| |
| if (default_compression_window == 32) |
| default_compression_window = 15; |
| else if (default_compression_window == 16) |
| default_compression_window = 14; |
| else if (default_compression_window == 8) |
| default_compression_window = 13; |
| else if (default_compression_window == 4) |
| default_compression_window = 12; |
| else if (default_compression_window == 2) |
| default_compression_window = 11; |
| else if (default_compression_window == 1) |
| default_compression_window = 10; |
| else if (default_compression_window == 512) |
| default_compression_window = 9; |
| /* Use of compression window size 256 is not recommended. */ |
| else if (default_compression_window == 256) |
| default_compression_window = 8; |
| else if (default_compression_window != 15) { |
| fprintf(STDERR, "Invalid window size (%d); using window size=4\n", |
| default_compression_window); |
| default_compression_window = 12; |
| } |
| |
| if (pngcrush_mode == DEFAULT_MODE) { |
| if (argc - names == 2) { |
| inname = argv[names]; |
| outname = argv[names + 1]; |
| } else { |
| if ((argc - names == 1 || nosave)) { |
| inname = argv[names]; |
| } |
| if (verbose && !nosave) { |
| print_usage(1); /* this exits */ |
| } |
| } |
| } |
| |
| for (ia = 0; ia < 256; ia++) |
| trns_array[ia]=255; |
| |
| for (;;) /* loop on input files */ |
| { |
| first_trial = 1; |
| |
| things_have_changed = global_things_have_changed; |
| |
| if (png_row_filters != NULL) { |
| free(png_row_filters); |
| png_row_filters = NULL; |
| } |
| |
| image_specified_gamma = 0; |
| |
| inname = argv[names++]; |
| |
| if (inname == NULL) { |
| if (verbose > 0) |
| show_result(); |
| break; |
| } |
| |
| if (pngcrush_mode == DIRECTORY_MODE || pngcrush_mode == DIREX_MODE) { |
| int inlen, outlen; |
| #ifndef __riscos |
| struct stat stat_buf; |
| if (stat(directory_name, &stat_buf)) |
| #else |
| if (fileexists(directory_name) & 2) |
| #endif |
| { |
| #if defined(_MBCS) || defined(WIN32) || defined(__WIN32__) |
| if (_mkdir(directory_name)) |
| #else |
| if (mkdir(directory_name, 0755)) |
| #endif |
| { |
| fprintf(STDERR, "could not create directory %s\n", |
| directory_name); |
| exit(1); |
| } |
| nofilecheck = 1; |
| } |
| outlen = strlen(directory_name); |
| if (outlen >= STR_BUF_SIZE-1) { |
| fprintf(STDERR, "directory %s is too long for buffer\n", |
| directory_name); |
| exit(1); |
| } |
| strcpy(out_string, directory_name); |
| /*strcpy(out_string+outlen, SLASH); */ |
| out_string[outlen++] = SLASH[0]; /* (assuming SLASH is 1 byte) */ |
| out_string[outlen] = '\0'; |
| |
| inlen = strlen(inname); |
| if (inlen >= STR_BUF_SIZE) { |
| fprintf(STDERR, "filename %s is too long for buffer\n", inname); |
| exit(1); |
| } |
| strcpy(in_string, inname); |
| in_string[inlen] = '\0'; |
| #ifdef __riscos |
| op = strrchr(in_string, '.'); |
| if (!op) |
| op = in_string; |
| else |
| op++; |
| #else |
| op = in_string; |
| ip = in_string + inlen - 1; /* start at last char in string */ |
| while (ip > in_string) { |
| if (*ip == '\\' || *ip == '/') { |
| op = ip + 1; |
| break; |
| } |
| --ip; |
| } |
| #endif |
| |
| if (outlen + (inlen - (op - in_string)) >= STR_BUF_SIZE) { |
| fprintf(STDERR, "full path is too long for buffer\n"); |
| exit(1); |
| } |
| strcpy(out_string+outlen, op); |
| /*outlen += inlen - (op - in_string); */ |
| outname = out_string; |
| } |
| |
| /* |
| * FIXME: need same input-validation fixes (as above) here, too |
| * |
| * FIXME: what was the point of setting in_string and out_string in |
| * DIREX_MODE above if going to do all over again here? |
| */ |
| if (pngcrush_mode == EXTENSION_MODE || pngcrush_mode == DIREX_MODE) { |
| ip = in_string; |
| in_string[0] = '\0'; |
| if (pngcrush_mode == EXTENSION_MODE) |
| strcat(in_string, inname); |
| else |
| strcat(in_string, outname); |
| ip = in_string; |
| op = dot = out_string; |
| while (*ip != '\0') { |
| *op++ = *ip++; |
| #ifdef __riscos |
| if (*ip == '/') |
| dot = op; |
| #else |
| if (*ip == '.') |
| dot = op; |
| #endif |
| } |
| *op = '\0'; |
| |
| if (dot != out_string) |
| *dot = '\0'; |
| |
| in_extension[0] = '\0'; |
| if (dot != out_string) { |
| strcat(in_extension, ++dot); |
| } |
| |
| strcat(out_string, extension); |
| outname = out_string; |
| } |
| |
| |
| if (nosave < 2) { |
| P1( "Opening file %s for length measurement\n", |
| inname); |
| |
| if ((fpin = FOPEN(inname, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find file: %s\n", inname); |
| continue; |
| } |
| number_of_open_files++; |
| |
| already_crushed = 0; |
| |
| #ifdef PNGCRUSH_LOCO |
| if (new_mng) { |
| |
| #ifdef PNG_USER_MEM_SUPPORTED |
| mng_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL, (png_voidp) NULL, |
| (png_malloc_ptr) png_debug_malloc, |
| (png_free_ptr) png_debug_free); |
| #else |
| mng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL); |
| #endif |
| if (mng_ptr == NULL) |
| fprintf(STDERR, "pngcrush could not create mng_ptr"); |
| |
| if ((mng_out = FOPEN(mngname, "wb")) == NULL) { |
| fprintf(STDERR, "Could not open output file %s\n", |
| mngname); |
| FCLOSE(fpin); |
| exit(1); |
| } |
| number_of_open_files++; |
| png_init_io(mng_ptr, mng_out); |
| png_set_write_fn(mng_ptr, (png_voidp) mng_out, |
| (png_rw_ptr) NULL, |
| NULL); |
| #endif |
| |
| } |
| |
| idat_length[0] = measure_idats(fpin); |
| |
| #ifdef PNGCRUSH_LOCO |
| if (new_mng) { |
| png_destroy_write_struct(&mng_ptr, NULL); |
| FCLOSE(mng_out); |
| } |
| #endif |
| |
| FCLOSE(fpin); |
| |
| |
| if (verbose > 0) { |
| |
| fprintf(STDERR, " Recompressing %s\n", inname); |
| fprintf(STDERR, |
| " Total length of data found in IDAT chunks = %8lu\n", |
| (unsigned long)idat_length[0]); |
| fflush(STDERR); |
| } |
| |
| if (idat_length[0] == 0) |
| continue; |
| |
| } else |
| idat_length[0] = 1; |
| |
| if (already_crushed) { |
| fprintf(STDERR, " File %s has already been crushed.\n", inname); |
| } |
| if (image_is_immutable) { |
| fprintf(STDERR, |
| " Image %s has a dSIG chunk and is immutable.\n", inname); |
| } |
| if (!already_crushed && !image_is_immutable) { |
| #ifdef PNGCRUSH_COUNT_COLORS |
| reduce_to_gray = 0; |
| it_is_opaque = 0; |
| output_color_type = input_color_type; |
| if (do_color_count) { |
| if (force_output_color_type == 8 && (input_color_type == 2 || |
| (input_color_type == 3) || |
| input_color_type == 4 |
| || input_color_type == 6)) |
| /* check for unused alpha channel or single transparent color */ |
| { |
| int alpha_status; |
| P1( "Opening file %s for alpha check\n", inname); |
| |
| if ((fpin = FOPEN(inname, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find file: %s\n", inname); |
| continue; |
| } |
| number_of_open_files++; |
| |
| alpha_status = count_colors(fpin); |
| if (num_rgba < 257) { |
| P1("Finished counting colors. num_rgba=%d\n", |
| num_rgba); |
| } else { |
| P1("Finished counting colors. num_rgba is more than 256\n"); |
| } |
| alpha_status = alpha_status; /* silence compiler warning. */ |
| |
| FCLOSE(fpin); |
| |
| if (it_is_opaque) { |
| if (output_color_type == 4) |
| output_color_type = 0; |
| else if (output_color_type == 6) |
| output_color_type = 2; |
| } |
| if (reduce_to_gray) { |
| if (output_color_type == 2) |
| output_color_type = 0; |
| else if (output_color_type == 6) |
| output_color_type = 4; |
| } |
| } |
| #if 0 /* TO DO */ |
| if (output_color_type == 0) |
| /* see if bit depth can be reduced */ |
| { |
| } |
| |
| if (input_color_type == 2) |
| /* check for 256 or fewer colors */ |
| { |
| /* TO DO */ |
| } |
| |
| if (input_color_type == 3) |
| /* check for unused palette entries */ |
| { |
| /* TO DO */ |
| } |
| #endif |
| if (force_output_color_type == 8 |
| && input_color_type != output_color_type) { |
| P1("setting output color type to %d\n", output_color_type); |
| force_output_color_type = output_color_type; |
| } |
| } |
| #else |
| if (do_color_count) |
| printf(" color counting (-cc option) is not supported.\n"); |
| #endif /* PNGCRUSH_COUNT_COLORS */ |
| |
| if (force_output_bit_depth != 0 && |
| force_output_bit_depth != 1 && |
| force_output_bit_depth != 2 && |
| force_output_bit_depth != 4 && |
| force_output_bit_depth != 8 && |
| force_output_bit_depth != 16) |
| { |
| fprintf(STDERR, "\n Ignoring invalid bit_depth: %d\n", |
| force_output_bit_depth); |
| force_output_bit_depth=0; |
| } |
| if (force_output_color_type != 8 && |
| force_output_color_type != 0 && |
| force_output_color_type != 2 && |
| force_output_color_type != 3 && |
| force_output_color_type != 4 && |
| force_output_color_type != 6) |
| { |
| fprintf(STDERR, "\n Ignoring invalid color_type: %d\n", |
| force_output_color_type); |
| force_output_color_type=8; |
| } |
| output_color_type = force_output_color_type; |
| output_bit_depth = force_output_bit_depth; |
| |
| if (!methods_specified || try_method[0] == 0) { |
| for (i = 1; i <= DEFAULT_METHODS; i++) |
| try_method[i] = 0; |
| try_method[6] = try_method[0]; |
| } |
| |
| best_of_three = 1; |
| |
| /* //////////////////////////////////////////////////////////////////// |
| //////////////// //////////////////// |
| //////////////// START OF MAIN LOOP OVER METHODS //////////////////// |
| //////////////// //////////////////// |
| //////////////////////////////////////////////////////////////////// */ |
| |
| /* MAX_METHODS is 200 */ |
| P1("\n\nENTERING MAIN LOOP OVER %d METHODS\n", MAX_METHODS); |
| for (trial = 1; trial <= MAX_METHODS; trial++) { |
| idat_length[trial] = (png_uint_32) 0xffffffff; |
| |
| /* this part of if-block is for final write-the-best-file |
| iteration */ |
| if (trial == MAX_METHODS) { |
| png_uint_32 best_length; |
| int j; |
| |
| /* check lengths */ |
| best = 0; /* i.e., input file */ |
| best_length = (png_uint_32) 0xffffffff; |
| for (j = things_have_changed; j < MAX_METHODS; j++) { |
| if (best_length > idat_length[j]) { |
| best_length = idat_length[j]; |
| best = j; |
| } |
| } |
| |
| if (image_is_immutable |
| || (idat_length[best] == idat_length[0] |
| && things_have_changed == 0 |
| && idat_length[best] != idat_length[final_method] |
| && nosave == 0)) |
| { |
| /* just copy input to output */ |
| |
| P2("prepare to copy input to output\n"); |
| png_crush_pause(); |
| |
| if ((fpin = FOPEN(inname, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find input file %s\n", |
| inname); |
| continue; |
| } |
| |
| number_of_open_files++; |
| if ((fpout = FOPEN(outname, "wb")) == NULL) { |
| fprintf(STDERR, "Could not open output file %s\n", |
| outname); |
| FCLOSE(fpin); |
| exit(1); |
| } |
| |
| number_of_open_files++; |
| P2("copying input to output... tc=%d ...", |
| things_have_changed); |
| |
| for (;;) { |
| png_size_t num_in; |
| |
| num_in = fread(buffer, 1, 1, fpin); |
| if (!num_in) |
| break; |
| fwrite(buffer, 1, 1, fpout); |
| } |
| P2("copy complete.\n"); |
| png_crush_pause(); |
| FCLOSE(fpin); |
| FCLOSE(fpout); |
| setfiletype(outname); |
| break; |
| } |
| |
| if (idat_length[best] == idat_length[final_method]) { |
| break; |
| } else { |
| filter_type = fm[best]; |
| zlib_level = lv[best]; |
| if (zs[best] == 1) |
| z_strategy = Z_FILTERED; |
| else if (zs[best] == 2) |
| z_strategy = Z_HUFFMAN_ONLY; |
| #ifdef Z_RLE |
| else if (zs[best] == 3) |
| z_strategy = Z_RLE; |
| #endif |
| else /* if (zs[best] == 0) */ |
| z_strategy = Z_DEFAULT_STRATEGY; |
| } |
| } else { |
| if (trial > 2 && trial < 5 && idat_length[trial - 1] |
| < idat_length[best_of_three]) |
| best_of_three = trial - 1; |
| if (try_method[trial]) { |
| P2("skipping \"late\" trial %d\n", trial); |
| continue; |
| } |
| if (!methods_specified && try_method[0]) { |
| if ((trial == 4 || trial == 7) && best_of_three != 1) { |
| P2("skipping \"early\" trial %d\n", trial); |
| continue; |
| } |
| if ((trial == 5 || trial == 8) && best_of_three != 2) { |
| P2("skipping \"early\" trial %d\n", trial); |
| continue; |
| } |
| if ((trial == 6 || trial == 9 || trial == 10) |
| && best_of_three != 3) { |
| P2("skipping \"early\" trial %d\n", trial); |
| continue; |
| } |
| } |
| filter_type = fm[trial]; |
| zlib_level = lv[trial]; |
| if (zs[trial] == 1) |
| z_strategy = Z_FILTERED; |
| else if (zs[trial] == 2) |
| z_strategy = Z_HUFFMAN_ONLY; |
| #ifdef Z_RLE |
| else if (zs[trial] == 3) |
| z_strategy = Z_RLE; |
| #endif |
| else /* if (zs[trial] == 0) */ |
| z_strategy = Z_DEFAULT_STRATEGY; |
| final_method = trial; |
| if (!nosave) { |
| P2("\n\n------------------------------------------------\n" |
| "Begin trial %d, filter %d, strategy %d, level %d\n", |
| trial, filter_type, z_strategy, zlib_level); |
| } |
| } |
| |
| P2("prepare to open files.\n"); |
| png_crush_pause(); |
| |
| if ((fpin = FOPEN(inname, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find input file %s\n", inname); |
| continue; |
| } |
| number_of_open_files++; |
| if (nosave == 0) { |
| #ifndef __riscos |
| /* Can't sensibly check this on RISC OS without opening a file |
| for update or output |
| */ |
| struct stat stat_in, stat_out; |
| if (first_trial && !nofilecheck |
| && (stat(inname, &stat_in) == 0) |
| && (stat(outname, &stat_out) == 0) && |
| #if defined(_MSC_VER) || defined(__MINGW32__) /* maybe others? */ |
| /* MSVC++6.0 will erroneously return 0 for both files, so |
| we simply check the size instead. It is possible that |
| we will erroneously reject the attempt when inputsize |
| and outputsize are equal, for different files |
| */ |
| (stat_in.st_size == stat_out.st_size) && |
| #else |
| (stat_in.st_ino == stat_out.st_ino) && |
| #endif |
| (stat_in.st_dev == stat_out.st_dev)) { |
| fprintf(STDERR, |
| "\n Cannot overwrite input file %s\n", |
| outname); |
| P1(" st_ino=%d, st_size=%d\n\n", |
| (int) stat_in.st_ino, (int) stat_in.st_size); |
| FCLOSE(fpin); |
| exit(1); |
| } |
| #endif |
| if ((fpout = FOPEN(outname, "wb")) == NULL) { |
| fprintf(STDERR, "Could not open output file %s\n", |
| outname); |
| FCLOSE(fpin); |
| exit(1); |
| } |
| |
| number_of_open_files++; |
| } |
| |
| P2("files are opened.\n"); |
| png_crush_pause(); |
| |
| /* OK to ignore any warning about the address of exception__prev in "Try" */ |
| Try { |
| png_uint_32 row_length; |
| P1( "Allocating read and write structures\n"); |
| #ifdef PNG_USER_MEM_SUPPORTED |
| read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL, (png_voidp) NULL, |
| (png_malloc_ptr) png_debug_malloc, |
| (png_free_ptr) png_debug_free); |
| #else |
| read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL); |
| #endif |
| if (read_ptr == NULL) |
| Throw "pngcrush could not create read_ptr"; |
| |
| if (nosave == 0) { |
| #ifdef PNG_USER_MEM_SUPPORTED |
| write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL, (png_voidp) NULL, |
| (png_malloc_ptr) png_debug_malloc, |
| (png_free_ptr) png_debug_free); |
| #else |
| write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| (png_voidp) NULL, (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL); |
| #endif |
| if (write_ptr == NULL) |
| Throw "pngcrush could not create write_ptr"; |
| |
| } |
| P1("Allocating read_info, write_info, end_info structures\n"); |
| read_info_ptr = png_create_info_struct(read_ptr); |
| if (read_info_ptr == NULL) |
| Throw "pngcrush could not create read_info_ptr"; |
| end_info_ptr = png_create_info_struct(read_ptr); |
| if (end_info_ptr == NULL) |
| Throw "pngcrush could not create end_info_ptr"; |
| if (nosave == 0) { |
| write_info_ptr = png_create_info_struct(write_ptr); |
| if (write_info_ptr == NULL) |
| Throw "pngcrush could not create write_info_ptr"; |
| write_end_info_ptr = png_create_info_struct(write_ptr); |
| if (write_end_info_ptr == NULL) |
| Throw |
| "pngcrush could not create write_end_info_ptr"; |
| } |
| |
| P2("structures created.\n"); |
| png_crush_pause(); |
| |
| P1( "Initializing input and output streams\n"); |
| #if !defined(PNG_NO_STDIO) |
| png_init_io(read_ptr, fpin); |
| if (nosave == 0) |
| png_init_io(write_ptr, fpout); |
| #else |
| png_set_read_fn(read_ptr, (png_voidp) fpin, |
| (png_rw_ptr) NULL); |
| if (nosave == 0) |
| png_set_write_fn(write_ptr, (png_voidp) fpout, |
| (png_rw_ptr) NULL, |
| #if defined(PNG_WRITE_FLUSH_SUPPORTED) |
| png_default_flush); |
| #else |
| NULL); |
| #endif |
| #endif |
| |
| P2("io has been initialized.\n"); |
| png_crush_pause(); |
| |
| /* We don't need to check CRC's because they were already |
| checked in the png_measure_idat function */ |
| |
| #ifdef PNG_CRC_QUIET_USE |
| png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, |
| PNG_CRC_QUIET_USE); |
| #endif |
| |
| #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) |
| png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, |
| (png_bytep) NULL, 0); |
| #endif |
| #if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) |
| if (nosave == 0) { |
| if (found_any_chunk == 1) |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| (png_bytep) "dSIG", 1); |
| if (all_chunks_are_safe) |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| (png_bytep) NULL, 0); |
| else { |
| #if !defined(PNG_cHRM_SUPPORTED) || !defined(PNG_hIST_SUPPORTED) || \ |
| !defined(PNG_iCCP_SUPPORTED) || !defined(PNG_sCAL_SUPPORTED) || \ |
| !defined(PNG_pCAL_SUPPORTED) || !defined(PNG_sPLT_SUPPORTED) || \ |
| !defined(PNG_sTER_SUPPORTED) || !defined(PNG_tIME_SUPPORTED) |
| png_byte chunk_name[5]; |
| chunk_name[4] = '\0'; |
| #endif |
| |
| if (keep_unknown_chunk("alla", argv) && |
| keep_unknown_chunk("allb", argv)) |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_IF_SAFE, |
| (png_bytep) NULL, |
| 0); |
| else |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep) NULL, |
| 0); |
| |
| #if !defined(PNG_cHRM_SUPPORTED) |
| if (keep_unknown_chunk("cHRM", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_cHRM); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_hIST_SUPPORTED) |
| if (keep_unknown_chunk("hIST", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_hIST); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_iCCP_SUPPORTED) |
| if (keep_unknown_chunk("iCCP", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_iCCP); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_sCAL_SUPPORTED) |
| if (keep_unknown_chunk("sCAL", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_sCAL); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_pCAL_SUPPORTED) |
| if (keep_unknown_chunk("pCAL", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_pCAL); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_sPLT_SUPPORTED) |
| if (keep_unknown_chunk("sPLT", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_sPLT); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_sTER_SUPPORTED) |
| if (keep_unknown_chunk("sTER", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_sTER); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| #if !defined(PNG_tIME_SUPPORTED) |
| if (keep_unknown_chunk("tIME", argv)) { |
| png_save_uint_32(chunk_name, PNG_UINT_tIME); |
| png_set_keep_unknown_chunks(write_ptr, |
| PNG_HANDLE_CHUNK_ALWAYS, |
| chunk_name, 1); |
| } |
| #endif |
| } |
| } |
| #endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ |
| |
| P1( "Reading info struct\n"); |
| { |
| #if defined(PNGCRUSH_LOCO) |
| png_byte mng_signature[8] = |
| { 138, 77, 78, 71, 13, 10, 26, 10 }; |
| #endif |
| png_byte png_signature[8] = |
| { 137, 80, 78, 71, 13, 10, 26, 10 }; |
| |
| png_default_read_data(read_ptr, png_signature, 8); |
| png_set_sig_bytes(read_ptr, 8); |
| |
| #if defined(PNGCRUSH_LOCO) |
| if (!(int) |
| (png_memcmp(mng_signature, png_signature, 8))) { |
| /* Skip the MHDR */ |
| png_permit_mng_features(read_ptr, |
| PNG_FLAG_MNG_FILTER_64); |
| png_skip_chunk(read_ptr); |
| 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"); |
| } |
| if(fix && found_CgBI) |
| { |
| /* Skip the CgBI chunk */ |
| |
| png_skip_chunk(read_ptr); |
| |
| /* iCCP and zTXt are probably unreadable |
| * because of the nonstandard deflate */ |
| |
| png_set_keep_unknown_chunks(read_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep)"iCCP", 1); |
| png_set_keep_unknown_chunks(read_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep)"zTXt", 1); |
| } |
| } |
| |
| png_read_info(read_ptr, read_info_ptr); |
| |
| /* { GRR added for quick %-navigation (1) */ |
| |
| /* Start of chunk-copying/removal code, in order: |
| * - IHDR |
| * - bKGD |
| * - cHRM |
| * - gAMA |
| * - sRGB |
| * - iCCP |
| * - oFFs |
| * - pCAL |
| * - pHYs |
| * - hIST |
| * - tRNS |
| * - PLTE |
| * - sBIT |
| * - sCAL |
| * - sPLT |
| * - sTER |
| * - tEXt/zTXt/iTXt |
| * - tIME |
| * - unknown chunks |
| */ |
| { |
| int interlace_method, compression_method, |
| filter_method; |
| |
| P1( "Transferring info struct\n"); |
| |
| if (png_get_IHDR |
| (read_ptr, read_info_ptr, &width, &height, |
| &bit_depth, &color_type, &interlace_method, |
| &compression_method, &filter_method)) { |
| int need_expand = 0; |
| input_color_type = color_type; |
| input_bit_depth = bit_depth; |
| |
| if (output_color_type > 7) { |
| output_color_type = input_color_type; |
| } |
| |
| if (verbose > 1 && first_trial) { |
| fprintf(STDERR, " IHDR chunk data:\n"); |
| fprintf(STDERR, |
| " Width=%lu, height=%lu\n", |
| (unsigned long)width, |
| (unsigned long)height); |
| fprintf(STDERR, " Bit depth =%d\n", |
| bit_depth); |
| fprintf(STDERR, " Color type=%d\n", |
| color_type); |
| if (output_color_type != color_type) |
| fprintf(STDERR, |
| " Output color type=%d\n", |
| output_color_type); |
| fprintf(STDERR, " Interlace =%d\n", |
| interlace_method); |
| } |
| |
| #ifndef PNG_WRITE_PACK_SUPPORTED |
| if (output_bit_depth == 0) |
| #else |
| if (force_output_bit_depth == 0) |
| #endif |
| { |
| output_bit_depth = input_bit_depth; |
| } |
| if ((output_color_type != 3 |
| || output_bit_depth > 8) |
| && output_bit_depth >= 8 |
| && output_bit_depth != input_bit_depth) |
| need_expand = 1; |
| |
| #if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) |
| if ((color_type == 2 || color_type == 6 |
| || color_type == 3) && |
| (output_color_type == 0 || output_color_type == 4)) |
| { |
| if (verbose > 0 && first_trial) { |
| #ifdef PNGCRUSH_COUNT_COLORS |
| if (reduce_to_gray) |
| fprintf(STDERR, " Reducing all-gray " |
| "truecolor image to grayscale.\n"); |
| else |
| #endif |
| fprintf(STDERR, " Reducing truecolor " |
| "image to grayscale.\n"); |
| } |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| png_set_rgb_to_gray_fixed(read_ptr, 1, -1, -1); |
| #else |
| png_set_rgb_to_gray(read_ptr, 1, 0., 0.); |
| #endif |
| if (output_bit_depth < 8) |
| output_bit_depth = 8; |
| if (color_type == 3) |
| need_expand = 1; |
| } |
| #endif |
| |
| if (color_type != 3 && output_color_type == 3) { |
| printf(" Cannot change to indexed color " |
| "(color_type 3)\n"); |
| output_color_type = input_color_type; |
| } |
| |
| if ((color_type == 0 || color_type == 4) && |
| (output_color_type == 2 |
| || output_color_type == 6)) { |
| png_set_gray_to_rgb(read_ptr); |
| } |
| |
| if ((color_type == 4 || color_type == 6) && |
| (output_color_type != 4 |
| && output_color_type != 6)) { |
| if (verbose > 0 && first_trial) { |
| #ifdef PNGCRUSH_COUNT_COLORS |
| if (it_is_opaque) |
| fprintf(STDERR, |
| " Stripping opaque alpha channel.\n"); |
| else |
| #endif |
| fprintf(STDERR, |
| " Stripping existing alpha channel.\n"); |
| } |
| #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED |
| png_set_strip_alpha(read_ptr); |
| #endif |
| } |
| |
| if ((output_color_type == 4 |
| || output_color_type == 6) && (color_type != 4 |
| && color_type |
| != 6)) { |
| if (verbose > 0 && first_trial) |
| fprintf(STDERR, |
| " Adding an opaque alpha channel.\n"); |
| #ifdef PNG_READ_FILLER_SUPPORTED |
| png_set_filler(read_ptr, (png_uint_32) 65535L, |
| PNG_FILLER_AFTER); |
| #endif |
| need_expand = 1; |
| } |
| |
| if (output_color_type && output_color_type != 3 && |
| output_bit_depth < 8) |
| output_bit_depth = 8; |
| |
| if ((output_color_type == 2 |
| || output_color_type == 6) |
| && color_type == 3) { |
| if (verbose > 0 && first_trial) |
| fprintf(STDERR, |
| " Expanding indexed color file.\n"); |
| need_expand = 1; |
| } |
| #ifdef PNG_READ_EXPAND_SUPPORTED |
| if (need_expand == 1) |
| png_set_expand(read_ptr); |
| #endif |
| |
| #ifdef PNG_READ_PACK_SUPPORTED |
| if (input_bit_depth < 8) { |
| png_set_packing(read_ptr); |
| } |
| if (output_color_type == 0 && output_bit_depth < 8) { |
| png_color_8 true_bits; |
| true_bits.gray = (png_byte) (output_bit_depth); |
| png_set_shift(read_ptr, &true_bits); |
| } |
| #endif |
| |
| if (verbose > 1) |
| fprintf(STDERR, " Setting IHDR\n"); |
| |
| #if defined(PNGCRUSH_LOCO) |
| output_format = 0; |
| if (do_loco) { |
| if (output_color_type == 2 |
| || output_color_type == 6) { |
| output_format = 1; |
| filter_method = 64; |
| png_permit_mng_features(write_ptr, |
| PNG_FLAG_MNG_FILTER_64); |
| } |
| } else |
| filter_method = 0; |
| if (input_format != output_format) |
| things_have_changed = 1; |
| #endif |
| |
| png_set_IHDR(write_ptr, write_info_ptr, width, |
| height, output_bit_depth, |
| output_color_type, interlace_method, |
| compression_method, filter_method); |
| |
| if (output_color_type != input_color_type) |
| things_have_changed = 1; |
| |
| } |
| } |
| |
| #if defined(PNG_READ_bKGD_SUPPORTED) && defined(PNG_WRITE_bKGD_SUPPORTED) |
| { |
| png_color_16p background; |
| if (!have_bkgd |
| && png_get_bKGD(read_ptr, read_info_ptr, |
| &background)) { |
| if (keep_chunk("bKGD", argv)) { |
| if ((input_color_type == 2 |
| || input_color_type == 6) |
| && (output_color_type == 0 |
| || output_color_type == 4)) |
| background->gray = background->green; |
| png_set_bKGD(write_ptr, write_info_ptr, |
| background); |
| } |
| } |
| if (have_bkgd) { |
| /* If we are reducing an RGB image to grayscale, but |
| the background color isn't gray, the green channel |
| is written. That's not spec-compliant. We should |
| really check for a non-gray bKGD and refuse to do |
| the reduction if one is present. */ |
| png_color_16 backgd; |
| png_color_16p backgrnd = &backgd; |
| backgrnd->red = bkgd_red; |
| backgrnd->green = bkgd_green; |
| backgrnd->blue = bkgd_blue; |
| backgrnd->gray = backgrnd->green; |
| png_set_bKGD(write_ptr, write_info_ptr, |
| backgrnd); |
| } |
| } |
| #endif /* defined(PNG_READ_bKGD_SUPPORTED)&&defined(PNG_WRITE_bKGD_SUPPORTED) */ |
| #if defined(PNG_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_cHRM_SUPPORTED) |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| { |
| png_fixed_point white_x, white_y, red_x, red_y, |
| green_x, green_y, blue_x, blue_y; |
| |
| if (found_cHRM && png_get_cHRM_fixed |
| (read_ptr, read_info_ptr, &white_x, &white_y, |
| &red_x, &red_y, &green_x, &green_y, &blue_x, |
| &blue_y)) { |
| if (keep_chunk("cHRM", argv)) { |
| png_set_cHRM_fixed(write_ptr, |
| write_info_ptr, white_x, |
| white_y, red_x, red_y, |
| green_x, green_y, |
| blue_x, blue_y); |
| } |
| } |
| } |
| #else |
| { |
| double white_x, white_y, red_x, red_y, green_x, |
| green_y, blue_x, blue_y; |
| |
| if (png_get_cHRM |
| (read_ptr, read_info_ptr, &white_x, &white_y, |
| &red_x, &red_y, &green_x, &green_y, &blue_x, |
| &blue_y)) { |
| if (keep_chunk("cHRM", argv)) { |
| png_set_cHRM(write_ptr, write_info_ptr, |
| white_x, white_y, red_x, |
| red_y, green_x, green_y, |
| blue_x, blue_y); |
| } |
| } |
| } |
| #endif /* ?PNG_FIXED_POINT_SUPPORTED */ |
| #endif /* PNG_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_cHRM_SUPPORTED */ |
| |
| #if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_WRITE_gAMA_SUPPORTED) |
| { |
| if (force_specified_gamma) { |
| if (first_trial) { |
| things_have_changed = 1; |
| if (verbose > 0) |
| fprintf(STDERR, " Inserting gAMA chunk with " |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| "gamma=(%d/100000)\n", |
| #else |
| "gamma=%f\n", |
| #endif |
| force_specified_gamma); |
| } |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| png_set_gAMA_fixed(write_ptr, write_info_ptr, |
| (png_fixed_point) |
| force_specified_gamma); |
| file_gamma = |
| (png_fixed_point) force_specified_gamma; |
| #else |
| png_set_gAMA(write_ptr, write_info_ptr, |
| force_specified_gamma); |
| file_gamma = force_specified_gamma; |
| #endif |
| } |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| else if (found_gAMA && png_get_gAMA_fixed |
| (read_ptr, read_info_ptr, &file_gamma)) |
| #else |
| else if (found_gAMA && png_get_gAMA |
| (read_ptr, read_info_ptr, &file_gamma)) |
| #endif |
| { |
| if (keep_chunk("gAMA", argv)) { |
| if (image_specified_gamma) |
| file_gamma = image_specified_gamma; |
| if (verbose > 1 && first_trial) |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| fprintf(STDERR, " gamma=(%d/100000)\n", |
| (int) file_gamma); |
| if (double_gamma) |
| file_gamma += file_gamma; |
| png_set_gAMA_fixed(write_ptr, write_info_ptr, |
| file_gamma); |
| #else |
| fprintf(STDERR, " gamma=%f\n", |
| file_gamma); |
| if (double_gamma) |
| file_gamma += file_gamma; |
| png_set_gAMA(write_ptr, write_info_ptr, |
| file_gamma); |
| #endif |
| } |
| } else if (specified_gamma) { |
| if (first_trial) { |
| things_have_changed = 1; |
| if (verbose > 0) |
| fprintf(STDERR, " Inserting gAMA chunk with " |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| "gamma=(%d/100000)\n", |
| #else |
| "gamma=%f\n", |
| #endif |
| specified_gamma); |
| } |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| png_set_gAMA_fixed(write_ptr, write_info_ptr, |
| specified_gamma); |
| file_gamma = (png_fixed_point) specified_gamma; |
| #else |
| png_set_gAMA(write_ptr, write_info_ptr, |
| specified_gamma); |
| file_gamma = specified_gamma; |
| #endif |
| } |
| } |
| #endif /* defined(PNG_READ_gAMA_SUPPORTED)&&defined(PNG_WRITE_gAMA_SUPPORTED) */ |
| |
| #if defined(PNG_READ_sRGB_SUPPORTED) && defined(PNG_WRITE_sRGB_SUPPORTED) |
| { |
| int file_intent; |
| |
| if (png_get_sRGB |
| (read_ptr, read_info_ptr, &file_intent)) { |
| if (keep_chunk("sRGB", argv)) { |
| png_set_sRGB(write_ptr, write_info_ptr, |
| file_intent); |
| intent = file_intent; |
| } |
| } else if (intent >= 0) { |
| #ifdef PNG_gAMA_SUPPORTED |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| if (file_gamma >= 45000L && file_gamma <= 46000L) |
| #else |
| if (file_gamma >= 0.45000 && file_gamma <= 0.46000) |
| #endif |
| { |
| things_have_changed = 1; |
| if (first_trial) |
| fprintf(STDERR, |
| " Inserting sRGB chunk with intent=%d\n", |
| intent); |
| png_set_sRGB(write_ptr, write_info_ptr, |
| intent); |
| } else if (file_gamma != 0) { |
| if (first_trial) { |
| fprintf(STDERR, " Ignoring sRGB request; " |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| "gamma=(%lu/100000)" |
| #else |
| "gamma=%f" |
| #endif |
| " is not approx. 0.455\n", |
| (unsigned long)file_gamma); |
| } |
| } |
| #endif /* PNG_gAMA_SUPPORTED */ |
| } |
| } |
| #endif /* defined(PNG_READ_sRGB_SUPPORTED)&&defined(PNG_WRITE_sRGB_SUPPORTED) */ |
| |
| #if defined(PNG_READ_iCCP_SUPPORTED) && defined(PNG_WRITE_iCCP_SUPPORTED) |
| if (intent < 0) { /* ignore iCCP if sRGB is being written */ |
| png_charp name; |
| png_charp profile; |
| png_uint_32 proflen; |
| int compression_method; |
| |
| if (png_get_iCCP |
| (read_ptr, read_info_ptr, &name, |
| &compression_method, &profile, &proflen)) { |
| P1("Got iCCP chunk, proflen=%lu\n", |
| (unsigned long)proflen); |
| if (iccp_length) |
| P0("Will not replace existing iCCP chunk.\n"); |
| if (keep_chunk("iCCP", argv)) |
| png_set_iCCP(write_ptr, write_info_ptr, name, |
| compression_method, profile, |
| proflen); |
| |
| } |
| #ifdef PNG_iCCP_SUPPORTED |
| else if (iccp_length) { |
| png_set_iCCP(write_ptr, write_info_ptr, iccp_name, |
| 0, iccp_text, iccp_length); |
| P1("Wrote iCCP chunk, proflen=%d\n", iccp_length); |
| } |
| #endif |
| |
| } |
| #endif /* defined(PNG_READ_iCCP_SUPPORTED)&&defined(PNG_WRITE_iCCP_SUPPORTED) */ |
| |
| #if defined(PNG_READ_oFFs_SUPPORTED) && defined(PNG_WRITE_oFFs_SUPPORTED) |
| { |
| png_int_32 offset_x, offset_y; |
| int unit_type; |
| |
| if (png_get_oFFs |
| (read_ptr, read_info_ptr, &offset_x, &offset_y, |
| &unit_type)) { |
| if (offset_x == 0 && offset_y == 0) { |
| if (verbose > 0 && first_trial) |
| fprintf(STDERR, |
| " Deleting useless oFFs 0 0 chunk\n"); |
| } else { |
| if (keep_chunk("oFFs", argv)) |
| png_set_oFFs(write_ptr, write_info_ptr, |
| offset_x, offset_y, |
| unit_type); |
| } |
| } |
| } |
| #endif |
| |
| #if defined(PNG_READ_pCAL_SUPPORTED) && defined(PNG_WRITE_pCAL_SUPPORTED) |
| { |
| png_charp purpose, units; |
| png_charpp params; |
| png_int_32 X0, X1; |
| int type, nparams; |
| |
| if (png_get_pCAL |
| (read_ptr, read_info_ptr, &purpose, &X0, &X1, |
| &type, &nparams, &units, ¶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", |
| (unsigned long)res_x, |
| (unsigned long)res_y); |
| } |
| } |
| #endif |
| |
| #if defined(PNG_READ_hIST_SUPPORTED) && defined(PNG_WRITE_hIST_SUPPORTED) |
| { |
| png_uint_16p hist; |
| |
| if (png_get_hIST(read_ptr, read_info_ptr, &hist)) { |
| if (keep_chunk("hIST", argv)) |
| png_set_hIST(write_ptr, write_info_ptr, hist); |
| } |
| } |
| #endif |
| |
| #if defined(PNG_READ_tRNS_SUPPORTED) && defined(PNG_WRITE_tRNS_SUPPORTED) |
| { |
| png_bytep trans; |
| int num_trans; |
| png_color_16p trans_values; |
| |
| if (png_get_tRNS |
| (read_ptr, read_info_ptr, &trans, &num_trans, |
| &trans_values)) { |
| if (verbose > 1) |
| fprintf(STDERR, |
| " Found tRNS chunk in input file.\n"); |
| if (have_trns == 1) { |
| P0(" Will not overwrite existing tRNS chunk.\n"); |
| } |
| if (keep_chunk("tRNS", argv)) { |
| int last_nonmax = -1; |
| trns_red = trans_values->red; |
| trns_green = trans_values->green; |
| trns_blue = trans_values->blue; |
| trns_gray = trans_values->gray; |
| if (output_color_type == 3) { |
| for (ia = 0; ia < num_trans; ia++) |
| trns_array[ia] = trans[ia]; |
| for (; ia < 256; ia++) |
| trns_array[ia] = 255; |
| for (ia = 0; ia < 256; ia++) { |
| if (trns_array[ia] != 255) |
| last_nonmax = ia; |
| } |
| if (first_trial && verbose > 0) { |
| if (last_nonmax < 0) |
| fprintf(STDERR, " Deleting " |
| "all-opaque tRNS chunk.\n"); |
| else if (last_nonmax + 1 < num_trans) |
| fprintf(STDERR, |
| " Truncating trailing opaque " |
| "entries from tRNS chunk.\n"); |
| } |
| num_trans = last_nonmax + 1; |
| } |
| if (verbose > 1) |
| fprintf(STDERR, |
| " png_set_tRNS, num_trans=%d\n", |
| num_trans); |
| if (output_color_type != 3 || num_trans) |
| png_set_tRNS(write_ptr, write_info_ptr, |
| trans, num_trans, |
| trans_values); |
| } |
| } else if (have_trns == 1) { |
| /* will not overwrite existing trns data */ |
| png_color_16 trans_data; |
| png_byte index_data = (png_byte) trns_index; |
| num_trans = index_data + 1; |
| if (verbose > 1) |
| fprintf(STDERR, "Have_tRNS, num_trans=%d\n", |
| num_trans); |
| if (output_color_type == 3) { |
| trans_values = NULL; |
| for (ia = 0; ia < num_trans; ia++) |
| trns_array[ia] = trans_in[ia]; |
| for (; ia < 256; ia++) |
| trns_array[ia] = 255; |
| } else { |
| for (ia = 0; ia < 256; ia++) |
| trns_array[ia] = 255; |
| trns_array[index_data] = 0; |
| |
| trans_data.index = index_data; |
| trans_data.red = trns_red; |
| trans_data.green = trns_green; |
| trans_data.blue = trns_blue; |
| trans_data.gray = trns_gray; |
| trans_values = &trans_data; |
| } |
| |
| P0(" Adding a tRNS chunk\n"); |
| png_set_tRNS(write_ptr, write_info_ptr, trns_array, |
| num_trans, trans_values); |
| |
| things_have_changed = 1; |
| } else { |
| for (ia = 0; ia < 256; ia++) |
| trns_array[ia] = 255; |
| } |
| if (verbose > 1 && first_trial) { |
| int last = -1; |
| for (i = 0; ia < num_palette; ia++) |
| if (trns_array[ia] != 255) |
| last = ia; |
| if (last >= 0) { |
| fprintf(STDERR, " Transparency:\n"); |
| if (output_color_type == 3) |
| for (i = 0; ia < num_palette; ia++) |
| fprintf(STDERR, " %4d %4d\n", ia, |
| trns_array[ia]); |
| else if (output_color_type == 0) |
| fprintf(STDERR, " %d\n", trns_gray); |
| else if (output_color_type == 2) |
| fprintf(STDERR, " %d %d %d\n", |
| trns_red, trns_green, trns_blue); |
| } |
| } |
| } |
| #endif /* defined(PNG_READ_tRNS_SUPPORTED)&&defined(PNG_WRITE_tRNS_SUPPORTED) */ |
| |
| if (png_get_PLTE |
| (read_ptr, read_info_ptr, &palette, &num_palette)) |
| { |
| if (plte_len > 0) |
| num_palette = plte_len; |
| if (do_pplt) { |
| printf("PPLT: %s\n", pplt_string); |
| printf("Sorry, PPLT is not implemented yet.\n"); |
| } |
| if (output_color_type == 3) |
| png_set_PLTE(write_ptr, write_info_ptr, palette, |
| num_palette); |
| else if (keep_chunk("PLTE", argv)) |
| png_set_PLTE(write_ptr, write_info_ptr, palette, |
| num_palette); |
| if (verbose > 1 && first_trial) { |
| png_colorp p = palette; |
| fprintf(STDERR, " Palette:\n"); |
| fprintf(STDERR, |
| " I R G B ( color ) A\n"); |
| for (i = 0; i < num_palette; i++) { |
| fprintf(STDERR, |
| " %4d %4d %4d %4d (#%2.2x%2.2x%2.2x) %4d\n", |
| i, p->red, p->green, p->blue, p->red, |
| p->green, p->blue, trns_array[i]); |
| p++; |
| } |
| } |
| } |
| |
| |
| #if defined(PNG_READ_sBIT_SUPPORTED) && defined(PNG_WRITE_sBIT_SUPPORTED) |
| { |
| png_color_8p sig_bit; |
| |
| /* If we are reducing a truecolor PNG to grayscale, and the |
| * RGB sBIT values aren't identical, we'll lose sBIT info. |
| */ |
| if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) { |
| if (keep_chunk("sBIT", argv)) { |
| if ((input_color_type == 0 |
| || input_color_type == 4) |
| && (output_color_type == 2 |
| || output_color_type == 6 |
| || output_color_type == 3)) |
| sig_bit->red = sig_bit->green = |
| sig_bit->blue = sig_bit->gray; |
| if ((input_color_type == 2 |
| || input_color_type == 6 |
| || output_color_type == 3) |
| && (output_color_type == 0 |
| || output_color_type == 4)) |
| sig_bit->gray = sig_bit->green; |
| |
| if ((input_color_type == 0 |
| || input_color_type == 2) |
| && (output_color_type == 4 |
| || output_color_type == 6)) |
| sig_bit->alpha = 1; |
| |
| png_set_sBIT(write_ptr, write_info_ptr, |
| sig_bit); |
| } |
| } |
| } |
| #endif /* defined(PNG_READ_sBIT_SUPPORTED)&&defined(PNG_WRITE_sBIT_SUPPORTED) */ |
| |
| #if defined(PNG_sCAL_SUPPORTED) |
| #ifdef PNG_FLOATING_POINT_SUPPORTED |
| { |
| int unit; |
| double scal_width, scal_height; |
| |
| if (png_get_sCAL |
| (read_ptr, read_info_ptr, &unit, &scal_width, |
| &scal_height)) { |
| png_set_sCAL(write_ptr, write_info_ptr, unit, |
| scal_width, scal_height); |
| } |
| } |
| #else |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| { |
| int unit; |
| png_charp scal_width, scal_height; |
| |
| if (png_get_sCAL_s |
| (read_ptr, read_info_ptr, &unit, &scal_width, |
| &scal_height)) { |
| if (keep_chunk("sCAL", argv)) |
| png_set_sCAL_s(write_ptr, write_info_ptr, unit, |
| scal_width, scal_height); |
| } |
| } |
| #endif |
| #endif /* PNG_FLOATING_POINT_SUPPORTED */ |
| #endif /* ?PNG_sCAL_SUPPORTED */ |
| |
| #if defined(PNG_sPLT_SUPPORTED) |
| { |
| png_sPLT_tp entries; |
| int num_entries; |
| |
| num_entries = |
| (int) png_get_sPLT(read_ptr, read_info_ptr, |
| &entries); |
| if (num_entries) { |
| if (keep_chunk("sPLT", argv)) |
| png_set_sPLT(write_ptr, write_info_ptr, |
| entries, num_entries); |
| png_free_data(read_ptr, read_info_ptr, |
| PNG_FREE_SPLT, num_entries); |
| } |
| } |
| #endif |
| |
| #if defined(PNG_TEXT_SUPPORTED) |
| { |
| png_textp text_ptr; |
| int num_text = 0; |
| |
| if (png_get_text |
| (read_ptr, read_info_ptr, &text_ptr, &num_text) > 0 |
| || text_inputs) { |
| int ntext; |
| P1( "Handling %d tEXt/zTXt chunks\n", |
| num_text); |
| |
| if (verbose > 1 && first_trial && num_text > 0) { |
| for (ntext = 0; ntext < num_text; ntext++) { |
| fprintf(STDERR, "%d %s", ntext, |
| text_ptr[ntext].key); |
| if (text_ptr[ntext].text_length) |
| fprintf(STDERR, ": %s\n", |
| text_ptr[ntext].text); |
| #ifdef PNG_iTXt_SUPPORTED |
| else if (text_ptr[ntext].itxt_length) { |
| fprintf(STDERR, " (%s: %s): \n", |
| text_ptr[ntext].lang, |
| text_ptr[ntext].lang_key); |
| fprintf(STDERR, "%s\n", |
| text_ptr[ntext].text); |
| } |
| #endif |
| else |
| fprintf(STDERR, "\n"); |
| } |
| } |
| |
| if (num_text > 0) { |
| if (keep_chunk("text", argv)) { |
| int num_to_write = num_text; |
| for (ntext = 0; ntext < num_text; ntext++) { |
| if (first_trial) |
| P2("Text chunk before IDAT, " |
| "compression=%d\n", |
| text_ptr[ntext].compression); |
| if (text_ptr[ntext].compression == |
| PNG_TEXT_COMPRESSION_NONE) { |
| if (!keep_chunk("tEXt", argv)) { |
| text_ptr[ntext].key[0] = '\0'; |
| num_to_write--; |
| } |
| } |
| if (text_ptr[ntext].compression == |
| PNG_TEXT_COMPRESSION_zTXt) { |
| if (!keep_chunk("zTXt", argv)) { |
| text_ptr[ntext].key[0] = '\0'; |
| num_to_write--; |
| } |
| } |
| #ifdef PNG_iTXt_SUPPORTED |
| if (text_ptr[ntext].compression == |
| PNG_ITXT_COMPRESSION_NONE |
| || text_ptr[ntext].compression == |
| PNG_ITXT_COMPRESSION_zTXt) { |
| if (!keep_chunk("iTXt", argv)) { |
| text_ptr[ntext].key[0] = '\0'; |
| num_to_write--; |
| } |
| } |
| #endif |
| } |
| if (num_to_write > 0) |
| png_set_text(write_ptr, write_info_ptr, |
| text_ptr, num_text); |
| } |
| } |
| for (ntext = 0; ntext < text_inputs; ntext++) { |
| if (text_where[ntext] == 1) { |
| png_textp added_text; |
| added_text = (png_textp) png_malloc(write_ptr, |
| (png_uint_32) sizeof(png_text)); |
| added_text[0].key = &text_keyword[ntext * 80]; |
| #ifdef PNG_iTXt_SUPPORTED |
| added_text[0].lang = &text_lang[ntext * 80]; |
| added_text[0].lang_key = |
| &text_lang_key[ntext * 80]; |
| #endif |
| added_text[0].text = &text_text[ntext * 2048]; |
| added_text[0].compression = |
| text_compression[ntext]; |
| png_set_text(write_ptr, write_info_ptr, |
| added_text, 1); |
| if (added_text[0].compression < 0) |
| printf(" Added a tEXt chunk.\n"); |
| else if (added_text[0].compression == 0) |
| printf(" Added a zTXt chunk.\n"); |
| #ifdef PNG_iTXt_SUPPORTED |
| else |
| printf(" Added a%scompressed iTXt chunk" |
| ".\n", (added_text[0].compression == 1)? |
| "n un" : " "); |
| #endif |
| png_free(write_ptr, added_text); |
| added_text = (png_textp) NULL; |
| } |
| } |
| } |
| } |
| #endif /* defined(PNG_TEXT_SUPPORTED) */ |
| |
| #if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED) |
| { |
| png_timep mod_time; |
| |
| if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) { |
| if (keep_chunk("tIME", argv)) |
| png_set_tIME(write_ptr, write_info_ptr, mod_time); |
| } |
| } |
| #endif |
| |
| #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED |
| /* This section handles pCAL and tIME (at least, in default |
| * build), gIFx/gIFg/gIFt, private Fireworks chunks, etc. */ |
| { |
| png_unknown_chunkp unknowns; /* allocated by libpng */ |
| int num_unknowns; |
| |
| if (nosave == 0 && ster_mode >= 0) { |
| /* Add sTER chunk */ |
| png_unknown_chunkp ster; |
| P1("Handling sTER as unknown chunk %d\n", i); |
| ster = (png_unknown_chunk*)png_malloc(read_ptr, |
| (png_uint_32) sizeof(png_unknown_chunk)); |
| png_memcpy((char *)ster[0].name, "sTER",5); |
| ster[0].size = 1; |
| ster[0].data = (png_byte*)png_malloc(read_ptr, 1); |
| ster[0].data[0] = (png_byte)ster_mode; |
| png_set_unknown_chunks(read_ptr, read_info_ptr, |
| ster, 1); |
| png_free(read_ptr,ster[0].data); |
| png_free(read_ptr,ster); |
| } |
| |
| num_unknowns = (int)png_get_unknown_chunks(read_ptr, |
| read_info_ptr, &unknowns); |
| |
| #ifndef PNG_HAVE_IHDR |
| #define PNG_HAVE_IHDR 0x01 |
| #endif |
| if (ster_mode >= 0) |
| png_set_unknown_chunk_location(read_ptr, read_info_ptr, |
| num_unknowns - 1, (int)PNG_HAVE_IHDR); |
| |
| P1("Found %d unknown chunks\n", num_unknowns); |
| |
| if (nosave == 0 && num_unknowns) { |
| png_unknown_chunkp unknowns_keep; /* allocated by us */ |
| int num_unknowns_keep; |
| |
| unknowns_keep = (png_unknown_chunk*)png_malloc( |
| write_ptr, (png_uint_32) num_unknowns |
| *sizeof(png_unknown_chunk)); |
| |
| P1("malloc for %d unknown chunks\n", num_unknowns); |
| num_unknowns_keep = 0; |
| |
| /* make an array of only those chunks we want to keep */ |
| for (i = 0; i < num_unknowns; i++) { |
| P1("Handling unknown chunk %d %s\n", i, |
| (char *)unknowns[i].name); |
| /* not EBCDIC-safe, but neither is keep_chunks(): */ |
| P2(" unknown[%d] = %s (%lu bytes, location %d)\n", |
| i, unknowns[i].name, |
| (unsigned long)unknowns[i].size, |
| unknowns[i].location); |
| if (keep_chunk((char *)unknowns[i].name, argv)) { |
| png_memcpy(&unknowns_keep[num_unknowns_keep], |
| &unknowns[i], sizeof(png_unknown_chunk)); |
| ++num_unknowns_keep; |
| } |
| } |
| |
| P1("Keeping %d unknown chunks\n", num_unknowns_keep); |
| png_set_unknown_chunks(write_ptr, write_info_ptr, |
| unknowns_keep, num_unknowns_keep); |
| |
| /* relevant location bits: |
| * (1) !PNG_HAVE_PLTE && !PNG_HAVE_IDAT (before PLTE) |
| * (2) PNG_HAVE_PLTE && !PNG_HAVE_IDAT (between) |
| * (3) PNG_AFTER_IDAT (after IDAT) |
| * PNG_HAVE_PLTE = 0x02 |
| * PNG_HAVE_IDAT = 0x04 |
| * PNG_AFTER_IDAT = 0x08 |
| */ |
| for (i = 0; i < num_unknowns_keep; i++) { |
| png_set_unknown_chunk_location(write_ptr, |
| write_info_ptr, i, |
| (int)unknowns_keep[i].location); |
| } |
| |
| /* png_set_unknown_chunks() makes own copy, so nuke |
| * ours */ |
| png_free(write_ptr, unknowns_keep); |
| } |
| } |
| P0("unknown chunk handling done.\n"); |
| #endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ |
| |
| /* } GRR added for quick %-navigation (1) */ |
| |
| png_read_transform_info(read_ptr, read_info_ptr); |
| |
| |
| /* this is the default case (nosave == 1 -> perf-testing |
| only) */ |
| if (nosave == 0) { |
| |
| if (filter_type == 0) |
| png_set_filter(write_ptr, 0, PNG_FILTER_NONE); |
| else if (filter_type == 1) |
| png_set_filter(write_ptr, 0, PNG_FILTER_SUB); |
| else if (filter_type == 2) |
| png_set_filter(write_ptr, 0, PNG_FILTER_UP); |
| else if (filter_type == 3) |
| png_set_filter(write_ptr, 0, PNG_FILTER_AVG); |
| else if (filter_type == 4) |
| png_set_filter(write_ptr, 0, PNG_FILTER_PAETH); |
| else if (filter_type == 5) |
| png_set_filter(write_ptr, 0, PNG_ALL_FILTERS); |
| else |
| png_set_filter(write_ptr, 0, PNG_FILTER_NONE); |
| |
| |
| /* GRR 20050220: not clear why unknowns treated differently from other chunks */ |
| /* (i.e., inside nosave==0 block)... Moved up 50 lines now. */ |
| #if 0 /* #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ |
| { |
| png_unknown_chunkp unknowns; |
| int num_unknowns = (int) png_get_unknown_chunks( |
| read_ptr, read_info_ptr, &unknowns); |
| |
| P1("Keeping %d unknown chunks\n", num_unknowns); |
| if (num_unknowns) { |
| png_set_unknown_chunks(write_ptr, write_info_ptr, |
| unknowns, num_unknowns); |
| for (i = 0; i < num_unknowns; i++) { |
| P2(" unknown[%d] = %s\n", i, unknowns[i].name); |
| png_set_unknown_chunk_location(write_ptr, |
| write_info_ptr, i, (int)unknowns[i].location); |
| } |
| } |
| } |
| #endif /* 0, was PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ |
| |
| #ifdef PNGCRUSH_LOCO |
| if (do_loco) { |
| png_byte buff[30]; |
| const png_byte png_MHDR[5] = { 77, 72, 68, 82, '\0' }; |
| png_byte mng_signature[8] = |
| { 138, 77, 78, 71, 13, 10, 26, 10 }; |
| /* write the MNG 8-byte signature */ |
| if (outname[strlen(outname) - 3] == 'p') |
| png_warning(read_ptr, |
| " Writing a MNG file with a .png extension"); |
| png_default_write_data(write_ptr, &mng_signature[0], |
| (png_size_t) 8); |
| png_set_sig_bytes(write_ptr, 8); |
| |
| /* Write a MHDR chunk */ |
| |
| buff[0] = (png_byte) ((width >> 24) & 0xff); |
| buff[1] = (png_byte) ((width >> 16) & 0xff); |
| buff[2] = (png_byte) ((width >> 8) & 0xff); |
| buff[3] = (png_byte) ((width) & 0xff); |
| buff[4] = (png_byte) ((height >> 24) & 0xff); |
| buff[5] = (png_byte) ((height >> 16) & 0xff); |
| buff[6] = (png_byte) ((height >> 8) & 0xff); |
| buff[7] = (png_byte) ((height) & 0xff); |
| for (i = 8; i < 27; i++) |
| buff[i] = 0x00; |
| buff[15] = 2; /* layer count */ |
| buff[19] = 1; /* frame count */ |
| if (output_color_type == 6) |
| buff[27] = 0x09; /* profile: MNG-VLC with trans. */ |
| else |
| buff[27] = 0x01; /* profile: MNG-VLC */ |
| png_write_chunk(write_ptr, (png_bytep) png_MHDR, |
| buff, (png_size_t) 28); |
| } |
| #endif /* PNGCRUSH_LOCO */ |
| |
| png_crush_pause(); |
| |
| if (found_CgBI) |
| { |
| png_warning(read_ptr, |
| "Cannot read Xcode CgBI PNG. Even if we could,"); |
| png_error(read_ptr, |
| "the original PNG could not be recovered."); |
| } |
| P1( "\nWriting info struct\n"); |
| |
| #if 0 /* doesn't work; compression level has to be the same as in IDAT */ |
| /* if zTXt other compressed chunk */ |
| png_set_compression_level(write_ptr, 9); |
| png_set_compression_window_bits(write_ptr, 15); |
| #endif |
| |
| png_crush_pause(); |
| { |
| int compression_window; |
| png_uint_32 zbuf_size; |
| png_uint_32 required_window; |
| int channels = 0; |
| png_set_compression_strategy(write_ptr, |
| z_strategy); |
| png_set_compression_mem_level(write_ptr, |
| compression_mem_level); |
| |
| if (output_color_type == 0) |
| channels = 1; |
| if (output_color_type == 2) |
| channels = 3; |
| if (output_color_type == 3) |
| channels = 1; |
| if (output_color_type == 4) |
| channels = 2; |
| if (output_color_type == 6) |
| channels = 4; |
| |
| required_window = |
| (png_uint_32) (height * |
| ((width * channels * bit_depth + |
| 15) >> 3) + 262); |
| |
| zbuf_size = |
| png_get_compression_buffer_size(write_ptr); |
| |
| /* reinitialize zbuf - compression buffer */ |
| if (zbuf_size != max_idat_size) { |
| png_uint_32 max_possible_size = |
| required_window; |
| if (max_possible_size > max_idat_size) |
| max_possible_size = max_idat_size; |
| P2("reinitializing write zbuf to %lu.\n", |
| (unsigned long)max_possible_size); |
| png_set_compression_buffer_size(write_ptr, |
| max_possible_size); |
| } |
| |
| #ifdef WBITS_8_OK |
| if (required_window <= 256) |
| compression_window = 8; |
| else if (required_window <= 512) |
| compression_window = 9; |
| #else |
| if (required_window <= 512) |
| compression_window = 9; |
| #endif |
| else if (required_window <= 1024) |
| compression_window = 10; |
| else if (required_window <= 2048) |
| compression_window = 11; |
| else if (required_window <= 4096) |
| compression_window = 12; |
| else if (required_window <= 8192) |
| compression_window = 13; |
| else if (required_window <= 16384) |
| compression_window = 14; |
| else |
| compression_window = 15; |
| if (compression_window > default_compression_window |
| || force_compression_window) |
| compression_window = |
| default_compression_window; |
| |
| if (verbose > 1 && first_trial |
| && (compression_window != 15 |
| || force_compression_window)) |
| fprintf(STDERR, |
| " Compression window for output= %d\n", |
| 1 << compression_window); |
| |
| png_set_compression_window_bits(write_ptr, |
| compression_window); |
| } |
| |
| png_set_compression_level(write_ptr, zlib_level); |
| png_write_info(write_ptr, write_info_ptr); |
| P1( "\nWrote info struct\n"); |
| #ifdef PNG_WRITE_PACK_SUPPORTED |
| if (output_bit_depth < 8) { |
| if (output_color_type == 0) { |
| png_color_8 true_bits; |
| true_bits.gray = (png_byte) (output_bit_depth); |
| png_set_shift(write_ptr, &true_bits); |
| } |
| png_set_packing(write_ptr); |
| } |
| #endif |
| } /* no save */ |
| #define LARGE_PNGCRUSH |
| |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| rows_at_a_time = max_rows_at_a_time; |
| if (rows_at_a_time == 0 || rows_at_a_time < height) |
| rows_at_a_time = height; |
| #endif |
| |
| #ifndef LARGE_PNGCRUSH |
| { |
| png_uint_32 rowbytes_s; |
| png_uint_32 rowbytes; |
| |
| rowbytes = png_get_rowbytes(read_ptr, read_info_ptr); |
| |
| rowbytes_s = (png_size_t) rowbytes; |
| if (rowbytes == (png_uint_32) rowbytes_s) |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| row_buf = |
| png_malloc(read_ptr, |
| rows_at_a_time * rowbytes + 16); |
| #else |
| row_buf = png_malloc(read_ptr, rowbytes + 16); |
| #endif |
| else |
| row_buf = NULL; |
| } |
| #else |
| { |
| png_uint_32 read_row_length, write_row_length; |
| read_row_length = |
| (png_uint_32) (png_get_rowbytes |
| (read_ptr, read_info_ptr)); |
| write_row_length = |
| (png_uint_32) (png_get_rowbytes |
| (write_ptr, write_info_ptr)); |
| row_length = |
| read_row_length > |
| write_row_length ? read_row_length : |
| write_row_length; |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| row_buf = |
| (png_bytep) png_malloc(read_ptr, |
| rows_at_a_time * |
| row_length + 16); |
| #else |
| row_buf = |
| (png_bytep) png_malloc(read_ptr, row_length + 16); |
| #endif |
| } |
| #endif |
| |
| if (row_buf == NULL) |
| png_error(read_ptr, |
| "Insufficient memory to allocate row buffer"); |
| |
| { |
| /* check for sufficient memory: we need 2*zlib_window and, |
| if filter_type == 5, 4*rowbytes in separate allocations. |
| If it's not enough we can drop the "average" filter and |
| we can reduce the zlib_window for writing. We can't |
| change the input zlib_window because the input file |
| might have used the full 32K sliding window. |
| */ |
| } |
| |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| row_pointers = (png_bytepp) png_malloc(read_ptr, |
| rows_at_a_time * |
| sizeof(png_bytepp)); |
| for (i = 0; i < rows_at_a_time; i++) |
| row_pointers[i] = row_buf + i * row_length; |
| #endif |
| |
| P2("allocated rowbuf.\n"); |
| png_crush_pause(); |
| |
| num_pass = png_set_interlace_handling(read_ptr); |
| if (nosave == 0) |
| png_set_interlace_handling(write_ptr); |
| |
| t_stop = (TIME_T) clock(); |
| t_misc += (t_stop - t_start); |
| if (t_stop < t_start) { |
| t_misc += PNG_UINT_31_MAX; |
| if (t_stop < 0) |
| t_misc += PNG_UINT_31_MAX; |
| } |
| t_start = t_stop; |
| for (pass = 0; pass < num_pass; pass++) { |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| png_uint_32 num_rows; |
| #endif |
| P1( "\nBegin interlace pass %d\n", pass); |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| num_rows = rows_at_a_time; |
| for (y = 0; y < height; y += rows_at_a_time) |
| #else |
| for (y = 0; y < height; y++) |
| #endif |
| { |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| if (y + num_rows > height) |
| num_rows = height - y; |
| png_read_rows(read_ptr, row_pointers, |
| (png_bytepp) NULL, num_rows); |
| #else |
| png_read_row(read_ptr, row_buf, (png_bytep) NULL); |
| #endif |
| if (nosave == 0) { |
| t_stop = (TIME_T) clock(); |
| t_decode += (t_stop - t_start); |
| if (t_stop < t_start) { |
| t_decode += PNG_UINT_31_MAX; |
| if (t_stop < 0) |
| t_decode += PNG_UINT_31_MAX; |
| } |
| t_start = t_stop; |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| png_write_rows(write_ptr, row_pointers, |
| num_rows); |
| #else |
| png_write_row(write_ptr, row_buf); |
| #endif |
| t_stop = (TIME_T) clock(); |
| t_encode += (t_stop - t_start); |
| if (t_stop < t_start) { |
| t_encode += PNG_UINT_31_MAX; |
| if (t_stop < 0) |
| t_encode += PNG_UINT_31_MAX; |
| } |
| t_start = t_stop; |
| } |
| } |
| P2( "End interlace pass %d\n\n", pass); |
| } |
| if (nosave) { |
| t_stop = (TIME_T) clock(); |
| t_decode += (t_stop - t_start); |
| if (t_stop < t_start) { |
| t_decode += PNG_UINT_31_MAX; |
| if (t_stop < 0) |
| t_decode += PNG_UINT_31_MAX; |
| } |
| t_start = t_stop; |
| } |
| |
| #if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) && \ |
| defined(PNG_FLOATING_POINT_SUPPORTED) |
| if ((color_type == 2 || color_type == 6 || color_type == 3) |
| && (output_color_type == 0 || output_color_type == 4)) |
| { |
| png_byte rgb_error = |
| png_get_rgb_to_gray_status(read_ptr); |
| if ((first_trial) && rgb_error) |
| printf( |
| " **** Converted non-gray image to gray. **** \n"); |
| } |
| #endif |
| |
| #ifdef PNG_FREE_UNKN |
| # if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) |
| png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); |
| # endif |
| # if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) |
| png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); |
| # endif |
| #else |
| # if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) |
| png_free_unknown_chunks(read_ptr, read_info_ptr, -1); |
| # endif |
| # if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) |
| png_free_unknown_chunks(write_ptr, write_info_ptr, -1); |
| # endif |
| #endif |
| P1( "Reading and writing end_info data\n"); |
| png_read_end(read_ptr, end_info_ptr); |
| |
| /* { GRR: added for %-navigation (2) */ |
| |
| #if (defined(PNG_READ_tEXt_SUPPORTED) && defined(PNG_WRITE_tEXt_SUPPORTED)) \ |
| || (defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED)) |
| { |
| png_textp text_ptr; |
| int num_text = 0; |
| |
| if (png_get_text |
| (read_ptr, end_info_ptr, &text_ptr, &num_text) > 0 |
| || text_inputs) { |
| int ntext; |
| P1( "Handling %d tEXt/zTXt chunks\n", |
| num_text); |
| |
| if (verbose > 1 && first_trial && num_text > 0) { |
| for (ntext = 0; ntext < num_text; ntext++) { |
| fprintf(STDERR, "%d %s", ntext, |
| text_ptr[ntext].key); |
| if (text_ptr[ntext].text_length) |
| fprintf(STDERR, ": %s\n", |
| text_ptr[ntext].text); |
| #ifdef PNG_iTXt_SUPPORTED |
| else if (text_ptr[ntext].itxt_length) { |
| fprintf(STDERR, " (%s: %s): \n", |
| text_ptr[ntext].lang, |
| text_ptr[ntext].lang_key); |
| fprintf(STDERR, "%s\n", |
| text_ptr[ntext].text); |
| } |
| #endif |
| else |
| fprintf(STDERR, "\n"); |
| } |
| } |
| if (nosave) { |
| 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; |
| } |
| } |
| } /* end of nosave block */ |
| } |
| } |
| #endif /* (PNG_READ_tEXt_SUPPORTED and PNG_WRITE_tEXt_SUPPORTED) or */ |
| /* (PNG_READ_zTXt_SUPPORTED and PNG_WRITE_zTXt_SUPPORTED) */ |
| #if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED) |
| { |
| png_timep mod_time; |
| |
| if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) { |
| if (keep_chunk("tIME", argv)) |
| png_set_tIME(write_ptr, write_end_info_ptr, |
| mod_time); |
| } |
| } |
| #endif |
| |
| #if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) |
| /* GRR FIXME? this block may need same fix as above */ |
| { |
| png_unknown_chunkp unknowns; |
| int num_unknowns = |
| (int) png_get_unknown_chunks(read_ptr, |
| end_info_ptr, |
| &unknowns); |
| if (num_unknowns && nosave == 0) { |
| printf("setting %d unknown chunks after IDAT\n", |
| num_unknowns); |
| png_set_unknown_chunks(write_ptr, |
| write_end_info_ptr, |
| unknowns, num_unknowns); |
| for (i = 0; i < num_unknowns; i++) |
| png_set_unknown_chunk_location(write_ptr, |
| write_end_info_ptr, |
| i, |
| (int) |
| unknowns[i]. |
| location); |
| } |
| } |
| #endif |
| /* } GRR: added for %-navigation (2) */ |
| |
| if (nosave == 0) { |
| #if 0 /* doesn't work; compression level has to be the same as in IDAT */ |
| /* if zTXt other compressed chunk */ |
| png_set_compression_level(write_ptr, 9); |
| png_set_compression_window_bits(write_ptr, 15); |
| png_set_compression_buffer_size(write_ptr, |
| PNG_ZBUF_SIZE); |
| png_set_compression_strategy(write_ptr, 0); |
| #endif |
| png_write_end(write_ptr, write_end_info_ptr); |
| } |
| |
| P1( "Destroying data structs\n"); |
| if (row_buf != (png_bytep) NULL) { |
| png_free(read_ptr, row_buf); |
| row_buf = (png_bytep) NULL; |
| } |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| if (row_pointers != (png_bytepp) NULL) { |
| png_free(read_ptr, row_pointers); |
| row_pointers = (png_bytepp) NULL; |
| } |
| #endif |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, |
| &end_info_ptr); |
| if (nosave == 0) { |
| #ifdef PNGCRUSH_LOCO |
| if (do_loco) { |
| const png_byte png_MEND[5] = |
| { 77, 69, 78, 68, '\0' }; |
| /* write the MNG MEND chunk */ |
| png_write_chunk(write_ptr, (png_bytep) png_MEND, |
| NULL, (png_size_t) 0); |
| } |
| #endif |
| png_destroy_info_struct(write_ptr, |
| &write_end_info_ptr); |
| png_destroy_write_struct(&write_ptr, &write_info_ptr); |
| } |
| } |
| Catch(msg) { |
| if (nosave == 0) |
| fprintf(stderr, "While converting %s to %s:\n", inname, |
| outname); |
| else |
| fprintf(stderr, "While reading %s:\n", inname); |
| fprintf(stderr, |
| " pngcrush caught libpng error:\n %s\n\n", msg); |
| if (row_buf) { |
| png_free(read_ptr, row_buf); |
| row_buf = (png_bytep) NULL; |
| } |
| #ifdef PNGCRUSH_MULTIPLE_ROWS |
| if (row_pointers != (png_bytepp) NULL) { |
| png_free(read_ptr, row_pointers); |
| row_pointers = (png_bytepp) NULL; |
| } |
| #endif |
| if (nosave == 0) { |
| png_destroy_info_struct(write_ptr, |
| &write_end_info_ptr); |
| png_destroy_write_struct(&write_ptr, &write_info_ptr); |
| setfiletype(outname); |
| } |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, |
| &end_info_ptr); |
| if (verbose > 1) |
| fprintf(stderr, "returning after cleanup\n"); |
| trial = MAX_METHODS + 1; |
| } |
| |
| read_ptr = NULL; |
| write_ptr = NULL; |
| FCLOSE(fpin); |
| if (nosave == 0) { |
| FCLOSE(fpout); |
| setfiletype(outname); |
| } |
| |
| if (nosave) |
| break; |
| |
| first_trial = 0; |
| |
| if (nosave == 0) { |
| P1( "Opening file for length measurement\n"); |
| if ((fpin = FOPEN(outname, "rb")) == NULL) { |
| fprintf(STDERR, "Could not find output file %s\n", outname); |
| if (png_row_filters != NULL) { |
| free(png_row_filters); |
| png_row_filters = NULL; |
| } |
| exit(1); |
| } |
| number_of_open_files++; |
| |
| idat_length[trial] = measure_idats(fpin); |
| |
| FCLOSE(fpin); |
| } |
| |
| if (verbose > 0 && trial != MAX_METHODS) { |
| fprintf(STDERR, |
| " IDAT length with method %3d (fm %d zl %d zs %d) = %8lu\n", |
| trial, filter_type, zlib_level, z_strategy, |
| (unsigned long)idat_length[trial]); |
| fflush(STDERR); |
| } |
| |
| } /* end of trial-loop */ |
| |
| P1("\n\nFINISHED MAIN LOOP OVER %d METHODS\n\n\n", MAX_METHODS); |
| |
| /* //////////////////////////////////////////////////////////////////// |
| ////////////////// //////////////////// |
| ////////////////// END OF MAIN LOOP OVER METHODS //////////////////// |
| ////////////////// //////////////////// |
| //////////////////////////////////////////////////////////////////// */ |
| } |
| |
| if (fpin) { |
| FCLOSE(fpin); |
| } |
| if (nosave == 0 && fpout) { |
| FCLOSE(fpout); |
| setfiletype(outname); |
| } |
| |
| if (nosave == 0) { |
| png_uint_32 input_length, output_length; |
| #ifndef __riscos |
| struct stat stat_buf; |
| struct utimbuf utim; |
| |
| stat(inname, &stat_buf); |
| input_length = (unsigned long) stat_buf.st_size; |
| utim.actime = stat_buf.st_atime; |
| utim.modtime = stat_buf.st_mtime; |
| stat(outname, &stat_buf); |
| output_length = (unsigned long) stat_buf.st_size; |
| if (new_time_stamp == 0) { |
| /* set file timestamp (no big deal if fails) */ |
| utime(outname, &utim); |
| } |
| #else |
| input_length = (unsigned long) filesize(inname); |
| output_length = (unsigned long) filesize(outname); |
| #endif |
| if (verbose > 0) { |
| total_input_length += input_length + output_length; |
| |
| if (!already_crushed && !image_is_immutable) { |
| fprintf(STDERR, " Best pngcrush method = %d (fm %d zl %d zs %d) " |
| "for %s\n", best, fm[best], lv[best], zs[best], outname); |
| } |
| if (idat_length[0] == idat_length[best]) |
| fprintf(STDERR, " (no IDAT change)\n"); |
| else if (idat_length[0] > idat_length[best]) |
| fprintf(STDERR, " (%4.2f%% IDAT reduction)\n", |
| (100.0 - (100.0 * idat_length[best]) / idat_length[0])); |
| else |
| fprintf(STDERR, " (%4.2f%% IDAT increase)\n", |
| -(100.0 - (100.0 * idat_length[best]) / idat_length[0])); |
| if (input_length == output_length) |
| fprintf(STDERR, " (no filesize change)\n\n"); |
| else if (input_length > output_length) |
| fprintf(STDERR, " (%4.2f%% filesize reduction)\n\n", |
| (100.0 - (100.0 * output_length) / input_length)); |
| else |
| fprintf(STDERR, " (%4.2f%% filesize increase)\n\n", |
| -(100.0 - (100.0 * output_length) / input_length)); |
| |
| if (verbose > 2) |
| fprintf(STDERR, " Number of open files=%d\n", |
| number_of_open_files); |
| |
| } |
| } |
| |
| if (pngcrush_mode == DEFAULT_MODE) { |
| if (png_row_filters != NULL) { |
| free(png_row_filters); |
| png_row_filters = NULL; |
| } |
| if (verbose > 0) |
| show_result(); |
| #ifdef PNG_iCCP_SUPPORTED |
| if (iccp_length) |
| free(iccp_text); |
| #endif |
| if (pngcrush_must_exit) |
| exit(0); |
| return 0; |
| } |
| } /* end of loop on input files */ |
| |
| return 0; /* just in case */ |
| |
| } /* end of main() */ |
| |
| |
| |
| |
| png_uint_32 measure_idats(FILE * fp_in) |
| { |
| /* Copyright (C) 1999-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net) |
| See notice in pngcrush.c for conditions of use and distribution */ |
| P2("\nmeasure_idats:\n"); |
| P1( "Allocating read structure\n"); |
| /* OK to ignore any warning about the address of exception__prev in "Try" */ |
| Try { |
| read_ptr = |
| png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, |
| (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL); |
| P1( "Allocating read_info, end_info structures\n"); |
| read_info_ptr = png_create_info_struct(read_ptr); |
| end_info_ptr = png_create_info_struct(read_ptr); |
| |
| #if !defined(PNG_NO_STDIO) |
| png_init_io(read_ptr, fp_in); |
| #else |
| png_set_read_fn(read_ptr, (png_voidp) fp_in, (png_rw_ptr) NULL); |
| #endif |
| |
| png_set_sig_bytes(read_ptr, 0); |
| measured_idat_length = png_measure_idat(read_ptr); |
| P2("measure_idats: IDAT length=%lu\n", |
| (unsigned long)measured_idat_length); |
| P1( "Destroying data structs\n"); |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); |
| } |
| Catch(msg) { |
| fprintf(STDERR, "\nWhile measuring IDATs in %s ", inname); |
| fprintf(STDERR, "pngcrush caught libpng error:\n %s\n\n", msg); |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); |
| P1( "Destroyed data structs\n"); |
| measured_idat_length = 0; |
| } |
| return measured_idat_length; |
| } |
| |
| |
| |
| |
| |
| png_uint_32 png_measure_idat(png_structp png_ptr) |
| { |
| /* Copyright (C) 1999-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net) |
| See notice in pngcrush.c for conditions of use and distribution */ |
| png_uint_32 sum_idat_length = 0; |
| png_byte *bb = NULL; |
| png_uint_32 malloced_length=0; |
| |
| { |
| png_byte png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; |
| #if defined(PNGCRUSH_LOCO) |
| png_byte mng_signature[8] = { 138, 77, 78, 71, 13, 10, 26, 10 }; |
| #endif |
| |
| png_default_read_data(png_ptr, png_signature, 8); |
| png_set_sig_bytes(png_ptr, 8); |
| |
| #if defined(PNGCRUSH_LOCO) |
| if (!(int) (png_memcmp(mng_signature, png_signature, 8))) { |
| const png_byte png_MHDR[5] = { 77, 72, 68, 82, '\0' }; |
| |
| int b; |
| png_byte buff[40]; |
| unsigned long length; |
| /* read the MHDR */ |
| png_default_read_data(read_ptr, buff, 4); |
| length=buff[3]+(buff[2]<<8)+(buff[1]<<16)+(buff[0]<<24); |
| png_default_read_data(read_ptr, buff, 4); |
| printf("Reading %c%c%c%c chunk.\n",buff[0],buff[1], |
| buff[2],buff[3]); |
| for (b=0; b<40; b++) |
| buff[b]='\0'; |
| png_default_read_data(read_ptr, buff, length); |
| if (verbose) { |
| printf(" width=%lu\n",(unsigned long)(buff[3]+(buff[2]<<8) |
| +(buff[1]<<16)+(buff[0]<<24))); |
| printf(" height=%lu\n",(unsigned long)(buff[7]+(buff[6]<<8) |
| +(buff[5]<<16)+(buff[4]<<24))); |
| printf(" ticksps=%lu\n",(unsigned long)(buff[11]+ |
| (buff[10]<<8)+(buff[9]<<16)+(buff[8]<<24))); |
| printf(" nomlayc=%lu\n",(unsigned long)(buff[15]+ |
| (buff[14]<<8)+(buff[13]<<16)+(buff[12]<<24))); |
| printf(" nomfram=%lu\n",(unsigned long)(buff[19]+ |
| (buff[18]<<8)+(buff[17]<<16)+(buff[16]<<24))); |
| printf(" nomplay=%lu\n",(unsigned long)(buff[23]+ |
| (buff[22]<<8)+(buff[21]<<16)+(buff[20]<<24))); |
| printf(" profile=%lu\n",(unsigned long)(buff[27]+ |
| (buff[26]<<8)+(buff[25]<<16)+(buff[24]<<24))); |
| } |
| |
| if (new_mng) { |
| /* write the MNG 8-byte signature */ |
| png_default_write_data(mng_ptr, &mng_signature[0], |
| (png_size_t) 8); |
| |
| /* Write a MHDR chunk */ |
| png_write_chunk(mng_ptr, (png_bytep) png_MHDR, |
| buff, (png_size_t) 28); |
| } |
| |
| png_default_read_data(read_ptr, buff, 4); |
| input_format = 1; |
| |
| } else |
| #endif |
| if (png_sig_cmp(png_signature, 0, 8)) { |
| if (png_sig_cmp(png_signature, 0, 4)) |
| png_error(png_ptr, "Not a PNG file.."); |
| else |
| png_error(png_ptr, |
| "PNG file corrupted by ASCII conversion"); |
| } |
| } |
| |
| if (fix) |
| { |
| #ifdef PNG_CRC_WARN_USE |
| png_set_crc_action(png_ptr, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); |
| #endif |
| #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR |
| inflateUndermine(&png_ptr->zstream, 1); |
| #endif |
| } |
| |
| for (;;) { |
| #ifndef PNG_UINT_IDAT |
| #ifdef PNG_USE_LOCAL_ARRAYS |
| PNG_IDAT; |
| PNG_IEND; |
| PNG_IHDR; |
| #ifdef PNG_iCCP_SUPPORTED |
| PNG_iCCP; |
| #else |
| const png_byte png_iCCP[5] = { 105, 67, 67, 80, '\0' }; |
| #endif |
| #endif |
| #endif |
| png_byte chunk_name[5]; |
| png_byte chunk_length[4]; |
| png_byte buff[32]; |
| png_uint_32 length; |
| |
| png_default_read_data(png_ptr, chunk_length, 4); |
| length = png_get_uint_31(png_ptr,chunk_length); |
| |
| png_reset_crc(png_ptr); |
| png_crc_read(png_ptr, chunk_name, 4); |
| |
| if (new_mng) { |
| const png_byte png_DHDR[5] = { 68, 72, 68, 82, '\0' }; |
| const png_byte png_DEFI[5] = { 68, 69, 70, 73, '\0' }; |
| const png_byte png_FRAM[5] = { 70, 82, 65, 77, '\0' }; |
| const png_byte png_nEED[5] = { 110, 69, 69, 68, '\0' }; |
| if (!png_memcmp(chunk_name, png_nEED, 4)) { |
| /* Skip the nEED chunk */ |
| printf (" skipping MNG %c%c%c%c chunk, %lu bytes\n",chunk_name[0], |
| chunk_name[1],chunk_name[2],chunk_name[3],(unsigned long)length); |
| } |
| else { |
| /* copy the chunk. */ |
| printf (" reading MNG %c%c%c%c chunk, %lu bytes\n",chunk_name[0], |
| chunk_name[1],chunk_name[2],chunk_name[3],(unsigned long)length); |
| if (length > malloced_length) { |
| png_free(mng_ptr,bb); |
| printf (" png_malloc %lu bytes.\n",(unsigned long)length); |
| bb=(png_byte*)png_malloc(mng_ptr, length); |
| malloced_length=length; |
| } |
| png_crc_read(png_ptr, bb, length); |
| png_write_chunk(mng_ptr, chunk_name, |
| bb, (png_size_t) length); |
| |
| if (!png_memcmp(chunk_name, png_DHDR, 4)) { |
| if (verbose > 1) { |
| printf(" objid=%lu\n",(unsigned long)(bb[1]+(bb[0]<<8))); |
| printf(" itype=%lu\n",(unsigned long)(bb[2])); |
| printf(" dtype=%lu\n",(unsigned long)(bb[3])); |
| printf(" width=%lu\n",(unsigned long)(bb[7]+(bb[6]<<8) |
| +(bb[5]<<16)+(bb[4]<<24))); |
| printf(" height=%lu\n",(unsigned long)(bb[11]+(bb[10]<<8) |
| +(bb[9]<<16)+(bb[8]<<24))); |
| printf(" xloc=%lu\n",(unsigned long)(bb[15]+(bb[14]<<8) |
| +(bb[13]<<16)+(bb[12]<<24))); |
| printf(" yloc=%lu\n",(unsigned long)(bb[19]+(bb[18]<<8) |
| +(bb[17]<<16)+(bb[16]<<24))); |
| } |
| } |
| |
| if (!png_memcmp(chunk_name, png_DEFI, 4)) { |
| if (verbose > 1) { |
| printf(" objid=%lu\n",(unsigned long)(bb[1]+(bb[0]<<8))); |
| printf(" do_not_show=%lu\n",(unsigned long)(bb[2])); |
| printf(" concrete=%lu\n",(unsigned long)(bb[3])); |
| if (length > 4) { |
| printf(" xloc=%lu\n",(unsigned long)(bb[15]+(bb[14]<<8) |
| +(bb[13]<<16)+(bb[12]<<24))); |
| printf(" yloc=%lu\n",(unsigned long)(bb[19]+(bb[18]<<8) |
| +(bb[17]<<16)+(bb[16]<<24))); |
| if (length > 12) { |
| printf(" l_cb=%lu\n",(unsigned long)(bb[20]+(bb[19]<<8) |
| +(bb[18]<<16)+(bb[17]<<24))); |
| printf(" r_cb=%lu\n",(unsigned long)(bb[24]+(bb[23]<<8) |
| +(bb[22]<<16)+(bb[21]<<24))); |
| } |
| } |
| } |
| } |
| if (!png_memcmp(chunk_name, png_FRAM, 4)) { |
| if (verbose > 1) { |
| printf(" mode=%lu\n",(unsigned long)bb[0]); |
| if (length > 1) { |
| int ib; |
| printf(" name = "); |
| for (ib=0; bb[ib]; ib++) |
| { |
| printf ("%c", bb[ib]); |
| } |
| printf ("\n"); |
| } |
| } |
| } |
| length=0; |
| } |
| } |
| |
| else { |
| #ifdef PNG_UINT_IDAT |
| if (png_get_uint_32(chunk_name) == PNG_UINT_IDAT) |
| #else |
| if (!png_memcmp(chunk_name, png_IDAT, 4)) |
| #endif |
| { |
| sum_idat_length += length; |
| if (length > crushed_idat_size) |
| already_crushed++; |
| } |
| |
| if (verbose > 1) { |
| chunk_name[4] = '\0'; |
| printf("Reading %s chunk, length = %lu.\n", chunk_name, |
| (unsigned long)length); |
| } |
| |
| if (png_get_uint_32(chunk_name) == PNG_UINT_CgBI) |
| { |
| printf(" This is an Xcode CGBI file, not a PNG file.\n"); |
| if (fix) |
| { |
| printf (" Removing the CgBI chunk.\n"); |
| } else { |
| printf (" Try \"pngcrush -fix ...\" to attempt to read it.\n"); |
| } |
| found_CgBI++; |
| nosave++; |
| } |
| |
| |
| #ifdef PNG_UINT_IHDR |
| if (png_get_uint_32(chunk_name) == PNG_UINT_IHDR) |
| #else |
| if (!png_memcmp(chunk_name, png_IHDR, 4)) |
| #endif |
| { |
| /* get the color type */ |
| png_crc_read(png_ptr, buff, 13); |
| length -= 13; |
| input_color_type = buff[9]; |
| } |
| else { |
| if (png_get_uint_32(chunk_name) == PNG_UINT_dSIG) |
| { |
| if (found_any_chunk == 0 && !all_chunks_are_safe) |
| { |
| image_is_immutable=1; |
| } |
| } |
| else |
| found_any_chunk=1; |
| } |
| |
| #ifdef PNG_gAMA_SUPPORTED |
| if (png_get_uint_32(chunk_name) == PNG_UINT_gAMA) |
| found_gAMA=1; |
| #endif |
| |
| #ifdef PNG_cHRM_SUPPORTED |
| if (png_get_uint_32(chunk_name) == PNG_UINT_cHRM) |
| found_cHRM=1; |
| #endif |
| |
| #ifdef PNG_iCCP_SUPPORTED |
| /* check for bad Photoshop iCCP chunk */ |
| #ifdef PNG_UINT_iCCP |
| if (png_get_uint_32(chunk_name) == PNG_UINT_iCCP) |
| #else |
| if (!png_memcmp(chunk_name, png_iCCP, 4)) |
| #endif |
| { |
| /* Check for bad Photoshop iCCP chunk. Libpng will reject the |
| * bad chunk because the Adler-32 bytes are missing, but we check |
| * here to see if it's really the sRGB profile, and if so, set the |
| * "intent" flag and gamma so pngcrush will write an sRGB chunk |
| * and a gamma chunk. |
| */ |
| if (length == 2615) { |
| png_crc_read(png_ptr, buff, 22); |
| length -= 22; |
| buff[23] = 0; |
| if (!strncmp((png_const_charp) buff, "Photoshop ICC profile", |
| 21)) |
| { |
| printf(" Replacing bad Photoshop ICCP chunk with an " |
| "sRGB chunk\n"); |
| #ifdef PNG_gAMA_SUPPORTED |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| image_specified_gamma = 45455L; |
| #else |
| image_specified_gamma = 0.45455; |
| #endif |
| #endif |
| intent = 0; |
| } |
| } |
| } |
| #endif |
| |
| } |
| png_crc_finish(png_ptr, length); |
| |
| #ifdef PNGCRUSH_LOCO |
| #ifdef PNG_UINT_MEND |
| if (png_get_uint_32(chunk_name) == PNG_UINT_MEND) |
| return sum_idat_length; |
| #else |
| { |
| const png_byte png_MEND[5] = |
| { 77, 69, 78, 68, '\0' }; |
| if (!png_memcmp(chunk_name, png_MEND, 4)) { |
| if (new_mng) { |
| png_free(mng_ptr,bb); |
| return (0); |
| } |
| return sum_idat_length; |
| } |
| } |
| #endif |
| #endif |
| |
| |
| if (input_format == 0) { |
| #ifdef PNG_UINT_IEND |
| if (png_get_uint_32(chunk_name) == PNG_UINT_IEND) { |
| #else |
| if (!png_memcmp(chunk_name, png_IEND, 4)) { |
| #endif |
| if (!fix && found_CgBI) |
| return 0; |
| else |
| return sum_idat_length; |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| #ifdef PNGCRUSH_COUNT_COLORS |
| #define USE_HASHCODE |
| int count_colors(FILE * fp_in) |
| { |
| /* Copyright (C) 2000-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net) |
| See notice in pngcrush.c for conditions of use and distribution */ |
| int bit_depth, color_type, interlace_method, filter_method, |
| compression_method; |
| png_uint_32 rowbytes; |
| volatile png_uint_32 channels; |
| |
| int i; |
| int pass, num_pass; |
| int ret; |
| volatile int result, hashmiss, hashinserts; |
| |
| png_uint_32 rgba_frequency[257]; |
| |
| png_uint_32 rgba_hi[257]; /* Actually contains ARGB not RGBA */ |
| #if 0 |
| png_uint_32 rgba_lo[257]; /* Low bytes of ARGB in 16-bit PNGs */ |
| #endif |
| |
| /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ |
| |
| /* start of interlace block */ |
| int png_pass_start[] = { 0, 4, 0, 2, 0, 1, 0 }; |
| |
| /* offset to next interlace block */ |
| int png_pass_inc[] = { 8, 8, 4, 4, 2, 2, 1 }; |
| |
| /* start of interlace block in the y direction */ |
| int png_pass_ystart[] = { 0, 0, 4, 0, 2, 0, 1 }; |
| |
| /* offset to next interlace block in the y direction */ |
| int png_pass_yinc[] = { 8, 8, 8, 4, 4, 2, 2 }; |
| |
| result = 0; |
| reduce_to_gray = 1; |
| it_is_opaque = 1; |
| hashmiss = 0; |
| hashinserts = 0; |
| row_buf = (png_bytep) NULL; |
| |
| num_rgba = 0; |
| for (i = 0; i < 257; i++) { |
| rgba_frequency[i] = 0; |
| } |
| |
| P2("Checking alphas:\n"); |
| P1( "Allocating read structure\n"); |
| Try { |
| read_ptr = |
| png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, |
| (png_error_ptr) png_cexcept_error, |
| (png_error_ptr) NULL); |
| if (read_ptr) { |
| P1( "Allocating read_info structure\n"); |
| read_info_ptr = png_create_info_struct(read_ptr); |
| if (read_info_ptr == NULL) |
| png_destroy_read_struct(&read_ptr, (png_infopp) NULL, |
| (png_infopp) NULL); |
| } else |
| read_info_ptr = NULL; |
| if (read_info_ptr) { |
| |
| #ifdef USE_HASHCODE |
| int hash[16385]; |
| #endif |
| |
| #ifdef USE_HASHCODE |
| for (i = 0; i < 16385; i++) |
| hash[i] = -1; |
| #endif |
| end_info_ptr = NULL; |
| |
| #if !defined(PNG_NO_STDIO) |
| png_init_io(read_ptr, fp_in); |
| #else |
| png_set_read_fn(read_ptr, (png_voidp) fp_in, (png_rw_ptr) NULL); |
| #endif |
| |
| { |
| #if defined(PNGCRUSH_LOCO) |
| png_byte mng_signature[8] = |
| { 138, 77, 78, 71, 13, 10, 26, 10 }; |
| #endif |
| png_byte png_signature[8] = |
| { 137, 80, 78, 71, 13, 10, 26, 10 }; |
| |
| png_default_read_data(read_ptr, png_signature, 8); |
| png_set_sig_bytes(read_ptr, 8); |
| |
| #if defined(PNGCRUSH_LOCO) |
| if (!(int) (png_memcmp(mng_signature, png_signature, 8))) { |
| png_byte buffer[40]; |
| unsigned long length; |
| /* Skip the MHDR chunk. */ |
| png_skip_chunk(read_ptr); |
| 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"); |
| } |
| } |
| |
| if (fix && found_CgBI){ |
| /* Skip the CgBI chunk. */ |
| png_skip_chunk(read_ptr); |
| /* iCCP is probably badly compressed */ |
| png_set_keep_unknown_chunks(read_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep)"iCCP", 1); |
| #ifdef PNG_iTXt_SUPPORTED |
| /* and iTXt */ |
| png_set_keep_unknown_chunks(read_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep)"iTXt", 1); |
| #endif |
| /* zTXt too */ |
| png_set_keep_unknown_chunks(read_ptr, |
| PNG_HANDLE_CHUNK_NEVER, |
| (png_bytep)"zTXt", 1); |
| } |
| |
| png_read_info(read_ptr, read_info_ptr); |
| |
| #ifdef PNG_CRC_QUIET_USE |
| png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, |
| PNG_CRC_QUIET_USE); |
| #endif |
| |
| png_get_IHDR(read_ptr, read_info_ptr, &width, &height, |
| &bit_depth, &color_type, &interlace_method, |
| &compression_method, &filter_method); |
| |
| if (color_type == 2) |
| channels = 3; |
| else if (color_type == 4) |
| channels = 2; |
| else if (color_type == 6) |
| channels = 4; |
| else |
| channels = 1; |
| |
| if (color_type == 0 || color_type == 3 || color_type == 4) |
| reduce_to_gray = 1; |
| |
| if (bit_depth == 8) { |
| if (interlace_method) |
| num_pass = 7; |
| else |
| num_pass = 1; |
| |
| rowbytes = png_get_rowbytes(read_ptr, read_info_ptr); |
| |
| row_buf = png_malloc(read_ptr, rowbytes + 16); |
| |
| for (pass = 0; pass < num_pass; pass++) { |
| png_byte *rp; |
| png_uint_32 pass_height, pass_width, y; |
| P2( "\nBegin count_colors() interlace pass %d\n", pass); |
| |
| if (interlace_method) { |
| pass_height = (height - png_pass_ystart[pass] |
| + png_pass_yinc[pass] - |
| 1) / png_pass_yinc[pass]; |
| pass_width = (width - png_pass_start[pass] |
| + png_pass_inc[pass] - |
| 1) / png_pass_inc[pass]; |
| } else { |
| pass_height = height; |
| pass_width = width; |
| } |
| |
| for (y = 0; y < pass_height; y++) { |
| png_uint_32 x; |
| png_read_row(read_ptr, row_buf, (png_bytep) NULL); |
| if (result < 2 || it_is_opaque || reduce_to_gray) { |
| if (color_type == 2) { |
| for (rp = row_buf, x = 0; x < pass_width; |
| x++, rp += channels) { |
| #ifdef USE_HASHCODE |
| int hashcode; |
| #endif |
| png_uint_32 rgba_high = |
| (255 << 24) | (*(rp) << 16) | |
| (*(rp + 1) << 8) | *(rp + 2); |
| assert(num_rgba < 258); |
| rgba_hi[num_rgba] = rgba_high; |
| |
| if (reduce_to_gray && |
| ((*(rp)) != (*(rp + 1)) |
| || (*(rp)) != (*(rp + 2)))) |
| reduce_to_gray = 0; |
| |
| if (result > 1 || !it_is_opaque) |
| continue; |
| |
| |
| #ifdef USE_HASHCODE |
| /* |
| * R G B mask |
| * 11,111 0,0000, 0000 0x3e00 |
| * 00,000 1,1111, 0000 0x01f0 |
| * 00,000 0,0000, 1111 0x000f |
| * |
| */ |
| |
| hashcode = |
| (int) (((rgba_high >> 10) & 0x3e00) |
| | ((rgba_high >> 7) & |
| 0x01f0) | ((rgba_high >> |
| 4) & |
| 0x000f)); |
| assert(hashcode < 16385); |
| if (hash[hashcode] < 0) { |
| hash[hashcode] = i = num_rgba; |
| if (i > 256) |
| result = 2; |
| else |
| num_rgba++; |
| } else { |
| int start = hash[hashcode]; |
| for (i = start; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += (i - start); |
| if (i == num_rgba) { |
| int j; |
| if (i > 256) |
| result = 2; |
| else { |
| for (j = num_rgba; |
| j > start + 1; j--) { |
| rgba_hi[j] = |
| rgba_hi[j - 1]; |
| rgba_frequency[j] = |
| rgba_frequency[j - |
| 1]; |
| } |
| assert(start + 1 < 258); |
| rgba_hi[start + 1] = |
| rgba_high; |
| rgba_frequency[start + 1] = |
| 0; |
| for (j = 0; j < 16384; j++) |
| if (hash[j] > start) |
| hash[j]++; |
| i = start + 1; |
| hashinserts++; |
| num_rgba++; |
| } |
| } |
| } |
| #else |
| for (i = 0; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += i; |
| if (i > 256) |
| result = 2; |
| else if (i == num_rgba) |
| num_rgba++; |
| #endif |
| assert(i < 258); |
| ++rgba_frequency[i]; |
| } |
| } else if (color_type == 6) { |
| for (rp = row_buf, x = 0; x < pass_width; |
| x++, rp += channels) { |
| #ifdef USE_HASHCODE |
| int hashcode; |
| #endif |
| png_uint_32 rgba_high = |
| (*(rp + 3) << 24) | (*(rp) << 16) | |
| (*(rp + 1) << 8) | *(rp + 2); |
| assert(rp - row_buf + 3 < rowbytes); |
| rgba_hi[num_rgba] = rgba_high; |
| if (reduce_to_gray && |
| ((*(rp)) != (*(rp + 1)) |
| || (*(rp)) != (*(rp + 2)))) |
| reduce_to_gray = 0; |
| if (it_is_opaque && (*(rp + 3)) != 255) |
| it_is_opaque = 0; |
| if (result > 1) |
| continue; |
| #ifdef USE_HASHCODE |
| /* |
| * A R G B mask |
| * 11,1 000,0 000,0 000 0x3800 |
| * 00,0 111,1 000,0 000 0x0780 |
| * 00,0 000,0 111,1 000 0x0078 |
| * 00,0 000,0 000,0 111 0x0007 |
| * |
| */ |
| |
| hashcode = |
| (int) (((rgba_high >> 18) & 0x3800) |
| | ((rgba_high >> 12) & |
| 0x0780) | ((rgba_high >> |
| 8) & 0x0078) |
| | ((rgba_high >> 4) & |
| 0x0007)); |
| assert(hashcode < 16385); |
| if (hash[hashcode] < 0) { |
| hash[hashcode] = i = num_rgba; |
| if (i > 256) |
| result = 2; |
| else |
| num_rgba++; |
| } else { |
| int start = hash[hashcode]; |
| for (i = start; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += (i - start); |
| if (i == num_rgba) { |
| if (i > 256) |
| result = 2; |
| else { |
| int j; |
| for (j = num_rgba; |
| j > start + 1; j--) { |
| rgba_hi[j] = |
| rgba_hi[j - 1]; |
| rgba_frequency[j] = |
| rgba_frequency[j - |
| 1]; |
| } |
| rgba_hi[start + 1] = |
| rgba_high; |
| rgba_frequency[start + 1] = |
| 0; |
| for (j = 0; j < 16384; j++) |
| if (hash[j] > start) |
| hash[j]++; |
| i = start + 1; |
| hashinserts++; |
| num_rgba++; |
| } |
| } |
| } |
| #else |
| for (i = 0; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += i; |
| if (i > 256) |
| result = 2; |
| else if (i == num_rgba) |
| num_rgba++; |
| #endif |
| ++rgba_frequency[i]; |
| } |
| } else if (color_type == 4) { |
| for (rp = row_buf, x = 0; x < pass_width; |
| x++, rp += channels) { |
| #ifdef USE_HASHCODE |
| int hashcode; |
| #endif |
| png_uint_32 rgba_high = |
| (*(rp + 1) << 24) | (*(rp) << 16) | |
| (*(rp) << 8) | (*rp); |
| assert(rp - row_buf + 1 < rowbytes); |
| rgba_hi[num_rgba] = rgba_high; |
| if (it_is_opaque && (*(rp + 1)) != 255) |
| it_is_opaque = 0; |
| #ifdef USE_HASHCODE |
| /* |
| * A G mask |
| * 11,1111, 0000,0000 0x3f00 |
| * 00,0000, 1111,1111 0x00ff |
| * |
| */ |
| |
| hashcode = |
| (int) (((rgba_high >> 18) & 0x3f00) |
| | ((rgba_high >> 4) & |
| 0x00ff)); |
| if (hash[hashcode] < 0) { |
| hash[hashcode] = i = num_rgba; |
| if (i > 256) |
| result = 2; |
| else |
| num_rgba++; |
| } else { |
| int start = hash[hashcode]; |
| for (i = start; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += (i - start); |
| if (i == num_rgba) { |
| if (i > 256) |
| result = 2; |
| else { |
| int j; |
| for (j = num_rgba; |
| j > start + 1; j--) { |
| rgba_hi[j] = |
| rgba_hi[j - 1]; |
| rgba_frequency[j] = |
| rgba_frequency[j - |
| 1]; |
| } |
| rgba_hi[start + 1] = |
| rgba_high; |
| rgba_frequency[start + 1] = |
| 0; |
| for (j = 0; j < 16384; j++) |
| if (hash[j] > start) |
| hash[j]++; |
| i = start + 1; |
| hashinserts++; |
| num_rgba++; |
| } |
| } |
| } |
| #else |
| for (i = 0; i <= num_rgba; i++) |
| if (rgba_high == rgba_hi[i]) |
| break; |
| hashmiss += i; |
| if (i > 256) |
| result = 2; |
| else if (i == num_rgba) |
| num_rgba++; |
| #endif |
| ++rgba_frequency[i]; |
| } |
| } else { /* other color type */ |
| |
| result = 2; |
| } |
| } |
| } |
| P2( "End count_colors() interlace pass %d\n\n", pass); |
| } |
| |
| } else /* (bit_depth != 8) */ { |
| |
| /* TO DO: 16-bit support */ |
| reduce_to_gray = 0; |
| it_is_opaque = 0; |
| result = 0; |
| } |
| |
| png_free(read_ptr, row_buf); |
| row_buf = (png_bytep) NULL; |
| P1( "Destroying data structs\n"); |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, |
| (png_infopp) NULL); |
| } else |
| result = 2; |
| } |
| Catch(msg) { |
| fprintf(STDERR, "\nWhile checking alphas in %s ", inname); |
| fprintf(STDERR, "pngcrush caught libpng error:\n %s\n\n", msg); |
| png_free(read_ptr, row_buf); |
| row_buf = (png_bytep) NULL; |
| png_destroy_read_struct(&read_ptr, &read_info_ptr, |
| (png_infopp) NULL); |
| P1( "Destroyed data structs\n"); |
| result = 2; |
| } |
| if (verbose > 1) { |
| int total = 0; |
| if (num_rgba && num_rgba < 257) { |
| for (i = 0; i < num_rgba; i++) { |
| printf("RGBA=(%3.3d,%3.3d,%3.3d,%3.3d), frequency=%d\n", |
| (int) (rgba_hi[i] >> 16) & 0xff, |
| (int) (rgba_hi[i] >> 8) & 0xff, |
| (int) (rgba_hi[i]) & 0xff, |
| (int) (rgba_hi[i] >> 24) & 0xff, |
| (int) rgba_frequency[i]); |
| total += rgba_frequency[i]; |
| } |
| P2("num_rgba=%d, total pixels=%d\n", num_rgba, total); |
| P2("hashcode misses=%d, inserts=%d\n", hashmiss, hashinserts); |
| } |
| if (color_type == 0 || color_type == 2) |
| it_is_opaque = 0; |
| if (reduction_ok) { |
| if (reduce_to_gray) |
| P1("The truecolor image is all gray and will be reduced.\n"); |
| if (it_is_opaque) |
| P1("The image is opaque and the alpha channel will be " |
| "removed.\n"); |
| } else { |
| if (reduce_to_gray) |
| P1("The truecolor image is all gray and could be reduced.\n"); |
| if (it_is_opaque) |
| P1("The image is opaque and the alpha channel could be " |
| "removed.\n"); |
| if (reduce_to_gray || it_is_opaque) |
| P1("Rerun pngcrush with the \"-reduce\" option to do so.\n"); |
| reduce_to_gray = 0; |
| it_is_opaque = 0; |
| } |
| P2("Finished checking alphas, result=%d\n", result); |
| } |
| ret = result; |
| return (ret); |
| } |
| #endif /* PNGCRUSH_COUNT_COLORS */ |
| |
| |
| |
| |
| |
| void print_version_info(void) |
| { |
| fprintf(STDERR, |
| "\n" |
| " | pngcrush %s\n" |
| /* If you have modified this source, you may insert additional notices |
| * immediately after this sentence: */ |
| " | Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson\n" |
| " | Copyright (C) 2005 Greg Roelofs\n" |
| " | This is a free, open-source program. Permission is irrevocably\n" |
| " | granted to everyone to use this version of pngcrush without\n" |
| " | payment of any fee.\n" |
| " | Executable name is %s\n" |
| " | It was built with libpng version %s, and is\n" |
| " | running with %s" |
| " | Copyright (C) 1998-2004,2006-2009 Glenn Randers-Pehrson,\n" |
| " | Copyright (C) 1996, 1997 Andreas Dilger,\n" |
| " | Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,\n" |
| " | and zlib version %s, Copyright (C) 1998-2002 (or later),\n" |
| " | Jean-loup Gailly and Mark Adler.\n", |
| PNGCRUSH_VERSION, progname, PNG_LIBPNG_VER_STRING, |
| png_get_header_version(NULL), ZLIB_VERSION); |
| |
| #if defined(__GNUC__) |
| fprintf(STDERR, |
| " | It was compiled with gcc version %s", __VERSION__); |
| # if defined(PNG_USE_PNGGCCRD) |
| fprintf(STDERR, |
| " and gas version %s", GAS_VERSION); |
| # endif |
| # if defined(__DJGPP__) |
| fprintf(STDERR, |
| "\n" |
| " | under DJGPP %d.%d, Copyright (C) 1995, D. J. Delorie\n" |
| " | and loaded with PMODE/DJ, by Thomas Pytel and Matthias Grimrath\n" |
| " | Copyright (C) 1996, Matthias Grimrath.\n", |
| __DJGPP__, __DJGPP_MINOR__); |
| # else |
| fprintf(STDERR, ".\n"); |
| # endif |
| #endif |
| |
| fprintf(STDERR, "\n"); |
| } |
| |
| |
| |
| |
| |
| static const char *pngcrush_legal[] = { |
| "", |
| "If you have modified this source, you may insert additional notices", |
| "immediately after this sentence.", |
| "Copyright (C) 1998-2002,2006-2009 Glenn Randers-Pehrson", |
| "Copyright (C) 2005 Greg Roelofs", |
| "", |
| "DISCLAIMER: The pngcrush computer program is supplied \"AS IS\".", |
| "The Author disclaims all warranties, expressed or implied, including,", |
| "without limitation, the warranties of merchantability and of fitness", |
| "for any purpose. The Author assumes no liability for direct, indirect,", |
| "incidental, special, exemplary, or consequential damages, which may", |
| "result from the use of the computer program, even if advised of the", |
| "possibility of such damage. There is no warranty against interference", |
| "with your enjoyment of the computer program or against infringement.", |
| "There is no warranty that my efforts or the computer program will", |
| "fulfill any of your particular purposes or needs. This computer", |
| "program is provided with all faults, and the entire risk of satisfactory", |
| "quality, performance, accuracy, and effort is with the user.", |
| "", |
| "LICENSE: Permission is hereby irrevocably granted to everyone to use,", |
| "copy, modify, and distribute this computer program, or portions hereof,", |
| "purpose, without payment of any fee, subject to the following", |
| "restrictions:", |
| "", |
| "1. The origin of this binary or source code must not be misrepresented.", |
| "", |
| "2. Altered versions must be plainly marked as such and must not be", |
| "misrepresented as being the original binary or source.", |
| "", |
| "3. The Copyright notice, disclaimer, and license may not be removed", |
| "or altered from any source, binary, or altered source distribution.", |
| "" |
| }; |
| |
| static const char *pngcrush_usage[] = { |
| "\nusage: %s [options] infile.png outfile.png\n", |
| " %s -e ext [other options] files.png ...\n", |
| " %s -d dir [other options] files.png ...\n" |
| }; |
| |
| struct options_help pngcrush_options[] = { |
| {0, " -already already_crushed_size [e.g., 8192]"}, |
| {2, ""}, /* blank */ |
| {2, " If file has an IDAT greater than this size, it"}, |
| {2, " will be considered to be already crushed and will"}, |
| {2, " not be processed, unless you are making other changes"}, |
| {2, " or the \"-force\" option is present."}, |
| {2, ""}, |
| |
| {0, " -bit_depth depth (bit_depth to use in output file)"}, |
| {2, ""}, |
| {2, " Default output depth is same as input depth."}, |
| {2, ""}, |
| |
| #ifdef Z_RLE |
| {0, " -brute (use brute-force: try 126 different methods [11-136])"}, |
| #else |
| {0, " -brute (use brute-force: try 114 different methods [11-124])"}, |
| #endif |
| {2, ""}, |
| {2, " Very time-consuming and generally not worthwhile."}, |
| {2, " You can restrict this option to certain filter types,"}, |
| {2, " compression levels, or strategies by following it"}, |
| {2, " with \"-f filter\", \"-l level\", or \"-z strategy\"."}, |
| {2, ""}, |
| |
| {0, " -c color_type of output file [0, 2, 4, or 6]"}, |
| {2, ""}, |
| {2, " Color type for the output file. Future versions"}, |
| {2, " will also allow color_type 3, if there are 256 or"}, |
| {2, " fewer colors present in the input file. Color types"}, |
| {2, " 4 and 6 are padded with an opaque alpha channel if"}, |
| {2, " the input file does not have alpha information."}, |
| {2, " You can use 0 or 4 to convert color to grayscale."}, |
| {2, " Use 0 or 2 to delete an unwanted alpha channel."}, |
| {2, " Default is to use same color type as the input file."}, |
| {2, ""}, |
| |
| #ifdef PNGCRUSH_COUNT_COLORS |
| {0, " -cc (do color counting)"}, |
| {2, ""}, |
| #endif |
| |
| {0, " -d directory_name (where output files will go)"}, |
| {2, ""}, |
| {2, " If a directory name is given, then the output"}, |
| {2, " files are placed in it, with the same filenames as"}, |
| {2, " those of the original files. For example,"}, |
| {2, " you would type 'pngcrush -directory CRUSHED *.png'"}, |
| {2, " to get *.png => CRUSHED/*.png"}, |
| {2, ""}, |
| |
| {0, FAKE_PAUSE_STRING}, |
| |
| {0, " -double_gamma (used for fixing gamma in PhotoShop 5.0/5.02 files)"}, |
| {2, ""}, |
| {2, " It has been claimed that the PS5 bug is actually"}, |
| {2, " more complex than that, in some unspecified way."}, |
| {2, ""}, |
| |
| {0, " -e extension (used for creating output filename)"}, |
| {2, ""}, |
| {2, " e.g., -ext .new means *.png => *.new"}, |
| {2, " and -e _C.png means *.png => *_C.png"}, |
| {2, ""}, |
| |
| {0, " -f user_filter [0-5]"}, |
| {2, ""}, |
| {2, " filter to use with the method specified in the"}, |
| {2, " preceding '-m method' or '-brute_force' argument."}, |
| {2, " 0: none; 1-4: use specified filter; 5: adaptive."}, |
| {2, ""}, |
| |
| {0, " -fix (fix otherwise fatal conditions such as bad CRCs)"}, |
| {2, ""}, |
| |
| {0, " -force (write a new output file even if larger than input)"}, |
| {2, ""}, |
| {2, " Otherwise the input file will be copied to output"}, |
| {2, " if it is smaller than any generated file and no chunk"}, |
| {2, " additions, removals, or changes were requested."}, |
| {2, ""}, |
| |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| {0, " -g gamma (float or fixed*100000, e.g., 0.45455 or 45455)"}, |
| #else |
| {0, " -g gamma (float, e.g., 0.45455)"}, |
| #endif |
| {2, ""}, |
| {2, " Value to insert in gAMA chunk, only if the input"}, |
| {2, " file has no gAMA chunk. To replace an existing"}, |
| {2, " gAMA chunk, use the '-replace_gamma' option."}, |
| {2, ""}, |
| |
| {0, FAKE_PAUSE_STRING}, |
| |
| {0, " -huffman (use only zlib strategy 2, Huffman-only)"}, |
| {2, ""}, |
| {2, " Fast, but almost never very effective except for"}, |
| {2, " certain rare image types."}, |
| {2, ""}, |
| |
| #ifdef PNG_iCCP_SUPPORTED |
| {0, " -iccp length \"Profile Name\" iccp_file"}, |
| {2, ""}, |
| {2, " file with ICC profile to insert in an iCCP chunk."}, |
| {2, ""}, |
| #endif |
| |
| #ifdef PNG_iTXt_SUPPORTED |
| {0, " -itxt b[efore_IDAT]|a[fter_IDAT] \"keyword\""}, |
| {2, " \"language_code\" \"translated_keyword\" \"text\""}, |
| {2, ""}, |
| {2, " Uncompressed iTXt chunk to insert (see -text)."}, |
| {2, ""}, |
| #endif |
| |
| {0, " -keep chunk_name"}, |
| {2, ""}, |
| {2, " keep named chunk even when pngcrush makes"}, |
| {2, " changes to the PNG datastream that cause it"}, |
| {2, " to become invalid. Currently only dSIG is"}, |
| {2, " recognized as a chunk to be kept."}, |
| {2, ""}, |
| |
| |
| {0, " -l zlib_compression_level [0-9]"}, |
| {2, ""}, |
| {2, " zlib compression level to use with method specified"}, |
| {2, " with the preceding '-m method' or '-brute_force'"}, |
| {2, " argument."}, |
| {2, ""}, |
| |
| #ifdef PNGCRUSH_LOCO |
| {0, " -loco (\"loco crush\" truecolor PNGs)"}, |
| {2, ""}, |
| {2, " Make the file more compressible by performing a"}, |
| {2, " lossless, reversible, color transformation."}, |
| {2, " The resulting file is a MNG, not a PNG, and should"}, |
| {2, " be given the \".mng\" file extension. The"}, |
| {2, " \"loco\" option has no effect on grayscale or"}, |
| {2, " indexed-color PNG files."}, |
| {2, ""}, |
| #endif |
| |
| {0, " -m method [0 through " STRNGIFY(MAX_METHODS) "]"}, |
| {2, ""}, |
| {2, " pngcrush method to try (0 means try all of 1-10)."}, |
| {2, " Can be repeated as in '-m 1 -m 4 -m 7'."}, |
| {2, " This can be useful if pngcrush runs out of memory"}, |
| {2, " when it tries methods 2, 3, 5, 6, 8, 9, or 10 which"}, |
| {2, " use filtering and are memory-intensive. Methods"}, |
| {2, " 1, 4, and 7 use no filtering; methods 11 and up use"}, |
| {2, " specified filter, compression level, and strategy."}, |
| {2, ""}, |
| {2, FAKE_PAUSE_STRING}, |
| |
| {0, " -max maximum_IDAT_size [default "STRNGIFY(PNG_ZBUF_SIZE)"]"}, |
| {2, ""}, |
| |
| #ifdef PNGCRUSH_LOCO |
| {0, " -mng (write a new MNG, do not crush embedded PNGs)"}, |
| {2, ""}, |
| #endif |
| |
| {0, " -newtimestamp"}, |
| {2, ""}, |
| {2, " Reset file modification time [default]."}, |
| {2, ""}, |
| |
| #ifdef PNGCRUSH_COUNT_COLORS |
| {0, " -no_cc (no color counting)"}, |
| {2, ""}, |
| #endif |
| |
| {0, " -nofilecheck (do not check for infile.png == outfile.png)"}, |
| {2, ""}, |
| {2, " To avoid false hits from MSVC-compiled code. Note"}, |
| {2, " that if you use this option, you are responsible for"}, |
| {2, " ensuring that the input file is not the output file."}, |
| {2, ""}, |
| |
| |
| {0, " -oldtimestamp"}, |
| {2, ""}, |
| {2, " Don't reset file modification time."}, |
| {2, ""}, |
| |
| {0, " -n (no save; doesn't do compression or write output PNG)"}, |
| {2, ""}, |
| {2, " Useful in conjunction with -v option to get info."}, |
| {2, ""}, |
| |
| {0, " -plte_len n (truncate PLTE)"}, |
| {2, ""}, |
| {2, " Truncates the PLTE. Be sure not to truncate it to"}, |
| {2, " less than the greatest index present in IDAT."}, |
| {2, ""}, |
| |
| {0, " -q (quiet)"}, |
| {2, ""}, |
| |
| {0, " -reduce (do lossless color-type or bit-depth reduction)"}, |
| {2, ""}, |
| {2, " (if possible)"}, |
| {2, ""}, |
| |
| {0, " -rem chunkname (or \"alla\" or \"allb\")"}, |
| {2, ""}, |
| {2, " Name of an ancillary chunk or optional PLTE to be"}, |
| {2, " removed. Be careful with this. Don't use this"}, |
| {2, " feature to remove transparency, gamma, copyright,"}, |
| {2, " or other valuable information. To remove several"}, |
| {2, " different chunks, repeat: -rem tEXt -rem pHYs."}, |
| {2, " Known chunks (those in the PNG 1.1 spec or extensions"}, |
| {2, " document) can be named with all lower-case letters,"}, |
| {2, " so \"-rem bkgd\" is equivalent to \"-rem bKGD\". But"}, |
| {2, " note: \"-rem text\" removes all forms of text chunks;"}, |
| {2, " Exact case is required to remove unknown chunks."}, |
| {2, " To do surgery with a chain-saw, \"-rem alla\" removes"}, |
| {2, " all known ancillary chunks except for tRNS, and"}, |
| {2, " \"-rem allb\" removes all but tRNS and gAMA."}, |
| {2, ""}, |
| |
| {0, FAKE_PAUSE_STRING}, |
| |
| #ifdef PNG_FIXED_POINT_SUPPORTED |
| {0, "-replace_gamma gamma (float or fixed*100000) even if it is present."}, |
| #else |
| {0, "-replace_gamma gamma (float, e.g. 0.45455) even if it is present."}, |
| #endif |
| {2, ""}, |
| |
| {0, " -res dpi"}, |
| {2, ""}, |
| {2, " Write a pHYs chunk with the given resolution."}, |
| {2, ""}, |
| |
| #ifdef Z_RLE |
| {0, " -rle (use only zlib strategy 3, RLE-only)"}, |
| {2, ""}, |
| {2, " A relatively fast subset of the \"-brute\" methods,"}, |
| {2, " generally more effective than \"-huffman\" on PNG,"}, |
| {2, " images (and quite effective on black-and-white"}, |
| {2, " images) but not necessarily worth the bother"}, |
| {2, " otherwise."}, |
| {2, ""}, |
| #endif |
| |
| {0, " -save (keep all copy-unsafe chunks)"}, |
| {2, ""}, |
| {2, " Save otherwise unknown ancillary chunks that would"}, |
| {2, " be considered copy-unsafe. This option makes"}, |
| {2, " chunks 'known' to pngcrush, so they can be copied."}, |
| {2, " It also causes the dSIG chunk to be saved, even when"}, |
| {2, " it becomes invalid due to datastream changes."}, |
| {2, ""}, |
| |
| {0, FAKE_PAUSE_STRING}, |
| |
| {0, " -srgb [0, 1, 2, or 3]"}, |
| {2, ""}, |
| {2, " Value of 'rendering intent' for sRGB chunk."}, |
| {2, ""}, |
| |
| {0, " -ster [0 or 1]"}, |
| {2, ""}, |
| {2, " Value of 'stereo mode' for sTER chunk."}, |
| {2, " 0: cross-fused; 1: divergent-fused"}, |
| {2, ""}, |
| |
| {0, " -text b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\""}, |
| {2, ""}, |
| {2, " tEXt chunk to insert. keyword < 80 chars,"}, |
| {2, " text < 2048 chars. For now, you can add no more than"}, |
| {2, " ten tEXt, iTXt, or zTXt chunks per pngcrush run."}, |
| {2, ""}, |
| |
| #ifdef PNG_tRNS_SUPPORTED |
| {0, " -trns_array n trns[0] trns[1] .. trns[n-1]"}, |
| {2, ""}, |
| {2, " Insert a tRNS chunk, if no tRNS chunk found in file."}, |
| {2, " Values are for the tRNS array in indexed-color PNG."}, |
| {2, ""}, |
| |
| {0, " -trns index red green blue gray"}, |
| {2, ""}, |
| {2, " Insert a tRNS chunk, if no tRNS chunk found in file."}, |
| {2, " You must give all five parameters regardless of the"}, |
| {2, " color type, scaled to the output bit depth."}, |
| {2, ""}, |
| #endif |
| |
| {0, " -v (display more detailed information)"}, |
| {2, ""}, |
| {2, " Repeat the option (use \"-v -v\") for even more."}, |
| {2, ""}, |
| |
| {0, " -version (display the pngcrush version)"}, |
| {2, ""}, |
| {2, " Look for the most recent version of pngcrush at"}, |
| {2, " http://pmt.sf.net"}, |
| {2, ""}, |
| |
| {0, " -w compression_window_size [32, 16, 8, 4, 2, 1, 512]"}, |
| {2, ""}, |
| {2, " Size of the sliding compression window, in kbytes"}, |
| {2, " (or bytes, in case of 512). It's best to"}, |
| {2, " use the default (32) unless you run out of memory."}, |
| {2, " The program will use a smaller window anyway when"}, |
| {2, " the uncompressed file is smaller than 16k."}, |
| {2, ""}, |
| |
| #ifdef Z_RLE |
| {0, " -z zlib_strategy [0, 1, 2, or 3]"}, |
| #else |
| {0, " -z zlib_strategy [0, 1, or 2]"}, |
| #endif |
| {2, ""}, |
| {2, " zlib compression strategy to use with the preceding"}, |
| {2, " '-m method' argument."}, |
| {2, ""}, |
| |
| {0, " -zmem zlib_compression_mem_level [1-9, default 9]"}, |
| {2, ""}, |
| |
| #ifdef PNG_iTXt_SUPPORTED |
| {0, " -zitxt b[efore_IDAT]|a[fter_IDAT] \"keyword\""}, |
| {2, " \"language_code\" \"translated_keyword\" \"text\""}, |
| {2, ""}, |
| {2, " Compressed iTXt chunk to insert (see -text)."}, |
| {2, ""}, |
| #endif |
| |
| {0, " -ztxt b[efore_IDAT]|a[fter_IDAT] \"keyword\" \"text\""}, |
| {2, ""}, |
| {2, " zTXt chunk to insert (see -text)."}, |
| {2, ""}, |
| {2, FAKE_PAUSE_STRING}, |
| |
| {0, " -h (help and legal notices)"}, |
| {2, ""}, |
| {2, " Display this information."}, |
| {2, ""}, |
| |
| {0, " -p (pause)"} |
| }; |
| |
| |
| |
| |
| |
| void print_usage(int retval) |
| { |
| int j, jmax; |
| |
| if (verbose) { |
| jmax = sizeof(pngcrush_legal) / sizeof(char *); |
| for (j = 0; j < jmax; ++j) |
| fprintf(STDERR, "%s\n", pngcrush_legal[j]); |
| |
| jmax = sizeof(pngcrush_usage) / sizeof(char *); |
| for (j = 0; j < jmax; ++j) |
| fprintf(STDERR, pngcrush_usage[j], progname); /* special case */ |
| } |
| |
| /* this block is also handled specially due to the "else" clause... */ |
| if (verbose > 1) { |
| png_crush_pause(); |
| fprintf(STDERR, |
| "\n" |
| "options (Note: any option can be spelled out for clarity, e.g.,\n" |
| " \"pngcrush -dir New -method 7 -remove bkgd *.png\"\n" |
| " is the same as \"pngcrush -d New -m 7 -rem bkgd *.png\"):" |
| "\n\n"); |
| } else |
| fprintf(STDERR, "options:\n"); |
| |
| /* this is the main part of the help screen; it is more complex than the |
| * other blocks due to the mix of verbose and non-verbose lines */ |
| jmax = sizeof(pngcrush_options) / sizeof(struct options_help); |
| for (j = 0; j < jmax; ++j) { |
| if (verbose >= pngcrush_options[j].verbosity) { |
| if (pngcrush_options[j].textline[0] == FAKE_PAUSE_STRING[0]) |
| png_crush_pause(); |
| else |
| fprintf(STDERR, "%s\n", pngcrush_options[j].textline); |
| } |
| } |
| |
| /* due to progname, the verbose part of the -p option is handled explicitly |
| * (fortunately, it's the very last option anyway) */ |
| if (verbose > 1) { |
| fprintf(STDERR, "\n" |
| " Wait for [enter] key before continuing display.\n" |
| " e.g., type '%s -pause -help', if the help\n" |
| " screen scrolls out of sight.\n\n", progname); |
| } |
| |
| exit(retval); |
| } |