1 /*
2 * pnglite.c - pnglite library
3 * For conditions of distribution and use, see copyright notice in pnglite.h
4 */
5
6 #ifdef _STANDALONE
7 #include <sys/cdefs.h>
8 #include <stand.h>
9 #else
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #endif
16 #include <zlib.h>
17 #include "pnglite.h"
18
19 #define abs(x) ((x) < 0? -(x):(x))
20
21 static size_t
22 file_read(png_t *png, void *out, size_t size, size_t numel)
23 {
24 size_t result;
25
26 if (!out) {
27 result = lseek(png->fd, (off_t)(size * numel), SEEK_CUR);
28 } else {
29 result = read(png->fd, out, size * numel);
30 }
31
32 return (result);
33 }
34
35 static int
36 file_read_ul(png_t *png, unsigned *out)
37 {
38 uint8_t buf[4];
39
40 if (file_read(png, buf, 1, 4) != 4)
41 return (PNG_FILE_ERROR);
42
43 *out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
44
45 return (PNG_NO_ERROR);
46 }
47
48 static unsigned get_ul(uint8_t *buf)
49 {
50 unsigned result;
51 uint8_t foo[4];
52
53 memcpy(foo, buf, 4);
54
55 result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
56
57 return (result);
58 }
59
60 static int png_get_bpp(png_t *png)
61 {
62 int bpp;
63
64 switch (png->color_type) {
65 case PNG_GREYSCALE:
66 bpp = 1; break;
67 case PNG_TRUECOLOR:
68 bpp = 3; break;
69 case PNG_INDEXED:
70 bpp = 1; break;
71 case PNG_GREYSCALE_ALPHA:
72 bpp = 2; break;
73 case PNG_TRUECOLOR_ALPHA:
74 bpp = 4; break;
75 default:
76 return (PNG_FILE_ERROR);
77 }
78
79 bpp *= png->depth/8;
80
81 return (bpp);
82 }
83
84 static int
85 png_read_ihdr(png_t *png)
86 {
87 unsigned length;
88 unsigned orig_crc;
89 unsigned calc_crc;
90 uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
91
92 file_read_ul(png, &length);
93
94 if (length != 13)
95 return (PNG_CRC_ERROR);
96
97 if (file_read(png, ihdr, 1, 13+4) != 13+4)
98 return (PNG_EOF_ERROR);
99
100 file_read_ul(png, &orig_crc);
101
102 calc_crc = crc32(0L, Z_NULL, 0);
103 calc_crc = crc32(calc_crc, ihdr, 13+4);
104
105 if (orig_crc != calc_crc) {
106 return (PNG_CRC_ERROR);
107 }
108
109 png->width = get_ul(ihdr+4);
110 png->height = get_ul(ihdr+8);
111 png->depth = ihdr[12];
112 png->color_type = ihdr[13];
113 png->compression_method = ihdr[14];
114 png->filter_method = ihdr[15];
115 png->interlace_method = ihdr[16];
116
117 if (png->color_type == PNG_INDEXED)
118 return (PNG_NOT_SUPPORTED);
119
120 if (png->depth != 8 && png->depth != 16)
121 return (PNG_NOT_SUPPORTED);
122
123 if (png->interlace_method)
124 return (PNG_NOT_SUPPORTED);
125
126 return (PNG_NO_ERROR);
127 }
128
129 void
130 png_print_info(png_t *png)
131 {
132 printf("PNG INFO:\n");
133 printf("\twidth:\t\t%d\n", png->width);
134 printf("\theight:\t\t%d\n", png->height);
135 printf("\tdepth:\t\t%d\n", png->depth);
136 printf("\tcolor:\t\t");
137
138 switch (png->color_type) {
139 case PNG_GREYSCALE:
140 printf("greyscale\n"); break;
141 case PNG_TRUECOLOR:
142 printf("truecolor\n"); break;
143 case PNG_INDEXED:
144 printf("palette\n"); break;
145 case PNG_GREYSCALE_ALPHA:
146 printf("greyscale with alpha\n"); break;
147 case PNG_TRUECOLOR_ALPHA:
148 printf("truecolor with alpha\n"); break;
149 default:
150 printf("unknown, this is not good\n"); break;
151 }
152
153 printf("\tcompression:\t%s\n",
154 png->compression_method?
155 "unknown, this is not good":"inflate/deflate");
156 printf("\tfilter:\t\t%s\n",
157 png->filter_method? "unknown, this is not good":"adaptive");
158 printf("\tinterlace:\t%s\n",
159 png->interlace_method? "interlace":"no interlace");
160 }
161
162 int
163 png_open(png_t *png, const char *filename)
164 {
165 char header[8];
166 int result;
167
168 png->image = NULL;
169 png->fd = open(filename, O_RDONLY);
170 if (png->fd == -1)
171 return (PNG_FILE_ERROR);
172
173 if (file_read(png, header, 1, 8) != 8) {
174 result = PNG_EOF_ERROR;
175 goto done;
176 }
177
178 if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
179 result = PNG_HEADER_ERROR;
180 goto done;
181 }
182
183 result = png_read_ihdr(png);
184
185 png->bpp = (uint8_t)png_get_bpp(png);
186
187 done:
188 if (result == PNG_NO_ERROR) {
189 png->image = malloc(png->width * png->height * png->bpp);
190 if (png->image == NULL)
191 result = PNG_MEMORY_ERROR;
192 }
193
194 if (result == PNG_NO_ERROR)
195 result = png_get_data(png, png->image);
196
197 if (result != PNG_NO_ERROR) {
198 free(png->image);
199 close(png->fd);
200 png->fd = -1;
201 return (result);
202 }
203
204 return (result);
205 }
206
207 int
208 png_close(png_t *png)
209 {
210 close(png->fd);
211 png->fd = -1;
212 free(png->image);
213 png->image = NULL;
214
215 return (PNG_NO_ERROR);
216 }
217
218 static int
219 png_init_inflate(png_t *png)
220 {
221 z_stream *stream;
222 png->zs = malloc(sizeof (z_stream));
223
224 stream = png->zs;
225
226 if (!stream)
227 return (PNG_MEMORY_ERROR);
228
229 memset(stream, 0, sizeof (z_stream));
230 if (inflateInit(stream) != Z_OK)
231 return (PNG_ZLIB_ERROR);
232
233 stream->next_out = png->png_data;
234 stream->avail_out = png->png_datalen;
235
236 return (PNG_NO_ERROR);
237 }
238
239 static int
240 png_end_inflate(png_t *png)
241 {
242 z_stream *stream = png->zs;
243
244 if (!stream)
245 return (PNG_MEMORY_ERROR);
246
247 if (inflateEnd(stream) != Z_OK) {
248 printf("ZLIB says: %s\n", stream->msg);
249 return (PNG_ZLIB_ERROR);
250 }
251
252 free(png->zs);
253
254 return (PNG_NO_ERROR);
255 }
256
257 static int
258 png_inflate(png_t *png, uint8_t *data, int len)
259 {
260 int result;
261 z_stream *stream = png->zs;
262
263 if (!stream)
264 return (PNG_MEMORY_ERROR);
265
266 stream->next_in = data;
267 stream->avail_in = len;
268
269 result = inflate(stream, Z_SYNC_FLUSH);
270
271 if (result != Z_STREAM_END && result != Z_OK) {
272 printf("%s\n", stream->msg);
273 return (PNG_ZLIB_ERROR);
274 }
275
276 if (stream->avail_in != 0)
277 return (PNG_ZLIB_ERROR);
278
279 return (PNG_NO_ERROR);
280 }
281
282 static int
283 png_read_idat(png_t *png, unsigned length)
284 {
285 unsigned orig_crc;
286 unsigned calc_crc;
287
288 if (!png->readbuf || png->readbuflen < length) {
289 if (png->readbuf)
290 free(png->readbuf);
291
292 png->readbuf = malloc(length);
293 png->readbuflen = length;
294 }
295
296 if (!png->readbuf)
297 return (PNG_MEMORY_ERROR);
298
299 if (file_read(png, png->readbuf, 1, length) != length)
300 return (PNG_FILE_ERROR);
301
302 calc_crc = crc32(0L, Z_NULL, 0);
303 calc_crc = crc32(calc_crc, (uint8_t *)"IDAT", 4);
304 calc_crc = crc32(calc_crc, (uint8_t *)png->readbuf, length);
305
306 file_read_ul(png, &orig_crc);
307
308 if (orig_crc != calc_crc)
309 return (PNG_CRC_ERROR);
310
311 return (png_inflate(png, png->readbuf, length));
312 }
313
314 static int
315 png_process_chunk(png_t *png)
316 {
317 int result = PNG_NO_ERROR;
318 unsigned type;
319 unsigned length;
320
321 file_read_ul(png, &length);
322
323 if (file_read(png, &type, 1, 4) != 4)
324 return (PNG_FILE_ERROR);
325
326 /*
327 * if we found an idat, all other idats should be followed with no
328 * other chunks in between
329 */
330 if (type == *(unsigned int *)"IDAT") {
331 if (!png->png_data) { /* first IDAT */
332 png->png_datalen = png->width * png->height *
333 png->bpp + png->height;
334 png->png_data = malloc(png->png_datalen);
335 }
336
337 if (!png->png_data)
338 return (PNG_MEMORY_ERROR);
339
340 if (!png->zs) {
341 result = png_init_inflate(png);
342 if (result != PNG_NO_ERROR)
343 return (result);
344 }
345
346 return (png_read_idat(png, length));
347 } else if (type == *(unsigned int *)"IEND")
348 return (PNG_DONE);
349 else
350 file_read(png, 0, 1, length + 4); /* unknown chunk */
351
352 return (result);
353 }
354
355 static void
356 png_filter_sub(int stride, uint8_t *in, uint8_t *out, int len)
357 {
358 int i;
359 uint8_t a = 0;
360
361 for (i = 0; i < len; i++) {
362 if (i >= stride)
363 a = out[i - stride];
364
365 out[i] = in[i] + a;
366 }
367 }
368
369 static void
370 png_filter_up(int stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
371 int len)
372 {
373 int i;
374
375 if (prev_line) {
376 for (i = 0; i < len; i++)
377 out[i] = in[i] + prev_line[i];
378 } else
379 memcpy(out, in, len);
380 }
381
382 static void
383 png_filter_average(int stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
384 int len)
385 {
386 int i;
387 uint8_t a = 0;
388 uint8_t b = 0;
389 unsigned int sum = 0;
390
391 for (i = 0; i < len; i++) {
392 if (prev_line)
393 b = prev_line[i];
394
395 if (i >= stride)
396 a = out[i - stride];
397
398 sum = a;
399 sum += b;
400
401 out[i] = (char)(in[i] + sum/2);
402 }
403 }
404
405 static uint8_t
406 png_paeth(uint8_t a, uint8_t b, uint8_t c)
407 {
408 int p = (int)a + b - c;
409 int pa = abs(p - a);
410 int pb = abs(p - b);
411 int pc = abs(p - c);
412
413 int pr;
414
415 if (pa <= pb && pa <= pc)
416 pr = a;
417 else if (pb <= pc)
418 pr = b;
419 else
420 pr = c;
421
422 return (pr);
423 }
424
425 static void
426 png_filter_paeth(int stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
427 int len)
428 {
429 int i;
430 uint8_t a;
431 uint8_t b;
432 uint8_t c;
433
434 for (i = 0; i < len; i++) {
435 if (prev_line && i >= stride) {
436 a = out[i - stride];
437 b = prev_line[i];
438 c = prev_line[i - stride];
439 } else {
440 if (prev_line)
441 b = prev_line[i];
442 else
443 b = 0;
444
445 if (i >= stride)
446 a = out[i - stride];
447 else
448 a = 0;
449
450 c = 0;
451 }
452
453 out[i] = in[i] + png_paeth(a, b, c);
454 }
455 }
456
457 static int
458 png_unfilter(png_t *png, uint8_t *data)
459 {
460 unsigned i;
461 unsigned pos = 0;
462 unsigned outpos = 0;
463 uint8_t *filtered = png->png_data;
464
465 int stride = png->bpp;
466
467 while (pos < png->png_datalen) {
468 uint8_t filter = filtered[pos];
469
470 pos++;
471
472 if (png->depth == 16) {
473 for (i = 0; i < png->width * stride; i += 2) {
474 *(short *)(filtered+pos+i) =
475 (filtered[pos+i] << 8) | filtered[pos+i+1];
476 }
477 }
478
479 switch (filter) {
480 case 0: /* none */
481 memcpy(data+outpos, filtered+pos, png->width * stride);
482 break;
483 case 1: /* sub */
484 png_filter_sub(stride, filtered+pos, data+outpos,
485 png->width * stride);
486 break;
487 case 2: /* up */
488 if (outpos) {
489 png_filter_up(stride, filtered+pos, data+outpos,
490 data + outpos - (png->width*stride),
491 png->width*stride);
492 } else {
493 png_filter_up(stride, filtered+pos, data+outpos,
494 0, png->width*stride);
495 }
496 break;
497 case 3: /* average */
498 if (outpos) {
499 png_filter_average(stride, filtered+pos,
500 data+outpos,
501 data + outpos - (png->width*stride),
502 png->width*stride);
503 } else {
504 png_filter_average(stride, filtered+pos,
505 data+outpos, 0, png->width*stride);
506 }
507 break;
508 case 4: /* paeth */
509 if (outpos) {
510 png_filter_paeth(stride, filtered+pos,
511 data+outpos,
512 data + outpos - (png->width*stride),
513 png->width*stride);
514 } else {
515 png_filter_paeth(stride, filtered+pos,
516 data+outpos, 0, png->width*stride);
517 }
518 break;
519 default:
520 return (PNG_UNKNOWN_FILTER);
521 }
522
523 outpos += png->width * stride;
524 pos += png->width * stride;
525 }
526
527 return (PNG_NO_ERROR);
528 }
529
530 int
531 png_get_data(png_t *png, uint8_t *data)
532 {
533 int result = PNG_NO_ERROR;
534
535 png->zs = NULL;
536 png->png_datalen = 0;
537 png->png_data = NULL;
538 png->readbuf = NULL;
539 png->readbuflen = 0;
540
541 while (result == PNG_NO_ERROR)
542 result = png_process_chunk(png);
543
544 if (png->readbuf) {
545 free(png->readbuf);
546 png->readbuflen = 0;
547 }
548 if (png->zs)
549 png_end_inflate(png);
550
551 if (result != PNG_DONE) {
552 free(png->png_data);
553 return (result);
554 }
555
556 result = png_unfilter(png, data);
557
558 free(png->png_data);
559
560 return (result);
561 }
562
563 char *
564 png_error_string(int error)
565 {
566 switch (error) {
567 case PNG_NO_ERROR:
568 return ("No error");
569 case PNG_FILE_ERROR:
570 return ("Unknown file error.");
571 case PNG_HEADER_ERROR:
572 return ("No PNG header found. Are you sure this is a PNG?");
573 case PNG_IO_ERROR:
574 return ("Failure while reading file.");
575 case PNG_EOF_ERROR:
576 return ("Reached end of file.");
577 case PNG_CRC_ERROR:
578 return ("CRC or chunk length error.");
579 case PNG_MEMORY_ERROR:
580 return ("Could not allocate memory.");
581 case PNG_ZLIB_ERROR:
582 return ("zlib reported an error.");
583 case PNG_UNKNOWN_FILTER:
584 return ("Unknown filter method used in scanline.");
585 case PNG_DONE:
586 return ("PNG done");
587 case PNG_NOT_SUPPORTED:
588 return ("The PNG is unsupported by pnglite, too bad for you!");
589 case PNG_WRONG_ARGUMENTS:
590 return ("Wrong combination of arguments passed to png_open. "
591 "You must use either a read_function or supply a file "
592 "pointer to use.");
593 default:
594 return ("Unknown error.");
595 };
596 }