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