1337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Copyright 2013 Google Inc. All Rights Reserved.
2337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne//
3337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Licensed under the Apache License, Version 2.0 (the "License");
4337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// you may not use this file except in compliance with the License.
5337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// You may obtain a copy of the License at
6337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne//
7337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne//    http://www.apache.org/licenses/LICENSE-2.0
8337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne//
9337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Unless required by applicable law or agreed to in writing, software
10337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// distributed under the License is distributed on an "AS IS" BASIS,
11337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// See the License for the specific language governing permissions and
13337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// limitations under the License.
14337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne//
15337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Author: lode.vandevenne@gmail.com (Lode Vandevenne)
16337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
17337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
18337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Command line tool to recompress and optimize PNG images, using zopflipng_lib.
19337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
20337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include <stdlib.h>
21337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include <stdio.h>
22337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
23337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "lodepng/lodepng.h"
24337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "zopflipng_lib.h"
25337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
26337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Returns directory path (including last slash) in dir, filename without
27337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// extension in file, extension (including the dot) in ext
28337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennevoid GetFileNameParts(const std::string& filename,
29337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    std::string* dir, std::string* file, std::string* ext) {
30337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t npos = (size_t)(-1);
31337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t slashpos = filename.find_last_of("/\\");
32337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  std::string nodir;
33337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (slashpos == npos) {
34337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *dir = "";
35337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    nodir = filename;
36337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  } else {
37337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *dir = filename.substr(0, slashpos + 1);
38337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    nodir = filename.substr(slashpos + 1);
39337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
40337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t dotpos = nodir.find_last_of('.');
41337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (dotpos == (size_t)(-1)) {
42337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *file = nodir;
43337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *ext = "";
44337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  } else {
45337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *file = nodir.substr(0, dotpos);
46337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    *ext = nodir.substr(dotpos);
47337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
48337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
49337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
50337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Returns the size of the file
51337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennesize_t GetFileSize(const std::string& filename) {
52337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t size;
53337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  FILE* file = fopen(filename.c_str(), "rb");
54337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (!file) return 0;
55337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  fseek(file , 0 , SEEK_END);
56337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size = static_cast<size_t>(ftell(file));
57337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  fclose(file);
58337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  return size;
59337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
60337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
61337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennevoid ShowHelp() {
62337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  printf("ZopfliPNG, a Portable Network Graphics (PNG) image optimizer.\n"
63337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "\n"
64337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Usage: zopflipng [options]... infile.png outfile.png\n"
65337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "       zopflipng [options]... --prefix=[fileprefix] [files.png]...\n"
66337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "\n"
67337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "If the output file exists, it is considered a result from a"
68337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " previous run and not overwritten if its filesize is smaller.\n"
69337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "\n"
70337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Options:\n"
71337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "-m: compress more: use more iterations (depending on file size) and"
72337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " use block split strategy 3\n"
73337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--prefix=[fileprefix]: Adds a prefix to output filenames. May also"
74337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " contain a directory path. When using a prefix, multiple input files"
75337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " can be given and the output filenames are generated with the"
76337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " prefix\n"
77337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " If --prefix is specified without value, 'zopfli_' is used.\n"
78337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " If input file names contain the prefix, they are not processed but"
79337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " considered as output from previous runs. This is handy when using"
80337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " *.png wildcard expansion with multiple runs.\n"
81337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "-y: do not ask about overwriting files.\n"
82337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--lossy_transparent: remove colors behind alpha channel 0. No visual"
83337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " difference, removes hidden information.\n"
84337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--lossy_8bit: convert 16-bit per channel image to 8-bit per"
85337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " channel.\n"
86337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "-d: dry run: don't save any files, just see the console output"
87337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " (e.g. for benchmarking)\n"
88337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--always_zopflify: always output the image encoded by Zopfli, even if"
89337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " it's bigger than the original, for benchmarking the algorithm. Not"
90337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " good for real optimization.\n"
91337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "-q: use quick, but not very good, compression"
92337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " (e.g. for only trying the PNG filter and color types)\n"
93337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--iterations=[number]: number of iterations, more iterations makes it"
94337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " slower but provides slightly better compression. Default: 15 for"
95337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " small files, 5 for large files.\n"
96337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--splitting=[0-3]: block split strategy:"
97337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " 0=none, 1=first, 2=last, 3=try both and take the best\n"
98337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--filters=[types]: filter strategies to try:\n"
99337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " 0-4: give all scanlines PNG filter type 0-4\n"
100337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " m: minimum sum\n"
101337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " e: entropy\n"
102337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " p: predefined (keep from input, this likely overlaps another"
103337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " strategy)\n"
104337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " b: brute force (experimental)\n"
105337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " By default, if this argument is not given, one that is most likely"
106337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " the best for this image is chosen by trying faster compression with"
107337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " each type.\n"
108337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " If this argument is used, all given filter types"
109337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " are tried with slow compression and the best result retained. A good"
110337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " set of filters to try is --filters=0me.\n"
111337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "--keepchunks=nAME,nAME,...: keep metadata chunks with these names"
112337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " that would normally be removed, e.g. tEXt,zTXt,iTXt,gAMA, ... \n"
113337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " Due to adding extra data, this increases the result size. By default"
114337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " ZopfliPNG only keeps the following chunks because they are"
115337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " essential: IHDR, PLTE, tRNS, IDAT and IEND.\n"
116337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "\n"
117337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Usage examples:\n"
118337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Optimize a file and overwrite if smaller: zopflipng infile.png"
119337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " outfile.png\n"
120337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Compress more: zopflipng -m infile.png outfile.png\n"
121337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Optimize multiple files: zopflipng --prefix a.png b.png c.png\n"
122337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         "Compress really good and trying all filter strategies: zopflipng"
123337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " --iterations=500 --splitting=3 --filters=01234mepb"
124337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         " --lossy_8bit --lossy_transparent infile.png outfile.png\n");
125337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
126337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
127337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennevoid PrintSize(const char* label, size_t size) {
128337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  printf("%s: %d (%dK)\n", label, (int) size, (int) size / 1024);
129337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
130337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
131337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennevoid PrintResultSize(const char* label, size_t oldsize, size_t newsize) {
132337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  printf("%s: %d (%dK). Percentage of original: %.3f%%\n",
133337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne         label, (int) newsize, (int) newsize / 1024, newsize * 100.0 / oldsize);
134337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
135337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
136337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenneint main(int argc, char *argv[]) {
137337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (argc < 2) {
138337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    ShowHelp();
139337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    return 0;
140337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
141337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
142337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  ZopfliPNGOptions png_options;
143337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
144337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  // cmd line options
145337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  bool always_zopflify = false;  // overwrite file even if we have bigger result
146337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  bool yes = false;  // do not ask to overwrite files
147337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  bool dryrun = false;  // never save anything
148337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
149337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  std::string user_out_filename;  // output filename if no prefix is used
150337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  bool use_prefix = false;
151337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  std::string prefix = "zopfli_";  // prefix for output filenames
152337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
153337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  std::vector<std::string> files;
154337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  std::vector<char> options;
155337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  for (int i = 1; i < argc; i++) {
156337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    std::string arg = argv[i];
157337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (arg[0] == '-' && arg.size() > 1 && arg[1] != '-') {
158337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      for (size_t pos = 1; pos < arg.size(); pos++) {
159337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        char c = arg[pos];
160337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (c == 'y') {
161337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          yes = true;
162337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else if (c == 'd') {
163337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          dryrun = true;
164337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else if (c == 'm') {
165337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.num_iterations *= 4;
166337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.num_iterations_large *= 4;
167337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.block_split_strategy = 3;
168337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else if (c == 'q') {
169337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.use_zopfli = false;
170337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else if (c == 'h') {
171337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          ShowHelp();
172337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          return 0;
173337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else {
174337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          printf("Unknown flag: %c\n", c);
175337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          return 0;
176337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
177337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
178337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    } else if (arg[0] == '-' && arg.size() > 1 && arg[1] == '-') {
179337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      size_t eq = arg.find('=');
180337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      std::string name = arg.substr(0, eq);
181337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      std::string value = eq >= arg.size() - 1 ? "" : arg.substr(eq + 1);
182337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      int num = atoi(value.c_str());
183337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (name == "--always_zopflify") {
184337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        always_zopflify = true;
185337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--lossy_transparent") {
186337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        png_options.lossy_transparent = true;
187337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--lossy_8bit") {
188337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        png_options.lossy_8bit = true;
189337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--iterations") {
190337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (num < 1) num = 1;
191337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        png_options.num_iterations = num;
192337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        png_options.num_iterations_large = num;
193337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--splitting") {
194337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (num < 0 || num > 3) num = 1;
195337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        png_options.block_split_strategy = num;
196337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--filters") {
197337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        for (size_t j = 0; j < value.size(); j++) {
198337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          ZopfliPNGFilterStrategy strategy = kStrategyZero;
199337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          char f = value[j];
200337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          switch (f) {
201337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case '0': strategy = kStrategyZero; break;
202337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case '1': strategy = kStrategyOne; break;
203337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case '2': strategy = kStrategyTwo; break;
204337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case '3': strategy = kStrategyThree; break;
205337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case '4': strategy = kStrategyFour; break;
206337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case 'm': strategy = kStrategyMinSum; break;
207337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case 'e': strategy = kStrategyEntropy; break;
208337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case 'p': strategy = kStrategyPredefined; break;
209337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            case 'b': strategy = kStrategyBruteForce; break;
210337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            default:
211337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne              printf("Unknown filter strategy: %c\n", f);
212337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne              return 1;
213337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          }
214337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.filter_strategies.push_back(strategy);
215337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          // Enable auto filter strategy only if no user-specified filter is
216337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          // given.
217337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.auto_filter_strategy = false;
218337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
219337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--keepchunks") {
220337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        bool correct = true;
221337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if ((value.size() + 1) % 5 != 0) correct = false;
222337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        for (size_t i = 0; i + 4 <= value.size() && correct; i += 5) {
223337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          png_options.keepchunks.push_back(value.substr(i, 4));
224337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          if (i > 4 && value[i - 1] != ',') correct = false;
225337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
226337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (!correct) {
227337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          printf("Error: keepchunks format must be like for example:\n"
228337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne                 " --keepchunks=gAMA,cHRM,sRGB,iCCP\n");
229337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          return 0;
230337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
231337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--prefix") {
232337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        use_prefix = true;
233337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (!value.empty()) prefix = value;
234337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (name == "--help") {
235337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        ShowHelp();
236337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        return 0;
237337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else {
238337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        printf("Unknown flag: %s\n", name.c_str());
239337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        return 0;
240337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
241337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    } else {
242337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      files.push_back(argv[i]);
243337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
244337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
245337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
246337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (!use_prefix) {
247337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (files.size() == 2) {
248337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // The second filename is the output instead of an input if no prefix is
249337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // given.
250337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      user_out_filename = files[1];
251337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      files.resize(1);
252337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    } else {
253337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      printf("Please provide one input and output filename\n\n");
254337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      ShowHelp();
255337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      return 0;
256337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
257337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
258337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
259337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_in_size = 0;
260337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  // Total output size, taking input size if the input file was smaller
261337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_out_size = 0;
262337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  // Total output size that zopfli produced, even if input was smaller, for
263337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  // benchmark information
264337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_out_size_zopfli = 0;
265337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_errors = 0;
266337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_files = 0;
267337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_files_smaller = 0;
268337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_files_saved = 0;
269337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  size_t total_files_equal = 0;
270337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
271337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  for (size_t i = 0; i < files.size(); i++) {
272337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (use_prefix && files.size() > 1) {
273337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      std::string dir, file, ext;
274337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      GetFileNameParts(files[i], &dir, &file, &ext);
275337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // avoid doing filenames which were already output by this so that you
276337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // don't get zopfli_zopfli_zopfli_... files after multiple runs.
277337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (file.find(prefix) == 0) continue;
278337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
279337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
280337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    total_files++;
281337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
282337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("Optimizing %s\n", files[i].c_str());
283337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    std::vector<unsigned char> image;
284337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    unsigned w, h;
285337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    std::vector<unsigned char> origpng;
286337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    unsigned error;
287337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    lodepng::State inputstate;
288337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    std::vector<unsigned char> resultpng;
289337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
290337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    lodepng::load_file(origpng, files[i]);
291337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    error = ZopfliPNGOptimize(origpng, png_options, true, &resultpng);
292337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
293337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (error) {
294337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      printf("Decoding error %i: %s\n", error, lodepng_error_text(error));
295337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
296337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
297337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    // Verify result, check that the result causes no decoding errors
298337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (!error) {
299337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      error = lodepng::decode(image, w, h, inputstate, resultpng);
300337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (error) printf("Error: verification of result failed.\n");
301337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
302337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
303337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (error) {
304337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      printf("There was an error\n");
305337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      total_errors++;
306337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    } else {
307337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      size_t origsize = GetFileSize(files[i]);
308337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      size_t resultsize = resultpng.size();
309337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
310337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (resultsize < origsize) {
311337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        printf("Result is smaller\n");
312337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else if (resultsize == origsize) {
313337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        printf("Result has exact same size\n");
314337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else {
315337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        printf(always_zopflify
316337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            ? "Original was smaller\n"
317337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            : "Preserving original PNG since it was smaller\n");
318337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
319337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      PrintSize("Input size", origsize);
320337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      PrintResultSize("Result size", origsize, resultsize);
321337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
322337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      std::string out_filename = user_out_filename;
323337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (use_prefix) {
324337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        std::string dir, file, ext;
325337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        GetFileNameParts(files[i], &dir, &file, &ext);
326337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        out_filename = dir + prefix + file + ext;
327337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
328337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      bool different_output_name = out_filename != files[i];
329337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
330337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      total_in_size += origsize;
331337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      total_out_size_zopfli += resultpng.size();
332337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (resultpng.size() < origsize) total_files_smaller++;
333337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      else if (resultpng.size() == origsize) total_files_equal++;
334337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
335337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (!always_zopflify && resultpng.size() > origsize) {
336337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        // Set output file to input since input was smaller.
337337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        resultpng = origpng;
338337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
339337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
340337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      size_t origoutfilesize = GetFileSize(out_filename);
341337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      bool already_exists = true;
342337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (origoutfilesize == 0) already_exists = false;
343337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
344337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // When using a prefix, and the output file already exist, assume it's
345337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // from a previous run. If that file is smaller, it may represent a
346337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // previous run with different parameters that gave a smaller PNG image.
347337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // In that case, do not overwrite it. This behaviour can be removed by
348337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      // adding the always_zopflify flag.
349337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      bool keep_earlier_output_file = already_exists &&
350337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          resultpng.size() >= origoutfilesize && !always_zopflify && use_prefix;
351337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
352337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      if (keep_earlier_output_file) {
353337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        // An output file from a previous run is kept, add that files' size
354337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        // to the output size statistics.
355337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        total_out_size += origoutfilesize;
356337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (different_output_name) {
357337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          printf(resultpng.size() == origoutfilesize
358337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne              ? "File not written because a previous run was as good.\n"
359337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne              : "File not written because a previous run was better.\n");
360337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
361337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      } else {
362337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        bool confirmed = true;
363337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (!yes && !dryrun && already_exists) {
364337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          printf("File %s exists, overwrite? (y/N) ", out_filename.c_str());
365c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne          char answer = 0;
366c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne          // Read the first character, the others and enter with getchar.
367c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne          while (int input = getchar()) {
368c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne            if (input == '\n' || input == EOF) break;
369c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne            else if (!answer) answer = input;
370c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne          }
371c54dc204ef4278f949a965dc90e693799b6aae41Lode Vandevenne          confirmed = answer == 'y' || answer == 'Y';
372337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
373337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        if (confirmed) {
374337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          if (!dryrun) {
375337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            lodepng::save_file(resultpng, out_filename);
376337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne            total_files_saved++;
377337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          }
378337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          total_out_size += resultpng.size();
379337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        } else {
380337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          // An output file from a previous run is kept, add that files' size
381337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          // to the output size statistics.
382337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne          total_out_size += origoutfilesize;
383337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne        }
384337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      }
385337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
386337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("\n");
387337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
388337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
389337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (total_files > 1) {
390337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("Summary for all files:\n");
391337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("Files tried: %d\n", (int) total_files);
392337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("Files smaller: %d\n", (int) total_files_smaller);
393337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (total_files_equal) {
394337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne      printf("Files equal: %d\n", (int) total_files_equal);
395337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    }
396337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    printf("Files saved: %d\n", (int) total_files_saved);
397337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    if (total_errors) printf("Errors: %d\n", (int) total_errors);
398337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    PrintSize("Total input size", total_in_size);
399337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    PrintResultSize("Total output size", total_in_size, total_out_size);
400337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne    PrintResultSize("Benchmark result size",
401337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne                    total_in_size, total_out_size_zopfli);
402337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  }
403337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
404337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  if (dryrun) printf("No files were written because dry run was specified\n");
405337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne
406337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne  return total_errors;
407337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne}
408