zopfli 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/main.yml +35 -0
- data/.github/workflows/publish.yml +34 -0
- data/.gitmodules +1 -1
- data/Gemfile +4 -0
- data/README.md +6 -1
- data/Rakefile +14 -10
- data/ext/extconf.rb +36 -32
- data/ext/zopfli.c +52 -20
- data/lib/zopfli/version.rb +1 -1
- data/smoke.sh +9 -0
- data/test/test_helper.rb +7 -0
- data/test/zopfli_test.rb +63 -0
- data/vendor/zopfli/src/zopfli/blocksplitter.c +41 -53
- data/vendor/zopfli/src/zopfli/blocksplitter.h +2 -6
- data/vendor/zopfli/src/zopfli/cache.c +6 -0
- data/vendor/zopfli/src/zopfli/deflate.c +613 -381
- data/vendor/zopfli/src/zopfli/deflate.h +8 -2
- data/vendor/zopfli/src/zopfli/gzip_container.c +54 -47
- data/vendor/zopfli/src/zopfli/hash.c +18 -10
- data/vendor/zopfli/src/zopfli/hash.h +10 -7
- data/vendor/zopfli/src/zopfli/katajainen.c +73 -62
- data/vendor/zopfli/src/zopfli/katajainen.h +1 -1
- data/vendor/zopfli/src/zopfli/lz77.c +190 -42
- data/vendor/zopfli/src/zopfli/lz77.h +39 -23
- data/vendor/zopfli/src/zopfli/squeeze.c +75 -61
- data/vendor/zopfli/src/zopfli/squeeze.h +1 -0
- data/vendor/zopfli/src/zopfli/symbols.h +239 -0
- data/vendor/zopfli/src/zopfli/util.c +0 -178
- data/vendor/zopfli/src/zopfli/util.h +6 -23
- data/vendor/zopfli/src/zopfli/zlib_container.c +1 -1
- data/vendor/zopfli/src/zopfli/zopfli.h +1 -4
- data/vendor/zopfli/src/zopfli/zopfli_bin.c +31 -15
- data/zopfli.gemspec +12 -32
- metadata +20 -68
- data/test/fixtures/alice29.txt +0 -3609
- data/test/test_zopfli_deflate.rb +0 -47
- data/vendor/zopfli/CONTRIBUTORS +0 -7
- data/vendor/zopfli/README +0 -32
- data/vendor/zopfli/README.zopflipng +0 -35
- data/vendor/zopfli/makefile +0 -37
- data/vendor/zopfli/src/zopflipng/lodepng/lodepng.cpp +0 -6253
- data/vendor/zopfli/src/zopflipng/lodepng/lodepng.h +0 -1705
- data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.cpp +0 -656
- data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.h +0 -151
- data/vendor/zopfli/src/zopflipng/zopflipng_bin.cc +0 -407
- data/vendor/zopfli/src/zopflipng/zopflipng_lib.cc +0 -376
- data/vendor/zopfli/src/zopflipng/zopflipng_lib.h +0 -79
@@ -1,656 +0,0 @@
|
|
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
|
-
#include "lodepng_util.h"
|
27
|
-
#include <iostream>
|
28
|
-
|
29
|
-
namespace lodepng
|
30
|
-
{
|
31
|
-
|
32
|
-
LodePNGInfo getPNGHeaderInfo(const std::vector<unsigned char>& png)
|
33
|
-
{
|
34
|
-
unsigned w, h;
|
35
|
-
lodepng::State state;
|
36
|
-
lodepng_inspect(&w, &h, &state, &png[0], png.size());
|
37
|
-
return state.info_png;
|
38
|
-
}
|
39
|
-
|
40
|
-
unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& sizes,
|
41
|
-
const std::vector<unsigned char>& png)
|
42
|
-
{
|
43
|
-
// Listing chunks is based on the original file, not the decoded png info.
|
44
|
-
const unsigned char *chunk, *begin, *end;
|
45
|
-
end = &png.back() + 1;
|
46
|
-
begin = chunk = &png.front() + 8;
|
47
|
-
|
48
|
-
while(chunk + 8 < end && chunk >= begin)
|
49
|
-
{
|
50
|
-
char type[5];
|
51
|
-
lodepng_chunk_type(type, chunk);
|
52
|
-
if(std::string(type).size() != 4) return 1;
|
53
|
-
|
54
|
-
names.push_back(type);
|
55
|
-
sizes.push_back(lodepng_chunk_length(chunk));
|
56
|
-
|
57
|
-
chunk = lodepng_chunk_next_const(chunk);
|
58
|
-
}
|
59
|
-
return 0;
|
60
|
-
}
|
61
|
-
|
62
|
-
unsigned getChunks(std::vector<std::string> names[3],
|
63
|
-
std::vector<std::vector<unsigned char> > chunks[3],
|
64
|
-
const std::vector<unsigned char>& png)
|
65
|
-
{
|
66
|
-
const unsigned char *chunk, *next, *begin, *end;
|
67
|
-
end = &png.back() + 1;
|
68
|
-
begin = chunk = &png.front() + 8;
|
69
|
-
|
70
|
-
int location = 0;
|
71
|
-
|
72
|
-
while(chunk + 8 < end && chunk >= begin)
|
73
|
-
{
|
74
|
-
char type[5];
|
75
|
-
lodepng_chunk_type(type, chunk);
|
76
|
-
std::string name(type);
|
77
|
-
if(name.size() != 4) return 1;
|
78
|
-
|
79
|
-
next = lodepng_chunk_next_const(chunk);
|
80
|
-
|
81
|
-
if(name == "IHDR")
|
82
|
-
{
|
83
|
-
location = 0;
|
84
|
-
}
|
85
|
-
else if(name == "PLTE")
|
86
|
-
{
|
87
|
-
location = 1;
|
88
|
-
}
|
89
|
-
else if(name == "IDAT")
|
90
|
-
{
|
91
|
-
location = 2;
|
92
|
-
}
|
93
|
-
else if(name != "IEND")
|
94
|
-
{
|
95
|
-
names[location].push_back(name);
|
96
|
-
chunks[location].push_back(std::vector<unsigned char>(chunk, next));
|
97
|
-
}
|
98
|
-
|
99
|
-
chunk = next;
|
100
|
-
}
|
101
|
-
return 0;
|
102
|
-
}
|
103
|
-
|
104
|
-
|
105
|
-
unsigned insertChunks(std::vector<unsigned char>& png,
|
106
|
-
const std::vector<std::vector<unsigned char> > chunks[3])
|
107
|
-
{
|
108
|
-
const unsigned char *chunk, *next, *begin, *end;
|
109
|
-
end = &png.back() + 1;
|
110
|
-
begin = chunk = &png.front() + 8;
|
111
|
-
|
112
|
-
size_t l0 = 0; //location 0: IHDR-l0-PLTE (or IHDR-l0-l1-IDAT)
|
113
|
-
size_t l1 = 0; //location 1: PLTE-l1-IDAT (or IHDR-l0-l1-IDAT)
|
114
|
-
size_t l2 = 0; //location 2: IDAT-l2-IEND
|
115
|
-
|
116
|
-
while(chunk + 8 < end && chunk >= begin)
|
117
|
-
{
|
118
|
-
char type[5];
|
119
|
-
lodepng_chunk_type(type, chunk);
|
120
|
-
std::string name(type);
|
121
|
-
if(name.size() != 4) return 1;
|
122
|
-
|
123
|
-
next = lodepng_chunk_next_const(chunk);
|
124
|
-
|
125
|
-
if(name == "PLTE")
|
126
|
-
{
|
127
|
-
if(l0 == 0) l0 = chunk - begin + 8;
|
128
|
-
}
|
129
|
-
else if(name == "IDAT")
|
130
|
-
{
|
131
|
-
if(l0 == 0) l0 = chunk - begin + 8;
|
132
|
-
if(l1 == 0) l1 = chunk - begin + 8;
|
133
|
-
}
|
134
|
-
else if(name == "IEND")
|
135
|
-
{
|
136
|
-
if(l2 == 0) l2 = chunk - begin + 8;
|
137
|
-
}
|
138
|
-
|
139
|
-
chunk = next;
|
140
|
-
}
|
141
|
-
|
142
|
-
std::vector<unsigned char> result;
|
143
|
-
result.insert(result.end(), png.begin(), png.begin() + l0);
|
144
|
-
for(size_t i = 0; i < chunks[0].size(); i++) result.insert(result.end(), chunks[0][i].begin(), chunks[0][i].end());
|
145
|
-
result.insert(result.end(), png.begin() + l0, png.begin() + l1);
|
146
|
-
for(size_t i = 0; i < chunks[1].size(); i++) result.insert(result.end(), chunks[1][i].begin(), chunks[1][i].end());
|
147
|
-
result.insert(result.end(), png.begin() + l1, png.begin() + l2);
|
148
|
-
for(size_t i = 0; i < chunks[2].size(); i++) result.insert(result.end(), chunks[2][i].begin(), chunks[2][i].end());
|
149
|
-
result.insert(result.end(), png.begin() + l2, png.end());
|
150
|
-
|
151
|
-
png = result;
|
152
|
-
return 0;
|
153
|
-
}
|
154
|
-
|
155
|
-
unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filterTypes,
|
156
|
-
const std::vector<unsigned char>& png)
|
157
|
-
{
|
158
|
-
//Get color type and interlace type
|
159
|
-
lodepng::State state;
|
160
|
-
unsigned w, h;
|
161
|
-
unsigned error;
|
162
|
-
error = lodepng_inspect(&w, &h, &state, &png[0], png.size());
|
163
|
-
|
164
|
-
if(error) return 1;
|
165
|
-
|
166
|
-
//Read literal data from all IDAT chunks
|
167
|
-
const unsigned char *chunk, *begin, *end;
|
168
|
-
end = &png.back() + 1;
|
169
|
-
begin = chunk = &png.front() + 8;
|
170
|
-
|
171
|
-
std::vector<unsigned char> zdata;
|
172
|
-
|
173
|
-
while(chunk + 8 < end && chunk >= begin)
|
174
|
-
{
|
175
|
-
char type[5];
|
176
|
-
lodepng_chunk_type(type, chunk);
|
177
|
-
if(std::string(type).size() != 4) return 1; //Probably not a PNG file
|
178
|
-
|
179
|
-
if(std::string(type) == "IDAT")
|
180
|
-
{
|
181
|
-
const unsigned char* cdata = lodepng_chunk_data_const(chunk);
|
182
|
-
unsigned clength = lodepng_chunk_length(chunk);
|
183
|
-
|
184
|
-
for(unsigned i = 0; i < clength; i++)
|
185
|
-
{
|
186
|
-
zdata.push_back(cdata[i]);
|
187
|
-
}
|
188
|
-
}
|
189
|
-
|
190
|
-
chunk = lodepng_chunk_next_const(chunk);
|
191
|
-
}
|
192
|
-
|
193
|
-
//Decompress all IDAT data
|
194
|
-
std::vector<unsigned char> data;
|
195
|
-
error = lodepng::decompress(data, &zdata[0], zdata.size());
|
196
|
-
|
197
|
-
if(error) return 1;
|
198
|
-
|
199
|
-
if(state.info_png.interlace_method == 0)
|
200
|
-
{
|
201
|
-
filterTypes.resize(1);
|
202
|
-
|
203
|
-
//A line is 1 filter byte + all pixels
|
204
|
-
size_t linebytes = 1 + lodepng_get_raw_size(w, 1, &state.info_png.color);
|
205
|
-
|
206
|
-
for(size_t i = 0; i < data.size(); i += linebytes)
|
207
|
-
{
|
208
|
-
filterTypes[0].push_back(data[i]);
|
209
|
-
}
|
210
|
-
}
|
211
|
-
else
|
212
|
-
{
|
213
|
-
//Interlaced
|
214
|
-
filterTypes.resize(7);
|
215
|
-
static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
|
216
|
-
static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
|
217
|
-
static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
|
218
|
-
static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
|
219
|
-
size_t pos = 0;
|
220
|
-
for(int j = 0; j < 7; j++)
|
221
|
-
{
|
222
|
-
unsigned w2 = (w - ADAM7_IX[j] + ADAM7_DX[j] - 1) / ADAM7_DX[j];
|
223
|
-
unsigned h2 = (h - ADAM7_IY[j] + ADAM7_DY[j] - 1) / ADAM7_DY[j];
|
224
|
-
if(ADAM7_IX[j] >= w || ADAM7_IY[j] >= h) w2 = h2 = 0;
|
225
|
-
size_t linebytes = 1 + lodepng_get_raw_size(w2, 1, &state.info_png.color);
|
226
|
-
for(size_t i = 0; i < h2; i++)
|
227
|
-
{
|
228
|
-
filterTypes[j].push_back(data[pos]);
|
229
|
-
pos += linebytes;
|
230
|
-
}
|
231
|
-
}
|
232
|
-
}
|
233
|
-
return 0; /* OK */
|
234
|
-
}
|
235
|
-
|
236
|
-
|
237
|
-
unsigned getFilterTypes(std::vector<unsigned char>& filterTypes, const std::vector<unsigned char>& png)
|
238
|
-
{
|
239
|
-
std::vector<std::vector<unsigned char> > passes;
|
240
|
-
unsigned error = getFilterTypesInterlaced(passes, png);
|
241
|
-
if(error) return error;
|
242
|
-
|
243
|
-
if(passes.size() == 1)
|
244
|
-
{
|
245
|
-
filterTypes.swap(passes[0]);
|
246
|
-
}
|
247
|
-
else
|
248
|
-
{
|
249
|
-
lodepng::State state;
|
250
|
-
unsigned w, h;
|
251
|
-
lodepng_inspect(&w, &h, &state, &png[0], png.size());
|
252
|
-
/*
|
253
|
-
Interlaced. Simplify it: put pass 6 and 7 alternating in the one vector so
|
254
|
-
that one filter per scanline of the uninterlaced image is given, with that
|
255
|
-
filter corresponding the closest to what it would be for non-interlaced
|
256
|
-
image.
|
257
|
-
*/
|
258
|
-
for(size_t i = 0; i < h; i++)
|
259
|
-
{
|
260
|
-
filterTypes.push_back(i % 2 == 0 ? passes[5][i / 2] : passes[6][i / 2]);
|
261
|
-
}
|
262
|
-
}
|
263
|
-
return 0; /* OK */
|
264
|
-
}
|
265
|
-
|
266
|
-
int getPaletteValue(const unsigned char* data, size_t i, int bits)
|
267
|
-
{
|
268
|
-
if(bits == 8) return data[i];
|
269
|
-
else if(bits == 4) return (data[i / 2] >> ((i % 2) * 4)) & 15;
|
270
|
-
else if(bits == 2) return (data[i / 4] >> ((i % 4) * 2)) & 3;
|
271
|
-
else if(bits == 1) return (data[i / 8] >> (i % 8)) & 1;
|
272
|
-
else return 0;
|
273
|
-
}
|
274
|
-
|
275
|
-
//This uses a stripped down version of picoPNG to extract detailed zlib information while decompressing.
|
276
|
-
static const unsigned long LENBASE[29] =
|
277
|
-
{3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258};
|
278
|
-
static const unsigned long LENEXTRA[29] =
|
279
|
-
{0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
|
280
|
-
static const unsigned long DISTBASE[30] =
|
281
|
-
{1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
|
282
|
-
static const unsigned long DISTEXTRA[30] =
|
283
|
-
{0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
|
284
|
-
static const unsigned long CLCL[19] =
|
285
|
-
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; //code length code lengths
|
286
|
-
|
287
|
-
struct ExtractZlib // Zlib decompression and information extraction
|
288
|
-
{
|
289
|
-
std::vector<ZlibBlockInfo>* zlibinfo;
|
290
|
-
int error;
|
291
|
-
|
292
|
-
ExtractZlib(std::vector<ZlibBlockInfo>* output) : zlibinfo(output) {};
|
293
|
-
|
294
|
-
unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits)
|
295
|
-
{
|
296
|
-
unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1;
|
297
|
-
bitp++;
|
298
|
-
return result;
|
299
|
-
}
|
300
|
-
|
301
|
-
unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits)
|
302
|
-
{
|
303
|
-
unsigned long result = 0;
|
304
|
-
for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i;
|
305
|
-
return result;
|
306
|
-
}
|
307
|
-
|
308
|
-
struct HuffmanTree
|
309
|
-
{
|
310
|
-
int makeFromLengths(const std::vector<unsigned long>& bitlen, unsigned long maxbitlen)
|
311
|
-
{ //make tree given the lengths
|
312
|
-
unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0;
|
313
|
-
std::vector<unsigned long> tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0);
|
314
|
-
//count number of instances of each code length
|
315
|
-
for(unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++;
|
316
|
-
for(unsigned long bits = 1; bits <= maxbitlen; bits++)
|
317
|
-
{
|
318
|
-
nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
|
319
|
-
}
|
320
|
-
//generate all the codes
|
321
|
-
for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++;
|
322
|
-
tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet
|
323
|
-
for(unsigned long n = 0; n < numcodes; n++) //the codes
|
324
|
-
for(unsigned long i = 0; i < bitlen[n]; i++) //the bits for this code
|
325
|
-
{
|
326
|
-
unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1;
|
327
|
-
if(treepos > numcodes - 2) return 55;
|
328
|
-
if(tree2d[2 * treepos + bit] == 32767) //not yet filled in
|
329
|
-
{
|
330
|
-
if(i + 1 == bitlen[n])
|
331
|
-
{
|
332
|
-
//last bit
|
333
|
-
tree2d[2 * treepos + bit] = n;
|
334
|
-
treepos = 0;
|
335
|
-
}
|
336
|
-
else
|
337
|
-
{
|
338
|
-
//addresses are encoded as values > numcodes
|
339
|
-
tree2d[2 * treepos + bit] = ++nodefilled + numcodes;
|
340
|
-
treepos = nodefilled;
|
341
|
-
}
|
342
|
-
}
|
343
|
-
else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value
|
344
|
-
}
|
345
|
-
return 0;
|
346
|
-
}
|
347
|
-
int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const
|
348
|
-
{ //Decodes a symbol from the tree
|
349
|
-
unsigned long numcodes = (unsigned long)tree2d.size() / 2;
|
350
|
-
if(treepos >= numcodes) return 11; //error: you appeared outside the codetree
|
351
|
-
result = tree2d[2 * treepos + bit];
|
352
|
-
decoded = (result < numcodes);
|
353
|
-
treepos = decoded ? 0 : result - numcodes;
|
354
|
-
return 0;
|
355
|
-
}
|
356
|
-
//2D representation of a huffman tree: one dimension is "0" or "1", the other contains all nodes and leaves.
|
357
|
-
std::vector<unsigned long> tree2d;
|
358
|
-
};
|
359
|
-
|
360
|
-
void inflate(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, size_t inpos = 0)
|
361
|
-
{
|
362
|
-
size_t bp = 0, pos = 0; //bit pointer and byte pointer
|
363
|
-
error = 0;
|
364
|
-
unsigned long BFINAL = 0;
|
365
|
-
while(!BFINAL && !error)
|
366
|
-
{
|
367
|
-
size_t uncomprblockstart = pos;
|
368
|
-
size_t bpstart = bp;
|
369
|
-
if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory
|
370
|
-
BFINAL = readBitFromStream(bp, &in[inpos]);
|
371
|
-
unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]);
|
372
|
-
zlibinfo->resize(zlibinfo->size() + 1);
|
373
|
-
zlibinfo->back().btype = BTYPE;
|
374
|
-
if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE
|
375
|
-
else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size());
|
376
|
-
else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE);
|
377
|
-
size_t uncomprblocksize = pos - uncomprblockstart;
|
378
|
-
zlibinfo->back().compressedbits = bp - bpstart;
|
379
|
-
zlibinfo->back().uncompressedbytes = uncomprblocksize;
|
380
|
-
}
|
381
|
-
}
|
382
|
-
|
383
|
-
void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) //get the tree of a deflated block with fixed tree
|
384
|
-
{
|
385
|
-
std::vector<unsigned long> bitlen(288, 8), bitlenD(32, 5);;
|
386
|
-
for(size_t i = 144; i <= 255; i++) bitlen[i] = 9;
|
387
|
-
for(size_t i = 256; i <= 279; i++) bitlen[i] = 7;
|
388
|
-
tree.makeFromLengths(bitlen, 15);
|
389
|
-
treeD.makeFromLengths(bitlenD, 15);
|
390
|
-
}
|
391
|
-
|
392
|
-
//the code tree for Huffman codes, dist codes, and code length codes
|
393
|
-
HuffmanTree codetree, codetreeD, codelengthcodetree;
|
394
|
-
unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength)
|
395
|
-
{
|
396
|
-
//decode a single symbol from given list of bits with given code tree. return value is the symbol
|
397
|
-
bool decoded; unsigned long ct;
|
398
|
-
for(size_t treepos = 0;;)
|
399
|
-
{
|
400
|
-
if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode
|
401
|
-
error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in));
|
402
|
-
if(error) return 0; //stop, an error happened
|
403
|
-
if(decoded) return ct;
|
404
|
-
}
|
405
|
-
}
|
406
|
-
|
407
|
-
void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD,
|
408
|
-
const unsigned char* in, size_t& bp, size_t inlength)
|
409
|
-
{
|
410
|
-
size_t bpstart = bp;
|
411
|
-
//get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree
|
412
|
-
std::vector<unsigned long> bitlen(288, 0), bitlenD(32, 0);
|
413
|
-
if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory
|
414
|
-
size_t HLIT = readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257
|
415
|
-
size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1
|
416
|
-
size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4
|
417
|
-
zlibinfo->back().hlit = HLIT - 257;
|
418
|
-
zlibinfo->back().hdist = HDIST - 1;
|
419
|
-
zlibinfo->back().hclen = HCLEN - 4;
|
420
|
-
std::vector<unsigned long> codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree
|
421
|
-
for(size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0;
|
422
|
-
//code length code lengths
|
423
|
-
for(size_t i = 0; i < codelengthcode.size(); i++) zlibinfo->back().clcl.push_back(codelengthcode[i]);
|
424
|
-
error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return;
|
425
|
-
size_t i = 0, replength;
|
426
|
-
while(i < HLIT + HDIST)
|
427
|
-
{
|
428
|
-
unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return;
|
429
|
-
zlibinfo->back().treecodes.push_back(code); //tree symbol code
|
430
|
-
if(code <= 15) { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code
|
431
|
-
else if(code == 16) //repeat previous
|
432
|
-
{
|
433
|
-
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
|
434
|
-
replength = 3 + readBitsFromStream(bp, in, 2);
|
435
|
-
unsigned long value; //set value to the previous code
|
436
|
-
if((i - 1) < HLIT) value = bitlen[i - 1];
|
437
|
-
else value = bitlenD[i - HLIT - 1];
|
438
|
-
for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
|
439
|
-
{
|
440
|
-
if(i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes
|
441
|
-
if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value;
|
442
|
-
}
|
443
|
-
}
|
444
|
-
else if(code == 17) //repeat "0" 3-10 times
|
445
|
-
{
|
446
|
-
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
|
447
|
-
replength = 3 + readBitsFromStream(bp, in, 3);
|
448
|
-
zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
|
449
|
-
for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
|
450
|
-
{
|
451
|
-
if(i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes
|
452
|
-
if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
|
453
|
-
}
|
454
|
-
}
|
455
|
-
else if(code == 18) //repeat "0" 11-138 times
|
456
|
-
{
|
457
|
-
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
|
458
|
-
replength = 11 + readBitsFromStream(bp, in, 7);
|
459
|
-
zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
|
460
|
-
for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
|
461
|
-
{
|
462
|
-
if(i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes
|
463
|
-
if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
|
464
|
-
}
|
465
|
-
}
|
466
|
-
else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen.
|
467
|
-
}
|
468
|
-
if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0
|
469
|
-
error = tree.makeFromLengths(bitlen, 15);
|
470
|
-
if(error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done
|
471
|
-
error = treeD.makeFromLengths(bitlenD, 15);
|
472
|
-
if(error) return;
|
473
|
-
zlibinfo->back().treebits = bp - bpstart;
|
474
|
-
//lit/len/end symbol lengths
|
475
|
-
for(size_t i = 0; i < bitlen.size(); i++) zlibinfo->back().litlenlengths.push_back(bitlen[i]);
|
476
|
-
//dist lengths
|
477
|
-
for(size_t i = 0; i < bitlenD.size(); i++) zlibinfo->back().distlengths.push_back(bitlenD[i]);
|
478
|
-
}
|
479
|
-
|
480
|
-
void inflateHuffmanBlock(std::vector<unsigned char>& out,
|
481
|
-
const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype)
|
482
|
-
{
|
483
|
-
size_t numcodes = 0, numlit = 0, numlen = 0; //for logging
|
484
|
-
if(btype == 1) { generateFixedTrees(codetree, codetreeD); }
|
485
|
-
else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; }
|
486
|
-
for(;;)
|
487
|
-
{
|
488
|
-
unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return;
|
489
|
-
numcodes++;
|
490
|
-
zlibinfo->back().lz77_lcode.push_back(code); //output code
|
491
|
-
zlibinfo->back().lz77_dcode.push_back(0);
|
492
|
-
zlibinfo->back().lz77_lbits.push_back(0);
|
493
|
-
zlibinfo->back().lz77_dbits.push_back(0);
|
494
|
-
zlibinfo->back().lz77_lvalue.push_back(0);
|
495
|
-
zlibinfo->back().lz77_dvalue.push_back(0);
|
496
|
-
|
497
|
-
if(code == 256) break; //end code
|
498
|
-
else if(code <= 255) //literal symbol
|
499
|
-
{
|
500
|
-
out.push_back((unsigned char)(code));
|
501
|
-
pos++;
|
502
|
-
numlit++;
|
503
|
-
}
|
504
|
-
else if(code >= 257 && code <= 285) //length code
|
505
|
-
{
|
506
|
-
size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];
|
507
|
-
if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
|
508
|
-
length += readBitsFromStream(bp, in, numextrabits);
|
509
|
-
unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return;
|
510
|
-
if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used)
|
511
|
-
unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD];
|
512
|
-
if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
|
513
|
-
dist += readBitsFromStream(bp, in, numextrabitsD);
|
514
|
-
size_t start = pos, back = start - dist; //backwards
|
515
|
-
for(size_t i = 0; i < length; i++)
|
516
|
-
{
|
517
|
-
out.push_back(out[back++]);
|
518
|
-
pos++;
|
519
|
-
if(back >= start) back = start - dist;
|
520
|
-
}
|
521
|
-
numlen++;
|
522
|
-
zlibinfo->back().lz77_dcode.back() = codeD; //output distance code
|
523
|
-
zlibinfo->back().lz77_lbits.back() = numextrabits; //output length extra bits
|
524
|
-
zlibinfo->back().lz77_dbits.back() = numextrabitsD; //output dist extra bits
|
525
|
-
zlibinfo->back().lz77_lvalue.back() = length; //output length
|
526
|
-
zlibinfo->back().lz77_dvalue.back() = dist; //output dist
|
527
|
-
}
|
528
|
-
}
|
529
|
-
zlibinfo->back().numlit = numlit; //output number of literal symbols
|
530
|
-
zlibinfo->back().numlen = numlen; //output number of length symbols
|
531
|
-
}
|
532
|
-
|
533
|
-
void inflateNoCompression(std::vector<unsigned char>& out,
|
534
|
-
const unsigned char* in, size_t& bp, size_t& pos, size_t inlength)
|
535
|
-
{
|
536
|
-
while((bp & 0x7) != 0) bp++; //go to first boundary of byte
|
537
|
-
size_t p = bp / 8;
|
538
|
-
if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory
|
539
|
-
unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4;
|
540
|
-
if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN
|
541
|
-
if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer
|
542
|
-
for(unsigned long n = 0; n < LEN; n++)
|
543
|
-
{
|
544
|
-
out.push_back(in[p++]); //read LEN bytes of literal data
|
545
|
-
pos++;
|
546
|
-
}
|
547
|
-
bp = p * 8;
|
548
|
-
}
|
549
|
-
|
550
|
-
int decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in) //returns error value
|
551
|
-
{
|
552
|
-
if(in.size() < 2) { return 53; } //error, size of zlib data too small
|
553
|
-
//error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way
|
554
|
-
if((in[0] * 256 + in[1]) % 31 != 0) { return 24; }
|
555
|
-
unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1;
|
556
|
-
//error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec
|
557
|
-
if(CM != 8 || CINFO > 7) { return 25; }
|
558
|
-
//error: the PNG spec says about the zlib stream: "The additional flags shall not specify a preset dictionary."
|
559
|
-
if(FDICT != 0) { return 26; }
|
560
|
-
inflate(out, in, 2);
|
561
|
-
return error; //note: adler32 checksum was skipped and ignored
|
562
|
-
}
|
563
|
-
};
|
564
|
-
|
565
|
-
struct ExtractPNG //PNG decoding and information extraction
|
566
|
-
{
|
567
|
-
std::vector<ZlibBlockInfo>* zlibinfo;
|
568
|
-
int error;
|
569
|
-
|
570
|
-
ExtractPNG(std::vector<ZlibBlockInfo>* output) : zlibinfo(output) {};
|
571
|
-
|
572
|
-
void decode(const unsigned char* in, size_t size)
|
573
|
-
{
|
574
|
-
error = 0;
|
575
|
-
if(size == 0 || in == 0) { error = 48; return; } //the given data is empty
|
576
|
-
readPngHeader(&in[0], size); if(error) return;
|
577
|
-
size_t pos = 33; //first byte of the first chunk after the header
|
578
|
-
std::vector<unsigned char> idat; //the data from idat chunks
|
579
|
-
bool IEND = false;
|
580
|
-
//loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
|
581
|
-
//IDAT data is put at the start of the in buffer
|
582
|
-
while(!IEND)
|
583
|
-
{
|
584
|
-
//error: size of the in buffer too small to contain next chunk
|
585
|
-
if(pos + 8 >= size) { error = 30; return; }
|
586
|
-
size_t chunkLength = read32bitInt(&in[pos]); pos += 4;
|
587
|
-
if(chunkLength > 2147483647) { error = 63; return; }
|
588
|
-
//error: size of the in buffer too small to contain next chunk
|
589
|
-
if(pos + chunkLength >= size) { error = 35; return; }
|
590
|
-
//IDAT chunk, containing compressed image data
|
591
|
-
if(in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T')
|
592
|
-
{
|
593
|
-
idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]);
|
594
|
-
pos += (4 + chunkLength);
|
595
|
-
}
|
596
|
-
else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D')
|
597
|
-
{
|
598
|
-
pos += 4;
|
599
|
-
IEND = true;
|
600
|
-
}
|
601
|
-
else //it's not an implemented chunk type, so ignore it: skip over the data
|
602
|
-
{
|
603
|
-
pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk
|
604
|
-
}
|
605
|
-
pos += 4; //step over CRC (which is ignored)
|
606
|
-
}
|
607
|
-
std::vector<unsigned char> out; //now the out buffer will be filled
|
608
|
-
ExtractZlib zlib(zlibinfo); //decompress with the Zlib decompressor
|
609
|
-
error = zlib.decompress(out, idat);
|
610
|
-
if(error) return; //stop if the zlib decompressor returned an error
|
611
|
-
}
|
612
|
-
|
613
|
-
//read the information from the header and store it in the Info
|
614
|
-
void readPngHeader(const unsigned char* in, size_t inlength)
|
615
|
-
{
|
616
|
-
if(inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header
|
617
|
-
if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71
|
618
|
-
|| in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { error = 28; return; } //no PNG signature
|
619
|
-
//error: it doesn't start with a IHDR chunk!
|
620
|
-
if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; }
|
621
|
-
}
|
622
|
-
|
623
|
-
unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits)
|
624
|
-
{
|
625
|
-
unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1;
|
626
|
-
bitp++;
|
627
|
-
return result;
|
628
|
-
}
|
629
|
-
|
630
|
-
unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits)
|
631
|
-
{
|
632
|
-
unsigned long result = 0;
|
633
|
-
for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i);
|
634
|
-
return result;
|
635
|
-
}
|
636
|
-
|
637
|
-
void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit)
|
638
|
-
{
|
639
|
-
bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++;
|
640
|
-
}
|
641
|
-
|
642
|
-
unsigned long read32bitInt(const unsigned char* buffer)
|
643
|
-
{
|
644
|
-
return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
|
645
|
-
}
|
646
|
-
};
|
647
|
-
|
648
|
-
void extractZlibInfo(std::vector<ZlibBlockInfo>& zlibinfo, const std::vector<unsigned char>& in)
|
649
|
-
{
|
650
|
-
ExtractPNG decoder(&zlibinfo);
|
651
|
-
decoder.decode(&in[0], in.size());
|
652
|
-
|
653
|
-
if(decoder.error) std::cout << "extract error: " << decoder.error << std::endl;
|
654
|
-
}
|
655
|
-
|
656
|
-
} // namespace lodepng
|