summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2012-10-24 07:43:28 -0500
committerGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2012-10-24 07:43:28 -0500
commit2816739f4808ea582ddfa7f1d6bcae929a084c15 (patch)
treec96282527cf8d27589a64fe31314eb3cd6dadda5
parent2c55b3d835b8836369ecdfd0c3ce22281de50590 (diff)
downloadlibpng-1.7.41.tar.gz
[pngcrush] Reverted to version 1.7.38; -39 and -40 failed to write output.v1.7.41
-rw-r--r--pngcrush.c950
1 files changed, 786 insertions, 164 deletions
diff --git a/pngcrush.c b/pngcrush.c
index f77a4873d..9356b4185 100644
--- a/pngcrush.c
+++ b/pngcrush.c
@@ -83,6 +83,7 @@
#define PNGCRUSH_VERSION "1.7.41"
/* Experimental: define these if you wish, but, good luck.
+#define PNGCRUSH_COUNT_COLORS
#define PNGCRUSH_MULTIPLE_ROWS
*/
#define PNGCRUSH_LARGE
@@ -212,7 +213,9 @@
* to the palette/colormap as it encounters them, so it might be improved.
* Also it might be made faster by using a hash table as was partially
* implemented in pngcrush-1.6.x. If the latter is done, also port that
- * back to ImageMagick/GraphicsMagick.
+ * back to ImageMagick/GraphicsMagick. See also ppmhist from the NetPBM
+ * package which counts RGB pixels in an image; this and its supporting
+ * lib/libppmcmap.c would need to be revised to count RGBA pixels instead.
*
* 8. Finish pplt (partial palette) feature.
*
@@ -250,7 +253,8 @@
Change log:
Version 1.7.41 (built with libpng-1.5.13 and zlib-1.2.7)
- Further revised the "To do" list.
+ Reverted to version 1.7.38. Versions 1.7.39 and 40 failed to open an
+ output file.
Version 1.7.40 (built with libpng-1.5.13 and zlib-1.2.7)
Revised the "To do" list.
@@ -266,17 +270,12 @@ Version 1.7.39 (built with libpng-1.5.13 and zlib-1.2.7)
function, instead of depending on STDIO_SUPPORTED.
Version 1.7.38 (built with libpng-1.5.13 and zlib-1.2.7)
- Bail out of a trial if byte count exceeds best byte count so far. This
- avoids wasting CPU time on trial compressions of trials that exceed the
- best compression found so far.
+ Bail out of a trial if byte count exceeds best byte count so far.
Added -bail and -nobail options. Use -nobail to get a complete report
- of filesizes; otherwise the report just says ">N" for any trial
- that exceeds size N where N is the best size achieved so far.
+ of filesizes.
Added -blacken option, to enable changing the color samples of any
fully-transparent pixels to zero in PNG files with color-type 4 or 6,
- potentially improving their compressibility. Note that this is an
- irreversible lossy change: the underlying colors of all fully transparent
- pixels are lost, if they were not already black.
+ potentially improving their compressibility.
Version 1.7.37 (built with libpng-1.5.12 and zlib-1.2.7)
Reverted pngcrush.c back to 1.7.35 and fixed the bug with PLTE handling.
@@ -414,8 +413,6 @@ Version 1.7.14 (built with libpng-1.5.1beta08 and zlib-1.2.5)
with bundled libpng-1.5.x. Pngcrush cannot be built yet with
a system libpng-1.5.x.
Dropped most of pngcrush.h, that eliminates various parts of libpng.
- Renamed png_default_read_data() to pngcrush_default_read_data() and
- png_crush_pause() to pngcrush_pause()
Version 1.7.13 (built with libpng-1.4.5 and zlib-1.2.5)
@@ -1344,7 +1341,6 @@ static int input_color_type;
static int input_bit_depth;
static int trial;
static int last_trial = 0;
-static int rerun_first_trial = 0;
static png_uint_32 pngcrush_write_byte_count;
static png_uint_32 pngcrush_best_byte_count=0xffffffff;
@@ -1437,13 +1433,12 @@ 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;
+static int num_rgba, reduce_to_gray, it_is_opaque;
#endif
-
-static int reduce_to_gray, it_is_opaque;
png_uint_32 png_measure_idat(png_structp png_ptr);
static png_uint_32 idat_length[MAX_METHODSP1];
@@ -1542,6 +1537,9 @@ void show_result(void);
png_uint_32 measure_idats(FILE * fp);
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);
@@ -1697,23 +1695,6 @@ pngcrush_default_read_data(png_structp png_ptr, png_bytep data, png_size_t lengt
if (check != length)
png_error(png_ptr, "Read Error");
-
- /* To do: fix CgBI files */
- if (fix && found_CgBI)
- {
- /* If this is the first IDAT insert the 2-byte zlib header */
-
- /* We could do this either by injecting the 2-byte header at the
- beginning of the first IDAT or by injecting a complete IDAT chunk
- containing only the 2-byte zlib header ahead of the first IDAT
- chunk. */
-
- /* The bytes for 32k window and level 9 compression are 'x', octal 332 */
-
- /* Add a 0000 CRC, which we will ignore, to the end of each IDAT chunk,
- if necessary */
- }
-
}
#else /* USE_FAR_KEYWORD */
/*
@@ -2296,107 +2277,83 @@ static void pngcrush_flush(png_structp png_ptr)
}
-void pngcrush_transform_pixels(png_structp png_ptr, png_row_infop row_info,
- png_bytep data)
+void blacken_fn(png_structp png_ptr, png_row_infop row_info, png_bytep data)
{
- if (blacken)
+ /* change the underlying color of any fully transparent pixels to black */
+
+ int i;
+
+ if (row_info->color_type < 4)
+ return;
+
+ i=(int) row_info->rowbytes-1;
+
+ if (row_info->color_type == 4) /* GA */
{
- /* change the underlying color of any fully transparent pixels to black */
-
- int i;
-
- if (row_info->color_type < 4)
- return;
-
- i=(int) row_info->rowbytes-1;
-
- if (row_info->color_type == 4) /* GA */
- {
- if (row_info->bit_depth == 8)
- {
- for ( ; i > 0 ; )
- {
- if (data[i--] == 0)
- data[i--]=0;
-
- else
- i--;
- }
- }
-
- else /* bit depth == 16 */
+ if (row_info->bit_depth == 8)
+ {
+ for ( ; i > 0 ; )
{
- for ( ; i > 0 ; )
- {
- if (data[i] && data[i]== 0)
- {
- i-=2;
- data[i--]=0;
- data[i--]=0;
- }
- else
- i-=4;
- }
- }
- }
-
- else /* color_type == 6, RGBA */
- {
- if (row_info->bit_depth == 8)
- {
- for ( ; i > 0 ; )
- {
- if (data[i] == 0)
- {
- i--;
- data[i--]=0;
- data[i--]=0;
- data[i--]=0;
- }
- else
- i-=4;
- }
+ if (data[i--] == 0)
+ data[i--]=0;
+
+ else
+ i--;
}
-
- else /* bit depth == 16 */
+ }
+
+ else /* bit depth == 16 */
+ {
+ for ( ; i > 0 ; )
{
- for ( ; i > 0 ; )
- {
- if (data[i]==0 && data[i-1]== 0)
- {
- i-=2;
- data[i--]=0;
- data[i--]=0;
- data[i--]=0;
- data[i--]=0;
- data[i--]=0;
- data[i--]=0;
- }
- else
- i-=8;
- }
+ if (data[i] && data[i]== 0)
+ {
+ i-=2;
+ data[i--]=0;
+ data[i--]=0;
+ }
+ else
+ i-=4;
}
- }
+ }
}
- /* To do: move this into a separate read_fn */
- if (rerun_first_trial)
+ else /* color_type == 6, RGBA */
{
- /* To do: first trial only, check image to see if it is all opaque.
- * If so, set it_is_opaque to 1
- */
-
- /* To do: first trial only, check image to see if it is all gray.
- * If so, set reduce_to_gray to 1
- */
-
- /* To do: if image has an alpha channel but is all opaque, remove
- * the alpha samples from the pixels in this row.
- */
+ if (row_info->bit_depth == 8)
+ {
+ for ( ; i > 0 ; )
+ {
+ if (data[i] == 0)
+ {
+ i--;
+ data[i--]=0;
+ data[i--]=0;
+ data[i--]=0;
+ }
+ else
+ i-=4;
+ }
+ }
- /* To do: if image is truecolor but is all gray, reduce the pixels in
- * this row to grayscale.
- */
+ else /* bit depth == 16 */
+ {
+ for ( ; i > 0 ; )
+ {
+ if (data[i]==0 && data[i-1]== 0)
+ {
+ i-=2;
+ data[i--]=0;
+ data[i--]=0;
+ data[i--]=0;
+ data[i--]=0;
+ data[i--]=0;
+ data[i--]=0;
+ }
+ else
+ i-=8;
+ }
+ }
}
}
@@ -2423,8 +2380,13 @@ int main(int argc, char *argv[])
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))
{
@@ -2609,14 +2571,20 @@ int main(int argc, char *argv[])
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++;
@@ -2925,6 +2893,7 @@ int main(int argc, char *argv[])
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))
@@ -3532,6 +3501,87 @@ int main(int argc, char *argv[])
}
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 */
+ {
+ /* TO DO */
+ }
+
+ 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 /* 0, TODO */
+ 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 (plte_len > 0 && force_output_bit_depth == 0)
{
if (plte_len <= 2)
@@ -3586,13 +3636,6 @@ int main(int argc, char *argv[])
/* MAX_METHODS is 200 */
P1("\n\nENTERING MAIN LOOP OVER %d METHODS\n", MAX_METHODS);
-
- /* To do: arrange to to an extra trial here to examine the
- * pixels for possible reducion to grayscale or removal of alpha
- */
- if (reduction_ok)
- rerun_first_trial=1;
-
for (trial = 1; trial <= MAX_METHODS; trial++)
{
if (nosave || trial == MAX_METHODS)
@@ -3689,21 +3732,8 @@ int main(int argc, char *argv[])
else /* if (zs[best] == 0) */
z_strategy = Z_DEFAULT_STRATEGY;
}
-
else
{
- if (rerun_first_trial == 1)
- {
- /* To do: run an extra trial here to find out if the
- * image is opaque or all-gray
- */
-
- trial--;
- rerun_first_trial = 0;
- }
- else
- {
-
if (trial > 2 && trial < 5 && idat_length[trial - 1]
< idat_length[best_of_three])
best_of_three = trial - 1;
@@ -3845,11 +3875,9 @@ int main(int argc, char *argv[])
(png_size_t)256);
#endif /* 0 */
- /* Change the underlying color of any fully transparent pixel to black
- * and do other permitted reductions
- */
- if (blacken || reduction_ok)
- png_set_read_user_transform_fn(read_ptr, pngcrush_transform_pixels);
+ /* Change the underlying color of any fully transparent pixel to black */
+ if (blacken)
+ png_set_read_user_transform_fn(read_ptr, blacken_fn);
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
if (last_trial == 0)
@@ -3901,8 +3929,12 @@ int main(int argc, char *argv[])
pngcrush_pause();
P1( "Initializing input and output streams\n");
+#ifdef PNG_STDIO_SUPPORTED
+ png_init_io(read_ptr, fpin);
+#else
png_set_read_fn(read_ptr, (png_voidp) fpin,
- (png_rw_ptr) pngcrush_default_read_data);
+ (png_rw_ptr) NULL);
+#endif /* PNG_STDIO_SUPPORTED */
if (nosave == 0)
png_set_write_fn(write_ptr, (png_voidp) fpout,
@@ -4117,7 +4149,7 @@ int main(int argc, char *argv[])
png_error(read_ptr,
"PNG file corrupted by ASCII conversion");
}
- if (fix && found_CgBI)
+ if(fix && found_CgBI)
{
/* Skip the CgBI chunk */
@@ -4227,10 +4259,12 @@ int main(int argc, char *argv[])
{
if (verbose > 0 && last_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");
}
@@ -4268,10 +4302,12 @@ int main(int argc, char *argv[])
{
if (verbose > 0 && last_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");
}
@@ -5339,7 +5375,9 @@ int main(int argc, char *argv[])
if (found_CgBI)
{
png_warning(read_ptr,
- "Cannot read Xcode CgBI PNG");
+ "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");
@@ -5948,7 +5986,7 @@ int main(int argc, char *argv[])
(unsigned long)idat_length[trial]);
fflush(STDERR);
}
- }
+
} /* end of trial-loop */
P1("\n\nFINISHED MAIN LOOP OVER %d METHODS\n\n\n", MAX_METHODS);
@@ -6089,8 +6127,11 @@ png_uint_32 measure_idats(FILE * fp_in)
read_info_ptr = png_create_info_struct(read_ptr);
end_info_ptr = png_create_info_struct(read_ptr);
- png_set_read_fn(read_ptr, (png_voidp) fp_in,
- (png_rw_ptr) pngcrush_default_read_data);
+#ifdef PNG_STDIO_SUPPORTED
+ 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);
@@ -6115,9 +6156,8 @@ png_uint_32 measure_idats(FILE * fp_in)
png_uint_32 png_measure_idat(png_structp png_ptr)
{
- /* Copyright (C) 1999-2002,2006-2012 Glenn Randers-Pehrson
- (glennrp@users.sf.net) See notice in pngcrush.c for conditions of
- use and distribution */
+ /* Copyright (C) 1999-2002,2006 Glenn Randers-Pehrson (glennrp@users.sf.net)
+ See notice in pngcrush.c for conditions of use and distribution */
/* Signature + IHDR + IEND; we'll add PLTE + IDAT lengths */
png_uint_32 sum_idat_length = 45;
@@ -6367,7 +6407,7 @@ png_uint_32 png_measure_idat(png_structp png_ptr)
if (png_get_uint_32(chunk_name) == PNG_UINT_CgBI)
{
- printf(" This is an Xcode CgBI file, not a PNG file.\n");
+ printf(" This is an Xcode CGBI file, not a PNG file.\n");
if (fix)
{
printf (" Removing the CgBI chunk.\n");
@@ -6444,7 +6484,7 @@ png_uint_32 png_measure_idat(png_structp png_ptr)
if (!strncmp((png_const_charp) buff, "Photoshop ICC profile",
21))
{
- printf(" Replacing bad Photoshop iCCP chunk with an "
+ printf(" Replacing bad Photoshop ICCP chunk with an "
"sRGB chunk\n");
#ifdef PNG_gAMA_SUPPORTED
# ifdef PNG_FIXED_POINT_SUPPORTED
@@ -6501,6 +6541,574 @@ png_uint_32 png_measure_idat(png_structp png_ptr)
}
+
+
+
+#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;
+
+#ifdef PNG_STDIO_SUPPORTED
+ png_init_io(read_ptr, fp_in);
+#else
+ png_set_read_fn(read_ptr, (png_voidp) fp_in, (png_rw_ptr) NULL);
+#endif
+
+ {
+#ifdef 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 };
+
+ pngcrush_default_read_data(read_ptr, png_signature, 8);
+ png_set_sig_bytes(read_ptr, 8);
+
+#ifdef 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 + 64);
+
+ 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)
{
char *zlib_copyright;
@@ -6669,6 +7277,11 @@ struct options_help pngcrush_options[] = {
{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"},
@@ -6794,12 +7407,19 @@ struct options_help pngcrush_options[] = {
{2, ""},
#endif
- {0, " -newtimestamp (Reset file modification time [default])"},
+ {0, " -newtimestamp"},
+ {2, ""},
+ {2, " Reset file modification time [default]."},
{2, ""},
{0, " -nobail (do not bail out early from trial -- see -bail)"},
{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"},
@@ -6813,7 +7433,9 @@ struct options_help pngcrush_options[] = {
{2, ""},
- {0, " -oldtimestamp (Do not reset file modification time)"},
+ {0, " -oldtimestamp"},
+ {2, ""},
+ {2, " Don't reset file modification time."},
{2, ""},
{0, " -ow (Overwrite)"},