Root/
Source at commit 284 created 13 years 10 months ago. By blackosx, Amended my mistake by updating the Default theme images in the trunk. Now put them back as they were.. (Sorry) | |
---|---|
1 | // picoPNG version 20080503 (cleaned up and ported to c by kaitek)␊ |
2 | // Copyright (c) 2005-2008 Lode Vandevenne␊ |
3 | //␊ |
4 | // This software is provided 'as-is', without any express or implied␊ |
5 | // warranty. In no event will the authors be held liable for any damages␊ |
6 | // arising from the use of this software.␊ |
7 | //␊ |
8 | // Permission is granted to anyone to use this software for any purpose,␊ |
9 | // including commercial applications, and to alter it and redistribute it␊ |
10 | // freely, subject to the following restrictions:␊ |
11 | //␊ |
12 | // 1. The origin of this software must not be misrepresented; you must not␊ |
13 | // claim that you wrote the original software. If you use this software␊ |
14 | // in a product, an acknowledgment in the product documentation would be␊ |
15 | // appreciated but is not required.␊ |
16 | // 2. Altered source versions must be plainly marked as such, and must not be␊ |
17 | // misrepresented as being the original software.␊ |
18 | // 3. This notice may not be removed or altered from any source distribution.␊ |
19 | ␊ |
20 | #include "libsa.h"␊ |
21 | #include "picopng.h"␊ |
22 | ␊ |
23 | /*************************************************************************************************/␊ |
24 | ␊ |
25 | typedef struct png_alloc_node {␊ |
26 | ␉struct png_alloc_node *prev, *next;␊ |
27 | ␉void *addr;␊ |
28 | ␉size_t size;␊ |
29 | } png_alloc_node_t;␊ |
30 | ␊ |
31 | png_alloc_node_t *png_alloc_head = NULL;␊ |
32 | png_alloc_node_t *png_alloc_tail = NULL;␊ |
33 | ␊ |
34 | png_alloc_node_t *png_alloc_find_node(void *addr)␊ |
35 | {␊ |
36 | ␉png_alloc_node_t *node;␊ |
37 | ␉for (node = png_alloc_head; node; node = node->next)␊ |
38 | ␉␉if (node->addr == addr)␊ |
39 | ␉␉␉break;␊ |
40 | ␉return node;␊ |
41 | }␊ |
42 | ␊ |
43 | void png_alloc_add_node(void *addr, size_t size)␊ |
44 | {␊ |
45 | ␉png_alloc_node_t *node;␊ |
46 | ␉if (png_alloc_find_node(addr))␊ |
47 | ␉␉return;␊ |
48 | ␉node = malloc(sizeof (png_alloc_node_t));␊ |
49 | ␉node->addr = addr;␊ |
50 | ␉node->size = size;␊ |
51 | ␉node->prev = png_alloc_tail;␊ |
52 | ␉node->next = NULL;␊ |
53 | ␉png_alloc_tail = node;␊ |
54 | ␉if (node->prev)␊ |
55 | ␉␉node->prev->next = node;␊ |
56 | ␉if (!png_alloc_head)␊ |
57 | ␉␉png_alloc_head = node;␊ |
58 | }␊ |
59 | ␊ |
60 | void png_alloc_remove_node(png_alloc_node_t *node)␊ |
61 | {␊ |
62 | ␉if (node->prev)␊ |
63 | ␉␉node->prev->next = node->next;␊ |
64 | ␉if (node->next)␊ |
65 | ␉␉node->next->prev = node->prev;␊ |
66 | ␉if (node == png_alloc_head)␊ |
67 | ␉␉png_alloc_head = node->next;␊ |
68 | ␉if (node == png_alloc_tail)␊ |
69 | ␉␉png_alloc_tail = node->prev;␊ |
70 | ␉node->prev = node->next = node->addr = NULL;␊ |
71 | ␉free(node);␊ |
72 | }␊ |
73 | ␊ |
74 | void *png_alloc_malloc(size_t size)␊ |
75 | {␊ |
76 | ␉void *addr = malloc(size);␊ |
77 | ␉png_alloc_add_node(addr, size);␊ |
78 | ␉return addr;␊ |
79 | }␊ |
80 | ␊ |
81 | void *png_alloc_realloc(void *addr, size_t size)␊ |
82 | {␊ |
83 | ␉void *new_addr;␊ |
84 | ␉if (!addr)␊ |
85 | ␉␉return png_alloc_malloc(size);␊ |
86 | ␉new_addr = realloc(addr, size);␊ |
87 | ␉if (new_addr != addr) {␊ |
88 | ␉␉png_alloc_node_t *old_node;␊ |
89 | ␉␉old_node = png_alloc_find_node(addr);␊ |
90 | ␉␉png_alloc_remove_node(old_node);␊ |
91 | ␉␉png_alloc_add_node(new_addr, size);␊ |
92 | ␉}␊ |
93 | ␉return new_addr;␊ |
94 | }␊ |
95 | ␊ |
96 | void png_alloc_free(void *addr)␊ |
97 | {␊ |
98 | ␉png_alloc_node_t *node = png_alloc_find_node(addr);␊ |
99 | ␉if (!node)␊ |
100 | ␉␉return;␊ |
101 | ␉png_alloc_remove_node(node);␊ |
102 | ␉free(addr);␊ |
103 | }␊ |
104 | ␊ |
105 | void png_alloc_free_all()␊ |
106 | {␊ |
107 | ␉while (png_alloc_tail) {␊ |
108 | ␉␉void *addr = png_alloc_tail->addr;␊ |
109 | ␉␉png_alloc_remove_node(png_alloc_tail);␊ |
110 | ␉␉free(addr);␊ |
111 | ␉}␊ |
112 | }␊ |
113 | ␊ |
114 | /*************************************************************************************************/␊ |
115 | ␊ |
116 | __unused void vector32_cleanup(vector32_t *p)␊ |
117 | {␊ |
118 | ␉p->size = p->allocsize = 0;␊ |
119 | ␉if (p->data)␊ |
120 | ␉␉png_alloc_free(p->data);␊ |
121 | ␉p->data = NULL;␊ |
122 | }␊ |
123 | ␊ |
124 | uint32_t vector32_resize(vector32_t *p, size_t size)␊ |
125 | {␉// returns 1 if success, 0 if failure ==> nothing done␊ |
126 | ␉if (size * sizeof (uint32_t) > p->allocsize) {␊ |
127 | ␉␉size_t newsize = size * sizeof (uint32_t) * 2;␊ |
128 | ␉␉void *data = png_alloc_realloc(p->data, newsize);␊ |
129 | ␉␉if (data) {␊ |
130 | ␉␉␉p->allocsize = newsize;␊ |
131 | ␉␉␉p->data = (uint32_t *) data;␊ |
132 | ␉␉␉p->size = size;␊ |
133 | ␉␉} else␊ |
134 | ␉␉␉return 0;␊ |
135 | ␉} else␊ |
136 | ␉␉p->size = size;␊ |
137 | ␉return 1;␊ |
138 | }␊ |
139 | ␊ |
140 | uint32_t vector32_resizev(vector32_t *p, size_t size, uint32_t value)␊ |
141 | {␉// resize and give all new elements the value␊ |
142 | ␉size_t oldsize = p->size, i;␊ |
143 | ␉if (!vector32_resize(p, size))␊ |
144 | ␉␉return 0;␊ |
145 | ␉for (i = oldsize; i < size; i++)␊ |
146 | ␉␉p->data[i] = value;␊ |
147 | ␉return 1;␊ |
148 | }␊ |
149 | ␊ |
150 | void vector32_init(vector32_t *p)␊ |
151 | {␊ |
152 | ␉p->data = NULL;␊ |
153 | ␉p->size = p->allocsize = 0;␊ |
154 | }␊ |
155 | ␊ |
156 | vector32_t *vector32_new(size_t size, uint32_t value)␊ |
157 | {␊ |
158 | ␉vector32_t *p = png_alloc_malloc(sizeof (vector32_t));␊ |
159 | ␉vector32_init(p);␊ |
160 | ␉if (size && !vector32_resizev(p, size, value))␊ |
161 | ␉␉return NULL;␊ |
162 | ␉return p;␊ |
163 | }␊ |
164 | ␊ |
165 | /*************************************************************************************************/␊ |
166 | ␊ |
167 | __unused void vector8_cleanup(vector8_t *p)␊ |
168 | {␊ |
169 | ␉p->size = p->allocsize = 0;␊ |
170 | ␉if (p->data)␊ |
171 | ␉␉png_alloc_free(p->data);␊ |
172 | ␉p->data = NULL;␊ |
173 | }␊ |
174 | ␊ |
175 | uint32_t vector8_resize(vector8_t *p, size_t size)␊ |
176 | {␉// returns 1 if success, 0 if failure ==> nothing done␊ |
177 | ␉// xxx: the use of sizeof uint32_t here seems like a bug (this descends from the lodepng vector␊ |
178 | ␉// compatibility functions which do the same). without this there is corruption in certain cases,␊ |
179 | ␉// so this was probably done to cover up allocation bug(s) in the original picopng code!␊ |
180 | ␉if (size * sizeof (uint32_t) > p->allocsize) {␊ |
181 | ␉␉size_t newsize = size * sizeof (uint32_t) * 2;␊ |
182 | ␉␉void *data = png_alloc_realloc(p->data, newsize);␊ |
183 | ␉␉if (data) {␊ |
184 | ␉␉␉p->allocsize = newsize;␊ |
185 | ␉␉␉p->data = (uint8_t *) data;␊ |
186 | ␉␉␉p->size = size;␊ |
187 | ␉␉} else␊ |
188 | ␉␉␉return 0; // error: not enough memory␊ |
189 | ␉} else␊ |
190 | ␉␉p->size = size;␊ |
191 | ␉return 1;␊ |
192 | }␊ |
193 | ␊ |
194 | uint32_t vector8_resizev(vector8_t *p, size_t size, uint8_t value)␊ |
195 | {␉// resize and give all new elements the value␊ |
196 | ␉size_t oldsize = p->size, i;␊ |
197 | ␉if (!vector8_resize(p, size))␊ |
198 | ␉␉return 0;␊ |
199 | ␉for (i = oldsize; i < size; i++)␊ |
200 | ␉␉p->data[i] = value;␊ |
201 | ␉return 1;␊ |
202 | }␊ |
203 | ␊ |
204 | void vector8_init(vector8_t *p)␊ |
205 | {␊ |
206 | ␉p->data = NULL;␊ |
207 | ␉p->size = p->allocsize = 0;␊ |
208 | }␊ |
209 | ␊ |
210 | vector8_t *vector8_new(size_t size, uint8_t value)␊ |
211 | {␊ |
212 | ␉vector8_t *p = png_alloc_malloc(sizeof (vector8_t));␊ |
213 | ␉vector8_init(p);␊ |
214 | ␉if (size && !vector8_resizev(p, size, value))␊ |
215 | ␉␉return NULL;␊ |
216 | ␉return p;␊ |
217 | }␊ |
218 | ␊ |
219 | vector8_t *vector8_copy(vector8_t *p)␊ |
220 | {␊ |
221 | ␉vector8_t *q = vector8_new(p->size, 0);␊ |
222 | ␉uint32_t n;␊ |
223 | ␉for (n = 0; n < q->size; n++)␊ |
224 | ␉␉q->data[n] = p->data[n];␊ |
225 | ␉return q;␊ |
226 | }␊ |
227 | ␊ |
228 | /*************************************************************************************************/␊ |
229 | ␊ |
230 | const uint32_t LENBASE[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51,␊ |
231 | ␉␉59, 67, 83, 99, 115, 131, 163, 195, 227, 258 };␊ |
232 | const uint32_t LENEXTRA[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,␊ |
233 | ␉␉4, 5, 5, 5, 5, 0 };␊ |
234 | const uint32_t DISTBASE[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,␊ |
235 | ␉␉513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 };␊ |
236 | const uint32_t DISTEXTRA[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,␊ |
237 | ␉␉10, 10, 11, 11, 12, 12, 13, 13 };␊ |
238 | // code length code lengths␊ |
239 | const uint32_t CLCL[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };␊ |
240 | ␊ |
241 | /*************************************************************************************************/␊ |
242 | ␊ |
243 | typedef struct {␊ |
244 | ␉// 2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all␊ |
245 | ␉// nodes and leaves of the tree.␊ |
246 | ␉vector32_t *tree2d;␊ |
247 | } HuffmanTree;␊ |
248 | ␊ |
249 | HuffmanTree *HuffmanTree_new()␊ |
250 | {␊ |
251 | ␉HuffmanTree *tree = png_alloc_malloc(sizeof (HuffmanTree));␊ |
252 | ␉tree->tree2d = NULL;␊ |
253 | ␉return tree;␊ |
254 | }␊ |
255 | ␊ |
256 | int HuffmanTree_makeFromLengths(HuffmanTree *tree, const vector32_t *bitlen, uint32_t maxbitlen)␊ |
257 | {␉// make tree given the lengths␊ |
258 | ␉uint32_t bits, n, i;␊ |
259 | ␉uint32_t numcodes = (uint32_t) bitlen->size, treepos = 0, nodefilled = 0;␊ |
260 | ␉vector32_t *tree1d, *blcount, *nextcode;␊ |
261 | ␉tree1d = vector32_new(numcodes, 0);␊ |
262 | ␉blcount = vector32_new(maxbitlen + 1, 0);␊ |
263 | ␉nextcode = vector32_new(maxbitlen + 1, 0);␊ |
264 | ␉for (bits = 0; bits < numcodes; bits++)␊ |
265 | ␉␉blcount->data[bitlen->data[bits]]++; // count number of instances of each code length␊ |
266 | ␉for (bits = 1; bits <= maxbitlen; bits++)␊ |
267 | ␉␉nextcode->data[bits] = (nextcode->data[bits - 1] + blcount->data[bits - 1]) << 1;␊ |
268 | ␉for (n = 0; n < numcodes; n++)␊ |
269 | ␉␉if (bitlen->data[n] != 0)␊ |
270 | ␉␉␉tree1d->data[n] = nextcode->data[bitlen->data[n]]++; // generate all the codes␊ |
271 | ␉// 0x7fff here means the tree2d isn't filled there yet␊ |
272 | ␉vector32_t *tree2d = vector32_new(numcodes * 2, 0x7fff);␊ |
273 | ␉tree->tree2d = tree2d;␊ |
274 | ␉for (n = 0; n < numcodes; n++) // the codes␊ |
275 | ␉␉for (i = 0; i < bitlen->data[n]; i++) { // the bits for this code␊ |
276 | ␉␉␉uint32_t bit = (tree1d->data[n] >> (bitlen->data[n] - i - 1)) & 1;␊ |
277 | ␉␉␉if (treepos > numcodes - 2)␊ |
278 | ␉␉␉␉return 55;␊ |
279 | ␉␉␉if (tree2d->data[2 * treepos + bit] == 0x7fff) { // not yet filled in␊ |
280 | ␉␉␉␉if (i + 1 == bitlen->data[n]) { // last bit␊ |
281 | ␉␉␉␉␉tree2d->data[2 * treepos + bit] = n;␊ |
282 | ␉␉␉␉␉treepos = 0;␊ |
283 | ␉␉␉␉} else { // addresses are encoded as values > numcodes␊ |
284 | ␉␉␉␉␉tree2d->data[2 * treepos + bit] = ++nodefilled + numcodes;␊ |
285 | ␉␉␉␉␉treepos = nodefilled;␊ |
286 | ␉␉␉␉}␊ |
287 | ␉␉␉} else // subtract numcodes from address to get address value␊ |
288 | ␉␉␉␉treepos = tree2d->data[2 * treepos + bit] - numcodes;␊ |
289 | ␉␉}␊ |
290 | ␉return 0;␊ |
291 | }␊ |
292 | ␊ |
293 | int HuffmanTree_decode(const HuffmanTree *tree, bool *decoded, uint32_t *result, size_t *treepos,␊ |
294 | ␉␉uint32_t bit)␊ |
295 | {␉// Decodes a symbol from the tree␊ |
296 | ␉const vector32_t *tree2d = tree->tree2d;␊ |
297 | ␉uint32_t numcodes = (uint32_t) tree2d->size / 2;␊ |
298 | ␉if (*treepos >= numcodes)␊ |
299 | ␉␉return 11; // error: you appeared outside the codetree␊ |
300 | ␉*result = tree2d->data[2 * (*treepos) + bit];␊ |
301 | ␉*decoded = (*result < numcodes);␊ |
302 | ␉*treepos = *decoded ? 0 : *result - numcodes;␊ |
303 | ␉return 0;␊ |
304 | }␊ |
305 | ␊ |
306 | /*************************************************************************************************/␊ |
307 | ␊ |
308 | int Inflator_error;␊ |
309 | ␊ |
310 | uint32_t Zlib_readBitFromStream(size_t *bitp, const uint8_t *bits)␊ |
311 | {␊ |
312 | ␉uint32_t result = (bits[*bitp >> 3] >> (*bitp & 0x7)) & 1;␊ |
313 | ␉(*bitp)++;␊ |
314 | ␉return result;␊ |
315 | }␊ |
316 | ␊ |
317 | uint32_t Zlib_readBitsFromStream(size_t *bitp, const uint8_t *bits, size_t nbits)␊ |
318 | {␊ |
319 | ␉uint32_t i, result = 0;␊ |
320 | ␉for (i = 0; i < nbits; i++)␊ |
321 | ␉␉result += (Zlib_readBitFromStream(bitp, bits)) << i;␊ |
322 | ␉return result;␊ |
323 | }␊ |
324 | ␊ |
325 | void Inflator_generateFixedTrees(HuffmanTree *tree, HuffmanTree *treeD)␊ |
326 | {␉// get the tree of a deflated block with fixed tree␊ |
327 | ␉size_t i;␊ |
328 | ␉vector32_t *bitlen, *bitlenD;␊ |
329 | ␉bitlen = vector32_new(288, 8);␊ |
330 | ␉bitlenD = vector32_new(32, 5);␊ |
331 | ␉for (i = 144; i <= 255; i++)␊ |
332 | ␉␉bitlen->data[i] = 9;␊ |
333 | ␉for (i = 256; i <= 279; i++)␊ |
334 | ␉␉bitlen->data[i] = 7;␊ |
335 | ␉HuffmanTree_makeFromLengths(tree, bitlen, 15);␊ |
336 | ␉HuffmanTree_makeFromLengths(treeD, bitlenD, 15);␊ |
337 | }␊ |
338 | ␊ |
339 | uint32_t Inflator_huffmanDecodeSymbol(const uint8_t *in, size_t *bp, const HuffmanTree *codetree,␊ |
340 | ␉␉size_t inlength)␊ |
341 | {␉// decode a single symbol from given list of bits with given code tree. returns the symbol␊ |
342 | ␉bool decoded = false;␊ |
343 | ␉uint32_t ct = 0;␊ |
344 | ␉size_t treepos = 0;␊ |
345 | ␉for (;;) {␊ |
346 | ␉␉if ((*bp & 0x07) == 0 && (*bp >> 3) > inlength) {␊ |
347 | ␉␉␉Inflator_error = 10; // error: end reached without endcode␊ |
348 | ␉␉␉return 0;␊ |
349 | ␉␉}␊ |
350 | ␉␉Inflator_error = HuffmanTree_decode(codetree, &decoded, &ct, &treepos,␊ |
351 | ␉␉␉␉Zlib_readBitFromStream(bp, in));␊ |
352 | ␉␉if (Inflator_error)␊ |
353 | ␉␉␉return 0; // stop, an error happened␊ |
354 | ␉␉if (decoded)␊ |
355 | ␉␉␉return ct;␊ |
356 | ␉}␊ |
357 | }␊ |
358 | ␊ |
359 | void Inflator_getTreeInflateDynamic(HuffmanTree *tree, HuffmanTree *treeD, const uint8_t *in,␊ |
360 | ␉␉size_t *bp, size_t inlength)␊ |
361 | {␉// get the tree of a deflated block with dynamic tree, the tree itself is also Huffman␊ |
362 | ␉// compressed with a known tree␊ |
363 | ␉size_t i, n;␊ |
364 | ␉HuffmanTree *codelengthcodetree = HuffmanTree_new(); // the code tree for code length codes␊ |
365 | ␉vector32_t *bitlen, *bitlenD;␊ |
366 | ␉bitlen = vector32_new(288, 0);␊ |
367 | ␉bitlenD = vector32_new(32, 0);␊ |
368 | ␉if (*bp >> 3 >= inlength - 2) {␊ |
369 | ␉␉Inflator_error = 49; // the bit pointer is or will go past the memory␊ |
370 | ␉␉return;␊ |
371 | ␉}␊ |
372 | ␉size_t HLIT = Zlib_readBitsFromStream(bp, in, 5) + 257;␉// number of literal/length codes + 257␊ |
373 | ␉size_t HDIST = Zlib_readBitsFromStream(bp, in, 5) + 1;␉// number of dist codes + 1␊ |
374 | ␉size_t HCLEN = Zlib_readBitsFromStream(bp, in, 4) + 4;␉// number of code length codes + 4␊ |
375 | ␉vector32_t *codelengthcode; // lengths of tree to decode the lengths of the dynamic tree␊ |
376 | ␉codelengthcode = vector32_new(19, 0);␊ |
377 | ␉for (i = 0; i < 19; i++)␊ |
378 | ␉␉codelengthcode->data[CLCL[i]] = (i < HCLEN) ? Zlib_readBitsFromStream(bp, in, 3) : 0;␊ |
379 | ␉Inflator_error = HuffmanTree_makeFromLengths(codelengthcodetree, codelengthcode, 7);␊ |
380 | ␉if (Inflator_error)␊ |
381 | ␉␉return;␊ |
382 | ␉size_t replength;␊ |
383 | ␉for (i = 0; i < HLIT + HDIST; ) {␊ |
384 | ␉␉uint32_t code = Inflator_huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength);␊ |
385 | ␉␉if (Inflator_error)␊ |
386 | ␉␉␉return;␊ |
387 | ␉␉if (code <= 15) { // a length code␊ |
388 | ␉␉␉if (i < HLIT)␊ |
389 | ␉␉␉␉bitlen->data[i++] = code;␊ |
390 | ␉␉␉else␊ |
391 | ␉␉␉␉bitlenD->data[i++ - HLIT] = code;␊ |
392 | ␉␉} else if (code == 16) { // repeat previous␊ |
393 | ␉␉␉if (*bp >> 3 >= inlength) {␊ |
394 | ␉␉␉␉Inflator_error = 50; // error, bit pointer jumps past memory␊ |
395 | ␉␉␉␉return;␊ |
396 | ␉␉␉}␊ |
397 | ␉␉␉replength = 3 + Zlib_readBitsFromStream(bp, in, 2);␊ |
398 | ␉␉␉uint32_t value; // set value to the previous code␊ |
399 | ␉␉␉if ((i - 1) < HLIT)␊ |
400 | ␉␉␉␉value = bitlen->data[i - 1];␊ |
401 | ␉␉␉else␊ |
402 | ␉␉␉␉value = bitlenD->data[i - HLIT - 1];␊ |
403 | ␉␉␉for (n = 0; n < replength; n++) { // repeat this value in the next lengths␊ |
404 | ␉␉␉␉if (i >= HLIT + HDIST) {␊ |
405 | ␉␉␉␉␉Inflator_error = 13; // error: i is larger than the amount of codes␊ |
406 | ␉␉␉␉␉return;␊ |
407 | ␉␉␉␉}␊ |
408 | ␉␉␉␉if (i < HLIT)␊ |
409 | ␉␉␉␉␉bitlen->data[i++] = value;␊ |
410 | ␉␉␉␉else␊ |
411 | ␉␉␉␉␉bitlenD->data[i++ - HLIT] = value;␊ |
412 | ␉␉␉}␊ |
413 | ␉␉} else if (code == 17) { // repeat "0" 3-10 times␊ |
414 | ␉␉␉if (*bp >> 3 >= inlength) {␊ |
415 | ␉␉␉␉Inflator_error = 50; // error, bit pointer jumps past memory␊ |
416 | ␉␉␉␉return;␊ |
417 | ␉␉␉}␊ |
418 | ␉␉␉replength = 3 + Zlib_readBitsFromStream(bp, in, 3);␊ |
419 | ␉␉␉for (n = 0; n < replength; n++) { // repeat this value in the next lengths␊ |
420 | ␉␉␉␉if (i >= HLIT + HDIST) {␊ |
421 | ␉␉␉␉␉Inflator_error = 14; // error: i is larger than the amount of codes␊ |
422 | ␉␉␉␉␉return;␊ |
423 | ␉␉␉␉}␊ |
424 | ␉␉␉␉if (i < HLIT)␊ |
425 | ␉␉␉␉␉bitlen->data[i++] = 0;␊ |
426 | ␉␉␉␉else␊ |
427 | ␉␉␉␉␉bitlenD->data[i++ - HLIT] = 0;␊ |
428 | ␉␉␉}␊ |
429 | ␉␉} else if (code == 18) { // repeat "0" 11-138 times␊ |
430 | ␉␉␉if (*bp >> 3 >= inlength) {␊ |
431 | ␉␉␉␉Inflator_error = 50; // error, bit pointer jumps past memory␊ |
432 | ␉␉␉␉return;␊ |
433 | ␉␉␉}␊ |
434 | ␉␉␉replength = 11 + Zlib_readBitsFromStream(bp, in, 7);␊ |
435 | ␉␉␉for (n = 0; n < replength; n++) { // repeat this value in the next lengths␊ |
436 | ␉␉␉␉if (i >= HLIT + HDIST) {␊ |
437 | ␉␉␉␉␉Inflator_error = 15; // error: i is larger than the amount of codes␊ |
438 | ␉␉␉␉␉return;␊ |
439 | ␉␉␉␉}␊ |
440 | ␉␉␉␉if (i < HLIT)␊ |
441 | ␉␉␉␉␉bitlen->data[i++] = 0;␊ |
442 | ␉␉␉␉else␊ |
443 | ␉␉␉␉␉bitlenD->data[i++ - HLIT] = 0;␊ |
444 | ␉␉␉}␊ |
445 | ␉␉} else {␊ |
446 | ␉␉␉Inflator_error = 16; // error: an nonexitent code appeared. This can never happen.␊ |
447 | ␉␉␉return;␊ |
448 | ␉␉}␊ |
449 | ␉}␊ |
450 | ␉if (bitlen->data[256] == 0) {␊ |
451 | ␉␉Inflator_error = 64; // the length of the end code 256 must be larger than 0␊ |
452 | ␉␉return;␊ |
453 | ␉}␊ |
454 | ␉// now we've finally got HLIT and HDIST, so generate the code trees, and the function is done␊ |
455 | ␉Inflator_error = HuffmanTree_makeFromLengths(tree, bitlen, 15);␊ |
456 | ␉if (Inflator_error)␊ |
457 | ␉␉return;␊ |
458 | ␉Inflator_error = HuffmanTree_makeFromLengths(treeD, bitlenD, 15);␊ |
459 | ␉if (Inflator_error)␊ |
460 | ␉␉return;␊ |
461 | }␊ |
462 | ␊ |
463 | void Inflator_inflateHuffmanBlock(vector8_t *out, const uint8_t *in, size_t *bp, size_t *pos,␊ |
464 | ␉␉size_t inlength, uint32_t btype)␊ |
465 | {␊ |
466 | ␉HuffmanTree *codetree, *codetreeD; // the code tree for Huffman codes, dist codes␊ |
467 | ␉codetree = HuffmanTree_new();␊ |
468 | ␉codetreeD = HuffmanTree_new();␊ |
469 | ␉if (btype == 1)␊ |
470 | ␉␉Inflator_generateFixedTrees(codetree, codetreeD);␊ |
471 | ␉else if (btype == 2) {␊ |
472 | ␉␉Inflator_getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength);␊ |
473 | ␉␉if (Inflator_error)␊ |
474 | ␉␉␉return;␊ |
475 | ␉}␊ |
476 | ␉for (;;) {␊ |
477 | ␉␉uint32_t code = Inflator_huffmanDecodeSymbol(in, bp, codetree, inlength);␊ |
478 | ␉␉if (Inflator_error)␊ |
479 | ␉␉␉return;␊ |
480 | ␉␉if (code == 256) // end code␊ |
481 | ␉␉␉return;␊ |
482 | ␉␉else if (code <= 255) { // literal symbol␊ |
483 | ␉␉␉if (*pos >= out->size)␊ |
484 | ␉␉␉␉vector8_resize(out, (*pos + 1) * 2); // reserve more room␊ |
485 | ␉␉␉out->data[(*pos)++] = (uint8_t) code;␊ |
486 | ␉␉} else if (code >= 257 && code <= 285) { // length code␊ |
487 | ␉␉␉size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];␊ |
488 | ␉␉␉if ((*bp >> 3) >= inlength) {␊ |
489 | ␉␉␉␉Inflator_error = 51; // error, bit pointer will jump past memory␊ |
490 | ␉␉␉␉return;␊ |
491 | ␉␉␉}␊ |
492 | ␉␉␉length += Zlib_readBitsFromStream(bp, in, numextrabits);␊ |
493 | ␉␉␉uint32_t codeD = Inflator_huffmanDecodeSymbol(in, bp, codetreeD, inlength);␊ |
494 | ␉␉␉if (Inflator_error)␊ |
495 | ␉␉␉␉return;␊ |
496 | ␉␉␉if (codeD > 29) {␊ |
497 | ␉␉␉␉Inflator_error = 18; // error: invalid dist code (30-31 are never used)␊ |
498 | ␉␉␉␉return;␊ |
499 | ␉␉␉}␊ |
500 | ␉␉␉uint32_t dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD];␊ |
501 | ␉␉␉if ((*bp >> 3) >= inlength) {␊ |
502 | ␉␉␉␉Inflator_error = 51; // error, bit pointer will jump past memory␊ |
503 | ␉␉␉␉return;␊ |
504 | ␉␉␉}␊ |
505 | ␉␉␉dist += Zlib_readBitsFromStream(bp, in, numextrabitsD);␊ |
506 | ␉␉␉size_t start = *pos, back = start - dist; // backwards␊ |
507 | ␉␉␉if (*pos + length >= out->size)␊ |
508 | ␉␉␉␉vector8_resize(out, (*pos + length) * 2); // reserve more room␊ |
509 | ␉␉␉size_t i;␊ |
510 | ␉␉␉for (i = 0; i < length; i++) {␊ |
511 | ␉␉␉␉out->data[(*pos)++] = out->data[back++];␊ |
512 | ␉␉␉␉if (back >= start)␊ |
513 | ␉␉␉␉␉back = start - dist;␊ |
514 | ␉␉␉}␊ |
515 | ␉␉}␊ |
516 | ␉}␊ |
517 | }␊ |
518 | ␊ |
519 | void Inflator_inflateNoCompression(vector8_t *out, const uint8_t *in, size_t *bp, size_t *pos,␊ |
520 | ␉␉size_t inlength)␊ |
521 | {␊ |
522 | ␉while ((*bp & 0x7) != 0)␊ |
523 | ␉␉(*bp)++; // go to first boundary of byte␊ |
524 | ␉size_t p = *bp / 8;␊ |
525 | ␉if (p >= inlength - 4) {␊ |
526 | ␉␉Inflator_error = 52; // error, bit pointer will jump past memory␊ |
527 | ␉␉return;␊ |
528 | ␉}␊ |
529 | ␉uint32_t LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3];␊ |
530 | ␉p += 4;␊ |
531 | ␉if (LEN + NLEN != 65535) {␊ |
532 | ␉␉Inflator_error = 21; // error: NLEN is not one's complement of LEN␊ |
533 | ␉␉return;␊ |
534 | ␉}␊ |
535 | ␉if (*pos + LEN >= out->size)␊ |
536 | ␉␉vector8_resize(out, *pos + LEN);␊ |
537 | ␉if (p + LEN > inlength) {␊ |
538 | ␉␉Inflator_error = 23; // error: reading outside of in buffer␊ |
539 | ␉␉return;␊ |
540 | ␉}␊ |
541 | ␉uint32_t n;␊ |
542 | ␉for (n = 0; n < LEN; n++)␊ |
543 | ␉␉out->data[(*pos)++] = in[p++]; // read LEN bytes of literal data␊ |
544 | ␉*bp = p * 8;␊ |
545 | }␊ |
546 | ␊ |
547 | void Inflator_inflate(vector8_t *out, const vector8_t *in, size_t inpos)␊ |
548 | {␊ |
549 | ␉size_t bp = 0, pos = 0; // bit pointer and byte pointer␊ |
550 | ␉Inflator_error = 0;␊ |
551 | ␉uint32_t BFINAL = 0;␊ |
552 | ␉while (!BFINAL && !Inflator_error) {␊ |
553 | ␉␉if (bp >> 3 >= in->size) {␊ |
554 | ␉␉␉Inflator_error = 52; // error, bit pointer will jump past memory␊ |
555 | ␉␉␉return;␊ |
556 | ␉␉}␊ |
557 | ␉␉BFINAL = Zlib_readBitFromStream(&bp, &in->data[inpos]);␊ |
558 | ␉␉uint32_t BTYPE = Zlib_readBitFromStream(&bp, &in->data[inpos]);␊ |
559 | ␉␉BTYPE += 2 * Zlib_readBitFromStream(&bp, &in->data[inpos]);␊ |
560 | ␉␉if (BTYPE == 3) {␊ |
561 | ␉␉␉Inflator_error = 20; // error: invalid BTYPE␊ |
562 | ␉␉␉return;␊ |
563 | ␉␉}␊ |
564 | ␉␉else if (BTYPE == 0)␊ |
565 | ␉␉␉Inflator_inflateNoCompression(out, &in->data[inpos], &bp, &pos, in->size);␊ |
566 | ␉␉else␊ |
567 | ␉␉␉Inflator_inflateHuffmanBlock(out, &in->data[inpos], &bp, &pos, in->size, BTYPE);␊ |
568 | ␉}␊ |
569 | ␉if (!Inflator_error)␊ |
570 | ␉␉vector8_resize(out, pos); // Only now we know the true size of out, resize it to that␊ |
571 | }␊ |
572 | ␊ |
573 | /*************************************************************************************************/␊ |
574 | ␊ |
575 | int Zlib_decompress(vector8_t *out, const vector8_t *in) // returns error value␊ |
576 | {␊ |
577 | ␉if (in->size < 2)␊ |
578 | ␉␉return 53; // error, size of zlib data too small␊ |
579 | ␉if ((in->data[0] * 256 + in->data[1]) % 31 != 0)␊ |
580 | ␉␉// error: 256 * in->data[0] + in->data[1] must be a multiple of 31, the FCHECK value is␊ |
581 | ␉␉// supposed to be made that way␊ |
582 | ␉␉return 24;␊ |
583 | ␉uint32_t CM = in->data[0] & 15, CINFO = (in->data[0] >> 4) & 15, FDICT = (in->data[1] >> 5) & 1;␊ |
584 | ␉if (CM != 8 || CINFO > 7)␊ |
585 | ␉␉// error: only compression method 8: inflate with sliding window of 32k is supported by␊ |
586 | ␉␉// the PNG spec␊ |
587 | ␉␉return 25;␊ |
588 | ␉if (FDICT != 0)␊ |
589 | ␉␉// error: the specification of PNG says about the zlib stream: "The additional flags shall␊ |
590 | ␉␉// not specify a preset dictionary."␊ |
591 | ␉␉return 26;␊ |
592 | ␉Inflator_inflate(out, in, 2);␊ |
593 | ␉return Inflator_error; // note: adler32 checksum was skipped and ignored␊ |
594 | }␊ |
595 | ␊ |
596 | /*************************************************************************************************/␊ |
597 | ␊ |
598 | #define PNG_SIGNATURE␉0x0a1a0a0d474e5089ull␊ |
599 | ␊ |
600 | #define CHUNK_IHDR␉␉0x52444849␊ |
601 | #define CHUNK_IDAT␉␉0x54414449␊ |
602 | #define CHUNK_IEND␉␉0x444e4549␊ |
603 | #define CHUNK_PLTE␉␉0x45544c50␊ |
604 | #define CHUNK_tRNS␉␉0x534e5274␊ |
605 | ␊ |
606 | int PNG_error;␊ |
607 | ␊ |
608 | uint32_t PNG_readBitFromReversedStream(size_t *bitp, const uint8_t *bits)␊ |
609 | {␊ |
610 | ␉uint32_t result = (bits[*bitp >> 3] >> (7 - (*bitp & 0x7))) & 1;␊ |
611 | ␉(*bitp)++;␊ |
612 | ␉return result;␊ |
613 | }␊ |
614 | ␊ |
615 | uint32_t PNG_readBitsFromReversedStream(size_t *bitp, const uint8_t *bits, uint32_t nbits)␊ |
616 | {␊ |
617 | ␉uint32_t i, result = 0;␊ |
618 | ␉for (i = nbits - 1; i < nbits; i--)␊ |
619 | ␉␉result += ((PNG_readBitFromReversedStream(bitp, bits)) << i);␊ |
620 | ␉return result;␊ |
621 | }␊ |
622 | ␊ |
623 | void PNG_setBitOfReversedStream(size_t *bitp, uint8_t *bits, uint32_t bit)␊ |
624 | {␊ |
625 | ␉bits[*bitp >> 3] |= (bit << (7 - (*bitp & 0x7)));␊ |
626 | ␉(*bitp)++;␊ |
627 | }␊ |
628 | ␊ |
629 | uint32_t PNG_read32bitInt(const uint8_t *buffer)␊ |
630 | {␊ |
631 | ␉return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];␊ |
632 | }␊ |
633 | ␊ |
634 | int PNG_checkColorValidity(uint32_t colorType, uint32_t bd) // return type is a LodePNG error code␊ |
635 | {␊ |
636 | ␉if ((colorType == 2 || colorType == 4 || colorType == 6)) {␊ |
637 | ␉␉if (!(bd == 8 || bd == 16))␊ |
638 | ␉␉␉return 37;␊ |
639 | ␉␉else␊ |
640 | ␉␉␉return 0;␊ |
641 | ␉} else if (colorType == 0) {␊ |
642 | ␉␉if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16))␊ |
643 | ␉␉␉return 37;␊ |
644 | ␉␉else␊ |
645 | ␉␉␉return 0;␊ |
646 | ␉} else if (colorType == 3) {␊ |
647 | ␉␉if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8))␊ |
648 | ␉␉␉return 37;␊ |
649 | ␉␉else␊ |
650 | ␉␉␉return 0;␊ |
651 | ␉} else␊ |
652 | ␉␉return 31; // nonexistent color type␊ |
653 | }␊ |
654 | ␊ |
655 | uint32_t PNG_getBpp(const PNG_info_t *info)␊ |
656 | {␊ |
657 | ␉uint32_t bitDepth, colorType;␊ |
658 | ␉bitDepth = info->bitDepth;␊ |
659 | ␉colorType = info->colorType;␊ |
660 | ␉if (colorType == 2)␊ |
661 | ␉␉return (3 * bitDepth);␊ |
662 | ␉else if (colorType >= 4)␊ |
663 | ␉␉return (colorType - 2) * bitDepth;␊ |
664 | ␉else␊ |
665 | ␉␉return bitDepth;␊ |
666 | }␊ |
667 | ␊ |
668 | void PNG_readPngHeader(PNG_info_t *info, const uint8_t *in, size_t inlength)␊ |
669 | {␉// read the information from the header and store it in the Info␊ |
670 | ␉if (inlength < 29) {␊ |
671 | ␉␉PNG_error = 27; // error: the data length is smaller than the length of the header␊ |
672 | ␉␉return;␊ |
673 | ␉}␊ |
674 | ␉if (*(uint64_t *) in != PNG_SIGNATURE) {␊ |
675 | ␉␉PNG_error = 28; // no PNG signature␊ |
676 | ␉␉return;␊ |
677 | ␉}␊ |
678 | ␉if (*(uint32_t *) &in[12] != CHUNK_IHDR) {␊ |
679 | ␉␉PNG_error = 29; // error: it doesn't start with a IHDR chunk!␊ |
680 | ␉␉return;␊ |
681 | ␉}␊ |
682 | ␉info->width = PNG_read32bitInt(&in[16]);␊ |
683 | ␉info->height = PNG_read32bitInt(&in[20]);␊ |
684 | ␉info->bitDepth = in[24];␊ |
685 | ␉info->colorType = in[25];␊ |
686 | ␉info->compressionMethod = in[26];␊ |
687 | ␉if (in[26] != 0) {␊ |
688 | ␉␉PNG_error = 32; // error: only compression method 0 is allowed in the specification␊ |
689 | ␉␉return;␊ |
690 | ␉}␊ |
691 | ␉info->filterMethod = in[27];␊ |
692 | ␉if (in[27] != 0) {␊ |
693 | ␉␉PNG_error = 33; // error: only filter method 0 is allowed in the specification␊ |
694 | ␉␉return;␊ |
695 | ␉}␊ |
696 | ␉info->interlaceMethod = in[28];␊ |
697 | ␉if (in[28] > 1) {␊ |
698 | ␉␉PNG_error = 34; // error: only interlace methods 0 and 1 exist in the specification␊ |
699 | ␉␉return;␊ |
700 | ␉}␊ |
701 | ␉PNG_error = PNG_checkColorValidity(info->colorType, info->bitDepth);␊ |
702 | }␊ |
703 | ␊ |
704 | int PNG_paethPredictor(int a, int b, int c) // Paeth predicter, used by PNG filter type 4␊ |
705 | {␊ |
706 | ␉int p, pa, pb, pc;␊ |
707 | ␉p = a + b - c;␊ |
708 | ␉pa = p > a ? (p - a) : (a - p);␊ |
709 | ␉pb = p > b ? (p - b) : (b - p);␊ |
710 | ␉pc = p > c ? (p - c) : (c - p);␊ |
711 | ␉return (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c);␊ |
712 | }␊ |
713 | ␊ |
714 | void PNG_unFilterScanline(uint8_t *recon, const uint8_t *scanline, const uint8_t *precon,␊ |
715 | ␉␉size_t bytewidth, uint32_t filterType, size_t length)␊ |
716 | {␊ |
717 | ␉size_t i;␊ |
718 | ␉switch (filterType) {␊ |
719 | ␉case 0:␊ |
720 | ␉␉for (i = 0; i < length; i++)␊ |
721 | ␉␉␉recon[i] = scanline[i];␊ |
722 | ␉␉break;␊ |
723 | ␉case 1:␊ |
724 | ␉␉for (i = 0; i < bytewidth; i++)␊ |
725 | ␉␉␉recon[i] = scanline[i];␊ |
726 | ␉␉for (i = bytewidth; i < length; i++)␊ |
727 | ␉␉␉recon[i] = scanline[i] + recon[i - bytewidth];␊ |
728 | ␉␉break;␊ |
729 | ␉case 2:␊ |
730 | ␉␉if (precon)␊ |
731 | ␉␉␉for (i = 0; i < length; i++)␊ |
732 | ␉␉␉␉recon[i] = scanline[i] + precon[i];␊ |
733 | ␉␉else␊ |
734 | ␉␉␉for (i = 0; i < length; i++)␊ |
735 | ␉␉␉␉recon[i] = scanline[i];␊ |
736 | ␉␉break;␊ |
737 | ␉case 3:␊ |
738 | ␉␉if (precon) {␊ |
739 | ␉␉␉for (i = 0; i < bytewidth; i++)␊ |
740 | ␉␉␉␉recon[i] = scanline[i] + precon[i] / 2;␊ |
741 | ␉␉␉for (i = bytewidth; i < length; i++)␊ |
742 | ␉␉␉␉recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);␊ |
743 | ␉␉} else {␊ |
744 | ␉␉␉for (i = 0; i < bytewidth; i++)␊ |
745 | ␉␉␉␉recon[i] = scanline[i];␊ |
746 | ␉␉␉for (i = bytewidth; i < length; i++)␊ |
747 | ␉␉␉␉recon[i] = scanline[i] + recon[i - bytewidth] / 2;␊ |
748 | ␉␉}␊ |
749 | ␉␉break;␊ |
750 | ␉case 4:␊ |
751 | ␉␉if (precon) {␊ |
752 | ␉␉␉for (i = 0; i < bytewidth; i++)␊ |
753 | ␉␉␉␉recon[i] = (uint8_t) (scanline[i] + PNG_paethPredictor(0, precon[i], 0));␊ |
754 | ␉␉␉for (i = bytewidth; i < length; i++)␊ |
755 | ␉␉␉␉recon[i] = (uint8_t) (scanline[i] + PNG_paethPredictor(recon[i - bytewidth],␊ |
756 | ␉␉␉␉␉␉precon[i], precon[i - bytewidth]));␊ |
757 | ␉␉} else {␊ |
758 | ␉␉␉for (i = 0; i < bytewidth; i++)␊ |
759 | ␉␉␉␉recon[i] = scanline[i];␊ |
760 | ␉␉␉for (i = bytewidth; i < length; i++)␊ |
761 | ␉␉␉␉recon[i] = (uint8_t) (scanline[i] + PNG_paethPredictor(recon[i - bytewidth], 0, 0));␊ |
762 | ␉␉}␊ |
763 | ␉␉break;␊ |
764 | ␉default:␊ |
765 | ␉␉PNG_error = 36; // error: nonexistent filter type given␊ |
766 | ␉␉return;␊ |
767 | ␉}␊ |
768 | }␊ |
769 | ␊ |
770 | void PNG_adam7Pass(uint8_t *out, uint8_t *linen, uint8_t *lineo, const uint8_t *in, uint32_t w,␊ |
771 | ␉␉size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh,␊ |
772 | ␉␉uint32_t bpp)␊ |
773 | {␉// filter and reposition the pixels into the output when the image is Adam7 interlaced. This␊ |
774 | ␉// function can only do it after the full image is already decoded. The out buffer must have␊ |
775 | ␉// the correct allocated memory size already.␊ |
776 | ␉if (passw == 0)␊ |
777 | ␉␉return;␊ |
778 | ␉size_t bytewidth = (bpp + 7) / 8, linelength = 1 + ((bpp * passw + 7) / 8);␊ |
779 | ␉uint32_t y;␊ |
780 | ␉for (y = 0; y < passh; y++) {␊ |
781 | ␉␉size_t i, b;␊ |
782 | ␉␉uint8_t filterType = in[y * linelength], *prevline = (y == 0) ? 0 : lineo;␊ |
783 | ␉␉PNG_unFilterScanline(linen, &in[y * linelength + 1], prevline, bytewidth, filterType,␊ |
784 | ␉␉␉␉(w * bpp + 7) / 8);␊ |
785 | ␉␉if (PNG_error)␊ |
786 | ␉␉␉return;␊ |
787 | ␉␉if (bpp >= 8)␊ |
788 | ␉␉␉for (i = 0; i < passw; i++)␊ |
789 | ␉␉␉␉for (b = 0; b < bytewidth; b++) // b = current byte of this pixel␊ |
790 | ␉␉␉␉␉out[bytewidth * w * (passtop + spacey * y) + bytewidth *␊ |
791 | ␉␉␉␉␉␉␉(passleft + spacex * i) + b] = linen[bytewidth * i + b];␊ |
792 | ␉␉else␊ |
793 | ␉␉␉for (i = 0; i < passw; i++) {␊ |
794 | ␉␉␉␉size_t obp, bp;␊ |
795 | ␉␉␉␉obp = bpp * w * (passtop + spacey * y) + bpp * (passleft + spacex * i);␊ |
796 | ␉␉␉␉bp = i * bpp;␊ |
797 | ␉␉␉␉for (b = 0; b < bpp; b++)␊ |
798 | ␉␉␉␉␉PNG_setBitOfReversedStream(&obp, out, PNG_readBitFromReversedStream(&bp, linen));␊ |
799 | ␉␉␉}␊ |
800 | ␉␉uint8_t *temp = linen;␊ |
801 | ␉␉linen = lineo;␊ |
802 | ␉␉lineo = temp; // swap the two buffer pointers "line old" and "line new"␊ |
803 | ␉}␊ |
804 | }␊ |
805 | ␊ |
806 | int PNG_convert(const PNG_info_t *info, vector8_t *out, const uint8_t *in)␊ |
807 | {␉// converts from any color type to 32-bit. return value = LodePNG error code␊ |
808 | ␉size_t i, c;␊ |
809 | ␉uint32_t bitDepth, colorType;␊ |
810 | ␉bitDepth = info->bitDepth;␊ |
811 | ␉colorType = info->colorType;␊ |
812 | ␉size_t numpixels = info->width * info->height, bp = 0;␊ |
813 | ␉vector8_resize(out, numpixels * 4);␊ |
814 | ␉uint8_t *out_data = out->size ? out->data : 0;␊ |
815 | ␉if (bitDepth == 8 && colorType == 0) // greyscale␊ |
816 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
817 | ␉␉␉out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[i];␊ |
818 | ␉␉␉out_data[4 * i + 3] = (info->key_defined && (in[i] == info->key_r)) ? 0 : 255;␊ |
819 | ␉␉}␊ |
820 | ␉else if (bitDepth == 8 && colorType == 2) // RGB color␊ |
821 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
822 | ␉␉␉for (c = 0; c < 3; c++)␊ |
823 | ␉␉␉␉out_data[4 * i + c] = in[3 * i + c];␊ |
824 | ␉␉␉out_data[4 * i + 3] = (info->key_defined && (in[3 * i + 0] == info->key_r) &&␊ |
825 | ␉␉␉␉␉(in[3 * i + 1] == info->key_g) && (in[3 * i + 2] == info->key_b)) ? 0 : 255;␊ |
826 | ␉␉}␊ |
827 | ␉else if (bitDepth == 8 && colorType == 3) // indexed color (palette)␊ |
828 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
829 | ␉␉␉if (4U * in[i] >= info->palette->size)␊ |
830 | ␉␉␉␉return 46;␊ |
831 | ␉␉␉for (c = 0; c < 4; c++) // get rgb colors from the palette␊ |
832 | ␉␉␉␉out_data[4 * i + c] = info->palette->data[4 * in[i] + c];␊ |
833 | ␉␉}␊ |
834 | ␉else if (bitDepth == 8 && colorType == 4) // greyscale with alpha␊ |
835 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
836 | ␉␉␉out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[2 * i + 0];␊ |
837 | ␉␉␉out_data[4 * i + 3] = in[2 * i + 1];␊ |
838 | ␉␉}␊ |
839 | ␉else if (bitDepth == 8 && colorType == 6)␊ |
840 | ␉␉for (i = 0; i < numpixels; i++)␊ |
841 | ␉␉␉for (c = 0; c < 4; c++)␊ |
842 | ␉␉␉␉out_data[4 * i + c] = in[4 * i + c]; // RGB with alpha␊ |
843 | ␉else if (bitDepth == 16 && colorType == 0) // greyscale␊ |
844 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
845 | ␉␉␉out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[2 * i];␊ |
846 | ␉␉␉out_data[4 * i + 3] = (info->key_defined && (256U * in[i] + in[i + 1] == info->key_r))␊ |
847 | ␉␉␉␉␉? 0 : 255;␊ |
848 | ␉␉}␊ |
849 | ␉else if (bitDepth == 16 && colorType == 2) // RGB color␊ |
850 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
851 | ␉␉␉for (c = 0; c < 3; c++)␊ |
852 | ␉␉␉␉out_data[4 * i + c] = in[6 * i + 2 * c];␊ |
853 | ␉␉␉out_data[4 * i + 3] = (info->key_defined &&␊ |
854 | ␉␉␉␉␉(256U * in[6 * i + 0] + in[6 * i + 1] == info->key_r) &&␊ |
855 | ␉␉␉␉␉(256U * in[6 * i + 2] + in[6 * i + 3] == info->key_g) &&␊ |
856 | ␉␉␉␉␉(256U * in[6 * i + 4] + in[6 * i + 5] == info->key_b)) ? 0 : 255;␊ |
857 | ␉␉}␊ |
858 | ␉else if (bitDepth == 16 && colorType == 4) // greyscale with alpha␊ |
859 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
860 | ␉␉␉out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[4 * i]; // msb␊ |
861 | ␉␉␉out_data[4 * i + 3] = in[4 * i + 2];␊ |
862 | ␉␉}␊ |
863 | ␉else if (bitDepth == 16 && colorType == 6)␊ |
864 | ␉␉for (i = 0; i < numpixels; i++)␊ |
865 | ␉␉␉for (c = 0; c < 4; c++)␊ |
866 | ␉␉␉␉out_data[4 * i + c] = in[8 * i + 2 * c]; // RGB with alpha␊ |
867 | ␉else if (bitDepth < 8 && colorType == 0) // greyscale␊ |
868 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
869 | ␉␉␉uint32_t value = (PNG_readBitsFromReversedStream(&bp, in, bitDepth) * 255) /␊ |
870 | ␉␉␉␉␉((1 << bitDepth) - 1); // scale value from 0 to 255␊ |
871 | ␉␉␉out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = (uint8_t) value;␊ |
872 | ␉␉␉out_data[4 * i + 3] = (info->key_defined && value &&␊ |
873 | ␉␉␉␉␉(((1U << bitDepth) - 1U) == info->key_r) && ((1U << bitDepth) - 1U)) ? 0 : 255;␊ |
874 | ␉␉}␊ |
875 | ␉else if (bitDepth < 8 && colorType == 3) // palette␊ |
876 | ␉␉for (i = 0; i < numpixels; i++) {␊ |
877 | ␉␉␉uint32_t value = PNG_readBitsFromReversedStream(&bp, in, bitDepth);␊ |
878 | ␉␉␉if (4 * value >= info->palette->size)␊ |
879 | ␉␉␉␉return 47;␊ |
880 | ␉␉␉for (c = 0; c < 4; c++) // get rgb colors from the palette␊ |
881 | ␉␉␉␉out_data[4 * i + c] = info->palette->data[4 * value + c];␊ |
882 | ␉␉}␊ |
883 | ␉return 0;␊ |
884 | }␊ |
885 | ␊ |
886 | PNG_info_t *PNG_info_new()␊ |
887 | {␊ |
888 | ␉PNG_info_t *info = png_alloc_malloc(sizeof (PNG_info_t));␊ |
889 | ␉uint32_t i;␊ |
890 | ␉for (i = 0; i < sizeof (PNG_info_t); i++)␊ |
891 | ␉␉((uint8_t *) info)[i] = 0;␊ |
892 | ␉info->palette = vector8_new(0, 0);␊ |
893 | ␉info->image = vector8_new(0, 0);␊ |
894 | ␉return info;␊ |
895 | }␊ |
896 | ␊ |
897 | PNG_info_t *PNG_decode(const uint8_t *in, uint32_t size)␊ |
898 | {␊ |
899 | ␉PNG_info_t *info;␊ |
900 | ␉PNG_error = 0;␊ |
901 | ␉if (size == 0 || in == 0) {␊ |
902 | ␉␉PNG_error = 48; // the given data is empty␊ |
903 | ␉␉return NULL;␊ |
904 | ␉}␊ |
905 | ␉info = PNG_info_new();␊ |
906 | ␉PNG_readPngHeader(info, in, size);␊ |
907 | ␉if (PNG_error)␊ |
908 | ␉␉return NULL;␊ |
909 | ␉size_t pos = 33; // first byte of the first chunk after the header␊ |
910 | ␉vector8_t *idat = NULL; // the data from idat chunks␊ |
911 | ␉bool IEND = false, known_type = true;␊ |
912 | ␉info->key_defined = false;␊ |
913 | ␉// loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is␊ |
914 | ␉// put at the start of the in buffer␊ |
915 | ␉while (!IEND) {␊ |
916 | ␉␉size_t i, j;␊ |
917 | ␉␉if (pos + 8 >= size) {␊ |
918 | ␉␉␉PNG_error = 30; // error: size of the in buffer too small to contain next chunk␊ |
919 | ␉␉␉return NULL;␊ |
920 | ␉␉}␊ |
921 | ␉␉size_t chunkLength = PNG_read32bitInt(&in[pos]);␊ |
922 | ␉␉pos += 4;␊ |
923 | ␉␉if (chunkLength > 0x7fffffff) {␊ |
924 | ␉␉␉PNG_error = 63;␊ |
925 | ␉␉␉return NULL;␊ |
926 | ␉␉}␊ |
927 | ␉␉if (pos + chunkLength >= size) {␊ |
928 | ␉␉␉PNG_error = 35; // error: size of the in buffer too small to contain next chunk␊ |
929 | ␉␉␉return NULL;␊ |
930 | ␉␉}␊ |
931 | ␉␉uint32_t chunkType = *(uint32_t *) &in[pos];␊ |
932 | ␉␉if (chunkType == CHUNK_IDAT) { // IDAT: compressed image data chunk␊ |
933 | ␉␉␉size_t offset = 0;␊ |
934 | ␉␉␉if (idat) {␊ |
935 | ␉␉␉␉offset = idat->size;␊ |
936 | ␉␉␉␉vector8_resize(idat, offset + chunkLength);␊ |
937 | ␉␉␉} else␊ |
938 | ␉␉␉␉idat = vector8_new(chunkLength, 0);␊ |
939 | ␉␉␉for (i = 0; i < chunkLength; i++)␊ |
940 | ␉␉␉␉idat->data[offset + i] = in[pos + 4 + i];␊ |
941 | ␉␉␉pos += (4 + chunkLength);␊ |
942 | ␉␉} else if (chunkType == CHUNK_IEND) { // IEND␊ |
943 | ␉␉␉pos += 4;␊ |
944 | ␉␉␉IEND = true;␊ |
945 | ␉␉} else if (chunkType == CHUNK_PLTE) { // PLTE: palette chunk␊ |
946 | ␉␉␉pos += 4; // go after the 4 letters␊ |
947 | ␉␉␉vector8_resize(info->palette, 4 * (chunkLength / 3));␊ |
948 | ␉␉␉if (info->palette->size > (4 * 256)) {␊ |
949 | ␉␉␉␉PNG_error = 38; // error: palette too big␊ |
950 | ␉␉␉␉return NULL;␊ |
951 | ␉␉␉}␊ |
952 | ␉␉␉for (i = 0; i < info->palette->size; i += 4) {␊ |
953 | ␉␉␉␉for (j = 0; j < 3; j++)␊ |
954 | ␉␉␉␉␉info->palette->data[i + j] = in[pos++]; // RGB␊ |
955 | ␉␉␉␉info->palette->data[i + 3] = 255; // alpha␊ |
956 | ␉␉␉}␊ |
957 | ␉␉} else if (chunkType == CHUNK_tRNS) { // tRNS: palette transparency chunk␊ |
958 | ␉␉␉pos += 4; // go after the 4 letters␊ |
959 | ␉␉␉if (info->colorType == 3) {␊ |
960 | ␉␉␉␉if (4 * chunkLength > info->palette->size) {␊ |
961 | ␉␉␉␉␉PNG_error = 39; // error: more alpha values given than there are palette entries␊ |
962 | ␉␉␉␉␉return NULL;␊ |
963 | ␉␉␉␉}␊ |
964 | ␉␉␉␉for (i = 0; i < chunkLength; i++)␊ |
965 | ␉␉␉␉␉info->palette->data[4 * i + 3] = in[pos++];␊ |
966 | ␉␉␉} else if (info->colorType == 0) {␊ |
967 | ␉␉␉␉if (chunkLength != 2) {␊ |
968 | ␉␉␉␉␉PNG_error = 40; // error: this chunk must be 2 bytes for greyscale image␊ |
969 | ␉␉␉␉␉return NULL;␊ |
970 | ␉␉␉␉}␊ |
971 | ␉␉␉␉info->key_defined = true;␊ |
972 | ␉␉␉␉info->key_r = info->key_g = info->key_b = 256 * in[pos] + in[pos + 1];␊ |
973 | ␉␉␉␉pos += 2;␊ |
974 | ␉␉␉} else if (info->colorType == 2) {␊ |
975 | ␉␉␉␉if (chunkLength != 6) {␊ |
976 | ␉␉␉␉␉PNG_error = 41; // error: this chunk must be 6 bytes for RGB image␊ |
977 | ␉␉␉␉␉return NULL;␊ |
978 | ␉␉␉␉}␊ |
979 | ␉␉␉␉info->key_defined = true;␊ |
980 | ␉␉␉␉info->key_r = 256 * in[pos] + in[pos + 1];␊ |
981 | ␉␉␉␉pos += 2;␊ |
982 | ␉␉␉␉info->key_g = 256 * in[pos] + in[pos + 1];␊ |
983 | ␉␉␉␉pos += 2;␊ |
984 | ␉␉␉␉info->key_b = 256 * in[pos] + in[pos + 1];␊ |
985 | ␉␉␉␉pos += 2;␊ |
986 | ␉␉␉} else {␊ |
987 | ␉␉␉␉PNG_error = 42; // error: tRNS chunk not allowed for other color models␊ |
988 | ␉␉␉␉return NULL;␊ |
989 | ␉␉␉}␊ |
990 | ␉␉} else { // it's not an implemented chunk type, so ignore it: skip over the data␊ |
991 | ␉␉␉if (!(in[pos + 0] & 32)) {␊ |
992 | ␉␉␉␉// error: unknown critical chunk (5th bit of first byte of chunk type is 0)␊ |
993 | ␉␉␉␉PNG_error = 69;␊ |
994 | ␉␉␉␉return NULL;␊ |
995 | ␉␉␉}␊ |
996 | ␉␉␉pos += (chunkLength + 4); // skip 4 letters and uninterpreted data of unimplemented chunk␊ |
997 | ␉␉␉known_type = false;␊ |
998 | ␉␉}␊ |
999 | ␉␉pos += 4; // step over CRC (which is ignored)␊ |
1000 | ␉}␊ |
1001 | ␉uint32_t bpp = PNG_getBpp(info);␊ |
1002 | ␉vector8_t *scanlines; // now the out buffer will be filled␊ |
1003 | ␉scanlines = vector8_new(((info->width * (info->height * bpp + 7)) / 8) + info->height, 0);␊ |
1004 | ␉PNG_error = Zlib_decompress(scanlines, idat);␊ |
1005 | ␉if (PNG_error)␊ |
1006 | ␉␉return NULL; // stop if the zlib decompressor returned an error␊ |
1007 | ␉size_t bytewidth = (bpp + 7) / 8, outlength = (info->height * info->width * bpp + 7) / 8;␊ |
1008 | ␉vector8_resize(info->image, outlength); // time to fill the out buffer␊ |
1009 | ␉uint8_t *out_data = outlength ? info->image->data : 0;␊ |
1010 | ␉if (info->interlaceMethod == 0) { // no interlace, just filter␊ |
1011 | ␉␉size_t y, obp, bp;␊ |
1012 | ␉␉size_t linestart, linelength;␊ |
1013 | ␉␉linestart = 0;␊ |
1014 | ␉␉// length in bytes of a scanline, excluding the filtertype byte␊ |
1015 | ␉␉linelength = (info->width * bpp + 7) / 8;␊ |
1016 | ␉␉if (bpp >= 8) // byte per byte␊ |
1017 | ␉␉␉for (y = 0; y < info->height; y++) {␊ |
1018 | ␉␉␉␉uint32_t filterType = scanlines->data[linestart];␊ |
1019 | ␉␉␉␉const uint8_t *prevline;␊ |
1020 | ␉␉␉␉prevline = (y == 0) ? 0 : &out_data[(y - 1) * info->width * bytewidth];␊ |
1021 | ␉␉␉␉PNG_unFilterScanline(&out_data[linestart - y], &scanlines->data[linestart + 1],␊ |
1022 | ␉␉␉␉␉␉prevline, bytewidth, filterType, linelength);␊ |
1023 | ␉␉␉␉if (PNG_error)␊ |
1024 | ␉␉␉␉␉return NULL;␊ |
1025 | ␉␉␉␉linestart += (1 + linelength); // go to start of next scanline␊ |
1026 | ␉␉} else { // less than 8 bits per pixel, so fill it up bit per bit␊ |
1027 | ␉␉␉vector8_t *templine; // only used if bpp < 8␊ |
1028 | ␉␉␉templine = vector8_new((info->width * bpp + 7) >> 3, 0);␊ |
1029 | ␉␉␉for (y = 0, obp = 0; y < info->height; y++) {␊ |
1030 | ␉␉␉␉uint32_t filterType = scanlines->data[linestart];␊ |
1031 | ␉␉␉␉const uint8_t *prevline;␊ |
1032 | ␉␉␉␉prevline = (y == 0) ? 0 : &out_data[(y - 1) * info->width * bytewidth];␊ |
1033 | ␉␉␉␉PNG_unFilterScanline(templine->data, &scanlines->data[linestart + 1], prevline,␊ |
1034 | ␉␉␉␉␉␉bytewidth, filterType, linelength);␊ |
1035 | ␉␉␉␉if (PNG_error)␊ |
1036 | ␉␉␉␉␉return NULL;␊ |
1037 | ␉␉␉␉for (bp = 0; bp < info->width * bpp;)␊ |
1038 | ␉␉␉␉␉PNG_setBitOfReversedStream(&obp, out_data, PNG_readBitFromReversedStream(&bp,␊ |
1039 | ␉␉␉␉␉␉␉templine->data));␊ |
1040 | ␉␉␉␉linestart += (1 + linelength); // go to start of next scanline␊ |
1041 | ␉␉␉}␊ |
1042 | ␉␉}␊ |
1043 | ␉} else { // interlaceMethod is 1 (Adam7)␊ |
1044 | ␉␉int i;␊ |
1045 | ␉␉size_t passw[7] = {␊ |
1046 | ␉␉␉(info->width + 7) / 8, (info->width + 3) / 8, (info->width + 3) / 4,␊ |
1047 | ␉␉␉(info->width + 1) / 4, (info->width + 1) / 2, (info->width + 0) / 2,␊ |
1048 | ␉␉␉(info->width + 0) / 1␊ |
1049 | ␉␉};␊ |
1050 | ␉␉size_t passh[7] = {␊ |
1051 | ␉␉␉(info->height + 7) / 8, (info->height + 7) / 8, (info->height + 3) / 8,␊ |
1052 | ␉␉␉(info->height + 3) / 4, (info->height + 1) / 4, (info->height + 1) / 2,␊ |
1053 | ␉␉␉(info->height + 0) / 2␊ |
1054 | ␉␉};␊ |
1055 | ␉␉size_t passstart[7] = { 0 };␊ |
1056 | ␉␉size_t pattern[28] = { 0, 4, 0, 2, 0, 1, 0, 0, 0, 4, 0, 2, 0, 1, 8, 8, 4, 4, 2, 2, 1, 8, 8,␊ |
1057 | ␉␉␉␉8, 4, 4, 2, 2 }; // values for the adam7 passes␊ |
1058 | ␉␉for (i = 0; i < 6; i++)␊ |
1059 | ␉␉␉passstart[i + 1] = passstart[i] + passh[i] * ((passw[i] ? 1 : 0) + (passw[i] * bpp + 7) / 8);␊ |
1060 | ␉␉vector8_t *scanlineo, *scanlinen; // "old" and "new" scanline␊ |
1061 | ␉␉scanlineo = vector8_new((info->width * bpp + 7) / 8, 0);␊ |
1062 | ␉␉scanlinen = vector8_new((info->width * bpp + 7) / 8, 0);␊ |
1063 | ␉␉for (i = 0; i < 7; i++)␊ |
1064 | ␉␉␉PNG_adam7Pass(out_data, scanlinen->data, scanlineo->data, &scanlines->data[passstart[i]],␊ |
1065 | ␉␉␉␉␉info->width, pattern[i], pattern[i + 7], pattern[i + 14], pattern[i + 21],␊ |
1066 | ␉␉␉␉␉passw[i], passh[i], bpp);␊ |
1067 | ␉}␊ |
1068 | ␉if (info->colorType != 6 || info->bitDepth != 8) { // conversion needed␊ |
1069 | ␉␉vector8_t *copy = vector8_copy(info->image); // xxx: is this copy necessary?␊ |
1070 | ␉␉PNG_error = PNG_convert(info, info->image, copy->data);␊ |
1071 | ␉}␊ |
1072 | ␉return info;␊ |
1073 | }␊ |
1074 | ␊ |
1075 | /*************************************************************************************************/␊ |
1076 | ␊ |
1077 | #ifdef TEST␊ |
1078 | ␊ |
1079 | #include <stdio.h>␊ |
1080 | #include <sys/stat.h>␊ |
1081 | ␊ |
1082 | int main(int argc, char **argv)␊ |
1083 | {␊ |
1084 | ␉char *fname = (argc > 1) ? argv[1] : "test.png";␊ |
1085 | ␉PNG_info_t *info;␊ |
1086 | ␉struct stat statbuf;␊ |
1087 | ␉uint32_t insize, outsize;␊ |
1088 | ␉FILE *infp, *outfp;␊ |
1089 | ␉uint8_t *inbuf;␊ |
1090 | ␉uint32_t n;␊ |
1091 | ␊ |
1092 | ␉if (stat(fname, &statbuf) != 0) {␊ |
1093 | ␉␉perror("stat");␊ |
1094 | ␉␉return 1;␊ |
1095 | ␉} else if (!statbuf.st_size) {␊ |
1096 | ␉␉printf("file empty\n");␊ |
1097 | ␉␉return 1;␊ |
1098 | ␉}␊ |
1099 | ␉insize = (uint32_t) statbuf.st_size;␊ |
1100 | ␉inbuf = malloc(insize);␊ |
1101 | ␉infp = fopen(fname, "rb");␊ |
1102 | ␉if (!infp) {␊ |
1103 | ␉␉perror("fopen");␊ |
1104 | ␉␉return 1;␊ |
1105 | ␉} else if (fread(inbuf, 1, insize, infp) != insize) {␊ |
1106 | ␉␉perror("fread");␊ |
1107 | ␉␉return 1;␊ |
1108 | ␉}␊ |
1109 | ␉fclose(infp);␊ |
1110 | ␊ |
1111 | ␉printf("input file: %s (size: %d)\n", fname, insize);␊ |
1112 | ␊ |
1113 | ␉info = PNG_decode(inbuf, insize);␊ |
1114 | ␉free(inbuf);␊ |
1115 | ␉printf("PNG_error: %d\n", PNG_error);␊ |
1116 | ␉if (PNG_error != 0)␊ |
1117 | ␉␉return 1;␊ |
1118 | ␊ |
1119 | ␉printf("width: %d, height: %d\nfirst 16 bytes: ", info->width, info->height);␊ |
1120 | ␉for (n = 0; n < 16; n++)␊ |
1121 | ␉␉printf("%02x ", info->image->data[n]);␊ |
1122 | ␉printf("\n");␊ |
1123 | ␊ |
1124 | ␉outsize = info->width * info->height * 4;␊ |
1125 | ␉printf("image size: %d\n", outsize);␊ |
1126 | ␉if (outsize != info->image->size) {␊ |
1127 | ␉␉printf("error: image size doesn't match dimensions\n");␊ |
1128 | ␉␉return 1;␊ |
1129 | ␉}␊ |
1130 | ␉outfp = fopen("out.bin", "wb");␊ |
1131 | ␉if (!outfp) {␊ |
1132 | ␉␉perror("fopen");␊ |
1133 | ␉␉return 1;␊ |
1134 | ␉} else if (fwrite(info->image->data, 1, outsize, outfp) != outsize) {␊ |
1135 | ␉␉perror("fwrite");␊ |
1136 | ␉␉return 1;␊ |
1137 | ␉}␊ |
1138 | ␉fclose(outfp);␊ |
1139 | ␊ |
1140 | #ifdef ALLOC_DEBUG␊ |
1141 | ␉png_alloc_node_t *node;␊ |
1142 | ␉for (node = png_alloc_head, n = 1; node; node = node->next, n++)␊ |
1143 | ␉␉printf("node %d (%p) addr = %p, size = %ld\n", n, node, node->addr, node->size);␊ |
1144 | #endif␊ |
1145 | ␉png_alloc_free_all(); // also frees info and image data from PNG_decode␊ |
1146 | ␊ |
1147 | ␉return 0;␊ |
1148 | }␊ |
1149 | ␊ |
1150 | #endif␊ |
1151 |