1 | /* pngcp.c
|
---|
2 | *
|
---|
3 | * Copyright (c) 2016 John Cunningham Bowler
|
---|
4 | *
|
---|
5 | * Last changed in libpng 1.6.24 [August 4, 2016]
|
---|
6 | *
|
---|
7 | * This code is released under the libpng license.
|
---|
8 | * For conditions of distribution and use, see the disclaimer
|
---|
9 | * and license in png.h
|
---|
10 | *
|
---|
11 | * This is an example of copying a PNG without changes using the png_read_png
|
---|
12 | * and png_write_png interfaces. A considerable number of options are provided
|
---|
13 | * to manipulate the compression of the PNG data and other compressed chunks.
|
---|
14 | *
|
---|
15 | * For a more extensive example that uses the transforms see
|
---|
16 | * contrib/libtests/pngimage.c in the libpng distribution.
|
---|
17 | */
|
---|
18 | #include "pnglibconf.h" /* To find how libpng was configured. */
|
---|
19 |
|
---|
20 | #ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
21 | /* WARNING:
|
---|
22 | *
|
---|
23 | * This test is here to allow POSIX.1b extensions to be used if enabled in
|
---|
24 | * the compile; specifically the code requires_POSIX_C_SOURCE support of
|
---|
25 | * 199309L or later to enable clock_gettime use.
|
---|
26 | *
|
---|
27 | * IF this causes problems THEN compile with a strict ANSI C compiler and let
|
---|
28 | * this code turn on the POSIX features that it minimally requires.
|
---|
29 | *
|
---|
30 | * IF this does not work there is probably a bug in your ANSI C compiler or
|
---|
31 | * your POSIX implementation.
|
---|
32 | */
|
---|
33 | # define _POSIX_C_SOURCE 199309L
|
---|
34 | #else /* No timing support required */
|
---|
35 | # define _POSIX_SOURCE 1
|
---|
36 | #endif
|
---|
37 |
|
---|
38 | #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
|
---|
39 | # include <config.h>
|
---|
40 | #endif
|
---|
41 |
|
---|
42 | #include <stdio.h>
|
---|
43 |
|
---|
44 | /* Define the following to use this test against your installed libpng, rather
|
---|
45 | * than the one being built here:
|
---|
46 | */
|
---|
47 | #ifdef PNG_FREESTANDING_TESTS
|
---|
48 | # include <png.h>
|
---|
49 | #else
|
---|
50 | # include "../../png.h"
|
---|
51 | #endif
|
---|
52 |
|
---|
53 | #if PNG_LIBPNG_VER < 10700
|
---|
54 | /* READ_PNG and WRITE_PNG were not defined, so: */
|
---|
55 | # ifdef PNG_INFO_IMAGE_SUPPORTED
|
---|
56 | # ifdef PNG_SEQUENTIAL_READ_SUPPORTED
|
---|
57 | # define PNG_READ_PNG_SUPPORTED
|
---|
58 | # endif /* SEQUENTIAL_READ */
|
---|
59 | # ifdef PNG_WRITE_SUPPORTED
|
---|
60 | # define PNG_WRITE_PNG_SUPPORTED
|
---|
61 | # endif /* WRITE */
|
---|
62 | # endif /* INFO_IMAGE */
|
---|
63 | #endif /* pre 1.7.0 */
|
---|
64 |
|
---|
65 | #if (defined(PNG_READ_PNG_SUPPORTED)) && (defined(PNG_WRITE_PNG_SUPPORTED))
|
---|
66 | #include <stdarg.h>
|
---|
67 | #include <stdlib.h>
|
---|
68 | #include <string.h>
|
---|
69 | #include <errno.h>
|
---|
70 | #include <limits.h>
|
---|
71 | #include <assert.h>
|
---|
72 |
|
---|
73 | #include <unistd.h>
|
---|
74 | #include <sys/stat.h>
|
---|
75 |
|
---|
76 | #include <zlib.h>
|
---|
77 |
|
---|
78 | #ifndef PNG_SETJMP_SUPPORTED
|
---|
79 | # include <setjmp.h> /* because png.h did *not* include this */
|
---|
80 | #endif
|
---|
81 |
|
---|
82 | #ifdef __cplusplus
|
---|
83 | # define voidcast(type, value) static_cast<type>(value)
|
---|
84 | #else
|
---|
85 | # define voidcast(type, value) (value)
|
---|
86 | #endif /* __cplusplus */
|
---|
87 |
|
---|
88 | #ifdef __GNUC__
|
---|
89 | /* Many versions of GCC erroneously report that local variables unmodified
|
---|
90 | * within the scope of a setjmp may be clobbered. This hacks round the
|
---|
91 | * problem (sometimes) without harming other compilers.
|
---|
92 | */
|
---|
93 | # define gv volatile
|
---|
94 | #else
|
---|
95 | # define gv
|
---|
96 | #endif
|
---|
97 |
|
---|
98 | /* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It
|
---|
99 | * need not be supported even when clock_gettime is available. It returns the
|
---|
100 | * 'CPU' time the process has consumed. 'CPU' time is assumed to include time
|
---|
101 | * when the CPU is actually blocked by a pending cache fill but not time
|
---|
102 | * waiting for page faults. The attempt is to get a measure of the actual time
|
---|
103 | * the implementation takes to read a PNG ignoring the potentially very large IO
|
---|
104 | * overhead.
|
---|
105 | */
|
---|
106 | #ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
107 | # include <time.h> /* clock_gettime and associated definitions */
|
---|
108 | # ifndef CLOCK_PROCESS_CPUTIME_ID
|
---|
109 | /* Prevent inclusion of the spurious code: */
|
---|
110 | # undef PNG_PNGCP_TIMING_SUPPORTED
|
---|
111 | # endif
|
---|
112 | #endif /* PNGCP_TIMING */
|
---|
113 |
|
---|
114 | /* So if the timing feature has been activated: */
|
---|
115 |
|
---|
116 | /* This structure is used to control the test of a single file. */
|
---|
117 | typedef enum
|
---|
118 | {
|
---|
119 | VERBOSE, /* switches on all messages */
|
---|
120 | INFORMATION,
|
---|
121 | WARNINGS, /* switches on warnings */
|
---|
122 | LIBPNG_WARNING,
|
---|
123 | APP_WARNING,
|
---|
124 | ERRORS, /* just errors */
|
---|
125 | APP_FAIL, /* continuable error - no need to longjmp */
|
---|
126 | LIBPNG_ERROR, /* this and higher cause a longjmp */
|
---|
127 | LIBPNG_BUG, /* erroneous behavior in libpng */
|
---|
128 | APP_ERROR, /* such as out-of-memory in a callback */
|
---|
129 | QUIET, /* no normal messages */
|
---|
130 | USER_ERROR, /* such as file-not-found */
|
---|
131 | INTERNAL_ERROR
|
---|
132 | } error_level;
|
---|
133 | #define LEVEL_MASK 0xf /* where the level is in 'options' */
|
---|
134 |
|
---|
135 | #define STRICT 0x010 /* Fail on warnings as well as errors */
|
---|
136 | #define LOG 0x020 /* Log pass/fail to stdout */
|
---|
137 | #define CONTINUE 0x040 /* Continue on APP_FAIL errors */
|
---|
138 | #define SIZES 0x080 /* Report input and output sizes */
|
---|
139 | #define SEARCH 0x100 /* Search IDAT compression options */
|
---|
140 | #define NOWRITE 0x200 /* Do not write an output file */
|
---|
141 | #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
|
---|
142 | # define IGNORE_INDEX 0x400 /* Ignore out of range palette indices (BAD!) */
|
---|
143 | # ifdef PNG_GET_PALETTE_MAX_SUPPORTED
|
---|
144 | # define FIX_INDEX 0x800 /* 'Fix' out of range palette indices (OK) */
|
---|
145 | # endif /* GET_PALETTE_MAX */
|
---|
146 | #endif /* CHECK_FOR_INVALID_INDEX */
|
---|
147 | #define OPTION 0x80000000 /* Used for handling options */
|
---|
148 | #define LIST 0x80000001 /* Used for handling options */
|
---|
149 |
|
---|
150 | /* Result masks apply to the result bits in the 'results' field below; these
|
---|
151 | * bits are simple 1U<<error_level. A pass requires either nothing worse than
|
---|
152 | * warnings (--relaxes) or nothing worse than information (--strict)
|
---|
153 | */
|
---|
154 | #define RESULT_STRICT(r) (((r) & ~((1U<<WARNINGS)-1)) == 0)
|
---|
155 | #define RESULT_RELAXED(r) (((r) & ~((1U<<ERRORS)-1)) == 0)
|
---|
156 |
|
---|
157 | /* OPTION DEFINITIONS */
|
---|
158 | static const char range_lo[] = "low";
|
---|
159 | static const char range_hi[] = "high";
|
---|
160 | static const char all[] = "all";
|
---|
161 | #define RANGE(lo,hi) { range_lo, lo }, { range_hi, hi }
|
---|
162 | typedef struct value_list
|
---|
163 | {
|
---|
164 | const char *name; /* the command line name of the value */
|
---|
165 | int value; /* the actual value to use */
|
---|
166 | } value_list;
|
---|
167 |
|
---|
168 | static const value_list
|
---|
169 | #ifdef PNG_SW_COMPRESS_png_level
|
---|
170 | vl_compression[] =
|
---|
171 | {
|
---|
172 | /* Overall compression control. The order controls the search order for
|
---|
173 | * 'all'. Since the search is for the smallest the order used is low memory
|
---|
174 | * then high speed.
|
---|
175 | */
|
---|
176 | { "low-memory", PNG_COMPRESSION_LOW_MEMORY },
|
---|
177 | { "high-speed", PNG_COMPRESSION_HIGH_SPEED },
|
---|
178 | { "high-read-speed", PNG_COMPRESSION_HIGH_READ_SPEED },
|
---|
179 | { "low", PNG_COMPRESSION_LOW },
|
---|
180 | { "medium", PNG_COMPRESSION_MEDIUM },
|
---|
181 | { "old", PNG_COMPRESSION_COMPAT },
|
---|
182 | { "high", PNG_COMPRESSION_HIGH },
|
---|
183 | { all, 0 }
|
---|
184 | },
|
---|
185 | #endif /* SW_COMPRESS_png_level */
|
---|
186 |
|
---|
187 | #if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\
|
---|
188 | defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
|
---|
189 | vl_strategy[] =
|
---|
190 | {
|
---|
191 | /* This controls the order of search. */
|
---|
192 | { "huffman", Z_HUFFMAN_ONLY },
|
---|
193 | { "RLE", Z_RLE },
|
---|
194 | { "fixed", Z_FIXED }, /* the remainder do window searches */
|
---|
195 | { "filtered", Z_FILTERED },
|
---|
196 | { "default", Z_DEFAULT_STRATEGY },
|
---|
197 | { all, 0 }
|
---|
198 | },
|
---|
199 | #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
|
---|
200 | vl_windowBits_text[] =
|
---|
201 | {
|
---|
202 | { "default", MAX_WBITS/*from zlib*/ },
|
---|
203 | { "minimum", 8 },
|
---|
204 | RANGE(8, MAX_WBITS/*from zlib*/),
|
---|
205 | { all, 0 }
|
---|
206 | },
|
---|
207 | #endif /* text compression */
|
---|
208 | vl_level[] =
|
---|
209 | {
|
---|
210 | { "default", Z_DEFAULT_COMPRESSION /* this is -1 */ },
|
---|
211 | { "none", Z_NO_COMPRESSION },
|
---|
212 | { "speed", Z_BEST_SPEED },
|
---|
213 | { "best", Z_BEST_COMPRESSION },
|
---|
214 | { "0", Z_NO_COMPRESSION },
|
---|
215 | RANGE(1, 9), /* this deliberately excludes '0' */
|
---|
216 | { all, 0 }
|
---|
217 | },
|
---|
218 | vl_memLevel[] =
|
---|
219 | {
|
---|
220 | { "max", MAX_MEM_LEVEL }, /* zlib maximum */
|
---|
221 | { "1", 1 }, /* zlib minimum */
|
---|
222 | { "default", 8 }, /* zlib default */
|
---|
223 | { "2", 2 },
|
---|
224 | { "3", 3 },
|
---|
225 | { "4", 4 },
|
---|
226 | { "5", 5 }, /* for explicit testing */
|
---|
227 | RANGE(6, MAX_MEM_LEVEL/*zlib*/), /* exclude 5 and below: zlib bugs */
|
---|
228 | { all, 0 }
|
---|
229 | },
|
---|
230 | #endif /* WRITE_CUSTOMIZE_*COMPRESSION */
|
---|
231 | #ifdef PNG_WRITE_FILTER_SUPPORTED
|
---|
232 | vl_filter[] =
|
---|
233 | {
|
---|
234 | { all, PNG_ALL_FILTERS },
|
---|
235 | { "off", PNG_NO_FILTERS },
|
---|
236 | { "none", PNG_FILTER_NONE },
|
---|
237 | { "sub", PNG_FILTER_SUB },
|
---|
238 | { "up", PNG_FILTER_UP },
|
---|
239 | { "avg", PNG_FILTER_AVG },
|
---|
240 | { "paeth", PNG_FILTER_PAETH }
|
---|
241 | },
|
---|
242 | #endif /* WRITE_FILTER */
|
---|
243 | #ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
244 | # define PNGCP_TIME_READ 1
|
---|
245 | # define PNGCP_TIME_WRITE 2
|
---|
246 | vl_time[] =
|
---|
247 | {
|
---|
248 | { "both", PNGCP_TIME_READ+PNGCP_TIME_WRITE },
|
---|
249 | { "off", 0 },
|
---|
250 | { "read", PNGCP_TIME_READ },
|
---|
251 | { "write", PNGCP_TIME_WRITE }
|
---|
252 | },
|
---|
253 | #endif /* PNGCP_TIMING */
|
---|
254 | vl_IDAT_size[] = /* for png_set_IDAT_size */
|
---|
255 | {
|
---|
256 | { "default", 0x7FFFFFFF },
|
---|
257 | { "minimal", 1 },
|
---|
258 | RANGE(1, 0x7FFFFFFF)
|
---|
259 | },
|
---|
260 | #ifndef PNG_SW_IDAT_size
|
---|
261 | /* Pre 1.7 API: */
|
---|
262 | # define png_set_IDAT_size(p,v) png_set_compression_buffer_size(p, v)
|
---|
263 | #endif /* !SW_IDAT_size */
|
---|
264 | #define SL 8 /* stack limit in display, below */
|
---|
265 | vl_log_depth[] = { { "on", 1 }, { "off", 0 }, RANGE(0, SL) },
|
---|
266 | vl_on_off[] = { { "on", 1 }, { "off", 0 } };
|
---|
267 |
|
---|
268 | #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
|
---|
269 | static value_list
|
---|
270 | vl_windowBits_IDAT[] =
|
---|
271 | {
|
---|
272 | { "default", MAX_WBITS },
|
---|
273 | { "small", 9 },
|
---|
274 | RANGE(8, MAX_WBITS), /* modified by set_windowBits_hi */
|
---|
275 | { all, 0 }
|
---|
276 | };
|
---|
277 | #endif /* IDAT compression */
|
---|
278 |
|
---|
279 | typedef struct option
|
---|
280 | {
|
---|
281 | const char *name; /* name of the option */
|
---|
282 | png_uint_32 opt; /* an option, or OPTION or LIST */
|
---|
283 | png_byte search; /* Search on --search */
|
---|
284 | png_byte value_count; /* length of the list of values: */
|
---|
285 | const value_list *values; /* values for OPTION or LIST */
|
---|
286 | } option;
|
---|
287 |
|
---|
288 | static const option options[] =
|
---|
289 | {
|
---|
290 | /* struct display options, these are set when the command line is read */
|
---|
291 | # define S(n,v) { #n, v, 0, 2, vl_on_off },
|
---|
292 | S(verbose, VERBOSE)
|
---|
293 | S(warnings, WARNINGS)
|
---|
294 | S(errors, ERRORS)
|
---|
295 | S(quiet, QUIET)
|
---|
296 | S(strict, STRICT)
|
---|
297 | S(log, LOG)
|
---|
298 | S(continue, CONTINUE)
|
---|
299 | S(sizes, SIZES)
|
---|
300 | S(search, SEARCH)
|
---|
301 | S(nowrite, NOWRITE)
|
---|
302 | # ifdef IGNORE_INDEX
|
---|
303 | S(ignore-palette-index, IGNORE_INDEX)
|
---|
304 | # endif /* IGNORE_INDEX */
|
---|
305 | # ifdef FIX_INDEX
|
---|
306 | S(fix-palette-index, FIX_INDEX)
|
---|
307 | # endif /* FIX_INDEX */
|
---|
308 | # undef S
|
---|
309 |
|
---|
310 | /* OPTION settings, these and LIST settings are read on demand */
|
---|
311 | # define VLNAME(name) vl_ ## name
|
---|
312 | # define VLSIZE(name) voidcast(png_byte,\
|
---|
313 | (sizeof VLNAME(name))/(sizeof VLNAME(name)[0]))
|
---|
314 | # define VL(oname, name, type, search)\
|
---|
315 | { oname, type, search, VLSIZE(name), VLNAME(name) },
|
---|
316 | # define VLO(oname, name, search) VL(oname, name, OPTION, search)
|
---|
317 |
|
---|
318 | # ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
|
---|
319 | # define VLCIDAT(name) VLO(#name, name, 1/*search*/)
|
---|
320 | # ifdef PNG_SW_COMPRESS_level
|
---|
321 | # define VLCiCCP(name) VLO("ICC-profile-" #name, name, 0/*search*/)
|
---|
322 | # else
|
---|
323 | # define VLCiCCP(name)
|
---|
324 | # endif
|
---|
325 | # else
|
---|
326 | # define VLCIDAT(name)
|
---|
327 | # define VLCiCCP(name)
|
---|
328 | # endif /* WRITE_CUSTOMIZE_COMPRESSION */
|
---|
329 |
|
---|
330 | # ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
|
---|
331 | # define VLCzTXt(name) VLO("text-" #name, name, 0/*search*/)
|
---|
332 | # else
|
---|
333 | # define VLCzTXt(name)
|
---|
334 | # endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
|
---|
335 |
|
---|
336 | # define VLC(name) VLCIDAT(name) VLCiCCP(name) VLCzTXt(name)
|
---|
337 |
|
---|
338 | # ifdef PNG_SW_COMPRESS_png_level
|
---|
339 | /* The libpng compression level isn't searched because it justs sets the
|
---|
340 | * other things that are searched!
|
---|
341 | */
|
---|
342 | VLO("compression", compression, 0)
|
---|
343 | VLO("text-compression", compression, 0)
|
---|
344 | VLO("ICC-profile-compression", compression, 0)
|
---|
345 | # endif /* SW_COMPRESS_png_level */
|
---|
346 | VLC(strategy)
|
---|
347 | VLO("windowBits", windowBits_IDAT, 1)
|
---|
348 | # ifdef PNG_SW_COMPRESS_windowBits
|
---|
349 | VLO("ICC-profile-windowBits", windowBits_text/*sic*/, 0)
|
---|
350 | # endif
|
---|
351 | VLO("text-windowBits", windowBits_text, 0)
|
---|
352 | VLC(level)
|
---|
353 | VLC(memLevel)
|
---|
354 | VLO("IDAT-size", IDAT_size, 0)
|
---|
355 | VLO("log-depth", log_depth, 0)
|
---|
356 |
|
---|
357 | # undef VLO
|
---|
358 |
|
---|
359 | /* LIST settings */
|
---|
360 | # define VLL(name, search) VL(#name, name, LIST, search)
|
---|
361 | #ifdef PNG_WRITE_FILTER_SUPPORTED
|
---|
362 | VLL(filter, 0)
|
---|
363 | #endif /* WRITE_FILTER */
|
---|
364 | #ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
365 | VLL(time, 0)
|
---|
366 | #endif /* PNGCP_TIMING */
|
---|
367 | # undef VLL
|
---|
368 | # undef VL
|
---|
369 | };
|
---|
370 |
|
---|
371 | #ifdef __cplusplus
|
---|
372 | static const size_t option_count((sizeof options)/(sizeof options[0]));
|
---|
373 | #else /* !__cplusplus */
|
---|
374 | # define option_count ((sizeof options)/(sizeof options[0]))
|
---|
375 | #endif /* !__cplusplus */
|
---|
376 |
|
---|
377 | static const char *
|
---|
378 | cts(int ct)
|
---|
379 | {
|
---|
380 | switch (ct)
|
---|
381 | {
|
---|
382 | case PNG_COLOR_TYPE_PALETTE: return "P";
|
---|
383 | case PNG_COLOR_TYPE_GRAY: return "G";
|
---|
384 | case PNG_COLOR_TYPE_GRAY_ALPHA: return "GA";
|
---|
385 | case PNG_COLOR_TYPE_RGB: return "RGB";
|
---|
386 | case PNG_COLOR_TYPE_RGB_ALPHA: return "RGBA";
|
---|
387 | default: return "INVALID";
|
---|
388 | }
|
---|
389 | }
|
---|
390 |
|
---|
391 | struct display
|
---|
392 | {
|
---|
393 | jmp_buf error_return; /* Where to go to on error */
|
---|
394 | unsigned int errset; /* error_return is set */
|
---|
395 |
|
---|
396 | const char *operation; /* What is happening */
|
---|
397 | const char *filename; /* The name of the original file */
|
---|
398 | const char *output_file; /* The name of the output file */
|
---|
399 |
|
---|
400 | /* Used on both read and write: */
|
---|
401 | FILE *fp;
|
---|
402 |
|
---|
403 | /* Used on a read, both the original read and when validating a written
|
---|
404 | * image.
|
---|
405 | */
|
---|
406 | png_alloc_size_t read_size;
|
---|
407 | png_structp read_pp;
|
---|
408 | png_infop ip;
|
---|
409 | # if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
|
---|
410 | png_textp text_ptr; /* stash of text chunks */
|
---|
411 | int num_text;
|
---|
412 | int text_stashed;
|
---|
413 | # endif /* pre 1.7 */
|
---|
414 |
|
---|
415 | # ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
416 | struct timespec read_time;
|
---|
417 | struct timespec read_time_total;
|
---|
418 | struct timespec write_time;
|
---|
419 | struct timespec write_time_total;
|
---|
420 | # endif /* PNGCP_TIMING */
|
---|
421 |
|
---|
422 | /* Used to write a new image (the original info_ptr is used) */
|
---|
423 | # define MAX_SIZE ((png_alloc_size_t)(-1))
|
---|
424 | png_alloc_size_t write_size;
|
---|
425 | png_alloc_size_t best_size;
|
---|
426 | png_structp write_pp;
|
---|
427 |
|
---|
428 | /* Base file information */
|
---|
429 | png_alloc_size_t size;
|
---|
430 | png_uint_32 w;
|
---|
431 | png_uint_32 h;
|
---|
432 | int bpp;
|
---|
433 | png_byte ct;
|
---|
434 | int no_warnings; /* Do not output libpng warnings */
|
---|
435 | int min_windowBits; /* The windowBits range is 8..8 */
|
---|
436 |
|
---|
437 | /* Options handling */
|
---|
438 | png_uint_32 results; /* A mask of errors seen */
|
---|
439 | png_uint_32 options; /* See display_log below */
|
---|
440 | png_byte entry[option_count]; /* The selected entry+1 of an option
|
---|
441 | * that appears on the command line, or
|
---|
442 | * 0 if it was not given. */
|
---|
443 | int value[option_count]; /* Corresponding value */
|
---|
444 |
|
---|
445 | /* Compression exhaustive testing */
|
---|
446 | /* Temporary variables used only while testing a single collection of
|
---|
447 | * settings:
|
---|
448 | */
|
---|
449 | unsigned int csp; /* next stack entry to use */
|
---|
450 | unsigned int nsp; /* highest active entry+1 found so far */
|
---|
451 |
|
---|
452 | /* Values used while iterating through all the combinations of settings for a
|
---|
453 | * single file:
|
---|
454 | */
|
---|
455 | unsigned int tsp; /* nsp from the last run; this is the
|
---|
456 | * index+1 of the highest active entry on
|
---|
457 | * this run; this entry will be advanced.
|
---|
458 | */
|
---|
459 | int opt_string_start; /* Position in buffer for the first
|
---|
460 | * searched option; non-zero if earlier
|
---|
461 | * options were set on the command line.
|
---|
462 | */
|
---|
463 | struct stack
|
---|
464 | {
|
---|
465 | png_alloc_size_t best_size; /* Best so far for this option */
|
---|
466 | png_alloc_size_t lo_size;
|
---|
467 | png_alloc_size_t hi_size;
|
---|
468 | int lo, hi; /* For binary chop of a range */
|
---|
469 | int best_val; /* Best value found so far */
|
---|
470 | int opt_string_end; /* End of the option string in 'curr' */
|
---|
471 | png_byte opt; /* The option being tested */
|
---|
472 | png_byte entry; /* The next value entry to be tested */
|
---|
473 | png_byte end; /* This is the last entry */
|
---|
474 | } stack[SL]; /* Stack of entries being tested */
|
---|
475 | char curr[32*SL]; /* current options being tested */
|
---|
476 | char best[32*SL]; /* best options */
|
---|
477 |
|
---|
478 | char namebuf[FILENAME_MAX]; /* output file name */
|
---|
479 | };
|
---|
480 |
|
---|
481 | static void
|
---|
482 | display_init(struct display *dp)
|
---|
483 | /* Call this only once right at the start to initialize the control
|
---|
484 | * structure, the (struct buffer) lists are maintained across calls - the
|
---|
485 | * memory is not freed.
|
---|
486 | */
|
---|
487 | {
|
---|
488 | memset(dp, 0, sizeof *dp);
|
---|
489 | dp->operation = "internal error";
|
---|
490 | dp->filename = "command line";
|
---|
491 | dp->output_file = "no output file";
|
---|
492 | dp->options = WARNINGS; /* default to !verbose, !quiet */
|
---|
493 | dp->fp = NULL;
|
---|
494 | dp->read_pp = NULL;
|
---|
495 | dp->ip = NULL;
|
---|
496 | dp->write_pp = NULL;
|
---|
497 | dp->min_windowBits = -1; /* this is an OPTIND, so -1 won't match anything */
|
---|
498 | # if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
|
---|
499 | dp->text_ptr = NULL;
|
---|
500 | dp->num_text = 0;
|
---|
501 | dp->text_stashed = 0;
|
---|
502 | # endif /* pre 1.7 */
|
---|
503 | }
|
---|
504 |
|
---|
505 | static void
|
---|
506 | display_clean_read(struct display *dp)
|
---|
507 | {
|
---|
508 | if (dp->read_pp != NULL)
|
---|
509 | png_destroy_read_struct(&dp->read_pp, NULL, NULL);
|
---|
510 |
|
---|
511 | if (dp->fp != NULL)
|
---|
512 | {
|
---|
513 | FILE *fp = dp->fp;
|
---|
514 | dp->fp = NULL;
|
---|
515 | (void)fclose(fp);
|
---|
516 | }
|
---|
517 | }
|
---|
518 |
|
---|
519 | static void
|
---|
520 | display_clean_write(struct display *dp)
|
---|
521 | {
|
---|
522 | if (dp->fp != NULL)
|
---|
523 | {
|
---|
524 | FILE *fp = dp->fp;
|
---|
525 | dp->fp = NULL;
|
---|
526 | (void)fclose(fp);
|
---|
527 | }
|
---|
528 |
|
---|
529 | if (dp->write_pp != NULL)
|
---|
530 | png_destroy_write_struct(&dp->write_pp, dp->tsp > 0 ? NULL : &dp->ip);
|
---|
531 | }
|
---|
532 |
|
---|
533 | static void
|
---|
534 | display_clean(struct display *dp)
|
---|
535 | {
|
---|
536 | display_clean_read(dp);
|
---|
537 | display_clean_write(dp);
|
---|
538 | dp->output_file = NULL;
|
---|
539 |
|
---|
540 | # if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
|
---|
541 | /* This is actually created and used by the write code, but only
|
---|
542 | * once; it has to be retained for subsequent writes of the same file.
|
---|
543 | */
|
---|
544 | if (dp->text_stashed)
|
---|
545 | {
|
---|
546 | dp->text_stashed = 0;
|
---|
547 | dp->num_text = 0;
|
---|
548 | free(dp->text_ptr);
|
---|
549 | dp->text_ptr = NULL;
|
---|
550 | }
|
---|
551 | # endif /* pre 1.7 */
|
---|
552 |
|
---|
553 | /* leave the filename for error detection */
|
---|
554 | dp->results = 0; /* reset for next time */
|
---|
555 | }
|
---|
556 |
|
---|
557 | static void
|
---|
558 | display_destroy(struct display *dp)
|
---|
559 | {
|
---|
560 | /* Release any memory held in the display. */
|
---|
561 | display_clean(dp);
|
---|
562 | }
|
---|
563 |
|
---|
564 | static struct display *
|
---|
565 | get_dp(png_structp pp)
|
---|
566 | /* The display pointer is always stored in the png_struct error pointer */
|
---|
567 | {
|
---|
568 | struct display *dp = (struct display*)png_get_error_ptr(pp);
|
---|
569 |
|
---|
570 | if (dp == NULL)
|
---|
571 | {
|
---|
572 | fprintf(stderr, "pngcp: internal error (no display)\n");
|
---|
573 | exit(99); /* prevents a crash */
|
---|
574 | }
|
---|
575 |
|
---|
576 | return dp;
|
---|
577 | }
|
---|
578 |
|
---|
579 | /* error handling */
|
---|
580 | #ifdef __GNUC__
|
---|
581 | # define VGATTR __attribute__((__format__ (__printf__,3,4)))
|
---|
582 | /* Required to quiet GNUC warnings when the compiler sees a stdarg function
|
---|
583 | * that calls one of the stdio v APIs.
|
---|
584 | */
|
---|
585 | #else
|
---|
586 | # define VGATTR
|
---|
587 | #endif
|
---|
588 | static void VGATTR
|
---|
589 | display_log(struct display *dp, error_level level, const char *fmt, ...)
|
---|
590 | /* 'level' is as above, fmt is a stdio style format string. This routine
|
---|
591 | * does not return if level is above LIBPNG_WARNING
|
---|
592 | */
|
---|
593 | {
|
---|
594 | dp->results |= 1U << level;
|
---|
595 |
|
---|
596 | if (level > (error_level)(dp->options & LEVEL_MASK))
|
---|
597 | {
|
---|
598 | const char *lp;
|
---|
599 | va_list ap;
|
---|
600 |
|
---|
601 | switch (level)
|
---|
602 | {
|
---|
603 | case INFORMATION: lp = "information"; break;
|
---|
604 | case LIBPNG_WARNING: lp = "warning(libpng)"; break;
|
---|
605 | case APP_WARNING: lp = "warning(pngcp)"; break;
|
---|
606 | case APP_FAIL: lp = "error(continuable)"; break;
|
---|
607 | case LIBPNG_ERROR: lp = "error(libpng)"; break;
|
---|
608 | case LIBPNG_BUG: lp = "bug(libpng)"; break;
|
---|
609 | case APP_ERROR: lp = "error(pngcp)"; break;
|
---|
610 | case USER_ERROR: lp = "error(user)"; break;
|
---|
611 |
|
---|
612 | case INTERNAL_ERROR: /* anything unexpected is an internal error: */
|
---|
613 | case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
|
---|
614 | default: lp = "bug(pngcp)"; break;
|
---|
615 | }
|
---|
616 |
|
---|
617 | fprintf(stderr, "%s: %s: %s",
|
---|
618 | dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation);
|
---|
619 |
|
---|
620 | fprintf(stderr, ": ");
|
---|
621 |
|
---|
622 | va_start(ap, fmt);
|
---|
623 | vfprintf(stderr, fmt, ap);
|
---|
624 | va_end(ap);
|
---|
625 |
|
---|
626 | fputc('\n', stderr);
|
---|
627 | }
|
---|
628 | /* else do not output any message */
|
---|
629 |
|
---|
630 | /* Errors cause this routine to exit to the fail code */
|
---|
631 | if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE)))
|
---|
632 | {
|
---|
633 | if (dp->errset)
|
---|
634 | longjmp(dp->error_return, level);
|
---|
635 |
|
---|
636 | else
|
---|
637 | exit(99);
|
---|
638 | }
|
---|
639 | }
|
---|
640 |
|
---|
641 | #if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
|
---|
642 | static void
|
---|
643 | text_stash(struct display *dp)
|
---|
644 | {
|
---|
645 | /* libpng 1.6 and earlier fixed a bug whereby text chunks were written
|
---|
646 | * multiple times by png_write_png; the issue was that png_write_png passed
|
---|
647 | * the same png_info to both png_write_info and png_write_end. Rather than
|
---|
648 | * fixing it by recording the information in the png_struct, or by recording
|
---|
649 | * where to write the chunks, the fix made was to change the 'compression'
|
---|
650 | * field of the chunk to invalid values, rendering the png_info somewhat
|
---|
651 | * useless.
|
---|
652 | *
|
---|
653 | * The only fix for this given that we use the png_info more than once is to
|
---|
654 | * make a copy of the text chunks and png_set_text it each time. This adds a
|
---|
655 | * text chunks, so they get replicated, but only the new set gets written
|
---|
656 | * each time. This uses memory like crazy but there is no way to delete the
|
---|
657 | * useless chunks from the png_info.
|
---|
658 | *
|
---|
659 | * To make this slightly more efficient only the top level structure is
|
---|
660 | * copied; since the old strings are actually preserved (in 1.6 and earlier)
|
---|
661 | * this happens to work.
|
---|
662 | */
|
---|
663 | png_textp chunks = NULL;
|
---|
664 |
|
---|
665 | dp->num_text = png_get_text(dp->write_pp, dp->ip, &chunks, NULL);
|
---|
666 |
|
---|
667 | if (dp->num_text > 0)
|
---|
668 | {
|
---|
669 | dp->text_ptr = voidcast(png_textp, malloc(dp->num_text * sizeof *chunks));
|
---|
670 |
|
---|
671 | if (dp->text_ptr == NULL)
|
---|
672 | display_log(dp, APP_ERROR, "text chunks: stash malloc failed");
|
---|
673 |
|
---|
674 | else
|
---|
675 | memcpy(dp->text_ptr, chunks, dp->num_text * sizeof *chunks);
|
---|
676 | }
|
---|
677 |
|
---|
678 | dp->text_stashed = 1; /* regardless of whether there are chunks or not */
|
---|
679 | }
|
---|
680 |
|
---|
681 | #define text_stash(dp) if (!dp->text_stashed) text_stash(dp)
|
---|
682 |
|
---|
683 | static void
|
---|
684 | text_restore(struct display *dp)
|
---|
685 | {
|
---|
686 | /* libpng makes a copy, so this is fine: */
|
---|
687 | if (dp->text_ptr != NULL)
|
---|
688 | png_set_text(dp->write_pp, dp->ip, dp->text_ptr, dp->num_text);
|
---|
689 | }
|
---|
690 |
|
---|
691 | #define text_restore(dp) if (dp->text_stashed) text_restore(dp)
|
---|
692 |
|
---|
693 | #else
|
---|
694 | #define text_stash(dp) ((void)0)
|
---|
695 | #define text_restore(dp) ((void)0)
|
---|
696 | #endif /* pre 1.7 */
|
---|
697 |
|
---|
698 | /* OPTIONS:
|
---|
699 | *
|
---|
700 | * The command handles options of the forms:
|
---|
701 | *
|
---|
702 | * --option
|
---|
703 | * Turn an option on (Option)
|
---|
704 | * --no-option
|
---|
705 | * Turn an option off (Option)
|
---|
706 | * --option=value
|
---|
707 | * Set an option to a value (Value)
|
---|
708 | * --option=val1,val2,val3
|
---|
709 | * Set an option to a bitmask constructed from the values (List)
|
---|
710 | */
|
---|
711 | static png_byte
|
---|
712 | option_index(struct display *dp, const char *opt, size_t len)
|
---|
713 | /* Return the index (in options[]) of the given option, outputs an error if
|
---|
714 | * it does not exist. Takes the name of the option and a length (number of
|
---|
715 | * characters in the name).
|
---|
716 | */
|
---|
717 | {
|
---|
718 | png_byte j;
|
---|
719 |
|
---|
720 | for (j=0; j<option_count; ++j)
|
---|
721 | if (strncmp(options[j].name, opt, len) == 0 && options[j].name[len] == 0)
|
---|
722 | return j;
|
---|
723 |
|
---|
724 | /* If the setjmp buffer is set the code is asking for an option index; this
|
---|
725 | * is bad. Otherwise this is the command line option parsing.
|
---|
726 | */
|
---|
727 | display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
|
---|
728 | "%.*s: unknown option", (int)/*SAFE*/len, opt);
|
---|
729 | abort(); /* NOT REACHED */
|
---|
730 | }
|
---|
731 |
|
---|
732 | /* This works for an option name (no quotes): */
|
---|
733 | #define OPTIND(dp, name) option_index(dp, #name, (sizeof #name)-1)
|
---|
734 |
|
---|
735 | static int
|
---|
736 | get_option(struct display *dp, const char *opt, int *value)
|
---|
737 | {
|
---|
738 | png_byte i = option_index(dp, opt, strlen(opt));
|
---|
739 |
|
---|
740 | if (dp->entry[i]) /* option was set on command line */
|
---|
741 | {
|
---|
742 | *value = dp->value[i];
|
---|
743 | return 1;
|
---|
744 | }
|
---|
745 |
|
---|
746 | else
|
---|
747 | return 0;
|
---|
748 | }
|
---|
749 |
|
---|
750 | static int
|
---|
751 | set_opt_string_(struct display *dp, unsigned int sp, png_byte opt,
|
---|
752 | const char *entry_name)
|
---|
753 | /* Add the appropriate option string to dp->curr. */
|
---|
754 | {
|
---|
755 | int offset, add;
|
---|
756 |
|
---|
757 | if (sp > 0)
|
---|
758 | offset = dp->stack[sp-1].opt_string_end;
|
---|
759 |
|
---|
760 | else
|
---|
761 | offset = dp->opt_string_start;
|
---|
762 |
|
---|
763 | if (entry_name == range_lo)
|
---|
764 | add = sprintf(dp->curr+offset, " --%s=%d", options[opt].name,
|
---|
765 | dp->value[opt]);
|
---|
766 |
|
---|
767 | else
|
---|
768 | add = sprintf(dp->curr+offset, " --%s=%s", options[opt].name, entry_name);
|
---|
769 |
|
---|
770 | if (add < 0)
|
---|
771 | display_log(dp, INTERNAL_ERROR, "sprintf failed");
|
---|
772 |
|
---|
773 | assert(offset+add < (int)/*SAFE*/sizeof dp->curr);
|
---|
774 | return offset+add;
|
---|
775 | }
|
---|
776 |
|
---|
777 | static void
|
---|
778 | set_opt_string(struct display *dp, unsigned int sp)
|
---|
779 | /* Add the appropriate option string to dp->curr. */
|
---|
780 | {
|
---|
781 | dp->stack[sp].opt_string_end = set_opt_string_(dp, sp, dp->stack[sp].opt,
|
---|
782 | options[dp->stack[sp].opt].values[dp->stack[sp].entry].name);
|
---|
783 | }
|
---|
784 |
|
---|
785 | static void
|
---|
786 | record_opt(struct display *dp, png_byte opt, const char *entry_name)
|
---|
787 | /* Record this option in dp->curr; called for an option not being searched,
|
---|
788 | * the caller passes in the name of the value, or range_lo to use the
|
---|
789 | * numerical value.
|
---|
790 | */
|
---|
791 | {
|
---|
792 | unsigned int sp = dp->csp; /* stack entry of next searched option */
|
---|
793 |
|
---|
794 | if (sp >= dp->tsp)
|
---|
795 | {
|
---|
796 | /* At top of stack; add the opt string for this entry to the previous
|
---|
797 | * searched entry or the start of the dp->curr buffer if there is nothing
|
---|
798 | * on the stack yet (sp == 0).
|
---|
799 | */
|
---|
800 | int offset = set_opt_string_(dp, sp, opt, entry_name);
|
---|
801 |
|
---|
802 | if (sp > 0)
|
---|
803 | dp->stack[sp-1].opt_string_end = offset;
|
---|
804 |
|
---|
805 | else
|
---|
806 | dp->opt_string_start = offset;
|
---|
807 | }
|
---|
808 |
|
---|
809 | /* else do nothing: option already recorded */
|
---|
810 | }
|
---|
811 |
|
---|
812 | static int
|
---|
813 | opt_list_end(struct display *dp, png_byte opt, png_byte entry)
|
---|
814 | {
|
---|
815 | if (options[opt].values[entry].name == range_lo)
|
---|
816 | return entry+1U >= options[opt].value_count /* missing range_hi */ ||
|
---|
817 | options[opt].values[entry+1U].name != range_hi /* likewise */ ||
|
---|
818 | options[opt].values[entry+1U].value <= dp->value[opt] /* range end */;
|
---|
819 |
|
---|
820 | else
|
---|
821 | return entry+1U >= options[opt].value_count /* missing 'all' */ ||
|
---|
822 | options[opt].values[entry+1U].name == all /* last entry */;
|
---|
823 | }
|
---|
824 |
|
---|
825 | static void
|
---|
826 | push_opt(struct display *dp, unsigned int sp, png_byte opt, int search)
|
---|
827 | /* Push a new option onto the stack, initializing the new stack entry
|
---|
828 | * appropriately; this does all the work of next_opt (setting end/nsp) for
|
---|
829 | * the first entry in the list.
|
---|
830 | */
|
---|
831 | {
|
---|
832 | png_byte entry;
|
---|
833 | const char *entry_name;
|
---|
834 |
|
---|
835 | assert(sp == dp->tsp && sp < SL);
|
---|
836 |
|
---|
837 | /* The starting entry is entry 0 unless there is a range in which case it is
|
---|
838 | * the entry corresponding to range_lo:
|
---|
839 | */
|
---|
840 | entry = options[opt].value_count;
|
---|
841 | assert(entry > 0U);
|
---|
842 |
|
---|
843 | do
|
---|
844 | {
|
---|
845 | entry_name = options[opt].values[--entry].name;
|
---|
846 | if (entry_name == range_lo)
|
---|
847 | break;
|
---|
848 | }
|
---|
849 | while (entry > 0U);
|
---|
850 |
|
---|
851 | dp->tsp = sp+1U;
|
---|
852 | dp->stack[sp].best_size =
|
---|
853 | dp->stack[sp].lo_size =
|
---|
854 | dp->stack[sp].hi_size = MAX_SIZE;
|
---|
855 |
|
---|
856 | if (search && entry_name == range_lo) /* search this range */
|
---|
857 | {
|
---|
858 | dp->stack[sp].lo = options[opt].values[entry].value;
|
---|
859 | /* check for a mal-formed RANGE above: */
|
---|
860 | assert(entry+1 < options[opt].value_count &&
|
---|
861 | options[opt].values[entry+1].name == range_hi);
|
---|
862 | dp->stack[sp].hi = options[opt].values[entry+1].value;
|
---|
863 | }
|
---|
864 |
|
---|
865 | else
|
---|
866 | {
|
---|
867 | /* next_opt will just iterate over the range. */
|
---|
868 | dp->stack[sp].lo = INT_MAX;
|
---|
869 | dp->stack[sp].hi = INT_MIN; /* Prevent range chop */
|
---|
870 | }
|
---|
871 |
|
---|
872 | dp->stack[sp].opt = opt;
|
---|
873 | dp->stack[sp].entry = entry;
|
---|
874 | dp->stack[sp].best_val = dp->value[opt] = options[opt].values[entry].value;
|
---|
875 |
|
---|
876 | set_opt_string(dp, sp);
|
---|
877 |
|
---|
878 | /* This works for the search case too; if the range has only one entry 'end'
|
---|
879 | * will be marked here.
|
---|
880 | */
|
---|
881 | if (opt_list_end(dp, opt, entry))
|
---|
882 | {
|
---|
883 | dp->stack[sp].end = 1;
|
---|
884 | /* Skip the warning if pngcp did this itself. See the code in
|
---|
885 | * set_windowBits_hi.
|
---|
886 | */
|
---|
887 | if (opt != dp->min_windowBits)
|
---|
888 | display_log(dp, APP_WARNING, "%s: only testing one value",
|
---|
889 | options[opt].name);
|
---|
890 | }
|
---|
891 |
|
---|
892 | else
|
---|
893 | {
|
---|
894 | dp->stack[sp].end = 0;
|
---|
895 | dp->nsp = dp->tsp;
|
---|
896 | }
|
---|
897 |
|
---|
898 | /* Do a lazy cache of the text chunks for libpng 1.6 and earlier; this is
|
---|
899 | * because they can only be written once(!) so if we are going to re-use the
|
---|
900 | * png_info we need a copy.
|
---|
901 | */
|
---|
902 | text_stash(dp);
|
---|
903 | }
|
---|
904 |
|
---|
905 | static void
|
---|
906 | next_opt(struct display *dp, unsigned int sp)
|
---|
907 | /* Return the next value for this option. When called 'sp' is expected to be
|
---|
908 | * the topmost stack entry - only the topmost entry changes each time round -
|
---|
909 | * and there must be a valid entry to return. next_opt will set dp->nsp to
|
---|
910 | * sp+1 if more entries are available, otherwise it will not change it and
|
---|
911 | * set dp->stack[s].end to true.
|
---|
912 | */
|
---|
913 | {
|
---|
914 | int search = 0;
|
---|
915 | png_byte entry, opt;
|
---|
916 | const char *entry_name;
|
---|
917 |
|
---|
918 | /* dp->stack[sp] must be the top stack entry and it must be active: */
|
---|
919 | assert(sp+1U == dp->tsp && !dp->stack[sp].end);
|
---|
920 |
|
---|
921 | opt = dp->stack[sp].opt;
|
---|
922 | entry = dp->stack[sp].entry;
|
---|
923 | assert(entry+1U < options[opt].value_count);
|
---|
924 | entry_name = options[opt].values[entry].name;
|
---|
925 | assert(entry_name != NULL);
|
---|
926 |
|
---|
927 | /* For ranges increment the value but don't change the entry, for all other
|
---|
928 | * cases move to the next entry and load its value:
|
---|
929 | */
|
---|
930 | if (entry_name == range_lo) /* a range */
|
---|
931 | {
|
---|
932 | /* A range can be iterated over or searched. The default iteration option
|
---|
933 | * is indicated by hi < lo on the stack, otherwise the range being search
|
---|
934 | * is [lo..hi] (inclusive).
|
---|
935 | */
|
---|
936 | if (dp->stack[sp].lo > dp->stack[sp].hi)
|
---|
937 | dp->value[opt]++;
|
---|
938 |
|
---|
939 | else
|
---|
940 | {
|
---|
941 | /* This is the best size found for this option value: */
|
---|
942 | png_alloc_size_t best_size = dp->stack[sp].best_size;
|
---|
943 | int lo = dp->stack[sp].lo;
|
---|
944 | int hi = dp->stack[sp].hi;
|
---|
945 | int val = dp->value[opt];
|
---|
946 |
|
---|
947 | search = 1; /* end is determined here */
|
---|
948 | assert(best_size < MAX_SIZE);
|
---|
949 |
|
---|
950 | if (val == lo)
|
---|
951 | {
|
---|
952 | /* Finding the best for the low end of the range: */
|
---|
953 | dp->stack[sp].lo_size = best_size;
|
---|
954 | assert(hi > val);
|
---|
955 |
|
---|
956 | if (hi == val+1) /* only 2 entries */
|
---|
957 | dp->stack[sp].end = 1;
|
---|
958 |
|
---|
959 | val = hi;
|
---|
960 | }
|
---|
961 |
|
---|
962 | else if (val == hi)
|
---|
963 | {
|
---|
964 | dp->stack[sp].hi_size = best_size;
|
---|
965 | assert(val > lo+1); /* else 'end' set above */
|
---|
966 |
|
---|
967 | if (val == lo+2) /* only three entries to test */
|
---|
968 | dp->stack[sp].end = 1;
|
---|
969 |
|
---|
970 | val = (lo + val)/2;
|
---|
971 | }
|
---|
972 |
|
---|
973 | else
|
---|
974 | {
|
---|
975 | png_alloc_size_t lo_size = dp->stack[sp].lo_size;
|
---|
976 | png_alloc_size_t hi_size = dp->stack[sp].hi_size;
|
---|
977 |
|
---|
978 | /* lo and hi should have been tested. */
|
---|
979 | assert(lo_size < MAX_SIZE && hi_size < MAX_SIZE);
|
---|
980 |
|
---|
981 | /* These cases arise with the 'probe' handling below when there is a
|
---|
982 | * dip or peak in the size curve.
|
---|
983 | */
|
---|
984 | if (val < lo) /* probing a new lo */
|
---|
985 | {
|
---|
986 | /* Swap lo and val: */
|
---|
987 | dp->stack[sp].lo = val;
|
---|
988 | dp->stack[sp].lo_size = best_size;
|
---|
989 | val = lo;
|
---|
990 | best_size = lo_size;
|
---|
991 | lo = dp->stack[sp].lo;
|
---|
992 | lo_size = dp->stack[sp].lo_size;
|
---|
993 | }
|
---|
994 |
|
---|
995 | else if (val > hi) /* probing a new hi */
|
---|
996 | {
|
---|
997 | /* Swap hi and val: */
|
---|
998 | dp->stack[sp].hi = val;
|
---|
999 | dp->stack[sp].hi_size = best_size;
|
---|
1000 | val = hi;
|
---|
1001 | best_size = hi_size;
|
---|
1002 | hi = dp->stack[sp].hi;
|
---|
1003 | hi_size = dp->stack[sp].hi_size;
|
---|
1004 | }
|
---|
1005 |
|
---|
1006 | /* The following should be true or something got messed up above. */
|
---|
1007 | assert(lo < val && val < hi);
|
---|
1008 |
|
---|
1009 | /* If there are only four entries (lo, val, hi plus one more) just
|
---|
1010 | * test the remaining entry.
|
---|
1011 | */
|
---|
1012 | if (hi == lo+3)
|
---|
1013 | {
|
---|
1014 | /* Because of the 'probe' code val can either be lo+1 or hi-1; we
|
---|
1015 | * need to test the other.
|
---|
1016 | */
|
---|
1017 | val = lo + ((val == lo+1) ? 2 : 1);
|
---|
1018 | assert(lo < val && val < hi);
|
---|
1019 | dp->stack[sp].end = 1;
|
---|
1020 | }
|
---|
1021 |
|
---|
1022 | else
|
---|
1023 | {
|
---|
1024 | /* There are at least 2 entries still untested between lo and hi,
|
---|
1025 | * i.e. hi >= lo+4. 'val' is the midpoint +/- 0.5
|
---|
1026 | *
|
---|
1027 | * Separate out the four easy cases when lo..val..hi are
|
---|
1028 | * monotonically decreased or (more weird) increasing:
|
---|
1029 | */
|
---|
1030 | assert(hi > lo+3);
|
---|
1031 |
|
---|
1032 | if (lo_size <= best_size && best_size <= hi_size)
|
---|
1033 | {
|
---|
1034 | /* Select the low range; testing this first favours the low
|
---|
1035 | * range over the high range when everything comes out equal.
|
---|
1036 | * Because of the probing 'val' may be lo+1. In that case end
|
---|
1037 | * the search and set 'val' to lo+2.
|
---|
1038 | */
|
---|
1039 | if (val == lo+1)
|
---|
1040 | {
|
---|
1041 | ++val;
|
---|
1042 | dp->stack[sp].end = 1;
|
---|
1043 | }
|
---|
1044 |
|
---|
1045 | else
|
---|
1046 | {
|
---|
1047 | dp->stack[sp].hi = hi = val;
|
---|
1048 | dp->stack[sp].hi_size = best_size;
|
---|
1049 | val = (lo + val) / 2;
|
---|
1050 | }
|
---|
1051 | }
|
---|
1052 |
|
---|
1053 | else if (lo_size >= best_size && best_size >= hi_size)
|
---|
1054 | {
|
---|
1055 | /* Monotonically decreasing size; this is the expected case.
|
---|
1056 | * Select the high end of the range. As above, val may be
|
---|
1057 | * hi-1.
|
---|
1058 | */
|
---|
1059 | if (val == hi-1)
|
---|
1060 | {
|
---|
1061 | --val;
|
---|
1062 | dp->stack[sp].end = 1;
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | else
|
---|
1066 | {
|
---|
1067 | dp->stack[sp].lo = lo = val;
|
---|
1068 | dp->stack[sp].lo_size = best_size;
|
---|
1069 | val = (val + hi) / 2;
|
---|
1070 | }
|
---|
1071 | }
|
---|
1072 |
|
---|
1073 | /* If both those tests failed 'best_size' is either greater than
|
---|
1074 | * or less than both lo_size and hi_size. There is a peak or dip
|
---|
1075 | * in the curve of sizes from lo to hi and val is on the peak or
|
---|
1076 | * dip.
|
---|
1077 | *
|
---|
1078 | * Because the ranges being searched as so small (level is 1..9,
|
---|
1079 | * windowBits 8..15, memLevel 1..9) there will only be at most
|
---|
1080 | * three untested values between lo..val and val..hi, so solve
|
---|
1081 | * the problem by probing down from hi or up from lo, whichever
|
---|
1082 | * is the higher.
|
---|
1083 | *
|
---|
1084 | * This is the place where 'val' is set to outside the range
|
---|
1085 | * lo..hi, described as 'probing', though maybe 'narrowing' would
|
---|
1086 | * be more accurate.
|
---|
1087 | */
|
---|
1088 | else if (lo_size <= hi_size) /* down from hi */
|
---|
1089 | {
|
---|
1090 | dp->stack[sp].hi = val;
|
---|
1091 | dp->stack[sp].hi_size = best_size;
|
---|
1092 | val = --hi;
|
---|
1093 | }
|
---|
1094 |
|
---|
1095 | else /* up from low */
|
---|
1096 | {
|
---|
1097 | dp->stack[sp].lo = val;
|
---|
1098 | dp->stack[sp].lo_size = best_size;
|
---|
1099 | val = ++lo;
|
---|
1100 | }
|
---|
1101 |
|
---|
1102 | /* lo and hi are still the true range limits, check for the end
|
---|
1103 | * condition.
|
---|
1104 | */
|
---|
1105 | assert(hi > lo+1);
|
---|
1106 | if (hi <= lo+2)
|
---|
1107 | dp->stack[sp].end = 1;
|
---|
1108 | }
|
---|
1109 | }
|
---|
1110 |
|
---|
1111 | assert(val != dp->stack[sp].best_val); /* should be a new value */
|
---|
1112 | dp->value[opt] = val;
|
---|
1113 | dp->stack[sp].best_size = MAX_SIZE;
|
---|
1114 | }
|
---|
1115 | }
|
---|
1116 |
|
---|
1117 | else
|
---|
1118 | {
|
---|
1119 | /* Increment 'entry' */
|
---|
1120 | dp->value[opt] = options[opt].values[++entry].value;
|
---|
1121 | dp->stack[sp].entry = entry;
|
---|
1122 | }
|
---|
1123 |
|
---|
1124 | set_opt_string(dp, sp);
|
---|
1125 |
|
---|
1126 | if (!search && opt_list_end(dp, opt, entry)) /* end of list */
|
---|
1127 | dp->stack[sp].end = 1;
|
---|
1128 |
|
---|
1129 | else if (!dp->stack[sp].end) /* still active after all these tests */
|
---|
1130 | dp->nsp = dp->tsp;
|
---|
1131 | }
|
---|
1132 |
|
---|
1133 | static int
|
---|
1134 | compare_option(const struct display *dp, unsigned int sp)
|
---|
1135 | {
|
---|
1136 | int opt = dp->stack[sp].opt;
|
---|
1137 |
|
---|
1138 | /* If the best so far is numerically less than the current value the
|
---|
1139 | * current set of options is invariably worse.
|
---|
1140 | */
|
---|
1141 | if (dp->stack[sp].best_val < dp->value[opt])
|
---|
1142 | return -1;
|
---|
1143 |
|
---|
1144 | /* Lists of options are searched out of numerical order (currently only
|
---|
1145 | * strategy), so only return +1 here when a range is being searched.
|
---|
1146 | */
|
---|
1147 | else if (dp->stack[sp].best_val > dp->value[opt])
|
---|
1148 | {
|
---|
1149 | if (dp->stack[sp].lo <= dp->stack[sp].hi /*searching*/)
|
---|
1150 | return 1;
|
---|
1151 |
|
---|
1152 | else
|
---|
1153 | return -1;
|
---|
1154 | }
|
---|
1155 |
|
---|
1156 | else
|
---|
1157 | return 0; /* match; current value is the best one */
|
---|
1158 | }
|
---|
1159 |
|
---|
1160 | static int
|
---|
1161 | advance_opt(struct display *dp, png_byte opt, int search)
|
---|
1162 | {
|
---|
1163 | unsigned int sp = dp->csp++; /* my stack entry */
|
---|
1164 |
|
---|
1165 | assert(sp >= dp->nsp); /* nsp starts off zero */
|
---|
1166 |
|
---|
1167 | /* If the entry was active in the previous run dp->stack[sp] is already
|
---|
1168 | * set up and dp->tsp will be greater than sp, otherwise a new entry
|
---|
1169 | * needs to be created.
|
---|
1170 | *
|
---|
1171 | * dp->nsp is handled this way:
|
---|
1172 | *
|
---|
1173 | * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are
|
---|
1174 | * both set (by push_opt) to the next stack entry *unless* there is
|
---|
1175 | * only one entry in the new list, in which case dp->stack[sp].end
|
---|
1176 | * is set.
|
---|
1177 | *
|
---|
1178 | * 2) For the top stack entry next_opt is called. The entry must be
|
---|
1179 | * active (dp->stack[sp].end is not set) and either 'nsp' or 'end'
|
---|
1180 | * will be updated as appropriate.
|
---|
1181 | *
|
---|
1182 | * 3) For lower stack entries nsp is set unless the stack entry is
|
---|
1183 | * already at the end. This means that when all the higher entries
|
---|
1184 | * are popped this entry will be too.
|
---|
1185 | */
|
---|
1186 | if (sp >= dp->tsp)
|
---|
1187 | {
|
---|
1188 | push_opt(dp, sp, opt, search); /* This sets tsp to sp+1 */
|
---|
1189 | return 1; /* initialized */
|
---|
1190 | }
|
---|
1191 |
|
---|
1192 | else
|
---|
1193 | {
|
---|
1194 | int ret = 0; /* unchanged */
|
---|
1195 |
|
---|
1196 | /* An option that is already on the stack; update best_size and best_val
|
---|
1197 | * if appropriate. On the first run there are no previous values and
|
---|
1198 | * dp->write_size will be MAX_SIZE, however on the first run dp->tsp
|
---|
1199 | * starts off as 0.
|
---|
1200 | */
|
---|
1201 | assert(dp->write_size > 0U && dp->write_size < MAX_SIZE);
|
---|
1202 |
|
---|
1203 | if (dp->stack[sp].best_size > dp->write_size ||
|
---|
1204 | (dp->stack[sp].best_size == dp->write_size &&
|
---|
1205 | compare_option(dp, sp) > 0))
|
---|
1206 | {
|
---|
1207 | dp->stack[sp].best_size = dp->write_size;
|
---|
1208 | dp->stack[sp].best_val = dp->value[opt];
|
---|
1209 | }
|
---|
1210 |
|
---|
1211 | if (sp+1U >= dp->tsp)
|
---|
1212 | {
|
---|
1213 | next_opt(dp, sp);
|
---|
1214 | ret = 1; /* advanced */
|
---|
1215 | }
|
---|
1216 |
|
---|
1217 | else if (!dp->stack[sp].end) /* Active, not at top of stack */
|
---|
1218 | dp->nsp = sp+1U;
|
---|
1219 |
|
---|
1220 | return ret; /* advanced || unchanged */
|
---|
1221 | }
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | static int
|
---|
1225 | getallopts_(struct display *dp, png_byte opt, int *value, int record)
|
---|
1226 | /* Like getop but iterate over all the values if the option was set to "all".
|
---|
1227 | */
|
---|
1228 | {
|
---|
1229 | if (dp->entry[opt]) /* option was set on command line */
|
---|
1230 | {
|
---|
1231 | /* Simple, single value, entries don't have a stack frame and have a fixed
|
---|
1232 | * value (it doesn't change once set on the command line). Otherwise the
|
---|
1233 | * value (entry) selected from the command line is 'all':
|
---|
1234 | */
|
---|
1235 | const char *entry_name = options[opt].values[dp->entry[opt]-1].name;
|
---|
1236 |
|
---|
1237 | if (entry_name == all)
|
---|
1238 | (void)advance_opt(dp, opt, 0/*do not search; iterate*/);
|
---|
1239 |
|
---|
1240 | else if (record)
|
---|
1241 | record_opt(dp, opt, entry_name);
|
---|
1242 |
|
---|
1243 | *value = dp->value[opt];
|
---|
1244 | return 1; /* set */
|
---|
1245 | }
|
---|
1246 |
|
---|
1247 | else
|
---|
1248 | return 0; /* not set */
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | static int
|
---|
1252 | getallopts(struct display *dp, const char *opt_str, int *value)
|
---|
1253 | {
|
---|
1254 | return getallopts_(dp, option_index(dp, opt_str, strlen(opt_str)), value, 0);
|
---|
1255 | }
|
---|
1256 |
|
---|
1257 | static int
|
---|
1258 | getsearchopts(struct display *dp, const char *opt_str, int *value)
|
---|
1259 | /* As above except that if the option was not set try a search */
|
---|
1260 | {
|
---|
1261 | png_byte istrat;
|
---|
1262 | png_byte opt = option_index(dp, opt_str, strlen(opt_str));
|
---|
1263 | int record = options[opt].search;
|
---|
1264 | const char *entry_name;
|
---|
1265 |
|
---|
1266 | /* If it was set on the command line honour the setting, including 'all'
|
---|
1267 | * which will override the built in search:
|
---|
1268 | */
|
---|
1269 | if (getallopts_(dp, opt, value, record))
|
---|
1270 | return 1;
|
---|
1271 |
|
---|
1272 | else if (!record) /* not a search option */
|
---|
1273 | return 0; /* unset and not searched */
|
---|
1274 |
|
---|
1275 | /* Otherwise decide what to do here. */
|
---|
1276 | istrat = OPTIND(dp, strategy);
|
---|
1277 | entry_name = range_lo; /* record the value, not the name */
|
---|
1278 |
|
---|
1279 | if (opt == istrat) /* search all strategies */
|
---|
1280 | (void)advance_opt(dp, opt, 0/*iterate*/), record=0;
|
---|
1281 |
|
---|
1282 | else if (opt == OPTIND(dp, level))
|
---|
1283 | {
|
---|
1284 | /* Both RLE and HUFFMAN don't benefit from level increases */
|
---|
1285 | if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY)
|
---|
1286 | dp->value[opt] = 1;
|
---|
1287 |
|
---|
1288 | else /* fixed, filtered or default */
|
---|
1289 | (void)advance_opt(dp, opt, 1/*search*/), record=0;
|
---|
1290 | }
|
---|
1291 |
|
---|
1292 | else if (opt == OPTIND(dp, windowBits))
|
---|
1293 | {
|
---|
1294 | /* Changing windowBits for strategies that do not search the window is
|
---|
1295 | * pointless. Huffman-only does not search, RLE only searches backwards
|
---|
1296 | * one byte, so given that the maximum string length is 258, a windowBits
|
---|
1297 | * of 9 is always sufficient.
|
---|
1298 | */
|
---|
1299 | if (dp->value[istrat] == Z_HUFFMAN_ONLY)
|
---|
1300 | dp->value[opt] = 8;
|
---|
1301 |
|
---|
1302 | else if (dp->value[istrat] == Z_RLE)
|
---|
1303 | dp->value[opt] = 9;
|
---|
1304 |
|
---|
1305 | else /* fixed, filtered or default */
|
---|
1306 | (void)advance_opt(dp, opt, 1/*search*/), record=0;
|
---|
1307 | }
|
---|
1308 |
|
---|
1309 | else if (opt == OPTIND(dp, memLevel))
|
---|
1310 | {
|
---|
1311 | # if 0
|
---|
1312 | (void)advance_opt(dp, opt, 0/*all*/), record=0;
|
---|
1313 | # else
|
---|
1314 | dp->value[opt] = MAX_MEM_LEVEL;
|
---|
1315 | # endif
|
---|
1316 | }
|
---|
1317 |
|
---|
1318 | else /* something else */
|
---|
1319 | assert(0=="reached");
|
---|
1320 |
|
---|
1321 | if (record)
|
---|
1322 | record_opt(dp, opt, entry_name);
|
---|
1323 |
|
---|
1324 | /* One of the above searched options: */
|
---|
1325 | *value = dp->value[opt];
|
---|
1326 | return 1;
|
---|
1327 | }
|
---|
1328 |
|
---|
1329 | static int
|
---|
1330 | find_val(struct display *dp, png_byte opt, const char *str, size_t len)
|
---|
1331 | /* Like option_index but sets (index+i) of the entry in options[opt] that
|
---|
1332 | * matches str[0..len-1] into dp->entry[opt] as well as returning the actual
|
---|
1333 | * value.
|
---|
1334 | */
|
---|
1335 | {
|
---|
1336 | int rlo = INT_MAX, rhi = INT_MIN;
|
---|
1337 | png_byte j, irange = 0;
|
---|
1338 |
|
---|
1339 | for (j=1U; j<=options[opt].value_count; ++j)
|
---|
1340 | {
|
---|
1341 | if (strncmp(options[opt].values[j-1U].name, str, len) == 0 &&
|
---|
1342 | options[opt].values[j-1U].name[len] == 0)
|
---|
1343 | {
|
---|
1344 | dp->entry[opt] = j;
|
---|
1345 | return options[opt].values[j-1U].value;
|
---|
1346 | }
|
---|
1347 | else if (options[opt].values[j-1U].name == range_lo)
|
---|
1348 | rlo = options[opt].values[j-1U].value, irange = j;
|
---|
1349 | else if (options[opt].values[j-1U].name == range_hi)
|
---|
1350 | rhi = options[opt].values[j-1U].value;
|
---|
1351 | }
|
---|
1352 |
|
---|
1353 | /* No match on the name, but there may be a range. */
|
---|
1354 | if (irange > 0)
|
---|
1355 | {
|
---|
1356 | char *ep = NULL;
|
---|
1357 | long l = strtol(str, &ep, 0);
|
---|
1358 |
|
---|
1359 | if (ep == str+len && l >= rlo && l <= rhi)
|
---|
1360 | {
|
---|
1361 | dp->entry[opt] = irange; /* range_lo */
|
---|
1362 | return (int)/*SAFE*/l;
|
---|
1363 | }
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
|
---|
1367 | "%s: unknown value setting '%.*s'", options[opt].name,
|
---|
1368 | (int)/*SAFE*/len, str);
|
---|
1369 | abort(); /* NOT REACHED */
|
---|
1370 | }
|
---|
1371 |
|
---|
1372 | static int
|
---|
1373 | opt_check(struct display *dp, const char *arg)
|
---|
1374 | {
|
---|
1375 | assert(dp->errset == 0);
|
---|
1376 |
|
---|
1377 | if (arg != NULL && arg[0] == '-' && arg[1] == '-')
|
---|
1378 | {
|
---|
1379 | int i = 0, negate = (strncmp(arg+2, "no-", 3) == 0), val;
|
---|
1380 | png_byte j;
|
---|
1381 |
|
---|
1382 | if (negate)
|
---|
1383 | arg += 5; /* --no- */
|
---|
1384 |
|
---|
1385 | else
|
---|
1386 | arg += 2; /* -- */
|
---|
1387 |
|
---|
1388 | /* Find the length (expect arg\0 or arg=) */
|
---|
1389 | while (arg[i] != 0 && arg[i] != '=') ++i;
|
---|
1390 |
|
---|
1391 | /* So arg[0..i-1] is the argument name, this does not return if this isn't
|
---|
1392 | * a valid option name.
|
---|
1393 | */
|
---|
1394 | j = option_index(dp, arg, i);
|
---|
1395 |
|
---|
1396 | /* It matcheth an option; check the remainder. */
|
---|
1397 | if (arg[i] == 0) /* no specified value, use the default */
|
---|
1398 | {
|
---|
1399 | val = options[j].values[negate].value;
|
---|
1400 | dp->entry[j] = (png_byte)/*SAFE*/(negate + 1U);
|
---|
1401 | }
|
---|
1402 |
|
---|
1403 | else
|
---|
1404 | {
|
---|
1405 | const char *list = arg + (i+1);
|
---|
1406 |
|
---|
1407 | /* Expect a single value here unless this is a list, in which case
|
---|
1408 | * multiple values are combined.
|
---|
1409 | */
|
---|
1410 | if (options[j].opt != LIST)
|
---|
1411 | {
|
---|
1412 | /* find_val sets 'dp->entry[j]' to a non-zero value: */
|
---|
1413 | val = find_val(dp, j, list, strlen(list));
|
---|
1414 |
|
---|
1415 | if (negate)
|
---|
1416 | {
|
---|
1417 | if (options[j].opt < OPTION)
|
---|
1418 | val = !val;
|
---|
1419 |
|
---|
1420 | else
|
---|
1421 | {
|
---|
1422 | display_log(dp, USER_ERROR,
|
---|
1423 | "%.*s: option=arg cannot be negated", i, arg);
|
---|
1424 | abort(); /* NOT REACHED */
|
---|
1425 | }
|
---|
1426 | }
|
---|
1427 | }
|
---|
1428 |
|
---|
1429 | else /* multiple options separated by ',' characters */
|
---|
1430 | {
|
---|
1431 | /* --no-option negates list values from the default, which should
|
---|
1432 | * therefore be 'all'. Notice that if the option list is empty in
|
---|
1433 | * this case nothing will be removed and therefore --no-option= is
|
---|
1434 | * the same as --option.
|
---|
1435 | */
|
---|
1436 | if (negate)
|
---|
1437 | val = options[j].values[0].value;
|
---|
1438 |
|
---|
1439 | else
|
---|
1440 | val = 0;
|
---|
1441 |
|
---|
1442 | while (*list != 0) /* allows option= which sets 0 */
|
---|
1443 | {
|
---|
1444 | /* A value is terminated by the end of the list or a ','
|
---|
1445 | * character.
|
---|
1446 | */
|
---|
1447 | int v, iv;
|
---|
1448 |
|
---|
1449 | iv = 0; /* an index into 'list' */
|
---|
1450 | while (list[++iv] != 0 && list[iv] != ',') {}
|
---|
1451 |
|
---|
1452 | v = find_val(dp, j, list, iv);
|
---|
1453 |
|
---|
1454 | if (negate)
|
---|
1455 | val &= ~v;
|
---|
1456 |
|
---|
1457 | else
|
---|
1458 | val |= v;
|
---|
1459 |
|
---|
1460 | list += iv;
|
---|
1461 | if (*list != 0)
|
---|
1462 | ++list; /* skip the ',' */
|
---|
1463 | }
|
---|
1464 | }
|
---|
1465 | }
|
---|
1466 |
|
---|
1467 | /* 'val' is the new value, store it for use later and debugging: */
|
---|
1468 | dp->value[j] = val;
|
---|
1469 |
|
---|
1470 | if (options[j].opt < LEVEL_MASK)
|
---|
1471 | {
|
---|
1472 | /* The handling for error levels is to set the level. */
|
---|
1473 | if (val) /* Set this level */
|
---|
1474 | dp->options = (dp->options & ~LEVEL_MASK) | options[j].opt;
|
---|
1475 |
|
---|
1476 | else
|
---|
1477 | display_log(dp, USER_ERROR,
|
---|
1478 | "%.*s: messages cannot be turned off individually; set a message level",
|
---|
1479 | i, arg);
|
---|
1480 | }
|
---|
1481 |
|
---|
1482 | else if (options[j].opt < OPTION)
|
---|
1483 | {
|
---|
1484 | if (val)
|
---|
1485 | dp->options |= options[j].opt;
|
---|
1486 |
|
---|
1487 | else
|
---|
1488 | dp->options &= ~options[j].opt;
|
---|
1489 | }
|
---|
1490 |
|
---|
1491 | return 1; /* this is an option */
|
---|
1492 | }
|
---|
1493 |
|
---|
1494 | else
|
---|
1495 | return 0; /* not an option */
|
---|
1496 | }
|
---|
1497 |
|
---|
1498 | #ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
1499 | static void
|
---|
1500 | set_timer(struct display *dp, struct timespec *timer)
|
---|
1501 | {
|
---|
1502 | /* Do the timing using clock_gettime and the per-process timer. */
|
---|
1503 | if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, timer))
|
---|
1504 | {
|
---|
1505 | display_log(dp, APP_ERROR,
|
---|
1506 | "CLOCK_PROCESS_CPUTIME_ID: %s: timing disabled\n", strerror(errno));
|
---|
1507 | dp->value[OPTIND(dp,time)] = 0; /* i.e. off */
|
---|
1508 | }
|
---|
1509 | }
|
---|
1510 |
|
---|
1511 | static void
|
---|
1512 | start_timer(struct display *dp, int what)
|
---|
1513 | {
|
---|
1514 | if ((dp->value[OPTIND(dp,time)] & what) != 0)
|
---|
1515 | set_timer(dp, what == PNGCP_TIME_READ ? &dp->read_time : &dp->write_time);
|
---|
1516 | }
|
---|
1517 |
|
---|
1518 | static void
|
---|
1519 | end_timer(struct display *dp, int what)
|
---|
1520 | {
|
---|
1521 | if ((dp->value[OPTIND(dp,time)] & what) != 0)
|
---|
1522 | {
|
---|
1523 | struct timespec t, tmp;
|
---|
1524 |
|
---|
1525 | set_timer(dp, &t);
|
---|
1526 |
|
---|
1527 | if (what == PNGCP_TIME_READ)
|
---|
1528 | tmp = dp->read_time;
|
---|
1529 |
|
---|
1530 | else
|
---|
1531 | tmp = dp->write_time;
|
---|
1532 |
|
---|
1533 | t.tv_sec -= tmp.tv_sec;
|
---|
1534 | t.tv_nsec -= tmp.tv_nsec;
|
---|
1535 |
|
---|
1536 | if (t.tv_nsec < 0)
|
---|
1537 | {
|
---|
1538 | --(t.tv_sec);
|
---|
1539 | t.tv_nsec += 1000000000L;
|
---|
1540 | }
|
---|
1541 |
|
---|
1542 | if (what == PNGCP_TIME_READ)
|
---|
1543 | dp->read_time = t, tmp = dp->read_time_total;
|
---|
1544 |
|
---|
1545 | else
|
---|
1546 | dp->write_time = t, tmp = dp->write_time_total;
|
---|
1547 |
|
---|
1548 | tmp.tv_sec += t.tv_sec;
|
---|
1549 | tmp.tv_nsec += t.tv_nsec;
|
---|
1550 |
|
---|
1551 | if (tmp.tv_nsec >= 1000000000L)
|
---|
1552 | {
|
---|
1553 | ++(tmp.tv_sec);
|
---|
1554 | tmp.tv_nsec -= 1000000000L;
|
---|
1555 | }
|
---|
1556 |
|
---|
1557 | if (what == PNGCP_TIME_READ)
|
---|
1558 | dp->read_time_total = tmp;
|
---|
1559 |
|
---|
1560 | else
|
---|
1561 | dp->write_time_total = tmp;
|
---|
1562 | }
|
---|
1563 | }
|
---|
1564 |
|
---|
1565 | static void
|
---|
1566 | print_time(const char *what, struct timespec t)
|
---|
1567 | {
|
---|
1568 | printf("%s %.2lu.%.9ld", what, (unsigned long)t.tv_sec, t.tv_nsec);
|
---|
1569 | }
|
---|
1570 | #else /* !PNGCP_TIMING */
|
---|
1571 | #define start_timer(dp, what) ((void)0)
|
---|
1572 | #define end_timer(dp, what) ((void)0)
|
---|
1573 | #endif /* !PNGCP_TIMING */
|
---|
1574 |
|
---|
1575 | /* The following is used in main to verify that the final argument is a
|
---|
1576 | * directory:
|
---|
1577 | */
|
---|
1578 | static int
|
---|
1579 | checkdir(const char *pathname)
|
---|
1580 | {
|
---|
1581 | struct stat buf;
|
---|
1582 | return stat(pathname, &buf) == 0 && S_ISDIR(buf.st_mode);
|
---|
1583 | }
|
---|
1584 |
|
---|
1585 | /* Work out whether a path is valid (if not a display_log occurs), a directory
|
---|
1586 | * (1 is returned) or a file *or* non-existent (0 is returned).
|
---|
1587 | *
|
---|
1588 | * Used for a write path.
|
---|
1589 | */
|
---|
1590 | static int
|
---|
1591 | isdir(struct display *dp, const char *pathname)
|
---|
1592 | {
|
---|
1593 | if (pathname == NULL)
|
---|
1594 | return 0; /* stdout */
|
---|
1595 |
|
---|
1596 | else if (pathname[0] == 0)
|
---|
1597 | return 1; /* empty string */
|
---|
1598 |
|
---|
1599 | else
|
---|
1600 | {
|
---|
1601 | struct stat buf;
|
---|
1602 | int ret = stat(pathname, &buf);
|
---|
1603 |
|
---|
1604 | if (ret == 0) /* the entry exists */
|
---|
1605 | {
|
---|
1606 | if (S_ISDIR(buf.st_mode))
|
---|
1607 | return 1;
|
---|
1608 |
|
---|
1609 | /* Else expect an object that exists and can be written: */
|
---|
1610 | if (access(pathname, W_OK) != 0)
|
---|
1611 | display_log(dp, USER_ERROR, "%s: cannot be written (%s)", pathname,
|
---|
1612 | strerror(errno));
|
---|
1613 |
|
---|
1614 | return 0; /* file (exists, can be written) */
|
---|
1615 | }
|
---|
1616 |
|
---|
1617 | else /* an error */
|
---|
1618 | {
|
---|
1619 | /* Non-existence is fine, other errors are not: */
|
---|
1620 | if (errno != ENOENT)
|
---|
1621 | display_log(dp, USER_ERROR, "%s: invalid output name (%s)",
|
---|
1622 | pathname, strerror(errno));
|
---|
1623 |
|
---|
1624 | return 0; /* file (does not exist) */
|
---|
1625 | }
|
---|
1626 | }
|
---|
1627 | }
|
---|
1628 |
|
---|
1629 | static void
|
---|
1630 | makename(struct display *dp, const char *dir, const char *infile)
|
---|
1631 | {
|
---|
1632 | /* Make a name for an output file (and check it). */
|
---|
1633 | dp->namebuf[0] = 0;
|
---|
1634 |
|
---|
1635 | if (dir == NULL || infile == NULL)
|
---|
1636 | display_log(dp, INTERNAL_ERROR, "NULL name to makename");
|
---|
1637 |
|
---|
1638 | else
|
---|
1639 | {
|
---|
1640 | size_t dsize = strlen(dir);
|
---|
1641 |
|
---|
1642 | if (dsize <= (sizeof dp->namebuf)-2) /* Allow for name + '/' + '\0' */
|
---|
1643 | {
|
---|
1644 | size_t isize = strlen(infile);
|
---|
1645 | size_t istart = isize-1;
|
---|
1646 |
|
---|
1647 | /* This should fail before here: */
|
---|
1648 | if (infile[istart] == '/')
|
---|
1649 | display_log(dp, INTERNAL_ERROR, "infile with trailing /");
|
---|
1650 |
|
---|
1651 | memcpy(dp->namebuf, dir, dsize);
|
---|
1652 | if (dsize > 0 && dp->namebuf[dsize-1] != '/')
|
---|
1653 | dp->namebuf[dsize++] = '/';
|
---|
1654 |
|
---|
1655 | /* Find the rightmost non-/ character: */
|
---|
1656 | while (istart > 0 && infile[istart-1] != '/')
|
---|
1657 | --istart;
|
---|
1658 |
|
---|
1659 | isize -= istart;
|
---|
1660 | infile += istart;
|
---|
1661 |
|
---|
1662 | if (dsize+isize < (sizeof dp->namebuf)) /* dsize + infile + '\0' */
|
---|
1663 | {
|
---|
1664 | memcpy(dp->namebuf+dsize, infile, isize+1);
|
---|
1665 |
|
---|
1666 | if (isdir(dp, dp->namebuf))
|
---|
1667 | display_log(dp, USER_ERROR, "%s: output file is a directory",
|
---|
1668 | dp->namebuf);
|
---|
1669 | }
|
---|
1670 |
|
---|
1671 | else
|
---|
1672 | {
|
---|
1673 | dp->namebuf[dsize] = 0; /* allowed for: -2 at start */
|
---|
1674 | display_log(dp, USER_ERROR, "%s%s: output file name too long",
|
---|
1675 | dp->namebuf, infile);
|
---|
1676 | }
|
---|
1677 | }
|
---|
1678 |
|
---|
1679 | else
|
---|
1680 | display_log(dp, USER_ERROR, "%s: output directory name too long", dir);
|
---|
1681 | }
|
---|
1682 | }
|
---|
1683 |
|
---|
1684 | /* error handler callbacks for libpng */
|
---|
1685 | static void PNGCBAPI
|
---|
1686 | display_warning(png_structp pp, png_const_charp warning)
|
---|
1687 | {
|
---|
1688 | struct display *dp = get_dp(pp);
|
---|
1689 |
|
---|
1690 | /* This is used to prevent repeated warnings while searching */
|
---|
1691 | if (!dp->no_warnings)
|
---|
1692 | display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
|
---|
1693 | }
|
---|
1694 |
|
---|
1695 | static void PNGCBAPI
|
---|
1696 | display_error(png_structp pp, png_const_charp error)
|
---|
1697 | {
|
---|
1698 | struct display *dp = get_dp(pp);
|
---|
1699 |
|
---|
1700 | display_log(dp, LIBPNG_ERROR, "%s", error);
|
---|
1701 | }
|
---|
1702 |
|
---|
1703 | static void
|
---|
1704 | display_start_read(struct display *dp, const char *filename)
|
---|
1705 | {
|
---|
1706 | if (filename != NULL)
|
---|
1707 | {
|
---|
1708 | dp->filename = filename;
|
---|
1709 | dp->fp = fopen(filename, "rb");
|
---|
1710 | }
|
---|
1711 |
|
---|
1712 | else
|
---|
1713 | {
|
---|
1714 | dp->filename = "<stdin>";
|
---|
1715 | dp->fp = stdin;
|
---|
1716 | }
|
---|
1717 |
|
---|
1718 | dp->w = dp->h = 0U;
|
---|
1719 | dp->bpp = 0U;
|
---|
1720 | dp->size = 0U;
|
---|
1721 | dp->read_size = 0U;
|
---|
1722 |
|
---|
1723 | if (dp->fp == NULL)
|
---|
1724 | display_log(dp, USER_ERROR, "file open failed (%s)", strerror(errno));
|
---|
1725 | }
|
---|
1726 |
|
---|
1727 | static void PNGCBAPI
|
---|
1728 | read_function(png_structp pp, png_bytep data, size_t size)
|
---|
1729 | {
|
---|
1730 | struct display *dp = get_dp(pp);
|
---|
1731 |
|
---|
1732 | if (size == 0U || fread(data, size, 1U, dp->fp) == 1U)
|
---|
1733 | dp->read_size += size;
|
---|
1734 |
|
---|
1735 | else
|
---|
1736 | {
|
---|
1737 | if (feof(dp->fp))
|
---|
1738 | display_log(dp, LIBPNG_ERROR, "PNG file truncated");
|
---|
1739 | else
|
---|
1740 | display_log(dp, LIBPNG_ERROR, "PNG file read failed (%s)",
|
---|
1741 | strerror(errno));
|
---|
1742 | }
|
---|
1743 | }
|
---|
1744 |
|
---|
1745 | static void
|
---|
1746 | read_png(struct display *dp, const char *filename)
|
---|
1747 | {
|
---|
1748 | display_clean_read(dp); /* safety */
|
---|
1749 | display_start_read(dp, filename);
|
---|
1750 |
|
---|
1751 | dp->read_pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp,
|
---|
1752 | display_error, display_warning);
|
---|
1753 | if (dp->read_pp == NULL)
|
---|
1754 | display_log(dp, LIBPNG_ERROR, "failed to create read struct");
|
---|
1755 |
|
---|
1756 | # ifdef PNG_BENIGN_ERRORS_SUPPORTED
|
---|
1757 | png_set_benign_errors(dp->read_pp, 1/*allowed*/);
|
---|
1758 | # endif /* BENIGN_ERRORS */
|
---|
1759 |
|
---|
1760 | # ifdef FIX_INDEX
|
---|
1761 | if ((dp->options & FIX_INDEX) != 0)
|
---|
1762 | png_set_check_for_invalid_index(dp->read_pp, 1/*on, no warning*/);
|
---|
1763 | # ifdef IGNORE_INDEX
|
---|
1764 | else
|
---|
1765 | # endif /* IGNORE_INDEX */
|
---|
1766 | # endif /* FIX_INDEX */
|
---|
1767 | # ifdef IGNORE_INDEX
|
---|
1768 | if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
|
---|
1769 | png_set_check_for_invalid_index(dp->read_pp, -1/*off completely*/);
|
---|
1770 | # endif /* IGNORE_INDEX */
|
---|
1771 |
|
---|
1772 | /* The png_read_png API requires us to make the info struct, but it does the
|
---|
1773 | * call to png_read_info.
|
---|
1774 | */
|
---|
1775 | dp->ip = png_create_info_struct(dp->read_pp);
|
---|
1776 | if (dp->ip == NULL)
|
---|
1777 | png_error(dp->read_pp, "failed to create info struct");
|
---|
1778 |
|
---|
1779 | /* Set the IO handling */
|
---|
1780 | png_set_read_fn(dp->read_pp, dp, read_function);
|
---|
1781 |
|
---|
1782 | # ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
---|
1783 | png_set_keep_unknown_chunks(dp->read_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
|
---|
1784 | 0);
|
---|
1785 | # endif /* HANDLE_AS_UNKNOWN */
|
---|
1786 |
|
---|
1787 | # ifdef PNG_SET_USER_LIMITS_SUPPORTED
|
---|
1788 | /* Remove the user limits, if any */
|
---|
1789 | png_set_user_limits(dp->read_pp, 0x7fffffff, 0x7fffffff);
|
---|
1790 | # endif /* SET_USER_LIMITS */
|
---|
1791 |
|
---|
1792 | /* Now read the PNG. */
|
---|
1793 | start_timer(dp, PNGCP_TIME_READ);
|
---|
1794 | png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
|
---|
1795 | end_timer(dp, PNGCP_TIME_READ);
|
---|
1796 | dp->w = png_get_image_width(dp->read_pp, dp->ip);
|
---|
1797 | dp->h = png_get_image_height(dp->read_pp, dp->ip);
|
---|
1798 | dp->ct = png_get_color_type(dp->read_pp, dp->ip);
|
---|
1799 | dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) *
|
---|
1800 | png_get_channels(dp->read_pp, dp->ip);
|
---|
1801 | {
|
---|
1802 | /* png_get_rowbytes should never return 0 because the value is set by the
|
---|
1803 | * first call to png_set_IHDR, which should have happened by now, but just
|
---|
1804 | * in case:
|
---|
1805 | */
|
---|
1806 | png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip);
|
---|
1807 |
|
---|
1808 | if (rb == 0)
|
---|
1809 | png_error(dp->read_pp, "invalid row byte count from libpng");
|
---|
1810 |
|
---|
1811 | /* The size calc can overflow. */
|
---|
1812 | if ((MAX_SIZE-dp->h)/rb < dp->h)
|
---|
1813 | png_error(dp->read_pp, "image too large");
|
---|
1814 |
|
---|
1815 | dp->size = rb * dp->h + dp->h/*filter byte*/;
|
---|
1816 | }
|
---|
1817 |
|
---|
1818 | #ifdef FIX_INDEX
|
---|
1819 | if (dp->ct == PNG_COLOR_TYPE_PALETTE && (dp->options & FIX_INDEX) != 0)
|
---|
1820 | {
|
---|
1821 | int max = png_get_palette_max(dp->read_pp, dp->ip);
|
---|
1822 | png_colorp palette = NULL;
|
---|
1823 | int num = -1;
|
---|
1824 |
|
---|
1825 | if (png_get_PLTE(dp->read_pp, dp->ip, &palette, &num) != PNG_INFO_PLTE
|
---|
1826 | || max < 0 || num <= 0 || palette == NULL)
|
---|
1827 | display_log(dp, LIBPNG_ERROR, "invalid png_get_PLTE result");
|
---|
1828 |
|
---|
1829 | if (max >= num)
|
---|
1830 | {
|
---|
1831 | /* 'Fix' the palette. */
|
---|
1832 | int i;
|
---|
1833 | png_color newpal[256];
|
---|
1834 |
|
---|
1835 | for (i=0; i<num; ++i)
|
---|
1836 | newpal[i] = palette[i];
|
---|
1837 |
|
---|
1838 | /* Fill in any remainder with a warning color: */
|
---|
1839 | for (; i<=max; ++i)
|
---|
1840 | {
|
---|
1841 | newpal[i].red = 0xbe;
|
---|
1842 | newpal[i].green = 0xad;
|
---|
1843 | newpal[i].blue = 0xed;
|
---|
1844 | }
|
---|
1845 |
|
---|
1846 | png_set_PLTE(dp->read_pp, dp->ip, newpal, i);
|
---|
1847 | }
|
---|
1848 | }
|
---|
1849 | #endif /* FIX_INDEX */
|
---|
1850 |
|
---|
1851 | display_clean_read(dp);
|
---|
1852 | dp->operation = "none";
|
---|
1853 | }
|
---|
1854 |
|
---|
1855 | static void
|
---|
1856 | display_start_write(struct display *dp, const char *filename)
|
---|
1857 | {
|
---|
1858 | assert(dp->fp == NULL);
|
---|
1859 |
|
---|
1860 | if ((dp->options & NOWRITE) != 0)
|
---|
1861 | dp->output_file = "<no write>";
|
---|
1862 |
|
---|
1863 | else
|
---|
1864 | {
|
---|
1865 | if (filename != NULL)
|
---|
1866 | {
|
---|
1867 | dp->output_file = filename;
|
---|
1868 | dp->fp = fopen(filename, "wb");
|
---|
1869 | }
|
---|
1870 |
|
---|
1871 | else
|
---|
1872 | {
|
---|
1873 | dp->output_file = "<stdout>";
|
---|
1874 | dp->fp = stdout;
|
---|
1875 | }
|
---|
1876 |
|
---|
1877 | if (dp->fp == NULL)
|
---|
1878 | display_log(dp, USER_ERROR, "%s: file open failed (%s)",
|
---|
1879 | dp->output_file, strerror(errno));
|
---|
1880 | }
|
---|
1881 | }
|
---|
1882 |
|
---|
1883 | static void PNGCBAPI
|
---|
1884 | write_function(png_structp pp, png_bytep data, size_t size)
|
---|
1885 | {
|
---|
1886 | struct display *dp = get_dp(pp);
|
---|
1887 |
|
---|
1888 | /* The write fail is classed as a USER_ERROR, so --quiet does not turn it
|
---|
1889 | * off, this seems more likely to be correct.
|
---|
1890 | */
|
---|
1891 | if (dp->fp == NULL || fwrite(data, size, 1U, dp->fp) == 1U)
|
---|
1892 | {
|
---|
1893 | dp->write_size += size;
|
---|
1894 | if (dp->write_size < size || dp->write_size == MAX_SIZE)
|
---|
1895 | png_error(pp, "IDAT size overflow");
|
---|
1896 | }
|
---|
1897 |
|
---|
1898 | else
|
---|
1899 | display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)",
|
---|
1900 | dp->output_file, strerror(errno));
|
---|
1901 | }
|
---|
1902 |
|
---|
1903 | /* Compression option, 'method' is never set: there is no choice.
|
---|
1904 | *
|
---|
1905 | * IMPORTANT: the order of the entries in this macro determines the preference
|
---|
1906 | * order when two different combos of two of these options produce an IDAT of
|
---|
1907 | * the same size. The logic here is to put the things that affect the decoding
|
---|
1908 | * of the PNG image ahead of those that are relevant only to the encoding.
|
---|
1909 | */
|
---|
1910 | #define SET_COMPRESSION\
|
---|
1911 | SET(strategy, strategy);\
|
---|
1912 | SET(windowBits, window_bits);\
|
---|
1913 | SET(level, level);\
|
---|
1914 | SET(memLevel, mem_level);
|
---|
1915 |
|
---|
1916 | #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
|
---|
1917 | static void
|
---|
1918 | search_compression(struct display *dp)
|
---|
1919 | {
|
---|
1920 | /* Like set_compression below but use a more restricted search than 'all' */
|
---|
1921 | int val;
|
---|
1922 |
|
---|
1923 | # define SET(name, func) if (getsearchopts(dp, #name, &val))\
|
---|
1924 | png_set_compression_ ## func(dp->write_pp, val);
|
---|
1925 | SET_COMPRESSION
|
---|
1926 | # undef SET
|
---|
1927 | }
|
---|
1928 |
|
---|
1929 | static void
|
---|
1930 | set_compression(struct display *dp)
|
---|
1931 | {
|
---|
1932 | int val;
|
---|
1933 |
|
---|
1934 | # define SET(name, func) if (getallopts(dp, #name, &val))\
|
---|
1935 | png_set_compression_ ## func(dp->write_pp, val);
|
---|
1936 | SET_COMPRESSION
|
---|
1937 | # undef SET
|
---|
1938 | }
|
---|
1939 |
|
---|
1940 | #ifdef PNG_SW_COMPRESS_level /* 1.7.0+ */
|
---|
1941 | static void
|
---|
1942 | set_ICC_profile_compression(struct display *dp)
|
---|
1943 | {
|
---|
1944 | int val;
|
---|
1945 |
|
---|
1946 | # define SET(name, func) if (getallopts(dp, "ICC-profile-" #name, &val))\
|
---|
1947 | png_set_ICC_profile_compression_ ## func(dp->write_pp, val);
|
---|
1948 | SET_COMPRESSION
|
---|
1949 | # undef SET
|
---|
1950 | }
|
---|
1951 | #else
|
---|
1952 | # define set_ICC_profile_compression(dp) ((void)0)
|
---|
1953 | #endif
|
---|
1954 | #else
|
---|
1955 | # define search_compression(dp) ((void)0)
|
---|
1956 | # define set_compression(dp) ((void)0)
|
---|
1957 | # define set_ICC_profile_compression(dp) ((void)0)
|
---|
1958 | #endif /* WRITE_CUSTOMIZE_COMPRESSION */
|
---|
1959 |
|
---|
1960 | #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
|
---|
1961 | static void
|
---|
1962 | set_text_compression(struct display *dp)
|
---|
1963 | {
|
---|
1964 | int val;
|
---|
1965 |
|
---|
1966 | # define SET(name, func) if (getallopts(dp, "text-" #name, &val))\
|
---|
1967 | png_set_text_compression_ ## func(dp->write_pp, val);
|
---|
1968 | SET_COMPRESSION
|
---|
1969 | # undef SET
|
---|
1970 | }
|
---|
1971 | #else
|
---|
1972 | # define set_text_compression(dp) ((void)0)
|
---|
1973 | #endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
|
---|
1974 |
|
---|
1975 | static void
|
---|
1976 | write_png(struct display *dp, const char *destname)
|
---|
1977 | {
|
---|
1978 | display_clean_write(dp); /* safety */
|
---|
1979 | display_start_write(dp, destname);
|
---|
1980 |
|
---|
1981 | dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp,
|
---|
1982 | display_error, display_warning);
|
---|
1983 |
|
---|
1984 | if (dp->write_pp == NULL)
|
---|
1985 | display_log(dp, LIBPNG_ERROR, "failed to create write png_struct");
|
---|
1986 |
|
---|
1987 | # ifdef PNG_BENIGN_ERRORS_SUPPORTED
|
---|
1988 | png_set_benign_errors(dp->write_pp, 1/*allowed*/);
|
---|
1989 | # endif /* BENIGN_ERRORS */
|
---|
1990 |
|
---|
1991 | png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/);
|
---|
1992 |
|
---|
1993 | #ifdef IGNORE_INDEX
|
---|
1994 | if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
|
---|
1995 | png_set_check_for_invalid_index(dp->write_pp, -1/*off completely*/);
|
---|
1996 | #endif /* IGNORE_INDEX */
|
---|
1997 |
|
---|
1998 | /* Restore the text chunks when using libpng 1.6 or less; this is a macro
|
---|
1999 | * which expands to nothing in 1.7+ In earlier versions it tests
|
---|
2000 | * dp->text_stashed, which is only set (below) *after* the first write.
|
---|
2001 | */
|
---|
2002 | text_restore(dp);
|
---|
2003 |
|
---|
2004 | # ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
---|
2005 | png_set_keep_unknown_chunks(dp->write_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
|
---|
2006 | 0);
|
---|
2007 | # endif /* HANDLE_AS_UNKNOWN */
|
---|
2008 |
|
---|
2009 | # ifdef PNG_SET_USER_LIMITS_SUPPORTED
|
---|
2010 | /* Remove the user limits, if any */
|
---|
2011 | png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff);
|
---|
2012 | # endif
|
---|
2013 |
|
---|
2014 | /* OPTION HANDLING */
|
---|
2015 | /* compression outputs, IDAT and zTXt/iTXt: */
|
---|
2016 | dp->tsp = dp->nsp;
|
---|
2017 | dp->nsp = dp->csp = 0;
|
---|
2018 | # ifdef PNG_SW_COMPRESS_png_level
|
---|
2019 | {
|
---|
2020 | int val;
|
---|
2021 |
|
---|
2022 | /* This sets everything, but then the following options just override
|
---|
2023 | * the specific settings for ICC profiles and text.
|
---|
2024 | */
|
---|
2025 | if (getallopts(dp, "compression", &val))
|
---|
2026 | png_set_compression(dp->write_pp, val);
|
---|
2027 |
|
---|
2028 | if (getallopts(dp, "ICC-profile-compression", &val))
|
---|
2029 | png_set_ICC_profile_compression(dp->write_pp, val);
|
---|
2030 |
|
---|
2031 | if (getallopts(dp, "text-compression", &val))
|
---|
2032 | png_set_text_compression(dp->write_pp, val);
|
---|
2033 | }
|
---|
2034 | # endif /* png_level support */
|
---|
2035 | if (dp->options & SEARCH)
|
---|
2036 | search_compression(dp);
|
---|
2037 | else
|
---|
2038 | set_compression(dp);
|
---|
2039 | set_ICC_profile_compression(dp);
|
---|
2040 | set_text_compression(dp);
|
---|
2041 |
|
---|
2042 | {
|
---|
2043 | int val;
|
---|
2044 |
|
---|
2045 | /* The permitted range is 1..0x7FFFFFFF, so the cast is safe */
|
---|
2046 | if (get_option(dp, "IDAT-size", &val))
|
---|
2047 | png_set_IDAT_size(dp->write_pp, val);
|
---|
2048 | }
|
---|
2049 |
|
---|
2050 | /* filter handling */
|
---|
2051 | # ifdef PNG_WRITE_FILTER_SUPPORTED
|
---|
2052 | {
|
---|
2053 | int val;
|
---|
2054 |
|
---|
2055 | if (get_option(dp, "filter", &val))
|
---|
2056 | png_set_filter(dp->write_pp, PNG_FILTER_TYPE_BASE, val);
|
---|
2057 | }
|
---|
2058 | # endif /* WRITE_FILTER */
|
---|
2059 |
|
---|
2060 | /* This just uses the 'read' info_struct directly, it contains the image. */
|
---|
2061 | dp->write_size = 0U;
|
---|
2062 | start_timer(dp, PNGCP_TIME_WRITE);
|
---|
2063 | png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
|
---|
2064 | end_timer(dp, PNGCP_TIME_WRITE);
|
---|
2065 |
|
---|
2066 | /* Make sure the file was written ok: */
|
---|
2067 | if (dp->fp != NULL)
|
---|
2068 | {
|
---|
2069 | FILE *fp = dp->fp;
|
---|
2070 | dp->fp = NULL;
|
---|
2071 | if (fclose(fp))
|
---|
2072 | display_log(dp, APP_ERROR, "%s: write failed (%s)",
|
---|
2073 | destname == NULL ? "stdout" : destname, strerror(errno));
|
---|
2074 | }
|
---|
2075 |
|
---|
2076 | /* Clean it on the way out - if control returns to the caller then the
|
---|
2077 | * written_file contains the required data.
|
---|
2078 | */
|
---|
2079 | display_clean_write(dp);
|
---|
2080 | dp->operation = "none";
|
---|
2081 | }
|
---|
2082 |
|
---|
2083 | static void
|
---|
2084 | set_windowBits_hi(struct display *dp)
|
---|
2085 | {
|
---|
2086 | /* windowBits is in the range 8..15 but zlib maps '8' to '9' so it is only
|
---|
2087 | * worth using if the data size is 256 byte or less.
|
---|
2088 | */
|
---|
2089 | int wb = MAX_WBITS; /* for large images */
|
---|
2090 | int i = VLSIZE(windowBits_IDAT);
|
---|
2091 |
|
---|
2092 | while (wb > 8 && dp->size <= 1U<<(wb-1)) --wb;
|
---|
2093 |
|
---|
2094 | while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break;
|
---|
2095 |
|
---|
2096 | assert(i > 1); /* vl_windowBits_IDAT always has a RANGE() */
|
---|
2097 | VLNAME(windowBits_IDAT)[i].value = wb;
|
---|
2098 |
|
---|
2099 | assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
|
---|
2100 | VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8;
|
---|
2101 |
|
---|
2102 | /* If wb == 8 then any search has been restricted to just one windowBits
|
---|
2103 | * entry. Record that here to avoid producing a spurious app-level warning
|
---|
2104 | * above.
|
---|
2105 | */
|
---|
2106 | if (wb == 8)
|
---|
2107 | dp->min_windowBits = OPTIND(dp, windowBits);
|
---|
2108 | }
|
---|
2109 |
|
---|
2110 | static int
|
---|
2111 | better_options(const struct display *dp)
|
---|
2112 | {
|
---|
2113 | /* Are these options better than the best found so far? Normally the
|
---|
2114 | * options are tested in preference order, best first, however when doing a
|
---|
2115 | * search operation on a range the range values are tested out of order. In
|
---|
2116 | * that case preferable options will get tested later.
|
---|
2117 | *
|
---|
2118 | * This function looks through the stack from the bottom up looking for an
|
---|
2119 | * option that does not match the current best value. When it finds one it
|
---|
2120 | * checks to see if it is more or less desirable and returns true or false
|
---|
2121 | * as appropriate.
|
---|
2122 | *
|
---|
2123 | * Notice that this means that the order options are pushed onto the stack
|
---|
2124 | * conveys a priority; lower/earlier options are more important than later
|
---|
2125 | * ones.
|
---|
2126 | */
|
---|
2127 | unsigned int sp;
|
---|
2128 |
|
---|
2129 | for (sp=0; sp<dp->csp; ++sp)
|
---|
2130 | {
|
---|
2131 | int c = compare_option(dp, sp);
|
---|
2132 |
|
---|
2133 | if (c < 0)
|
---|
2134 | return 0; /* worse */
|
---|
2135 |
|
---|
2136 | else if (c > 0)
|
---|
2137 | return 1; /* better */
|
---|
2138 | }
|
---|
2139 |
|
---|
2140 | assert(0 && "unreached");
|
---|
2141 | }
|
---|
2142 |
|
---|
2143 | static void
|
---|
2144 | print_search_results(struct display *dp)
|
---|
2145 | {
|
---|
2146 | assert(dp->filename != NULL);
|
---|
2147 | printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu with '%s'\n",
|
---|
2148 | dp->filename, (unsigned long)dp->w, (unsigned long)dp->h, dp->bpp,
|
---|
2149 | cts(dp->ct), (unsigned long)dp->size, (unsigned long)dp->read_size,
|
---|
2150 | (unsigned long)dp->best_size, dp->best);
|
---|
2151 | fflush(stdout);
|
---|
2152 | }
|
---|
2153 |
|
---|
2154 | static void
|
---|
2155 | log_search(struct display *dp, unsigned int log_depth)
|
---|
2156 | {
|
---|
2157 | /* Log, and reset, the search so far: */
|
---|
2158 | if (dp->nsp/*next entry to change*/ <= log_depth)
|
---|
2159 | {
|
---|
2160 | print_search_results(dp);
|
---|
2161 | /* Start again with this entry: */
|
---|
2162 | dp->best_size = MAX_SIZE;
|
---|
2163 | }
|
---|
2164 | }
|
---|
2165 |
|
---|
2166 | static void
|
---|
2167 | cp_one_file(struct display *dp, const char *filename, const char *destname)
|
---|
2168 | {
|
---|
2169 | unsigned int log_depth;
|
---|
2170 |
|
---|
2171 | dp->filename = filename;
|
---|
2172 | dp->operation = "read";
|
---|
2173 | dp->no_warnings = 0;
|
---|
2174 |
|
---|
2175 | /* Read it then write it: */
|
---|
2176 | if (filename != NULL && access(filename, R_OK) != 0)
|
---|
2177 | display_log(dp, USER_ERROR, "%s: invalid file name (%s)",
|
---|
2178 | filename, strerror(errno));
|
---|
2179 |
|
---|
2180 | read_png(dp, filename);
|
---|
2181 |
|
---|
2182 | /* But 'destname' may be a directory. */
|
---|
2183 | dp->operation = "write";
|
---|
2184 |
|
---|
2185 | /* Limit the upper end of the windowBits range for this file */
|
---|
2186 | set_windowBits_hi(dp);
|
---|
2187 |
|
---|
2188 | /* For logging, depth to log: */
|
---|
2189 | {
|
---|
2190 | int val;
|
---|
2191 |
|
---|
2192 | if (get_option(dp, "log-depth", &val) && val >= 0)
|
---|
2193 | log_depth = (unsigned int)/*SAFE*/val;
|
---|
2194 |
|
---|
2195 | else
|
---|
2196 | log_depth = 0U;
|
---|
2197 | }
|
---|
2198 |
|
---|
2199 | if (destname != NULL) /* else stdout */
|
---|
2200 | {
|
---|
2201 | if (isdir(dp, destname))
|
---|
2202 | {
|
---|
2203 | makename(dp, destname, filename);
|
---|
2204 | destname = dp->namebuf;
|
---|
2205 | }
|
---|
2206 |
|
---|
2207 | else if (access(destname, W_OK) != 0 && errno != ENOENT)
|
---|
2208 | display_log(dp, USER_ERROR, "%s: invalid output name (%s)", destname,
|
---|
2209 | strerror(errno));
|
---|
2210 | }
|
---|
2211 |
|
---|
2212 | dp->nsp = 0;
|
---|
2213 | dp->curr[0] = 0; /* acts as a flag for the caller */
|
---|
2214 | dp->opt_string_start = 0;
|
---|
2215 | dp->best[0] = 0; /* safety */
|
---|
2216 | dp->best_size = MAX_SIZE;
|
---|
2217 | write_png(dp, destname);
|
---|
2218 |
|
---|
2219 | /* Initialize the 'best' fields: */
|
---|
2220 | strcpy(dp->best, dp->curr);
|
---|
2221 | dp->best_size = dp->write_size;
|
---|
2222 |
|
---|
2223 | if (dp->nsp > 0) /* iterating over lists */
|
---|
2224 | {
|
---|
2225 | char *tmpname, tmpbuf[(sizeof dp->namebuf) + 4];
|
---|
2226 | assert(dp->curr[0] == ' ' && dp->tsp > 0);
|
---|
2227 |
|
---|
2228 | /* Cancel warnings on subsequent writes */
|
---|
2229 | log_search(dp, log_depth);
|
---|
2230 | dp->no_warnings = 1;
|
---|
2231 |
|
---|
2232 | /* Make a temporary name for the subsequent tests: */
|
---|
2233 | if (destname != NULL)
|
---|
2234 | {
|
---|
2235 | strcpy(tmpbuf, destname);
|
---|
2236 | strcat(tmpbuf, ".tmp"); /* space for .tmp allocated above */
|
---|
2237 | tmpname = tmpbuf;
|
---|
2238 | }
|
---|
2239 |
|
---|
2240 | else
|
---|
2241 | tmpname = NULL; /* stdout */
|
---|
2242 |
|
---|
2243 | /* Loop to find the best option. */
|
---|
2244 | do
|
---|
2245 | {
|
---|
2246 | write_png(dp, tmpname);
|
---|
2247 |
|
---|
2248 | /* And compare the sizes (the write function makes sure write_size
|
---|
2249 | * doesn't overflow.)
|
---|
2250 | */
|
---|
2251 | assert(dp->csp > 0);
|
---|
2252 |
|
---|
2253 | if (dp->write_size < dp->best_size ||
|
---|
2254 | (dp->write_size == dp->best_size && better_options(dp)))
|
---|
2255 | {
|
---|
2256 | if (destname != NULL && rename(tmpname, destname) != 0)
|
---|
2257 | display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname,
|
---|
2258 | destname, strerror(errno));
|
---|
2259 |
|
---|
2260 | strcpy(dp->best, dp->curr);
|
---|
2261 | dp->best_size = dp->write_size;
|
---|
2262 | }
|
---|
2263 |
|
---|
2264 | else if (tmpname != NULL && unlink(tmpname) != 0)
|
---|
2265 | display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname,
|
---|
2266 | strerror(errno));
|
---|
2267 |
|
---|
2268 | log_search(dp, log_depth);
|
---|
2269 | }
|
---|
2270 | while (dp->nsp > 0);
|
---|
2271 |
|
---|
2272 | /* Do this for the 'sizes' option so that it reports the correct size. */
|
---|
2273 | dp->write_size = dp->best_size;
|
---|
2274 | }
|
---|
2275 | }
|
---|
2276 |
|
---|
2277 | static int
|
---|
2278 | cppng(struct display *dp, const char *file, const char *gv dest)
|
---|
2279 | /* Exists solely to isolate the setjmp clobbers which some versions of GCC
|
---|
2280 | * erroneously generate.
|
---|
2281 | */
|
---|
2282 | {
|
---|
2283 | int ret = setjmp(dp->error_return);
|
---|
2284 |
|
---|
2285 | if (ret == 0)
|
---|
2286 | {
|
---|
2287 | dp->errset = 1;
|
---|
2288 | cp_one_file(dp, file, dest);
|
---|
2289 | dp->errset = 0;
|
---|
2290 | return 0;
|
---|
2291 | }
|
---|
2292 |
|
---|
2293 | else
|
---|
2294 | {
|
---|
2295 | dp->errset = 0;
|
---|
2296 |
|
---|
2297 | if (ret < ERRORS) /* shouldn't longjmp on warnings */
|
---|
2298 | display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret);
|
---|
2299 |
|
---|
2300 | return ret;
|
---|
2301 | }
|
---|
2302 | }
|
---|
2303 |
|
---|
2304 | int
|
---|
2305 | main(int argc, char **argv)
|
---|
2306 | {
|
---|
2307 | /* For each file on the command line test it with a range of transforms */
|
---|
2308 | int option_end;
|
---|
2309 | struct display d;
|
---|
2310 |
|
---|
2311 | display_init(&d);
|
---|
2312 |
|
---|
2313 | d.operation = "options";
|
---|
2314 | for (option_end = 1;
|
---|
2315 | option_end < argc && opt_check(&d, argv[option_end]);
|
---|
2316 | ++option_end)
|
---|
2317 | {
|
---|
2318 | }
|
---|
2319 |
|
---|
2320 | /* Do a quick check on the directory target case; when there are more than
|
---|
2321 | * two arguments the last one must be a directory.
|
---|
2322 | */
|
---|
2323 | if (!(d.options & NOWRITE) && option_end+2 < argc && !checkdir(argv[argc-1]))
|
---|
2324 | {
|
---|
2325 | fprintf(stderr,
|
---|
2326 | "pngcp: %s: directory required with more than two arguments\n",
|
---|
2327 | argv[argc-1]);
|
---|
2328 | return 99;
|
---|
2329 | }
|
---|
2330 |
|
---|
2331 | {
|
---|
2332 | int errors = 0;
|
---|
2333 | int i = option_end;
|
---|
2334 |
|
---|
2335 | /* Do this at least once; if there are no arguments stdin/stdout are used.
|
---|
2336 | */
|
---|
2337 | d.operation = "files";
|
---|
2338 | do
|
---|
2339 | {
|
---|
2340 | const char *infile = NULL;
|
---|
2341 | const char *outfile = NULL;
|
---|
2342 | int ret;
|
---|
2343 |
|
---|
2344 | if (i < argc)
|
---|
2345 | {
|
---|
2346 | infile = argv[i++];
|
---|
2347 | if (!(d.options & NOWRITE) && i < argc)
|
---|
2348 | outfile = argv[argc-1];
|
---|
2349 | }
|
---|
2350 |
|
---|
2351 | ret = cppng(&d, infile, outfile);
|
---|
2352 |
|
---|
2353 | if (ret)
|
---|
2354 | {
|
---|
2355 | if (ret > QUIET) /* abort on user or internal error */
|
---|
2356 | return 99;
|
---|
2357 |
|
---|
2358 | /* An error: the output is meaningless */
|
---|
2359 | }
|
---|
2360 |
|
---|
2361 | else if (d.best[0] != 0)
|
---|
2362 | {
|
---|
2363 | /* This result may already have been output, in which case best_size
|
---|
2364 | * has been reset.
|
---|
2365 | */
|
---|
2366 | if (d.best_size < MAX_SIZE)
|
---|
2367 | print_search_results(&d);
|
---|
2368 | }
|
---|
2369 |
|
---|
2370 | else if (d.options & SIZES)
|
---|
2371 | {
|
---|
2372 | printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu [0x%lx]\n",
|
---|
2373 | infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
|
---|
2374 | cts(d.ct), (unsigned long)d.size, (unsigned long)d.read_size,
|
---|
2375 | (unsigned long)d.write_size, (unsigned long)d.results);
|
---|
2376 | fflush(stdout);
|
---|
2377 | }
|
---|
2378 |
|
---|
2379 | /* Here on any return, including failures, except user/internal issues
|
---|
2380 | */
|
---|
2381 | {
|
---|
2382 | int pass = (d.options & STRICT) ?
|
---|
2383 | RESULT_STRICT(d.results) : RESULT_RELAXED(d.results);
|
---|
2384 |
|
---|
2385 | if (!pass)
|
---|
2386 | ++errors;
|
---|
2387 |
|
---|
2388 | if (d.options & LOG)
|
---|
2389 | {
|
---|
2390 | int j;
|
---|
2391 |
|
---|
2392 | printf("%s: pngcp", pass ? "PASS" : "FAIL");
|
---|
2393 |
|
---|
2394 | for (j=1; j<option_end; ++j)
|
---|
2395 | printf(" %s", argv[j]);
|
---|
2396 |
|
---|
2397 | if (infile != NULL)
|
---|
2398 | printf(" %s", infile);
|
---|
2399 |
|
---|
2400 | # ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
2401 | /* When logging output the files for each file, if enabled. */
|
---|
2402 | if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
|
---|
2403 | print_time(" read", d.read_time);
|
---|
2404 |
|
---|
2405 | if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
|
---|
2406 | print_time(" write", d.write_time);
|
---|
2407 | # endif /* PNGCP_TIMING */
|
---|
2408 |
|
---|
2409 | printf("\n");
|
---|
2410 | fflush(stdout);
|
---|
2411 | }
|
---|
2412 | }
|
---|
2413 |
|
---|
2414 | display_clean(&d);
|
---|
2415 | }
|
---|
2416 | while (i+!(d.options & NOWRITE) < argc);
|
---|
2417 | /* I.e. for write cases after the first time through the loop require
|
---|
2418 | * there to be at least two arguments left and for the last one to be a
|
---|
2419 | * directory (this was checked above).
|
---|
2420 | */
|
---|
2421 |
|
---|
2422 | /* Release allocated memory */
|
---|
2423 | display_destroy(&d);
|
---|
2424 |
|
---|
2425 | # ifdef PNG_PNGCP_TIMING_SUPPORTED
|
---|
2426 | {
|
---|
2427 | int output = 0;
|
---|
2428 |
|
---|
2429 | if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
|
---|
2430 | print_time("read", d.read_time_total), output = 1;
|
---|
2431 |
|
---|
2432 | if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
|
---|
2433 | {
|
---|
2434 | if (output) putchar(' ');
|
---|
2435 | print_time("write", d.write_time_total);
|
---|
2436 | output = 1;
|
---|
2437 | }
|
---|
2438 |
|
---|
2439 | if (output) putchar('\n');
|
---|
2440 | }
|
---|
2441 | # endif /* PNGCP_TIMING */
|
---|
2442 |
|
---|
2443 | return errors != 0;
|
---|
2444 | }
|
---|
2445 | }
|
---|
2446 | #else /* !READ_PNG || !WRITE_PNG */
|
---|
2447 | int
|
---|
2448 | main(void)
|
---|
2449 | {
|
---|
2450 | fprintf(stderr, "pngcp: no support for png_read/write_image\n");
|
---|
2451 | return 77;
|
---|
2452 | }
|
---|
2453 | #endif /* !READ_PNG || !WRITE_PNG */
|
---|