mirror of
				https://git.code.sf.net/p/libpng/code.git
				synced 2025-07-10 18:04:09 +02:00 
			
		
		
		
	contrib/examples/pngcp search mode
This is still a work-in-progress but it seems fairly stable (if not exactly 100% optimal). pngcp now allows 'all' for some options which iterates through all possible settings (this reliably produces the smallest IDAT that libpng can produce with those settings.) It also contains a --search command line option which attempts to optimize this by skipping pointless tests; it is close, most of the time, but not perfect. Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
		
							parent
							
								
									faf68f8d57
								
							
						
					
					
						commit
						b9014ed336
					
				@ -20,6 +20,7 @@
 | 
				
			|||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <errno.h>
 | 
					#include <errno.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <limits.h>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
@ -90,6 +91,8 @@ typedef enum
 | 
				
			|||||||
#define LOG             0x020 /* Log pass/fail to stdout */
 | 
					#define LOG             0x020 /* Log pass/fail to stdout */
 | 
				
			||||||
#define CONTINUE        0x040 /* Continue on APP_FAIL errors */
 | 
					#define CONTINUE        0x040 /* Continue on APP_FAIL errors */
 | 
				
			||||||
#define SIZES           0x080 /* Report input and output sizes */
 | 
					#define SIZES           0x080 /* Report input and output sizes */
 | 
				
			||||||
 | 
					#define SEARCH          0x100 /* Search IDAT compression options */
 | 
				
			||||||
 | 
					#define NOWRITE         0x200 /* Do not write an output file */
 | 
				
			||||||
#define OPTION     0x80000000 /* Used for handling options */
 | 
					#define OPTION     0x80000000 /* Used for handling options */
 | 
				
			||||||
#define LIST       0x80000001 /* Used for handling options */
 | 
					#define LIST       0x80000001 /* Used for handling options */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -108,18 +111,18 @@ static const char all[] = "all";
 | 
				
			|||||||
static const struct value_list
 | 
					static const struct value_list
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   const char *name;  /* the command line name of the value */
 | 
					   const char *name;  /* the command line name of the value */
 | 
				
			||||||
   const int   value; /* the actual value to use */
 | 
					   int         value; /* the actual value to use */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\
 | 
					#if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\
 | 
				
			||||||
    defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
 | 
					    defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
 | 
				
			||||||
vl_strategy[] =
 | 
					vl_strategy[] =
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   /* This controls the order of search and also the default (which is huffman
 | 
					   /* This controls the order of search and also the default (which is RLE
 | 
				
			||||||
    * only compression):
 | 
					    * compression):
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
   { "fixed", Z_FIXED },
 | 
					 | 
				
			||||||
   { "huffman", Z_HUFFMAN_ONLY },
 | 
					   { "huffman", Z_HUFFMAN_ONLY },
 | 
				
			||||||
   { "RLE", Z_RLE },
 | 
					   { "RLE", Z_RLE },
 | 
				
			||||||
 | 
					   { "fixed", Z_FIXED }, /* the remainder do window searchs */
 | 
				
			||||||
   { "filtered", Z_FILTERED },
 | 
					   { "filtered", Z_FILTERED },
 | 
				
			||||||
   { "default", Z_DEFAULT_STRATEGY },
 | 
					   { "default", Z_DEFAULT_STRATEGY },
 | 
				
			||||||
   { all, 0 }
 | 
					   { all, 0 }
 | 
				
			||||||
@ -130,21 +133,24 @@ vl_level[] =
 | 
				
			|||||||
   { "none", Z_NO_COMPRESSION },
 | 
					   { "none", Z_NO_COMPRESSION },
 | 
				
			||||||
   { "speed", Z_BEST_SPEED },
 | 
					   { "speed", Z_BEST_SPEED },
 | 
				
			||||||
   { "best", Z_BEST_COMPRESSION },
 | 
					   { "best", Z_BEST_COMPRESSION },
 | 
				
			||||||
   RANGE(0, 9),
 | 
					   { "0", Z_NO_COMPRESSION },
 | 
				
			||||||
 | 
					   RANGE(1, 9), /* this deliberately excludes '0' */
 | 
				
			||||||
   { all, 0 }
 | 
					   { all, 0 }
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
vl_windowBits[] =
 | 
					#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 | 
				
			||||||
 | 
					vl_windowBits_text[] =
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   { "default", 15 },
 | 
					   { "default", 15 },
 | 
				
			||||||
   { "small", 9 },
 | 
					   { "small", 9 },
 | 
				
			||||||
   RANGE(8, 15),
 | 
					   RANGE(8, 15),
 | 
				
			||||||
   { all, 0 }
 | 
					   { all, 0 }
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
 | 
					#endif /* text compression */
 | 
				
			||||||
vl_memLevel[] =
 | 
					vl_memLevel[] =
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   { "default", 8 },
 | 
					   { "default", 8 },
 | 
				
			||||||
   { "least", 1 },
 | 
					   { "least", 1 },
 | 
				
			||||||
   RANGE(1, 9),
 | 
					   RANGE(2, 9), /* exclude 1: there seems to be a zlib bug */
 | 
				
			||||||
   { all, 0 }
 | 
					   { all, 0 }
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
#endif /* WRITE_CUSTOMIZE_*COMPRESSION */
 | 
					#endif /* WRITE_CUSTOMIZE_*COMPRESSION */
 | 
				
			||||||
@ -179,6 +185,17 @@ vl_select[] =
 | 
				
			|||||||
#endif /* SELECT_FILTER_HEURISTICALLY || SELECT_FILTER_METHODICALLY */
 | 
					#endif /* SELECT_FILTER_HEURISTICALLY || SELECT_FILTER_METHODICALLY */
 | 
				
			||||||
vl_on_off[] = { { "on", 1 }, { "off", 2 } };
 | 
					vl_on_off[] = { { "on", 1 }, { "off", 2 } };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 | 
				
			||||||
 | 
					static struct value_list
 | 
				
			||||||
 | 
					vl_windowBits_IDAT[] =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   { "default", 15 },
 | 
				
			||||||
 | 
					   { "small", 9 },
 | 
				
			||||||
 | 
					   RANGE(8, 15), /* modified by set_windowBits_hi */
 | 
				
			||||||
 | 
					   { all, 0 }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif /* IDAT compression */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct option
 | 
					static const struct option
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   const char              *name;         /* name of the option */
 | 
					   const char              *name;         /* name of the option */
 | 
				
			||||||
@ -197,11 +214,15 @@ static const struct option
 | 
				
			|||||||
   S(log,      LOG)
 | 
					   S(log,      LOG)
 | 
				
			||||||
   S(continue, CONTINUE)
 | 
					   S(continue, CONTINUE)
 | 
				
			||||||
   S(sizes,    SIZES)
 | 
					   S(sizes,    SIZES)
 | 
				
			||||||
 | 
					   S(search,   SEARCH)
 | 
				
			||||||
 | 
					   S(nowrite,  NOWRITE)
 | 
				
			||||||
#  undef S
 | 
					#  undef S
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* OPTION settings, these and LIST settings are read on demand */
 | 
					   /* OPTION settings, these and LIST settings are read on demand */
 | 
				
			||||||
 | 
					#  define VLNAME(name) vl_ ## name
 | 
				
			||||||
 | 
					#  define VLSIZE(name) ((sizeof VLNAME(name))/(sizeof VLNAME(name)[0]))
 | 
				
			||||||
#  define VL(oname, name, type)\
 | 
					#  define VL(oname, name, type)\
 | 
				
			||||||
   { oname, type, (sizeof vl_ ## name)/(sizeof vl_ ## name[0]), vl_ ## name },
 | 
					   { oname, type, VLSIZE(name), VLNAME(name) },
 | 
				
			||||||
#  define VLO(oname, name) VL(oname, name, OPTION)
 | 
					#  define VLO(oname, name) VL(oname, name, OPTION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#  ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 | 
					#  ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 | 
				
			||||||
@ -220,7 +241,8 @@ static const struct option
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   VLC(strategy)
 | 
					   VLC(strategy)
 | 
				
			||||||
   VLC(level)
 | 
					   VLC(level)
 | 
				
			||||||
   VLC(windowBits)
 | 
					   VLO("windowBits", windowBits_IDAT)
 | 
				
			||||||
 | 
					   VLO("text-windowBits", windowBits_text)
 | 
				
			||||||
   VLC(memLevel)
 | 
					   VLC(memLevel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#  undef VLO
 | 
					#  undef VLO
 | 
				
			||||||
@ -240,6 +262,20 @@ static const struct option
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define opt_count ((sizeof options)/(sizeof options[0]))
 | 
					#define opt_count ((sizeof options)/(sizeof options[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *
 | 
				
			||||||
 | 
					cts(int ct)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   switch (ct)
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      case PNG_COLOR_TYPE_PALETTE:     return "P";
 | 
				
			||||||
 | 
					      case PNG_COLOR_TYPE_GRAY:        return "G";
 | 
				
			||||||
 | 
					      case PNG_COLOR_TYPE_GRAY_ALPHA:  return "GA";
 | 
				
			||||||
 | 
					      case PNG_COLOR_TYPE_RGB:         return "RGB";
 | 
				
			||||||
 | 
					      case PNG_COLOR_TYPE_RGB_ALPHA:   return "RGBA";
 | 
				
			||||||
 | 
					      default:                         return "INVALID";
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct display
 | 
					struct display
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   jmp_buf          error_return;      /* Where to go to on error */
 | 
					   jmp_buf          error_return;      /* Where to go to on error */
 | 
				
			||||||
@ -249,12 +285,6 @@ struct display
 | 
				
			|||||||
   const char      *filename;          /* The name of the original file */
 | 
					   const char      *filename;          /* The name of the original file */
 | 
				
			||||||
   const char      *output_file;       /* The name of the output file */
 | 
					   const char      *output_file;       /* The name of the output file */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Base file information */
 | 
					 | 
				
			||||||
   png_uint_32      w;
 | 
					 | 
				
			||||||
   png_uint_32      h;
 | 
					 | 
				
			||||||
   int              bpp;
 | 
					 | 
				
			||||||
   png_alloc_size_t size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   /* Used on both read and write: */
 | 
					   /* Used on both read and write: */
 | 
				
			||||||
   FILE            *fp;
 | 
					   FILE            *fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -266,10 +296,19 @@ struct display
 | 
				
			|||||||
   png_infop        ip;
 | 
					   png_infop        ip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Used to write a new image (the original info_ptr is used) */
 | 
					   /* Used to write a new image (the original info_ptr is used) */
 | 
				
			||||||
 | 
					#  define MAX_SIZE ((png_alloc_size_t)(-1))
 | 
				
			||||||
   png_alloc_size_t write_size;
 | 
					   png_alloc_size_t write_size;
 | 
				
			||||||
   png_alloc_size_t best_size;
 | 
					   png_alloc_size_t best_size;
 | 
				
			||||||
   png_structp      write_pp;
 | 
					   png_structp      write_pp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* Base file information */
 | 
				
			||||||
 | 
					   png_alloc_size_t size;
 | 
				
			||||||
 | 
					   png_uint_32      w;
 | 
				
			||||||
 | 
					   png_uint_32      h;
 | 
				
			||||||
 | 
					   int              bpp;
 | 
				
			||||||
 | 
					   png_byte         ct;
 | 
				
			||||||
 | 
					   int              no_warnings;       /* Do not output libpng warnings */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Options handling */
 | 
					   /* Options handling */
 | 
				
			||||||
   png_uint_32      results;           /* A mask of errors seen */
 | 
					   png_uint_32      results;           /* A mask of errors seen */
 | 
				
			||||||
   png_uint_32      options;           /* See display_log below */
 | 
					   png_uint_32      options;           /* See display_log below */
 | 
				
			||||||
@ -295,10 +334,15 @@ struct display
 | 
				
			|||||||
#  define SL 8 /* stack limit */
 | 
					#  define SL 8 /* stack limit */
 | 
				
			||||||
   struct stack
 | 
					   struct stack
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      png_byte      opt;               /* The option being tested */
 | 
					      png_alloc_size_t best_size;      /* Best so far for this option */
 | 
				
			||||||
      png_byte      entry;             /* The next value entry to be tested */
 | 
					      png_alloc_size_t lo_size;
 | 
				
			||||||
      png_byte      end;               /* This is the last entry */
 | 
					      png_alloc_size_t hi_size;
 | 
				
			||||||
      int           opt_string_end;    /* End of the option string in 'curr' */
 | 
					      int              lo, hi;         /* For binary chop of a range */
 | 
				
			||||||
 | 
					      int              best_val;       /* Best value found so far */
 | 
				
			||||||
 | 
					      int              opt_string_end; /* End of the option string in 'curr' */
 | 
				
			||||||
 | 
					      png_byte         opt;            /* The option being tested */
 | 
				
			||||||
 | 
					      png_byte         entry;          /* The next value entry to be tested */
 | 
				
			||||||
 | 
					      png_byte         end;            /* This is the last entry */
 | 
				
			||||||
   }                stack[SL];         /* Stack of entries being tested */
 | 
					   }                stack[SL];         /* Stack of entries being tested */
 | 
				
			||||||
   char             curr[32*SL];       /* current options being tested */
 | 
					   char             curr[32*SL];       /* current options being tested */
 | 
				
			||||||
   char             best[32*SL];       /* best options */
 | 
					   char             best[32*SL];       /* best options */
 | 
				
			||||||
@ -481,6 +525,9 @@ optind(struct display *dp, const char *opt, size_t len)
 | 
				
			|||||||
   abort(); /* NOT REACHED */
 | 
					   abort(); /* NOT REACHED */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This works for an option name (no quotes): */
 | 
				
			||||||
 | 
					#define OPTIND(dp, name) optind(dp, #name, (sizeof #name)-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
getopt(struct display *dp, const char *opt, int *value)
 | 
					getopt(struct display *dp, const char *opt, int *value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -538,7 +585,7 @@ opt_list_end(struct display *dp, png_byte opt, png_byte entry)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
push_opt(struct display *dp, unsigned int sp, png_byte opt)
 | 
					push_opt(struct display *dp, unsigned int sp, png_byte opt, int search)
 | 
				
			||||||
   /* Push a new option onto the stack, initializing the new stack entry
 | 
					   /* Push a new option onto the stack, initializing the new stack entry
 | 
				
			||||||
    * appropriately; this does all the work of next_opt (setting end/nsp) for
 | 
					    * appropriately; this does all the work of next_opt (setting end/nsp) for
 | 
				
			||||||
    * the first entry in the list.
 | 
					    * the first entry in the list.
 | 
				
			||||||
@ -564,12 +611,35 @@ push_opt(struct display *dp, unsigned int sp, png_byte opt)
 | 
				
			|||||||
   while (entry > 0U);
 | 
					   while (entry > 0U);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   dp->tsp = sp+1U;
 | 
					   dp->tsp = sp+1U;
 | 
				
			||||||
 | 
					   dp->stack[sp].best_size =
 | 
				
			||||||
 | 
					      dp->stack[sp].lo_size =
 | 
				
			||||||
 | 
					      dp->stack[sp].hi_size = MAX_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   if (search && entry_name == range_lo) /* search this range */
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      dp->stack[sp].lo = options[opt].values[entry].value;
 | 
				
			||||||
 | 
					      /* check for a mal-formed RANGE above: */
 | 
				
			||||||
 | 
					      assert(entry+1 < options[opt].value_count &&
 | 
				
			||||||
 | 
					             options[opt].values[entry+1].name == range_hi);
 | 
				
			||||||
 | 
					      dp->stack[sp].hi = options[opt].values[entry+1].value;
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      /* next_opt will just iterate over the range. */
 | 
				
			||||||
 | 
					      dp->stack[sp].lo = INT_MAX;
 | 
				
			||||||
 | 
					      dp->stack[sp].hi = INT_MIN; /* Prevent range chop */
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   dp->stack[sp].opt = opt;
 | 
					   dp->stack[sp].opt = opt;
 | 
				
			||||||
   dp->stack[sp].entry = entry;
 | 
					   dp->stack[sp].entry = entry;
 | 
				
			||||||
   dp->value[opt] = options[opt].values[entry].value;
 | 
					   dp->stack[sp].best_val = dp->value[opt] = options[opt].values[entry].value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   set_opt_string(dp, sp);
 | 
					   set_opt_string(dp, sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* This works for the search case too; if the range has only one entry 'end'
 | 
				
			||||||
 | 
					    * will be marked here.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
   if (opt_list_end(dp, opt, entry))
 | 
					   if (opt_list_end(dp, opt, entry))
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      dp->stack[sp].end = 1;
 | 
					      dp->stack[sp].end = 1;
 | 
				
			||||||
@ -593,6 +663,7 @@ next_opt(struct display *dp, unsigned int sp)
 | 
				
			|||||||
    * set dp->stack[s].end to true.
 | 
					    * set dp->stack[s].end to true.
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					   int search = 0;
 | 
				
			||||||
   png_byte entry, opt;
 | 
					   png_byte entry, opt;
 | 
				
			||||||
   const char *entry_name;
 | 
					   const char *entry_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -609,7 +680,191 @@ next_opt(struct display *dp, unsigned int sp)
 | 
				
			|||||||
    * cases move to the next entry and load its value:
 | 
					    * cases move to the next entry and load its value:
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
   if (entry_name == range_lo) /* a range */
 | 
					   if (entry_name == range_lo) /* a range */
 | 
				
			||||||
      dp->value[opt]++;
 | 
					   {
 | 
				
			||||||
 | 
					      /* A range can be iterated over or searched.  The default iteration option
 | 
				
			||||||
 | 
					       * is indicated by hi < lo on the stack, otherwise the range being search
 | 
				
			||||||
 | 
					       * is [lo..hi] (inclusive).
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      if (dp->stack[sp].lo > dp->stack[sp].hi)
 | 
				
			||||||
 | 
					         dp->value[opt]++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         /* This is the best size found for this option value: */
 | 
				
			||||||
 | 
					         png_alloc_size_t best_size = dp->stack[sp].best_size;
 | 
				
			||||||
 | 
					         int lo = dp->stack[sp].lo;
 | 
				
			||||||
 | 
					         int hi = dp->stack[sp].hi;
 | 
				
			||||||
 | 
					         int val = dp->value[opt];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         search = 1; /* end is determined here */
 | 
				
			||||||
 | 
					         assert(best_size < MAX_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         if (val == lo)
 | 
				
			||||||
 | 
					         {
 | 
				
			||||||
 | 
					            /* Finding the best for the low end of the range: */
 | 
				
			||||||
 | 
					            dp->stack[sp].lo_size = best_size;
 | 
				
			||||||
 | 
					            assert(hi > val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (hi == val+1) /* only 2 entries */
 | 
				
			||||||
 | 
					               dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val = hi;
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         else if (val == hi)
 | 
				
			||||||
 | 
					         {
 | 
				
			||||||
 | 
					            dp->stack[sp].hi_size = best_size;
 | 
				
			||||||
 | 
					            assert(val > lo+1); /* else 'end' set above */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (val == lo+2) /* only three entries to test */
 | 
				
			||||||
 | 
					               dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val = (lo + val)/2;
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         else
 | 
				
			||||||
 | 
					         {
 | 
				
			||||||
 | 
					            png_alloc_size_t lo_size = dp->stack[sp].lo_size;
 | 
				
			||||||
 | 
					            png_alloc_size_t hi_size = dp->stack[sp].hi_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* lo and hi should have been tested. */
 | 
				
			||||||
 | 
					            assert(lo_size < MAX_SIZE && hi_size < MAX_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* These cases arise with the 'probe' handling below when there is a
 | 
				
			||||||
 | 
					             * dip or peak in the size curve.
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            if (val < lo) /* probing a new lo */
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               /* Swap lo and val: */
 | 
				
			||||||
 | 
					               dp->stack[sp].lo = val;
 | 
				
			||||||
 | 
					               dp->stack[sp].lo_size = best_size;
 | 
				
			||||||
 | 
					               val = lo;
 | 
				
			||||||
 | 
					               best_size = lo_size;
 | 
				
			||||||
 | 
					               lo = dp->stack[sp].lo;
 | 
				
			||||||
 | 
					               lo_size = dp->stack[sp].lo_size;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else if (val > hi) /* probing a new hi */
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               /* Swap hi and val: */
 | 
				
			||||||
 | 
					               dp->stack[sp].hi = val;
 | 
				
			||||||
 | 
					               dp->stack[sp].hi_size = best_size;
 | 
				
			||||||
 | 
					               val = hi;
 | 
				
			||||||
 | 
					               best_size = hi_size;
 | 
				
			||||||
 | 
					               hi = dp->stack[sp].hi;
 | 
				
			||||||
 | 
					               hi_size = dp->stack[sp].hi_size;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* The following should be true or something got messed up above. */
 | 
				
			||||||
 | 
					            assert(lo < val && val < hi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* If there are only four entries (lo, val, hi plus one more) just
 | 
				
			||||||
 | 
					             * test the remaining entry.
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            if (hi == lo+3)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               /* Because of the 'probe' code val can either be lo+1 or hi-1; we
 | 
				
			||||||
 | 
					                * need to test the other.
 | 
				
			||||||
 | 
					                */
 | 
				
			||||||
 | 
					               val = lo + ((val == lo+1) ? 2 : 1);
 | 
				
			||||||
 | 
					               assert(lo < val && val < hi);
 | 
				
			||||||
 | 
					               dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               /* There are at least 2 entries still untested between lo and hi,
 | 
				
			||||||
 | 
					                * i.e. hi >= lo+4.  'val' is the midpoint +/- 0.5
 | 
				
			||||||
 | 
					                *
 | 
				
			||||||
 | 
					                * Separate out the four easy cases when lo..val..hi are
 | 
				
			||||||
 | 
					                * monotonically decreased or (more weird) increasing:
 | 
				
			||||||
 | 
					                */
 | 
				
			||||||
 | 
					               assert(hi > lo+3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               if (lo_size <= best_size && best_size <= hi_size)
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					                  /* Select the low range; testing this first favours the low
 | 
				
			||||||
 | 
					                   * range over the high range when everything comes out equal.
 | 
				
			||||||
 | 
					                   * Because of the probing 'val' may be lo+1.  In that case end
 | 
				
			||||||
 | 
					                   * the search and set 'val' to lo+2.
 | 
				
			||||||
 | 
					                   */
 | 
				
			||||||
 | 
					                  if (val == lo+1)
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                     ++val;
 | 
				
			||||||
 | 
					                     dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  else
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                     dp->stack[sp].hi = hi = val;
 | 
				
			||||||
 | 
					                     dp->stack[sp].hi_size = best_size;
 | 
				
			||||||
 | 
					                     val = (lo + val) / 2;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               else if (lo_size >= best_size && best_size >= hi_size)
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					                  /* Monotonically decreasing size; this is the expected case.
 | 
				
			||||||
 | 
					                   * Select the high end of the range.  As above, val may be
 | 
				
			||||||
 | 
					                   * hi-1.
 | 
				
			||||||
 | 
					                   */
 | 
				
			||||||
 | 
					                  if (val == hi-1)
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                     --val;
 | 
				
			||||||
 | 
					                     dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  else
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                     dp->stack[sp].lo = lo = val;
 | 
				
			||||||
 | 
					                     dp->stack[sp].lo_size = best_size;
 | 
				
			||||||
 | 
					                     val = (val + hi) / 2;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               /* If both those tests failed 'best_size' is either greater than
 | 
				
			||||||
 | 
					                * or less than both lo_size and hi_size.  There is a peak or dip
 | 
				
			||||||
 | 
					                * in the curve of sizes from lo to hi and val is on the peak or
 | 
				
			||||||
 | 
					                * dip.
 | 
				
			||||||
 | 
					                *
 | 
				
			||||||
 | 
					                * Because the ranges being searched as so small (level is 1..9,
 | 
				
			||||||
 | 
					                * windowBits 8..15, memLevel 1..9) there will only be at most
 | 
				
			||||||
 | 
					                * three untested values between lo..val and val..hi, so solve
 | 
				
			||||||
 | 
					                * the problem by probing down from hi or up from lo, whichever
 | 
				
			||||||
 | 
					                * is the higher.
 | 
				
			||||||
 | 
					                *
 | 
				
			||||||
 | 
					                * This is the place where 'val' is set to outside the range
 | 
				
			||||||
 | 
					                * lo..hi, described as 'probing', though maybe 'narrowing' would
 | 
				
			||||||
 | 
					                * be more accurate.
 | 
				
			||||||
 | 
					                */
 | 
				
			||||||
 | 
					               else if (lo_size <= hi_size) /* down from hi */
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					                  dp->stack[sp].hi = val;
 | 
				
			||||||
 | 
					                  dp->stack[sp].hi_size = best_size;
 | 
				
			||||||
 | 
					                  val = --hi;
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               else /* up from low */
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					                  dp->stack[sp].lo = val;
 | 
				
			||||||
 | 
					                  dp->stack[sp].lo_size = best_size;
 | 
				
			||||||
 | 
					                  val = ++lo;
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               /* lo and hi are still the true range limits, check for the end
 | 
				
			||||||
 | 
					                * condition.
 | 
				
			||||||
 | 
					                */
 | 
				
			||||||
 | 
					               assert(hi > lo+1);
 | 
				
			||||||
 | 
					               if (hi <= lo+2)
 | 
				
			||||||
 | 
					                  dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         assert(val != dp->stack[sp].best_val); /* should be a new value */
 | 
				
			||||||
 | 
					         dp->value[opt] = val;
 | 
				
			||||||
 | 
					         dp->stack[sp].best_size = MAX_SIZE;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   else
 | 
					   else
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
@ -620,20 +875,109 @@ next_opt(struct display *dp, unsigned int sp)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   set_opt_string(dp, sp);
 | 
					   set_opt_string(dp, sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if (opt_list_end(dp, opt, entry)) /* end of list */
 | 
					   if (!search && opt_list_end(dp, opt, entry)) /* end of list */
 | 
				
			||||||
      dp->stack[sp].end = 1;
 | 
					      dp->stack[sp].end = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   else /* still active after all these tests */
 | 
					   else if (!dp->stack[sp].end) /* still active after all these tests */
 | 
				
			||||||
      dp->nsp = dp->tsp;
 | 
					      dp->nsp = dp->tsp;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
getallopts(struct display *dp, const char *opt_str, int *value)
 | 
					compare_option(const struct display *dp, unsigned int sp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   int opt = dp->stack[sp].opt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* If the best so far is numerically less than the current value the
 | 
				
			||||||
 | 
					    * current set of options is invariably worse.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   if (dp->stack[sp].best_val < dp->value[opt])
 | 
				
			||||||
 | 
					      return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* Lists of options are searched out of numerical order (currently only
 | 
				
			||||||
 | 
					    * strategy), so only return +1 here when a range is being searched.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   else if (dp->stack[sp].best_val > dp->value[opt])
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      if (dp->stack[sp].lo <= dp->stack[sp].hi /*searching*/)
 | 
				
			||||||
 | 
					         return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					         return -1;
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else
 | 
				
			||||||
 | 
					      return 0; /* match; current value is the best one */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					advance_opt(struct display *dp, png_byte opt, int search)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   unsigned int sp = dp->csp++; /* my stack entry */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   assert(sp >= dp->nsp); /* nsp starts off zero */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* If the entry was active in the previous run dp->stack[sp] is already
 | 
				
			||||||
 | 
					    * set up and dp->tsp will be greater than sp, otherwise a new entry
 | 
				
			||||||
 | 
					    * needs to be created.
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * dp->nsp is handled this way:
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are
 | 
				
			||||||
 | 
					    *    both set (by push_opt) to the next stack entry *unless* there is
 | 
				
			||||||
 | 
					    *    only one entry in the new list, in which case dp->stack[sp].end
 | 
				
			||||||
 | 
					    *    is set.
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * 2) For the top stack entry next_opt is called.  The entry must be
 | 
				
			||||||
 | 
					    *    active (dp->stack[sp].end is not set) and either 'nsp' or 'end'
 | 
				
			||||||
 | 
					    *    will be updated as appropriate.
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * 3) For lower stack entries nsp is set unless the stack entry is
 | 
				
			||||||
 | 
					    *    already at the end.  This means that when all the higher entries
 | 
				
			||||||
 | 
					    *    are popped this entry will be too.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   if (sp >= dp->tsp)
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      push_opt(dp, sp, opt, search); /* This sets tsp to sp+1 */
 | 
				
			||||||
 | 
					      return 1; /* initialized */
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      int ret = 0; /* unchanged */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* An option that is already on the stack; update best_size and best_val
 | 
				
			||||||
 | 
					       * if appropriate.  On the first run there are no previous values and
 | 
				
			||||||
 | 
					       * dp->write_size will be MAX_SIZE, however on the first run dp->tsp
 | 
				
			||||||
 | 
					       * starts off as 0.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      assert(dp->write_size > 0U && dp->write_size < MAX_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (dp->stack[sp].best_size > dp->write_size ||
 | 
				
			||||||
 | 
					          (dp->stack[sp].best_size == dp->write_size &&
 | 
				
			||||||
 | 
					           compare_option(dp, sp) > 0))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         dp->stack[sp].best_size = dp->write_size;
 | 
				
			||||||
 | 
					         dp->stack[sp].best_val = dp->value[opt];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (sp+1U >= dp->tsp)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         next_opt(dp, sp);
 | 
				
			||||||
 | 
					         ret = 1; /* advanced */
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else if (!dp->stack[sp].end) /* Active, not at top of stack */
 | 
				
			||||||
 | 
					         dp->nsp = sp+1U;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return ret; /* advanced || unchanged */
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					getallopts_(struct display *dp, const png_byte opt, int *value)
 | 
				
			||||||
   /* Like getop but iterate over all the values if the option was set to "all".
 | 
					   /* Like getop but iterate over all the values if the option was set to "all".
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   const png_byte opt = optind(dp, opt_str, strlen(opt_str));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   if (dp->entry[opt]) /* option was set on command line */
 | 
					   if (dp->entry[opt]) /* option was set on command line */
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      /* Simple, single value, entries don't have a stack frame and have a fixed
 | 
					      /* Simple, single value, entries don't have a stack frame and have a fixed
 | 
				
			||||||
@ -641,39 +985,7 @@ getallopts(struct display *dp, const char *opt_str, int *value)
 | 
				
			|||||||
       * value (entry) selected from the command line is 'all':
 | 
					       * value (entry) selected from the command line is 'all':
 | 
				
			||||||
       */
 | 
					       */
 | 
				
			||||||
      if (options[opt].values[dp->entry[opt]-1].name == all)
 | 
					      if (options[opt].values[dp->entry[opt]-1].name == all)
 | 
				
			||||||
      {
 | 
					         (void)advance_opt(dp, opt, 0/*do not search; iterate*/);
 | 
				
			||||||
         unsigned int sp = dp->csp++; /* my stack entry */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         assert(sp >= dp->nsp); /* nsp starts off zero */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         /* If the entry was active in the previous run dp->stack[sp] is already
 | 
					 | 
				
			||||||
          * set up and dp->tsp will be greater than sp, otherwise a new entry
 | 
					 | 
				
			||||||
          * needs to be created.
 | 
					 | 
				
			||||||
          *
 | 
					 | 
				
			||||||
          * dp->nsp is handled this way:
 | 
					 | 
				
			||||||
          *
 | 
					 | 
				
			||||||
          * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are
 | 
					 | 
				
			||||||
          *    both set (by push_opt) to the next stack entry *unless* there is
 | 
					 | 
				
			||||||
          *    only one entry in the new list, in which case dp->stack[sp].end
 | 
					 | 
				
			||||||
          *    is set.
 | 
					 | 
				
			||||||
          *
 | 
					 | 
				
			||||||
          * 2) For the top stack entry next_opt is called.  The entry must be
 | 
					 | 
				
			||||||
          *    active (dp->stack[sp].end is not set) and either 'nsp' or 'end'
 | 
					 | 
				
			||||||
          *    will be updated as appropriate.
 | 
					 | 
				
			||||||
          *
 | 
					 | 
				
			||||||
          * 3) For lower stack entries nsp is set unless the stack entry is
 | 
					 | 
				
			||||||
          *    already at the end.  This means that when all the higher entries
 | 
					 | 
				
			||||||
          *    are popped this entry will be too.
 | 
					 | 
				
			||||||
          */
 | 
					 | 
				
			||||||
         if (sp >= dp->tsp)
 | 
					 | 
				
			||||||
            push_opt(dp, sp, opt); /* This sets tsp to sp+1 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         else if (sp+1U >= dp->tsp)
 | 
					 | 
				
			||||||
            next_opt(dp, sp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         else if (!dp->stack[sp].end) /* Active, not at top of stack */
 | 
					 | 
				
			||||||
            dp->nsp = sp+1U;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      *value = dp->value[opt];
 | 
					      *value = dp->value[opt];
 | 
				
			||||||
      return 1; /* set */
 | 
					      return 1; /* set */
 | 
				
			||||||
@ -683,6 +995,67 @@ getallopts(struct display *dp, const char *opt_str, int *value)
 | 
				
			|||||||
      return 0; /* not set */
 | 
					      return 0; /* not set */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					getallopts(struct display *dp, const char *opt_str, int *value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   return getallopts_(dp, optind(dp, opt_str, strlen(opt_str)), value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					getsearchopts(struct display *dp, const char *opt_str, int *value)
 | 
				
			||||||
 | 
					   /* As above except that if the option was not set try a search */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   png_byte istrat;
 | 
				
			||||||
 | 
					   const png_byte opt = optind(dp, opt_str, strlen(opt_str));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* If it was set on the command line honour the setting, including 'all'
 | 
				
			||||||
 | 
					    * which will override the built in search:
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   if (getallopts_(dp, opt, value))
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* Otherwise decide what to do here. */
 | 
				
			||||||
 | 
					   istrat = OPTIND(dp, strategy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   if (opt == OPTIND(dp, select))
 | 
				
			||||||
 | 
					      dp->value[opt] = SELECT_METHODICALLY; /* should probably be all */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else if (opt == istrat) /* search all strategies */
 | 
				
			||||||
 | 
					      (void)advance_opt(dp, opt, 0/*iterate*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else if (opt == OPTIND(dp, level))
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      /* Both RLE and HUFFMAN don't benefit from level increases */
 | 
				
			||||||
 | 
					      if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY)
 | 
				
			||||||
 | 
					         dp->value[opt] = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else /* fixed, filtered or default */
 | 
				
			||||||
 | 
					         (void)advance_opt(dp, opt, 1/*search*/);
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else if (opt == OPTIND(dp, windowBits))
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      /* Changing windowBits for strategies that do not search the window is
 | 
				
			||||||
 | 
					       * pointless.  Use the minimum window bits for these.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY)
 | 
				
			||||||
 | 
					         dp->value[opt] = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else /* fixed, filtered or default */
 | 
				
			||||||
 | 
					         (void)advance_opt(dp, opt, 1/*search*/);
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else if (opt == OPTIND(dp, memLevel))
 | 
				
			||||||
 | 
					      dp->value[opt] = 9; /* fixed */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   else /* something else */
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* One of the above searched options: */
 | 
				
			||||||
 | 
					   *value = dp->value[opt];
 | 
				
			||||||
 | 
					   return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
find_val(struct display *dp, png_byte opt, const char *str, size_t len)
 | 
					find_val(struct display *dp, png_byte opt, const char *str, size_t len)
 | 
				
			||||||
   /* Like optind but sets (index+i) of the entry in options[opt] that matches
 | 
					   /* Like optind but sets (index+i) of the entry in options[opt] that matches
 | 
				
			||||||
@ -964,7 +1337,11 @@ makename(struct display *dp, const char *dir, const char *infile)
 | 
				
			|||||||
static void PNGCBAPI
 | 
					static void PNGCBAPI
 | 
				
			||||||
display_warning(png_structp pp, png_const_charp warning)
 | 
					display_warning(png_structp pp, png_const_charp warning)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
 | 
					   struct display *dp = get_dp(pp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* This is used to prevent repeated warnings while searching */
 | 
				
			||||||
 | 
					   if (!dp->no_warnings)
 | 
				
			||||||
 | 
					      display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void PNGCBAPI
 | 
					static void PNGCBAPI
 | 
				
			||||||
@ -1010,9 +1387,9 @@ read_function(png_structp pp, png_bytep data, png_size_t size)
 | 
				
			|||||||
   else
 | 
					   else
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      if (feof(dp->fp))
 | 
					      if (feof(dp->fp))
 | 
				
			||||||
         display_log(dp, USER_ERROR, "PNG file truncated");
 | 
					         display_log(dp, LIBPNG_ERROR, "PNG file truncated");
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
         display_log(dp, USER_ERROR, "PNG file read failed (%s)",
 | 
					         display_log(dp, LIBPNG_ERROR, "PNG file read failed (%s)",
 | 
				
			||||||
               strerror(errno));
 | 
					               strerror(errno));
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1028,12 +1405,14 @@ read_png(struct display *dp, const char *filename)
 | 
				
			|||||||
   if (dp->read_pp == NULL)
 | 
					   if (dp->read_pp == NULL)
 | 
				
			||||||
      display_log(dp, LIBPNG_ERROR, "failed to create read struct");
 | 
					      display_log(dp, LIBPNG_ERROR, "failed to create read struct");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   png_set_benign_errors(dp->read_pp, 1/*allowed*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* The png_read_png API requires us to make the info struct, but it does the
 | 
					   /* The png_read_png API requires us to make the info struct, but it does the
 | 
				
			||||||
    * call to png_read_info.
 | 
					    * call to png_read_info.
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
   dp->ip = png_create_info_struct(dp->read_pp);
 | 
					   dp->ip = png_create_info_struct(dp->read_pp);
 | 
				
			||||||
   if (dp->ip == NULL)
 | 
					   if (dp->ip == NULL)
 | 
				
			||||||
      display_log(dp, LIBPNG_ERROR, "failed to create info struct");
 | 
					      png_error(dp->read_pp, "failed to create info struct");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Set the IO handling */
 | 
					   /* Set the IO handling */
 | 
				
			||||||
   png_set_read_fn(dp->read_pp, dp, read_function);
 | 
					   png_set_read_fn(dp->read_pp, dp, read_function);
 | 
				
			||||||
@ -1052,9 +1431,19 @@ read_png(struct display *dp, const char *filename)
 | 
				
			|||||||
   png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
 | 
					   png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
 | 
				
			||||||
   dp->w = png_get_image_width(dp->read_pp, dp->ip);
 | 
					   dp->w = png_get_image_width(dp->read_pp, dp->ip);
 | 
				
			||||||
   dp->h = png_get_image_height(dp->read_pp, dp->ip);
 | 
					   dp->h = png_get_image_height(dp->read_pp, dp->ip);
 | 
				
			||||||
 | 
					   dp->ct = png_get_color_type(dp->read_pp, dp->ip);
 | 
				
			||||||
   dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) *
 | 
					   dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) *
 | 
				
			||||||
             png_get_channels(dp->read_pp, dp->ip);
 | 
					             png_get_channels(dp->read_pp, dp->ip);
 | 
				
			||||||
   dp->size = png_get_rowbytes(dp->read_pp, dp->ip) * dp->h; /* can overflow */
 | 
					   {
 | 
				
			||||||
 | 
					      png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* The size calc can overflow. */
 | 
				
			||||||
 | 
					      if (MAX_SIZE/rb < dp->h)
 | 
				
			||||||
 | 
					         png_error(dp->read_pp, "image too large");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      dp->size = rb * dp->h;
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   display_clean_read(dp);
 | 
					   display_clean_read(dp);
 | 
				
			||||||
   dp->operation = "none";
 | 
					   dp->operation = "none";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1062,23 +1451,29 @@ read_png(struct display *dp, const char *filename)
 | 
				
			|||||||
static void
 | 
					static void
 | 
				
			||||||
display_start_write(struct display *dp, const char *filename)
 | 
					display_start_write(struct display *dp, const char *filename)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   if (filename != NULL)
 | 
					   assert(dp->fp == NULL);
 | 
				
			||||||
   {
 | 
					
 | 
				
			||||||
      dp->output_file = filename;
 | 
					   if ((dp->options & NOWRITE) != 0)
 | 
				
			||||||
      dp->fp = fopen(filename, "wb");
 | 
					      dp->output_file = "<no write>";
 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
   else
 | 
					   else
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      dp->output_file = "<stdout>";
 | 
					      if (filename != NULL)
 | 
				
			||||||
      dp->fp = stdout;
 | 
					      {
 | 
				
			||||||
 | 
					         dp->output_file = filename;
 | 
				
			||||||
 | 
					         dp->fp = fopen(filename, "wb");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         dp->output_file = "<stdout>";
 | 
				
			||||||
 | 
					         dp->fp = stdout;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (dp->fp == NULL)
 | 
				
			||||||
 | 
					         display_log(dp, USER_ERROR, "%s: file open failed (%s)",
 | 
				
			||||||
 | 
					               dp->output_file, strerror(errno));
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					 | 
				
			||||||
   dp->write_size = 0U;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   if (dp->fp == NULL)
 | 
					 | 
				
			||||||
      display_log(dp, USER_ERROR, "%s: file open failed (%s)", dp->output_file,
 | 
					 | 
				
			||||||
            strerror(errno));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void PNGCBAPI
 | 
					static void PNGCBAPI
 | 
				
			||||||
@ -1086,8 +1481,15 @@ write_function(png_structp pp, png_bytep data, png_size_t size)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
   struct display *dp = get_dp(pp);
 | 
					   struct display *dp = get_dp(pp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if (fwrite(data, size, 1U, dp->fp) == 1U)
 | 
					   /* The write fail is classed as a USER_ERROR, so --quiet does not turn it
 | 
				
			||||||
 | 
					    * off, this seems more likely to be correct.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   if (dp->fp == NULL || fwrite(data, size, 1U, dp->fp) == 1U)
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
      dp->write_size += size;
 | 
					      dp->write_size += size;
 | 
				
			||||||
 | 
					      if (dp->write_size < size || dp->write_size == MAX_SIZE)
 | 
				
			||||||
 | 
					         png_error(pp, "IDAT size overflow");
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   else
 | 
					   else
 | 
				
			||||||
      display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)",
 | 
					      display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)",
 | 
				
			||||||
@ -1102,6 +1504,18 @@ write_function(png_structp pp, png_bytep data, png_size_t size)
 | 
				
			|||||||
   SET(memLevel, mem_level);
 | 
					   SET(memLevel, mem_level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 | 
					#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					search_compression(struct display *dp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   /* Like set_compression below but use a more restricted search than 'all' */
 | 
				
			||||||
 | 
					   int val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#  define SET(name, func) if (getsearchopts(dp, #name, &val))\
 | 
				
			||||||
 | 
					      png_set_compression_ ## func(dp->write_pp, val);
 | 
				
			||||||
 | 
					   SET_COMPRESSION
 | 
				
			||||||
 | 
					#  undef SET
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
set_compression(struct display *dp)
 | 
					set_compression(struct display *dp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -1113,7 +1527,8 @@ set_compression(struct display *dp)
 | 
				
			|||||||
#  undef SET
 | 
					#  undef SET
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#  define set_compression(dp, pp) ((void)0)
 | 
					#  define search_compression(dp) ((void)0)
 | 
				
			||||||
 | 
					#  define set_compression(dp) ((void)0)
 | 
				
			||||||
#endif /* WRITE_CUSTOMIZE_COMPRESSION */
 | 
					#endif /* WRITE_CUSTOMIZE_COMPRESSION */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 | 
					#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 | 
				
			||||||
@ -1128,7 +1543,7 @@ set_text_compression(struct display *dp)
 | 
				
			|||||||
#  undef SET
 | 
					#  undef SET
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#  define set_text_compression(dp, pp) ((void)0)
 | 
					#  define set_text_compression(dp) ((void)0)
 | 
				
			||||||
#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
 | 
					#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
@ -1141,8 +1556,9 @@ write_png(struct display *dp, const char *destname)
 | 
				
			|||||||
      display_error, display_warning);
 | 
					      display_error, display_warning);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if (dp->write_pp == NULL)
 | 
					   if (dp->write_pp == NULL)
 | 
				
			||||||
      display_log(dp, APP_ERROR, "failed to create write png_struct");
 | 
					      display_log(dp, LIBPNG_ERROR, "failed to create write png_struct");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   png_set_benign_errors(dp->write_pp, 1/*allowed*/);
 | 
				
			||||||
   png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/);
 | 
					   png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#  ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
 | 
					#  ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
 | 
				
			||||||
@ -1159,7 +1575,10 @@ write_png(struct display *dp, const char *destname)
 | 
				
			|||||||
   /* compression outputs, IDAT and zTXt/iTXt: */
 | 
					   /* compression outputs, IDAT and zTXt/iTXt: */
 | 
				
			||||||
   dp->tsp = dp->nsp;
 | 
					   dp->tsp = dp->nsp;
 | 
				
			||||||
   dp->nsp = dp->csp = 0;
 | 
					   dp->nsp = dp->csp = 0;
 | 
				
			||||||
   set_compression(dp);
 | 
					   if (dp->options & SEARCH)
 | 
				
			||||||
 | 
					      search_compression(dp);
 | 
				
			||||||
 | 
					   else
 | 
				
			||||||
 | 
					      set_compression(dp);
 | 
				
			||||||
   set_text_compression(dp);
 | 
					   set_text_compression(dp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* filter handling */
 | 
					   /* filter handling */
 | 
				
			||||||
@ -1192,15 +1611,17 @@ write_png(struct display *dp, const char *destname)
 | 
				
			|||||||
#  endif /* WRITE_FILTER */
 | 
					#  endif /* WRITE_FILTER */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* This just uses the 'read' info_struct directly, it contains the image. */
 | 
					   /* This just uses the 'read' info_struct directly, it contains the image. */
 | 
				
			||||||
 | 
					   dp->write_size = 0U;
 | 
				
			||||||
   png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
 | 
					   png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Make sure the file was written ok: */
 | 
					   /* Make sure the file was written ok: */
 | 
				
			||||||
 | 
					   if (dp->fp != NULL)
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      FILE *fp = dp->fp;
 | 
					      FILE *fp = dp->fp;
 | 
				
			||||||
      dp->fp = NULL;
 | 
					      dp->fp = NULL;
 | 
				
			||||||
      if (fclose(fp))
 | 
					      if (fclose(fp))
 | 
				
			||||||
         display_log(dp, APP_ERROR, "%s: write failed (%s)", destname,
 | 
					         display_log(dp, APP_ERROR, "%s: write failed (%s)",
 | 
				
			||||||
               strerror(errno));
 | 
					               destname == NULL ? "stdout" : destname, strerror(errno));
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Clean it on the way out - if control returns to the caller then the
 | 
					   /* Clean it on the way out - if control returns to the caller then the
 | 
				
			||||||
@ -1210,11 +1631,62 @@ write_png(struct display *dp, const char *destname)
 | 
				
			|||||||
   dp->operation = "none";
 | 
					   dp->operation = "none";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					set_windowBits_hi(struct display *dp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   /* windowBits is in the range 8..15, but it is said that setting '8'
 | 
				
			||||||
 | 
					    * prevents adequate search even if the image size is 256 bytes or less.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   int wb = 15; /* for large images */
 | 
				
			||||||
 | 
					   int i = VLSIZE(windowBits_IDAT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   while (wb > 9 && dp->size <= 1U<<(wb-1)) --wb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   assert(i > 0); /* vl_windowBits_IDAT always has a RANGE() */
 | 
				
			||||||
 | 
					   VLNAME(windowBits_IDAT)[i].value = wb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					better_options(const struct display *dp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   /* Are these options better than the best found so far?  Normally the
 | 
				
			||||||
 | 
					    * options are tested in preference order, best first, however when doing a
 | 
				
			||||||
 | 
					    * search operation on a range the range values are tested out of order.  In
 | 
				
			||||||
 | 
					    * that case preferable options will get tested later.
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * This function looks through the stack from the bottom up looking for an
 | 
				
			||||||
 | 
					    * option that does not match the current best value.  When it finds one it
 | 
				
			||||||
 | 
					    * checks to see if it is more or less desireable and returns true or false
 | 
				
			||||||
 | 
					    * as appropriate.
 | 
				
			||||||
 | 
					    *
 | 
				
			||||||
 | 
					    * Notice that this means that the order options are pushed onto the stack
 | 
				
			||||||
 | 
					    * conveys a priority; lower/earlier options are more important than later
 | 
				
			||||||
 | 
					    * ones.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   unsigned int sp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   for (sp=0; sp<dp->csp; ++sp)
 | 
				
			||||||
 | 
					   {
 | 
				
			||||||
 | 
					      int c = compare_option(dp, sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (c < 0)
 | 
				
			||||||
 | 
					         return 0; /* worse */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else if (c > 0)
 | 
				
			||||||
 | 
					         return 1; /* better */
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   assert(0 && "unreached");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
cp_one_file(struct display *dp, const char *filename, const char *destname)
 | 
					cp_one_file(struct display *dp, const char *filename, const char *destname)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   dp->filename = filename;
 | 
					   dp->filename = filename;
 | 
				
			||||||
   dp->operation = "read";
 | 
					   dp->operation = "read";
 | 
				
			||||||
 | 
					   dp->no_warnings = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* Read it then write it: */
 | 
					   /* Read it then write it: */
 | 
				
			||||||
   if (filename != NULL && access(filename, R_OK) != 0)
 | 
					   if (filename != NULL && access(filename, R_OK) != 0)
 | 
				
			||||||
@ -1226,6 +1698,9 @@ cp_one_file(struct display *dp, const char *filename, const char *destname)
 | 
				
			|||||||
   /* But 'destname' may be a directory. */
 | 
					   /* But 'destname' may be a directory. */
 | 
				
			||||||
   dp->operation = "write";
 | 
					   dp->operation = "write";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /* Limit the upper end of the windowBits range for this file */
 | 
				
			||||||
 | 
					   set_windowBits_hi(dp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if (destname != NULL) /* else stdout */
 | 
					   if (destname != NULL) /* else stdout */
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      if (isdir(dp, destname))
 | 
					      if (isdir(dp, destname))
 | 
				
			||||||
@ -1242,29 +1717,44 @@ cp_one_file(struct display *dp, const char *filename, const char *destname)
 | 
				
			|||||||
   dp->nsp = 0;
 | 
					   dp->nsp = 0;
 | 
				
			||||||
   dp->curr[0] = 0;
 | 
					   dp->curr[0] = 0;
 | 
				
			||||||
   dp->best[0] = 0; /* acts as a flag for the caller */
 | 
					   dp->best[0] = 0; /* acts as a flag for the caller */
 | 
				
			||||||
 | 
					   dp->best_size = MAX_SIZE;
 | 
				
			||||||
   write_png(dp, destname);
 | 
					   write_png(dp, destname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if (dp->nsp > 0) /* interating over lists */
 | 
					   if (dp->nsp > 0) /* interating over lists */
 | 
				
			||||||
   {
 | 
					   {
 | 
				
			||||||
      char tmpname[(sizeof dp->namebuf) + 4];
 | 
					      char *tmpname, tmpbuf[(sizeof dp->namebuf) + 4];
 | 
				
			||||||
      assert(dp->curr[0] == ' ' && dp->tsp > 0);
 | 
					      assert(dp->curr[0] == ' ' && dp->tsp > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Cancel warnings on subsequent writes */
 | 
				
			||||||
 | 
					      dp->no_warnings = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      /* Loop to find the best option, first initialize the 'best' fields: */
 | 
					      /* Loop to find the best option, first initialize the 'best' fields: */
 | 
				
			||||||
      strcpy(dp->best, dp->curr);
 | 
					      strcpy(dp->best, dp->curr);
 | 
				
			||||||
      dp->best_size = dp->write_size;
 | 
					      dp->best_size = dp->write_size;
 | 
				
			||||||
      strcpy(tmpname, destname);
 | 
					
 | 
				
			||||||
      strcat(tmpname, ".tmp"); /* space for .tmp allocated above */
 | 
					      if (destname != NULL)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					         strcpy(tmpbuf, destname);
 | 
				
			||||||
 | 
					         strcat(tmpbuf, ".tmp"); /* space for .tmp allocated above */
 | 
				
			||||||
 | 
					         tmpname = tmpbuf;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					         tmpname = NULL; /* stdout */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      do
 | 
					      do
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
         write_png(dp, tmpname);
 | 
					         write_png(dp, tmpname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
         /* And compare the sizes (theoretically this could overflow, in which
 | 
					         /* And compare the sizes (the write function makes sure write_size
 | 
				
			||||||
          * case this program will need to be rewritten perhaps considerably).
 | 
					          * doesn't overflow.)
 | 
				
			||||||
          */
 | 
					          */
 | 
				
			||||||
         if (dp->write_size < dp->best_size)
 | 
					         assert(dp->csp > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         if (dp->write_size < dp->best_size ||
 | 
				
			||||||
 | 
					             (dp->write_size == dp->best_size && better_options(dp)))
 | 
				
			||||||
         {
 | 
					         {
 | 
				
			||||||
            if (rename(tmpname, destname) != 0)
 | 
					            if (destname != NULL && rename(tmpname, destname) != 0)
 | 
				
			||||||
               display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname,
 | 
					               display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname,
 | 
				
			||||||
                     destname, strerror(errno));
 | 
					                     destname, strerror(errno));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1272,7 +1762,7 @@ cp_one_file(struct display *dp, const char *filename, const char *destname)
 | 
				
			|||||||
            dp->best_size = dp->write_size;
 | 
					            dp->best_size = dp->write_size;
 | 
				
			||||||
         }
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
         else if (unlink(tmpname) != 0)
 | 
					         else if (tmpname != NULL && unlink(tmpname) != 0)
 | 
				
			||||||
            display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname,
 | 
					            display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname,
 | 
				
			||||||
                  strerror(errno));
 | 
					                  strerror(errno));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -1363,20 +1853,18 @@ main(const int argc, const char * const * const argv)
 | 
				
			|||||||
               return 99;
 | 
					               return 99;
 | 
				
			||||||
         }
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
         if (d.options & SIZES)
 | 
					         if (d.best[0] != 0)
 | 
				
			||||||
            printf("%s [%ld x %ld %d bpp %lu bytes] 0x%lx %lu -> %lu\n",
 | 
					            printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu with '%s'\n",
 | 
				
			||||||
                  infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
 | 
					                  infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
 | 
				
			||||||
                  (unsigned long)d.size, (unsigned long)d.results,
 | 
					                  cts(d.ct), (unsigned long)d.size, (unsigned long)d.read_size,
 | 
				
			||||||
 | 
					                  (unsigned long)d.best_size, d.best);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         else if (d.options & SIZES)
 | 
				
			||||||
 | 
					            printf("%s [%ld x %ld %d bpp %s, %lu bytes] 0x%lx %lu -> %lu\n",
 | 
				
			||||||
 | 
					                  infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
 | 
				
			||||||
 | 
					                  cts(d.ct), (unsigned long)d.size, (unsigned long)d.results,
 | 
				
			||||||
                  (unsigned long)d.read_size, (unsigned long)d.write_size);
 | 
					                  (unsigned long)d.read_size, (unsigned long)d.write_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
         /* This somewhat replicates the above information, but it seems better
 | 
					 | 
				
			||||||
          * to do both.
 | 
					 | 
				
			||||||
          */
 | 
					 | 
				
			||||||
         if (d.best[0] != 0 && (error_level)(d.options & LEVEL_MASK) < QUIET)
 | 
					 | 
				
			||||||
            printf("%s [%ld x %ld %d bpp %lu bytes] %lu -> %lu with '%s'\n",
 | 
					 | 
				
			||||||
                  infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
 | 
					 | 
				
			||||||
                  (unsigned long)d.size, (unsigned long)d.read_size,
 | 
					 | 
				
			||||||
                  (unsigned long)d.best_size, d.best);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
         /* Here on any return, including failures, except user/internal issues
 | 
					         /* Here on any return, including failures, except user/internal issues
 | 
				
			||||||
          */
 | 
					          */
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user