Back to index

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