xref: /illumos-gate/usr/src/common/pnglite/pnglite.c (revision 051a710dcbf091c85ceb0f5d86bd684a64639477)
1 /*
2  * pnglite.c - pnglite library
3  * For conditions of distribution and use, see copyright notice in pnglite.h
4  */
5 
6 #ifdef _STANDALONE
7 #include <sys/cdefs.h>
8 #include <stand.h>
9 #else
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #endif
16 #include <zlib.h>
17 #include "pnglite.h"
18 
19 #ifndef abs
20 #define	abs(x)	((x) < 0? -(x):(x))
21 #endif
22 
23 #define	PNG_32b(b, s) ((uint32_t)(b) << (s))
24 #define	PNG_U32(b1, b2, b3, b4) \
25 	(PNG_32b(b1, 24) | PNG_32b(b2, 16) | PNG_32b(b3, 8) | PNG_32b(b4, 0))
26 
27 #define	png_IDAT PNG_U32(73,  68,  65,  84)
28 #define	png_IEND PNG_U32(73,  69,  78,  68)
29 
30 static ssize_t
31 file_read(png_t *png, void *out, size_t size, size_t numel)
32 {
33 	ssize_t result;
34 	off_t offset = (off_t)(size * numel);
35 
36 	if (offset < 0)
37 		return (PNG_FILE_ERROR);
38 
39 	if (!out) {
40 		result = lseek(png->fd, offset, SEEK_CUR);
41 	} else {
42 		result = read(png->fd, out, size * numel);
43 	}
44 
45 	return (result);
46 }
47 
48 static int
49 file_read_ul(png_t *png, unsigned *out)
50 {
51 	uint8_t buf[4];
52 
53 	if (file_read(png, buf, 1, 4) != 4)
54 		return (PNG_FILE_ERROR);
55 
56 	*out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
57 
58 	return (PNG_NO_ERROR);
59 }
60 
61 static unsigned
62 get_ul(uint8_t *buf)
63 {
64 	unsigned result;
65 	uint8_t foo[4];
66 
67 	memcpy(foo, buf, 4);
68 
69 	result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
70 
71 	return (result);
72 }
73 
74 static int
75 png_get_bpp(png_t *png)
76 {
77 	int bpp;
78 
79 	switch (png->color_type) {
80 	case PNG_GREYSCALE:
81 		bpp = 1; break;
82 	case PNG_TRUECOLOR:
83 		bpp = 3; break;
84 	case PNG_INDEXED:
85 		bpp = 1; break;
86 	case PNG_GREYSCALE_ALPHA:
87 		bpp = 2; break;
88 	case PNG_TRUECOLOR_ALPHA:
89 		bpp = 4; break;
90 	default:
91 		return (PNG_FILE_ERROR);
92 	}
93 
94 	bpp *= png->depth / 8;
95 
96 	return (bpp);
97 }
98 
99 static int
100 png_read_ihdr(png_t *png)
101 {
102 	unsigned length;
103 	unsigned orig_crc;
104 	unsigned calc_crc;
105 	uint8_t ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
106 
107 	file_read_ul(png, &length);
108 
109 	if (length != 13)
110 		return (PNG_CRC_ERROR);
111 
112 	if (file_read(png, ihdr, 1, 13+4) != 13+4)
113 		return (PNG_EOF_ERROR);
114 
115 	file_read_ul(png, &orig_crc);
116 
117 	calc_crc = crc32(0L, Z_NULL, 0);
118 	calc_crc = crc32(calc_crc, ihdr, 13+4);
119 
120 	if (orig_crc != calc_crc) {
121 		return (PNG_CRC_ERROR);
122 	}
123 
124 	png->width = get_ul(ihdr+4);
125 	png->height = get_ul(ihdr+8);
126 	png->depth = ihdr[12];
127 	png->color_type = ihdr[13];
128 	png->compression_method = ihdr[14];
129 	png->filter_method = ihdr[15];
130 	png->interlace_method = ihdr[16];
131 
132 	if (png->color_type == PNG_INDEXED)
133 		return (PNG_NOT_SUPPORTED);
134 
135 	if (png->depth != 8 && png->depth != 16)
136 		return (PNG_NOT_SUPPORTED);
137 
138 	if (png->interlace_method)
139 		return (PNG_NOT_SUPPORTED);
140 
141 	return (PNG_NO_ERROR);
142 }
143 
144 void
145 png_print_info(png_t *png)
146 {
147 	printf("PNG INFO:\n");
148 	printf("\twidth:\t\t%d\n", png->width);
149 	printf("\theight:\t\t%d\n", png->height);
150 	printf("\tdepth:\t\t%d\n", png->depth);
151 	printf("\tcolor:\t\t");
152 
153 	switch (png->color_type) {
154 	case PNG_GREYSCALE:
155 		printf("greyscale\n"); break;
156 	case PNG_TRUECOLOR:
157 		printf("truecolor\n"); break;
158 	case PNG_INDEXED:
159 		printf("palette\n"); break;
160 	case PNG_GREYSCALE_ALPHA:
161 		printf("greyscale with alpha\n"); break;
162 	case PNG_TRUECOLOR_ALPHA:
163 		printf("truecolor with alpha\n"); break;
164 	default:
165 		printf("unknown, this is not good\n"); break;
166 	}
167 
168 	printf("\tcompression:\t%s\n",
169 	    png->compression_method?
170 	    "unknown, this is not good":"inflate/deflate");
171 	printf("\tfilter:\t\t%s\n",
172 	    png->filter_method? "unknown, this is not good":"adaptive");
173 	printf("\tinterlace:\t%s\n",
174 	    png->interlace_method? "interlace":"no interlace");
175 }
176 
177 int
178 png_open(png_t *png, const char *filename)
179 {
180 	char header[8];
181 	int result;
182 
183 	png->image = NULL;
184 	png->fd = open(filename, O_RDONLY);
185 	if (png->fd == -1)
186 		return (PNG_FILE_ERROR);
187 
188 	if (file_read(png, header, 1, 8) != 8) {
189 		result = PNG_EOF_ERROR;
190 		goto done;
191 	}
192 
193 	if (memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) {
194 		result = PNG_HEADER_ERROR;
195 		goto done;
196 	}
197 
198 	result = png_read_ihdr(png);
199 	if (result == PNG_NO_ERROR) {
200 		result = png_get_bpp(png);
201 		if (result > 0) {
202 			png->bpp = (uint8_t)result;
203 			result = PNG_NO_ERROR;
204 		}
205 	}
206 
207 done:
208 	if (result == PNG_NO_ERROR) {
209 		uint64_t size = png->width * png->height * png->bpp;
210 
211 		if (size < UINT_MAX)
212 			png->image = malloc(size);
213 		if (png->image == NULL)
214 			result = PNG_MEMORY_ERROR;
215 	}
216 
217 	if (result == PNG_NO_ERROR)
218 		result = png_get_data(png, png->image);
219 
220 	if (result != PNG_NO_ERROR) {
221 		free(png->image);
222 		close(png->fd);
223 		png->fd = -1;
224 		return (result);
225 	}
226 
227 	return (result);
228 }
229 
230 int
231 png_close(png_t *png)
232 {
233 	close(png->fd);
234 	png->fd = -1;
235 	free(png->image);
236 	png->image = NULL;
237 
238 	return (PNG_NO_ERROR);
239 }
240 
241 static int
242 png_init_inflate(png_t *png)
243 {
244 	z_stream *stream;
245 	png->zs = calloc(1, sizeof (z_stream));
246 
247 	stream = png->zs;
248 
249 	if (!stream)
250 		return (PNG_MEMORY_ERROR);
251 
252 	if (inflateInit(stream) != Z_OK) {
253 		free(png->zs);
254 		png->zs = NULL;
255 		return (PNG_ZLIB_ERROR);
256 	}
257 
258 	stream->next_out = png->png_data;
259 	stream->avail_out = png->png_datalen;
260 
261 	return (PNG_NO_ERROR);
262 }
263 
264 static int
265 png_end_inflate(png_t *png)
266 {
267 	z_stream *stream = png->zs;
268 	int rc = PNG_NO_ERROR;
269 
270 	if (!stream)
271 		return (PNG_MEMORY_ERROR);
272 
273 	if (inflateEnd(stream) != Z_OK) {
274 		printf("ZLIB says: %s\n", stream->msg);
275 		rc = PNG_ZLIB_ERROR;
276 	}
277 
278 	free(png->zs);
279 	png->zs = NULL;
280 
281 	return (rc);
282 }
283 
284 static int
285 png_inflate(png_t *png, uint8_t *data, int len)
286 {
287 	int result;
288 	z_stream *stream = png->zs;
289 
290 	if (!stream)
291 		return (PNG_MEMORY_ERROR);
292 
293 	stream->next_in = data;
294 	stream->avail_in = len;
295 
296 	result = inflate(stream, Z_SYNC_FLUSH);
297 
298 	if (result != Z_STREAM_END && result != Z_OK) {
299 		printf("%s\n", stream->msg);
300 		return (PNG_ZLIB_ERROR);
301 	}
302 
303 	if (stream->avail_in != 0)
304 		return (PNG_ZLIB_ERROR);
305 
306 	return (PNG_NO_ERROR);
307 }
308 
309 static int
310 png_read_idat(png_t *png, unsigned length)
311 {
312 	unsigned orig_crc;
313 	unsigned calc_crc;
314 	ssize_t len = length;
315 
316 	if (!png->readbuf || png->readbuflen < length) {
317 		png->readbuf = realloc(png->readbuf, length);
318 		png->readbuflen = length;
319 	}
320 
321 	if (!png->readbuf)
322 		return (PNG_MEMORY_ERROR);
323 
324 	if (file_read(png, png->readbuf, 1, length) != len)
325 		return (PNG_FILE_ERROR);
326 
327 	calc_crc = crc32(0L, Z_NULL, 0);
328 	calc_crc = crc32(calc_crc, (uint8_t *)"IDAT", 4);
329 	calc_crc = crc32(calc_crc, (uint8_t *)png->readbuf, length);
330 
331 	file_read_ul(png, &orig_crc);
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 	file_read_ul(png, &length);
347 
348 	if (file_read_ul(png, &type))
349 		return (PNG_FILE_ERROR);
350 
351 	/*
352 	 * if we found an idat, all other idats should be followed with no
353 	 * other chunks in between
354 	 */
355 	if (type == png_IDAT) {
356 		if (!png->png_data) {	/* first IDAT */
357 			png->png_datalen = png->width * png->height *
358 			    png->bpp + png->height;
359 			png->png_data = malloc(png->png_datalen);
360 		}
361 
362 		if (!png->png_data)
363 			return (PNG_MEMORY_ERROR);
364 
365 		if (!png->zs) {
366 			result = png_init_inflate(png);
367 			if (result != PNG_NO_ERROR)
368 				return (result);
369 		}
370 
371 		return (png_read_idat(png, length));
372 	} else if (type == png_IEND)
373 		return (PNG_DONE);
374 	else
375 		file_read(png, 0, 1, length + 4); /* unknown chunk */
376 
377 	return (result);
378 }
379 
380 static void
381 png_filter_sub(unsigned stride, uint8_t *in, uint8_t *out, unsigned len)
382 {
383 	unsigned i;
384 	uint8_t a = 0;
385 
386 	for (i = 0; i < len; i++) {
387 		if (i >= stride)
388 			a = out[i - stride];
389 
390 		out[i] = in[i] + a;
391 	}
392 }
393 
394 static void
395 png_filter_up(unsigned stride __unused, uint8_t *in, uint8_t *out,
396     uint8_t *prev_line, unsigned len)
397 {
398 	unsigned i;
399 
400 	if (prev_line) {
401 		for (i = 0; i < len; i++)
402 			out[i] = in[i] + prev_line[i];
403 	} else
404 		memcpy(out, in, len);
405 }
406 
407 static void
408 png_filter_average(unsigned stride, uint8_t *in, uint8_t *out,
409     uint8_t *prev_line, unsigned len)
410 {
411 	unsigned int i;
412 	uint8_t a = 0;
413 	uint8_t b = 0;
414 	unsigned int sum = 0;
415 
416 	for (i = 0; i < len; i++) {
417 		if (prev_line)
418 			b = prev_line[i];
419 
420 		if (i >= stride)
421 			a = out[i - stride];
422 
423 		sum = a;
424 		sum += b;
425 
426 		out[i] = in[i] + sum/2;
427 	}
428 }
429 
430 static uint8_t
431 png_paeth(uint8_t a, uint8_t b, uint8_t c)
432 {
433 	int p = (int)a + b - c;
434 	int pa = abs(p - a);
435 	int pb = abs(p - b);
436 	int pc = abs(p - c);
437 
438 	int pr;
439 
440 	if (pa <= pb && pa <= pc)
441 		pr = a;
442 	else if (pb <= pc)
443 		pr = b;
444 	else
445 		pr = c;
446 
447 	return (pr);
448 }
449 
450 static void
451 png_filter_paeth(unsigned stride, uint8_t *in, uint8_t *out, uint8_t *prev_line,
452     unsigned len)
453 {
454 	unsigned i;
455 	uint8_t a;
456 	uint8_t b;
457 	uint8_t c;
458 
459 	for (i = 0; i < len; i++) {
460 		if (prev_line && i >= stride) {
461 			a = out[i - stride];
462 			b = prev_line[i];
463 			c = prev_line[i - stride];
464 		} else {
465 			if (prev_line)
466 				b = prev_line[i];
467 			else
468 				b = 0;
469 
470 			if (i >= stride)
471 				a = out[i - stride];
472 			else
473 				a = 0;
474 
475 			c = 0;
476 		}
477 
478 		out[i] = in[i] + png_paeth(a, b, c);
479 	}
480 }
481 
482 static int
483 png_unfilter(png_t *png, uint8_t *data)
484 {
485 	unsigned i;
486 	unsigned pos = 0;
487 	unsigned outpos = 0;
488 	uint8_t *filtered = png->png_data;
489 	unsigned stride = png->bpp;
490 
491 	while (pos < png->png_datalen) {
492 		uint8_t filter = filtered[pos];
493 
494 		pos++;
495 
496 		if (png->depth == 16) {
497 			for (i = 0; i < png->width * stride; i += 2) {
498 				*(short *)(filtered+pos+i) =
499 				    (filtered[pos+i] << 8) | filtered[pos+i+1];
500 			}
501 		}
502 
503 		switch (filter) {
504 		case 0: /* none */
505 			memcpy(data+outpos, filtered+pos, png->width * stride);
506 			break;
507 		case 1: /* sub */
508 			png_filter_sub(stride, filtered+pos, data+outpos,
509 			    png->width * stride);
510 			break;
511 		case 2: /* up */
512 			if (outpos) {
513 				png_filter_up(stride, filtered+pos, data+outpos,
514 				    data + outpos - (png->width*stride),
515 				    png->width*stride);
516 			} else {
517 				png_filter_up(stride, filtered+pos, data+outpos,
518 				    0, png->width*stride);
519 			}
520 			break;
521 		case 3: /* average */
522 			if (outpos) {
523 				png_filter_average(stride, filtered+pos,
524 				    data+outpos,
525 				    data + outpos - (png->width*stride),
526 				    png->width*stride);
527 			} else {
528 				png_filter_average(stride, filtered+pos,
529 				    data+outpos, 0, png->width*stride);
530 			}
531 			break;
532 		case 4: /* paeth */
533 			if (outpos) {
534 				png_filter_paeth(stride, filtered+pos,
535 				    data+outpos,
536 				    data + outpos - (png->width*stride),
537 				    png->width*stride);
538 			} else {
539 				png_filter_paeth(stride, filtered+pos,
540 				    data+outpos, 0, png->width*stride);
541 			}
542 			break;
543 		default:
544 			return (PNG_UNKNOWN_FILTER);
545 		}
546 
547 		outpos += png->width * stride;
548 		pos += png->width * stride;
549 	}
550 
551 	return (PNG_NO_ERROR);
552 }
553 
554 int
555 png_get_data(png_t *png, uint8_t *data)
556 {
557 	int result = PNG_NO_ERROR;
558 
559 	png->zs = NULL;
560 	png->png_datalen = 0;
561 	png->png_data = NULL;
562 	png->readbuf = NULL;
563 	png->readbuflen = 0;
564 
565 	while (result == PNG_NO_ERROR)
566 		result = png_process_chunk(png);
567 
568 	if (png->readbuf) {
569 		free(png->readbuf);
570 		png->readbuflen = 0;
571 	}
572 	if (png->zs)
573 		png_end_inflate(png);
574 
575 	if (result != PNG_DONE) {
576 		free(png->png_data);
577 		return (result);
578 	}
579 
580 	result = png_unfilter(png, data);
581 
582 	free(png->png_data);
583 
584 	return (result);
585 }
586 
587 char *
588 png_error_string(int error)
589 {
590 	switch (error) {
591 	case PNG_NO_ERROR:
592 		return ("No error");
593 	case PNG_FILE_ERROR:
594 		return ("Unknown file error.");
595 	case PNG_HEADER_ERROR:
596 		return ("No PNG header found. Are you sure this is a PNG?");
597 	case PNG_IO_ERROR:
598 		return ("Failure while reading file.");
599 	case PNG_EOF_ERROR:
600 		return ("Reached end of file.");
601 	case PNG_CRC_ERROR:
602 		return ("CRC or chunk length error.");
603 	case PNG_MEMORY_ERROR:
604 		return ("Could not allocate memory.");
605 	case PNG_ZLIB_ERROR:
606 		return ("zlib reported an error.");
607 	case PNG_UNKNOWN_FILTER:
608 		return ("Unknown filter method used in scanline.");
609 	case PNG_DONE:
610 		return ("PNG done");
611 	case PNG_NOT_SUPPORTED:
612 		return ("The PNG is unsupported by pnglite, too bad for you!");
613 	case PNG_WRONG_ARGUMENTS:
614 		return ("Wrong combination of arguments passed to png_open. "
615 		    "You must use either a read_function or supply a file "
616 		    "pointer to use.");
617 	default:
618 		return ("Unknown error.");
619 	};
620 }
621