Annotation of embedaddon/php/ext/gd/libgd/gd_png.c, revision 1.1.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>