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