2 * pnglite.c - pnglite library
3 * For conditions of distribution and use, see copyright notice in pnglite.h
7 * Note: this source is updated to enable build for FreeBSD boot loader.
11 #include <sys/cdefs.h>
16 #include <sys/types.h>
24 #define abs(x) ((x) < 0? -(x):(x))
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))
31 #define png_IDAT PNG_U32(73, 68, 65, 84)
32 #define png_IEND PNG_U32(73, 69, 78, 68)
35 file_read(png_t *png, void *out, size_t size, size_t numel)
38 off_t offset = (off_t)(size * numel);
41 return (PNG_FILE_ERROR);
44 result = lseek(png->fd, offset, SEEK_CUR);
46 result = read(png->fd, out, size * numel);
53 file_read_ul(png_t *png, unsigned *out)
57 if (file_read(png, &buf, 1, 4) != 4)
58 return (PNG_FILE_ERROR);
62 return (PNG_NO_ERROR);
68 return (ntohl(*(uint32_t *)buf));
72 png_get_bpp(png_t *png)
76 switch (png->color_type) {
83 case PNG_GREYSCALE_ALPHA:
85 case PNG_TRUECOLOR_ALPHA:
88 return (PNG_FILE_ERROR);
91 bpp *= png->depth / 8;
97 png_read_ihdr(png_t *png)
102 uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
104 if (file_read_ul(png, &length) != PNG_NO_ERROR)
105 return (PNG_FILE_ERROR);
108 return (PNG_CRC_ERROR);
110 if (file_read(png, ihdr, 1, 13+4) != 13+4)
111 return (PNG_EOF_ERROR);
113 if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
114 return (PNG_FILE_ERROR);
116 calc_crc = crc32(0L, Z_NULL, 0);
117 calc_crc = crc32(calc_crc, ihdr, 13+4);
119 if (orig_crc != calc_crc) {
120 return (PNG_CRC_ERROR);
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];
131 if (png->color_type == PNG_INDEXED)
132 return (PNG_NOT_SUPPORTED);
134 if (png->depth != 8 && png->depth != 16)
135 return (PNG_NOT_SUPPORTED);
137 if (png->interlace_method)
138 return (PNG_NOT_SUPPORTED);
140 return (PNG_NO_ERROR);
144 png_print_info(png_t *png)
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");
152 switch (png->color_type) {
154 printf("greyscale\n"); break;
156 printf("truecolor\n"); break;
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;
164 printf("unknown, this is not good\n"); break;
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");
177 png_open(png_t *png, const char *filename)
183 png->fd = open(filename, O_RDONLY);
185 return (PNG_FILE_ERROR);
187 if (file_read(png, header, 1, 8) != 8) {
188 result = PNG_EOF_ERROR;
192 if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
193 result = PNG_HEADER_ERROR;
197 result = png_read_ihdr(png);
198 if (result == PNG_NO_ERROR) {
199 result = png_get_bpp(png);
201 png->bpp = (uint8_t)result;
202 result = PNG_NO_ERROR;
207 if (result == PNG_NO_ERROR) {
208 uint64_t size = png->width * png->height * png->bpp;
211 png->image = malloc(size);
212 if (png->image == NULL)
213 result = PNG_MEMORY_ERROR;
216 if (result == PNG_NO_ERROR)
217 result = png_get_data(png, png->image);
219 if (result != PNG_NO_ERROR) {
221 (void) close(png->fd);
230 png_close(png_t *png)
232 (void) close(png->fd);
237 return (PNG_NO_ERROR);
241 png_init_inflate(png_t *png)
244 png->zs = calloc(1, sizeof (z_stream));
249 return (PNG_MEMORY_ERROR);
251 if (inflateInit(stream) != Z_OK) {
254 return (PNG_ZLIB_ERROR);
257 stream->next_out = png->png_data;
258 stream->avail_out = png->png_datalen;
260 return (PNG_NO_ERROR);
264 png_end_inflate(png_t *png)
266 z_stream *stream = png->zs;
267 int rc = PNG_NO_ERROR;
270 return (PNG_MEMORY_ERROR);
272 if (inflateEnd(stream) != Z_OK) {
273 printf("ZLIB says: %s\n", stream->msg);
284 png_inflate(png_t *png, uint8_t *data, int len)
287 z_stream *stream = png->zs;
290 return (PNG_MEMORY_ERROR);
292 stream->next_in = data;
293 stream->avail_in = len;
295 result = inflate(stream, Z_SYNC_FLUSH);
297 if (result != Z_STREAM_END && result != Z_OK) {
298 printf("%s\n", stream->msg);
299 return (PNG_ZLIB_ERROR);
302 if (stream->avail_in != 0)
303 return (PNG_ZLIB_ERROR);
305 return (PNG_NO_ERROR);
309 png_read_idat(png_t *png, unsigned length)
313 ssize_t len = length;
315 if (!png->readbuf || png->readbuflen < length) {
316 png->readbuf = realloc(png->readbuf, length);
317 png->readbuflen = length;
321 return (PNG_MEMORY_ERROR);
323 if (file_read(png, png->readbuf, 1, length) != len)
324 return (PNG_FILE_ERROR);
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);
330 if (file_read_ul(png, &orig_crc) != PNG_NO_ERROR)
331 return (PNG_FILE_ERROR);
333 if (orig_crc != calc_crc)
334 return (PNG_CRC_ERROR);
336 return (png_inflate(png, png->readbuf, length));
340 png_process_chunk(png_t *png)
342 int result = PNG_NO_ERROR;
346 if (file_read_ul(png, &length) != PNG_NO_ERROR)
347 return (PNG_FILE_ERROR);
349 if (file_read_ul(png, &type) != PNG_NO_ERROR)
350 return (PNG_FILE_ERROR);
353 * if we found an idat, all other idats should be followed with no
354 * other chunks in between
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);
364 return (PNG_MEMORY_ERROR);
367 result = png_init_inflate(png);
368 if (result != PNG_NO_ERROR)
372 return (png_read_idat(png, length));
373 } else if (type == png_IEND)
376 (void) file_read(png, 0, 1, length + 4); /* unknown chunk */
382 png_filter_sub(unsigned stride, uint8_t *in, uint8_t *out, unsigned len)
387 for (i = 0; i < len; i++) {
396 png_filter_up(unsigned stride __unused, uint8_t *in, uint8_t *out,
397 uint8_t *prev_line, unsigned len)
402 for (i = 0; i < len; i++)
403 out[i] = in[i] + prev_line[i];
405 memcpy(out, in, len);
409 png_filter_average(unsigned stride, uint8_t *in, uint8_t *out,
410 uint8_t *prev_line, unsigned len)
415 unsigned int sum = 0;
417 for (i = 0; i < len; i++) {
427 out[i] = in[i] + sum/2;
432 png_paeth(uint8_t a, uint8_t b, uint8_t c)
434 int p = (int)a + b - c;
441 if (pa <= pb && pa <= pc)
452 png_filter_paeth(unsigned stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
460 for (i = 0; i < len; i++) {
461 if (prev_line && i >= stride) {
464 c = prev_line[i - stride];
479 out[i] = in[i] + png_paeth(a, b, c);
484 png_unfilter(png_t *png, uint8_t *data)
489 uint8_t *filtered = png->png_data;
490 unsigned stride = png->bpp;
492 while (pos < png->png_datalen) {
493 uint8_t filter = filtered[pos];
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];
506 memcpy(data+outpos, filtered+pos, png->width * stride);
509 png_filter_sub(stride, filtered+pos, data+outpos,
510 png->width * stride);
514 png_filter_up(stride, filtered+pos, data+outpos,
515 data + outpos - (png->width*stride),
518 png_filter_up(stride, filtered+pos, data+outpos,
519 0, png->width*stride);
522 case 3: /* average */
524 png_filter_average(stride, filtered+pos,
526 data + outpos - (png->width*stride),
529 png_filter_average(stride, filtered+pos,
530 data+outpos, 0, png->width*stride);
535 png_filter_paeth(stride, filtered+pos,
537 data + outpos - (png->width*stride),
540 png_filter_paeth(stride, filtered+pos,
541 data+outpos, 0, png->width*stride);
545 return (PNG_UNKNOWN_FILTER);
548 outpos += png->width * stride;
549 pos += png->width * stride;
552 return (PNG_NO_ERROR);
556 png_get_data(png_t *png, uint8_t *data)
558 int result = PNG_NO_ERROR;
561 png->png_datalen = 0;
562 png->png_data = NULL;
566 while (result == PNG_NO_ERROR)
567 result = png_process_chunk(png);
574 (void) png_end_inflate(png);
576 if (result != PNG_DONE) {
581 result = png_unfilter(png, data);
589 png_error_string(int error)
595 return ("Unknown file error.");
596 case PNG_HEADER_ERROR:
597 return ("No PNG header found. Are you sure this is a PNG?");
599 return ("Failure while reading file.");
601 return ("Reached end of file.");
603 return ("CRC or chunk length error.");
604 case PNG_MEMORY_ERROR:
605 return ("Could not allocate memory.");
607 return ("zlib reported an error.");
608 case PNG_UNKNOWN_FILTER:
609 return ("Unknown filter method used in scanline.");
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 "
619 return ("Unknown error.");