zopfli 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +1 -1
  3. data/.travis.yml +24 -0
  4. data/Gemfile +2 -0
  5. data/Rakefile +8 -10
  6. data/ext/extconf.rb +1 -0
  7. data/ext/zopfli.c +37 -17
  8. data/lib/zopfli/version.rb +1 -1
  9. data/smoke.sh +9 -0
  10. data/{test → spec}/fixtures/alice29.txt +0 -0
  11. data/spec/spec_helper.rb +2 -0
  12. data/spec/zopfli_spec.rb +68 -0
  13. data/vendor/zopfli/src/zopfli/blocksplitter.c +34 -44
  14. data/vendor/zopfli/src/zopfli/blocksplitter.h +2 -6
  15. data/vendor/zopfli/src/zopfli/cache.c +3 -1
  16. data/vendor/zopfli/src/zopfli/deflate.c +351 -287
  17. data/vendor/zopfli/src/zopfli/deflate.h +8 -2
  18. data/vendor/zopfli/src/zopfli/gzip_container.c +54 -47
  19. data/vendor/zopfli/src/zopfli/hash.c +18 -10
  20. data/vendor/zopfli/src/zopfli/hash.h +6 -3
  21. data/vendor/zopfli/src/zopfli/katajainen.c +73 -62
  22. data/vendor/zopfli/src/zopfli/lz77.c +190 -42
  23. data/vendor/zopfli/src/zopfli/lz77.h +32 -19
  24. data/vendor/zopfli/src/zopfli/squeeze.c +75 -61
  25. data/vendor/zopfli/src/zopfli/squeeze.h +1 -0
  26. data/vendor/zopfli/src/zopfli/symbols.h +239 -0
  27. data/vendor/zopfli/src/zopfli/util.c +0 -178
  28. data/vendor/zopfli/src/zopfli/util.h +6 -23
  29. data/vendor/zopfli/src/zopfli/zlib_container.c +1 -1
  30. data/vendor/zopfli/src/zopfli/zopfli.h +1 -4
  31. data/vendor/zopfli/src/zopfli/zopfli_bin.c +11 -8
  32. data/zopfli.gemspec +8 -27
  33. metadata +13 -20
  34. data/test/test_zopfli_deflate.rb +0 -45
  35. data/vendor/zopfli/CONTRIBUTING.md +0 -24
  36. data/vendor/zopfli/CONTRIBUTORS +0 -8
  37. data/vendor/zopfli/Makefile +0 -42
  38. data/vendor/zopfli/README +0 -32
  39. data/vendor/zopfli/README.zopflipng +0 -35
  40. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.cpp +0 -6252
  41. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.h +0 -1716
  42. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.cpp +0 -656
  43. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.h +0 -151
  44. data/vendor/zopfli/src/zopflipng/zopflipng_bin.cc +0 -408
  45. data/vendor/zopfli/src/zopflipng/zopflipng_lib.cc +0 -492
  46. data/vendor/zopfli/src/zopflipng/zopflipng_lib.h +0 -134
@@ -1,492 +0,0 @@
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 <errno.h>
23
- #include <stdio.h>
24
- #include <stdlib.h>
25
- #include <string.h>
26
- #include <set>
27
- #include <vector>
28
-
29
- #include "lodepng/lodepng.h"
30
- #include "lodepng/lodepng_util.h"
31
- #include "../zopfli/deflate.h"
32
-
33
- ZopfliPNGOptions::ZopfliPNGOptions()
34
- : lossy_transparent(false)
35
- , verbose(false)
36
- , lossy_8bit(false)
37
- , auto_filter_strategy(true)
38
- , use_zopfli(true)
39
- , num_iterations(15)
40
- , num_iterations_large(5)
41
- , block_split_strategy(1) {
42
- }
43
-
44
- // Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
45
- // as its compression backend.
46
- unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize,
47
- const unsigned char* in, size_t insize,
48
- const LodePNGCompressSettings* settings) {
49
- const ZopfliPNGOptions* png_options =
50
- static_cast<const ZopfliPNGOptions*>(settings->custom_context);
51
- unsigned char bp = 0;
52
- ZopfliOptions options;
53
- ZopfliInitOptions(&options);
54
-
55
- options.verbose = png_options->verbose;
56
- options.numiterations = insize < 200000
57
- ? png_options->num_iterations : png_options->num_iterations_large;
58
-
59
- if (png_options->block_split_strategy == 3) {
60
- // Try both block splitting first and last.
61
- unsigned char* out2 = 0;
62
- size_t outsize2 = 0;
63
- options.blocksplittinglast = 0;
64
- ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);
65
- bp = 0;
66
- options.blocksplittinglast = 1;
67
- ZopfliDeflate(&options, 2 /* Dynamic */, 1,
68
- in, insize, &bp, &out2, &outsize2);
69
-
70
- if (outsize2 < *outsize) {
71
- free(*out);
72
- *out = out2;
73
- *outsize = outsize2;
74
- printf("Block splitting last was better\n");
75
- } else {
76
- free(out2);
77
- }
78
- } else {
79
- if (png_options->block_split_strategy == 0) options.blocksplitting = 0;
80
- options.blocksplittinglast = png_options->block_split_strategy == 2;
81
- ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);
82
- }
83
-
84
- return 0; // OK
85
- }
86
-
87
- // Returns 32-bit integer value for RGBA color.
88
- static unsigned ColorIndex(const unsigned char* color) {
89
- return color[0] + 256u * color[1] + 65536u * color[2] + 16777216u * color[3];
90
- }
91
-
92
- // Counts amount of colors in the image, up to 257. If transparent_counts_as_one
93
- // is enabled, any color with alpha channel 0 is treated as a single color with
94
- // index 0.
95
- void CountColors(std::set<unsigned>* unique,
96
- const unsigned char* image, unsigned w, unsigned h,
97
- bool transparent_counts_as_one) {
98
- unique->clear();
99
- for (size_t i = 0; i < w * h; i++) {
100
- unsigned index = ColorIndex(&image[i * 4]);
101
- if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0;
102
- unique->insert(index);
103
- if (unique->size() > 256) break;
104
- }
105
- }
106
-
107
- // Remove RGB information from pixels with alpha=0
108
- void LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image,
109
- unsigned w, unsigned h) {
110
- // First check if we want to preserve potential color-key background color,
111
- // or instead use the last encountered RGB value all the time to save bytes.
112
- bool key = true;
113
- for (size_t i = 0; i < w * h; i++) {
114
- if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
115
- key = false;
116
- break;
117
- }
118
- }
119
- std::set<unsigned> count; // Color count, up to 257.
120
- CountColors(&count, image, w, h, true);
121
- // If true, means palette is possible so avoid using different RGB values for
122
- // the transparent color.
123
- bool palette = count.size() <= 256;
124
-
125
- // Choose the color key or first initial background color.
126
- int r = 0, g = 0, b = 0;
127
- if (key || palette) {
128
- for (size_t i = 0; i < w * h; i++) {
129
- if (image[i * 4 + 3] == 0) {
130
- // Use RGB value of first encountered transparent pixel. This can be
131
- // used as a valid color key, or in case of palette ensures a color
132
- // existing in the input image palette is used.
133
- r = image[i * 4 + 0];
134
- g = image[i * 4 + 1];
135
- b = image[i * 4 + 2];
136
- break;
137
- }
138
- }
139
- }
140
-
141
- for (size_t i = 0; i < w * h; i++) {
142
- // if alpha is 0, alter the RGB value to a possibly more efficient one.
143
- if (image[i * 4 + 3] == 0) {
144
- image[i * 4 + 0] = r;
145
- image[i * 4 + 1] = g;
146
- image[i * 4 + 2] = b;
147
- } else {
148
- if (!key && !palette) {
149
- // Use the last encountered RGB value if no key or palette is used: that
150
- // way more values can be 0 thanks to the PNG filter types.
151
- r = image[i * 4 + 0];
152
- g = image[i * 4 + 1];
153
- b = image[i * 4 + 2];
154
- }
155
- }
156
- }
157
-
158
- // If there are now less colors, update palette of input image to match this.
159
- if (palette && inputstate->info_png.color.palettesize > 0) {
160
- CountColors(&count, image, w, h, false);
161
- if (count.size() < inputstate->info_png.color.palettesize) {
162
- std::vector<unsigned char> palette_out;
163
- unsigned char* palette_in = inputstate->info_png.color.palette;
164
- for (size_t i = 0; i < inputstate->info_png.color.palettesize; i++) {
165
- if (count.count(ColorIndex(&palette_in[i * 4])) != 0) {
166
- palette_out.push_back(palette_in[i * 4 + 0]);
167
- palette_out.push_back(palette_in[i * 4 + 1]);
168
- palette_out.push_back(palette_in[i * 4 + 2]);
169
- palette_out.push_back(palette_in[i * 4 + 3]);
170
- }
171
- }
172
- inputstate->info_png.color.palettesize = palette_out.size() / 4;
173
- for (size_t i = 0; i < palette_out.size(); i++) {
174
- palette_in[i] = palette_out[i];
175
- }
176
- }
177
- }
178
- }
179
-
180
- // Tries to optimize given a single PNG filter strategy.
181
- // Returns 0 if ok, other value for error
182
- unsigned TryOptimize(
183
- const std::vector<unsigned char>& image, unsigned w, unsigned h,
184
- const lodepng::State& inputstate, bool bit16,
185
- const std::vector<unsigned char>& origfile,
186
- ZopfliPNGFilterStrategy filterstrategy,
187
- bool use_zopfli, int windowsize, const ZopfliPNGOptions* png_options,
188
- std::vector<unsigned char>* out) {
189
- unsigned error = 0;
190
-
191
- lodepng::State state;
192
- state.encoder.zlibsettings.windowsize = windowsize;
193
- if (use_zopfli && png_options->use_zopfli) {
194
- state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate;
195
- state.encoder.zlibsettings.custom_context = png_options;
196
- }
197
-
198
- if (inputstate.info_png.color.colortype == LCT_PALETTE) {
199
- // Make it preserve the original palette order
200
- lodepng_color_mode_copy(&state.info_raw, &inputstate.info_png.color);
201
- state.info_raw.colortype = LCT_RGBA;
202
- state.info_raw.bitdepth = 8;
203
- }
204
- if (bit16) {
205
- state.info_raw.bitdepth = 16;
206
- }
207
-
208
- state.encoder.filter_palette_zero = 0;
209
-
210
- std::vector<unsigned char> filters;
211
- switch (filterstrategy) {
212
- case kStrategyZero:
213
- state.encoder.filter_strategy = LFS_ZERO;
214
- break;
215
- case kStrategyMinSum:
216
- state.encoder.filter_strategy = LFS_MINSUM;
217
- break;
218
- case kStrategyEntropy:
219
- state.encoder.filter_strategy = LFS_ENTROPY;
220
- break;
221
- case kStrategyBruteForce:
222
- state.encoder.filter_strategy = LFS_BRUTE_FORCE;
223
- break;
224
- case kStrategyOne:
225
- case kStrategyTwo:
226
- case kStrategyThree:
227
- case kStrategyFour:
228
- // Set the filters of all scanlines to that number.
229
- filters.resize(h, filterstrategy);
230
- state.encoder.filter_strategy = LFS_PREDEFINED;
231
- state.encoder.predefined_filters = &filters[0];
232
- break;
233
- case kStrategyPredefined:
234
- lodepng::getFilterTypes(filters, origfile);
235
- state.encoder.filter_strategy = LFS_PREDEFINED;
236
- state.encoder.predefined_filters = &filters[0];
237
- break;
238
- default:
239
- break;
240
- }
241
-
242
- state.encoder.add_id = false;
243
- state.encoder.text_compression = 1;
244
-
245
- error = lodepng::encode(*out, image, w, h, state);
246
-
247
- // For very small output, also try without palette, it may be smaller thanks
248
- // to no palette storage overhead.
249
- if (!error && out->size() < 4096) {
250
- lodepng::State teststate;
251
- std::vector<unsigned char> temp;
252
- lodepng::decode(temp, w, h, teststate, *out);
253
- LodePNGColorMode& color = teststate.info_png.color;
254
- if (color.colortype == LCT_PALETTE) {
255
- std::vector<unsigned char> out2;
256
- state.encoder.auto_convert = LAC_ALPHA;
257
- bool grey = true;
258
- for (size_t i = 0; i < color.palettesize; i++) {
259
- if (color.palette[i * 4 + 0] != color.palette[i * 4 + 2]
260
- || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) {
261
- grey = false;
262
- break;
263
- }
264
- }
265
- if (grey) state.info_png.color.colortype = LCT_GREY_ALPHA;
266
-
267
- error = lodepng::encode(out2, image, w, h, state);
268
- if (out2.size() < out->size()) out->swap(out2);
269
- }
270
- }
271
-
272
- if (error) {
273
- printf("Encoding error %u: %s\n", error, lodepng_error_text(error));
274
- return error;
275
- }
276
-
277
- return 0;
278
- }
279
-
280
- // Use fast compression to check which PNG filter strategy gives the smallest
281
- // output. This allows to then do the slow and good compression only on that
282
- // filter type.
283
- unsigned AutoChooseFilterStrategy(const std::vector<unsigned char>& image,
284
- unsigned w, unsigned h,
285
- const lodepng::State& inputstate, bool bit16,
286
- const std::vector<unsigned char>& origfile,
287
- int numstrategies,
288
- ZopfliPNGFilterStrategy* strategies,
289
- bool* enable) {
290
- std::vector<unsigned char> out;
291
- size_t bestsize = 0;
292
- int bestfilter = 0;
293
-
294
- // A large window size should still be used to do the quick compression to
295
- // try out filter strategies: which filter strategy is the best depends
296
- // largely on the window size, the closer to the actual used window size the
297
- // better.
298
- int windowsize = 8192;
299
-
300
- for (int i = 0; i < numstrategies; i++) {
301
- out.clear();
302
- unsigned error = TryOptimize(image, w, h, inputstate, bit16, origfile,
303
- strategies[i], false, windowsize, 0, &out);
304
- if (error) return error;
305
- if (bestsize == 0 || out.size() < bestsize) {
306
- bestsize = out.size();
307
- bestfilter = i;
308
- }
309
- }
310
-
311
- for (int i = 0; i < numstrategies; i++) {
312
- enable[i] = (i == bestfilter);
313
- }
314
-
315
- return 0; /* OK */
316
- }
317
-
318
- // Keeps chunks with given names from the original png by literally copying them
319
- // into the new png
320
- void KeepChunks(const std::vector<unsigned char>& origpng,
321
- const std::vector<std::string>& keepnames,
322
- std::vector<unsigned char>* png) {
323
- std::vector<std::string> names[3];
324
- std::vector<std::vector<unsigned char> > chunks[3];
325
-
326
- lodepng::getChunks(names, chunks, origpng);
327
- std::vector<std::vector<unsigned char> > keepchunks[3];
328
-
329
- // There are 3 distinct locations in a PNG file for chunks: between IHDR and
330
- // PLTE, between PLTE and IDAT, and between IDAT and IEND. Keep each chunk at
331
- // its corresponding location in the new PNG.
332
- for (size_t i = 0; i < 3; i++) {
333
- for (size_t j = 0; j < names[i].size(); j++) {
334
- for (size_t k = 0; k < keepnames.size(); k++) {
335
- if (keepnames[k] == names[i][j]) {
336
- keepchunks[i].push_back(chunks[i][j]);
337
- }
338
- }
339
- }
340
- }
341
-
342
- lodepng::insertChunks(*png, keepchunks);
343
- }
344
-
345
- int ZopfliPNGOptimize(const std::vector<unsigned char>& origpng,
346
- const ZopfliPNGOptions& png_options,
347
- bool verbose,
348
- std::vector<unsigned char>* resultpng) {
349
- // Use the largest possible deflate window size
350
- int windowsize = 32768;
351
-
352
- ZopfliPNGFilterStrategy filterstrategies[kNumFilterStrategies] = {
353
- kStrategyZero, kStrategyOne, kStrategyTwo, kStrategyThree, kStrategyFour,
354
- kStrategyMinSum, kStrategyEntropy, kStrategyPredefined, kStrategyBruteForce
355
- };
356
- bool strategy_enable[kNumFilterStrategies] = {
357
- false, false, false, false, false, false, false, false, false
358
- };
359
- std::string strategy_name[kNumFilterStrategies] = {
360
- "zero", "one", "two", "three", "four",
361
- "minimum sum", "entropy", "predefined", "brute force"
362
- };
363
- for (size_t i = 0; i < png_options.filter_strategies.size(); i++) {
364
- strategy_enable[png_options.filter_strategies[i]] = true;
365
- }
366
-
367
- std::vector<unsigned char> image;
368
- unsigned w, h;
369
- unsigned error;
370
- lodepng::State inputstate;
371
- error = lodepng::decode(image, w, h, inputstate, origpng);
372
-
373
- if (error) {
374
- if (verbose) {
375
- printf("Decoding error %u: %s\n", error, lodepng_error_text(error));
376
- }
377
- return error;
378
- }
379
-
380
- bool bit16 = false; // Using 16-bit per channel raw image
381
- if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) {
382
- // Decode as 16-bit
383
- image.clear();
384
- error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
385
- bit16 = true;
386
- }
387
-
388
- if (!error) {
389
- // If lossy_transparent, remove RGB information from pixels with alpha=0
390
- if (png_options.lossy_transparent && !bit16) {
391
- LossyOptimizeTransparent(&inputstate, &image[0], w, h);
392
- }
393
-
394
- if (png_options.auto_filter_strategy) {
395
- error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16,
396
- origpng,
397
- /* Don't try brute force */
398
- kNumFilterStrategies - 1,
399
- filterstrategies, strategy_enable);
400
- }
401
- }
402
-
403
- if (!error) {
404
- size_t bestsize = 0;
405
-
406
- for (int i = 0; i < kNumFilterStrategies; i++) {
407
- if (!strategy_enable[i]) continue;
408
-
409
- std::vector<unsigned char> temp;
410
- error = TryOptimize(image, w, h, inputstate, bit16, origpng,
411
- filterstrategies[i], true /* use_zopfli */,
412
- windowsize, &png_options, &temp);
413
- if (!error) {
414
- if (verbose) {
415
- printf("Filter strategy %s: %d bytes\n",
416
- strategy_name[i].c_str(), (int) temp.size());
417
- }
418
- if (bestsize == 0 || temp.size() < bestsize) {
419
- bestsize = temp.size();
420
- (*resultpng).swap(temp); // Store best result so far in the output.
421
- }
422
- }
423
- }
424
-
425
- if (!png_options.keepchunks.empty()) {
426
- KeepChunks(origpng, png_options.keepchunks, resultpng);
427
- }
428
- }
429
-
430
- return error;
431
- }
432
-
433
- extern "C" void CZopfliPNGSetDefaults(CZopfliPNGOptions* png_options) {
434
-
435
- memset(png_options, 0, sizeof(*png_options));
436
- // Constructor sets the defaults
437
- ZopfliPNGOptions opts;
438
-
439
- png_options->lossy_transparent = opts.lossy_transparent;
440
- png_options->lossy_8bit = opts.lossy_8bit;
441
- png_options->auto_filter_strategy = opts.auto_filter_strategy;
442
- png_options->use_zopfli = opts.use_zopfli;
443
- png_options->num_iterations = opts.num_iterations;
444
- png_options->num_iterations_large = opts.num_iterations_large;
445
- png_options->block_split_strategy = opts.block_split_strategy;
446
- }
447
-
448
- extern "C" int CZopfliPNGOptimize(const unsigned char* origpng,
449
- const size_t origpng_size,
450
- const CZopfliPNGOptions* png_options,
451
- int verbose,
452
- unsigned char** resultpng,
453
- size_t* resultpng_size) {
454
- ZopfliPNGOptions opts;
455
-
456
- // Copy over to the C++-style struct
457
- opts.lossy_transparent = !!png_options->lossy_transparent;
458
- opts.lossy_8bit = !!png_options->lossy_8bit;
459
- opts.auto_filter_strategy = !!png_options->auto_filter_strategy;
460
- opts.use_zopfli = !!png_options->use_zopfli;
461
- opts.num_iterations = png_options->num_iterations;
462
- opts.num_iterations_large = png_options->num_iterations_large;
463
- opts.block_split_strategy = png_options->block_split_strategy;
464
-
465
- for (int i = 0; i < png_options->num_filter_strategies; i++) {
466
- opts.filter_strategies.push_back(png_options->filter_strategies[i]);
467
- }
468
-
469
- for (int i = 0; i < png_options->num_keepchunks; i++) {
470
- opts.keepchunks.push_back(png_options->keepchunks[i]);
471
- }
472
-
473
- const std::vector<unsigned char> origpng_cc(origpng, origpng + origpng_size);
474
- std::vector<unsigned char> resultpng_cc;
475
-
476
- int ret = ZopfliPNGOptimize(origpng_cc, opts, !!verbose, &resultpng_cc);
477
- if (ret) {
478
- return ret;
479
- }
480
-
481
- *resultpng_size = resultpng_cc.size();
482
- *resultpng = (unsigned char*) malloc(resultpng_cc.size());
483
- if (!(*resultpng)) {
484
- return ENOMEM;
485
- }
486
-
487
- memcpy(*resultpng,
488
- reinterpret_cast<unsigned char*>(&resultpng_cc[0]),
489
- resultpng_cc.size());
490
-
491
- return 0;
492
- }