git ssb

1+

cel / pngspark



Tree: 5fdd8756359433583b7e3aea100e9cf6a2b4436b

Files: 5fdd8756359433583b7e3aea100e9cf6a2b4436b / 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. */
73static 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)). */
124static 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]. */
137static 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
148typedef struct
149{
150 uint32_t length;
151 uint8_t *type;
152 uint8_t *data;
153 uint32_t crc;
154} PngChunk;
155
156typedef 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: */
199static const uint8_t PNG_SIG[] =
200/* P N G \r \n SUB \n */
201{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
202
203static const int startingRow[] = { 0, 0, 0, 4, 0, 2, 0, 1 };
204static const int startingCol[] = { 0, 0, 4, 0, 2, 0, 1, 0 };
205static const int rowIncrement[] = { 1, 8, 8, 8, 4, 4, 2, 2 };
206static const int colIncrement[] = { 1, 8, 8, 4, 4, 2, 2, 1 };
207
208
209
210/********************************************************
211 * Helper functions
212 ********************************************************/
213
214static 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
221static 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
235static 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
246static 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 ********************************************************/
263static inline int absi(int val)
264{
265 return val > 0 ? val : -val;
266}
267
268static inline uint8_t raw(PngInfoStruct *info, int32_t col)
269{
270 if (col < 0)
271 return 0;
272 return info->currentScanline[col];
273}
274
275static 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
282static 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
297static inline uint8_t deSub(PngInfoStruct *info, uint8_t filtered)
298{
299 return filtered + raw(info, info->currentByte-info->bytesPerPixel);
300}
301
302static inline uint8_t deUp(PngInfoStruct *info, uint8_t filtered)
303{
304 return filtered + prior(info, info->currentByte);
305}
306
307static 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
315static 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
323static inline uint8_t none(PngInfoStruct *info)
324{
325 return raw(info, info->currentByte);
326}
327
328static inline uint8_t sub(PngInfoStruct *info)
329{
330 return raw(info, info->currentByte) - raw(info, info->currentByte-info->bytesPerPixel);
331}
332
333static inline uint8_t up(PngInfoStruct *info)
334{
335 return raw(info, info->currentByte) - prior(info, info->currentByte);
336}
337
338static 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
346static 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 ********************************************************/
359static 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
446static 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
474static 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 */
501static 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
590static 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
679static 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
715static 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
742LuImage *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
787static 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
834static 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
855static 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
868static 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 */
892static 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
997static 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
1016int 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
1049void luImageRelease(LuImage *img)
1050{
1051 free((void *)img->data);
1052 free((void *)img);
1053}
1054
1055LuImage *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