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>