root/ext/gd/libgd/gd_png.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gdPngGetVersionString
  2. gdPngErrorHandler
  3. gdPngReadData
  4. gdPngWriteData
  5. gdPngFlushData
  6. gdImageCreateFromPng
  7. gdImageCreateFromPngPtr
  8. gdImageCreateFromPngCtx
  9. gdImagePngEx
  10. gdImagePng
  11. gdImagePngPtr
  12. gdImagePngPtrEx
  13. gdImagePngCtx
  14. gdImagePngCtxEx

   1 #include <stdio.h>
   2 #include <math.h>
   3 #include <string.h>
   4 #include <stdlib.h>
   5 #include "gd.h"
   6 
   7 /* JCE: Arrange HAVE_LIBPNG so that it can be set in gd.h */
   8 #ifdef HAVE_LIBPNG
   9 
  10 #include "png.h"                /* includes zlib.h and setjmp.h */
  11 #include "gdhelpers.h"
  12 
  13 #define TRUE 1
  14 #define FALSE 0
  15 
  16 /*---------------------------------------------------------------------------
  17 
  18     gd_png.c                 Copyright 1999 Greg Roelofs and Thomas Boutell
  19 
  20     The routines in this file, gdImagePng*() and gdImageCreateFromPng*(),
  21     are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(),
  22     except that these functions are noisier in the case of errors (comment
  23     out all fprintf() statements to disable that).
  24 
  25     GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs.
  26     GD 2.0 supports 8 bits of color resolution per channel and
  27     7 bits of alpha channel resolution. Images with more than 8 bits
  28     per channel are reduced to 8 bits. Images with an alpha channel are
  29     only able to resolve down to '1/128th opaque' instead of '1/256th',
  30     and this conversion is also automatic. I very much doubt you can see it.
  31     Both tRNS and true alpha are supported.
  32 
  33     Gamma is ignored, and there is no support for text annotations.
  34 
  35     Last updated:  9 February 2001
  36 
  37   ---------------------------------------------------------------------------*/
  38 
  39 const char * gdPngGetVersionString()
  40 {
  41         return PNG_LIBPNG_VER_STRING;
  42 }
  43 
  44 #ifdef PNG_SETJMP_SUPPORTED
  45 typedef struct _jmpbuf_wrapper
  46 {
  47         jmp_buf jmpbuf;
  48 } jmpbuf_wrapper;
  49 
  50 static void gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
  51 {
  52         jmpbuf_wrapper *jmpbuf_ptr;
  53 
  54         /* This function, aside from the extra step of retrieving the "error
  55          * pointer" (below) and the fact that it exists within the application
  56          * rather than within libpng, is essentially identical to libpng's
  57          * default error handler.  The second point is critical:  since both
  58          * setjmp() and longjmp() are called from the same code, they are
  59          * guaranteed to have compatible notions of how big a jmp_buf is,
  60          * regardless of whether _BSD_SOURCE or anything else has (or has not)
  61          * been defined.
  62          */
  63 
  64         php_gd_error_ex(E_WARNING, "gd-png:  fatal libpng error: %s", msg);
  65 
  66         jmpbuf_ptr = png_get_error_ptr (png_ptr);
  67         if (jmpbuf_ptr == NULL) { /* we are completely hosed now */
  68                 php_gd_error_ex(E_ERROR, "gd-png:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
  69         }
  70 
  71         longjmp (jmpbuf_ptr->jmpbuf, 1);
  72 }
  73 #endif
  74 
  75 static void gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length)
  76 {
  77         int check;
  78         check = gdGetBuf(data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
  79         if (check != length) {
  80                 png_error(png_ptr, "Read Error: truncated data");
  81         }
  82 }
  83 
  84 static void gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length)
  85 {
  86         gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
  87 }
  88 
  89 static void gdPngFlushData (png_structp png_ptr)
  90 {
  91 }
  92 
  93 gdImagePtr gdImageCreateFromPng (FILE * inFile)
  94 {
  95         gdImagePtr im;
  96         gdIOCtx *in = gdNewFileCtx(inFile);
  97         im = gdImageCreateFromPngCtx(in);
  98         in->gd_free(in);
  99 
 100         return im;
 101 }
 102 
 103 gdImagePtr gdImageCreateFromPngPtr (int size, void *data)
 104 {
 105         gdImagePtr im;
 106         gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
 107         im = gdImageCreateFromPngCtx(in);
 108         in->gd_free(in);
 109         return im;
 110 }
 111 
 112 /* This routine is based in part on the Chapter 13 demo code in "PNG: The
 113  *  Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html).
 114  */
 115 gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
 116 {
 117         png_byte sig[8];
 118 #ifdef PNG_SETJMP_SUPPORTED
 119         jmpbuf_wrapper jbw;
 120 #endif
 121         png_structp png_ptr;
 122         png_infop info_ptr;
 123         png_uint_32 width, height, rowbytes, w, h;
 124         int bit_depth, color_type, interlace_type;
 125         int num_palette, num_trans;
 126         png_colorp palette;
 127         png_color_16p trans_gray_rgb;
 128         png_color_16p trans_color_rgb;
 129         png_bytep trans;
 130         volatile png_bytep image_data = NULL;
 131         volatile png_bytepp row_pointers = NULL;
 132         gdImagePtr im = NULL;
 133         int i, j, *open = NULL;
 134         volatile int transparent = -1;
 135         volatile int palette_allocated = FALSE;
 136 
 137 
 138         /* Make sure the signature can't match by dumb luck -- TBB */
 139         /* GRR: isn't sizeof(infile) equal to the size of the pointer? */
 140         memset (sig, 0, sizeof(sig));
 141 
 142           /* first do a quick check that the file really is a PNG image; could
 143            * have used slightly more general png_sig_cmp() function instead
 144            */
 145         if (gdGetBuf(sig, 8, infile) < 8) {
 146                 return NULL;
 147         }
 148 
 149         if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */
 150                 return NULL;
 151         }
 152 
 153 #ifdef PNG_SETJMP_SUPPORTED
 154         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, NULL);
 155 #else
 156         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 157 #endif
 158         if (png_ptr == NULL) {
 159                 php_gd_error("gd-png error: cannot allocate libpng main struct");
 160                 return NULL;
 161         }
 162 
 163         info_ptr = png_create_info_struct(png_ptr);
 164         if (info_ptr == NULL) {
 165                 php_gd_error("gd-png error: cannot allocate libpng info struct");
 166                 png_destroy_read_struct (&png_ptr, NULL, NULL);
 167 
 168                 return NULL;
 169         }
 170 
 171         /* we could create a second info struct here (end_info), but it's only
 172          * useful if we want to keep pre- and post-IDAT chunk info separated
 173          * (mainly for PNG-aware image editors and converters)
 174          */
 175 
 176         /* setjmp() must be called in every non-callback function that calls a
 177          * PNG-reading libpng function
 178          */
 179 #ifdef PNG_SETJMP_SUPPORTED
 180         if (setjmp(jbw.jmpbuf)) {
 181                 php_gd_error("gd-png error: setjmp returns error condition");
 182                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 183 
 184                 return NULL;
 185         }
 186 #endif
 187 
 188         png_set_sig_bytes(png_ptr, 8);  /* we already read the 8 signature bytes */
 189 
 190         png_set_read_fn(png_ptr, (void *) infile, gdPngReadData);
 191         png_read_info(png_ptr, info_ptr);       /* read all PNG info up to image data */
 192 
 193         png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
 194         if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
 195                 || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
 196                 im = gdImageCreateTrueColor((int) width, (int) height);
 197         } else {
 198                 im = gdImageCreate((int) width, (int) height);
 199         }
 200         if (im == NULL) {
 201                 php_gd_error("gd-png error: cannot allocate gdImage struct");
 202                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 203 
 204                 return NULL;
 205         }
 206 
 207         if (bit_depth == 16) {
 208                 png_set_strip_16(png_ptr);
 209         } else if (bit_depth < 8) {
 210                 png_set_packing (png_ptr); /* expand to 1 byte per pixel */
 211         }
 212 
 213         /* setjmp() must be called in every non-callback function that calls a
 214          * PNG-reading libpng function
 215          */
 216 #ifdef PNG_SETJMP_SUPPORTED
 217         if (setjmp(jbw.jmpbuf)) {
 218                 php_gd_error("gd-png error: setjmp returns error condition");
 219                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 220                 gdFree(image_data);
 221                 gdFree(row_pointers);
 222                 if (im) {
 223                         gdImageDestroy(im);
 224                 }
 225                 return NULL;
 226         }
 227 #endif
 228 
 229         switch (color_type) {
 230                 case PNG_COLOR_TYPE_PALETTE:
 231                         png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
 232 #ifdef DEBUG
 233                         php_gd_error("gd-png color_type is palette, colors: %d", num_palette);
 234 #endif /* DEBUG */
 235                         if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
 236                                 /* gd 2.0: we support this rather thoroughly now. Grab the
 237                                  * first fully transparent entry, if any, as the value of
 238                                  * the simple-transparency index, mostly for backwards
 239                                  * binary compatibility. The alpha channel is where it's
 240                                  * really at these days.
 241                                  */
 242                                 int firstZero = 1;
 243                                 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
 244                                 for (i = 0; i < num_trans; ++i) {
 245                                         im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
 246                                         if ((trans[i] == 0) && (firstZero)) {
 247                                                 transparent = i;
 248                                                 firstZero = 0;
 249                                         }
 250                                 }
 251                         }
 252                         break;
 253                 case PNG_COLOR_TYPE_GRAY:
 254                         /* create a fake palette and check for single-shade transparency */
 255                         if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) {
 256                                 php_gd_error("gd-png error: cannot allocate gray palette");
 257                                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 258 
 259                                 return NULL;
 260                         }
 261                         palette_allocated = TRUE;
 262                         if (bit_depth < 8) {
 263                                 num_palette = 1 << bit_depth;
 264                                 for (i = 0; i < 256; ++i) {
 265                                         j = (255 * i) / (num_palette - 1);
 266                                         palette[i].red = palette[i].green = palette[i].blue = j;
 267                                 }
 268                         } else {
 269                                 num_palette = 256;
 270                                 for (i = 0; i < 256; ++i) {
 271                                         palette[i].red = palette[i].green = palette[i].blue = i;
 272                                 }
 273                         }
 274                         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
 275                                 png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
 276                                 if (bit_depth == 16) {  /* png_set_strip_16() not yet in effect */
 277                                         transparent = trans_gray_rgb->gray >> 8;
 278                                 } else {
 279                                         transparent = trans_gray_rgb->gray;
 280                                 }
 281                                 /* Note slight error in 16-bit case:  up to 256 16-bit shades
 282                                  * may get mapped to a single 8-bit shade, and only one of them
 283                                  * is supposed to be transparent.  IOW, both opaque pixels and
 284                                  * transparent pixels will be mapped into the transparent entry.
 285                                  * There is no particularly good way around this in the case
 286                                  * that all 256 8-bit shades are used, but one could write some
 287                                  * custom 16-bit code to handle the case where there are gdFree
 288                                  * palette entries.  This error will be extremely rare in
 289                                  * general, though.  (Quite possibly there is only one such
 290                                  * image in existence.)
 291                                  */
 292                         }
 293                         break;
 294 
 295                 case PNG_COLOR_TYPE_GRAY_ALPHA:
 296                         png_set_gray_to_rgb(png_ptr);
 297 
 298                         case PNG_COLOR_TYPE_RGB:
 299                         case PNG_COLOR_TYPE_RGB_ALPHA:
 300                                 /* gd 2.0: we now support truecolor. See the comment above
 301                                  * for a rare situation in which the transparent pixel may not
 302                                  * work properly with 16-bit channels.
 303                                  */
 304                                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
 305                                         png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
 306                                         if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
 307                                                 transparent = gdTrueColor(trans_color_rgb->red >> 8,
 308                                                                         trans_color_rgb->green >> 8,
 309                                                                         trans_color_rgb->blue >> 8);
 310                                         } else {
 311                                                 transparent = gdTrueColor(trans_color_rgb->red,
 312                                                                         trans_color_rgb->green,
 313                                                                         trans_color_rgb->blue);
 314                                         }
 315                                 }
 316                                 break;
 317         }
 318 
 319         png_read_update_info(png_ptr, info_ptr);
 320 
 321         /* allocate space for the PNG image data */
 322         rowbytes = png_get_rowbytes(png_ptr, info_ptr);
 323         image_data = (png_bytep) safe_emalloc(rowbytes, height, 0);
 324 
 325         row_pointers = (png_bytepp) safe_emalloc(height, sizeof(png_bytep), 0);
 326 
 327         /* set the individual row_pointers to point at the correct offsets */
 328         for (h = 0; h < height; ++h) {
 329                 row_pointers[h] = image_data + h * rowbytes;
 330         }
 331 
 332         png_read_image(png_ptr, row_pointers);  /* read whole image... */
 333         png_read_end(png_ptr, NULL);            /* ...done! */
 334 
 335         if (!im->trueColor) {
 336                 im->colorsTotal = num_palette;
 337                 /* load the palette and mark all entries "open" (unused) for now */
 338                 open = im->open;
 339                 for (i = 0; i < num_palette; ++i) {
 340                         im->red[i] = palette[i].red;
 341                         im->green[i] = palette[i].green;
 342                         im->blue[i] = palette[i].blue;
 343                         open[i] = 1;
 344                 }
 345                 for (i = num_palette; i < gdMaxColors; ++i) {
 346                         open[i] = 1;
 347                 }
 348         }
 349 
 350         /* 2.0.12: Slaven Rezic: palette images are not the only images
 351          * with a simple transparent color setting.
 352          */
 353         im->transparent = transparent;
 354         im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
 355 
 356         /* can't nuke structs until done with palette */
 357         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 358         switch (color_type) {
 359                 case PNG_COLOR_TYPE_RGB:
 360                         for (h = 0; h < height; h++) {
 361                                 int boffset = 0;
 362                                 for (w = 0; w < width; w++) {
 363                                         register png_byte r = row_pointers[h][boffset++];
 364                                         register png_byte g = row_pointers[h][boffset++];
 365                                         register png_byte b = row_pointers[h][boffset++];
 366                                         im->tpixels[h][w] = gdTrueColor (r, g, b);
 367                                 }
 368                         }
 369                         break;
 370 
 371                 case PNG_COLOR_TYPE_GRAY_ALPHA:
 372                 case PNG_COLOR_TYPE_RGB_ALPHA:
 373                         for (h = 0; h < height; h++) {
 374                                 int boffset = 0;
 375                                 for (w = 0; w < width; w++) {
 376                                         register png_byte r = row_pointers[h][boffset++];
 377                                         register png_byte g = row_pointers[h][boffset++];
 378                                         register png_byte b = row_pointers[h][boffset++];
 379 
 380                                         /* gd has only 7 bits of alpha channel resolution, and
 381                                          * 127 is transparent, 0 opaque. A moment of convenience,
 382                                          *  a lifetime of compatibility.
 383                                          */
 384 
 385                                         register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1);
 386                                         im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a);
 387                                 }
 388                         }
 389                         break;
 390 
 391                 default:
 392                         /* Palette image, or something coerced to be one */
 393                         for (h = 0; h < height; ++h) {
 394                                 for (w = 0; w < width; ++w) {
 395                                         register png_byte idx = row_pointers[h][w];
 396                                         im->pixels[h][w] = idx;
 397                                         open[idx] = 0;
 398                                 }
 399                         }
 400         }
 401 #ifdef DEBUG
 402         if (!im->trueColor) {
 403                 for (i = num_palette; i < gdMaxColors; ++i) {
 404                         if (!open[i]) {
 405                                 php_gd_error("gd-png warning: image data references out-of-range color index (%d)", i);
 406                         }
 407                 }
 408         }
 409 #endif
 410 
 411         if (palette_allocated) {
 412                 gdFree(palette);
 413         }
 414         gdFree(image_data);
 415         gdFree(row_pointers);
 416 
 417         return im;
 418 }
 419 
 420 void gdImagePngEx (gdImagePtr im, FILE * outFile, int level, int basefilter)
 421 {
 422         gdIOCtx *out = gdNewFileCtx(outFile);
 423         gdImagePngCtxEx(im, out, level, basefilter);
 424         out->gd_free(out);
 425 }
 426 
 427 void gdImagePng (gdImagePtr im, FILE * outFile)
 428 {
 429         gdIOCtx *out = gdNewFileCtx(outFile);
 430         gdImagePngCtxEx(im, out, -1, -1);
 431         out->gd_free(out);
 432 }
 433 
 434 void * gdImagePngPtr (gdImagePtr im, int *size)
 435 {
 436         void *rv;
 437         gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
 438         gdImagePngCtxEx(im, out, -1, -1);
 439         rv = gdDPExtractData(out, size);
 440         out->gd_free(out);
 441 
 442         return rv;
 443 }
 444 
 445 void * gdImagePngPtrEx (gdImagePtr im, int *size, int level, int basefilter)
 446 {
 447         void *rv;
 448         gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
 449         gdImagePngCtxEx(im, out, level, basefilter);
 450         rv = gdDPExtractData(out, size);
 451         out->gd_free(out);
 452         return rv;
 453 }
 454 
 455 void gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
 456 {
 457         gdImagePngCtxEx(im, outfile, -1, -1);
 458 }
 459 
 460 /* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
 461  *  and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
 462  *  (http://www.cdrom.com/pub/png/pngbook.html).
 463  */
 464 void gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level, int basefilter)
 465 {
 466         int i, j, bit_depth = 0, interlace_type;
 467         int width = im->sx;
 468         int height = im->sy;
 469         int colors = im->colorsTotal;
 470         int *open = im->open;
 471         int mapping[gdMaxColors];       /* mapping[gd_index] == png_index */
 472         png_byte trans_values[256];
 473         png_color_16 trans_rgb_value;
 474         png_color palette[gdMaxColors];
 475         png_structp png_ptr;
 476         png_infop info_ptr;
 477         volatile int transparent = im->transparent;
 478         volatile int remap = FALSE;
 479 #ifdef PNG_SETJMP_SUPPORTED
 480         jmpbuf_wrapper jbw;
 481 
 482         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, NULL);
 483 #else
 484         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 485 #endif
 486         if (png_ptr == NULL) {
 487                 php_gd_error("gd-png error: cannot allocate libpng main struct");
 488                 return;
 489         }
 490 
 491         info_ptr = png_create_info_struct(png_ptr);
 492         if (info_ptr == NULL) {
 493                 php_gd_error("gd-png error: cannot allocate libpng info struct");
 494                 png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
 495 
 496                 return;
 497     }
 498 
 499 #ifdef PNG_SETJMP_SUPPORTED
 500         if (setjmp(jbw.jmpbuf)) {
 501                 php_gd_error("gd-png error: setjmp returns error condition");
 502                 png_destroy_write_struct (&png_ptr, &info_ptr);
 503 
 504                 return;
 505         }
 506 #endif
 507 
 508         png_set_write_fn(png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
 509 
 510         /* This is best for palette images, and libpng defaults to it for
 511          * palette images anyway, so we don't need to do it explicitly.
 512          * What to ideally do for truecolor images depends, alas, on the image.
 513          * gd is intentionally imperfect and doesn't spend a lot of time
 514          * fussing with such things.
 515          */
 516 
 517         /*  png_set_filter(png_ptr, 0, PNG_FILTER_NONE);  */
 518 
 519         /* 2.0.12: this is finally a parameter */
 520         if (level != -1 && (level < 0 || level > 9)) {
 521                 php_gd_error("gd-png error: compression level must be 0 through 9");
 522                 return;
 523         }
 524         png_set_compression_level(png_ptr, level);
 525         if (basefilter >= 0) {
 526                 png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, basefilter);
 527         }
 528 
 529         /* can set this to a smaller value without compromising compression if all
 530          * image data is 16K or less; will save some decoder memory [min == 8]
 531          */
 532 
 533         /*  png_set_compression_window_bits(png_ptr, 15);  */
 534 
 535         if (!im->trueColor) {
 536                 if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) {
 537                         transparent = -1;
 538                 }
 539 
 540                 for (i = 0; i < gdMaxColors; ++i) {
 541                         mapping[i] = -1;
 542                 }
 543 
 544                 /* count actual number of colors used (colorsTotal == high-water mark) */
 545                 colors = 0;
 546                 for (i = 0; i < im->colorsTotal; ++i) {
 547                         if (!open[i]) {
 548                                 mapping[i] = colors;
 549                                 ++colors;
 550                         }
 551                 }
 552                 if (colors == 0) {
 553                         php_gd_error("gd-png error: no colors in palette");
 554                         goto bail;
 555                 }
 556                 if (colors < im->colorsTotal) {
 557                         remap = TRUE;
 558                 }
 559                 if (colors <= 2) {
 560                         bit_depth = 1;
 561                 } else if (colors <= 4) {
 562                         bit_depth = 2;
 563                 } else if (colors <= 16) {
 564                         bit_depth = 4;
 565                 } else {
 566                         bit_depth = 8;
 567                 }
 568         }
 569 
 570         interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
 571 
 572         if (im->trueColor) {
 573                 if (im->saveAlphaFlag) {
 574                         png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
 575                                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 576                 } else {
 577                         png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type,
 578                                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 579                 }
 580         } else {
 581                 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type,
 582                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 583         }
 584 
 585         if (im->trueColor && !im->saveAlphaFlag && (transparent >= 0)) {
 586                 /* 2.0.9: fixed by Thomas Winzig */
 587                 trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
 588                 trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
 589                 trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
 590                 png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value);
 591         }
 592 
 593         if (!im->trueColor) {
 594                 /* Oy veh. Remap the PNG palette to put the entries with interesting alpha channel
 595                  * values first. This minimizes the size of the tRNS chunk and thus the size
 596                  * of the PNG file as a whole.
 597                  */
 598 
 599                 int tc = 0;
 600                 int i;
 601                 int j;
 602                 int k;
 603 
 604                 for (i = 0; (i < im->colorsTotal); i++) {
 605                         if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) {
 606                                 tc++;
 607                         }
 608                 }
 609                 if (tc) {
 610 #if 0
 611                         for (i = 0; (i < im->colorsTotal); i++) {
 612                                 trans_values[i] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
 613                         }
 614                         png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
 615 #endif
 616                         if (!remap) {
 617                                 remap = TRUE;
 618                         }
 619 
 620                         /* (Semi-)transparent indexes come up from the bottom of the list of real colors; opaque
 621                          * indexes come down from the top
 622                          */
 623                         j = 0;
 624                         k = colors - 1;
 625 
 626                         for (i = 0; i < im->colorsTotal; i++) {
 627                                 if (!im->open[i]) {
 628                                         if (im->alpha[i] != gdAlphaOpaque) {
 629                                                 /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
 630                                                 trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
 631                                                 mapping[i] = j++;
 632                                         } else {
 633                                                 mapping[i] = k--;
 634                                         }
 635                                 }
 636                         }
 637                         png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL);
 638                 }
 639         }
 640 
 641         /* convert palette to libpng layout */
 642         if (!im->trueColor) {
 643                 if (remap) {
 644                         for (i = 0; i < im->colorsTotal; ++i) {
 645                                 if (mapping[i] < 0) {
 646                                         continue;
 647                                 }
 648 
 649                                 palette[mapping[i]].red = im->red[i];
 650                                 palette[mapping[i]].green = im->green[i];
 651                                 palette[mapping[i]].blue = im->blue[i];
 652                         }
 653                 } else {
 654                         for (i = 0; i < colors; ++i) {
 655                                 palette[i].red = im->red[i];
 656                                 palette[i].green = im->green[i];
 657                                 palette[i].blue = im->blue[i];
 658                         }
 659                 }
 660                 png_set_PLTE(png_ptr, info_ptr, palette, colors);
 661         }
 662 
 663         /* write out the PNG header info (everything up to first IDAT) */
 664         png_write_info(png_ptr, info_ptr);
 665 
 666         /* make sure < 8-bit images are packed into pixels as tightly as possible */
 667         png_set_packing(png_ptr);
 668 
 669         /* This code allocates a set of row buffers and copies the gd image data
 670          * into them only in the case that remapping is necessary; in gd 1.3 and
 671          * later, the im->pixels array is laid out identically to libpng's row
 672          * pointers and can be passed to png_write_image() function directly.
 673          * The remapping case could be accomplished with less memory for non-
 674          * interlaced images, but interlacing causes some serious complications.
 675          */
 676 
 677         if (im->trueColor) {
 678                 /* performance optimizations by Phong Tran */
 679                 int channels = im->saveAlphaFlag ? 4 : 3;
 680                 /* Our little 7-bit alpha channel trick costs us a bit here. */
 681                 png_bytep *row_pointers;
 682                 unsigned char* pOutputRow;
 683                 int **ptpixels = im->tpixels;
 684                 int *pThisRow;
 685                 unsigned char a;
 686                 int thisPixel;
 687                 png_bytep *prow_pointers;
 688                 int saveAlphaFlag = im->saveAlphaFlag;
 689 
 690                 row_pointers = safe_emalloc(sizeof(png_bytep), height, 0);
 691                 prow_pointers = row_pointers;
 692                 for (j = 0; j < height; ++j) {
 693                         *prow_pointers = (png_bytep) safe_emalloc(width, channels, 0);
 694                         pOutputRow = *prow_pointers++;
 695                         pThisRow = *ptpixels++;
 696                         for (i = 0; i < width; ++i) {
 697                                 thisPixel = *pThisRow++;
 698                                 *pOutputRow++ = gdTrueColorGetRed(thisPixel);
 699                                 *pOutputRow++ = gdTrueColorGetGreen(thisPixel);
 700                                 *pOutputRow++ = gdTrueColorGetBlue(thisPixel);
 701                                 if (saveAlphaFlag) {
 702                                         /* convert the 7-bit alpha channel to an 8-bit alpha channel.
 703                                          * We do a little bit-flipping magic, repeating the MSB
 704                                          * as the LSB, to ensure that 0 maps to 0 and
 705                                          * 127 maps to 255. We also have to invert to match
 706                                          * PNG's convention in which 255 is opaque.
 707                                          */
 708                                         a = gdTrueColorGetAlpha(thisPixel);
 709                                         /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
 710                                         if (a == 127) {
 711                                                 *pOutputRow++ = 0;
 712                                         } else {
 713                                                 *pOutputRow++ = 255 - ((a << 1) + (a >> 6));
 714                                         }
 715 
 716                                 }
 717                         }
 718                 }
 719 
 720                 png_write_image(png_ptr, row_pointers);
 721                 png_write_end(png_ptr, info_ptr);
 722 
 723                 for (j = 0; j < height; ++j) {
 724                         gdFree(row_pointers[j]);
 725                 }
 726 
 727                 gdFree(row_pointers);
 728         } else {
 729                 if (remap) {
 730                         png_bytep *row_pointers;
 731                         row_pointers = safe_emalloc(height, sizeof(png_bytep), 0);
 732                         for (j = 0; j < height; ++j) {
 733                                 row_pointers[j] = (png_bytep) gdMalloc(width);
 734                                 for (i = 0; i < width; ++i) {
 735                                         row_pointers[j][i] = mapping[im->pixels[j][i]];
 736                                 }
 737                         }
 738 
 739                         png_write_image(png_ptr, row_pointers);
 740                         png_write_end(png_ptr, info_ptr);
 741 
 742                         for (j = 0; j < height; ++j) {
 743                                 gdFree(row_pointers[j]);
 744                         }
 745 
 746                         gdFree(row_pointers);
 747                 } else {
 748                         png_write_image(png_ptr, im->pixels);
 749                         png_write_end(png_ptr, info_ptr);
 750                 }
 751         }
 752         /* 1.6.3: maybe we should give that memory BACK! TBB */
 753  bail:
 754         png_destroy_write_struct(&png_ptr, &info_ptr);
 755 }
 756 
 757 #endif /* HAVE_LIBPNG */

/* [<][>][^][v][top][bottom][index][help] */