xref: /freebsd/contrib/pnglite/pnglite.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
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 	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
66 get_ul(uint8_t *buf)
67 {
68 	return (ntohl(*(uint32_t *)buf));
69 }
70 
71 static int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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