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