xref: /illumos-gate/usr/src/common/pnglite/pnglite.c (revision f7e4f33f27df122daef98e5b4b537dc159597da5)
19890ff83SToomas Soome /*
29890ff83SToomas Soome  * pnglite.c - pnglite library
39890ff83SToomas Soome  * For conditions of distribution and use, see copyright notice in pnglite.h
49890ff83SToomas Soome  */
59890ff83SToomas Soome 
69890ff83SToomas Soome #ifdef _STANDALONE
79890ff83SToomas Soome #include <sys/cdefs.h>
89890ff83SToomas Soome #include <stand.h>
99890ff83SToomas Soome #else
109890ff83SToomas Soome #include <stdio.h>
119890ff83SToomas Soome #include <stdlib.h>
129890ff83SToomas Soome #include <sys/types.h>
139890ff83SToomas Soome #include <sys/stat.h>
149890ff83SToomas Soome #include <fcntl.h>
159890ff83SToomas Soome #endif
169890ff83SToomas Soome #include <zlib.h>
179890ff83SToomas Soome #include "pnglite.h"
189890ff83SToomas Soome 
199890ff83SToomas Soome #ifndef abs
209890ff83SToomas Soome #define	abs(x)	((x) < 0? -(x):(x))
219890ff83SToomas Soome #endif
229890ff83SToomas Soome 
239890ff83SToomas Soome #define	PNG_32b(b, s) ((uint32_t)(b) << (s))
249890ff83SToomas Soome #define	PNG_U32(b1, b2, b3, b4) \
259890ff83SToomas Soome 	(PNG_32b(b1, 24) | PNG_32b(b2, 16) | PNG_32b(b3, 8) | PNG_32b(b4, 0))
269890ff83SToomas Soome 
279890ff83SToomas Soome #define	png_IDAT PNG_U32(73,  68,  65,  84)
289890ff83SToomas Soome #define	png_IEND PNG_U32(73,  69,  78,  68)
299890ff83SToomas Soome 
309890ff83SToomas Soome static ssize_t
file_read(png_t * png,void * out,size_t size,size_t numel)319890ff83SToomas Soome file_read(png_t *png, void *out, size_t size, size_t numel)
329890ff83SToomas Soome {
339890ff83SToomas Soome 	ssize_t result;
349890ff83SToomas Soome 	off_t offset = (off_t)(size * numel);
359890ff83SToomas Soome 
369890ff83SToomas Soome 	if (offset < 0)
379890ff83SToomas Soome 		return (PNG_FILE_ERROR);
389890ff83SToomas Soome 
399890ff83SToomas Soome 	if (!out) {
409890ff83SToomas Soome 		result = lseek(png->fd, offset, SEEK_CUR);
419890ff83SToomas Soome 	} else {
429890ff83SToomas Soome 		result = read(png->fd, out, size * numel);
439890ff83SToomas Soome 	}
449890ff83SToomas Soome 
459890ff83SToomas Soome 	return (result);
469890ff83SToomas Soome }
479890ff83SToomas Soome 
489890ff83SToomas Soome static int
file_read_ul(png_t * png,unsigned * out)499890ff83SToomas Soome file_read_ul(png_t *png, unsigned *out)
509890ff83SToomas Soome {
519890ff83SToomas Soome 	uint8_t buf[4];
529890ff83SToomas Soome 
539890ff83SToomas Soome 	if (file_read(png, buf, 1, 4) != 4)
549890ff83SToomas Soome 		return (PNG_FILE_ERROR);
559890ff83SToomas Soome 
569890ff83SToomas Soome 	*out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
579890ff83SToomas Soome 
589890ff83SToomas Soome 	return (PNG_NO_ERROR);
599890ff83SToomas Soome }
609890ff83SToomas Soome 
619890ff83SToomas Soome static unsigned
get_ul(uint8_t * buf)629890ff83SToomas Soome get_ul(uint8_t *buf)
639890ff83SToomas Soome {
649890ff83SToomas Soome 	unsigned result;
659890ff83SToomas Soome 	uint8_t foo[4];
669890ff83SToomas Soome 
679890ff83SToomas Soome 	memcpy(foo, buf, 4);
689890ff83SToomas Soome 
699890ff83SToomas Soome 	result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
709890ff83SToomas Soome 
719890ff83SToomas Soome 	return (result);
729890ff83SToomas Soome }
739890ff83SToomas Soome 
749890ff83SToomas Soome static int
png_get_bpp(png_t * png)759890ff83SToomas Soome png_get_bpp(png_t *png)
769890ff83SToomas Soome {
779890ff83SToomas Soome 	int bpp;
789890ff83SToomas Soome 
799890ff83SToomas Soome 	switch (png->color_type) {
809890ff83SToomas Soome 	case PNG_GREYSCALE:
819890ff83SToomas Soome 		bpp = 1; break;
829890ff83SToomas Soome 	case PNG_TRUECOLOR:
839890ff83SToomas Soome 		bpp = 3; break;
849890ff83SToomas Soome 	case PNG_INDEXED:
859890ff83SToomas Soome 		bpp = 1; break;
869890ff83SToomas Soome 	case PNG_GREYSCALE_ALPHA:
879890ff83SToomas Soome 		bpp = 2; break;
889890ff83SToomas Soome 	case PNG_TRUECOLOR_ALPHA:
899890ff83SToomas Soome 		bpp = 4; break;
909890ff83SToomas Soome 	default:
919890ff83SToomas Soome 		return (PNG_FILE_ERROR);
929890ff83SToomas Soome 	}
939890ff83SToomas Soome 
949890ff83SToomas Soome 	bpp *= png->depth / 8;
959890ff83SToomas Soome 
969890ff83SToomas Soome 	return (bpp);
979890ff83SToomas Soome }
989890ff83SToomas Soome 
999890ff83SToomas Soome static int
png_read_ihdr(png_t * png)1009890ff83SToomas Soome png_read_ihdr(png_t *png)
1019890ff83SToomas Soome {
102*f7e4f33fSToomas Soome 	unsigned length = 0;
1039890ff83SToomas Soome 	unsigned orig_crc;
1049890ff83SToomas Soome 	unsigned calc_crc;
1059890ff83SToomas Soome 	uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
1069890ff83SToomas Soome 
107*f7e4f33fSToomas Soome 	if (file_read_ul(png, &length) != PNG_NO_ERROR)
108*f7e4f33fSToomas Soome 		return (PNG_FILE_ERROR);
1099890ff83SToomas Soome 
1109890ff83SToomas Soome 	if (length != 13)
1119890ff83SToomas Soome 		return (PNG_CRC_ERROR);
1129890ff83SToomas Soome 
1139890ff83SToomas Soome 	if (file_read(png, ihdr, 1, 13+4) != 13+4)
1149890ff83SToomas Soome 		return (PNG_EOF_ERROR);
1159890ff83SToomas Soome 
116*f7e4f33fSToomas Soome 	if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
117*f7e4f33fSToomas Soome 		return (PNG_FILE_ERROR);
1189890ff83SToomas Soome 
1199890ff83SToomas Soome 	calc_crc = crc32(0L, Z_NULL, 0);
1209890ff83SToomas Soome 	calc_crc = crc32(calc_crc, ihdr, 13+4);
1219890ff83SToomas Soome 
1229890ff83SToomas Soome 	if (orig_crc != calc_crc) {
1239890ff83SToomas Soome 		return (PNG_CRC_ERROR);
1249890ff83SToomas Soome 	}
1259890ff83SToomas Soome 
1269890ff83SToomas Soome 	png->width = get_ul(ihdr+4);
1279890ff83SToomas Soome 	png->height = get_ul(ihdr+8);
1289890ff83SToomas Soome 	png->depth = ihdr[12];
1299890ff83SToomas Soome 	png->color_type = ihdr[13];
1309890ff83SToomas Soome 	png->compression_method = ihdr[14];
1319890ff83SToomas Soome 	png->filter_method = ihdr[15];
1329890ff83SToomas Soome 	png->interlace_method = ihdr[16];
1339890ff83SToomas Soome 
1349890ff83SToomas Soome 	if (png->color_type == PNG_INDEXED)
1359890ff83SToomas Soome 		return (PNG_NOT_SUPPORTED);
1369890ff83SToomas Soome 
1379890ff83SToomas Soome 	if (png->depth != 8 && png->depth != 16)
1389890ff83SToomas Soome 		return (PNG_NOT_SUPPORTED);
1399890ff83SToomas Soome 
1409890ff83SToomas Soome 	if (png->interlace_method)
1419890ff83SToomas Soome 		return (PNG_NOT_SUPPORTED);
1429890ff83SToomas Soome 
1439890ff83SToomas Soome 	return (PNG_NO_ERROR);
1449890ff83SToomas Soome }
1459890ff83SToomas Soome 
1469890ff83SToomas Soome void
png_print_info(png_t * png)1479890ff83SToomas Soome png_print_info(png_t *png)
1489890ff83SToomas Soome {
1499890ff83SToomas Soome 	printf("PNG INFO:\n");
1509890ff83SToomas Soome 	printf("\twidth:\t\t%d\n", png->width);
1519890ff83SToomas Soome 	printf("\theight:\t\t%d\n", png->height);
1529890ff83SToomas Soome 	printf("\tdepth:\t\t%d\n", png->depth);
1539890ff83SToomas Soome 	printf("\tcolor:\t\t");
1549890ff83SToomas Soome 
1559890ff83SToomas Soome 	switch (png->color_type) {
1569890ff83SToomas Soome 	case PNG_GREYSCALE:
1579890ff83SToomas Soome 		printf("greyscale\n"); break;
1589890ff83SToomas Soome 	case PNG_TRUECOLOR:
1599890ff83SToomas Soome 		printf("truecolor\n"); break;
1609890ff83SToomas Soome 	case PNG_INDEXED:
1619890ff83SToomas Soome 		printf("palette\n"); break;
1629890ff83SToomas Soome 	case PNG_GREYSCALE_ALPHA:
1639890ff83SToomas Soome 		printf("greyscale with alpha\n"); break;
1649890ff83SToomas Soome 	case PNG_TRUECOLOR_ALPHA:
1659890ff83SToomas Soome 		printf("truecolor with alpha\n"); break;
1669890ff83SToomas Soome 	default:
1679890ff83SToomas Soome 		printf("unknown, this is not good\n"); break;
1689890ff83SToomas Soome 	}
1699890ff83SToomas Soome 
1709890ff83SToomas Soome 	printf("\tcompression:\t%s\n",
1719890ff83SToomas Soome 	    png->compression_method?
1729890ff83SToomas Soome 	    "unknown, this is not good":"inflate/deflate");
1739890ff83SToomas Soome 	printf("\tfilter:\t\t%s\n",
1749890ff83SToomas Soome 	    png->filter_method? "unknown, this is not good":"adaptive");
1759890ff83SToomas Soome 	printf("\tinterlace:\t%s\n",
1769890ff83SToomas Soome 	    png->interlace_method? "interlace":"no interlace");
1779890ff83SToomas Soome }
1789890ff83SToomas Soome 
1799890ff83SToomas Soome int
png_open(png_t * png,const char * filename)1809890ff83SToomas Soome png_open(png_t *png, const char *filename)
1819890ff83SToomas Soome {
1829890ff83SToomas Soome 	char header[8];
1839890ff83SToomas Soome 	int result;
1849890ff83SToomas Soome 
1859890ff83SToomas Soome 	png->image = NULL;
1869890ff83SToomas Soome 	png->fd = open(filename, O_RDONLY);
1879890ff83SToomas Soome 	if (png->fd == -1)
1889890ff83SToomas Soome 		return (PNG_FILE_ERROR);
1899890ff83SToomas Soome 
1909890ff83SToomas Soome 	if (file_read(png, header, 1, 8) != 8) {
1919890ff83SToomas Soome 		result = PNG_EOF_ERROR;
1929890ff83SToomas Soome 		goto done;
1939890ff83SToomas Soome 	}
1949890ff83SToomas Soome 
1959890ff83SToomas Soome 	if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
1969890ff83SToomas Soome 		result = PNG_HEADER_ERROR;
1979890ff83SToomas Soome 		goto done;
1989890ff83SToomas Soome 	}
1999890ff83SToomas Soome 
2009890ff83SToomas Soome 	result = png_read_ihdr(png);
2019890ff83SToomas Soome 	if (result == PNG_NO_ERROR) {
2029890ff83SToomas Soome 		result = png_get_bpp(png);
2039890ff83SToomas Soome 		if (result > 0) {
2049890ff83SToomas Soome 			png->bpp = (uint8_t)result;
2059890ff83SToomas Soome 			result = PNG_NO_ERROR;
2069890ff83SToomas Soome 		}
2079890ff83SToomas Soome 	}
2089890ff83SToomas Soome 
2099890ff83SToomas Soome done:
2109890ff83SToomas Soome 	if (result == PNG_NO_ERROR) {
2119890ff83SToomas Soome 		uint64_t size = png->width * png->height * png->bpp;
2129890ff83SToomas Soome 
2139890ff83SToomas Soome 		if (size < UINT_MAX)
2149890ff83SToomas Soome 			png->image = malloc(size);
2159890ff83SToomas Soome 		if (png->image == NULL)
2169890ff83SToomas Soome 			result = PNG_MEMORY_ERROR;
2179890ff83SToomas Soome 	}
2189890ff83SToomas Soome 
2199890ff83SToomas Soome 	if (result == PNG_NO_ERROR)
2209890ff83SToomas Soome 		result = png_get_data(png, png->image);
2219890ff83SToomas Soome 
2229890ff83SToomas Soome 	if (result != PNG_NO_ERROR) {
2239890ff83SToomas Soome 		free(png->image);
224*f7e4f33fSToomas Soome 		(void) close(png->fd);
2259890ff83SToomas Soome 		png->fd = -1;
2269890ff83SToomas Soome 		return (result);
2279890ff83SToomas Soome 	}
2289890ff83SToomas Soome 
2299890ff83SToomas Soome 	return (result);
2309890ff83SToomas Soome }
2319890ff83SToomas Soome 
2329890ff83SToomas Soome int
png_close(png_t * png)2339890ff83SToomas Soome png_close(png_t *png)
2349890ff83SToomas Soome {
235*f7e4f33fSToomas Soome 	(void) close(png->fd);
2369890ff83SToomas Soome 	png->fd = -1;
2379890ff83SToomas Soome 	free(png->image);
2389890ff83SToomas Soome 	png->image = NULL;
2399890ff83SToomas Soome 
2409890ff83SToomas Soome 	return (PNG_NO_ERROR);
2419890ff83SToomas Soome }
2429890ff83SToomas Soome 
2439890ff83SToomas Soome static int
png_init_inflate(png_t * png)2449890ff83SToomas Soome png_init_inflate(png_t *png)
2459890ff83SToomas Soome {
2469890ff83SToomas Soome 	z_stream *stream;
2479890ff83SToomas Soome 	png->zs = calloc(1, sizeof (z_stream));
2489890ff83SToomas Soome 
2499890ff83SToomas Soome 	stream = png->zs;
2509890ff83SToomas Soome 
2519890ff83SToomas Soome 	if (!stream)
2529890ff83SToomas Soome 		return (PNG_MEMORY_ERROR);
2539890ff83SToomas Soome 
2549890ff83SToomas Soome 	if (inflateInit(stream) != Z_OK) {
2559890ff83SToomas Soome 		free(png->zs);
2569890ff83SToomas Soome 		png->zs = NULL;
2579890ff83SToomas Soome 		return (PNG_ZLIB_ERROR);
2589890ff83SToomas Soome 	}
2599890ff83SToomas Soome 
2609890ff83SToomas Soome 	stream->next_out = png->png_data;
2619890ff83SToomas Soome 	stream->avail_out = png->png_datalen;
2629890ff83SToomas Soome 
2639890ff83SToomas Soome 	return (PNG_NO_ERROR);
2649890ff83SToomas Soome }
2659890ff83SToomas Soome 
2669890ff83SToomas Soome static int
png_end_inflate(png_t * png)2679890ff83SToomas Soome png_end_inflate(png_t *png)
2689890ff83SToomas Soome {
2699890ff83SToomas Soome 	z_stream *stream = png->zs;
2709890ff83SToomas Soome 	int rc = PNG_NO_ERROR;
2719890ff83SToomas Soome 
2729890ff83SToomas Soome 	if (!stream)
2739890ff83SToomas Soome 		return (PNG_MEMORY_ERROR);
2749890ff83SToomas Soome 
2759890ff83SToomas Soome 	if (inflateEnd(stream) != Z_OK) {
2769890ff83SToomas Soome 		printf("ZLIB says: %s\n", stream->msg);
2779890ff83SToomas Soome 		rc = PNG_ZLIB_ERROR;
2789890ff83SToomas Soome 	}
2799890ff83SToomas Soome 
2809890ff83SToomas Soome 	free(png->zs);
2819890ff83SToomas Soome 	png->zs = NULL;
2829890ff83SToomas Soome 
2839890ff83SToomas Soome 	return (rc);
2849890ff83SToomas Soome }
2859890ff83SToomas Soome 
2869890ff83SToomas Soome static int
png_inflate(png_t * png,uint8_t * data,int len)2879890ff83SToomas Soome png_inflate(png_t *png, uint8_t *data, int len)
2889890ff83SToomas Soome {
2899890ff83SToomas Soome 	int result;
2909890ff83SToomas Soome 	z_stream *stream = png->zs;
2919890ff83SToomas Soome 
2929890ff83SToomas Soome 	if (!stream)
2939890ff83SToomas Soome 		return (PNG_MEMORY_ERROR);
2949890ff83SToomas Soome 
2959890ff83SToomas Soome 	stream->next_in = data;
2969890ff83SToomas Soome 	stream->avail_in = len;
2979890ff83SToomas Soome 
2989890ff83SToomas Soome 	result = inflate(stream, Z_SYNC_FLUSH);
2999890ff83SToomas Soome 
3009890ff83SToomas Soome 	if (result != Z_STREAM_END && result != Z_OK) {
3019890ff83SToomas Soome 		printf("%s\n", stream->msg);
3029890ff83SToomas Soome 		return (PNG_ZLIB_ERROR);
3039890ff83SToomas Soome 	}
3049890ff83SToomas Soome 
3059890ff83SToomas Soome 	if (stream->avail_in != 0)
3069890ff83SToomas Soome 		return (PNG_ZLIB_ERROR);
3079890ff83SToomas Soome 
3089890ff83SToomas Soome 	return (PNG_NO_ERROR);
3099890ff83SToomas Soome }
3109890ff83SToomas Soome 
3119890ff83SToomas Soome static int
png_read_idat(png_t * png,unsigned length)3129890ff83SToomas Soome png_read_idat(png_t *png, unsigned length)
3139890ff83SToomas Soome {
3149890ff83SToomas Soome 	unsigned orig_crc;
3159890ff83SToomas Soome 	unsigned calc_crc;
3169890ff83SToomas Soome 	ssize_t len = length;
3179890ff83SToomas Soome 
3189890ff83SToomas Soome 	if (!png->readbuf || png->readbuflen < length) {
3199890ff83SToomas Soome 		png->readbuf = realloc(png->readbuf, length);
3209890ff83SToomas Soome 		png->readbuflen = length;
3219890ff83SToomas Soome 	}
3229890ff83SToomas Soome 
3239890ff83SToomas Soome 	if (!png->readbuf)
3249890ff83SToomas Soome 		return (PNG_MEMORY_ERROR);
3259890ff83SToomas Soome 
3269890ff83SToomas Soome 	if (file_read(png, png->readbuf, 1, length) != len)
3279890ff83SToomas Soome 		return (PNG_FILE_ERROR);
3289890ff83SToomas Soome 
3299890ff83SToomas Soome 	calc_crc = crc32(0L, Z_NULL, 0);
3309890ff83SToomas Soome 	calc_crc = crc32(calc_crc, (uint8_t *)"IDAT", 4);
3319890ff83SToomas Soome 	calc_crc = crc32(calc_crc, (uint8_t *)png->readbuf, length);
3329890ff83SToomas Soome 
333*f7e4f33fSToomas Soome 	if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
334*f7e4f33fSToomas Soome 		return (PNG_FILE_ERROR);
3359890ff83SToomas Soome 
3369890ff83SToomas Soome 	if (orig_crc != calc_crc)
3379890ff83SToomas Soome 		return (PNG_CRC_ERROR);
3389890ff83SToomas Soome 
3399890ff83SToomas Soome 	return (png_inflate(png, png->readbuf, length));
3409890ff83SToomas Soome }
3419890ff83SToomas Soome 
3429890ff83SToomas Soome static int
png_process_chunk(png_t * png)3439890ff83SToomas Soome png_process_chunk(png_t *png)
3449890ff83SToomas Soome {
3459890ff83SToomas Soome 	int result = PNG_NO_ERROR;
3469890ff83SToomas Soome 	unsigned type;
3479890ff83SToomas Soome 	unsigned length;
3489890ff83SToomas Soome 
349*f7e4f33fSToomas Soome 	if (file_read_ul(png, &length) != PNG_NO_ERROR)
350*f7e4f33fSToomas Soome 		return (PNG_FILE_ERROR);
3519890ff83SToomas Soome 
352*f7e4f33fSToomas Soome 	if (file_read_ul(png, &type) != PNG_NO_ERROR)
3539890ff83SToomas Soome 		return (PNG_FILE_ERROR);
3549890ff83SToomas Soome 
3559890ff83SToomas Soome 	/*
3569890ff83SToomas Soome 	 * if we found an idat, all other idats should be followed with no
3579890ff83SToomas Soome 	 * other chunks in between
3589890ff83SToomas Soome 	 */
3599890ff83SToomas Soome 	if (type == png_IDAT) {
3609890ff83SToomas Soome 		if (!png->png_data) {	/* first IDAT */
3619890ff83SToomas Soome 			png->png_datalen = png->width * png->height *
3629890ff83SToomas Soome 			    png->bpp + png->height;
3639890ff83SToomas Soome 			png->png_data = malloc(png->png_datalen);
3649890ff83SToomas Soome 		}
3659890ff83SToomas Soome 
3669890ff83SToomas Soome 		if (!png->png_data)
3679890ff83SToomas Soome 			return (PNG_MEMORY_ERROR);
3689890ff83SToomas Soome 
3699890ff83SToomas Soome 		if (!png->zs) {
3709890ff83SToomas Soome 			result = png_init_inflate(png);
3719890ff83SToomas Soome 			if (result != PNG_NO_ERROR)
3729890ff83SToomas Soome 				return (result);
3739890ff83SToomas Soome 		}
3749890ff83SToomas Soome 
3759890ff83SToomas Soome 		return (png_read_idat(png, length));
3769890ff83SToomas Soome 	} else if (type == png_IEND)
3779890ff83SToomas Soome 		return (PNG_DONE);
3789890ff83SToomas Soome 	else
379*f7e4f33fSToomas Soome 		(void) file_read(png, 0, 1, length + 4); /* unknown chunk */
3809890ff83SToomas Soome 
3819890ff83SToomas Soome 	return (result);
3829890ff83SToomas Soome }
3839890ff83SToomas Soome 
3849890ff83SToomas Soome static void
png_filter_sub(unsigned stride,uint8_t * in,uint8_t * out,unsigned len)3859890ff83SToomas Soome png_filter_sub(unsigned stride, uint8_t *in, uint8_t *out, unsigned len)
3869890ff83SToomas Soome {
3879890ff83SToomas Soome 	unsigned i;
3889890ff83SToomas Soome 	uint8_t a = 0;
3899890ff83SToomas Soome 
3909890ff83SToomas Soome 	for (i = 0; i < len; i++) {
3919890ff83SToomas Soome 		if (i >= stride)
3929890ff83SToomas Soome 			a = out[i - stride];
3939890ff83SToomas Soome 
3949890ff83SToomas Soome 		out[i] = in[i] + a;
3959890ff83SToomas Soome 	}
3969890ff83SToomas Soome }
3979890ff83SToomas Soome 
3989890ff83SToomas Soome static void
png_filter_up(unsigned stride __unused,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)3999890ff83SToomas Soome png_filter_up(unsigned stride __unused, uint8_t *in, uint8_t *out,
4009890ff83SToomas Soome     uint8_t *prev_line, unsigned len)
4019890ff83SToomas Soome {
4029890ff83SToomas Soome 	unsigned i;
4039890ff83SToomas Soome 
4049890ff83SToomas Soome 	if (prev_line) {
4059890ff83SToomas Soome 		for (i = 0; i < len; i++)
4069890ff83SToomas Soome 			out[i] = in[i] + prev_line[i];
4079890ff83SToomas Soome 	} else
4089890ff83SToomas Soome 		memcpy(out, in, len);
4099890ff83SToomas Soome }
4109890ff83SToomas Soome 
4119890ff83SToomas Soome static void
png_filter_average(unsigned stride,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)4129890ff83SToomas Soome png_filter_average(unsigned stride, uint8_t *in, uint8_t *out,
4139890ff83SToomas Soome     uint8_t *prev_line, unsigned len)
4149890ff83SToomas Soome {
4159890ff83SToomas Soome 	unsigned int i;
4169890ff83SToomas Soome 	uint8_t a = 0;
4179890ff83SToomas Soome 	uint8_t b = 0;
4189890ff83SToomas Soome 	unsigned int sum = 0;
4199890ff83SToomas Soome 
4209890ff83SToomas Soome 	for (i = 0; i < len; i++) {
4219890ff83SToomas Soome 		if (prev_line)
4229890ff83SToomas Soome 			b = prev_line[i];
4239890ff83SToomas Soome 
4249890ff83SToomas Soome 		if (i >= stride)
4259890ff83SToomas Soome 			a = out[i - stride];
4269890ff83SToomas Soome 
4279890ff83SToomas Soome 		sum = a;
4289890ff83SToomas Soome 		sum += b;
4299890ff83SToomas Soome 
4309890ff83SToomas Soome 		out[i] = in[i] + sum/2;
4319890ff83SToomas Soome 	}
4329890ff83SToomas Soome }
4339890ff83SToomas Soome 
4349890ff83SToomas Soome static uint8_t
png_paeth(uint8_t a,uint8_t b,uint8_t c)4359890ff83SToomas Soome png_paeth(uint8_t a, uint8_t b, uint8_t c)
4369890ff83SToomas Soome {
4379890ff83SToomas Soome 	int p = (int)a + b - c;
4389890ff83SToomas Soome 	int pa = abs(p - a);
4399890ff83SToomas Soome 	int pb = abs(p - b);
4409890ff83SToomas Soome 	int pc = abs(p - c);
4419890ff83SToomas Soome 
4429890ff83SToomas Soome 	int pr;
4439890ff83SToomas Soome 
4449890ff83SToomas Soome 	if (pa <= pb && pa <= pc)
4459890ff83SToomas Soome 		pr = a;
4469890ff83SToomas Soome 	else if (pb <= pc)
4479890ff83SToomas Soome 		pr = b;
4489890ff83SToomas Soome 	else
4499890ff83SToomas Soome 		pr = c;
4509890ff83SToomas Soome 
4519890ff83SToomas Soome 	return (pr);
4529890ff83SToomas Soome }
4539890ff83SToomas Soome 
4549890ff83SToomas Soome static void
png_filter_paeth(unsigned stride,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)4559890ff83SToomas Soome png_filter_paeth(unsigned stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
4569890ff83SToomas Soome     unsigned len)
4579890ff83SToomas Soome {
4589890ff83SToomas Soome 	unsigned i;
4599890ff83SToomas Soome 	uint8_t a;
4609890ff83SToomas Soome 	uint8_t b;
4619890ff83SToomas Soome 	uint8_t c;
4629890ff83SToomas Soome 
4639890ff83SToomas Soome 	for (i = 0; i < len; i++) {
4649890ff83SToomas Soome 		if (prev_line && i >= stride) {
4659890ff83SToomas Soome 			a = out[i - stride];
4669890ff83SToomas Soome 			b = prev_line[i];
4679890ff83SToomas Soome 			c = prev_line[i - stride];
4689890ff83SToomas Soome 		} else {
4699890ff83SToomas Soome 			if (prev_line)
4709890ff83SToomas Soome 				b = prev_line[i];
4719890ff83SToomas Soome 			else
4729890ff83SToomas Soome 				b = 0;
4739890ff83SToomas Soome 
4749890ff83SToomas Soome 			if (i >= stride)
4759890ff83SToomas Soome 				a = out[i - stride];
4769890ff83SToomas Soome 			else
4779890ff83SToomas Soome 				a = 0;
4789890ff83SToomas Soome 
4799890ff83SToomas Soome 			c = 0;
4809890ff83SToomas Soome 		}
4819890ff83SToomas Soome 
4829890ff83SToomas Soome 		out[i] = in[i] + png_paeth(a, b, c);
4839890ff83SToomas Soome 	}
4849890ff83SToomas Soome }
4859890ff83SToomas Soome 
4869890ff83SToomas Soome static int
png_unfilter(png_t * png,uint8_t * data)4879890ff83SToomas Soome png_unfilter(png_t *png, uint8_t *data)
4889890ff83SToomas Soome {
4899890ff83SToomas Soome 	unsigned i;
4909890ff83SToomas Soome 	unsigned pos = 0;
4919890ff83SToomas Soome 	unsigned outpos = 0;
4929890ff83SToomas Soome 	uint8_t *filtered = png->png_data;
4939890ff83SToomas Soome 	unsigned stride = png->bpp;
4949890ff83SToomas Soome 
4959890ff83SToomas Soome 	while (pos < png->png_datalen) {
4969890ff83SToomas Soome 		uint8_t filter = filtered[pos];
4979890ff83SToomas Soome 
4989890ff83SToomas Soome 		pos++;
4999890ff83SToomas Soome 
5009890ff83SToomas Soome 		if (png->depth == 16) {
5019890ff83SToomas Soome 			for (i = 0; i < png->width * stride; i += 2) {
5029890ff83SToomas Soome 				*(short *)(filtered+pos+i) =
5039890ff83SToomas Soome 				    (filtered[pos+i] << 8) | filtered[pos+i+1];
5049890ff83SToomas Soome 			}
5059890ff83SToomas Soome 		}
5069890ff83SToomas Soome 
5079890ff83SToomas Soome 		switch (filter) {
5089890ff83SToomas Soome 		case 0: /* none */
5099890ff83SToomas Soome 			memcpy(data+outpos, filtered+pos, png->width * stride);
5109890ff83SToomas Soome 			break;
5119890ff83SToomas Soome 		case 1: /* sub */
5129890ff83SToomas Soome 			png_filter_sub(stride, filtered+pos, data+outpos,
5139890ff83SToomas Soome 			    png->width * stride);
5149890ff83SToomas Soome 			break;
5159890ff83SToomas Soome 		case 2: /* up */
5169890ff83SToomas Soome 			if (outpos) {
5179890ff83SToomas Soome 				png_filter_up(stride, filtered+pos, data+outpos,
5189890ff83SToomas Soome 				    data + outpos - (png->width*stride),
5199890ff83SToomas Soome 				    png->width*stride);
5209890ff83SToomas Soome 			} else {
5219890ff83SToomas Soome 				png_filter_up(stride, filtered+pos, data+outpos,
5229890ff83SToomas Soome 				    0, png->width*stride);
5239890ff83SToomas Soome 			}
5249890ff83SToomas Soome 			break;
5259890ff83SToomas Soome 		case 3: /* average */
5269890ff83SToomas Soome 			if (outpos) {
5279890ff83SToomas Soome 				png_filter_average(stride, filtered+pos,
5289890ff83SToomas Soome 				    data+outpos,
5299890ff83SToomas Soome 				    data + outpos - (png->width*stride),
5309890ff83SToomas Soome 				    png->width*stride);
5319890ff83SToomas Soome 			} else {
5329890ff83SToomas Soome 				png_filter_average(stride, filtered+pos,
5339890ff83SToomas Soome 				    data+outpos, 0, png->width*stride);
5349890ff83SToomas Soome 			}
5359890ff83SToomas Soome 			break;
5369890ff83SToomas Soome 		case 4: /* paeth */
5379890ff83SToomas Soome 			if (outpos) {
5389890ff83SToomas Soome 				png_filter_paeth(stride, filtered+pos,
5399890ff83SToomas Soome 				    data+outpos,
5409890ff83SToomas Soome 				    data + outpos - (png->width*stride),
5419890ff83SToomas Soome 				    png->width*stride);
5429890ff83SToomas Soome 			} else {
5439890ff83SToomas Soome 				png_filter_paeth(stride, filtered+pos,
5449890ff83SToomas Soome 				    data+outpos, 0, png->width*stride);
5459890ff83SToomas Soome 			}
5469890ff83SToomas Soome 			break;
5479890ff83SToomas Soome 		default:
5489890ff83SToomas Soome 			return (PNG_UNKNOWN_FILTER);
5499890ff83SToomas Soome 		}
5509890ff83SToomas Soome 
5519890ff83SToomas Soome 		outpos += png->width * stride;
5529890ff83SToomas Soome 		pos += png->width * stride;
5539890ff83SToomas Soome 	}
5549890ff83SToomas Soome 
5559890ff83SToomas Soome 	return (PNG_NO_ERROR);
5569890ff83SToomas Soome }
5579890ff83SToomas Soome 
5589890ff83SToomas Soome int
png_get_data(png_t * png,uint8_t * data)5599890ff83SToomas Soome png_get_data(png_t *png, uint8_t *data)
5609890ff83SToomas Soome {
5619890ff83SToomas Soome 	int result = PNG_NO_ERROR;
5629890ff83SToomas Soome 
5639890ff83SToomas Soome 	png->zs = NULL;
5649890ff83SToomas Soome 	png->png_datalen = 0;
5659890ff83SToomas Soome 	png->png_data = NULL;
5669890ff83SToomas Soome 	png->readbuf = NULL;
5679890ff83SToomas Soome 	png->readbuflen = 0;
5689890ff83SToomas Soome 
5699890ff83SToomas Soome 	while (result == PNG_NO_ERROR)
5709890ff83SToomas Soome 		result = png_process_chunk(png);
5719890ff83SToomas Soome 
5729890ff83SToomas Soome 	if (png->readbuf) {
5739890ff83SToomas Soome 		free(png->readbuf);
5749890ff83SToomas Soome 		png->readbuflen = 0;
5759890ff83SToomas Soome 	}
5769890ff83SToomas Soome 	if (png->zs)
577*f7e4f33fSToomas Soome 		(void) png_end_inflate(png);
5789890ff83SToomas Soome 
5799890ff83SToomas Soome 	if (result != PNG_DONE) {
5809890ff83SToomas Soome 		free(png->png_data);
5819890ff83SToomas Soome 		return (result);
5829890ff83SToomas Soome 	}
5839890ff83SToomas Soome 
5849890ff83SToomas Soome 	result = png_unfilter(png, data);
5859890ff83SToomas Soome 
5869890ff83SToomas Soome 	free(png->png_data);
5879890ff83SToomas Soome 
5889890ff83SToomas Soome 	return (result);
5899890ff83SToomas Soome }
5909890ff83SToomas Soome 
5919890ff83SToomas Soome char *
png_error_string(int error)5929890ff83SToomas Soome png_error_string(int error)
5939890ff83SToomas Soome {
5949890ff83SToomas Soome 	switch (error) {
5959890ff83SToomas Soome 	case PNG_NO_ERROR:
5969890ff83SToomas Soome 		return ("No error");
5979890ff83SToomas Soome 	case PNG_FILE_ERROR:
5989890ff83SToomas Soome 		return ("Unknown file error.");
5999890ff83SToomas Soome 	case PNG_HEADER_ERROR:
6009890ff83SToomas Soome 		return ("No PNG header found. Are you sure this is a PNG?");
6019890ff83SToomas Soome 	case PNG_IO_ERROR:
6029890ff83SToomas Soome 		return ("Failure while reading file.");
6039890ff83SToomas Soome 	case PNG_EOF_ERROR:
6049890ff83SToomas Soome 		return ("Reached end of file.");
6059890ff83SToomas Soome 	case PNG_CRC_ERROR:
6069890ff83SToomas Soome 		return ("CRC or chunk length error.");
6079890ff83SToomas Soome 	case PNG_MEMORY_ERROR:
6089890ff83SToomas Soome 		return ("Could not allocate memory.");
6099890ff83SToomas Soome 	case PNG_ZLIB_ERROR:
6109890ff83SToomas Soome 		return ("zlib reported an error.");
6119890ff83SToomas Soome 	case PNG_UNKNOWN_FILTER:
6129890ff83SToomas Soome 		return ("Unknown filter method used in scanline.");
6139890ff83SToomas Soome 	case PNG_DONE:
6149890ff83SToomas Soome 		return ("PNG done");
6159890ff83SToomas Soome 	case PNG_NOT_SUPPORTED:
6169890ff83SToomas Soome 		return ("The PNG is unsupported by pnglite, too bad for you!");
6179890ff83SToomas Soome 	case PNG_WRONG_ARGUMENTS:
6189890ff83SToomas Soome 		return ("Wrong combination of arguments passed to png_open. "
6199890ff83SToomas Soome 		    "You must use either a read_function or supply a file "
6209890ff83SToomas Soome 		    "pointer to use.");
6219890ff83SToomas Soome 	default:
6229890ff83SToomas Soome 		return ("Unknown error.");
6239890ff83SToomas Soome 	};
6249890ff83SToomas Soome }
625