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