zopfli-bin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitmodules +3 -0
  4. data/.testguardrc +1 -0
  5. data/Gemfile +17 -0
  6. data/Gemfile.lock +111 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.rdoc +19 -0
  9. data/Rakefile +39 -0
  10. data/VERSION +1 -0
  11. data/ext/Makefile +8 -0
  12. data/ext/extconf.rb +4 -0
  13. data/lib/zopfli-bin.rb +5 -0
  14. data/lib/zopfli/bin.rb +34 -0
  15. data/test/helper.rb +19 -0
  16. data/test/test_zopfli-bin.rb +33 -0
  17. data/vendor/zopfli/CONTRIBUTORS +7 -0
  18. data/vendor/zopfli/COPYING +201 -0
  19. data/vendor/zopfli/Makefile +37 -0
  20. data/vendor/zopfli/README +32 -0
  21. data/vendor/zopfli/README.zopflipng +35 -0
  22. data/vendor/zopfli/src/zopfli/blocksplitter.c +342 -0
  23. data/vendor/zopfli/src/zopfli/blocksplitter.h +77 -0
  24. data/vendor/zopfli/src/zopfli/cache.c +119 -0
  25. data/vendor/zopfli/src/zopfli/cache.h +66 -0
  26. data/vendor/zopfli/src/zopfli/deflate.c +866 -0
  27. data/vendor/zopfli/src/zopfli/deflate.h +86 -0
  28. data/vendor/zopfli/src/zopfli/gzip_container.c +117 -0
  29. data/vendor/zopfli/src/zopfli/gzip_container.h +50 -0
  30. data/vendor/zopfli/src/zopfli/hash.c +135 -0
  31. data/vendor/zopfli/src/zopfli/hash.h +70 -0
  32. data/vendor/zopfli/src/zopfli/katajainen.c +251 -0
  33. data/vendor/zopfli/src/zopfli/katajainen.h +42 -0
  34. data/vendor/zopfli/src/zopfli/lz77.c +482 -0
  35. data/vendor/zopfli/src/zopfli/lz77.h +129 -0
  36. data/vendor/zopfli/src/zopfli/squeeze.c +546 -0
  37. data/vendor/zopfli/src/zopfli/squeeze.h +60 -0
  38. data/vendor/zopfli/src/zopfli/tree.c +101 -0
  39. data/vendor/zopfli/src/zopfli/tree.h +51 -0
  40. data/vendor/zopfli/src/zopfli/util.c +213 -0
  41. data/vendor/zopfli/src/zopfli/util.h +175 -0
  42. data/vendor/zopfli/src/zopfli/zlib_container.c +79 -0
  43. data/vendor/zopfli/src/zopfli/zlib_container.h +50 -0
  44. data/vendor/zopfli/src/zopfli/zopfli.h +97 -0
  45. data/vendor/zopfli/src/zopfli/zopfli_bin.c +203 -0
  46. data/vendor/zopfli/src/zopfli/zopfli_lib.c +42 -0
  47. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.cpp +6260 -0
  48. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.h +1716 -0
  49. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.cpp +656 -0
  50. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.h +151 -0
  51. data/vendor/zopfli/src/zopflipng/zopflipng_bin.cc +407 -0
  52. data/vendor/zopfli/src/zopflipng/zopflipng_lib.cc +425 -0
  53. data/vendor/zopfli/src/zopflipng/zopflipng_lib.h +79 -0
  54. data/zopfli-bin.gemspec +119 -0
  55. metadata +225 -0
@@ -0,0 +1,151 @@
1
+ /*
2
+ LodePNG Utils
3
+
4
+ Copyright (c) 2005-2012 Lode Vandevenne
5
+
6
+ This software is provided 'as-is', without any express or implied
7
+ warranty. In no event will the authors be held liable for any damages
8
+ arising from the use of this software.
9
+
10
+ Permission is granted to anyone to use this software for any purpose,
11
+ including commercial applications, and to alter it and redistribute it
12
+ freely, subject to the following restrictions:
13
+
14
+ 1. The origin of this software must not be misrepresented; you must not
15
+ claim that you wrote the original software. If you use this software
16
+ in a product, an acknowledgment in the product documentation would be
17
+ appreciated but is not required.
18
+
19
+ 2. Altered source versions must be plainly marked as such, and must not be
20
+ misrepresented as being the original software.
21
+
22
+ 3. This notice may not be removed or altered from any source
23
+ distribution.
24
+ */
25
+
26
+ /*
27
+ Extra C++ utilities for LodePNG, for convenience.
28
+ */
29
+
30
+ #include <string>
31
+ #include <vector>
32
+ #include "lodepng.h"
33
+
34
+ #pragma once
35
+
36
+ namespace lodepng
37
+ {
38
+
39
+ /*
40
+ Returns info from the header of the PNG by value, purely for convenience.
41
+ Does NOT check for errors. Returns bogus info if the PNG has an error.
42
+ Does not require cleanup of allocated memory because no palette or text chunk
43
+ info is in the LodePNGInfo object after checking only the header of the PNG.
44
+ */
45
+ LodePNGInfo getPNGHeaderInfo(const std::vector<unsigned char>& png);
46
+
47
+ /*
48
+ Get the names and sizes of all chunks in the PNG file.
49
+ Returns 0 if ok, non-0 if error happened.
50
+ */
51
+ unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& sizes,
52
+ const std::vector<unsigned char>& png);
53
+
54
+ /*
55
+ Returns the names and full chunks (including the name and everything else that
56
+ makes up the chunk) for all chunks except IHDR, PLTE, IDAT and IEND.
57
+ It separates the chunks into 3 separate lists, representing the chunks between
58
+ certain critical chunks: 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND
59
+ Returns 0 if ok, non-0 if error happened.
60
+ */
61
+ unsigned getChunks(std::vector<std::string> names[3],
62
+ std::vector<std::vector<unsigned char> > chunks[3],
63
+ const std::vector<unsigned char>& png);
64
+
65
+ /*
66
+ Inserts chunks into the given png file. The chunks must be fully encoded,
67
+ including length, type, content and CRC.
68
+ The array index determines where it goes:
69
+ 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
70
+ They're appended at the end of those locations within the PNG.
71
+ Returns 0 if ok, non-0 if error happened.
72
+ */
73
+ unsigned insertChunks(std::vector<unsigned char>& png,
74
+ const std::vector<std::vector<unsigned char> > chunks[3]);
75
+
76
+ /*
77
+ Get the filtertypes of each scanline in this PNG file.
78
+ Returns 0 if ok, 1 if PNG decoding error happened.
79
+
80
+ For a non-interlaced PNG, it returns one filtertype per scanline, in order.
81
+
82
+ For interlaced PNGs, it returns a result as if it's not interlaced. It returns
83
+ one filtertype per scanline, in order. The values match pass 6 and 7 of the
84
+ Adam7 interlacing, alternating between the two, so that the values correspond
85
+ the most to their scanlines.
86
+ */
87
+ unsigned getFilterTypes(std::vector<unsigned char>& filterTypes, const std::vector<unsigned char>& png);
88
+
89
+ /*
90
+ Get the filtertypes of each scanline in every interlace pass this PNG file.
91
+ Returns 0 if ok, 1 if PNG decoding error happened.
92
+
93
+ For a non-interlaced PNG, it returns one filtertype per scanline, in order, in
94
+ a single std::vector in filterTypes.
95
+
96
+ For an interlaced PNG, it returns 7 std::vectors in filterTypes, one for each
97
+ Adam7 pass. The amount of values per pass can be calculated as follows, where
98
+ w and h are the size of the image and all divisions are integer divisions:
99
+ pass 1: (h + 7) / 8
100
+ pass 2: w <= 4 ? 0 : (h + 7) / 8
101
+ pass 3: h <= 4 ? 0 : (h + 7) / 8
102
+ pass 4: w <= 2 ? 0 : (h + 3) / 4
103
+ pass 5: h <= 2 ? 0 : (h + 3) / 4
104
+ pass 6: w <= 1 ? 0 : (h + 1) / 2
105
+ pass 7: h <= 1 ? 0 : (h + 1) / 2
106
+ */
107
+ unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filterTypes,
108
+ const std::vector<unsigned char>& png);
109
+
110
+ /*
111
+ Returns the value of the i-th pixel in an image with 1, 2, 4 or 8-bit color.
112
+ E.g. if bits is 4 and i is 5, it returns the 5th nibble (4-bit group), which
113
+ is the second half of the 3th byte, in big endian (PNG's endian order).
114
+ */
115
+ int getPaletteValue(const unsigned char* data, size_t i, int bits);
116
+
117
+ /*
118
+ The information for extractZlibInfo.
119
+ */
120
+ struct ZlibBlockInfo
121
+ {
122
+ int btype; //block type (0-2)
123
+ size_t compressedbits; //size of compressed block in bits
124
+ size_t uncompressedbytes; //size of uncompressed block in bytes
125
+
126
+ // only filled in for block type 2
127
+ size_t treebits; //encoded tree size in bits
128
+ int hlit; //the HLIT value that was filled in for this tree
129
+ int hdist; //the HDIST value that was filled in for this tree
130
+ int hclen; //the HCLEN value that was filled in for this tree
131
+ std::vector<int> clcl; //19 code length code lengths (compressed tree's tree)
132
+ std::vector<int> treecodes; //N tree codes, with values 0-18. Values 17 or 18 are followed by the repetition value.
133
+ std::vector<int> litlenlengths; //288 code lengths for lit/len symbols
134
+ std::vector<int> distlengths; //32 code lengths for dist symbols
135
+
136
+ // only filled in for block types 1 or 2
137
+ std::vector<int> lz77_lcode; //LZ77 codes. 0-255: literals. 256: end symbol. 257-285: length code of length/dist pairs
138
+ // the next vectors have the same size as lz77_lcode, but an element only has meaningful value if lz77_lcode contains a length code.
139
+ std::vector<int> lz77_dcode;
140
+ std::vector<int> lz77_lbits;
141
+ std::vector<int> lz77_dbits;
142
+ std::vector<int> lz77_lvalue;
143
+ std::vector<int> lz77_dvalue;
144
+ size_t numlit; //number of lit codes in this block
145
+ size_t numlen; //number of len codes in this block
146
+ };
147
+
148
+ //Extracts all info needed from a PNG file to reconstruct the zlib compression exactly.
149
+ void extractZlibInfo(std::vector<ZlibBlockInfo>& zlibinfo, const std::vector<unsigned char>& in);
150
+
151
+ } // namespace lodepng
@@ -0,0 +1,407 @@
1
+ // Copyright 2013 Google Inc. All Rights Reserved.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // Author: lode.vandevenne@gmail.com (Lode Vandevenne)
16
+ // Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
17
+
18
+ // Command line tool to recompress and optimize PNG images, using zopflipng_lib.
19
+
20
+ #include <stdlib.h>
21
+ #include <stdio.h>
22
+
23
+ #include "lodepng/lodepng.h"
24
+ #include "zopflipng_lib.h"
25
+
26
+ // Returns directory path (including last slash) in dir, filename without
27
+ // extension in file, extension (including the dot) in ext
28
+ void GetFileNameParts(const std::string& filename,
29
+ std::string* dir, std::string* file, std::string* ext) {
30
+ size_t npos = (size_t)(-1);
31
+ size_t slashpos = filename.find_last_of("/\\");
32
+ std::string nodir;
33
+ if (slashpos == npos) {
34
+ *dir = "";
35
+ nodir = filename;
36
+ } else {
37
+ *dir = filename.substr(0, slashpos + 1);
38
+ nodir = filename.substr(slashpos + 1);
39
+ }
40
+ size_t dotpos = nodir.find_last_of('.');
41
+ if (dotpos == (size_t)(-1)) {
42
+ *file = nodir;
43
+ *ext = "";
44
+ } else {
45
+ *file = nodir.substr(0, dotpos);
46
+ *ext = nodir.substr(dotpos);
47
+ }
48
+ }
49
+
50
+ // Returns the size of the file
51
+ size_t GetFileSize(const std::string& filename) {
52
+ size_t size;
53
+ FILE* file = fopen(filename.c_str(), "rb");
54
+ if (!file) return 0;
55
+ fseek(file , 0 , SEEK_END);
56
+ size = static_cast<size_t>(ftell(file));
57
+ fclose(file);
58
+ return size;
59
+ }
60
+
61
+ void ShowHelp() {
62
+ printf("ZopfliPNG, a Portable Network Graphics (PNG) image optimizer.\n"
63
+ "\n"
64
+ "Usage: zopflipng [options]... infile.png outfile.png\n"
65
+ " zopflipng [options]... --prefix=[fileprefix] [files.png]...\n"
66
+ "\n"
67
+ "If the output file exists, it is considered a result from a"
68
+ " previous run and not overwritten if its filesize is smaller.\n"
69
+ "\n"
70
+ "Options:\n"
71
+ "-m: compress more: use more iterations (depending on file size) and"
72
+ " use block split strategy 3\n"
73
+ "--prefix=[fileprefix]: Adds a prefix to output filenames. May also"
74
+ " contain a directory path. When using a prefix, multiple input files"
75
+ " can be given and the output filenames are generated with the"
76
+ " prefix\n"
77
+ " If --prefix is specified without value, 'zopfli_' is used.\n"
78
+ " If input file names contain the prefix, they are not processed but"
79
+ " considered as output from previous runs. This is handy when using"
80
+ " *.png wildcard expansion with multiple runs.\n"
81
+ "-y: do not ask about overwriting files.\n"
82
+ "--lossy_transparent: remove colors behind alpha channel 0. No visual"
83
+ " difference, removes hidden information.\n"
84
+ "--lossy_8bit: convert 16-bit per channel image to 8-bit per"
85
+ " channel.\n"
86
+ "-d: dry run: don't save any files, just see the console output"
87
+ " (e.g. for benchmarking)\n"
88
+ "--always_zopflify: always output the image encoded by Zopfli, even if"
89
+ " it's bigger than the original, for benchmarking the algorithm. Not"
90
+ " good for real optimization.\n"
91
+ "-q: use quick, but not very good, compression"
92
+ " (e.g. for only trying the PNG filter and color types)\n"
93
+ "--iterations=[number]: number of iterations, more iterations makes it"
94
+ " slower but provides slightly better compression. Default: 15 for"
95
+ " small files, 5 for large files.\n"
96
+ "--splitting=[0-3]: block split strategy:"
97
+ " 0=none, 1=first, 2=last, 3=try both and take the best\n"
98
+ "--filters=[types]: filter strategies to try:\n"
99
+ " 0-4: give all scanlines PNG filter type 0-4\n"
100
+ " m: minimum sum\n"
101
+ " e: entropy\n"
102
+ " p: predefined (keep from input, this likely overlaps another"
103
+ " strategy)\n"
104
+ " b: brute force (experimental)\n"
105
+ " By default, if this argument is not given, one that is most likely"
106
+ " the best for this image is chosen by trying faster compression with"
107
+ " each type.\n"
108
+ " If this argument is used, all given filter types"
109
+ " are tried with slow compression and the best result retained. A good"
110
+ " set of filters to try is --filters=0me.\n"
111
+ "--keepchunks=nAME,nAME,...: keep metadata chunks with these names"
112
+ " that would normally be removed, e.g. tEXt,zTXt,iTXt,gAMA, ... \n"
113
+ " Due to adding extra data, this increases the result size. By default"
114
+ " ZopfliPNG only keeps the following chunks because they are"
115
+ " essential: IHDR, PLTE, tRNS, IDAT and IEND.\n"
116
+ "\n"
117
+ "Usage examples:\n"
118
+ "Optimize a file and overwrite if smaller: zopflipng infile.png"
119
+ " outfile.png\n"
120
+ "Compress more: zopflipng -m infile.png outfile.png\n"
121
+ "Optimize multiple files: zopflipng --prefix a.png b.png c.png\n"
122
+ "Compress really good and trying all filter strategies: zopflipng"
123
+ " --iterations=500 --splitting=3 --filters=01234mepb"
124
+ " --lossy_8bit --lossy_transparent infile.png outfile.png\n");
125
+ }
126
+
127
+ void PrintSize(const char* label, size_t size) {
128
+ printf("%s: %d (%dK)\n", label, (int) size, (int) size / 1024);
129
+ }
130
+
131
+ void PrintResultSize(const char* label, size_t oldsize, size_t newsize) {
132
+ printf("%s: %d (%dK). Percentage of original: %.3f%%\n",
133
+ label, (int) newsize, (int) newsize / 1024, newsize * 100.0 / oldsize);
134
+ }
135
+
136
+ int main(int argc, char *argv[]) {
137
+ if (argc < 2) {
138
+ ShowHelp();
139
+ return 0;
140
+ }
141
+
142
+ ZopfliPNGOptions png_options;
143
+
144
+ // cmd line options
145
+ bool always_zopflify = false; // overwrite file even if we have bigger result
146
+ bool yes = false; // do not ask to overwrite files
147
+ bool dryrun = false; // never save anything
148
+
149
+ std::string user_out_filename; // output filename if no prefix is used
150
+ bool use_prefix = false;
151
+ std::string prefix = "zopfli_"; // prefix for output filenames
152
+
153
+ std::vector<std::string> files;
154
+ std::vector<char> options;
155
+ for (int i = 1; i < argc; i++) {
156
+ std::string arg = argv[i];
157
+ if (arg[0] == '-' && arg.size() > 1 && arg[1] != '-') {
158
+ for (size_t pos = 1; pos < arg.size(); pos++) {
159
+ char c = arg[pos];
160
+ if (c == 'y') {
161
+ yes = true;
162
+ } else if (c == 'd') {
163
+ dryrun = true;
164
+ } else if (c == 'm') {
165
+ png_options.num_iterations *= 4;
166
+ png_options.num_iterations_large *= 4;
167
+ png_options.block_split_strategy = 3;
168
+ } else if (c == 'q') {
169
+ png_options.use_zopfli = false;
170
+ } else if (c == 'h') {
171
+ ShowHelp();
172
+ return 0;
173
+ } else {
174
+ printf("Unknown flag: %c\n", c);
175
+ return 0;
176
+ }
177
+ }
178
+ } else if (arg[0] == '-' && arg.size() > 1 && arg[1] == '-') {
179
+ size_t eq = arg.find('=');
180
+ std::string name = arg.substr(0, eq);
181
+ std::string value = eq >= arg.size() - 1 ? "" : arg.substr(eq + 1);
182
+ int num = atoi(value.c_str());
183
+ if (name == "--always_zopflify") {
184
+ always_zopflify = true;
185
+ } else if (name == "--lossy_transparent") {
186
+ png_options.lossy_transparent = true;
187
+ } else if (name == "--lossy_8bit") {
188
+ png_options.lossy_8bit = true;
189
+ } else if (name == "--iterations") {
190
+ if (num < 1) num = 1;
191
+ png_options.num_iterations = num;
192
+ png_options.num_iterations_large = num;
193
+ } else if (name == "--splitting") {
194
+ if (num < 0 || num > 3) num = 1;
195
+ png_options.block_split_strategy = num;
196
+ } else if (name == "--filters") {
197
+ for (size_t j = 0; j < value.size(); j++) {
198
+ ZopfliPNGFilterStrategy strategy = kStrategyZero;
199
+ char f = value[j];
200
+ switch (f) {
201
+ case '0': strategy = kStrategyZero; break;
202
+ case '1': strategy = kStrategyOne; break;
203
+ case '2': strategy = kStrategyTwo; break;
204
+ case '3': strategy = kStrategyThree; break;
205
+ case '4': strategy = kStrategyFour; break;
206
+ case 'm': strategy = kStrategyMinSum; break;
207
+ case 'e': strategy = kStrategyEntropy; break;
208
+ case 'p': strategy = kStrategyPredefined; break;
209
+ case 'b': strategy = kStrategyBruteForce; break;
210
+ default:
211
+ printf("Unknown filter strategy: %c\n", f);
212
+ return 1;
213
+ }
214
+ png_options.filter_strategies.push_back(strategy);
215
+ // Enable auto filter strategy only if no user-specified filter is
216
+ // given.
217
+ png_options.auto_filter_strategy = false;
218
+ }
219
+ } else if (name == "--keepchunks") {
220
+ bool correct = true;
221
+ if ((value.size() + 1) % 5 != 0) correct = false;
222
+ for (size_t i = 0; i + 4 <= value.size() && correct; i += 5) {
223
+ png_options.keepchunks.push_back(value.substr(i, 4));
224
+ if (i > 4 && value[i - 1] != ',') correct = false;
225
+ }
226
+ if (!correct) {
227
+ printf("Error: keepchunks format must be like for example:\n"
228
+ " --keepchunks=gAMA,cHRM,sRGB,iCCP\n");
229
+ return 0;
230
+ }
231
+ } else if (name == "--prefix") {
232
+ use_prefix = true;
233
+ if (!value.empty()) prefix = value;
234
+ } else if (name == "--help") {
235
+ ShowHelp();
236
+ return 0;
237
+ } else {
238
+ printf("Unknown flag: %s\n", name.c_str());
239
+ return 0;
240
+ }
241
+ } else {
242
+ files.push_back(argv[i]);
243
+ }
244
+ }
245
+
246
+ if (!use_prefix) {
247
+ if (files.size() == 2) {
248
+ // The second filename is the output instead of an input if no prefix is
249
+ // given.
250
+ user_out_filename = files[1];
251
+ files.resize(1);
252
+ } else {
253
+ printf("Please provide one input and output filename\n\n");
254
+ ShowHelp();
255
+ return 0;
256
+ }
257
+ }
258
+
259
+ size_t total_in_size = 0;
260
+ // Total output size, taking input size if the input file was smaller
261
+ size_t total_out_size = 0;
262
+ // Total output size that zopfli produced, even if input was smaller, for
263
+ // benchmark information
264
+ size_t total_out_size_zopfli = 0;
265
+ size_t total_errors = 0;
266
+ size_t total_files = 0;
267
+ size_t total_files_smaller = 0;
268
+ size_t total_files_saved = 0;
269
+ size_t total_files_equal = 0;
270
+
271
+ for (size_t i = 0; i < files.size(); i++) {
272
+ if (use_prefix && files.size() > 1) {
273
+ std::string dir, file, ext;
274
+ GetFileNameParts(files[i], &dir, &file, &ext);
275
+ // avoid doing filenames which were already output by this so that you
276
+ // don't get zopfli_zopfli_zopfli_... files after multiple runs.
277
+ if (file.find(prefix) == 0) continue;
278
+ }
279
+
280
+ total_files++;
281
+
282
+ printf("Optimizing %s\n", files[i].c_str());
283
+ std::vector<unsigned char> image;
284
+ unsigned w, h;
285
+ std::vector<unsigned char> origpng;
286
+ unsigned error;
287
+ lodepng::State inputstate;
288
+ std::vector<unsigned char> resultpng;
289
+
290
+ lodepng::load_file(origpng, files[i]);
291
+ error = ZopfliPNGOptimize(origpng, png_options, true, &resultpng);
292
+
293
+ if (error) {
294
+ printf("Decoding error %i: %s\n", error, lodepng_error_text(error));
295
+ }
296
+
297
+ // Verify result, check that the result causes no decoding errors
298
+ if (!error) {
299
+ error = lodepng::decode(image, w, h, inputstate, resultpng);
300
+ if (error) printf("Error: verification of result failed.\n");
301
+ }
302
+
303
+ if (error) {
304
+ printf("There was an error\n");
305
+ total_errors++;
306
+ } else {
307
+ size_t origsize = GetFileSize(files[i]);
308
+ size_t resultsize = resultpng.size();
309
+
310
+ if (resultsize < origsize) {
311
+ printf("Result is smaller\n");
312
+ } else if (resultsize == origsize) {
313
+ printf("Result has exact same size\n");
314
+ } else {
315
+ printf(always_zopflify
316
+ ? "Original was smaller\n"
317
+ : "Preserving original PNG since it was smaller\n");
318
+ }
319
+ PrintSize("Input size", origsize);
320
+ PrintResultSize("Result size", origsize, resultsize);
321
+
322
+ std::string out_filename = user_out_filename;
323
+ if (use_prefix) {
324
+ std::string dir, file, ext;
325
+ GetFileNameParts(files[i], &dir, &file, &ext);
326
+ out_filename = dir + prefix + file + ext;
327
+ }
328
+ bool different_output_name = out_filename != files[i];
329
+
330
+ total_in_size += origsize;
331
+ total_out_size_zopfli += resultpng.size();
332
+ if (resultpng.size() < origsize) total_files_smaller++;
333
+ else if (resultpng.size() == origsize) total_files_equal++;
334
+
335
+ if (!always_zopflify && resultpng.size() > origsize) {
336
+ // Set output file to input since input was smaller.
337
+ resultpng = origpng;
338
+ }
339
+
340
+ size_t origoutfilesize = GetFileSize(out_filename);
341
+ bool already_exists = true;
342
+ if (origoutfilesize == 0) already_exists = false;
343
+
344
+ // When using a prefix, and the output file already exist, assume it's
345
+ // from a previous run. If that file is smaller, it may represent a
346
+ // previous run with different parameters that gave a smaller PNG image.
347
+ // In that case, do not overwrite it. This behaviour can be removed by
348
+ // adding the always_zopflify flag.
349
+ bool keep_earlier_output_file = already_exists &&
350
+ resultpng.size() >= origoutfilesize && !always_zopflify && use_prefix;
351
+
352
+ if (keep_earlier_output_file) {
353
+ // An output file from a previous run is kept, add that files' size
354
+ // to the output size statistics.
355
+ total_out_size += origoutfilesize;
356
+ if (different_output_name) {
357
+ printf(resultpng.size() == origoutfilesize
358
+ ? "File not written because a previous run was as good.\n"
359
+ : "File not written because a previous run was better.\n");
360
+ }
361
+ } else {
362
+ bool confirmed = true;
363
+ if (!yes && !dryrun && already_exists) {
364
+ printf("File %s exists, overwrite? (y/N) ", out_filename.c_str());
365
+ char answer = 0;
366
+ // Read the first character, the others and enter with getchar.
367
+ while (int input = getchar()) {
368
+ if (input == '\n' || input == EOF) break;
369
+ else if (!answer) answer = input;
370
+ }
371
+ confirmed = answer == 'y' || answer == 'Y';
372
+ }
373
+ if (confirmed) {
374
+ if (!dryrun) {
375
+ lodepng::save_file(resultpng, out_filename);
376
+ total_files_saved++;
377
+ }
378
+ total_out_size += resultpng.size();
379
+ } else {
380
+ // An output file from a previous run is kept, add that files' size
381
+ // to the output size statistics.
382
+ total_out_size += origoutfilesize;
383
+ }
384
+ }
385
+ }
386
+ printf("\n");
387
+ }
388
+
389
+ if (total_files > 1) {
390
+ printf("Summary for all files:\n");
391
+ printf("Files tried: %d\n", (int) total_files);
392
+ printf("Files smaller: %d\n", (int) total_files_smaller);
393
+ if (total_files_equal) {
394
+ printf("Files equal: %d\n", (int) total_files_equal);
395
+ }
396
+ printf("Files saved: %d\n", (int) total_files_saved);
397
+ if (total_errors) printf("Errors: %d\n", (int) total_errors);
398
+ PrintSize("Total input size", total_in_size);
399
+ PrintResultSize("Total output size", total_in_size, total_out_size);
400
+ PrintResultSize("Benchmark result size",
401
+ total_in_size, total_out_size_zopfli);
402
+ }
403
+
404
+ if (dryrun) printf("No files were written because dry run was specified\n");
405
+
406
+ return total_errors;
407
+ }