~ubuntu-branches/ubuntu/lucid/graphviz/lucid-security

« back to all changes in this revision

Viewing changes to gd/gd_png.c

  • Committer: Bazaar Package Importer
  • Author(s): Stephen M Moraco
  • Date: 2002-02-05 18:52:12 UTC
  • Revision ID: james.westby@ubuntu.com-20020205185212-8i04c70te00rc40y
Tags: upstream-1.7.16
ImportĀ upstreamĀ versionĀ 1.7.16

Show diffs side-by-side

added added

removed removed

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