diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2018-01-02 20:34:49 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2018-01-02 20:34:49 (GMT) |
commit | 89c1ac99d375fbd73892aa659f06ef5e2c5ea56e (patch) | |
tree | e76ce80d68d11f1ea137bc33a42f71a1d1f32028 /tk8.6/generic/tkImgPNG.c | |
parent | 01e4cd2ef2ff59418766b2259fbc99771646aba6 (diff) | |
download | blt-89c1ac99d375fbd73892aa659f06ef5e2c5ea56e.zip blt-89c1ac99d375fbd73892aa659f06ef5e2c5ea56e.tar.gz blt-89c1ac99d375fbd73892aa659f06ef5e2c5ea56e.tar.bz2 |
upgrade to tcl/tk 8.6.8
Diffstat (limited to 'tk8.6/generic/tkImgPNG.c')
-rw-r--r-- | tk8.6/generic/tkImgPNG.c | 3563 |
1 files changed, 0 insertions, 3563 deletions
diff --git a/tk8.6/generic/tkImgPNG.c b/tk8.6/generic/tkImgPNG.c deleted file mode 100644 index 6e64afa..0000000 --- a/tk8.6/generic/tkImgPNG.c +++ /dev/null @@ -1,3563 +0,0 @@ -/* - * tkImgPNG.c -- - * - * A Tk photo image file handler for PNG files. - * - * Copyright (c) 2006-2008 Muonics, Inc. - * Copyright (c) 2008 Donal K. Fellows - * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - */ - -#include "assert.h" -#include "tkInt.h" - -#define PNG_INT32(a,b,c,d) \ - (((long)(a) << 24) | ((long)(b) << 16) | ((long)(c) << 8) | (long)(d)) -#define PNG_BLOCK_SZ 1024 /* Process up to 1k at a time. */ -#define PNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) - -/* - * Every PNG image starts with the following 8-byte signature. - */ - -#define PNG_SIG_SZ 8 -static const unsigned char pngSignature[] = { - 137, 80, 78, 71, 13, 10, 26, 10 -}; - -static const int startLine[8] = { - 0, 0, 0, 4, 0, 2, 0, 1 -}; - -/* - * Chunk type flags. - */ - -#define PNG_CF_ANCILLARY 0x10000000L /* Non-critical chunk (can ignore). */ -#define PNG_CF_PRIVATE 0x00100000L /* Application-specific chunk. */ -#define PNG_CF_RESERVED 0x00001000L /* Not used. */ -#define PNG_CF_COPYSAFE 0x00000010L /* Opaque data safe for copying. */ - -/* - * Chunk types, not all of which have support implemented. Note that there are - * others in the official extension set which we will never support (as they - * are officially deprecated). - */ - -#define CHUNK_IDAT PNG_INT32('I','D','A','T') /* Pixel data. */ -#define CHUNK_IEND PNG_INT32('I','E','N','D') /* End of Image. */ -#define CHUNK_IHDR PNG_INT32('I','H','D','R') /* Header. */ -#define CHUNK_PLTE PNG_INT32('P','L','T','E') /* Palette. */ - -#define CHUNK_bKGD PNG_INT32('b','K','G','D') /* Background Color */ -#define CHUNK_cHRM PNG_INT32('c','H','R','M') /* Chroma values. */ -#define CHUNK_gAMA PNG_INT32('g','A','M','A') /* Gamma. */ -#define CHUNK_hIST PNG_INT32('h','I','S','T') /* Histogram. */ -#define CHUNK_iCCP PNG_INT32('i','C','C','P') /* Color profile. */ -#define CHUNK_iTXt PNG_INT32('i','T','X','t') /* Internationalized - * text (comments, - * etc.) */ -#define CHUNK_oFFs PNG_INT32('o','F','F','s') /* Image offset. */ -#define CHUNK_pCAL PNG_INT32('p','C','A','L') /* Pixel calibration - * data. */ -#define CHUNK_pHYs PNG_INT32('p','H','Y','s') /* Physical pixel - * dimensions. */ -#define CHUNK_sBIT PNG_INT32('s','B','I','T') /* Significant bits */ -#define CHUNK_sCAL PNG_INT32('s','C','A','L') /* Physical scale. */ -#define CHUNK_sPLT PNG_INT32('s','P','L','T') /* Suggested - * palette. */ -#define CHUNK_sRGB PNG_INT32('s','R','G','B') /* Standard RGB space - * declaration. */ -#define CHUNK_tEXt PNG_INT32('t','E','X','t') /* Plain Latin-1 - * text. */ -#define CHUNK_tIME PNG_INT32('t','I','M','E') /* Time stamp. */ -#define CHUNK_tRNS PNG_INT32('t','R','N','S') /* Transparency. */ -#define CHUNK_zTXt PNG_INT32('z','T','X','t') /* Compressed Latin-1 - * text. */ - -/* - * Color flags. - */ - -#define PNG_COLOR_INDEXED 1 -#define PNG_COLOR_USED 2 -#define PNG_COLOR_ALPHA 4 - -/* - * Actual color types. - */ - -#define PNG_COLOR_GRAY 0 -#define PNG_COLOR_RGB (PNG_COLOR_USED) -#define PNG_COLOR_PLTE (PNG_COLOR_USED | PNG_COLOR_INDEXED) -#define PNG_COLOR_GRAYALPHA (PNG_COLOR_GRAY | PNG_COLOR_ALPHA) -#define PNG_COLOR_RGBA (PNG_COLOR_USED | PNG_COLOR_ALPHA) - -/* - * Compression Methods. - */ - -#define PNG_COMPRESS_DEFLATE 0 - -/* - * Filter Methods. - */ - -#define PNG_FILTMETH_STANDARD 0 - -/* - * Interlacing Methods. - */ - -#define PNG_INTERLACE_NONE 0 -#define PNG_INTERLACE_ADAM7 1 - -/* - * State information, used to store everything about the PNG image being - * currently parsed or created. - */ - -typedef struct { - /* - * PNG data source/destination channel/object/byte array. - */ - - Tcl_Channel channel; /* Channel for from-file reads. */ - Tcl_Obj *objDataPtr; - unsigned char *strDataBuf; /* Raw source data for from-string reads. */ - int strDataLen; /* Length of source data. */ - unsigned char *base64Data; /* base64 encoded string data. */ - unsigned char base64Bits; /* Remaining bits from last base64 read. */ - unsigned char base64State; /* Current state of base64 decoder. */ - double alpha; /* Alpha from -format option. */ - - /* - * Image header information. - */ - - unsigned char bitDepth; /* Number of bits per pixel. */ - unsigned char colorType; /* Grayscale, TrueColor, etc. */ - unsigned char compression; /* Compression Mode (always zlib). */ - unsigned char filter; /* Filter mode (0 - 3). */ - unsigned char interlace; /* Type of interlacing (if any). */ - unsigned char numChannels; /* Number of channels per pixel. */ - unsigned char bytesPerPixel;/* Bytes per pixel in scan line. */ - int bitScale; /* Scale factor for RGB/Gray depths < 8. */ - int currentLine; /* Current line being unfiltered. */ - unsigned char phase; /* Interlacing phase (0..6). */ - Tk_PhotoImageBlock block; - int blockLen; /* Number of bytes in Tk image pixels. */ - - /* - * For containing data read from PLTE (palette) and tRNS (transparency) - * chunks. - */ - - int paletteLen; /* Number of PLTE entries (1..256). */ - int useTRNS; /* Flag to indicate whether there was a - * palette given. */ - struct { - unsigned char red; - unsigned char green; - unsigned char blue; - unsigned char alpha; - } palette[256]; /* Palette RGB/Transparency table. */ - unsigned char transVal[6]; /* Fully-transparent RGB/Gray Value. */ - - /* - * For compressing and decompressing IDAT chunks. - */ - - Tcl_ZlibStream stream; /* Inflating or deflating stream; this one is - * not bound to a Tcl command. */ - Tcl_Obj *lastLineObj; /* Last line of pixels, for unfiltering. */ - Tcl_Obj *thisLineObj; /* Current line of pixels to process. */ - int lineSize; /* Number of bytes in a PNG line. */ - int phaseSize; /* Number of bytes/line in current phase. */ -} PNGImage; - -/* - * Maximum size of various chunks. - */ - -#define PNG_PLTE_MAXSZ 768 /* 3 bytes/RGB entry, 256 entries max */ -#define PNG_TRNS_MAXSZ 256 /* 1-byte alpha, 256 entries max */ - -/* - * Forward declarations of non-global functions defined in this file: - */ - -static void ApplyAlpha(PNGImage *pngPtr); -static int CheckColor(Tcl_Interp *interp, PNGImage *pngPtr); -static inline int CheckCRC(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned long calculated); -static void CleanupPNGImage(PNGImage *pngPtr); -static int DecodeLine(Tcl_Interp *interp, PNGImage *pngPtr); -static int DecodePNG(Tcl_Interp *interp, PNGImage *pngPtr, - Tcl_Obj *fmtObj, Tk_PhotoHandle imageHandle, - int destX, int destY); -static int EncodePNG(Tcl_Interp *interp, - Tk_PhotoImageBlock *blockPtr, PNGImage *pngPtr); -static int FileMatchPNG(Tcl_Channel chan, const char *fileName, - Tcl_Obj *fmtObj, int *widthPtr, int *heightPtr, - Tcl_Interp *interp); -static int FileReadPNG(Tcl_Interp *interp, Tcl_Channel chan, - const char *fileName, Tcl_Obj *fmtObj, - Tk_PhotoHandle imageHandle, int destX, int destY, - int width, int height, int srcX, int srcY); -static int FileWritePNG(Tcl_Interp *interp, const char *filename, - Tcl_Obj *fmtObj, Tk_PhotoImageBlock *blockPtr); -static int InitPNGImage(Tcl_Interp *interp, PNGImage *pngPtr, - Tcl_Channel chan, Tcl_Obj *objPtr, int dir); -static inline unsigned char Paeth(int a, int b, int c); -static int ParseFormat(Tcl_Interp *interp, Tcl_Obj *fmtObj, - PNGImage *pngPtr); -static int ReadBase64(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned char *destPtr, int destSz, - unsigned long *crcPtr); -static int ReadByteArray(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned char *destPtr, int destSz, - unsigned long *crcPtr); -static int ReadData(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned char *destPtr, int destSz, - unsigned long *crcPtr); -static int ReadChunkHeader(Tcl_Interp *interp, PNGImage *pngPtr, - int *sizePtr, unsigned long *typePtr, - unsigned long *crcPtr); -static int ReadIDAT(Tcl_Interp *interp, PNGImage *pngPtr, - int chunkSz, unsigned long crc); -static int ReadIHDR(Tcl_Interp *interp, PNGImage *pngPtr); -static inline int ReadInt32(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned long *resultPtr, unsigned long *crcPtr); -static int ReadPLTE(Tcl_Interp *interp, PNGImage *pngPtr, - int chunkSz, unsigned long crc); -static int ReadTRNS(Tcl_Interp *interp, PNGImage *pngPtr, - int chunkSz, unsigned long crc); -static int SkipChunk(Tcl_Interp *interp, PNGImage *pngPtr, - int chunkSz, unsigned long crc); -static int StringMatchPNG(Tcl_Obj *dataObj, Tcl_Obj *fmtObj, - int *widthPtr, int *heightPtr, - Tcl_Interp *interp); -static int StringReadPNG(Tcl_Interp *interp, Tcl_Obj *dataObj, - Tcl_Obj *fmtObj, Tk_PhotoHandle imageHandle, - int destX, int destY, int width, int height, - int srcX, int srcY); -static int StringWritePNG(Tcl_Interp *interp, Tcl_Obj *fmtObj, - Tk_PhotoImageBlock *blockPtr); -static int UnfilterLine(Tcl_Interp *interp, PNGImage *pngPtr); -static inline int WriteByte(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned char c, unsigned long *crcPtr); -static inline int WriteChunk(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned long chunkType, - const unsigned char *dataPtr, int dataSize); -static int WriteData(Tcl_Interp *interp, PNGImage *pngPtr, - const unsigned char *srcPtr, int srcSz, - unsigned long *crcPtr); -static int WriteExtraChunks(Tcl_Interp *interp, - PNGImage *pngPtr); -static int WriteIHDR(Tcl_Interp *interp, PNGImage *pngPtr, - Tk_PhotoImageBlock *blockPtr); -static int WriteIDAT(Tcl_Interp *interp, PNGImage *pngPtr, - Tk_PhotoImageBlock *blockPtr); -static inline int WriteInt32(Tcl_Interp *interp, PNGImage *pngPtr, - unsigned long l, unsigned long *crcPtr); - -/* - * The format record for the PNG file format: - */ - -Tk_PhotoImageFormat tkImgFmtPNG = { - "png", /* name */ - FileMatchPNG, /* fileMatchProc */ - StringMatchPNG, /* stringMatchProc */ - FileReadPNG, /* fileReadProc */ - StringReadPNG, /* stringReadProc */ - FileWritePNG, /* fileWriteProc */ - StringWritePNG, /* stringWriteProc */ - NULL -}; - -/* - *---------------------------------------------------------------------- - * - * InitPNGImage -- - * - * This function is invoked by each of the Tk image handler procs - * (MatchStringProc, etc.) to initialize state information used during - * the course of encoding or decoding a PNG image. - * - * Results: - * TCL_OK, or TCL_ERROR if initialization failed. - * - * Side effects: - * The reference count of the -data Tcl_Obj*, if any, is incremented. - * - *---------------------------------------------------------------------- - */ - -static int -InitPNGImage( - Tcl_Interp *interp, - PNGImage *pngPtr, - Tcl_Channel chan, - Tcl_Obj *objPtr, - int dir) -{ - memset(pngPtr, 0, sizeof(PNGImage)); - - pngPtr->channel = chan; - pngPtr->alpha = 1.0; - - /* - * If decoding from a -data string object, increment its reference count - * for the duration of the decode and get its length and byte array for - * reading with ReadData(). - */ - - if (objPtr) { - Tcl_IncrRefCount(objPtr); - pngPtr->objDataPtr = objPtr; - pngPtr->strDataBuf = - Tcl_GetByteArrayFromObj(objPtr, &pngPtr->strDataLen); - } - - /* - * Initialize the palette transparency table to fully opaque. - */ - - memset(pngPtr->palette, 255, sizeof(pngPtr->palette)); - - /* - * Initialize Zlib inflate/deflate stream. - */ - - if (Tcl_ZlibStreamInit(NULL, dir, TCL_ZLIB_FORMAT_ZLIB, - TCL_ZLIB_COMPRESS_DEFAULT, NULL, &pngPtr->stream) != TCL_OK) { - if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "zlib initialization failed", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "ZLIB_INIT", NULL); - } - if (objPtr) { - Tcl_DecrRefCount(objPtr); - } - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * CleanupPNGImage -- - * - * This function is invoked by each of the Tk image handler procs - * (MatchStringProc, etc.) prior to returning to Tcl in order to clean up - * any allocated memory and call other cleanup handlers such as zlib's - * inflateEnd/deflateEnd. - * - * Results: - * None. - * - * Side effects: - * The reference count of the -data Tcl_Obj*, if any, is decremented. - * Buffers are freed, streams are closed. The PNGImage should not be used - * for any purpose without being reinitialized post-cleanup. - * - *---------------------------------------------------------------------- - */ - -static void -CleanupPNGImage( - PNGImage *pngPtr) -{ - /* - * Don't need the object containing the -data value anymore. - */ - - if (pngPtr->objDataPtr) { - Tcl_DecrRefCount(pngPtr->objDataPtr); - } - - /* - * Discard pixel buffer. - */ - - if (pngPtr->stream) { - Tcl_ZlibStreamClose(pngPtr->stream); - } - - if (pngPtr->block.pixelPtr) { - ckfree(pngPtr->block.pixelPtr); - } - if (pngPtr->thisLineObj) { - Tcl_DecrRefCount(pngPtr->thisLineObj); - } - if (pngPtr->lastLineObj) { - Tcl_DecrRefCount(pngPtr->lastLineObj); - } - - memset(pngPtr, 0, sizeof(PNGImage)); -} - -/* - *---------------------------------------------------------------------- - * - * ReadBase64 -- - * - * This function is invoked to read the specified number of bytes from - * base-64 encoded image data. - * - * Note: It would be better if the Tk_PhotoImage stuff handled this by - * creating a channel from the -data value, which would take care of - * base64 decoding and made the data readable as if it were coming from a - * file. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs. - * - * Side effects: - * The file position will change. The running CRC is updated if a pointer - * to it is provided. - * - *---------------------------------------------------------------------- - */ - -static int -ReadBase64( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned char *destPtr, - int destSz, - unsigned long *crcPtr) -{ - static const unsigned char from64[] = { - 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80, 0x80, - 0x83, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x3e, - 0x83, 0x83, 0x83, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, - 0x3b, 0x3c, 0x3d, 0x83, 0x83, 0x83, 0x81, 0x83, 0x83, 0x83, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x19, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, - 0x32, 0x33, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83 - }; - - /* - * Definitions for the base-64 decoder. - */ - -#define PNG64_SPECIAL 0x80 /* Flag bit */ -#define PNG64_SPACE 0x80 /* Whitespace */ -#define PNG64_PAD 0x81 /* Padding */ -#define PNG64_DONE 0x82 /* End of data */ -#define PNG64_BAD 0x83 /* Ooooh, naughty! */ - - while (destSz && pngPtr->strDataLen) { - unsigned char c = 0; - unsigned char c64 = from64[*pngPtr->strDataBuf++]; - - pngPtr->strDataLen--; - - if (PNG64_SPACE == c64) { - continue; - } - - if (c64 & PNG64_SPECIAL) { - c = (unsigned char) pngPtr->base64Bits; - } else { - switch (pngPtr->base64State++) { - case 0: - pngPtr->base64Bits = c64 << 2; - continue; - case 1: - c = (unsigned char) (pngPtr->base64Bits | (c64 >> 4)); - pngPtr->base64Bits = (c64 & 0xF) << 4; - break; - case 2: - c = (unsigned char) (pngPtr->base64Bits | (c64 >> 2)); - pngPtr->base64Bits = (c64 & 0x3) << 6; - break; - case 3: - c = (unsigned char) (pngPtr->base64Bits | c64); - pngPtr->base64State = 0; - pngPtr->base64Bits = 0; - break; - } - } - - if (crcPtr) { - *crcPtr = Tcl_ZlibCRC32(*crcPtr, &c, 1); - } - - if (destPtr) { - *destPtr++ = c; - } - - destSz--; - - if (c64 & PNG64_SPECIAL) { - break; - } - } - - if (destSz) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unexpected end of image data", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL); - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadByteArray -- - * - * This function is invoked to read the specified number of bytes from a - * non-base64-encoded byte array provided via the -data option. - * - * Note: It would be better if the Tk_PhotoImage stuff handled this by - * creating a channel from the -data value and made the data readable as - * if it were coming from a file. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs. - * - * Side effects: - * The file position will change. The running CRC is updated if a pointer - * to it is provided. - * - *---------------------------------------------------------------------- - */ - -static int -ReadByteArray( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned char *destPtr, - int destSz, - unsigned long *crcPtr) -{ - /* - * Check to make sure the number of requested bytes are available. - */ - - if (pngPtr->strDataLen < destSz) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unexpected end of image data", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL); - return TCL_ERROR; - } - - while (destSz) { - int blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ); - - memcpy(destPtr, pngPtr->strDataBuf, blockSz); - - pngPtr->strDataBuf += blockSz; - pngPtr->strDataLen -= blockSz; - - if (crcPtr) { - *crcPtr = Tcl_ZlibCRC32(*crcPtr, destPtr, blockSz); - } - - destPtr += blockSz; - destSz -= blockSz; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadData -- - * - * This function is invoked to read the specified number of bytes from - * the image file or data. It is a wrapper around the choice of byte - * array Tcl_Obj or Tcl_Channel which depends on whether the image data - * is coming from a file or -data. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs. - * - * Side effects: - * The file position will change. The running CRC is updated if a pointer - * to it is provided. - * - *---------------------------------------------------------------------- - */ - -static int -ReadData( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned char *destPtr, - int destSz, - unsigned long *crcPtr) -{ - if (pngPtr->base64Data) { - return ReadBase64(interp, pngPtr, destPtr, destSz, crcPtr); - } else if (pngPtr->strDataBuf) { - return ReadByteArray(interp, pngPtr, destPtr, destSz, crcPtr); - } - - while (destSz) { - int blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ); - - blockSz = Tcl_Read(pngPtr->channel, (char *)destPtr, blockSz); - if (blockSz < 0) { - /* TODO: failure info... */ - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "channel read failed: %s", Tcl_PosixError(interp))); - return TCL_ERROR; - } - - /* - * Update CRC, pointer, and remaining count if anything was read. - */ - - if (blockSz) { - if (crcPtr) { - *crcPtr = Tcl_ZlibCRC32(*crcPtr, destPtr, blockSz); - } - - destPtr += blockSz; - destSz -= blockSz; - } - - /* - * Check for EOF before all desired data was read. - */ - - if (destSz && Tcl_Eof(pngPtr->channel)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unexpected end of file", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EOF", NULL); - return TCL_ERROR; - } - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadInt32 -- - * - * This function is invoked to read a 32-bit integer in network byte - * order from the image data and return the value in host byte order. - * This is used, for example, to read the 32-bit CRC value for a chunk - * stored in the image file for comparison with the calculated CRC value. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs. - * - * Side effects: - * The file position will change. The running CRC is updated if a pointer - * to it is provided. - * - *---------------------------------------------------------------------- - */ - -static inline int -ReadInt32( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned long *resultPtr, - unsigned long *crcPtr) -{ - unsigned char p[4]; - - if (ReadData(interp, pngPtr, p, 4, crcPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - *resultPtr = PNG_INT32(p[0], p[1], p[2], p[3]); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * CheckCRC -- - * - * This function is reads the final 4-byte integer CRC from a chunk and - * compares it to the running CRC calculated over the chunk type and data - * fields. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs. - * - * Side effects: - * The file position will change. - * - *---------------------------------------------------------------------- - */ - -static inline int -CheckCRC( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned long calculated) -{ - unsigned long chunked; - - /* - * Read the CRC field at the end of the chunk. - */ - - if (ReadInt32(interp, pngPtr, &chunked, NULL) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Compare the read CRC to what we calculate to make sure they match. - */ - - if (calculated != chunked) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("CRC check failed", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "CRC", NULL); - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * SkipChunk -- - * - * This function is used to skip a PNG chunk that is not used by this - * implementation. Given the input stream has had the chunk length and - * chunk type fields already read, this function will read the number of - * bytes indicated by the chunk length, plus four for the CRC, and will - * verify that CRC is correct for the skipped data. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs. - * - * Side effects: - * The file position will change. - * - *---------------------------------------------------------------------- - */ - -static int -SkipChunk( - Tcl_Interp *interp, - PNGImage *pngPtr, - int chunkSz, - unsigned long crc) -{ - unsigned char buffer[PNG_BLOCK_SZ]; - - /* - * Skip data in blocks until none is left. Read up to PNG_BLOCK_SZ bytes - * at a time, rather than trusting the claimed chunk size, which may not - * be trustworthy. - */ - - while (chunkSz) { - int blockSz = PNG_MIN(chunkSz, PNG_BLOCK_SZ); - - if (ReadData(interp, pngPtr, buffer, blockSz, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - chunkSz -= blockSz; - } - - if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - * 4.3. Summary of standard chunks - * - * This table summarizes some properties of the standard chunk types. - * - * Critical chunks (must appear in this order, except PLTE is optional): - * - * Name Multiple Ordering constraints OK? - * - * IHDR No Must be first - * PLTE No Before IDAT - * IDAT Yes Multiple IDATs must be consecutive - * IEND No Must be last - * - * Ancillary chunks (need not appear in this order): - * - * Name Multiple Ordering constraints OK? - * - * cHRM No Before PLTE and IDAT - * gAMA No Before PLTE and IDAT - * iCCP No Before PLTE and IDAT - * sBIT No Before PLTE and IDAT - * sRGB No Before PLTE and IDAT - * bKGD No After PLTE; before IDAT - * hIST No After PLTE; before IDAT - * tRNS No After PLTE; before IDAT - * pHYs No Before IDAT - * sPLT Yes Before IDAT - * tIME No None - * iTXt Yes None - * tEXt Yes None - * zTXt Yes None - * - * [From the PNG specification.] - */ - -/* - *---------------------------------------------------------------------- - * - * ReadChunkHeader -- - * - * This function is used at the start of each chunk to extract the - * four-byte chunk length and four-byte chunk type fields. It will - * continue reading until it finds a chunk type that is handled by this - * implementation, checking the CRC of any chunks it skips. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs or an unknown critical - * chunk type is encountered. - * - * Side effects: - * The file position will change. The running CRC is updated. - * - *---------------------------------------------------------------------- - */ - -static int -ReadChunkHeader( - Tcl_Interp *interp, - PNGImage *pngPtr, - int *sizePtr, - unsigned long *typePtr, - unsigned long *crcPtr) -{ - unsigned long chunkType = 0; - int chunkSz = 0; - unsigned long crc = 0; - - /* - * Continue until finding a chunk type that is handled. - */ - - while (!chunkType) { - unsigned long temp; - unsigned char pc[4]; - int i; - - /* - * Read the 4-byte length field for the chunk. The length field is not - * included in the CRC calculation, so the running CRC must be reset - * afterward. Limit chunk lengths to INT_MAX, to align with the - * maximum size for Tcl_Read, Tcl_GetByteArrayFromObj, etc. - */ - - if (ReadData(interp, pngPtr, pc, 4, NULL) == TCL_ERROR) { - return TCL_ERROR; - } - - temp = PNG_INT32(pc[0], pc[1], pc[2], pc[3]); - - if (temp > INT_MAX) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "chunk size is out of supported range on this architecture", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "OUTSIZE", NULL); - return TCL_ERROR; - } - - chunkSz = (int) temp; - crc = Tcl_ZlibCRC32(0, NULL, 0); - - /* - * Read the 4-byte chunk type. - */ - - if (ReadData(interp, pngPtr, pc, 4, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Convert it to a host-order integer for simple comparison. - */ - - chunkType = PNG_INT32(pc[0], pc[1], pc[2], pc[3]); - - /* - * Check to see if this is a known/supported chunk type. Note that the - * PNG specs require non-critical (i.e., ancillary) chunk types that - * are not recognized to be ignored, rather than be treated as an - * error. It does, however, recommend that an unknown critical chunk - * type be treated as a failure. - * - * This switch/loop acts as a filter of sorts for undesired chunk - * types. The chunk type should still be checked elsewhere for - * determining it is in the correct order. - */ - - switch (chunkType) { - /* - * These chunk types are required and/or supported. - */ - - case CHUNK_IDAT: - case CHUNK_IEND: - case CHUNK_IHDR: - case CHUNK_PLTE: - case CHUNK_tRNS: - break; - - /* - * These chunk types are part of the standard, but are not used by - * this implementation (at least not yet). Note that these are all - * ancillary chunks (lowercase first letter). - */ - - case CHUNK_bKGD: - case CHUNK_cHRM: - case CHUNK_gAMA: - case CHUNK_hIST: - case CHUNK_iCCP: - case CHUNK_iTXt: - case CHUNK_oFFs: - case CHUNK_pCAL: - case CHUNK_pHYs: - case CHUNK_sBIT: - case CHUNK_sCAL: - case CHUNK_sPLT: - case CHUNK_sRGB: - case CHUNK_tEXt: - case CHUNK_tIME: - case CHUNK_zTXt: - /* - * TODO: might want to check order here. - */ - - if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - chunkType = 0; - break; - - default: - /* - * Unknown chunk type. If it's critical, we can't continue. - */ - - if (!(chunkType & PNG_CF_ANCILLARY)) { - if (chunkType & PNG_INT32(128,128,128,128)) { - /* - * No nice ASCII conversion; shouldn't happen either, but - * we'll be doubly careful. - */ - - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "encountered an unsupported criticial chunk type", - -1)); - } else { - char typeString[5]; - - typeString[0] = (char) ((chunkType >> 24) & 255); - typeString[1] = (char) ((chunkType >> 16) & 255); - typeString[2] = (char) ((chunkType >> 8) & 255); - typeString[3] = (char) (chunkType & 255); - typeString[4] = '\0'; - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "encountered an unsupported criticial chunk type" - " \"%s\"", typeString)); - } - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", - "UNSUPPORTED_CRITICAL", NULL); - return TCL_ERROR; - } - - /* - * Check to see if the chunk type has legal bytes. - */ - - for (i=0 ; i<4 ; i++) { - if ((pc[i] < 65) || (pc[i] > 122) || - ((pc[i] > 90) && (pc[i] < 97))) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid chunk type", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", - "INVALID_CHUNK", NULL); - return TCL_ERROR; - } - } - - /* - * It seems to be an otherwise legally labelled ancillary chunk - * that we don't want, so skip it after at least checking its CRC. - */ - - if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - chunkType = 0; - } - } - - /* - * Found a known chunk type that's handled, albiet possibly not in the - * right order. Send back the chunk type (for further checking or - * handling), the chunk size and the current CRC for the rest of the - * calculation. - */ - - *typePtr = chunkType; - *sizePtr = chunkSz; - *crcPtr = crc; - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * CheckColor -- - * - * Do validation on color type, depth, and related information, and - * calculates storage requirements and offsets based on image dimensions - * and color. - * - * Results: - * TCL_OK, or TCL_ERROR if color information is invalid or some other - * failure occurs. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -CheckColor( - Tcl_Interp *interp, - PNGImage *pngPtr) -{ - int offset; - - /* - * Verify the color type is valid and the bit depth is allowed. - */ - - switch (pngPtr->colorType) { - case PNG_COLOR_GRAY: - pngPtr->numChannels = 1; - if ((1 != pngPtr->bitDepth) && (2 != pngPtr->bitDepth) && - (4 != pngPtr->bitDepth) && (8 != pngPtr->bitDepth) && - (16 != pngPtr->bitDepth)) { - goto unsupportedDepth; - } - break; - - case PNG_COLOR_RGB: - pngPtr->numChannels = 3; - if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) { - goto unsupportedDepth; - } - break; - - case PNG_COLOR_PLTE: - pngPtr->numChannels = 1; - if ((1 != pngPtr->bitDepth) && (2 != pngPtr->bitDepth) && - (4 != pngPtr->bitDepth) && (8 != pngPtr->bitDepth)) { - goto unsupportedDepth; - } - break; - - case PNG_COLOR_GRAYALPHA: - pngPtr->numChannels = 2; - if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) { - goto unsupportedDepth; - } - break; - - case PNG_COLOR_RGBA: - pngPtr->numChannels = 4; - if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) { - unsupportedDepth: - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "bit depth is not allowed for given color type", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_DEPTH", NULL); - return TCL_ERROR; - } - break; - - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown color type field %d", pngPtr->colorType)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "UNKNOWN_COLOR", NULL); - return TCL_ERROR; - } - - /* - * Set up the Tk photo block's pixel size and channel offsets. offset - * array elements should already be 0 from the memset during InitPNGImage. - */ - - offset = (pngPtr->bitDepth > 8) ? 2 : 1; - - if (pngPtr->colorType & PNG_COLOR_USED) { - pngPtr->block.pixelSize = offset * 4; - pngPtr->block.offset[1] = offset; - pngPtr->block.offset[2] = offset * 2; - pngPtr->block.offset[3] = offset * 3; - } else { - pngPtr->block.pixelSize = offset * 2; - pngPtr->block.offset[3] = offset; - } - - /* - * Calculate the block pitch, which is the number of bytes per line in the - * image, given image width and depth of color. Make sure that it it isn't - * larger than Tk can handle. - */ - - if (pngPtr->block.width > INT_MAX / pngPtr->block.pixelSize) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "image pitch is out of supported range on this architecture", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PITCH", NULL); - return TCL_ERROR; - } - - pngPtr->block.pitch = pngPtr->block.pixelSize * pngPtr->block.width; - - /* - * Calculate the total size of the image as represented to Tk given pitch - * and image height. Make sure that it isn't larger than Tk can handle. - */ - - if (pngPtr->block.height > INT_MAX / pngPtr->block.pitch) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "image total size is out of supported range on this architecture", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "SIZE", NULL); - return TCL_ERROR; - } - - pngPtr->blockLen = pngPtr->block.height * pngPtr->block.pitch; - - /* - * Determine number of bytes per pixel in the source for later use. - */ - - switch (pngPtr->colorType) { - case PNG_COLOR_GRAY: - pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 2 : 1; - break; - case PNG_COLOR_RGB: - pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 6 : 3; - break; - case PNG_COLOR_PLTE: - pngPtr->bytesPerPixel = 1; - break; - case PNG_COLOR_GRAYALPHA: - pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 4 : 2; - break; - case PNG_COLOR_RGBA: - pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 8 : 4; - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown color type %d", pngPtr->colorType)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "UNKNOWN_COLOR", NULL); - return TCL_ERROR; - } - - /* - * Calculate scale factor for bit depths less than 8, in order to adjust - * them to a minimum of 8 bits per pixel in the Tk image. - */ - - if (pngPtr->bitDepth < 8) { - pngPtr->bitScale = 255 / (int)(pow(2, pngPtr->bitDepth) - 1); - } else { - pngPtr->bitScale = 1; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadIHDR -- - * - * This function reads the PNG header from the beginning of a PNG file - * and returns the dimensions of the image. - * - * Results: - * The return value is 1 if file "f" appears to start with a valid PNG - * header, 0 otherwise. If the header is valid, then *widthPtr and - * *heightPtr are modified to hold the dimensions of the image. - * - * Side effects: - * The access position in f advances. - * - *---------------------------------------------------------------------- - */ - -static int -ReadIHDR( - Tcl_Interp *interp, - PNGImage *pngPtr) -{ - unsigned char sigBuf[PNG_SIG_SZ]; - unsigned long chunkType; - int chunkSz; - unsigned long crc; - unsigned long width, height; - int mismatch; - - /* - * Read the appropriate number of bytes for the PNG signature. - */ - - if (ReadData(interp, pngPtr, sigBuf, PNG_SIG_SZ, NULL) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Compare the read bytes to the expected signature. - */ - - mismatch = memcmp(sigBuf, pngSignature, PNG_SIG_SZ); - - /* - * If reading from string, reset position and try base64 decode. - */ - - if (mismatch && pngPtr->strDataBuf) { - pngPtr->strDataBuf = Tcl_GetByteArrayFromObj(pngPtr->objDataPtr, - &pngPtr->strDataLen); - pngPtr->base64Data = pngPtr->strDataBuf; - - if (ReadData(interp, pngPtr, sigBuf, PNG_SIG_SZ, NULL) == TCL_ERROR) { - return TCL_ERROR; - } - - mismatch = memcmp(sigBuf, pngSignature, PNG_SIG_SZ); - } - - if (mismatch) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "data stream does not have a PNG signature", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NO_SIG", NULL); - return TCL_ERROR; - } - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Read in the IHDR (header) chunk for width, height, etc. - * - * The first chunk in the file must be the IHDR (headr) chunk. - */ - - if (chunkType != CHUNK_IHDR) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "expected IHDR chunk type", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NO_IHDR", NULL); - return TCL_ERROR; - } - - if (chunkSz != 13) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid IHDR chunk size", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IHDR", NULL); - return TCL_ERROR; - } - - /* - * Read and verify the image width and height to be sure Tk can handle its - * dimensions. The PNG specification does not permit zero-width or - * zero-height images. - */ - - if (ReadInt32(interp, pngPtr, &width, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (ReadInt32(interp, pngPtr, &height, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (!width || !height || (width > INT_MAX) || (height > INT_MAX)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "image dimensions are invalid or beyond architecture limits", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DIMENSIONS", NULL); - return TCL_ERROR; - } - - /* - * Set height and width for the Tk photo block. - */ - - pngPtr->block.width = (int) width; - pngPtr->block.height = (int) height; - - /* - * Read and the Bit Depth and Color Type. - */ - - if (ReadData(interp, pngPtr, &pngPtr->bitDepth, 1, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (ReadData(interp, pngPtr, &pngPtr->colorType, 1, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Verify that the color type is valid, the bit depth is allowed for the - * color type, and calculate the number of channels and pixel depth (bits - * per pixel * channels). Also set up offsets and sizes in the Tk photo - * block for the pixel data. - */ - - if (CheckColor(interp, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Only one compression method is currently defined by the standard. - */ - - if (ReadData(interp, pngPtr, &pngPtr->compression, 1, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (pngPtr->compression != PNG_COMPRESS_DEFLATE) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown compression method %d", pngPtr->compression)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_COMPRESS", NULL); - return TCL_ERROR; - } - - /* - * Only one filter method is currently defined by the standard; the method - * has five actual filter types associated with it. - */ - - if (ReadData(interp, pngPtr, &pngPtr->filter, 1, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (pngPtr->filter != PNG_FILTMETH_STANDARD) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown filter method %d", pngPtr->filter)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_FILTER", NULL); - return TCL_ERROR; - } - - if (ReadData(interp, pngPtr, &pngPtr->interlace, 1, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - switch (pngPtr->interlace) { - case PNG_INTERLACE_NONE: - case PNG_INTERLACE_ADAM7: - break; - - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown interlace method %d", pngPtr->interlace)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_INTERLACE", NULL); - return TCL_ERROR; - } - - return CheckCRC(interp, pngPtr, crc); -} - -/* - *---------------------------------------------------------------------- - * - * ReadPLTE -- - * - * This function reads the PLTE (indexed color palette) chunk data from - * the PNG file and populates the palette table in the PNGImage - * structure. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs or the PLTE chunk is - * invalid. - * - * Side effects: - * The access position in f advances. - * - *---------------------------------------------------------------------- - */ - -static int -ReadPLTE( - Tcl_Interp *interp, - PNGImage *pngPtr, - int chunkSz, - unsigned long crc) -{ - unsigned char buffer[PNG_PLTE_MAXSZ]; - int i, c; - - /* - * This chunk is mandatory for color type 3 and forbidden for 2 and 6. - */ - - switch (pngPtr->colorType) { - case PNG_COLOR_GRAY: - case PNG_COLOR_GRAYALPHA: - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "PLTE chunk type forbidden for grayscale", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PLTE_UNEXPECTED", - NULL); - return TCL_ERROR; - - default: - break; - } - - /* - * The palette chunk contains from 1 to 256 palette entries. Each entry - * consists of a 3-byte RGB value. It must therefore contain a non-zero - * multiple of 3 bytes, up to 768. - */ - - if (!chunkSz || (chunkSz > PNG_PLTE_MAXSZ) || (chunkSz % 3)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid palette chunk size", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PLTE", NULL); - return TCL_ERROR; - } - - /* - * Read the palette contents and stash them for later, possibly. - */ - - if (ReadData(interp, pngPtr, buffer, chunkSz, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Stash away the palette entries and entry count for later mapping each - * pixel's palette index to its color. - */ - - for (i=0, c=0 ; c<chunkSz ; i++) { - pngPtr->palette[i].red = buffer[c++]; - pngPtr->palette[i].green = buffer[c++]; - pngPtr->palette[i].blue = buffer[c++]; - } - - pngPtr->paletteLen = i; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadTRNS -- - * - * This function reads the tRNS (transparency) chunk data from the PNG - * file and populates the alpha field of the palette table in the - * PNGImage structure or the single color transparency, as appropriate - * for the color type. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs or the tRNS chunk is - * invalid. - * - * Side effects: - * The access position in f advances. - * - *---------------------------------------------------------------------- - */ - -static int -ReadTRNS( - Tcl_Interp *interp, - PNGImage *pngPtr, - int chunkSz, - unsigned long crc) -{ - unsigned char buffer[PNG_TRNS_MAXSZ]; - int i; - - if (pngPtr->colorType & PNG_COLOR_ALPHA) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "tRNS chunk not allowed color types with a full alpha channel", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "INVALID_TRNS", NULL); - return TCL_ERROR; - } - - /* - * For indexed color, there is up to one single-byte transparency value - * per palette entry (thus a max of 256). - */ - - if (chunkSz > PNG_TRNS_MAXSZ) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid tRNS chunk size", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL); - return TCL_ERROR; - } - - /* - * Read in the raw transparency information. - */ - - if (ReadData(interp, pngPtr, buffer, chunkSz, &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - switch (pngPtr->colorType) { - case PNG_COLOR_GRAYALPHA: - case PNG_COLOR_RGBA: - break; - - case PNG_COLOR_PLTE: - /* - * The number of tRNS entries must be less than or equal to the number - * of PLTE entries, and consists of a single-byte alpha level for the - * corresponding PLTE entry. - */ - - if (chunkSz > pngPtr->paletteLen) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "size of tRNS chunk is too large for the palette", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TRNS_SIZE", NULL); - return TCL_ERROR; - } - - for (i=0 ; i<chunkSz ; i++) { - pngPtr->palette[i].alpha = buffer[i]; - } - break; - - case PNG_COLOR_GRAY: - /* - * Grayscale uses a single 2-byte gray level, which we'll store in - * palette index 0, since we're not using the palette. - */ - - if (chunkSz != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid tRNS chunk size - must 2 bytes for grayscale", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL); - return TCL_ERROR; - } - - /* - * According to the PNG specs, if the bit depth is less than 16, then - * only the lower byte is used. - */ - - if (16 == pngPtr->bitDepth) { - pngPtr->transVal[0] = buffer[0]; - pngPtr->transVal[1] = buffer[1]; - } else { - pngPtr->transVal[0] = buffer[1]; - } - pngPtr->useTRNS = 1; - break; - - case PNG_COLOR_RGB: - /* - * TrueColor uses a single RRGGBB triplet. - */ - - if (chunkSz != 6) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invalid tRNS chunk size - must 6 bytes for RGB", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL); - return TCL_ERROR; - } - - /* - * According to the PNG specs, if the bit depth is less than 16, then - * only the lower byte is used. But the tRNS chunk still contains two - * bytes per channel. - */ - - if (16 == pngPtr->bitDepth) { - memcpy(pngPtr->transVal, buffer, 6); - } else { - pngPtr->transVal[0] = buffer[1]; - pngPtr->transVal[1] = buffer[3]; - pngPtr->transVal[2] = buffer[5]; - } - pngPtr->useTRNS = 1; - break; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Paeth -- - * - * Utility function for applying the Paeth filter to a pixel. The Paeth - * filter is a linear function of the pixel to be filtered and the pixels - * to the left, above, and above-left of the pixel to be unfiltered. - * - * Results: - * Result of the Paeth function for the left, above, and above-left - * pixels. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static inline unsigned char -Paeth( - int a, - int b, - int c) -{ - int pa = abs(b - c); - int pb = abs(a - c); - int pc = abs(a + b - c - c); - - if ((pa <= pb) && (pa <= pc)) { - return (unsigned char) a; - } - - if (pb <= pc) { - return (unsigned char) b; - } - - return (unsigned char) c; -} - -/* - *---------------------------------------------------------------------- - * - * UnfilterLine -- - * - * Applies the filter algorithm specified in first byte of a line to the - * line of pixels being read from a PNG image. - * - * PNG specifies four filter algorithms (Sub, Up, Average, and Paeth) - * that combine a pixel's value with those of other pixels in the same - * and/or previous lines. Filtering is intended to make an image more - * compressible. - * - * Results: - * TCL_OK, or TCL_ERROR if the filter type is not recognized. - * - * Side effects: - * Pixel data in thisLineObj are modified. - * - *---------------------------------------------------------------------- - */ - -static int -UnfilterLine( - Tcl_Interp *interp, - PNGImage *pngPtr) -{ - unsigned char *thisLine = - Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, NULL); - unsigned char *lastLine = - Tcl_GetByteArrayFromObj(pngPtr->lastLineObj, NULL); - -#define PNG_FILTER_NONE 0 -#define PNG_FILTER_SUB 1 -#define PNG_FILTER_UP 2 -#define PNG_FILTER_AVG 3 -#define PNG_FILTER_PAETH 4 - - switch (*thisLine) { - case PNG_FILTER_NONE: /* Nothing to do */ - break; - case PNG_FILTER_SUB: { /* Sub(x) = Raw(x) - Raw(x-bpp) */ - unsigned char *rawBpp = thisLine + 1; - unsigned char *raw = rawBpp + pngPtr->bytesPerPixel; - unsigned char *end = thisLine + pngPtr->phaseSize; - - while (raw < end) { - *raw++ += *rawBpp++; - } - break; - } - case PNG_FILTER_UP: /* Up(x) = Raw(x) - Prior(x) */ - if (pngPtr->currentLine > startLine[pngPtr->phase]) { - unsigned char *prior = lastLine + 1; - unsigned char *raw = thisLine + 1; - unsigned char *end = thisLine + pngPtr->phaseSize; - - while (raw < end) { - *raw++ += *prior++; - } - } - break; - case PNG_FILTER_AVG: - /* Avg(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) */ - if (pngPtr->currentLine > startLine[pngPtr->phase]) { - unsigned char *prior = lastLine + 1; - unsigned char *rawBpp = thisLine + 1; - unsigned char *raw = rawBpp; - unsigned char *end = thisLine + pngPtr->phaseSize; - unsigned char *end2 = raw + pngPtr->bytesPerPixel; - - while ((raw < end2) && (raw < end)) { - *raw++ += *prior++ / 2; - } - - while (raw < end) { - *raw++ += (unsigned char) - (((int) *rawBpp++ + (int) *prior++) / 2); - } - } else { - unsigned char *rawBpp = thisLine + 1; - unsigned char *raw = rawBpp + pngPtr->bytesPerPixel; - unsigned char *end = thisLine + pngPtr->phaseSize; - - while (raw < end) { - *raw++ += *rawBpp++ / 2; - } - } - break; - case PNG_FILTER_PAETH: - /* Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) */ - if (pngPtr->currentLine > startLine[pngPtr->phase]) { - unsigned char *priorBpp = lastLine + 1; - unsigned char *prior = priorBpp; - unsigned char *rawBpp = thisLine + 1; - unsigned char *raw = rawBpp; - unsigned char *end = thisLine + pngPtr->phaseSize; - unsigned char *end2 = rawBpp + pngPtr->bytesPerPixel; - - while ((raw < end) && (raw < end2)) { - *raw++ += *prior++; - } - - while (raw < end) { - *raw++ += Paeth(*rawBpp++, *prior++, *priorBpp++); - } - } else { - unsigned char *rawBpp = thisLine + 1; - unsigned char *raw = rawBpp + pngPtr->bytesPerPixel; - unsigned char *end = thisLine + pngPtr->phaseSize; - - while (raw < end) { - *raw++ += *rawBpp++; - } - } - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "invalid filter type %d", *thisLine)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_FILTER", NULL); - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * DecodeLine -- - * - * Unfilters a line of pixels from the PNG source data and decodes the - * data into the Tk_PhotoImageBlock for later copying into the Tk image. - * - * Results: - * TCL_OK, or TCL_ERROR if the filter type is not recognized. - * - * Side effects: - * Pixel data in thisLine and block are modified and state information - * updated. - * - *---------------------------------------------------------------------- - */ - -static int -DecodeLine( - Tcl_Interp *interp, - PNGImage *pngPtr) -{ - unsigned char *pixelPtr = pngPtr->block.pixelPtr; - int colNum = 0; /* Current pixel column */ - unsigned char chan = 0; /* Current channel (0..3) = (R, G, B, A) */ - unsigned char readByte = 0; /* Current scan line byte */ - int haveBits = 0; /* Number of bits remaining in current byte */ - unsigned char pixBits = 0; /* Extracted bits for current channel */ - int shifts = 0; /* Number of channels extracted from byte */ - int offset = 0; /* Current offset into pixelPtr */ - int colStep = 1; /* Column increment each pass */ - int pixStep = 0; /* extra pixelPtr increment each pass */ - unsigned char lastPixel[6]; - unsigned char *p = Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, NULL); - - p++; - if (UnfilterLine(interp, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - if (pngPtr->currentLine >= pngPtr->block.height) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "PNG image data overflow")); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DATA_OVERFLOW", NULL); - return TCL_ERROR; - } - - - if (pngPtr->interlace) { - switch (pngPtr->phase) { - case 1: /* Phase 1: */ - colStep = 8; /* 1 pixel per block of 8 per line */ - break; /* Start at column 0 */ - case 2: /* Phase 2: */ - colStep = 8; /* 1 pixels per block of 8 per line */ - colNum = 4; /* Start at column 4 */ - break; - case 3: /* Phase 3: */ - colStep = 4; /* 2 pixels per block of 8 per line */ - break; /* Start at column 0 */ - case 4: /* Phase 4: */ - colStep = 4; /* 2 pixels per block of 8 per line */ - colNum = 2; /* Start at column 2 */ - break; - case 5: /* Phase 5: */ - colStep = 2; /* 4 pixels per block of 8 per line */ - break; /* Start at column 0 */ - case 6: /* Phase 6: */ - colStep = 2; /* 4 pixels per block of 8 per line */ - colNum = 1; /* Start at column 1 */ - break; - /* Phase 7: */ - /* 8 pixels per block of 8 per line */ - /* Start at column 0 */ - } - } - - /* - * Calculate offset into pixelPtr for the first pixel of the line. - */ - - offset = pngPtr->currentLine * pngPtr->block.pitch; - - /* - * Adjust up for the starting pixel of the line. - */ - - offset += colNum * pngPtr->block.pixelSize; - - /* - * Calculate the extra number of bytes to skip between columns. - */ - - pixStep = (colStep - 1) * pngPtr->block.pixelSize; - - for ( ; colNum < pngPtr->block.width ; colNum += colStep) { - if (haveBits < (pngPtr->bitDepth * pngPtr->numChannels)) { - haveBits = 0; - } - - for (chan = 0 ; chan < pngPtr->numChannels ; chan++) { - if (!haveBits) { - shifts = 0; - readByte = *p++; - haveBits += 8; - } - - if (16 == pngPtr->bitDepth) { - pngPtr->block.pixelPtr[offset++] = readByte; - - if (pngPtr->useTRNS) { - lastPixel[chan * 2] = readByte; - } - - readByte = *p++; - - if (pngPtr->useTRNS) { - lastPixel[(chan * 2) + 1] = readByte; - } - - pngPtr->block.pixelPtr[offset++] = readByte; - - haveBits = 0; - continue; - } - - switch (pngPtr->bitDepth) { - case 1: - pixBits = (unsigned char)((readByte >> (7-shifts)) & 0x01); - break; - case 2: - pixBits = (unsigned char)((readByte >> (6-shifts*2)) & 0x03); - break; - case 4: - pixBits = (unsigned char)((readByte >> (4-shifts*4)) & 0x0f); - break; - case 8: - pixBits = readByte; - break; - } - - if (PNG_COLOR_PLTE == pngPtr->colorType) { - pixelPtr[offset++] = pngPtr->palette[pixBits].red; - pixelPtr[offset++] = pngPtr->palette[pixBits].green; - pixelPtr[offset++] = pngPtr->palette[pixBits].blue; - pixelPtr[offset++] = pngPtr->palette[pixBits].alpha; - chan += 2; - } else { - pixelPtr[offset++] = (unsigned char) - (pixBits * pngPtr->bitScale); - - if (pngPtr->useTRNS) { - lastPixel[chan] = pixBits; - } - } - - haveBits -= pngPtr->bitDepth; - shifts++; - } - - /* - * Apply boolean transparency via tRNS data if necessary (where - * necessary means a tRNS chunk was provided and we're not using an - * alpha channel or indexed alpha). - */ - - if ((PNG_COLOR_PLTE != pngPtr->colorType) && - !(pngPtr->colorType & PNG_COLOR_ALPHA)) { - unsigned char alpha; - - if (pngPtr->useTRNS) { - if (memcmp(lastPixel, pngPtr->transVal, - pngPtr->bytesPerPixel) == 0) { - alpha = 0x00; - } else { - alpha = 0xff; - } - } else { - alpha = 0xff; - } - - pixelPtr[offset++] = alpha; - - if (16 == pngPtr->bitDepth) { - pixelPtr[offset++] = alpha; - } - } - - offset += pixStep; - } - - if (pngPtr->interlace) { - /* Skip lines */ - - switch (pngPtr->phase) { - case 1: case 2: case 3: - pngPtr->currentLine += 8; - break; - case 4: case 5: - pngPtr->currentLine += 4; - break; - case 6: case 7: - pngPtr->currentLine += 2; - break; - } - - /* - * Start the next phase if there are no more lines to do. - */ - - if (pngPtr->currentLine >= pngPtr->block.height) { - unsigned long pixels = 0; - - while ((!pixels || (pngPtr->currentLine >= pngPtr->block.height)) - && (pngPtr->phase < 7)) { - pngPtr->phase++; - - switch (pngPtr->phase) { - case 2: - pixels = (pngPtr->block.width + 3) >> 3; - pngPtr->currentLine = 0; - break; - case 3: - pixels = (pngPtr->block.width + 3) >> 2; - pngPtr->currentLine = 4; - break; - case 4: - pixels = (pngPtr->block.width + 1) >> 2; - pngPtr->currentLine = 0; - break; - case 5: - pixels = (pngPtr->block.width + 1) >> 1; - pngPtr->currentLine = 2; - break; - case 6: - pixels = pngPtr->block.width >> 1; - pngPtr->currentLine = 0; - break; - case 7: - pngPtr->currentLine = 1; - pixels = pngPtr->block.width; - break; - } - } - - if (16 == pngPtr->bitDepth) { - pngPtr->phaseSize = 1 + (pngPtr->numChannels * pixels * 2); - } else { - pngPtr->phaseSize = 1 + ((pngPtr->numChannels * pixels * - pngPtr->bitDepth + 7) >> 3); - } - } - } else { - pngPtr->currentLine++; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ReadIDAT -- - * - * This function reads the IDAT (pixel data) chunk from the PNG file to - * build the image. It will continue reading until all IDAT chunks have - * been processed or an error occurs. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs or an IDAT chunk is - * invalid. - * - * Side effects: - * The access position in f advances. Memory may be allocated by zlib - * through PNGZAlloc. - * - *---------------------------------------------------------------------- - */ - -static int -ReadIDAT( - Tcl_Interp *interp, - PNGImage *pngPtr, - int chunkSz, - unsigned long crc) -{ - /* - * Process IDAT contents until there is no more in this chunk. - */ - - while (chunkSz && !Tcl_ZlibStreamEof(pngPtr->stream)) { - int len1, len2; - - /* - * Read another block of input into the zlib stream if data remains. - */ - - if (chunkSz) { - Tcl_Obj *inputObj = NULL; - int blockSz = PNG_MIN(chunkSz, PNG_BLOCK_SZ); - unsigned char *inputPtr = NULL; - - /* - * Check for end of zlib stream. - */ - - if (Tcl_ZlibStreamEof(pngPtr->stream)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "extra data after end of zlib stream", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", - NULL); - return TCL_ERROR; - } - - inputObj = Tcl_NewObj(); - Tcl_IncrRefCount(inputObj); - inputPtr = Tcl_SetByteArrayLength(inputObj, blockSz); - - /* - * Read the next bit of IDAT chunk data, up to read buffer size. - */ - - if (ReadData(interp, pngPtr, inputPtr, blockSz, - &crc) == TCL_ERROR) { - Tcl_DecrRefCount(inputObj); - return TCL_ERROR; - } - - chunkSz -= blockSz; - - Tcl_ZlibStreamPut(pngPtr->stream, inputObj, TCL_ZLIB_NO_FLUSH); - Tcl_DecrRefCount(inputObj); - } - - /* - * Inflate, processing each output buffer's worth as a line of pixels, - * until we cannot fill the buffer any more. - */ - - getNextLine: - Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, &len1); - if (Tcl_ZlibStreamGet(pngPtr->stream, pngPtr->thisLineObj, - pngPtr->phaseSize - len1) == TCL_ERROR) { - return TCL_ERROR; - } - Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, &len2); - - if (len2 == pngPtr->phaseSize) { - if (pngPtr->phase > 7) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "extra data after final scan line of final phase", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", - NULL); - return TCL_ERROR; - } - - if (DecodeLine(interp, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Swap the current/last lines so that we always have the last - * line processed available, which is necessary for filtering. - */ - - { - Tcl_Obj *temp = pngPtr->lastLineObj; - - pngPtr->lastLineObj = pngPtr->thisLineObj; - pngPtr->thisLineObj = temp; - } - Tcl_SetByteArrayLength(pngPtr->thisLineObj, 0); - - /* - * Try to read another line of pixels out of the buffer - * immediately, but don't allow write past end of block. - */ - - if (pngPtr->currentLine < pngPtr->block.height) { - goto getNextLine; - } - - } - - /* - * Got less than a whole buffer-load of pixels. Either we're going to - * be getting more data from the next IDAT, or we've done what we can - * here. - */ - } - - /* - * Ensure that if we've got to the end of the compressed data, we've - * also got to the end of the compressed stream. This sanity check is - * enforced by most PNG readers. - */ - - if (chunkSz != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "compressed data after stream finalize in PNG data", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL); - return TCL_ERROR; - } - - return CheckCRC(interp, pngPtr, crc); -} - -/* - *---------------------------------------------------------------------- - * - * ApplyAlpha -- - * - * Applies an overall alpha value to a complete image that has been read. - * This alpha value is specified using the -format option to [image - * create photo]. - * - * Results: - * N/A - * - * Side effects: - * The access position in f may change. - * - *---------------------------------------------------------------------- - */ - -static void -ApplyAlpha( - PNGImage *pngPtr) -{ - if (pngPtr->alpha != 1.0) { - register unsigned char *p = pngPtr->block.pixelPtr; - unsigned char *endPtr = p + pngPtr->blockLen; - int offset = pngPtr->block.offset[3]; - - p += offset; - - if (16 == pngPtr->bitDepth) { - register unsigned int channel; - - while (p < endPtr) { - channel = (unsigned int) - (((p[0] << 8) | p[1]) * pngPtr->alpha); - - *p++ = (unsigned char) (channel >> 8); - *p++ = (unsigned char) (channel & 0xff); - - p += offset; - } - } else { - while (p < endPtr) { - p[0] = (unsigned char) (pngPtr->alpha * p[0]); - p += 1 + offset; - } - } - } -} - -/* - *---------------------------------------------------------------------- - * - * ParseFormat -- - * - * This function parses the -format string that can be specified to the - * [image create photo] command to extract options for postprocessing of - * loaded images. Currently, this just allows specifying and applying an - * overall alpha value to the loaded image (for example, to make it - * entirely 50% as transparent as the actual image file). - * - * Results: - * TCL_OK, or TCL_ERROR if the format specification is invalid. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -ParseFormat( - Tcl_Interp *interp, - Tcl_Obj *fmtObj, - PNGImage *pngPtr) -{ - Tcl_Obj **objv = NULL; - int objc = 0; - static const char *const fmtOptions[] = { - "-alpha", NULL - }; - enum fmtOptions { - OPT_ALPHA - }; - - /* - * Extract elements of format specification as a list. - */ - - if (fmtObj && - Tcl_ListObjGetElements(interp, fmtObj, &objc, &objv) != TCL_OK) { - return TCL_ERROR; - } - - for (; objc>0 ; objc--, objv++) { - int optIndex; - - /* - * Ignore the "png" part of the format specification. - */ - - if (!strcasecmp(Tcl_GetString(objv[0]), "png")) { - continue; - } - - if (Tcl_GetIndexFromObjStruct(interp, objv[0], fmtOptions, - sizeof(char *), "option", 0, &optIndex) == TCL_ERROR) { - return TCL_ERROR; - } - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "value"); - return TCL_ERROR; - } - - objc--; - objv++; - - switch ((enum fmtOptions) optIndex) { - case OPT_ALPHA: - if (Tcl_GetDoubleFromObj(interp, objv[0], - &pngPtr->alpha) == TCL_ERROR) { - return TCL_ERROR; - } - - if ((pngPtr->alpha < 0.0) || (pngPtr->alpha > 1.0)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "-alpha value must be between 0.0 and 1.0", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_ALPHA", - NULL); - return TCL_ERROR; - } - break; - } - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * DecodePNG -- - * - * This function handles the entirety of reading a PNG file (or data) - * from the first byte to the last. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O error occurs or any problems are - * detected in the PNG file. - * - * Side effects: - * The access position in f advances. Memory may be allocated and image - * dimensions and contents may change. - * - *---------------------------------------------------------------------- - */ - -static int -DecodePNG( - Tcl_Interp *interp, - PNGImage *pngPtr, - Tcl_Obj *fmtObj, - Tk_PhotoHandle imageHandle, - int destX, - int destY) -{ - unsigned long chunkType; - int chunkSz; - unsigned long crc; - - /* - * Parse the PNG signature and IHDR (header) chunk. - */ - - if (ReadIHDR(interp, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Extract alpha value from -format object, if specified. - */ - - if (ParseFormat(interp, fmtObj, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * The next chunk may either be a PLTE (Palette) chunk or the first of at - * least one IDAT (data) chunks. It could also be one of a number of - * ancillary chunks, but those are skipped for us by the switch in - * ReadChunkHeader(). - * - * PLTE is mandatory for color type 3 and forbidden for 2 and 6 - */ - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (CHUNK_PLTE == chunkType) { - /* - * Finish parsing the PLTE chunk. - */ - - if (ReadPLTE(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Begin the next chunk. - */ - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - } else if (PNG_COLOR_PLTE == pngPtr->colorType) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "PLTE chunk required for indexed color", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_PLTE", NULL); - return TCL_ERROR; - } - - /* - * The next chunk may be a tRNS (palette transparency) chunk, depending on - * the color type. It must come after the PLTE chunk and before the IDAT - * chunk, but can be present if there is no PLTE chunk because it can be - * used for Grayscale and TrueColor in lieu of an alpha channel. - */ - - if (CHUNK_tRNS == chunkType) { - /* - * Finish parsing the tRNS chunk. - */ - - if (ReadTRNS(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Begin the next chunk. - */ - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - } - - /* - * Other ancillary chunk types could appear here, but for now we're only - * interested in IDAT. The others should have been skipped. - */ - - if (chunkType != CHUNK_IDAT) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "at least one IDAT chunk is required", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_IDAT", NULL); - return TCL_ERROR; - } - - /* - * Expand the photo size (if not set by the user) to provide enough space - * for the image being parsed. It does not matter if width or height wrap - * to negative here: Tk will not shrink the image. - */ - - if (Tk_PhotoExpand(interp, imageHandle, destX + pngPtr->block.width, - destY + pngPtr->block.height) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * A scan line consists of one byte for a filter type, plus the number of - * bits per color sample times the number of color samples per pixel. - */ - - if (pngPtr->block.width > ((INT_MAX - 1) / (pngPtr->numChannels * 2))) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "line size is out of supported range on this architecture", - -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "LINE_SIZE", NULL); - return TCL_ERROR; - } - - if (16 == pngPtr->bitDepth) { - pngPtr->lineSize = 1 + (pngPtr->numChannels * pngPtr->block.width*2); - } else { - pngPtr->lineSize = 1 + ((pngPtr->numChannels * pngPtr->block.width) / - (8 / pngPtr->bitDepth)); - if (pngPtr->block.width % (8 / pngPtr->bitDepth)) { - pngPtr->lineSize++; - } - } - - /* - * Allocate space for decoding the scan lines. - */ - - pngPtr->lastLineObj = Tcl_NewObj(); - Tcl_IncrRefCount(pngPtr->lastLineObj); - pngPtr->thisLineObj = Tcl_NewObj(); - Tcl_IncrRefCount(pngPtr->thisLineObj); - - pngPtr->block.pixelPtr = attemptckalloc(pngPtr->blockLen); - if (!pngPtr->block.pixelPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "memory allocation failed", -1)); - Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); - return TCL_ERROR; - } - - /* - * Determine size of the first phase if interlaced. Phase size should - * always be <= line size, so probably not necessary to check for - * arithmetic overflow here: should be covered by line size check. - */ - - if (pngPtr->interlace) { - /* - * Only one pixel per block of 8 per line in the first phase. - */ - - unsigned int pixels = (pngPtr->block.width + 7) >> 3; - - pngPtr->phase = 1; - if (16 == pngPtr->bitDepth) { - pngPtr->phaseSize = 1 + pngPtr->numChannels*pixels*2; - } else { - pngPtr->phaseSize = 1 + - ((pngPtr->numChannels*pixels*pngPtr->bitDepth + 7) >> 3); - } - } else { - pngPtr->phaseSize = pngPtr->lineSize; - } - - /* - * All of the IDAT (data) chunks must be consecutive. - */ - - while (CHUNK_IDAT == chunkType) { - if (ReadIDAT(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - } - - /* - * Ensure that we've got to the end of the compressed stream now that - * there are no more IDAT segments. This sanity check is enforced by most - * PNG readers. - */ - - if (!Tcl_ZlibStreamEof(pngPtr->stream)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unfinalized data stream in PNG data", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL); - return TCL_ERROR; - } - - /* - * Now skip the remaining chunks which we're also not interested in. - */ - - while (CHUNK_IEND != chunkType) { - if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType, - &crc) == TCL_ERROR) { - return TCL_ERROR; - } - } - - /* - * Got the IEND (end of image) chunk. Do some final checks... - */ - - if (chunkSz) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "IEND chunk contents must be empty", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL); - return TCL_ERROR; - } - - /* - * Check the CRC on the IEND chunk. - */ - - if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * TODO: verify that nothing else comes after the IEND chunk, or do we - * really care? - */ - -#if 0 - if (ReadData(interp, pngPtr, &c, 1, NULL) != TCL_ERROR) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "extra data following IEND chunk", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL); - return TCL_ERROR; - } -#endif - - /* - * Apply overall image alpha if specified. - */ - - ApplyAlpha(pngPtr); - - /* - * Copy the decoded image block into the Tk photo image. - */ - - if (Tk_PhotoPutBlock(interp, imageHandle, &pngPtr->block, destX, destY, - pngPtr->block.width, pngPtr->block.height, - TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) { - return TCL_ERROR; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * FileMatchPNG -- - * - * This function is invoked by the photo image type to see if a file - * contains image data in PNG format. - * - * Results: - * The return value is 1 if the first characters in file f look like PNG - * data, and 0 otherwise. - * - * Side effects: - * The access position in f may change. - * - *---------------------------------------------------------------------- - */ - -static int -FileMatchPNG( - Tcl_Channel chan, - const char *fileName, - Tcl_Obj *fmtObj, - int *widthPtr, - int *heightPtr, - Tcl_Interp *interp) -{ - PNGImage png; - int match = 0; - - InitPNGImage(NULL, &png, chan, NULL, TCL_ZLIB_STREAM_INFLATE); - - if (ReadIHDR(interp, &png) == TCL_OK) { - *widthPtr = png.block.width; - *heightPtr = png.block.height; - match = 1; - } - - CleanupPNGImage(&png); - - return match; -} - -/* - *---------------------------------------------------------------------- - * - * FileReadPNG -- - * - * This function is called by the photo image type to read PNG format - * data from a file and write it into a given photo image. - * - * Results: - * A standard TCL completion code. If TCL_ERROR is returned then an error - * message is left in the interp's result. - * - * Side effects: - * The access position in file f is changed, and new data is added to the - * image given by imageHandle. - * - *---------------------------------------------------------------------- - */ - -static int -FileReadPNG( - Tcl_Interp *interp, - Tcl_Channel chan, - const char *fileName, - Tcl_Obj *fmtObj, - Tk_PhotoHandle imageHandle, - int destX, - int destY, - int width, - int height, - int srcX, - int srcY) -{ - PNGImage png; - int result = TCL_ERROR; - - result = InitPNGImage(interp, &png, chan, NULL, TCL_ZLIB_STREAM_INFLATE); - - if (TCL_OK == result) { - result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY); - } - - CleanupPNGImage(&png); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * StringMatchPNG -- - * - * This function is invoked by the photo image type to see if an object - * contains image data in PNG format. - * - * Results: - * The return value is 1 if the first characters in the data are like PNG - * data, and 0 otherwise. - * - * Side effects: - * The size of the image is placed in widthPre and heightPtr. - * - *---------------------------------------------------------------------- - */ - -static int -StringMatchPNG( - Tcl_Obj *pObjData, - Tcl_Obj *fmtObj, - int *widthPtr, - int *heightPtr, - Tcl_Interp *interp) -{ - PNGImage png; - int match = 0; - - InitPNGImage(NULL, &png, NULL, pObjData, TCL_ZLIB_STREAM_INFLATE); - - png.strDataBuf = Tcl_GetByteArrayFromObj(pObjData, &png.strDataLen); - - if (ReadIHDR(interp, &png) == TCL_OK) { - *widthPtr = png.block.width; - *heightPtr = png.block.height; - match = 1; - } - - CleanupPNGImage(&png); - return match; -} - -/* - *---------------------------------------------------------------------- - * - * StringReadPNG -- - * - * This function is called by the photo image type to read PNG format - * data from an object and give it to the photo image. - * - * Results: - * A standard TCL completion code. If TCL_ERROR is returned then an error - * message is left in the interp's result. - * - * Side effects: - * New data is added to the image given by imageHandle. - * - *---------------------------------------------------------------------- - */ - -static int -StringReadPNG( - Tcl_Interp *interp, - Tcl_Obj *pObjData, - Tcl_Obj *fmtObj, - Tk_PhotoHandle imageHandle, - int destX, - int destY, - int width, - int height, - int srcX, - int srcY) -{ - PNGImage png; - int result = TCL_ERROR; - - result = InitPNGImage(interp, &png, NULL, pObjData, - TCL_ZLIB_STREAM_INFLATE); - - if (TCL_OK == result) { - result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY); - } - - CleanupPNGImage(&png); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * WriteData -- - * - * This function writes a bytes from a buffer out to the PNG image. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * File or buffer will be modified. - * - *---------------------------------------------------------------------- - */ - -static int -WriteData( - Tcl_Interp *interp, - PNGImage *pngPtr, - const unsigned char *srcPtr, - int srcSz, - unsigned long *crcPtr) -{ - if (!srcPtr || !srcSz) { - return TCL_OK; - } - - if (crcPtr) { - *crcPtr = Tcl_ZlibCRC32(*crcPtr, srcPtr, srcSz); - } - - /* - * TODO: is Tcl_AppendObjToObj faster here? i.e., does Tcl join the - * objects immediately or store them in a multi-object rep? - */ - - if (pngPtr->objDataPtr) { - int objSz; - unsigned char *destPtr; - - Tcl_GetByteArrayFromObj(pngPtr->objDataPtr, &objSz); - - if (objSz > INT_MAX - srcSz) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "image too large to store completely in byte array", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL); - return TCL_ERROR; - } - - destPtr = Tcl_SetByteArrayLength(pngPtr->objDataPtr, objSz + srcSz); - - if (!destPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "memory allocation failed", -1)); - Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); - return TCL_ERROR; - } - - memcpy(destPtr+objSz, srcPtr, srcSz); - } else if (Tcl_Write(pngPtr->channel, (const char *) srcPtr, srcSz) < 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "write to channel failed: %s", Tcl_PosixError(interp))); - return TCL_ERROR; - } - - return TCL_OK; -} - -static inline int -WriteByte( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned char c, - unsigned long *crcPtr) -{ - return WriteData(interp, pngPtr, &c, 1, crcPtr); -} - -/* - *---------------------------------------------------------------------- - * - * WriteInt32 -- - * - * This function writes a 32-bit integer value out to the PNG image as - * four bytes in network byte order. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * File or buffer will be modified. - * - *---------------------------------------------------------------------- - */ - -static inline int -WriteInt32( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned long l, - unsigned long *crcPtr) -{ - unsigned char pc[4]; - - pc[0] = (unsigned char) ((l & 0xff000000) >> 24); - pc[1] = (unsigned char) ((l & 0x00ff0000) >> 16); - pc[2] = (unsigned char) ((l & 0x0000ff00) >> 8); - pc[3] = (unsigned char) ((l & 0x000000ff) >> 0); - - return WriteData(interp, pngPtr, pc, 4, crcPtr); -} - -/* - *---------------------------------------------------------------------- - * - * WriteChunk -- - * - * Writes a complete chunk to the PNG image, including chunk type, - * length, contents, and CRC. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static inline int -WriteChunk( - Tcl_Interp *interp, - PNGImage *pngPtr, - unsigned long chunkType, - const unsigned char *dataPtr, - int dataSize) -{ - unsigned long crc = Tcl_ZlibCRC32(0, NULL, 0); - int result = TCL_OK; - - /* - * Write the length field for the chunk. - */ - - result = WriteInt32(interp, pngPtr, dataSize, NULL); - - /* - * Write the Chunk Type. - */ - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, chunkType, &crc); - } - - /* - * Write the contents (if any). - */ - - if (TCL_OK == result) { - result = WriteData(interp, pngPtr, dataPtr, dataSize, &crc); - } - - /* - * Write out the CRC at the end of the chunk. - */ - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, crc, NULL); - } - - return result; -} - -/* - *---------------------------------------------------------------------- - * - * WriteIHDR -- - * - * This function writes the PNG header at the beginning of a PNG file, - * which includes information such as dimensions and color type. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * File or buffer will be modified. - * - *---------------------------------------------------------------------- - */ - -static int -WriteIHDR( - Tcl_Interp *interp, - PNGImage *pngPtr, - Tk_PhotoImageBlock *blockPtr) -{ - unsigned long crc = Tcl_ZlibCRC32(0, NULL, 0); - int result = TCL_OK; - - /* - * The IHDR (header) chunk has a fixed size of 13 bytes. - */ - - result = WriteInt32(interp, pngPtr, 13, NULL); - - /* - * Write the IHDR Chunk Type. - */ - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, CHUNK_IHDR, &crc); - } - - /* - * Write the image width, height. - */ - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, (unsigned long) blockPtr->width, - &crc); - } - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, (unsigned long) blockPtr->height, - &crc); - } - - /* - * Write bit depth. Although the PNG format supports 16 bits per channel, - * Tk supports only 8 in the internal representation, which blockPtr - * points to. - */ - - if (TCL_OK == result) { - result = WriteByte(interp, pngPtr, 8, &crc); - } - - /* - * Write out the color type, previously determined. - */ - - if (TCL_OK == result) { - result = WriteByte(interp, pngPtr, pngPtr->colorType, &crc); - } - - /* - * Write compression method (only one method is defined). - */ - - if (TCL_OK == result) { - result = WriteByte(interp, pngPtr, PNG_COMPRESS_DEFLATE, &crc); - } - - /* - * Write filter method (only one method is defined). - */ - - if (TCL_OK == result) { - result = WriteByte(interp, pngPtr, PNG_FILTMETH_STANDARD, &crc); - } - - /* - * Write interlace method as not interlaced. - * - * TODO: support interlace through -format? - */ - - if (TCL_OK == result) { - result = WriteByte(interp, pngPtr, PNG_INTERLACE_NONE, &crc); - } - - /* - * Write out the CRC at the end of the chunk. - */ - - if (TCL_OK == result) { - result = WriteInt32(interp, pngPtr, crc, NULL); - } - - return result; -} - -/* - *---------------------------------------------------------------------- - * - * WriteIDAT -- - * - * Writes the IDAT (data) chunk to the PNG image, containing the pixel - * channel data. Currently, image lines are not filtered and writing - * interlaced pixels is not supported. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -WriteIDAT( - Tcl_Interp *interp, - PNGImage *pngPtr, - Tk_PhotoImageBlock *blockPtr) -{ - int rowNum, flush = TCL_ZLIB_NO_FLUSH, outputSize, result; - Tcl_Obj *outputObj; - unsigned char *outputBytes; - - /* - * Filter and compress each row one at a time. - */ - - for (rowNum=0 ; rowNum < blockPtr->height ; rowNum++) { - int colNum; - unsigned char *srcPtr, *destPtr; - - srcPtr = blockPtr->pixelPtr + (rowNum * blockPtr->pitch); - destPtr = Tcl_SetByteArrayLength(pngPtr->thisLineObj, - pngPtr->lineSize); - - /* - * TODO: use Paeth filtering. - */ - - *destPtr++ = PNG_FILTER_NONE; - - /* - * Copy each pixel into the destination buffer after the filter type - * before filtering. - */ - - for (colNum = 0 ; colNum < blockPtr->width ; colNum++) { - /* - * Copy red or gray channel. - */ - - *destPtr++ = srcPtr[blockPtr->offset[0]]; - - /* - * If not grayscale, copy the green and blue channels. - */ - - if (pngPtr->colorType & PNG_COLOR_USED) { - *destPtr++ = srcPtr[blockPtr->offset[1]]; - *destPtr++ = srcPtr[blockPtr->offset[2]]; - } - - /* - * Copy the alpha channel, if used. - */ - - if (pngPtr->colorType & PNG_COLOR_ALPHA) { - *destPtr++ = srcPtr[blockPtr->offset[3]]; - } - - /* - * Point to the start of the next pixel. - */ - - srcPtr += blockPtr->pixelSize; - } - - /* - * Compress the line of pixels into the destination. If this is the - * last line, finalize the compressor at the same time. Note that this - * can't be just a flush; that leads to a file that some PNG readers - * choke on. [Bug 2984787] - */ - - if (rowNum + 1 == blockPtr->height) { - flush = TCL_ZLIB_FINALIZE; - } - if (Tcl_ZlibStreamPut(pngPtr->stream, pngPtr->thisLineObj, - flush) != TCL_OK) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "deflate() returned error", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DEFLATE", NULL); - return TCL_ERROR; - } - - /* - * Swap line buffers to keep the last around for filtering next. - */ - - { - Tcl_Obj *temp = pngPtr->lastLineObj; - - pngPtr->lastLineObj = pngPtr->thisLineObj; - pngPtr->thisLineObj = temp; - } - } - - /* - * Now get the compressed data and write it as one big IDAT chunk. - */ - - outputObj = Tcl_NewObj(); - (void) Tcl_ZlibStreamGet(pngPtr->stream, outputObj, -1); - outputBytes = Tcl_GetByteArrayFromObj(outputObj, &outputSize); - result = WriteChunk(interp, pngPtr, CHUNK_IDAT, outputBytes, outputSize); - Tcl_DecrRefCount(outputObj); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * WriteExtraChunks -- - * - * Writes an sBIT and a tEXt chunks to the PNG image, describing a bunch - * of not very important metadata that many readers seem to need anyway. - * - * Results: - * TCL_OK, or TCL_ERROR if the write fails. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -WriteExtraChunks( - Tcl_Interp *interp, - PNGImage *pngPtr) -{ - static const unsigned char sBIT_contents[] = { - 8, 8, 8, 8 - }; - int sBIT_length = 4; - Tcl_DString buf; - - /* - * Each byte of each channel is always significant; we always write RGBA - * images with 8 bits per channel as that is what the photo image's basic - * data model is. - */ - - switch (pngPtr->colorType) { - case PNG_COLOR_GRAY: - sBIT_length = 1; - break; - case PNG_COLOR_GRAYALPHA: - sBIT_length = 2; - break; - case PNG_COLOR_RGB: - case PNG_COLOR_PLTE: - sBIT_length = 3; - break; - case PNG_COLOR_RGBA: - sBIT_length = 4; - break; - } - if (WriteChunk(interp, pngPtr, CHUNK_sBIT, sBIT_contents, sBIT_length) - != TCL_OK) { - return TCL_ERROR; - } - - /* - * Say that it is Tk that made the PNG. Note that we *need* the NUL at the - * end of "Software" to be transferred; do *not* change the length - * parameter to -1 there! - */ - - Tcl_DStringInit(&buf); - Tcl_DStringAppend(&buf, "Software", 9); - Tcl_DStringAppend(&buf, "Tk Toolkit v", -1); - Tcl_DStringAppend(&buf, TK_PATCH_LEVEL, -1); - if (WriteChunk(interp, pngPtr, CHUNK_tEXt, - (unsigned char *) Tcl_DStringValue(&buf), - Tcl_DStringLength(&buf)) != TCL_OK) { - Tcl_DStringFree(&buf); - return TCL_ERROR; - } - Tcl_DStringFree(&buf); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * EncodePNG -- - * - * This function handles the entirety of writing a PNG file (or data) - * from the first byte to the last. No effort is made to optimize the - * image data for best compression. - * - * Results: - * TCL_OK, or TCL_ERROR if an I/O or memory error occurs. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -EncodePNG( - Tcl_Interp *interp, - Tk_PhotoImageBlock *blockPtr, - PNGImage *pngPtr) -{ - int greenOffset, blueOffset, alphaOffset; - - /* - * Determine appropriate color type based on color usage (e.g., only red - * and maybe alpha channel = grayscale). - * - * TODO: Check whether this is doing any good; Tk might just be pushing - * full RGBA data all the time through here, even though the actual image - * doesn't need it... - */ - - greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; - blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; - alphaOffset = blockPtr->offset[3]; - if ((alphaOffset >= blockPtr->pixelSize) || (alphaOffset < 0)) { - alphaOffset = 0; - } else { - alphaOffset -= blockPtr->offset[0]; - } - - if ((greenOffset != 0) || (blueOffset != 0)) { - if (alphaOffset) { - pngPtr->colorType = PNG_COLOR_RGBA; - pngPtr->bytesPerPixel = 4; - } else { - pngPtr->colorType = PNG_COLOR_RGB; - pngPtr->bytesPerPixel = 3; - } - } else { - if (alphaOffset) { - pngPtr->colorType = PNG_COLOR_GRAYALPHA; - pngPtr->bytesPerPixel = 2; - } else { - pngPtr->colorType = PNG_COLOR_GRAY; - pngPtr->bytesPerPixel = 1; - } - } - - /* - * Allocate buffers for lines for filtering and compressed data. - */ - - pngPtr->lineSize = 1 + (pngPtr->bytesPerPixel * blockPtr->width); - pngPtr->blockLen = pngPtr->lineSize * blockPtr->height; - - if ((blockPtr->width > (INT_MAX - 1) / (pngPtr->bytesPerPixel)) || - (blockPtr->height > INT_MAX / pngPtr->lineSize)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "image is too large to encode pixel data", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL); - return TCL_ERROR; - } - - pngPtr->lastLineObj = Tcl_NewObj(); - Tcl_IncrRefCount(pngPtr->lastLineObj); - pngPtr->thisLineObj = Tcl_NewObj(); - Tcl_IncrRefCount(pngPtr->thisLineObj); - - /* - * Write out the PNG Signature that all PNGs begin with. - */ - - if (WriteData(interp, pngPtr, pngSignature, PNG_SIG_SZ, - NULL) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Write out the IHDR (header) chunk containing image dimensions, color - * type, etc. - */ - - if (WriteIHDR(interp, pngPtr, blockPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Write out the extra chunks containing metadata that is of interest to - * other programs more than us. - */ - - if (WriteExtraChunks(interp, pngPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Write out the image pixels in the IDAT (data) chunk. - */ - - if (WriteIDAT(interp, pngPtr, blockPtr) == TCL_ERROR) { - return TCL_ERROR; - } - - /* - * Write out the IEND chunk that all PNGs end with. - */ - - return WriteChunk(interp, pngPtr, CHUNK_IEND, NULL, 0); -} - -/* - *---------------------------------------------------------------------- - * - * FileWritePNG -- - * - * This function is called by the photo image type to write PNG format - * data to a file. - * - * Results: - * A standard TCL completion code. If TCL_ERROR is returned then an error - * message is left in the interp's result. - * - * Side effects: - * The specified file is overwritten. - * - *---------------------------------------------------------------------- - */ - -static int -FileWritePNG( - Tcl_Interp *interp, - const char *filename, - Tcl_Obj *fmtObj, - Tk_PhotoImageBlock *blockPtr) -{ - Tcl_Channel chan; - PNGImage png; - int result = TCL_ERROR; - - /* - * Open a Tcl file channel where the image data will be stored. Tk ought - * to take care of this, and just provide a channel, but it doesn't. - */ - - chan = Tcl_OpenFileChannel(interp, filename, "w", 0644); - - if (!chan) { - return TCL_ERROR; - } - - /* - * Initalize PNGImage instance for encoding. - */ - - if (InitPNGImage(interp, &png, chan, NULL, - TCL_ZLIB_STREAM_DEFLATE) == TCL_ERROR) { - goto cleanup; - } - - /* - * Set the translation mode to binary so that CR and LF are not to the - * platform's EOL sequence. - */ - - if (Tcl_SetChannelOption(interp, chan, "-translation", - "binary") != TCL_OK) { - goto cleanup; - } - - /* - * Write the raw PNG data out to the file. - */ - - result = EncodePNG(interp, blockPtr, &png); - - cleanup: - Tcl_Close(interp, chan); - CleanupPNGImage(&png); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * StringWritePNG -- - * - * This function is called by the photo image type to write PNG format - * data to a Tcl object and return it in the result. - * - * Results: - * A standard TCL completion code. If TCL_ERROR is returned then an error - * message is left in the interp's result. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -static int -StringWritePNG( - Tcl_Interp *interp, - Tcl_Obj *fmtObj, - Tk_PhotoImageBlock *blockPtr) -{ - Tcl_Obj *resultObj = Tcl_NewObj(); - PNGImage png; - int result = TCL_ERROR; - - /* - * Initalize PNGImage instance for encoding. - */ - - if (InitPNGImage(interp, &png, NULL, resultObj, - TCL_ZLIB_STREAM_DEFLATE) == TCL_ERROR) { - goto cleanup; - } - - /* - * Write the raw PNG data into the prepared Tcl_Obj buffer. Set the result - * back to the interpreter if successful. - */ - - result = EncodePNG(interp, blockPtr, &png); - - if (TCL_OK == result) { - Tcl_SetObjResult(interp, png.objDataPtr); - } - - cleanup: - CleanupPNGImage(&png); - return result; -} - -/* - * Local Variables: - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |