Back to index

php5  5.3.10
gd_png.c
Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <math.h>
00003 #include <string.h>
00004 #include <stdlib.h>
00005 #include "gd.h"
00006 
00007 /* JCE: Arrange HAVE_LIBPNG so that it can be set in gd.h */
00008 #ifdef HAVE_LIBPNG
00009 
00010 #include "png.h"            /* includes zlib.h and setjmp.h */
00011 #include "gdhelpers.h"
00012 
00013 #define TRUE 1
00014 #define FALSE 0
00015 
00016 /*---------------------------------------------------------------------------
00017 
00018     gd_png.c                 Copyright 1999 Greg Roelofs and Thomas Boutell
00019 
00020     The routines in this file, gdImagePng*() and gdImageCreateFromPng*(),
00021     are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(),
00022     except that these functions are noisier in the case of errors (comment
00023     out all fprintf() statements to disable that).
00024 
00025     GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs.
00026     GD 2.0 supports 8 bits of color resolution per channel and
00027     7 bits of alpha channel resolution. Images with more than 8 bits
00028     per channel are reduced to 8 bits. Images with an alpha channel are
00029     only able to resolve down to '1/128th opaque' instead of '1/256th',
00030     and this conversion is also automatic. I very much doubt you can see it.
00031     Both tRNS and true alpha are supported.
00032 
00033     Gamma is ignored, and there is no support for text annotations.
00034 
00035     Last updated:  9 February 2001
00036 
00037   ---------------------------------------------------------------------------*/
00038 
00039 const char * gdPngGetVersionString()
00040 {
00041        return PNG_LIBPNG_VER_STRING;
00042 }
00043 
00044 #ifdef PNG_SETJMP_SUPPORTED
00045 typedef struct _jmpbuf_wrapper
00046 {
00047        jmp_buf jmpbuf;
00048 } jmpbuf_wrapper;
00049 
00050 static void gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
00051 {
00052        jmpbuf_wrapper *jmpbuf_ptr;
00053 
00054        /* This function, aside from the extra step of retrieving the "error
00055         * pointer" (below) and the fact that it exists within the application
00056         * rather than within libpng, is essentially identical to libpng's
00057         * default error handler.  The second point is critical:  since both
00058         * setjmp() and longjmp() are called from the same code, they are
00059         * guaranteed to have compatible notions of how big a jmp_buf is,
00060         * regardless of whether _BSD_SOURCE or anything else has (or has not)
00061         * been defined.
00062         */
00063 
00064        php_gd_error_ex(E_WARNING, "gd-png:  fatal libpng error: %s", msg);
00065 
00066        jmpbuf_ptr = png_get_error_ptr (png_ptr);
00067        if (jmpbuf_ptr == NULL) { /* we are completely hosed now */
00068               php_gd_error_ex(E_ERROR, "gd-png:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
00069        }
00070 
00071        longjmp (jmpbuf_ptr->jmpbuf, 1);
00072 }
00073 #endif
00074 
00075 static void gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length)
00076 {
00077        int check;
00078        check = gdGetBuf(data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
00079        if (check != length) {
00080               png_error(png_ptr, "Read Error: truncated data");
00081        }
00082 }
00083 
00084 static void gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length)
00085 {
00086        gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
00087 }
00088 
00089 static void gdPngFlushData (png_structp png_ptr)
00090 {
00091 }
00092 
00093 gdImagePtr gdImageCreateFromPng (FILE * inFile)
00094 {
00095        gdImagePtr im;
00096        gdIOCtx *in = gdNewFileCtx(inFile);
00097        im = gdImageCreateFromPngCtx(in);
00098        in->gd_free(in);
00099 
00100        return im;
00101 }
00102 
00103 gdImagePtr gdImageCreateFromPngPtr (int size, void *data)
00104 {
00105        gdImagePtr im;
00106        gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
00107        im = gdImageCreateFromPngCtx(in);
00108        in->gd_free(in);
00109        return im;
00110 }
00111 
00112 /* This routine is based in part on the Chapter 13 demo code in "PNG: The
00113  *  Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html).
00114  */
00115 gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
00116 {
00117        png_byte sig[8];
00118 #ifdef PNG_SETJMP_SUPPORTED
00119        jmpbuf_wrapper jbw;
00120 #endif
00121        png_structp png_ptr;
00122        png_infop info_ptr;
00123        png_uint_32 width, height, rowbytes, w, h;
00124        int bit_depth, color_type, interlace_type;
00125        int num_palette, num_trans;
00126        png_colorp palette;
00127        png_color_16p trans_gray_rgb;
00128        png_color_16p trans_color_rgb;
00129        png_bytep trans;
00130        png_bytep image_data = NULL;
00131        png_bytepp row_pointers = NULL;
00132        gdImagePtr im = NULL;
00133        int i, j, *open = NULL;
00134        volatile int transparent = -1;
00135        volatile int palette_allocated = FALSE;
00136 
00137        /* Make sure the signature can't match by dumb luck -- TBB */
00138        /* GRR: isn't sizeof(infile) equal to the size of the pointer? */
00139        memset (sig, 0, sizeof(sig));
00140 
00141          /* first do a quick check that the file really is a PNG image; could
00142           * have used slightly more general png_sig_cmp() function instead
00143           */
00144        if (gdGetBuf(sig, 8, infile) < 8) {
00145               return NULL;
00146        }
00147 
00148        if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */
00149               return NULL;
00150        }
00151 
00152 #ifdef PNG_SETJMP_SUPPORTED
00153        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, NULL);
00154 #else
00155        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00156 #endif
00157        if (png_ptr == NULL) {
00158               php_gd_error("gd-png error: cannot allocate libpng main struct");
00159               return NULL;
00160        }
00161 
00162        info_ptr = png_create_info_struct(png_ptr);
00163        if (info_ptr == NULL) {
00164               php_gd_error("gd-png error: cannot allocate libpng info struct");
00165               png_destroy_read_struct (&png_ptr, NULL, NULL);
00166 
00167               return NULL;
00168        }
00169 
00170        /* we could create a second info struct here (end_info), but it's only
00171         * useful if we want to keep pre- and post-IDAT chunk info separated
00172         * (mainly for PNG-aware image editors and converters)
00173         */
00174 
00175        /* setjmp() must be called in every non-callback function that calls a
00176         * PNG-reading libpng function
00177         */
00178 #ifdef PNG_SETJMP_SUPPORTED
00179        if (setjmp(jbw.jmpbuf)) {
00180               php_gd_error("gd-png error: setjmp returns error condition");
00181               png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00182 
00183               return NULL;
00184        }
00185 #endif
00186 
00187        png_set_sig_bytes(png_ptr, 8);     /* we already read the 8 signature bytes */
00188 
00189        png_set_read_fn(png_ptr, (void *) infile, gdPngReadData);
00190        png_read_info(png_ptr, info_ptr);  /* read all PNG info up to image data */
00191 
00192        png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
00193        if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
00194               || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
00195               im = gdImageCreateTrueColor((int) width, (int) height);
00196        } else {
00197               im = gdImageCreate((int) width, (int) height);
00198        }
00199        if (im == NULL) {
00200               php_gd_error("gd-png error: cannot allocate gdImage struct");
00201               png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00202 
00203               return NULL;
00204        }
00205 
00206        if (bit_depth == 16) {
00207               png_set_strip_16(png_ptr);
00208        } else if (bit_depth < 8) {
00209               png_set_packing (png_ptr); /* expand to 1 byte per pixel */
00210        }
00211 
00212        /* setjmp() must be called in every non-callback function that calls a
00213         * PNG-reading libpng function
00214         */
00215 #ifdef PNG_SETJMP_SUPPORTED
00216        if (setjmp(jbw.jmpbuf)) {
00217               php_gd_error("gd-png error: setjmp returns error condition");
00218               png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00219               gdFree(image_data);
00220               gdFree(row_pointers);
00221               if (im) {
00222                      gdImageDestroy(im);
00223               }
00224               return NULL;
00225        }
00226 #endif
00227 
00228        switch (color_type) {
00229               case PNG_COLOR_TYPE_PALETTE:
00230                      png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
00231 #ifdef DEBUG
00232                      php_gd_error("gd-png color_type is palette, colors: %d", num_palette);
00233 #endif /* DEBUG */
00234                      if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
00235                             /* gd 2.0: we support this rather thoroughly now. Grab the
00236                              * first fully transparent entry, if any, as the value of
00237                              * the simple-transparency index, mostly for backwards
00238                              * binary compatibility. The alpha channel is where it's
00239                              * really at these days.
00240                              */
00241                             int firstZero = 1;
00242                             png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
00243                             for (i = 0; i < num_trans; ++i) {
00244                                    im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
00245                                    if ((trans[i] == 0) && (firstZero)) {
00246                                           transparent = i;
00247                                           firstZero = 0;
00248                                    }
00249                             }
00250                      }
00251                      break;
00252               case PNG_COLOR_TYPE_GRAY:
00253                      /* create a fake palette and check for single-shade transparency */
00254                      if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) {
00255                             php_gd_error("gd-png error: cannot allocate gray palette");
00256                             png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00257 
00258                             return NULL;
00259                      }
00260                      palette_allocated = TRUE;
00261                      if (bit_depth < 8) {
00262                             num_palette = 1 << bit_depth;
00263                             for (i = 0; i < 256; ++i) {
00264                                    j = (255 * i) / (num_palette - 1);
00265                                    palette[i].red = palette[i].green = palette[i].blue = j;
00266                             }
00267                      } else {
00268                             num_palette = 256;
00269                             for (i = 0; i < 256; ++i) {
00270                                    palette[i].red = palette[i].green = palette[i].blue = i;
00271                             }
00272                      }
00273                      if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00274                             png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
00275                             if (bit_depth == 16) {      /* png_set_strip_16() not yet in effect */
00276                                    transparent = trans_gray_rgb->gray >> 8;
00277                             } else {
00278                                    transparent = trans_gray_rgb->gray;
00279                             }
00280                             /* Note slight error in 16-bit case:  up to 256 16-bit shades
00281                              * may get mapped to a single 8-bit shade, and only one of them
00282                              * is supposed to be transparent.  IOW, both opaque pixels and
00283                              * transparent pixels will be mapped into the transparent entry.
00284                              * There is no particularly good way around this in the case
00285                              * that all 256 8-bit shades are used, but one could write some
00286                              * custom 16-bit code to handle the case where there are gdFree
00287                              * palette entries.  This error will be extremely rare in
00288                              * general, though.  (Quite possibly there is only one such
00289                              * image in existence.)
00290                              */
00291                      }
00292                      break;
00293 
00294               case PNG_COLOR_TYPE_GRAY_ALPHA:
00295                      png_set_gray_to_rgb(png_ptr);
00296 
00297                      case PNG_COLOR_TYPE_RGB:
00298                      case PNG_COLOR_TYPE_RGB_ALPHA:
00299                             /* gd 2.0: we now support truecolor. See the comment above
00300                              * for a rare situation in which the transparent pixel may not
00301                              * work properly with 16-bit channels.
00302                              */
00303                             if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00304                                    png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
00305                                    if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
00306                                           transparent = gdTrueColor(trans_color_rgb->red >> 8,
00307                                                                trans_color_rgb->green >> 8,
00308                                                                trans_color_rgb->blue >> 8);
00309                                    } else {
00310                                           transparent = gdTrueColor(trans_color_rgb->red,
00311                                                                trans_color_rgb->green,
00312                                                                trans_color_rgb->blue);
00313                                    }
00314                             }
00315                             break;
00316        }
00317 
00318        png_read_update_info(png_ptr, info_ptr);
00319 
00320        /* allocate space for the PNG image data */
00321        rowbytes = png_get_rowbytes(png_ptr, info_ptr);
00322        image_data = (png_bytep) safe_emalloc(rowbytes, height, 0);
00323 
00324        row_pointers = (png_bytepp) safe_emalloc(height, sizeof(png_bytep), 0);
00325 
00326        /* set the individual row_pointers to point at the correct offsets */
00327        for (h = 0; h < height; ++h) {
00328               row_pointers[h] = image_data + h * rowbytes;
00329        }
00330 
00331        png_read_image(png_ptr, row_pointers);    /* read whole image... */
00332        png_read_end(png_ptr, NULL);              /* ...done! */
00333 
00334        if (!im->trueColor) {
00335               im->colorsTotal = num_palette;
00336               /* load the palette and mark all entries "open" (unused) for now */
00337               open = im->open;
00338               for (i = 0; i < num_palette; ++i) {
00339                      im->red[i] = palette[i].red;
00340                      im->green[i] = palette[i].green;
00341                      im->blue[i] = palette[i].blue;
00342                      open[i] = 1;
00343               }
00344               for (i = num_palette; i < gdMaxColors; ++i) {
00345                      open[i] = 1;
00346               }
00347        }
00348        /* 2.0.12: Slaven Rezic: palette images are not the only images
00349         * with a simple transparent color setting.
00350         */
00351        im->transparent = transparent;
00352        im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
00353 
00354        /* can't nuke structs until done with palette */
00355        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00356        switch (color_type) {
00357               case PNG_COLOR_TYPE_RGB:
00358                      for (h = 0; h < height; h++) {
00359                             int boffset = 0;
00360                             for (w = 0; w < width; w++) {
00361                                    register png_byte r = row_pointers[h][boffset++];
00362                                    register png_byte g = row_pointers[h][boffset++];
00363                                    register png_byte b = row_pointers[h][boffset++];
00364                                    im->tpixels[h][w] = gdTrueColor (r, g, b);
00365                             }
00366                      }
00367                      break;
00368 
00369               case PNG_COLOR_TYPE_GRAY_ALPHA:
00370               case PNG_COLOR_TYPE_RGB_ALPHA:
00371                      for (h = 0; h < height; h++) {
00372                             int boffset = 0;
00373                             for (w = 0; w < width; w++) {
00374                                    register png_byte r = row_pointers[h][boffset++];
00375                                    register png_byte g = row_pointers[h][boffset++];
00376                                    register png_byte b = row_pointers[h][boffset++];
00377 
00378                                    /* gd has only 7 bits of alpha channel resolution, and
00379                                     * 127 is transparent, 0 opaque. A moment of convenience,
00380                                     *  a lifetime of compatibility.
00381                                     */
00382 
00383                                    register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1);
00384                                    im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a);
00385                             }
00386                      }
00387                      break;
00388 
00389               default:
00390                      /* Palette image, or something coerced to be one */
00391                      for (h = 0; h < height; ++h) {
00392                             for (w = 0; w < width; ++w) {
00393                                    register png_byte idx = row_pointers[h][w];
00394                                    im->pixels[h][w] = idx;
00395                                    open[idx] = 0;
00396                             }
00397                      }
00398        }
00399 #ifdef DEBUG
00400        if (!im->trueColor) {
00401               for (i = num_palette; i < gdMaxColors; ++i) {
00402                      if (!open[i]) {
00403                             php_gd_error("gd-png warning: image data references out-of-range color index (%d)", i);
00404                      }
00405               }
00406        }
00407 #endif
00408 
00409        if (palette_allocated) {
00410               gdFree(palette);
00411        }
00412        gdFree(image_data);
00413        gdFree(row_pointers);
00414 
00415        return im;
00416 }
00417 
00418 void gdImagePngEx (gdImagePtr im, FILE * outFile, int level, int basefilter)
00419 {
00420        gdIOCtx *out = gdNewFileCtx(outFile);
00421        gdImagePngCtxEx(im, out, level, basefilter);
00422        out->gd_free(out);
00423 }
00424 
00425 void gdImagePng (gdImagePtr im, FILE * outFile)
00426 {
00427        gdIOCtx *out = gdNewFileCtx(outFile);
00428        gdImagePngCtxEx(im, out, -1, -1);
00429        out->gd_free(out);
00430 }
00431 
00432 void * gdImagePngPtr (gdImagePtr im, int *size)
00433 {
00434        void *rv;
00435        gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
00436        gdImagePngCtxEx(im, out, -1, -1);
00437        rv = gdDPExtractData(out, size);
00438        out->gd_free(out);
00439 
00440        return rv;
00441 }
00442 
00443 void * gdImagePngPtrEx (gdImagePtr im, int *size, int level, int basefilter)
00444 {
00445        void *rv;
00446        gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
00447        gdImagePngCtxEx(im, out, level, basefilter);
00448        rv = gdDPExtractData(out, size);
00449        out->gd_free(out);
00450        return rv;
00451 }
00452 
00453 void gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
00454 {
00455        gdImagePngCtxEx(im, outfile, -1, -1);
00456 }
00457 
00458 /* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
00459  *  and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
00460  *  (http://www.cdrom.com/pub/png/pngbook.html).
00461  */
00462 void gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level, int basefilter)
00463 {
00464        int i, j, bit_depth = 0, interlace_type;
00465        int width = im->sx;
00466        int height = im->sy;
00467        int colors = im->colorsTotal;
00468        int *open = im->open;
00469        int mapping[gdMaxColors];   /* mapping[gd_index] == png_index */
00470        png_byte trans_values[256];
00471        png_color_16 trans_rgb_value;
00472        png_color palette[gdMaxColors];
00473        png_structp png_ptr;
00474        png_infop info_ptr;
00475        volatile int transparent = im->transparent;
00476        volatile int remap = FALSE;
00477 #ifdef PNG_SETJMP_SUPPORTED
00478        jmpbuf_wrapper jbw;
00479 
00480        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, NULL);
00481 #else
00482        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00483 #endif
00484        if (png_ptr == NULL) {
00485               php_gd_error("gd-png error: cannot allocate libpng main struct");
00486               return;
00487        }
00488 
00489        info_ptr = png_create_info_struct(png_ptr);
00490        if (info_ptr == NULL) {
00491               php_gd_error("gd-png error: cannot allocate libpng info struct");
00492               png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
00493 
00494               return;
00495     }
00496 
00497 #ifdef PNG_SETJMP_SUPPORTED
00498        if (setjmp(jbw.jmpbuf)) {
00499               php_gd_error("gd-png error: setjmp returns error condition");
00500               png_destroy_write_struct (&png_ptr, &info_ptr);
00501 
00502               return;
00503        }
00504 #endif
00505 
00506        png_set_write_fn(png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
00507 
00508        /* This is best for palette images, and libpng defaults to it for
00509         * palette images anyway, so we don't need to do it explicitly.
00510         * What to ideally do for truecolor images depends, alas, on the image.
00511         * gd is intentionally imperfect and doesn't spend a lot of time
00512         * fussing with such things.
00513         */
00514 
00515        /*  png_set_filter(png_ptr, 0, PNG_FILTER_NONE);  */
00516 
00517        /* 2.0.12: this is finally a parameter */
00518        if (level != -1 && (level < 0 || level > 9)) {
00519               php_gd_error("gd-png error: compression level must be 0 through 9");
00520               return;
00521        }
00522        png_set_compression_level(png_ptr, level);
00523        if (basefilter >= 0) {
00524               png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, basefilter);
00525        }
00526 
00527        /* can set this to a smaller value without compromising compression if all
00528         * image data is 16K or less; will save some decoder memory [min == 8]
00529         */
00530 
00531        /*  png_set_compression_window_bits(png_ptr, 15);  */
00532 
00533        if (!im->trueColor) {
00534               if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) {
00535                      transparent = -1;
00536               }
00537 
00538               for (i = 0; i < gdMaxColors; ++i) {
00539                      mapping[i] = -1;
00540               }
00541 
00542               /* count actual number of colors used (colorsTotal == high-water mark) */
00543               colors = 0;
00544               for (i = 0; i < im->colorsTotal; ++i) {
00545                      if (!open[i]) {
00546                             mapping[i] = colors;
00547                             ++colors;
00548                      }
00549               }
00550               if (colors == 0) {
00551                      php_gd_error("gd-png error: no colors in palette");
00552                      goto bail;
00553               }
00554               if (colors < im->colorsTotal) {
00555                      remap = TRUE;
00556               }
00557               if (colors <= 2) {
00558                      bit_depth = 1;
00559               } else if (colors <= 4) {
00560                      bit_depth = 2;
00561               } else if (colors <= 16) {
00562                      bit_depth = 4;
00563               } else {
00564                      bit_depth = 8;
00565               }
00566        }
00567 
00568        interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
00569 
00570        if (im->trueColor) {
00571               if (im->saveAlphaFlag) {
00572                      png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
00573                                    PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00574               } else {
00575                      png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type,
00576                                    PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00577               }
00578        } else {
00579               png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type,
00580                      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00581        }
00582 
00583        if (im->trueColor && !im->saveAlphaFlag && (transparent >= 0)) {
00584               /* 2.0.9: fixed by Thomas Winzig */
00585               trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
00586               trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
00587               trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
00588               png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value);
00589        }
00590 
00591        if (!im->trueColor) {
00592               /* Oy veh. Remap the PNG palette to put the entries with interesting alpha channel
00593                * values first. This minimizes the size of the tRNS chunk and thus the size
00594                * of the PNG file as a whole.
00595                */
00596 
00597               int tc = 0;
00598               int i;
00599               int j;
00600               int k;
00601 
00602               for (i = 0; (i < im->colorsTotal); i++) {
00603                      if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) {
00604                             tc++;
00605                      }
00606               }
00607               if (tc) {
00608 #if 0
00609                      for (i = 0; (i < im->colorsTotal); i++) {
00610                             trans_values[i] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
00611                      }
00612                      png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
00613 #endif
00614                      if (!remap) {
00615                             remap = TRUE;
00616                      }
00617 
00618                      /* (Semi-)transparent indexes come up from the bottom of the list of real colors; opaque
00619                       * indexes come down from the top
00620                       */
00621                      j = 0;
00622                      k = colors - 1;
00623 
00624                      for (i = 0; i < im->colorsTotal; i++) {
00625                             if (!im->open[i]) {
00626                                    if (im->alpha[i] != gdAlphaOpaque) {
00627                                           /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
00628                                           trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
00629                                           mapping[i] = j++;
00630                                    } else {
00631                                           mapping[i] = k--;
00632                                    }
00633                             }
00634                      }
00635                      png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL);
00636               }
00637        }
00638 
00639        /* convert palette to libpng layout */
00640        if (!im->trueColor) {
00641               if (remap) {
00642                      for (i = 0; i < im->colorsTotal; ++i) {
00643                             if (mapping[i] < 0) {
00644                                    continue;
00645                             }
00646 
00647                             palette[mapping[i]].red = im->red[i];
00648                             palette[mapping[i]].green = im->green[i];
00649                             palette[mapping[i]].blue = im->blue[i];
00650                      }
00651               } else {
00652                      for (i = 0; i < colors; ++i) {
00653                             palette[i].red = im->red[i];
00654                             palette[i].green = im->green[i];
00655                             palette[i].blue = im->blue[i];
00656                      }
00657               }
00658               png_set_PLTE(png_ptr, info_ptr, palette, colors);
00659        }
00660 
00661        /* write out the PNG header info (everything up to first IDAT) */
00662        png_write_info(png_ptr, info_ptr);
00663 
00664        /* make sure < 8-bit images are packed into pixels as tightly as possible */
00665        png_set_packing(png_ptr);
00666 
00667        /* This code allocates a set of row buffers and copies the gd image data
00668         * into them only in the case that remapping is necessary; in gd 1.3 and
00669         * later, the im->pixels array is laid out identically to libpng's row
00670         * pointers and can be passed to png_write_image() function directly.
00671         * The remapping case could be accomplished with less memory for non-
00672         * interlaced images, but interlacing causes some serious complications.
00673         */
00674 
00675        if (im->trueColor) {
00676               /* performance optimizations by Phong Tran */
00677               int channels = im->saveAlphaFlag ? 4 : 3;
00678               /* Our little 7-bit alpha channel trick costs us a bit here. */
00679               png_bytep *row_pointers;
00680               unsigned char* pOutputRow;
00681               int **ptpixels = im->tpixels;
00682               int *pThisRow;
00683               unsigned char a;
00684               int thisPixel;
00685               png_bytep *prow_pointers;
00686               int saveAlphaFlag = im->saveAlphaFlag;
00687 
00688               row_pointers = safe_emalloc(sizeof(png_bytep), height, 0);
00689               prow_pointers = row_pointers;
00690               for (j = 0; j < height; ++j) {
00691                      *prow_pointers = (png_bytep) safe_emalloc(width, channels, 0);
00692                      pOutputRow = *prow_pointers++;
00693                      pThisRow = *ptpixels++;
00694                      for (i = 0; i < width; ++i) {
00695                             thisPixel = *pThisRow++;
00696                             *pOutputRow++ = gdTrueColorGetRed(thisPixel);
00697                             *pOutputRow++ = gdTrueColorGetGreen(thisPixel);
00698                             *pOutputRow++ = gdTrueColorGetBlue(thisPixel);
00699                             if (saveAlphaFlag) {
00700                                    /* convert the 7-bit alpha channel to an 8-bit alpha channel.
00701                                     * We do a little bit-flipping magic, repeating the MSB
00702                                     * as the LSB, to ensure that 0 maps to 0 and
00703                                     * 127 maps to 255. We also have to invert to match
00704                                     * PNG's convention in which 255 is opaque.
00705                                     */
00706                                    a = gdTrueColorGetAlpha(thisPixel);
00707                                    /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
00708                                    if (a == 127) {
00709                                           *pOutputRow++ = 0;
00710                                    } else {
00711                                           *pOutputRow++ = 255 - ((a << 1) + (a >> 6));
00712                                    }
00713 
00714                             }
00715                      }
00716               }
00717 
00718               png_write_image(png_ptr, row_pointers);
00719               png_write_end(png_ptr, info_ptr);
00720 
00721               for (j = 0; j < height; ++j) {
00722                      gdFree(row_pointers[j]);
00723               }
00724 
00725               gdFree(row_pointers);
00726        } else {
00727               if (remap) {
00728                      png_bytep *row_pointers;
00729                      row_pointers = safe_emalloc(height, sizeof(png_bytep), 0);
00730                      for (j = 0; j < height; ++j) {
00731                             row_pointers[j] = (png_bytep) gdMalloc(width);
00732                             for (i = 0; i < width; ++i) {
00733                                    row_pointers[j][i] = mapping[im->pixels[j][i]];
00734                             }
00735                      }
00736 
00737                      png_write_image(png_ptr, row_pointers);
00738                      png_write_end(png_ptr, info_ptr);
00739 
00740                      for (j = 0; j < height; ++j) {
00741                             gdFree(row_pointers[j]);
00742                      }
00743 
00744                      gdFree(row_pointers);
00745               } else {
00746                      png_write_image(png_ptr, im->pixels);
00747                      png_write_end(png_ptr, info_ptr);
00748               }
00749        }
00750        /* 1.6.3: maybe we should give that memory BACK! TBB */
00751  bail:
00752        png_destroy_write_struct(&png_ptr, &info_ptr);
00753 }
00754 
00755 #endif /* HAVE_LIBPNG */