Back to index

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