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
34static ssize_t
35file_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
52static int
53file_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
65static unsigned
66get_ul(uint8_t *buf)
67{
68	return (ntohl(*(uint32_t *)buf));
69}
70
71static int
72png_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
96static int
97png_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
143void
144png_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
176int
177png_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
206done:
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
229int
230png_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
240static int
241png_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
263static int
264png_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
283static int
284png_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
308static int
309png_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
339static int
340png_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
381static void
382png_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
395static void
396png_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
408static void
409png_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
431static uint8_t
432png_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
451static void
452png_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
483static int
484png_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
555int
556png_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
588char *
589png_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