1
1
/* pngquant.c - quantize the colors in an alphamap down to a specified number
3
3
** Copyright (C) 1989, 1991 by Jef Poskanzer.
4
** Copyright (C) 1997, 2000, 2002 by Greg Roelofs; based on an idea by
6
** © 2009-2013 by Kornel Lesinski.
8
5
** Permission to use, copy, modify, and distribute this software and its
9
6
** documentation for any purpose and without fee is hereby granted, provided
11
8
** copyright notice and this permission notice appear in supporting
12
9
** documentation. This software is provided "as is" without express or
13
10
** implied warranty.
14
** © 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
15
** © 2009-2014 by Kornel Lesiński.
17
** All rights reserved.
19
** Redistribution and use in source and binary forms, with or without modification,
20
** are permitted provided that the following conditions are met:
22
** 1. Redistributions of source code must retain the above copyright notice,
23
** this list of conditions and the following disclaimer.
25
** 2. Redistributions in binary form must reproduce the above copyright notice,
26
** this list of conditions and the following disclaimer in the documentation
27
** and/or other materials provided with the distribution.
29
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
33
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
36
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
#define PNGQUANT_VERSION "2.1.0 (February 2014)"
42
#define PNGQUANT_VERSION "2.3.0 (July 2014)"
18
44
#define PNGQUANT_USAGE "\
19
45
usage: pngquant [options] [ncolors] [pngfile [pngfile ...]]\n\n\
21
47
--force overwrite existing output files (synonym: -f)\n\
22
48
--nofs disable Floyd-Steinberg dithering\n\
23
49
--ext new.png set custom suffix/extension for output filename\n\
50
--output file output path, only if one input file is specified (synonym: -o)\n\
24
51
--speed N speed/quality trade-off. 1=slow, 3=default, 11=fast & rough\n\
25
52
--quality min-max don't save below min, use less colors below max (0-100)\n\
26
53
--verbose print status messages (synonym: -v)\n\
66
93
void *log_callback_user_info;
68
95
bool using_stdin, force, fast_compression, ie_mode,
69
min_quality_limit, skip_if_larger;
96
min_quality_limit, skip_if_larger,
72
100
static pngquant_error prepare_output_image(liq_result *result, liq_image *input_image, png8_image *output_image);
73
101
static void set_palette(liq_result *result, png8_image *output_image);
74
static void pngquant_output_image_free(png8_image *output_image);
75
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels);
102
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels, bool verbose);
76
103
static pngquant_error write_image(png8_image *output_image, png24_image *output_image24, const char *outname, struct pngquant_options *options);
77
104
static char *add_filename_extension(const char *filename, const char *newext);
78
105
static bool file_exists(const char *outname);
134
162
fprintf(fd, "pngquant, %s, by Greg Roelofs, Kornel Lesinski.\n"
136
" DEBUG (slow) version.\n"
164
" DEBUG (slow) version.\n" /* NDEBUG disables assert() */
167
" Compiled with SSE instructions.\n"
139
170
" Compiled with OpenMP (multicore support).\n"
255
286
options.liq = liq_attr_create();
257
288
if (!options.liq) {
258
fputs("SSE2-capable CPU is required for this build.\n", stderr);
289
fputs("SSE-capable CPU is required for this build.\n", stderr);
259
290
return WRONG_ARCHITECTURE;
270
301
opt = getopt_long(argc, argv, "Vvqfhs:Q:o:", long_options, NULL);
273
liq_set_log_callback(options.liq, log_callback, NULL);
274
options.log_callback = log_callback;
304
options.verbose = true;
277
liq_set_log_callback(options.liq, NULL, NULL);
278
options.log_callback = NULL;
307
options.verbose = false;
282
311
options.floyd = optarg ? atof(optarg) : 1.0;
283
if (options.floyd < 0 || options.floyd > 1.0) {
312
if (options.floyd < 0 || options.floyd > 1.f) {
284
313
fputs("--floyd argument must be in 0..1 range\n", stderr);
285
314
return INVALID_ARGUMENT;
288
317
case arg_ordered: options.floyd = 0; break;
289
319
case 'f': options.force = true; break;
290
320
case arg_no_force: options.force = false; break;
347
377
png24_image tmp = {};
348
if (SUCCESS != read_image(options.liq, optarg, false, &tmp, &options.fixed_palette_image, false)) {
378
if (SUCCESS != read_image(options.liq, optarg, false, &tmp, &options.fixed_palette_image, false, false)) {
349
379
fprintf(stderr, " error: Unable to load %s", optarg);
350
380
return INVALID_ARGUMENT;
380
410
return MISSING_ARGUMENT;
413
if (options.verbose) {
414
liq_set_log_callback(options.liq, log_callback, NULL);
415
options.log_callback = log_callback;
383
418
char *colors_end;
384
419
unsigned long colors = strtoul(argv[argn], &colors_end, 10);
385
420
if (colors_end != argv[argn] && '\0' == colors_end[0]) {
398
433
// new filename extension depends on options used. Typically basename-fs8.png
399
434
if (newext == NULL) {
400
435
newext = options.floyd > 0 ? "-ie-fs8.png" : "-ie-or8.png";
401
if (!options.ie_mode) newext += 3; /* skip "-ie" */
436
if (!options.ie_mode) {
437
newext += 3; /* skip "-ie" */
404
441
if (argn == argc || (argn == argc-1 && 0==strcmp(argv[argn],"-"))) {
431
468
#pragma omp parallel for \
432
schedule(dynamic) reduction(+:skipped_count) reduction(+:error_count) reduction(+:file_count) shared(latest_error)
469
schedule(static, 1) reduction(+:skipped_count) reduction(+:error_count) reduction(+:file_count) shared(latest_error)
433
470
for(int i=0; i < num_files; i++) {
434
471
struct pngquant_options opts = options;
435
472
opts.liq = liq_attr_copy(options.liq);
502
539
return latest_error;
506
static void pngquant_output_image_free(png8_image *output_image)
508
free(output_image->indexed_data);
509
output_image->indexed_data = NULL;
511
free(output_image->row_pointers);
512
output_image->row_pointers = NULL;
515
542
pngquant_error pngquant_file(const char *filename, const char *outname, struct pngquant_options *options)
517
544
pngquant_error retval = SUCCESS;
522
549
png24_image input_image_rwpng = {};
523
550
bool keep_input_pixels = options->skip_if_larger || (options->using_stdin && options->min_quality_limit); // original may need to be output to stdout
525
retval = read_image(options->liq, filename, options->using_stdin, &input_image_rwpng, &input_image, keep_input_pixels);
552
retval = read_image(options->liq, filename, options->using_stdin, &input_image_rwpng, &input_image, keep_input_pixels, options->verbose);
528
555
int quality_percent = 90; // quality on 0-100 scale, updated upon successful remap
529
556
png8_image output_image = {};
531
verbose_printf(options, " read %luKB file corrected for gamma %2.1f",
532
(input_image_rwpng.file_size+1023UL)/1024UL, 1.0/input_image_rwpng.gamma);
558
verbose_printf(options, " read %luKB file", (input_image_rwpng.file_size+1023UL)/1024UL);
561
if (input_image_rwpng.lcms_status == ICCP) {
562
verbose_printf(options, " used embedded ICC profile to transform image to sRGB colorspace");
563
} else if (input_image_rwpng.lcms_status == GAMA_CHRM) {
564
verbose_printf(options, " used gAMA and cHRM chunks to transform image to sRGB colorspace");
565
} else if (input_image_rwpng.lcms_status == ICCP_WARN_GRAY) {
566
verbose_printf(options, " warning: ignored ICC profile in GRAY colorspace");
570
if (input_image_rwpng.gamma != 0.45455) {
571
verbose_printf(options, " corrected image from gamma %2.1f to sRGB gamma",
572
1.0/input_image_rwpng.gamma);
534
575
// when using image as source of a fixed palette the palette is extracted using regular quantization
535
576
liq_result *remap = liq_quantize_image(options->liq, options->fixed_palette_image ? options->fixed_palette_image : input_image);
570
611
output_image.fast_compression = options->fast_compression;
612
output_image.chunks = input_image_rwpng.chunks; input_image_rwpng.chunks = NULL;
571
613
retval = write_image(&output_image, NULL, outname, options);
573
615
if (TOO_LARGE_FILE == retval) {
580
622
// so if quality is too low, output 24-bit original
581
623
if (keep_input_pixels) {
582
624
pngquant_error write_retval = write_image(NULL, &input_image_rwpng, outname, options);
583
if (write_retval) retval = write_retval;
626
retval = write_retval;
587
631
liq_image_destroy(input_image);
588
pngquant_output_image_free(&output_image);
590
free(input_image_rwpng.row_pointers);
591
free(input_image_rwpng.rgba_data);
632
rwpng_free_image24(&input_image_rwpng);
633
rwpng_free_image8(&output_image);
669
711
const char *outfilename = strrchr(outname, '/');
670
if (outfilename) outfilename++; else outfilename = outname;
715
outfilename = outname;
672
718
if (output_image) {
673
719
verbose_printf(options, " writing %d-color image as %s", output_image->num_palette, outfilename);
690
736
fprintf(stderr, " error: failed writing image to %s\n", outname);
693
if (!options->using_stdin)
739
if (!options->using_stdin) {
699
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels)
746
static pngquant_error read_image(liq_attr *options, const char *filename, int using_stdin, png24_image *input_image_p, liq_image **liq_image_p, bool keep_input_pixels, bool verbose)
711
758
pngquant_error retval;
712
759
#pragma omp critical (libpng)
714
retval = rwpng_read_image24(infile, input_image_p);
761
retval = rwpng_read_image24(infile, input_image_p, verbose);
721
769
fprintf(stderr, " error: rwpng_read_image() error %d\n", retval);