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