Annotation of embedaddon/php/ext/gd/libgd/gd_png.c, revision 1.1
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;
! 130: png_bytep image_data = NULL;
! 131: png_bytepp row_pointers = NULL;
! 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>