180aa9319SToomas Soome /*
280aa9319SToomas Soome * pnglite.c - pnglite library
380aa9319SToomas Soome * For conditions of distribution and use, see copyright notice in pnglite.h
472c59f17SToomas Soome */
572c59f17SToomas Soome
680aa9319SToomas Soome /*
780aa9319SToomas Soome * Note: this source is updated to enable build for FreeBSD boot loader.
880aa9319SToomas Soome */
980aa9319SToomas Soome
1080aa9319SToomas Soome #ifdef _STANDALONE
1180aa9319SToomas Soome #include <sys/cdefs.h>
1280aa9319SToomas Soome #include <stand.h>
1372c59f17SToomas Soome #else
1472c59f17SToomas Soome #include <stdio.h>
1572c59f17SToomas Soome #include <stdlib.h>
1680aa9319SToomas Soome #include <sys/types.h>
1780aa9319SToomas Soome #include <sys/stat.h>
1880aa9319SToomas Soome #include <fcntl.h>
1980aa9319SToomas Soome #endif
2080aa9319SToomas Soome #include <zlib.h>
2172c59f17SToomas Soome #include "pnglite.h"
2272c59f17SToomas Soome
2380aa9319SToomas Soome #ifndef abs
2480aa9319SToomas Soome #define abs(x) ((x) < 0? -(x):(x))
2580aa9319SToomas Soome #endif
2672c59f17SToomas Soome
2780aa9319SToomas Soome #define PNG_32b(b, s) ((uint32_t)(b) << (s))
2880aa9319SToomas Soome #define PNG_U32(b1, b2, b3, b4) \
2980aa9319SToomas Soome (PNG_32b(b1, 24) | PNG_32b(b2, 16) | PNG_32b(b3, 8) | PNG_32b(b4, 0))
3080aa9319SToomas Soome
3180aa9319SToomas Soome #define png_IDAT PNG_U32(73, 68, 65, 84)
3280aa9319SToomas Soome #define png_IEND PNG_U32(73, 69, 78, 68)
3380aa9319SToomas Soome
3480aa9319SToomas Soome static ssize_t
file_read(png_t * png,void * out,size_t size,size_t numel)3580aa9319SToomas Soome file_read(png_t *png, void *out, size_t size, size_t numel)
3672c59f17SToomas Soome {
3780aa9319SToomas Soome ssize_t result;
3880aa9319SToomas Soome off_t offset = (off_t)(size * numel);
3980aa9319SToomas Soome
4080aa9319SToomas Soome if (offset < 0)
4180aa9319SToomas Soome return (PNG_FILE_ERROR);
4280aa9319SToomas Soome
4380aa9319SToomas Soome if (!out) {
4480aa9319SToomas Soome result = lseek(png->fd, offset, SEEK_CUR);
4580aa9319SToomas Soome } else {
4680aa9319SToomas Soome result = read(png->fd, out, size * numel);
4772c59f17SToomas Soome }
4872c59f17SToomas Soome
4980aa9319SToomas Soome return (result);
5072c59f17SToomas Soome }
5172c59f17SToomas Soome
5280aa9319SToomas Soome static int
file_read_ul(png_t * png,unsigned * out)5380aa9319SToomas Soome file_read_ul(png_t *png, unsigned *out)
5472c59f17SToomas Soome {
55*2c52512cSToomas Soome uint32_t buf;
5672c59f17SToomas Soome
57*2c52512cSToomas Soome if (file_read(png, &buf, 1, 4) != 4)
5880aa9319SToomas Soome return (PNG_FILE_ERROR);
5972c59f17SToomas Soome
60*2c52512cSToomas Soome *out = ntohl(buf);
6172c59f17SToomas Soome
6280aa9319SToomas Soome return (PNG_NO_ERROR);
6372c59f17SToomas Soome }
6472c59f17SToomas Soome
6580aa9319SToomas Soome static unsigned
get_ul(uint8_t * buf)6680aa9319SToomas Soome get_ul(uint8_t *buf)
6772c59f17SToomas Soome {
68*2c52512cSToomas Soome return (ntohl(*(uint32_t *)buf));
6972c59f17SToomas Soome }
7072c59f17SToomas Soome
7180aa9319SToomas Soome static int
png_get_bpp(png_t * png)7280aa9319SToomas Soome png_get_bpp(png_t *png)
7372c59f17SToomas Soome {
7472c59f17SToomas Soome int bpp;
7572c59f17SToomas Soome
7680aa9319SToomas Soome switch (png->color_type) {
7772c59f17SToomas Soome case PNG_GREYSCALE:
7872c59f17SToomas Soome bpp = 1; break;
7972c59f17SToomas Soome case PNG_TRUECOLOR:
8072c59f17SToomas Soome bpp = 3; break;
8172c59f17SToomas Soome case PNG_INDEXED:
8272c59f17SToomas Soome bpp = 1; break;
8372c59f17SToomas Soome case PNG_GREYSCALE_ALPHA:
8472c59f17SToomas Soome bpp = 2; break;
8572c59f17SToomas Soome case PNG_TRUECOLOR_ALPHA:
8672c59f17SToomas Soome bpp = 4; break;
8772c59f17SToomas Soome default:
8880aa9319SToomas Soome return (PNG_FILE_ERROR);
8972c59f17SToomas Soome }
9072c59f17SToomas Soome
9172c59f17SToomas Soome bpp *= png->depth / 8;
9272c59f17SToomas Soome
9380aa9319SToomas Soome return (bpp);
9472c59f17SToomas Soome }
9572c59f17SToomas Soome
9680aa9319SToomas Soome static int
png_read_ihdr(png_t * png)9780aa9319SToomas Soome png_read_ihdr(png_t *png)
9872c59f17SToomas Soome {
9980aa9319SToomas Soome unsigned length = 0;
10072c59f17SToomas Soome unsigned orig_crc;
10172c59f17SToomas Soome unsigned calc_crc;
10280aa9319SToomas Soome uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
10372c59f17SToomas Soome
10480aa9319SToomas Soome if (file_read_ul(png, &length) != PNG_NO_ERROR)
10580aa9319SToomas Soome return (PNG_FILE_ERROR);
10672c59f17SToomas Soome
10772c59f17SToomas Soome if (length != 13)
10880aa9319SToomas Soome return (PNG_CRC_ERROR);
10972c59f17SToomas Soome
11072c59f17SToomas Soome if (file_read(png, ihdr, 1, 13+4) != 13+4)
11180aa9319SToomas Soome return (PNG_EOF_ERROR);
11272c59f17SToomas Soome
11380aa9319SToomas Soome if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
11480aa9319SToomas Soome return (PNG_FILE_ERROR);
11580aa9319SToomas Soome
11680aa9319SToomas Soome calc_crc = crc32(0L, Z_NULL, 0);
11772c59f17SToomas Soome calc_crc = crc32(calc_crc, ihdr, 13+4);
11872c59f17SToomas Soome
11980aa9319SToomas Soome if (orig_crc != calc_crc) {
12080aa9319SToomas Soome return (PNG_CRC_ERROR);
12180aa9319SToomas Soome }
12272c59f17SToomas Soome
12372c59f17SToomas Soome png->width = get_ul(ihdr+4);
12472c59f17SToomas Soome png->height = get_ul(ihdr+8);
12572c59f17SToomas Soome png->depth = ihdr[12];
12672c59f17SToomas Soome png->color_type = ihdr[13];
12772c59f17SToomas Soome png->compression_method = ihdr[14];
12872c59f17SToomas Soome png->filter_method = ihdr[15];
12972c59f17SToomas Soome png->interlace_method = ihdr[16];
13072c59f17SToomas Soome
13172c59f17SToomas Soome if (png->color_type == PNG_INDEXED)
13280aa9319SToomas Soome return (PNG_NOT_SUPPORTED);
13372c59f17SToomas Soome
13472c59f17SToomas Soome if (png->depth != 8 && png->depth != 16)
13580aa9319SToomas Soome return (PNG_NOT_SUPPORTED);
13672c59f17SToomas Soome
13772c59f17SToomas Soome if (png->interlace_method)
13880aa9319SToomas Soome return (PNG_NOT_SUPPORTED);
13972c59f17SToomas Soome
14080aa9319SToomas Soome return (PNG_NO_ERROR);
14172c59f17SToomas Soome }
14272c59f17SToomas Soome
14380aa9319SToomas Soome void
png_print_info(png_t * png)14480aa9319SToomas Soome png_print_info(png_t *png)
14572c59f17SToomas Soome {
14672c59f17SToomas Soome printf("PNG INFO:\n");
14772c59f17SToomas Soome printf("\twidth:\t\t%d\n", png->width);
14872c59f17SToomas Soome printf("\theight:\t\t%d\n", png->height);
14972c59f17SToomas Soome printf("\tdepth:\t\t%d\n", png->depth);
15072c59f17SToomas Soome printf("\tcolor:\t\t");
15172c59f17SToomas Soome
15280aa9319SToomas Soome switch (png->color_type) {
15380aa9319SToomas Soome case PNG_GREYSCALE:
15480aa9319SToomas Soome printf("greyscale\n"); break;
15580aa9319SToomas Soome case PNG_TRUECOLOR:
15680aa9319SToomas Soome printf("truecolor\n"); break;
15780aa9319SToomas Soome case PNG_INDEXED:
15880aa9319SToomas Soome printf("palette\n"); break;
15980aa9319SToomas Soome case PNG_GREYSCALE_ALPHA:
16080aa9319SToomas Soome printf("greyscale with alpha\n"); break;
16180aa9319SToomas Soome case PNG_TRUECOLOR_ALPHA:
16280aa9319SToomas Soome printf("truecolor with alpha\n"); break;
16380aa9319SToomas Soome default:
16480aa9319SToomas Soome printf("unknown, this is not good\n"); break;
16572c59f17SToomas Soome }
16672c59f17SToomas Soome
16780aa9319SToomas Soome printf("\tcompression:\t%s\n",
16880aa9319SToomas Soome png->compression_method?
16980aa9319SToomas Soome "unknown, this is not good":"inflate/deflate");
17080aa9319SToomas Soome printf("\tfilter:\t\t%s\n",
17180aa9319SToomas Soome png->filter_method? "unknown, this is not good":"adaptive");
17280aa9319SToomas Soome printf("\tinterlace:\t%s\n",
17380aa9319SToomas Soome png->interlace_method? "interlace":"no interlace");
17472c59f17SToomas Soome }
17572c59f17SToomas Soome
17680aa9319SToomas Soome int
png_open(png_t * png,const char * filename)17780aa9319SToomas Soome png_open(png_t *png, const char *filename)
17872c59f17SToomas Soome {
17972c59f17SToomas Soome char header[8];
18072c59f17SToomas Soome int result;
18172c59f17SToomas Soome
18280aa9319SToomas Soome png->image = NULL;
18380aa9319SToomas Soome png->fd = open(filename, O_RDONLY);
18480aa9319SToomas Soome if (png->fd == -1)
18580aa9319SToomas Soome return (PNG_FILE_ERROR);
18672c59f17SToomas Soome
18780aa9319SToomas Soome if (file_read(png, header, 1, 8) != 8) {
18880aa9319SToomas Soome result = PNG_EOF_ERROR;
18980aa9319SToomas Soome goto done;
19080aa9319SToomas Soome }
19172c59f17SToomas Soome
19280aa9319SToomas Soome if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
19380aa9319SToomas Soome result = PNG_HEADER_ERROR;
19480aa9319SToomas Soome goto done;
19580aa9319SToomas Soome }
19672c59f17SToomas Soome
19772c59f17SToomas Soome result = png_read_ihdr(png);
19880aa9319SToomas Soome if (result == PNG_NO_ERROR) {
19980aa9319SToomas Soome result = png_get_bpp(png);
20080aa9319SToomas Soome if (result > 0) {
20180aa9319SToomas Soome png->bpp = (uint8_t)result;
20280aa9319SToomas Soome result = PNG_NO_ERROR;
20380aa9319SToomas Soome }
20472c59f17SToomas Soome }
20572c59f17SToomas Soome
20680aa9319SToomas Soome done:
20780aa9319SToomas Soome if (result == PNG_NO_ERROR) {
20880aa9319SToomas Soome uint64_t size = png->width * png->height * png->bpp;
20980aa9319SToomas Soome
21080aa9319SToomas Soome if (size < UINT_MAX)
21180aa9319SToomas Soome png->image = malloc(size);
21280aa9319SToomas Soome if (png->image == NULL)
21380aa9319SToomas Soome result = PNG_MEMORY_ERROR;
21480aa9319SToomas Soome }
21580aa9319SToomas Soome
21680aa9319SToomas Soome if (result == PNG_NO_ERROR)
21780aa9319SToomas Soome result = png_get_data(png, png->image);
21880aa9319SToomas Soome
21980aa9319SToomas Soome if (result != PNG_NO_ERROR) {
22080aa9319SToomas Soome free(png->image);
22180aa9319SToomas Soome (void) close(png->fd);
22280aa9319SToomas Soome png->fd = -1;
22380aa9319SToomas Soome return (result);
22480aa9319SToomas Soome }
22580aa9319SToomas Soome
22680aa9319SToomas Soome return (result);
22780aa9319SToomas Soome }
22880aa9319SToomas Soome
22980aa9319SToomas Soome int
png_close(png_t * png)23080aa9319SToomas Soome png_close(png_t *png)
23172c59f17SToomas Soome {
23280aa9319SToomas Soome (void) close(png->fd);
23380aa9319SToomas Soome png->fd = -1;
23480aa9319SToomas Soome free(png->image);
23580aa9319SToomas Soome png->image = NULL;
23672c59f17SToomas Soome
23780aa9319SToomas Soome return (PNG_NO_ERROR);
23872c59f17SToomas Soome }
23972c59f17SToomas Soome
24080aa9319SToomas Soome static int
png_init_inflate(png_t * png)24180aa9319SToomas Soome png_init_inflate(png_t *png)
24272c59f17SToomas Soome {
24372c59f17SToomas Soome z_stream *stream;
24480aa9319SToomas Soome png->zs = calloc(1, sizeof (z_stream));
24572c59f17SToomas Soome
24672c59f17SToomas Soome stream = png->zs;
24772c59f17SToomas Soome
24872c59f17SToomas Soome if (!stream)
24980aa9319SToomas Soome return (PNG_MEMORY_ERROR);
25072c59f17SToomas Soome
25180aa9319SToomas Soome if (inflateInit(stream) != Z_OK) {
25280aa9319SToomas Soome free(png->zs);
25380aa9319SToomas Soome png->zs = NULL;
25480aa9319SToomas Soome return (PNG_ZLIB_ERROR);
25572c59f17SToomas Soome }
25672c59f17SToomas Soome
25772c59f17SToomas Soome stream->next_out = png->png_data;
25872c59f17SToomas Soome stream->avail_out = png->png_datalen;
25972c59f17SToomas Soome
26080aa9319SToomas Soome return (PNG_NO_ERROR);
26172c59f17SToomas Soome }
26272c59f17SToomas Soome
26380aa9319SToomas Soome static int
png_end_inflate(png_t * png)26480aa9319SToomas Soome png_end_inflate(png_t *png)
26572c59f17SToomas Soome {
26672c59f17SToomas Soome z_stream *stream = png->zs;
26780aa9319SToomas Soome int rc = PNG_NO_ERROR;
26872c59f17SToomas Soome
26972c59f17SToomas Soome if (!stream)
27080aa9319SToomas Soome return (PNG_MEMORY_ERROR);
27172c59f17SToomas Soome
27280aa9319SToomas Soome if (inflateEnd(stream) != Z_OK) {
27372c59f17SToomas Soome printf("ZLIB says: %s\n", stream->msg);
27480aa9319SToomas Soome rc = PNG_ZLIB_ERROR;
27572c59f17SToomas Soome }
27672c59f17SToomas Soome
27780aa9319SToomas Soome free(png->zs);
27880aa9319SToomas Soome png->zs = NULL;
27972c59f17SToomas Soome
28080aa9319SToomas Soome return (rc);
28172c59f17SToomas Soome }
28272c59f17SToomas Soome
28380aa9319SToomas Soome static int
png_inflate(png_t * png,uint8_t * data,int len)28480aa9319SToomas Soome png_inflate(png_t *png, uint8_t *data, int len)
28572c59f17SToomas Soome {
28672c59f17SToomas Soome int result;
28772c59f17SToomas Soome z_stream *stream = png->zs;
28872c59f17SToomas Soome
28972c59f17SToomas Soome if (!stream)
29080aa9319SToomas Soome return (PNG_MEMORY_ERROR);
29172c59f17SToomas Soome
29272c59f17SToomas Soome stream->next_in = data;
29372c59f17SToomas Soome stream->avail_in = len;
29472c59f17SToomas Soome
29572c59f17SToomas Soome result = inflate(stream, Z_SYNC_FLUSH);
29672c59f17SToomas Soome
29780aa9319SToomas Soome if (result != Z_STREAM_END && result != Z_OK) {
29872c59f17SToomas Soome printf("%s\n", stream->msg);
29980aa9319SToomas Soome return (PNG_ZLIB_ERROR);
30072c59f17SToomas Soome }
30172c59f17SToomas Soome
30272c59f17SToomas Soome if (stream->avail_in != 0)
30380aa9319SToomas Soome return (PNG_ZLIB_ERROR);
30472c59f17SToomas Soome
30580aa9319SToomas Soome return (PNG_NO_ERROR);
30672c59f17SToomas Soome }
30772c59f17SToomas Soome
30880aa9319SToomas Soome static int
png_read_idat(png_t * png,unsigned length)30980aa9319SToomas Soome png_read_idat(png_t *png, unsigned length)
31072c59f17SToomas Soome {
31172c59f17SToomas Soome unsigned orig_crc;
31272c59f17SToomas Soome unsigned calc_crc;
31380aa9319SToomas Soome ssize_t len = length;
31472c59f17SToomas Soome
31580aa9319SToomas Soome if (!png->readbuf || png->readbuflen < length) {
31680aa9319SToomas Soome png->readbuf = realloc(png->readbuf, length);
31772c59f17SToomas Soome png->readbuflen = length;
31872c59f17SToomas Soome }
31972c59f17SToomas Soome
32072c59f17SToomas Soome if (!png->readbuf)
32180aa9319SToomas Soome return (PNG_MEMORY_ERROR);
32272c59f17SToomas Soome
32380aa9319SToomas Soome if (file_read(png, png->readbuf, 1, length) != len)
32480aa9319SToomas Soome return (PNG_FILE_ERROR);
32572c59f17SToomas Soome
32672c59f17SToomas Soome calc_crc = crc32(0L, Z_NULL, 0);
32780aa9319SToomas Soome calc_crc = crc32(calc_crc, (uint8_t *)"IDAT", 4);
32880aa9319SToomas Soome calc_crc = crc32(calc_crc, (uint8_t *)png->readbuf, length);
32972c59f17SToomas Soome
33080aa9319SToomas Soome if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
33180aa9319SToomas Soome return (PNG_FILE_ERROR);
33272c59f17SToomas Soome
33372c59f17SToomas Soome if (orig_crc != calc_crc)
33480aa9319SToomas Soome return (PNG_CRC_ERROR);
33572c59f17SToomas Soome
33680aa9319SToomas Soome return (png_inflate(png, png->readbuf, length));
33772c59f17SToomas Soome }
33872c59f17SToomas Soome
33980aa9319SToomas Soome static int
png_process_chunk(png_t * png)34080aa9319SToomas Soome png_process_chunk(png_t *png)
34172c59f17SToomas Soome {
34272c59f17SToomas Soome int result = PNG_NO_ERROR;
34372c59f17SToomas Soome unsigned type;
34472c59f17SToomas Soome unsigned length;
34572c59f17SToomas Soome
34680aa9319SToomas Soome if (file_read_ul(png, &length) != PNG_NO_ERROR)
34780aa9319SToomas Soome return (PNG_FILE_ERROR);
34872c59f17SToomas Soome
34980aa9319SToomas Soome if (file_read_ul(png, &type) != PNG_NO_ERROR)
35080aa9319SToomas Soome return (PNG_FILE_ERROR);
35172c59f17SToomas Soome
35280aa9319SToomas Soome /*
35380aa9319SToomas Soome * if we found an idat, all other idats should be followed with no
35480aa9319SToomas Soome * other chunks in between
35580aa9319SToomas Soome */
35680aa9319SToomas Soome if (type == png_IDAT) {
35780aa9319SToomas Soome if (!png->png_data) { /* first IDAT */
35880aa9319SToomas Soome png->png_datalen = png->width * png->height *
35980aa9319SToomas Soome png->bpp + png->height;
36080aa9319SToomas Soome png->png_data = malloc(png->png_datalen);
36172c59f17SToomas Soome }
36272c59f17SToomas Soome
36372c59f17SToomas Soome if (!png->png_data)
36480aa9319SToomas Soome return (PNG_MEMORY_ERROR);
36572c59f17SToomas Soome
36680aa9319SToomas Soome if (!png->zs) {
36772c59f17SToomas Soome result = png_init_inflate(png);
36872c59f17SToomas Soome if (result != PNG_NO_ERROR)
36980aa9319SToomas Soome return (result);
37072c59f17SToomas Soome }
37172c59f17SToomas Soome
37280aa9319SToomas Soome return (png_read_idat(png, length));
37380aa9319SToomas Soome } else if (type == png_IEND)
37480aa9319SToomas Soome return (PNG_DONE);
37572c59f17SToomas Soome else
37680aa9319SToomas Soome (void) file_read(png, 0, 1, length + 4); /* unknown chunk */
37780aa9319SToomas Soome
37880aa9319SToomas Soome return (result);
37972c59f17SToomas Soome }
38072c59f17SToomas Soome
38180aa9319SToomas Soome static void
png_filter_sub(unsigned stride,uint8_t * in,uint8_t * out,unsigned len)38280aa9319SToomas Soome png_filter_sub(unsigned stride, uint8_t *in, uint8_t *out, unsigned len)
38372c59f17SToomas Soome {
38480aa9319SToomas Soome unsigned i;
38580aa9319SToomas Soome uint8_t a = 0;
38672c59f17SToomas Soome
38780aa9319SToomas Soome for (i = 0; i < len; i++) {
38872c59f17SToomas Soome if (i >= stride)
38972c59f17SToomas Soome a = out[i - stride];
39072c59f17SToomas Soome
39172c59f17SToomas Soome out[i] = in[i] + a;
39272c59f17SToomas Soome }
39372c59f17SToomas Soome }
39472c59f17SToomas Soome
39580aa9319SToomas Soome static void
png_filter_up(unsigned stride __unused,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)39680aa9319SToomas Soome png_filter_up(unsigned stride __unused, uint8_t *in, uint8_t *out,
39780aa9319SToomas Soome uint8_t *prev_line, unsigned len)
39872c59f17SToomas Soome {
39980aa9319SToomas Soome unsigned i;
40072c59f17SToomas Soome
40180aa9319SToomas Soome if (prev_line) {
40272c59f17SToomas Soome for (i = 0; i < len; i++)
40372c59f17SToomas Soome out[i] = in[i] + prev_line[i];
40480aa9319SToomas Soome } else
40572c59f17SToomas Soome memcpy(out, in, len);
40672c59f17SToomas Soome }
40772c59f17SToomas Soome
40880aa9319SToomas Soome static void
png_filter_average(unsigned stride,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)40980aa9319SToomas Soome png_filter_average(unsigned stride, uint8_t *in, uint8_t *out,
41080aa9319SToomas Soome uint8_t *prev_line, unsigned len)
41172c59f17SToomas Soome {
41280aa9319SToomas Soome unsigned int i;
41380aa9319SToomas Soome uint8_t a = 0;
41480aa9319SToomas Soome uint8_t b = 0;
41572c59f17SToomas Soome unsigned int sum = 0;
41672c59f17SToomas Soome
41780aa9319SToomas Soome for (i = 0; i < len; i++) {
41872c59f17SToomas Soome if (prev_line)
41972c59f17SToomas Soome b = prev_line[i];
42072c59f17SToomas Soome
42172c59f17SToomas Soome if (i >= stride)
42272c59f17SToomas Soome a = out[i - stride];
42372c59f17SToomas Soome
42472c59f17SToomas Soome sum = a;
42572c59f17SToomas Soome sum += b;
42672c59f17SToomas Soome
42780aa9319SToomas Soome out[i] = in[i] + sum/2;
42872c59f17SToomas Soome }
42972c59f17SToomas Soome }
43072c59f17SToomas Soome
43180aa9319SToomas Soome static uint8_t
png_paeth(uint8_t a,uint8_t b,uint8_t c)43280aa9319SToomas Soome png_paeth(uint8_t a, uint8_t b, uint8_t c)
43372c59f17SToomas Soome {
43472c59f17SToomas Soome int p = (int)a + b - c;
43572c59f17SToomas Soome int pa = abs(p - a);
43672c59f17SToomas Soome int pb = abs(p - b);
43772c59f17SToomas Soome int pc = abs(p - c);
43872c59f17SToomas Soome
43972c59f17SToomas Soome int pr;
44072c59f17SToomas Soome
44172c59f17SToomas Soome if (pa <= pb && pa <= pc)
44272c59f17SToomas Soome pr = a;
44372c59f17SToomas Soome else if (pb <= pc)
44472c59f17SToomas Soome pr = b;
44572c59f17SToomas Soome else
44672c59f17SToomas Soome pr = c;
44772c59f17SToomas Soome
44880aa9319SToomas Soome return (pr);
44972c59f17SToomas Soome }
45072c59f17SToomas Soome
45180aa9319SToomas Soome static void
png_filter_paeth(unsigned stride,uint8_t * in,uint8_t * out,uint8_t * prev_line,unsigned len)45280aa9319SToomas Soome png_filter_paeth(unsigned stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
45380aa9319SToomas Soome unsigned len)
45472c59f17SToomas Soome {
45580aa9319SToomas Soome unsigned i;
45680aa9319SToomas Soome uint8_t a;
45780aa9319SToomas Soome uint8_t b;
45880aa9319SToomas Soome uint8_t c;
45972c59f17SToomas Soome
46080aa9319SToomas Soome for (i = 0; i < len; i++) {
46180aa9319SToomas Soome if (prev_line && i >= stride) {
46272c59f17SToomas Soome a = out[i - stride];
46372c59f17SToomas Soome b = prev_line[i];
46472c59f17SToomas Soome c = prev_line[i - stride];
46580aa9319SToomas Soome } else {
46672c59f17SToomas Soome if (prev_line)
46772c59f17SToomas Soome b = prev_line[i];
46872c59f17SToomas Soome else
46972c59f17SToomas Soome b = 0;
47072c59f17SToomas Soome
47172c59f17SToomas Soome if (i >= stride)
47272c59f17SToomas Soome a = out[i - stride];
47372c59f17SToomas Soome else
47472c59f17SToomas Soome a = 0;
47572c59f17SToomas Soome
47672c59f17SToomas Soome c = 0;
47772c59f17SToomas Soome }
47872c59f17SToomas Soome
47972c59f17SToomas Soome out[i] = in[i] + png_paeth(a, b, c);
48072c59f17SToomas Soome }
48172c59f17SToomas Soome }
48272c59f17SToomas Soome
48380aa9319SToomas Soome static int
png_unfilter(png_t * png,uint8_t * data)48480aa9319SToomas Soome png_unfilter(png_t *png, uint8_t *data)
48572c59f17SToomas Soome {
48672c59f17SToomas Soome unsigned i;
48772c59f17SToomas Soome unsigned pos = 0;
48872c59f17SToomas Soome unsigned outpos = 0;
48980aa9319SToomas Soome uint8_t *filtered = png->png_data;
49080aa9319SToomas Soome unsigned stride = png->bpp;
49172c59f17SToomas Soome
49280aa9319SToomas Soome while (pos < png->png_datalen) {
49380aa9319SToomas Soome uint8_t filter = filtered[pos];
49472c59f17SToomas Soome
49572c59f17SToomas Soome pos++;
49672c59f17SToomas Soome
49780aa9319SToomas Soome if (png->depth == 16) {
49880aa9319SToomas Soome for (i = 0; i < png->width * stride; i += 2) {
49980aa9319SToomas Soome *(short *)(filtered+pos+i) =
50080aa9319SToomas Soome (filtered[pos+i] << 8) | filtered[pos+i+1];
50172c59f17SToomas Soome }
50272c59f17SToomas Soome }
50372c59f17SToomas Soome
50480aa9319SToomas Soome switch (filter) {
50572c59f17SToomas Soome case 0: /* none */
50672c59f17SToomas Soome memcpy(data+outpos, filtered+pos, png->width * stride);
50772c59f17SToomas Soome break;
50872c59f17SToomas Soome case 1: /* sub */
50980aa9319SToomas Soome png_filter_sub(stride, filtered+pos, data+outpos,
51080aa9319SToomas Soome png->width * stride);
51172c59f17SToomas Soome break;
51272c59f17SToomas Soome case 2: /* up */
51380aa9319SToomas Soome if (outpos) {
51480aa9319SToomas Soome png_filter_up(stride, filtered+pos, data+outpos,
51580aa9319SToomas Soome data + outpos - (png->width*stride),
51680aa9319SToomas Soome png->width*stride);
51780aa9319SToomas Soome } else {
51880aa9319SToomas Soome png_filter_up(stride, filtered+pos, data+outpos,
51980aa9319SToomas Soome 0, png->width*stride);
52080aa9319SToomas Soome }
52172c59f17SToomas Soome break;
52272c59f17SToomas Soome case 3: /* average */
52380aa9319SToomas Soome if (outpos) {
52480aa9319SToomas Soome png_filter_average(stride, filtered+pos,
52580aa9319SToomas Soome data+outpos,
52680aa9319SToomas Soome data + outpos - (png->width*stride),
52780aa9319SToomas Soome png->width*stride);
52880aa9319SToomas Soome } else {
52980aa9319SToomas Soome png_filter_average(stride, filtered+pos,
53080aa9319SToomas Soome data+outpos, 0, png->width*stride);
53180aa9319SToomas Soome }
53272c59f17SToomas Soome break;
53372c59f17SToomas Soome case 4: /* paeth */
53480aa9319SToomas Soome if (outpos) {
53580aa9319SToomas Soome png_filter_paeth(stride, filtered+pos,
53680aa9319SToomas Soome data+outpos,
53780aa9319SToomas Soome data + outpos - (png->width*stride),
53880aa9319SToomas Soome png->width*stride);
53980aa9319SToomas Soome } else {
54080aa9319SToomas Soome png_filter_paeth(stride, filtered+pos,
54180aa9319SToomas Soome data+outpos, 0, png->width*stride);
54280aa9319SToomas Soome }
54372c59f17SToomas Soome break;
54472c59f17SToomas Soome default:
54580aa9319SToomas Soome return (PNG_UNKNOWN_FILTER);
54672c59f17SToomas Soome }
54772c59f17SToomas Soome
54872c59f17SToomas Soome outpos += png->width * stride;
54972c59f17SToomas Soome pos += png->width * stride;
55072c59f17SToomas Soome }
55172c59f17SToomas Soome
55280aa9319SToomas Soome return (PNG_NO_ERROR);
55372c59f17SToomas Soome }
55472c59f17SToomas Soome
55580aa9319SToomas Soome int
png_get_data(png_t * png,uint8_t * data)55680aa9319SToomas Soome png_get_data(png_t *png, uint8_t *data)
55772c59f17SToomas Soome {
55872c59f17SToomas Soome int result = PNG_NO_ERROR;
55972c59f17SToomas Soome
56072c59f17SToomas Soome png->zs = NULL;
56172c59f17SToomas Soome png->png_datalen = 0;
56272c59f17SToomas Soome png->png_data = NULL;
56372c59f17SToomas Soome png->readbuf = NULL;
56472c59f17SToomas Soome png->readbuflen = 0;
56572c59f17SToomas Soome
56672c59f17SToomas Soome while (result == PNG_NO_ERROR)
56772c59f17SToomas Soome result = png_process_chunk(png);
56872c59f17SToomas Soome
56980aa9319SToomas Soome if (png->readbuf) {
57080aa9319SToomas Soome free(png->readbuf);
57172c59f17SToomas Soome png->readbuflen = 0;
57272c59f17SToomas Soome }
57372c59f17SToomas Soome if (png->zs)
57480aa9319SToomas Soome (void) png_end_inflate(png);
57572c59f17SToomas Soome
57680aa9319SToomas Soome if (result != PNG_DONE) {
57780aa9319SToomas Soome free(png->png_data);
57880aa9319SToomas Soome return (result);
57972c59f17SToomas Soome }
58072c59f17SToomas Soome
58172c59f17SToomas Soome result = png_unfilter(png, data);
58272c59f17SToomas Soome
58380aa9319SToomas Soome free(png->png_data);
58472c59f17SToomas Soome
58580aa9319SToomas Soome return (result);
58672c59f17SToomas Soome }
58772c59f17SToomas Soome
58880aa9319SToomas Soome char *
png_error_string(int error)58980aa9319SToomas Soome png_error_string(int error)
59072c59f17SToomas Soome {
59180aa9319SToomas Soome switch (error) {
59272c59f17SToomas Soome case PNG_NO_ERROR:
59380aa9319SToomas Soome return ("No error");
59472c59f17SToomas Soome case PNG_FILE_ERROR:
59580aa9319SToomas Soome return ("Unknown file error.");
59672c59f17SToomas Soome case PNG_HEADER_ERROR:
59780aa9319SToomas Soome return ("No PNG header found. Are you sure this is a PNG?");
59872c59f17SToomas Soome case PNG_IO_ERROR:
59980aa9319SToomas Soome return ("Failure while reading file.");
60072c59f17SToomas Soome case PNG_EOF_ERROR:
60180aa9319SToomas Soome return ("Reached end of file.");
60272c59f17SToomas Soome case PNG_CRC_ERROR:
60380aa9319SToomas Soome return ("CRC or chunk length error.");
60472c59f17SToomas Soome case PNG_MEMORY_ERROR:
60580aa9319SToomas Soome return ("Could not allocate memory.");
60672c59f17SToomas Soome case PNG_ZLIB_ERROR:
60780aa9319SToomas Soome return ("zlib reported an error.");
60872c59f17SToomas Soome case PNG_UNKNOWN_FILTER:
60980aa9319SToomas Soome return ("Unknown filter method used in scanline.");
61072c59f17SToomas Soome case PNG_DONE:
61180aa9319SToomas Soome return ("PNG done");
61272c59f17SToomas Soome case PNG_NOT_SUPPORTED:
61380aa9319SToomas Soome return ("The PNG is unsupported by pnglite, too bad for you!");
61472c59f17SToomas Soome case PNG_WRONG_ARGUMENTS:
61580aa9319SToomas Soome return ("Wrong combination of arguments passed to png_open. "
61680aa9319SToomas Soome "You must use either a read_function or supply a file "
61780aa9319SToomas Soome "pointer to use.");
61872c59f17SToomas Soome default:
61980aa9319SToomas Soome return ("Unknown error.");
62072c59f17SToomas Soome };
62172c59f17SToomas Soome }
622