zopfli 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- }