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