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