zopfli 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +6 -14
  2. data/ext/extconf.rb +1 -1
  3. data/lib/zopfli/version.rb +1 -1
  4. data/vendor/zopfli/CONTRIBUTORS +1 -0
  5. data/vendor/zopfli/README +18 -11
  6. data/vendor/zopfli/README.zopflipng +35 -0
  7. data/vendor/zopfli/makefile +36 -4
  8. data/vendor/zopfli/{blocksplitter.c → src/zopfli/blocksplitter.c} +0 -0
  9. data/vendor/zopfli/{blocksplitter.h → src/zopfli/blocksplitter.h} +0 -0
  10. data/vendor/zopfli/{cache.c → src/zopfli/cache.c} +0 -0
  11. data/vendor/zopfli/{cache.h → src/zopfli/cache.h} +0 -0
  12. data/vendor/zopfli/{deflate.c → src/zopfli/deflate.c} +4 -3
  13. data/vendor/zopfli/{deflate.h → src/zopfli/deflate.h} +9 -0
  14. data/vendor/zopfli/{gzip_container.c → src/zopfli/gzip_container.c} +0 -0
  15. data/vendor/zopfli/{gzip_container.h → src/zopfli/gzip_container.h} +8 -0
  16. data/vendor/zopfli/{hash.c → src/zopfli/hash.c} +0 -0
  17. data/vendor/zopfli/{hash.h → src/zopfli/hash.h} +0 -0
  18. data/vendor/zopfli/{katajainen.c → src/zopfli/katajainen.c} +0 -0
  19. data/vendor/zopfli/{katajainen.h → src/zopfli/katajainen.h} +0 -0
  20. data/vendor/zopfli/{lz77.c → src/zopfli/lz77.c} +0 -0
  21. data/vendor/zopfli/{lz77.h → src/zopfli/lz77.h} +0 -0
  22. data/vendor/zopfli/{squeeze.c → src/zopfli/squeeze.c} +0 -0
  23. data/vendor/zopfli/{squeeze.h → src/zopfli/squeeze.h} +0 -0
  24. data/vendor/zopfli/{tree.c → src/zopfli/tree.c} +0 -0
  25. data/vendor/zopfli/{tree.h → src/zopfli/tree.h} +0 -0
  26. data/vendor/zopfli/{util.c → src/zopfli/util.c} +0 -0
  27. data/vendor/zopfli/{util.h → src/zopfli/util.h} +0 -0
  28. data/vendor/zopfli/{zlib_container.c → src/zopfli/zlib_container.c} +0 -0
  29. data/vendor/zopfli/{zlib_container.h → src/zopfli/zlib_container.h} +8 -0
  30. data/vendor/zopfli/{zopfli.h → src/zopfli/zopfli.h} +9 -0
  31. data/vendor/zopfli/{zopfli_bin.c → src/zopfli/zopfli_bin.c} +0 -0
  32. data/vendor/zopfli/{zopfli_lib.c → src/zopfli/zopfli_lib.c} +1 -2
  33. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.cpp +6253 -0
  34. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.h +1705 -0
  35. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.cpp +656 -0
  36. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.h +151 -0
  37. data/vendor/zopfli/src/zopflipng/zopflipng_bin.cc +407 -0
  38. data/vendor/zopfli/src/zopflipng/zopflipng_lib.cc +376 -0
  39. data/vendor/zopfli/src/zopflipng/zopflipng_lib.h +79 -0
  40. metadata +42 -34
@@ -0,0 +1,376 @@
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
+ // See zopflipng_lib.h
19
+
20
+ #include "zopflipng_lib.h"
21
+
22
+ #include <stdio.h>
23
+ #include <vector>
24
+
25
+ #include "lodepng/lodepng.h"
26
+ #include "lodepng/lodepng_util.h"
27
+ #include "../zopfli/deflate.h"
28
+
29
+ ZopfliPNGOptions::ZopfliPNGOptions()
30
+ : lossy_transparent(false)
31
+ , lossy_8bit(false)
32
+ , auto_filter_strategy(true)
33
+ , use_zopfli(true)
34
+ , num_iterations(15)
35
+ , num_iterations_large(5)
36
+ , block_split_strategy(1) {
37
+ }
38
+
39
+ // Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
40
+ // as its compression backend.
41
+ unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize,
42
+ const unsigned char* in, size_t insize,
43
+ const LodePNGCompressSettings* settings) {
44
+ const ZopfliPNGOptions* png_options =
45
+ static_cast<const ZopfliPNGOptions*>(settings->custom_context);
46
+ unsigned char bp = 0;
47
+ ZopfliOptions options;
48
+ ZopfliInitOptions(&options);
49
+
50
+ options.numiterations = insize < 200000
51
+ ? png_options->num_iterations : png_options->num_iterations_large;
52
+
53
+ if (png_options->block_split_strategy == 3) {
54
+ // Try both block splitting first and last.
55
+ unsigned char* out2 = 0;
56
+ size_t outsize2 = 0;
57
+ options.blocksplittinglast = 0;
58
+ ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);
59
+ bp = 0;
60
+ options.blocksplittinglast = 1;
61
+ ZopfliDeflate(&options, 2 /* Dynamic */, 1,
62
+ in, insize, &bp, &out2, &outsize2);
63
+
64
+ if (outsize2 < *outsize) {
65
+ free(*out);
66
+ *out = out2;
67
+ *outsize = outsize2;
68
+ printf("Block splitting last was better\n");
69
+ } else {
70
+ free(out2);
71
+ }
72
+ } else {
73
+ if (png_options->block_split_strategy == 0) options.blocksplitting = 0;
74
+ options.blocksplittinglast = png_options->block_split_strategy == 2;
75
+ ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);
76
+ }
77
+
78
+ return 0; // OK
79
+ }
80
+
81
+ // Remove RGB information from pixels with alpha=0
82
+ void LossyOptimizeTransparent(unsigned char* image, unsigned w, unsigned h) {
83
+ // First check if we want to preserve potential color-key background color,
84
+ // or instead use the last encountered RGB value all the time to save bytes.
85
+ bool key = true;
86
+ for (size_t i = 0; i < w * h; i++) {
87
+ if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
88
+ key = false;
89
+ break;
90
+ }
91
+ }
92
+
93
+ // Choose the color key if color keying is used.
94
+ int r = 0, g = 0, b = 0;
95
+ if (key) {
96
+ for (size_t i = 0; i < w * h; i++) {
97
+ if (image[i * 4 + 3] == 0) {
98
+ // Use first encountered transparent pixel as the color key
99
+ r = image[i * 4 + 0];
100
+ g = image[i * 4 + 1];
101
+ b = image[i * 4 + 2];
102
+ }
103
+ }
104
+ }
105
+
106
+ for (size_t i = 0; i < w * h; i++) {
107
+ // if alpha is 0
108
+ if (image[i * 4 + 3] == 0) {
109
+ image[i * 4 + 0] = r;
110
+ image[i * 4 + 1] = g;
111
+ image[i * 4 + 2] = b;
112
+ } else {
113
+ if (!key) {
114
+ // Use the last encountered RGB value if no color keying is used.
115
+ r = image[i * 4 + 0];
116
+ g = image[i * 4 + 1];
117
+ b = image[i * 4 + 2];
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ // Tries to optimize given a single PNG filter strategy.
124
+ // Returns 0 if ok, other value for error
125
+ unsigned TryOptimize(
126
+ const std::vector<unsigned char>& image, unsigned w, unsigned h,
127
+ const lodepng::State& inputstate, bool bit16,
128
+ const std::vector<unsigned char>& origfile,
129
+ ZopfliPNGFilterStrategy filterstrategy,
130
+ bool use_zopfli, int windowsize, const ZopfliPNGOptions* png_options,
131
+ std::vector<unsigned char>* out) {
132
+ unsigned error = 0;
133
+
134
+ lodepng::State state;
135
+ state.encoder.zlibsettings.windowsize = windowsize;
136
+ if (use_zopfli && png_options->use_zopfli) {
137
+ ZopfliPNGOptions custom_context = *png_options;
138
+ state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate;
139
+ state.encoder.zlibsettings.custom_context = &custom_context;
140
+ }
141
+
142
+ if (inputstate.info_png.color.colortype == LCT_PALETTE) {
143
+ // Make it preserve the original palette order
144
+ lodepng_color_mode_copy(&state.info_raw, &inputstate.info_png.color);
145
+ state.info_raw.colortype = LCT_RGBA;
146
+ state.info_raw.bitdepth = 8;
147
+ }
148
+ if (bit16) {
149
+ state.info_raw.bitdepth = 16;
150
+ }
151
+
152
+ state.encoder.filter_palette_zero = 0;
153
+
154
+ std::vector<unsigned char> filters;
155
+ switch (filterstrategy) {
156
+ case kStrategyZero:
157
+ state.encoder.filter_strategy = LFS_ZERO;
158
+ break;
159
+ case kStrategyMinSum:
160
+ state.encoder.filter_strategy = LFS_MINSUM;
161
+ break;
162
+ case kStrategyEntropy:
163
+ state.encoder.filter_strategy = LFS_ENTROPY;
164
+ break;
165
+ case kStrategyBruteForce:
166
+ state.encoder.filter_strategy = LFS_BRUTE_FORCE;
167
+ break;
168
+ case kStrategyOne:
169
+ case kStrategyTwo:
170
+ case kStrategyThree:
171
+ case kStrategyFour:
172
+ // Set the filters of all scanlines to that number.
173
+ filters.resize(h, filterstrategy);
174
+ state.encoder.filter_strategy = LFS_PREDEFINED;
175
+ state.encoder.predefined_filters = &filters[0];
176
+ break;
177
+ case kStrategyPredefined:
178
+ lodepng::getFilterTypes(filters, origfile);
179
+ state.encoder.filter_strategy = LFS_PREDEFINED;
180
+ state.encoder.predefined_filters = &filters[0];
181
+ break;
182
+ default:
183
+ break;
184
+ }
185
+
186
+ state.encoder.add_id = false;
187
+ state.encoder.text_compression = 1;
188
+
189
+ error = lodepng::encode(*out, image, w, h, state);
190
+
191
+ // For very small output, also try without palette, it may be smaller thanks
192
+ // to no palette storage overhead.
193
+ if (!error && out->size() < 4096) {
194
+ lodepng::State teststate;
195
+ std::vector<unsigned char> temp;
196
+ lodepng::decode(temp, w, h, teststate, *out);
197
+ LodePNGColorMode& color = teststate.info_png.color;
198
+ if (color.colortype == LCT_PALETTE) {
199
+ std::vector<unsigned char> out2;
200
+ state.encoder.auto_convert = LAC_ALPHA;
201
+ bool grey = true;
202
+ for (size_t i = 0; i < color.palettesize; i++) {
203
+ if (color.palette[i * 4 + 0] != color.palette[i * 4 + 2]
204
+ || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) {
205
+ grey = false;
206
+ break;
207
+ }
208
+ }
209
+ if (grey) state.info_png.color.colortype = LCT_GREY_ALPHA;
210
+
211
+ error = lodepng::encode(out2, image, w, h, state);
212
+ if (out2.size() < out->size()) out->swap(out2);
213
+ }
214
+ }
215
+
216
+ if (error) {
217
+ printf("Encoding error %i: %s\n", error, lodepng_error_text(error));
218
+ return error;
219
+ }
220
+
221
+ return 0;
222
+ }
223
+
224
+ // Use fast compression to check which PNG filter strategy gives the smallest
225
+ // output. This allows to then do the slow and good compression only on that
226
+ // filter type.
227
+ unsigned AutoChooseFilterStrategy(const std::vector<unsigned char>& image,
228
+ unsigned w, unsigned h,
229
+ const lodepng::State& inputstate, bool bit16,
230
+ const std::vector<unsigned char>& origfile,
231
+ int numstrategies,
232
+ ZopfliPNGFilterStrategy* strategies,
233
+ bool* enable) {
234
+ std::vector<unsigned char> out;
235
+ size_t bestsize = 0;
236
+ int bestfilter = 0;
237
+
238
+ // A large window size should still be used to do the quick compression to
239
+ // try out filter strategies: which filter strategy is the best depends
240
+ // largely on the window size, the closer to the actual used window size the
241
+ // better.
242
+ int windowsize = 8192;
243
+
244
+ for (int i = 0; i < numstrategies; i++) {
245
+ out.clear();
246
+ unsigned error = TryOptimize(image, w, h, inputstate, bit16, origfile,
247
+ strategies[i], false, windowsize, 0, &out);
248
+ if (error) return error;
249
+ if (bestsize == 0 || out.size() < bestsize) {
250
+ bestsize = out.size();
251
+ bestfilter = i;
252
+ }
253
+ }
254
+
255
+ for (int i = 0; i < numstrategies; i++) {
256
+ enable[i] = (i == bestfilter);
257
+ }
258
+
259
+ return 0; /* OK */
260
+ }
261
+
262
+ // Keeps chunks with given names from the original png by literally copying them
263
+ // into the new png
264
+ void KeepChunks(const std::vector<unsigned char>& origpng,
265
+ const std::vector<std::string>& keepnames,
266
+ std::vector<unsigned char>* png) {
267
+ std::vector<std::string> names[3];
268
+ std::vector<std::vector<unsigned char> > chunks[3];
269
+
270
+ lodepng::getChunks(names, chunks, origpng);
271
+ std::vector<std::vector<unsigned char> > keepchunks[3];
272
+
273
+ // There are 3 distinct locations in a PNG file for chunks: between IHDR and
274
+ // PLTE, between PLTE and IDAT, and between IDAT and IEND. Keep each chunk at
275
+ // its corresponding location in the new PNG.
276
+ for (size_t i = 0; i < 3; i++) {
277
+ for (size_t j = 0; j < names[i].size(); j++) {
278
+ for (size_t k = 0; k < keepnames.size(); k++) {
279
+ if (keepnames[k] == names[i][j]) {
280
+ keepchunks[i].push_back(chunks[i][j]);
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ lodepng::insertChunks(*png, keepchunks);
287
+ }
288
+
289
+ int ZopfliPNGOptimize(const std::vector<unsigned char>& origpng,
290
+ const ZopfliPNGOptions& png_options,
291
+ bool verbose,
292
+ std::vector<unsigned char>* resultpng) {
293
+ // Use the largest possible deflate window size
294
+ int windowsize = 32768;
295
+
296
+ ZopfliPNGFilterStrategy filterstrategies[kNumFilterStrategies] = {
297
+ kStrategyZero, kStrategyOne, kStrategyTwo, kStrategyThree, kStrategyFour,
298
+ kStrategyMinSum, kStrategyEntropy, kStrategyPredefined, kStrategyBruteForce
299
+ };
300
+ bool strategy_enable[kNumFilterStrategies] = {
301
+ false, false, false, false, false, false, false, false, false
302
+ };
303
+ std::string strategy_name[kNumFilterStrategies] = {
304
+ "zero", "one", "two", "three", "four",
305
+ "minimum sum", "entropy", "predefined", "brute force"
306
+ };
307
+ for (size_t i = 0; i < png_options.filter_strategies.size(); i++) {
308
+ strategy_enable[png_options.filter_strategies[i]] = true;
309
+ }
310
+
311
+
312
+ std::vector<unsigned char> image;
313
+ unsigned w, h;
314
+ unsigned error;
315
+ lodepng::State inputstate;
316
+ error = lodepng::decode(image, w, h, inputstate, origpng);
317
+
318
+ if (error) {
319
+ if (verbose) {
320
+ printf("Decoding error %i: %s\n", error, lodepng_error_text(error));
321
+ }
322
+ return error;
323
+ }
324
+
325
+ bool bit16 = false; // Using 16-bit per channel raw image
326
+ if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) {
327
+ // Decode as 16-bit
328
+ image.clear();
329
+ error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
330
+ bit16 = true;
331
+ }
332
+
333
+ if (!error) {
334
+ // If lossy_transparent, remove RGB information from pixels with alpha=0
335
+ if (png_options.lossy_transparent && !bit16) {
336
+ LossyOptimizeTransparent(&image[0], w, h);
337
+ }
338
+
339
+ if (png_options.auto_filter_strategy) {
340
+ error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16,
341
+ origpng,
342
+ /* Don't try brute force */
343
+ kNumFilterStrategies - 1,
344
+ filterstrategies, strategy_enable);
345
+ }
346
+ }
347
+
348
+ if (!error) {
349
+ size_t bestsize = 0;
350
+
351
+ for (int i = 0; i < kNumFilterStrategies; i++) {
352
+ if (!strategy_enable[i]) continue;
353
+
354
+ std::vector<unsigned char> temp;
355
+ error = TryOptimize(image, w, h, inputstate, bit16, origpng,
356
+ filterstrategies[i], true /* use_zopfli */,
357
+ windowsize, &png_options, &temp);
358
+ if (!error) {
359
+ if (verbose) {
360
+ printf("Filter strategy %s: %d bytes\n",
361
+ strategy_name[i].c_str(), (int) temp.size());
362
+ }
363
+ if (bestsize == 0 || temp.size() < bestsize) {
364
+ bestsize = temp.size();
365
+ (*resultpng).swap(temp); // Store best result so far in the output.
366
+ }
367
+ }
368
+ }
369
+
370
+ if (!png_options.keepchunks.empty()) {
371
+ KeepChunks(origpng, png_options.keepchunks, resultpng);
372
+ }
373
+ }
374
+
375
+ return error;
376
+ }
@@ -0,0 +1,79 @@
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
+ // Library to recompress and optimize PNG images. Uses Zopfli as the compression
19
+ // backend, chooses optimal PNG color model, and tries out several PNG filter
20
+ // strategies.
21
+
22
+ #ifndef UTIL_COMPRESSION_ZOPFLI_PNG_ZOPFLIPNG_LIB_H_
23
+ #define UTIL_COMPRESSION_ZOPFLI_PNG_ZOPFLIPNG_LIB_H_
24
+
25
+ #include <string>
26
+ #include <vector>
27
+
28
+ enum ZopfliPNGFilterStrategy {
29
+ kStrategyZero = 0,
30
+ kStrategyOne = 1,
31
+ kStrategyTwo = 2,
32
+ kStrategyThree = 3,
33
+ kStrategyFour = 4,
34
+ kStrategyMinSum,
35
+ kStrategyEntropy,
36
+ kStrategyPredefined,
37
+ kStrategyBruteForce,
38
+ kNumFilterStrategies /* Not a strategy but used for the size of this enum */
39
+ };
40
+
41
+ struct ZopfliPNGOptions {
42
+ ZopfliPNGOptions();
43
+
44
+ // Allow altering hidden colors of fully transparent pixels
45
+ bool lossy_transparent;
46
+ // Convert 16-bit per channel images to 8-bit per channel
47
+ bool lossy_8bit;
48
+
49
+ // Filter strategies to try
50
+ std::vector<ZopfliPNGFilterStrategy> filter_strategies;
51
+
52
+ // Automatically choose filter strategy using less good compression
53
+ bool auto_filter_strategy;
54
+
55
+ // PNG chunks to keep
56
+ // chunks to literally copy over from the original PNG to the resulting one
57
+ std::vector<std::string> keepchunks;
58
+
59
+ // Use Zopfli deflate compression
60
+ bool use_zopfli;
61
+
62
+ // Zopfli number of iterations
63
+ int num_iterations;
64
+
65
+ // Zopfli number of iterations on large images
66
+ int num_iterations_large;
67
+
68
+ // 0=none, 1=first, 2=last, 3=both
69
+ int block_split_strategy;
70
+ };
71
+
72
+ // Returns 0 on success, error code otherwise.
73
+ // If verbose is true, it will print some info while working.
74
+ int ZopfliPNGOptimize(const std::vector<unsigned char>& origpng,
75
+ const ZopfliPNGOptions& png_options,
76
+ bool verbose,
77
+ std::vector<unsigned char>* resultpng);
78
+
79
+ #endif // UTIL_COMPRESSION_ZOPFLI_PNG_ZOPFLIPNG_LIB_H_