Files: 1fe01596a886b6ef6fe6d2b10c5b11a6e40a23ba / lupng.c
31942 bytesRaw
1 | /* |
2 | * The MIT License (MIT) |
3 | * |
4 | * Copyright (c) 2014 Jan Solanti |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in all |
14 | * copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | * SOFTWARE. |
23 | */ |
24 | |
25 | #include <stdio.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | |
29 | #ifndef LUPNG_USE_ZLIB |
30 | #include <miniz.h> |
31 | #else |
32 | #include <zlib.h> |
33 | #endif |
34 | |
35 | #include "lupng.h" |
36 | |
37 | #define PNG_NONE 0 |
38 | #define PNG_IHDR 0x01 |
39 | #define PNG_PLTE 0x02 |
40 | #define PNG_IDAT 0x04 |
41 | #define PNG_IEND 0x08 |
42 | |
43 | #define PNG_GRAYSCALE 0 |
44 | #define PNG_TRUECOLOR 2 |
45 | /* 24bpp RGB palette */ |
46 | #define PNG_PALETTED 3 |
47 | #define PNG_GRAYSCALE_ALPHA 4 |
48 | #define PNG_TRUECOLOR_ALPHA 6 |
49 | |
50 | #define PNG_FILTER_NONE 0 |
51 | #define PNG_FILTER_SUB 1 |
52 | #define PNG_FILTER_UP 2 |
53 | #define PNG_FILTER_AVERAGE 3 |
54 | #define PNG_FILTER_PAETH 4 |
55 | |
56 | #define PNG_SIG_SIZE 8 |
57 | |
58 | #define PNG_DONE 1 |
59 | #define PNG_OK 0 |
60 | #define PNG_ERROR -1 |
61 | |
62 | #define BUF_SIZE 8192 |
63 | #define MAX(x, y) (x > y ? x : y) |
64 | |
65 | |
66 | |
67 | /******************************************************** |
68 | * CRC computation as per PNG spec |
69 | ********************************************************/ |
70 | |
71 | /* Precomputed table of CRCs of all 8-bit messages |
72 | using the polynomial from the PNG spec, 0xEDB88320L. */ |
73 | static const uint32_t crcTable[] = |
74 | { |
75 | 0x0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x76DC419, 0x706AF48F, |
76 | 0xE963A535, 0x9E6495A3, 0xEDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, |
77 | 0x9B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, |
78 | 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, |
79 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, |
80 | 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, |
81 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, |
82 | 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, |
83 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, |
84 | 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, |
85 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x1DB7106, |
86 | 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x6B6B51F, 0x9FBFE4A5, 0xE8B8D433, |
87 | 0x7807C9A2, 0xF00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x86D3D2D, |
88 | 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, |
89 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, |
90 | 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, |
91 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, |
92 | 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, |
93 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, |
94 | 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, |
95 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, |
96 | 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x3B6E20C, 0x74B1D29A, |
97 | 0xEAD54739, 0x9DD277AF, 0x4DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, |
98 | 0xD6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0xA00AE27, 0x7D079EB1, |
99 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, |
100 | 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, |
101 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, |
102 | 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, |
103 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, |
104 | 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, |
105 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, |
106 | 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, |
107 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x26D930A, 0x9C0906A9, 0xEB0E363F, |
108 | 0x72076785, 0x5005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0xCB61B38, |
109 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, 0x86D3D2D4, 0xF1D4E242, |
110 | 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, |
111 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, |
112 | 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, |
113 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, |
114 | 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, |
115 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, |
116 | 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, |
117 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D |
118 | }; |
119 | |
120 | /* Update a running CRC with the bytes buf[0..len-1]--the CRC |
121 | should be initialized to all 1's, and the transmitted value |
122 | is the 1's complement of the final running CRC (see the |
123 | crc() routine below)). */ |
124 | static uint32_t updateCrc(uint32_t crc, unsigned char *buf, |
125 | int len) |
126 | { |
127 | unsigned long c = crc; |
128 | int n; |
129 | |
130 | for (n = 0; n < len; n++) |
131 | c = crcTable[(c ^ buf[n]) & 0xFF] ^ (c >> 8); |
132 | |
133 | return c; |
134 | } |
135 | |
136 | /* Return the CRC of the bytes buf[0..len-1]. */ |
137 | static uint32_t crc(unsigned char *buf, int len) |
138 | { |
139 | return updateCrc(0xFFFFFFFFL, buf, len) ^ 0xFFFFFFFFL; |
140 | } |
141 | |
142 | |
143 | |
144 | /******************************************************** |
145 | * Helper structs |
146 | ********************************************************/ |
147 | |
148 | typedef struct |
149 | { |
150 | uint32_t length; |
151 | uint8_t *type; |
152 | uint8_t *data; |
153 | uint32_t crc; |
154 | } PngChunk; |
155 | |
156 | typedef struct { |
157 | void *userPtr; |
158 | PngReadProc readProc; |
159 | PngWriteProc writeProc; |
160 | int8_t chunksFound; |
161 | |
162 | /* IHDR info */ |
163 | uint32_t width; |
164 | uint32_t height; |
165 | uint8_t depth; |
166 | uint8_t colorType; |
167 | uint8_t channels; |
168 | uint8_t compression; |
169 | uint8_t filter; |
170 | uint8_t interlace; |
171 | |
172 | /* PLTE info */ |
173 | uint32_t paletteItems; |
174 | uint8_t *palette; |
175 | |
176 | /* fields used for (de)compression & (de-)filtering */ |
177 | z_stream stream; |
178 | size_t scanlineBytes; |
179 | int32_t currentCol; |
180 | int32_t currentRow; |
181 | uint32_t currentElem; |
182 | size_t currentByte; |
183 | int bytesPerPixel; |
184 | uint8_t *currentScanline; |
185 | uint8_t *previousScanline; |
186 | uint8_t currentFilter; |
187 | uint8_t interlacePass; |
188 | size_t compressedBytes; |
189 | |
190 | /* used for constructing 16 bit deep pixels */ |
191 | int tmpCount; |
192 | uint8_t tmpBytes[2]; |
193 | |
194 | /* the output image */ |
195 | LuImage *img; |
196 | } PngInfoStruct; |
197 | |
198 | /* PNG header: */ |
199 | static const uint8_t PNG_SIG[] = |
200 | /* P N G \r \n SUB \n */ |
201 | {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; |
202 | |
203 | static const int startingRow[] = { 0, 0, 0, 4, 0, 2, 0, 1 }; |
204 | static const int startingCol[] = { 0, 0, 4, 0, 2, 0, 1, 0 }; |
205 | static const int rowIncrement[] = { 1, 8, 8, 8, 4, 4, 2, 2 }; |
206 | static const int colIncrement[] = { 1, 8, 8, 4, 4, 2, 2, 1 }; |
207 | |
208 | |
209 | |
210 | /******************************************************** |
211 | * Helper functions |
212 | ********************************************************/ |
213 | |
214 | static inline void releaseChunk(PngChunk *chunk) |
215 | { |
216 | /* Only release chunk->type since chunk->data points to the same memory. */ |
217 | free((void *)chunk->type); |
218 | free((void *)chunk); |
219 | } |
220 | |
221 | static inline uint32_t swap32(uint32_t n) |
222 | { |
223 | union { |
224 | unsigned char np[4]; |
225 | uint32_t i; |
226 | } u; |
227 | u.i = n; |
228 | |
229 | return ((uint32_t)u.np[0] << 24) | |
230 | ((uint32_t)u.np[1] << 16) | |
231 | ((uint32_t)u.np[2] << 8) | |
232 | (uint32_t)u.np[3]; |
233 | } |
234 | |
235 | static inline uint16_t swap16(uint16_t n) |
236 | { |
237 | union { |
238 | unsigned char np[2]; |
239 | uint16_t i; |
240 | } u; |
241 | u.i = n; |
242 | |
243 | return ((uint16_t)u.np[0] << 8) | (uint16_t)u.np[1]; |
244 | } |
245 | |
246 | static int bytesEqual(const uint8_t *a, const uint8_t *b, size_t count) |
247 | { |
248 | size_t i; |
249 | for (i = 0; i < count; ++i) |
250 | { |
251 | if (*(a+i) != *(b+i)) |
252 | return 0; |
253 | } |
254 | |
255 | return 1; |
256 | } |
257 | |
258 | |
259 | |
260 | /******************************************************** |
261 | * Png filter functions |
262 | ********************************************************/ |
263 | static inline int absi(int val) |
264 | { |
265 | return val > 0 ? val : -val; |
266 | } |
267 | |
268 | static inline uint8_t raw(PngInfoStruct *info, int32_t col) |
269 | { |
270 | if (col < 0) |
271 | return 0; |
272 | return info->currentScanline[col]; |
273 | } |
274 | |
275 | static inline uint8_t prior(PngInfoStruct *info, int32_t col) |
276 | { |
277 | if (info->currentRow <= startingRow[info->interlacePass] || col < 0) |
278 | return 0; |
279 | return info->previousScanline[col]; |
280 | } |
281 | |
282 | static inline uint8_t paethPredictor(uint8_t a, uint8_t b, uint8_t c) |
283 | { |
284 | unsigned int A = a, B = b, C = c; |
285 | int p = (int)A + (int)B - (int)C; |
286 | int pa = absi(p - (int)A); |
287 | int pb = absi(p - (int)B); |
288 | int pc = absi(p - (int)C); |
289 | |
290 | if (pa <= pb && pa <= pc) |
291 | return a; |
292 | if (pb <= pc) |
293 | return b; |
294 | return c; |
295 | } |
296 | |
297 | static inline uint8_t deSub(PngInfoStruct *info, uint8_t filtered) |
298 | { |
299 | return filtered + raw(info, info->currentByte-info->bytesPerPixel); |
300 | } |
301 | |
302 | static inline uint8_t deUp(PngInfoStruct *info, uint8_t filtered) |
303 | { |
304 | return filtered + prior(info, info->currentByte); |
305 | } |
306 | |
307 | static inline uint8_t deAverage(PngInfoStruct *info, uint8_t filtered) |
308 | { |
309 | uint16_t avg = (uint16_t)(raw(info, info->currentByte-info->bytesPerPixel) |
310 | + prior(info, info->currentByte)); |
311 | avg >>= 1; |
312 | return filtered + avg; |
313 | } |
314 | |
315 | static inline uint8_t dePaeth(PngInfoStruct *info, uint8_t filtered) |
316 | { |
317 | return filtered + paethPredictor( |
318 | raw(info, info->currentByte-info->bytesPerPixel), |
319 | prior(info, info->currentByte), |
320 | prior(info, info->currentByte-info->bytesPerPixel)); |
321 | } |
322 | |
323 | static inline uint8_t none(PngInfoStruct *info) |
324 | { |
325 | return raw(info, info->currentByte); |
326 | } |
327 | |
328 | static inline uint8_t sub(PngInfoStruct *info) |
329 | { |
330 | return raw(info, info->currentByte) - raw(info, info->currentByte-info->bytesPerPixel); |
331 | } |
332 | |
333 | static inline uint8_t up(PngInfoStruct *info) |
334 | { |
335 | return raw(info, info->currentByte) - prior(info, info->currentByte); |
336 | } |
337 | |
338 | static inline uint8_t average(PngInfoStruct *info) |
339 | { |
340 | uint16_t avg = (uint16_t)(raw(info, info->currentByte-info->bytesPerPixel) |
341 | + prior(info, info->currentByte)); |
342 | avg >>= 1; |
343 | return raw(info, info->currentByte) - avg; |
344 | } |
345 | |
346 | static inline uint8_t paeth(PngInfoStruct *info) |
347 | { |
348 | return raw(info, info->currentByte) - paethPredictor( |
349 | raw(info, info->currentByte-info->bytesPerPixel), |
350 | prior(info, info->currentByte), |
351 | prior(info, info->currentByte-info->bytesPerPixel)); |
352 | } |
353 | |
354 | |
355 | |
356 | /******************************************************** |
357 | * Actual implementation |
358 | ********************************************************/ |
359 | static inline int parseIhdr(PngInfoStruct *info, PngChunk *chunk) |
360 | { |
361 | if (info->chunksFound) |
362 | { |
363 | printf("PNG: malformed PNG file!\n"); |
364 | return PNG_ERROR; |
365 | } |
366 | |
367 | info->chunksFound |= PNG_IHDR; |
368 | info->width = swap32(*(uint32_t *)chunk->data); |
369 | info->height = swap32(*((uint32_t *)chunk->data + 1)); |
370 | info->depth = *(chunk->data + 8); |
371 | info->colorType = *(chunk->data + 9); |
372 | info->compression = *(chunk->data + 10); |
373 | info->filter = *(chunk->data + 11); |
374 | info->interlace = *(chunk->data + 12); |
375 | |
376 | switch (info->colorType) |
377 | { |
378 | case PNG_GRAYSCALE: |
379 | info->channels = 1; |
380 | break; |
381 | case PNG_TRUECOLOR: |
382 | info->channels = 3; |
383 | break; |
384 | case PNG_PALETTED: |
385 | info->channels = 3; |
386 | break; |
387 | case PNG_GRAYSCALE_ALPHA: |
388 | info->channels = 2; |
389 | break; |
390 | case PNG_TRUECOLOR_ALPHA: |
391 | info->channels = 4; |
392 | break; |
393 | default: |
394 | printf("PNG: illegal color type: %i\n", |
395 | (unsigned int)info->colorType); |
396 | return PNG_ERROR; |
397 | break; |
398 | } |
399 | |
400 | if ((info->colorType != PNG_GRAYSCALE && info->colorType != PNG_PALETTED && |
401 | info->depth < 8) || |
402 | (info->colorType == PNG_PALETTED && info->depth == 16) || |
403 | info->depth > 16) |
404 | { |
405 | printf("PNG: illegal bit depth for color type\n"); |
406 | return PNG_ERROR; |
407 | } |
408 | |
409 | if (info->compression) |
410 | { |
411 | printf("PNG: unknown compression method: %i\n", |
412 | (unsigned int)info->compression); |
413 | return PNG_ERROR; |
414 | } |
415 | |
416 | if (info->filter) |
417 | { |
418 | printf("PNG: unknown filter scheme: %i\n", |
419 | (unsigned int)info->filter); |
420 | return PNG_ERROR; |
421 | } |
422 | |
423 | memset(&(info->stream), 0, sizeof(info->stream)); |
424 | if(inflateInit(&(info->stream)) != Z_OK) |
425 | { |
426 | printf("PNG: inflateInit failed!\n"); |
427 | return PNG_ERROR; |
428 | } |
429 | info->img = luImageCreate(info->width, info->height, |
430 | info->channels, info->depth < 16 ? 8 : 16); |
431 | info->scanlineBytes = MAX((info->width * info->channels * info->depth) >> 3, 1); |
432 | info->currentScanline = (uint8_t *)malloc(info->scanlineBytes); |
433 | info->previousScanline = (uint8_t *)malloc(info->scanlineBytes); |
434 | info->currentCol = -1; |
435 | info->interlacePass = info->interlace ? 1 : 0; |
436 | info->bytesPerPixel = MAX((info->channels * info->depth) >> 3, 1); |
437 | if (!info->img || !info->currentScanline || !info->previousScanline) |
438 | { |
439 | printf("PNG: memory allocation failed!\n"); |
440 | return PNG_ERROR; |
441 | } |
442 | |
443 | return PNG_OK; |
444 | } |
445 | |
446 | static inline int parsePlte(PngInfoStruct *info, PngChunk *chunk) |
447 | { |
448 | if (info->chunksFound & PNG_IDAT || !(info->chunksFound & PNG_IHDR)) |
449 | { |
450 | printf("PNG: malformed PNG file!\n"); |
451 | return PNG_ERROR; |
452 | } |
453 | |
454 | info->chunksFound |= PNG_PLTE; |
455 | |
456 | if (chunk->length % 3 != 0) |
457 | { |
458 | printf("PNG: invalid palette size!\n"); |
459 | return PNG_ERROR; |
460 | } |
461 | |
462 | info->paletteItems = chunk->length/3; |
463 | info->palette = (uint8_t *)malloc(chunk->length); |
464 | if (!info->palette) |
465 | { |
466 | printf("PNG: memory allocation failed!\n"); |
467 | return PNG_ERROR; |
468 | } |
469 | memcpy(info->palette, chunk->data, chunk->length); |
470 | |
471 | return PNG_OK; |
472 | } |
473 | |
474 | static inline void stretchBits(uint8_t inByte, uint8_t outBytes[8], int depth) |
475 | { |
476 | int i; |
477 | switch (depth) { |
478 | case 1: |
479 | for (i = 0; i < 8; ++i) |
480 | outBytes[i] = (inByte >> (7-i)) & 0x01; |
481 | break; |
482 | |
483 | case 2: |
484 | outBytes[0] = (inByte >> 6) & 0x03; |
485 | outBytes[1] = (inByte >> 4) & 0x03; |
486 | outBytes[2] = (inByte >> 2) & 0x03; |
487 | outBytes[3] = inByte & 0x03; |
488 | break; |
489 | |
490 | case 4: |
491 | outBytes[0] = (inByte >> 4) & 0x0F; |
492 | outBytes[1] = inByte & 0x0F; |
493 | break; |
494 | |
495 | default: |
496 | break; |
497 | } |
498 | } |
499 | |
500 | /* returns: 1 if at end of scanline, 0 otherwise */ |
501 | static inline int insertByte(PngInfoStruct *info, uint8_t byte) |
502 | { |
503 | int advance = 0; |
504 | const uint8_t scale[] = {0x00, 0xFF, 0x55, 0x00, 0x11, 0x00, 0x00, 0x00}; |
505 | |
506 | /* for paletted images currentElem will always be 0 */ |
507 | size_t idx = info->currentRow * info->width * info->channels |
508 | + info->currentCol * info->channels |
509 | + info->currentElem; |
510 | |
511 | if (info->colorType != PNG_PALETTED) |
512 | { |
513 | if (info->depth == 8) |
514 | info->img->data[idx] = byte; |
515 | |
516 | else if (info->depth < 8) |
517 | info->img->data[idx] = byte * scale[info->depth]; |
518 | |
519 | else /* depth == 16 */ |
520 | { |
521 | info->tmpBytes[info->tmpCount] = byte; |
522 | if (info->tmpCount) /* just inserted 2nd byte */ |
523 | { |
524 | uint16_t val = *(uint16_t *)info->tmpBytes; |
525 | val = swap16(val); |
526 | info->tmpCount = 0; |
527 | |
528 | ((uint16_t *)(info->img->data))[idx] = val; |
529 | } |
530 | else |
531 | { |
532 | ++info->tmpCount; |
533 | return 0; |
534 | } |
535 | } |
536 | |
537 | ++info->currentElem; |
538 | if (info->currentElem >= info->channels) |
539 | { |
540 | advance = 1; |
541 | info->currentElem = 0; |
542 | } |
543 | } |
544 | else |
545 | { |
546 | /* The spec limits palette size to 256 entries */ |
547 | if (byte < info->paletteItems) |
548 | { |
549 | info->img->data[idx ] = info->palette[3*byte ]; |
550 | info->img->data[idx+1] = info->palette[3*byte+1]; |
551 | info->img->data[idx+2] = info->palette[3*byte+2]; |
552 | } |
553 | else |
554 | { |
555 | printf("PNG: invalid palette index encountered!\n"); |
556 | } |
557 | advance = 1; |
558 | } |
559 | |
560 | if (advance) |
561 | { |
562 | /* advance to next pixel */ |
563 | info->currentCol += colIncrement[info->interlacePass]; |
564 | |
565 | if (info->currentCol >= info->width) |
566 | { |
567 | uint8_t *tmp = info->currentScanline; |
568 | info->currentScanline = info->previousScanline; |
569 | info->previousScanline = tmp; |
570 | |
571 | info->currentCol = -1; |
572 | info->currentByte = 0; |
573 | |
574 | info->currentRow += rowIncrement[info->interlacePass]; |
575 | if (info->currentRow >= info->height && info->interlace) |
576 | { |
577 | ++info->interlacePass; |
578 | while (startingCol[info->interlacePass] >= info->width || |
579 | startingRow[info->interlacePass] >= info->height) |
580 | ++info->interlacePass; |
581 | info->currentRow = startingRow[info->interlacePass]; |
582 | } |
583 | return 1; |
584 | } |
585 | } |
586 | |
587 | return 0; |
588 | } |
589 | |
590 | static inline int parseIdat(PngInfoStruct *info, PngChunk *chunk) |
591 | { |
592 | unsigned char filtered[BUF_SIZE]; |
593 | int status = Z_OK; |
594 | |
595 | if (!(info->chunksFound & PNG_IHDR)) |
596 | { |
597 | printf("PNG: malformed PNG file!\n"); |
598 | return PNG_ERROR; |
599 | } |
600 | |
601 | if (info->colorType == PNG_PALETTED && !(info->chunksFound & PNG_PLTE)) |
602 | { |
603 | printf("PNG: palette required but missing!\n"); |
604 | return PNG_ERROR; |
605 | } |
606 | |
607 | info->chunksFound |= PNG_IDAT; |
608 | info->stream.next_in = (unsigned char *)chunk->data; |
609 | info->stream.avail_in = chunk->length; |
610 | do |
611 | { |
612 | info->stream.next_out = filtered; |
613 | info->stream.avail_out = BUF_SIZE; |
614 | status = inflate(&(info->stream), Z_NO_FLUSH); |
615 | size_t decompressed = BUF_SIZE - info->stream.avail_out; |
616 | size_t i; |
617 | |
618 | if (status != Z_OK && |
619 | status != Z_STREAM_END && |
620 | status != Z_BUF_ERROR && |
621 | status != Z_NEED_DICT) |
622 | { |
623 | printf("PNG: inflate error!\n"); |
624 | return PNG_ERROR; |
625 | } |
626 | |
627 | for (i = 0; i < decompressed; ++i) |
628 | { |
629 | if (info->currentCol < 0) |
630 | { |
631 | info->currentCol = startingCol[info->interlacePass]; |
632 | info->currentFilter = filtered[i]; |
633 | } |
634 | else |
635 | { |
636 | uint8_t rawByte = 0; |
637 | uint8_t fullBytes[8] = {0}; |
638 | switch (info->currentFilter) |
639 | { |
640 | case PNG_FILTER_NONE: |
641 | rawByte = filtered[i]; |
642 | break; |
643 | case PNG_FILTER_SUB: |
644 | rawByte = deSub(info, filtered[i]); |
645 | break; |
646 | case PNG_FILTER_UP: |
647 | rawByte = deUp(info, filtered[i]); |
648 | break; |
649 | case PNG_FILTER_AVERAGE: |
650 | rawByte = deAverage(info, filtered[i]); |
651 | break; |
652 | case PNG_FILTER_PAETH: |
653 | rawByte = dePaeth(info, filtered[i]); |
654 | break; |
655 | default: |
656 | break; |
657 | } |
658 | |
659 | info->currentScanline[info->currentByte] = rawByte; |
660 | ++info->currentByte; |
661 | |
662 | if (info->depth < 8) |
663 | { |
664 | int j; |
665 | stretchBits(rawByte, fullBytes, info->depth); |
666 | for (j = 0; j < 8/info->depth; ++j) |
667 | if(insertByte(info, fullBytes[j])) |
668 | break; |
669 | } |
670 | else |
671 | insertByte(info, rawByte); |
672 | } |
673 | } |
674 | } while ((info->stream.avail_in > 0 || info->stream.avail_out == 0)); |
675 | |
676 | return PNG_OK; |
677 | } |
678 | |
679 | static inline PngChunk *readChunk(PngInfoStruct *info) |
680 | { |
681 | PngChunk *chunk = (PngChunk *)malloc(sizeof(PngChunk)); |
682 | size_t read = 0; |
683 | if (!chunk) |
684 | { |
685 | printf("PNG: memory allocation failed!\n"); |
686 | return 0; |
687 | } |
688 | |
689 | info->readProc((void *)&chunk->length, 4, 1, info->userPtr); |
690 | chunk->length = swap32(chunk->length); |
691 | chunk->type = (uint8_t *)malloc(chunk->length + 4); |
692 | chunk->data = chunk->type + 4; |
693 | |
694 | info->readProc((void *)chunk->type, 1, chunk->length + 4, info->userPtr); |
695 | read = info->readProc((void *)&chunk->crc, 4, 1, info->userPtr); |
696 | chunk->crc = swap32(chunk->crc); |
697 | |
698 | if (read != 1) |
699 | { |
700 | printf("PNG: read error\n"); |
701 | releaseChunk(chunk); |
702 | return 0; |
703 | } |
704 | |
705 | if (crc(chunk->type, chunk->length+4) != chunk->crc) |
706 | { |
707 | printf("PNG: CRC mismatch in \'%.4s\'\n", (char *)chunk->type); |
708 | releaseChunk(chunk); |
709 | return 0; |
710 | } |
711 | |
712 | return chunk; |
713 | } |
714 | |
715 | static inline int handleChunk(PngInfoStruct *info, PngChunk *chunk) |
716 | { |
717 | /* critical chunk */ |
718 | if (!(chunk->type[0] & 0x20)) |
719 | { |
720 | if (bytesEqual(chunk->type, (const uint8_t *)"IHDR", 4)) |
721 | return parseIhdr(info, chunk); |
722 | if (bytesEqual(chunk->type, (const uint8_t *)"PLTE", 4)) |
723 | return parsePlte(info, chunk); |
724 | if (bytesEqual(chunk->type, (const uint8_t *)"IDAT", 4)) |
725 | return parseIdat(info, chunk); |
726 | if (bytesEqual(chunk->type, (const uint8_t *)"IEND", 4)) |
727 | { |
728 | info->chunksFound |= PNG_IEND; |
729 | if (!(info->chunksFound & PNG_IDAT)) |
730 | { |
731 | printf("PNG: no IDAT chunk found\n"); |
732 | return PNG_ERROR; |
733 | } |
734 | return PNG_DONE; |
735 | } |
736 | } |
737 | /* ignore ancillary chunks for now */ |
738 | |
739 | return PNG_OK; |
740 | } |
741 | |
742 | LuImage *luPngRead(PngReadProc readProc, void *userPtr) |
743 | { |
744 | PngInfoStruct info = |
745 | { |
746 | userPtr, |
747 | readProc, |
748 | 0, |
749 | PNG_NONE |
750 | }; |
751 | uint8_t signature[PNG_SIG_SIZE]; |
752 | int status = PNG_ERROR; |
753 | |
754 | info.readProc((void *)signature, 1, PNG_SIG_SIZE, userPtr); |
755 | if (bytesEqual(signature, PNG_SIG, PNG_SIG_SIZE)) |
756 | { |
757 | PngChunk *chunk; |
758 | while ((chunk = readChunk(&info))) |
759 | { |
760 | status = handleChunk(&info, chunk); |
761 | releaseChunk(chunk); |
762 | |
763 | if (status != PNG_OK) |
764 | break; |
765 | } |
766 | } |
767 | else |
768 | printf("PNG: invalid header\n"); |
769 | |
770 | if (info.currentScanline) |
771 | free((void *)info.currentScanline); |
772 | if (info.previousScanline) |
773 | free((void *)info.previousScanline); |
774 | if (info.palette) |
775 | free((void *)info.palette); |
776 | inflateEnd(&info.stream); |
777 | |
778 | if (status == PNG_DONE) |
779 | return info.img; |
780 | else |
781 | if (info.img) |
782 | luImageRelease(info.img); |
783 | |
784 | return 0; |
785 | } |
786 | |
787 | static inline int writeIhdr(PngInfoStruct *info) |
788 | { |
789 | static uint8_t buf[17]; |
790 | static const uint8_t colorType[] = { |
791 | PNG_GRAYSCALE, |
792 | PNG_GRAYSCALE_ALPHA, |
793 | PNG_TRUECOLOR, |
794 | PNG_TRUECOLOR_ALPHA |
795 | }; |
796 | size_t written = 0; |
797 | PngChunk c; |
798 | |
799 | if (info->img->channels > 4) |
800 | { |
801 | printf("PNG: too many channels in image\n"); |
802 | return PNG_ERROR; |
803 | } |
804 | |
805 | c.length = swap32(13); |
806 | c.type = buf; // 4 (type) + 4 + 4 + 5x1 |
807 | c.data = c.type + 4; |
808 | |
809 | memcpy((void *)c.type, (void *)"IHDR", 4); |
810 | *(uint32_t *)(c.data) = swap32((uint32_t)info->img->width); |
811 | *(uint32_t *)(c.data + 4) = swap32((uint32_t)info->img->height); |
812 | *(c.data + 8) = info->img->depth; |
813 | *(c.data + 9) = colorType[info->img->channels-1]; |
814 | *(c.data + 10) = 0; // compression method |
815 | *(c.data + 11) = 0; // filter method |
816 | *(c.data + 12) = 0; // interlace method: none |
817 | |
818 | c.crc = swap32(crc(c.type, 17)); |
819 | |
820 | written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
821 | written += info->writeProc((void *)c.type, 1, 4, info->userPtr); |
822 | written += info->writeProc((void *)c.data, 1, 13, info->userPtr); |
823 | written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
824 | |
825 | if (written != 25) |
826 | { |
827 | printf("PNG: write error\n"); |
828 | return PNG_ERROR; |
829 | } |
830 | |
831 | return PNG_OK; |
832 | } |
833 | |
834 | static inline int writeIdat(PngInfoStruct *info, uint8_t *buf, size_t buflen) |
835 | { |
836 | size_t written = 0; |
837 | PngChunk c; |
838 | |
839 | c.length = swap32(buflen-4); |
840 | c.crc = swap32(crc(buf, buflen)); |
841 | |
842 | written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
843 | written += info->writeProc((void *)buf, 1, buflen, info->userPtr); |
844 | written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
845 | |
846 | if (written != buflen+8) |
847 | { |
848 | printf("PNG: write error\n"); |
849 | return PNG_ERROR; |
850 | } |
851 | |
852 | return PNG_OK; |
853 | } |
854 | |
855 | static inline void advanceBytep(PngInfoStruct *info, int is16bit) |
856 | { |
857 | if (is16bit) |
858 | { |
859 | if (info->currentByte%2) |
860 | --info->currentByte; |
861 | else |
862 | info->currentByte+=3; |
863 | } |
864 | else |
865 | ++info->currentByte; |
866 | } |
867 | |
868 | static inline size_t filterScanline(PngInfoStruct *info, |
869 | uint8_t(*f)(PngInfoStruct *info), |
870 | uint8_t filter, |
871 | uint8_t *filterCandidate, |
872 | int is16bit) |
873 | { |
874 | size_t curSum = 0; |
875 | filterCandidate[0] = filter; |
876 | size_t fc; |
877 | for (info->currentByte = is16bit ? 1 : 0, fc = 1; |
878 | info->currentByte < info->scanlineBytes; ++fc, advanceBytep(info, is16bit) ) |
879 | { |
880 | uint8_t val = f(info); |
881 | filterCandidate[fc] = val; |
882 | curSum += val; |
883 | } |
884 | |
885 | return curSum; |
886 | } |
887 | |
888 | /* |
889 | * Processes the input image and calls writeIdat for every BUF_SIZE compressed |
890 | * bytes. |
891 | */ |
892 | static inline int processPixels(PngInfoStruct *info) |
893 | { |
894 | uint8_t idatBuf[BUF_SIZE+4] = {'I', 'D', 'A', 'T'}; |
895 | uint8_t *compressed = idatBuf+4; |
896 | uint8_t *filterCandidate = (uint8_t *)malloc(info->scanlineBytes+1); |
897 | uint8_t *bestCandidate = (uint8_t *)malloc(info->scanlineBytes+1); |
898 | size_t minSum = (size_t)-1, curSum = 0; |
899 | int status = Z_OK; |
900 | int is16bit = info->img->depth == 16; |
901 | |
902 | if (!filterCandidate || !bestCandidate) |
903 | { |
904 | printf("PNG: memory allocation failed!\n"); |
905 | } |
906 | |
907 | memset(&(info->stream), 0, sizeof(info->stream)); |
908 | if(deflateInit(&(info->stream), Z_DEFAULT_COMPRESSION) != Z_OK) |
909 | { |
910 | printf("PNG: deflateInit failed!\n"); |
911 | free(filterCandidate); |
912 | free(bestCandidate); |
913 | return PNG_ERROR; |
914 | } |
915 | |
916 | info->stream.avail_out = BUF_SIZE; |
917 | info->stream.next_out = compressed; |
918 | |
919 | for (info->currentRow = 0; info->currentRow < info->img->height; |
920 | ++info->currentRow) |
921 | { |
922 | int flush = (info->currentRow < info->img->height-1) ? |
923 | Z_NO_FLUSH : Z_FINISH; |
924 | minSum = (size_t)-1; |
925 | |
926 | /* |
927 | * 1st time it doesn't matter, the filters never look at the previous |
928 | * scanline when processing row 0. And next time it'll be valid. |
929 | */ |
930 | info->previousScanline = info->currentScanline; |
931 | info->currentScanline = info->img->data + (info->currentRow*info->scanlineBytes); |
932 | |
933 | /* |
934 | * Try to choose the best filter for each scanline. |
935 | * Breaks in case of overflow, but hey it's just a heuristic. |
936 | */ |
937 | for (info->currentFilter = PNG_FILTER_NONE; info->currentFilter <= PNG_FILTER_PAETH; ++info->currentFilter) |
938 | { |
939 | |
940 | switch (info->currentFilter) |
941 | { |
942 | case PNG_FILTER_NONE: |
943 | curSum = filterScanline(info, none, PNG_FILTER_NONE, filterCandidate, is16bit); |
944 | break; |
945 | |
946 | case PNG_FILTER_SUB: |
947 | curSum = filterScanline(info, sub, PNG_FILTER_SUB, filterCandidate, is16bit); |
948 | break; |
949 | |
950 | case PNG_FILTER_UP: |
951 | curSum = filterScanline(info, up, PNG_FILTER_UP, filterCandidate, is16bit); |
952 | break; |
953 | |
954 | case PNG_FILTER_AVERAGE: |
955 | curSum = filterScanline(info, average, PNG_FILTER_AVERAGE, filterCandidate, is16bit); |
956 | break; |
957 | |
958 | case PNG_FILTER_PAETH: |
959 | curSum = filterScanline(info, paeth, PNG_FILTER_PAETH, filterCandidate, is16bit); |
960 | break; |
961 | |
962 | default: |
963 | break; |
964 | } |
965 | |
966 | if (curSum < minSum || !info->currentFilter) |
967 | { |
968 | uint8_t *tmp = bestCandidate; |
969 | bestCandidate = filterCandidate; |
970 | filterCandidate = tmp; |
971 | minSum = curSum; |
972 | } |
973 | } |
974 | |
975 | info->stream.avail_in = info->scanlineBytes+1; |
976 | info->stream.next_in = bestCandidate; |
977 | |
978 | // compress bestCandidate |
979 | do |
980 | { |
981 | status = deflate(&info->stream, flush); |
982 | |
983 | if (info->stream.avail_out < BUF_SIZE) |
984 | { |
985 | writeIdat(info, idatBuf, BUF_SIZE-info->stream.avail_out+4); |
986 | info->stream.next_out = compressed; |
987 | info->stream.avail_out = BUF_SIZE; |
988 | } |
989 | } while ((flush == Z_FINISH && status != Z_STREAM_END) |
990 | || (flush == Z_NO_FLUSH && info->stream.avail_in)); |
991 | // TODO: fix loop conditions, apparently data gets truncated |
992 | } |
993 | |
994 | return PNG_OK; |
995 | } |
996 | |
997 | static inline int writeIend(PngInfoStruct *info) |
998 | { |
999 | PngChunk c = { 0, (uint8_t *)"IEND", 0, 0 }; |
1000 | size_t written = 0; |
1001 | c.crc = swap32(crc(c.type, 4)); |
1002 | |
1003 | written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
1004 | written += info->writeProc((void *)c.type, 1, 4, info->userPtr); |
1005 | written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
1006 | |
1007 | if (written != 12) |
1008 | { |
1009 | printf("PNG: write error\n"); |
1010 | return PNG_ERROR; |
1011 | } |
1012 | |
1013 | return PNG_OK; |
1014 | } |
1015 | |
1016 | int luPngWrite(PngWriteProc writeProc, void *userPtr, LuImage *img) |
1017 | { |
1018 | PngInfoStruct info = { |
1019 | userPtr, |
1020 | 0, |
1021 | writeProc, |
1022 | PNG_NONE |
1023 | }; |
1024 | |
1025 | info.img = img; |
1026 | info.bytesPerPixel = (info.img->channels * info.img->depth) >> 3; |
1027 | |
1028 | if (writeProc((void *)PNG_SIG, 1, PNG_SIG_SIZE, userPtr) != PNG_SIG_SIZE) |
1029 | { |
1030 | printf("PNG: write error\n"); |
1031 | return PNG_ERROR; |
1032 | } |
1033 | |
1034 | if (writeIhdr(&info) != PNG_OK) |
1035 | return PNG_ERROR; |
1036 | |
1037 | info.scanlineBytes = (img->depth >> 3) * img->channels * img->width; |
1038 | if (processPixels(&info) != PNG_OK) |
1039 | { |
1040 | deflateEnd(&(info.stream)); |
1041 | return PNG_ERROR; |
1042 | } |
1043 | |
1044 | deflateEnd(&(info.stream)); |
1045 | return writeIend(&info); |
1046 | } |
1047 | |
1048 | |
1049 | void luImageRelease(LuImage *img) |
1050 | { |
1051 | free((void *)img->data); |
1052 | free((void *)img); |
1053 | } |
1054 | |
1055 | LuImage *luImageCreate(size_t width, size_t height, uint8_t channels, uint8_t depth) |
1056 | { |
1057 | LuImage *img; |
1058 | |
1059 | if (depth != 8 && depth != 16) |
1060 | { |
1061 | printf("Image: only bit depths 8 and 16 are supported!\n"); |
1062 | return 0; |
1063 | } |
1064 | |
1065 | img = (LuImage *)malloc(sizeof(LuImage)); |
1066 | if (!img) |
1067 | return 0; |
1068 | |
1069 | img->width = width; |
1070 | img->height = height; |
1071 | img->channels = channels; |
1072 | img->depth = depth; |
1073 | img->dataSize = (size_t)((depth >> 3) * width * height * channels); |
1074 | img->data = (uint8_t *)malloc(img->dataSize); |
1075 | |
1076 | return img; |
1077 | } |
1078 |
Built with git-ssb-web