summaryrefslogtreecommitdiffstats
path: root/generic/tkImgPNG.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkImgPNG.c')
-rw-r--r--generic/tkImgPNG.c493
1 files changed, 387 insertions, 106 deletions
diff --git a/generic/tkImgPNG.c b/generic/tkImgPNG.c
index 99ba37a..a49ed15 100644
--- a/generic/tkImgPNG.c
+++ b/generic/tkImgPNG.c
@@ -3,8 +3,8 @@
*
* A Tk photo image file handler for PNG files.
*
- * Copyright (c) 2006-2008 Muonics, Inc.
- * Copyright (c) 2008 Donal K. Fellows
+ * Copyright © 2006-2008 Muonics, Inc.
+ * Copyright © 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.
@@ -126,7 +126,7 @@ typedef struct {
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. */
+ Tcl_Size 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. */
@@ -175,6 +175,15 @@ typedef struct {
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. */
+
+
+ /*
+ * Physical size: pHYS chunks.
+ */
+
+ double DPI;
+ double aspect;
+
} PNGImage;
/*
@@ -199,32 +208,36 @@ static int DecodePNG(Tcl_Interp *interp, PNGImage *pngPtr,
int destX, int destY, int width, int height,
int srcX, int srcY);
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);
+ Tk_PhotoImageBlock *blockPtr, PNGImage *pngPtr,
+ Tcl_Obj *metadataInObj);
+static int FileMatchPNG(Tcl_Interp *interp, Tcl_Channel chan,
+ const char *fileName, Tcl_Obj *fmtObj,
+ Tcl_Obj *metadataInObj, int *widthPtr,
+ int *heightPtr, Tcl_Obj *metadataOut);
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);
+ Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle,
+ int destX, int destY, int width, int height,
+ int srcX, int srcY, Tcl_Obj *metadataOutPtr);
static int FileWritePNG(Tcl_Interp *interp, const char *filename,
- Tcl_Obj *fmtObj, Tk_PhotoImageBlock *blockPtr);
+ Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
+ 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 char *destPtr, Tcl_Size destSz,
unsigned long *crcPtr);
static int ReadByteArray(Tcl_Interp *interp, PNGImage *pngPtr,
- unsigned char *destPtr, int destSz,
+ unsigned char *destPtr, Tcl_Size destSz,
unsigned long *crcPtr);
static int ReadData(Tcl_Interp *interp, PNGImage *pngPtr,
- unsigned char *destPtr, int destSz,
+ unsigned char *destPtr, Tcl_Size destSz,
unsigned long *crcPtr);
static int ReadChunkHeader(Tcl_Interp *interp, PNGImage *pngPtr,
- int *sizePtr, unsigned long *typePtr,
+ Tcl_Size *sizePtr, unsigned long *typePtr,
unsigned long *crcPtr);
static int ReadIDAT(Tcl_Interp *interp, PNGImage *pngPtr,
int chunkSz, unsigned long crc);
@@ -237,26 +250,30 @@ 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,
+static int StringMatchPNG(Tcl_Interp *interp, Tcl_Obj *pObjData,
+ Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
int *widthPtr, int *heightPtr,
- Tcl_Interp *interp);
-static int StringReadPNG(Tcl_Interp *interp, Tcl_Obj *dataObj,
- Tcl_Obj *fmtObj, Tk_PhotoHandle imageHandle,
+ Tcl_Obj *metadataOutObj);
+static int StringReadPNG(Tcl_Interp *interp, Tcl_Obj *pObjData,
+ Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
+ Tk_PhotoHandle imageHandle,
int destX, int destY, int width, int height,
- int srcX, int srcY);
+ int srcX, int srcY, Tcl_Obj *metadataOutObj);
+
static int StringWritePNG(Tcl_Interp *interp, Tcl_Obj *fmtObj,
+ Tcl_Obj *metadataInObj,
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);
+ const unsigned char *dataPtr, Tcl_Size dataSize);
static int WriteData(Tcl_Interp *interp, PNGImage *pngPtr,
- const unsigned char *srcPtr, int srcSz,
+ const unsigned char *srcPtr, Tcl_Size srcSz,
unsigned long *crcPtr);
static int WriteExtraChunks(Tcl_Interp *interp,
- PNGImage *pngPtr);
+ PNGImage *pngPtr, Tcl_Obj *metadataInObj);
static int WriteIHDR(Tcl_Interp *interp, PNGImage *pngPtr,
Tk_PhotoImageBlock *blockPtr);
static int WriteIDAT(Tcl_Interp *interp, PNGImage *pngPtr,
@@ -268,7 +285,7 @@ static inline int WriteInt32(Tcl_Interp *interp, PNGImage *pngPtr,
* The format record for the PNG file format:
*/
-Tk_PhotoImageFormat tkImgFmtPNG = {
+Tk_PhotoImageFormatVersion3 tkImgFmtPNG = {
"png", /* name */
FileMatchPNG, /* fileMatchProc */
StringMatchPNG, /* stringMatchProc */
@@ -337,7 +354,7 @@ InitPNGImage(
TCL_ZLIB_COMPRESS_DEFAULT, NULL, &pngPtr->stream) != TCL_OK) {
if (interp) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "zlib initialization failed", -1));
+ "zlib initialization failed", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "ZLIB_INIT", NULL);
}
if (objPtr) {
@@ -346,6 +363,13 @@ InitPNGImage(
return TCL_ERROR;
}
+ /*
+ * Initialize physical size pHYS values
+ */
+
+ pngPtr->DPI = -1;
+ pngPtr->aspect = -1;
+
return TCL_OK;
}
@@ -431,7 +455,7 @@ ReadBase64(
Tcl_Interp *interp,
PNGImage *pngPtr,
unsigned char *destPtr,
- int destSz,
+ Tcl_Size destSz,
unsigned long *crcPtr)
{
static const unsigned char from64[] = {
@@ -521,7 +545,7 @@ ReadBase64(
if (destSz) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "unexpected end of image data", -1));
+ "unexpected end of image data", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL);
return TCL_ERROR;
}
@@ -556,7 +580,7 @@ ReadByteArray(
Tcl_Interp *interp,
PNGImage *pngPtr,
unsigned char *destPtr,
- int destSz,
+ Tcl_Size destSz,
unsigned long *crcPtr)
{
/*
@@ -565,13 +589,13 @@ ReadByteArray(
if (pngPtr->strDataLen < destSz) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "unexpected end of image data", -1));
+ "unexpected end of image data", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL);
return TCL_ERROR;
}
while (destSz) {
- int blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
+ Tcl_Size blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
memcpy(destPtr, pngPtr->strDataBuf, blockSz);
@@ -614,7 +638,7 @@ ReadData(
Tcl_Interp *interp,
PNGImage *pngPtr,
unsigned char *destPtr,
- int destSz,
+ Tcl_Size destSz,
unsigned long *crcPtr)
{
if (pngPtr->base64Data) {
@@ -624,10 +648,10 @@ ReadData(
}
while (destSz) {
- int blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
+ Tcl_Size blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
blockSz = Tcl_Read(pngPtr->channel, (char *)destPtr, blockSz);
- if (blockSz == -1) {
+ if (blockSz == TCL_IO_FAILURE) {
/* TODO: failure info... */
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"channel read failed: %s", Tcl_PosixError(interp)));
@@ -653,7 +677,7 @@ ReadData(
if (destSz && Tcl_Eof(pngPtr->channel)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "unexpected end of file", -1));
+ "unexpected end of file", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EOF", NULL);
return TCL_ERROR;
}
@@ -739,7 +763,7 @@ CheckCRC(
*/
if (calculated != chunked) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj("CRC check failed", -1));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("CRC check failed", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "CRC", NULL);
return TCL_ERROR;
}
@@ -859,7 +883,7 @@ static int
ReadChunkHeader(
Tcl_Interp *interp,
PNGImage *pngPtr,
- int *sizePtr,
+ Tcl_Size *sizePtr,
unsigned long *typePtr,
unsigned long *crcPtr)
{
@@ -934,6 +958,7 @@ ReadChunkHeader(
case CHUNK_IDAT:
case CHUNK_IEND:
case CHUNK_IHDR:
+ case CHUNK_pHYs:
case CHUNK_PLTE:
case CHUNK_tRNS:
break;
@@ -952,7 +977,6 @@ ReadChunkHeader(
case CHUNK_iTXt:
case CHUNK_oFFs:
case CHUNK_pCAL:
- case CHUNK_pHYs:
case CHUNK_sBIT:
case CHUNK_sCAL:
case CHUNK_sPLT:
@@ -1011,7 +1035,7 @@ ReadChunkHeader(
if ((pc[i] < 65) || (pc[i] > 122) ||
((pc[i] > 90) && (pc[i] < 97))) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "invalid chunk type", -1));
+ "invalid chunk type", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG",
"INVALID_CHUNK", NULL);
return TCL_ERROR;
@@ -1112,7 +1136,7 @@ CheckColor(
if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) {
unsupportedDepth:
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "bit depth is not allowed for given color type", -1));
+ "bit depth is not allowed for given color type", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_DEPTH", NULL);
return TCL_ERROR;
}
@@ -1240,7 +1264,7 @@ ReadIHDR(
{
unsigned char sigBuf[PNG_SIG_SZ];
unsigned long chunkType;
- int chunkSz;
+ Tcl_Size chunkSz;
unsigned long crc;
unsigned long width, height;
int mismatch;
@@ -1277,7 +1301,7 @@ ReadIHDR(
if (mismatch) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "data stream does not have a PNG signature", -1));
+ "data stream does not have a PNG signature", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NO_SIG", NULL);
return TCL_ERROR;
}
@@ -1295,14 +1319,14 @@ ReadIHDR(
if (chunkType != CHUNK_IHDR) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "expected IHDR chunk type", -1));
+ "expected IHDR chunk type", TCL_INDEX_NONE));
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));
+ "invalid IHDR chunk size", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IHDR", NULL);
return TCL_ERROR;
}
@@ -1446,7 +1470,7 @@ ReadPLTE(
case PNG_COLOR_GRAY:
case PNG_COLOR_GRAYALPHA:
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "PLTE chunk type forbidden for grayscale", -1));
+ "PLTE chunk type forbidden for grayscale", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PLTE_UNEXPECTED",
NULL);
return TCL_ERROR;
@@ -1463,7 +1487,7 @@ ReadPLTE(
if (!chunkSz || (chunkSz > PNG_PLTE_MAXSZ) || (chunkSz % 3)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "invalid palette chunk size", -1));
+ "invalid palette chunk size", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PLTE", NULL);
return TCL_ERROR;
}
@@ -1540,7 +1564,7 @@ ReadTRNS(
if (chunkSz > PNG_TRNS_MAXSZ) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "invalid tRNS chunk size", -1));
+ "invalid tRNS chunk size", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL);
return TCL_ERROR;
}
@@ -1571,7 +1595,7 @@ ReadTRNS(
if (chunkSz > pngPtr->paletteLen) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "size of tRNS chunk is too large for the palette", -1));
+ "size of tRNS chunk is too large for the palette", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TRNS_SIZE", NULL);
return TCL_ERROR;
}
@@ -1616,7 +1640,7 @@ ReadTRNS(
if (chunkSz != 6) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "invalid tRNS chunk size - must 6 bytes for RGB", -1));
+ "invalid tRNS chunk size - must 6 bytes for RGB", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL);
return TCL_ERROR;
}
@@ -1644,6 +1668,84 @@ ReadTRNS(
/*
*----------------------------------------------------------------------
*
+ * ReadPHYS --
+ *
+ * This function reads the PHYS (physical size) chunk data from
+ * the PNG file and populates the fields in the PNGImage
+ * structure.
+ *
+ * Results:
+ * TCL_OK, or TCL_ERROR if an I/O error occurs or the PHYS chunk is
+ * invalid.
+ *
+ * Side effects:
+ * The access position in f advances.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ReadPHYS(
+ Tcl_Interp *interp,
+ PNGImage *pngPtr,
+ int chunkSz,
+ unsigned long crc)
+{
+ unsigned long PPUx, PPUy;
+ char unitSpecifier;
+
+ /*
+ * Check chunk size equal 9 bytes
+ */
+
+ if (chunkSz != 9) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "invalid physical chunk size", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PHYS", NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Read the chunk data
+ * 4 bytes: Pixels per unit, x axis
+ * 4 bytes: Pixels per unit, y axis
+ * 1 byte: unit specifier
+ */
+
+ if (ReadInt32(interp, pngPtr, &PPUx, &crc) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (ReadInt32(interp, pngPtr, &PPUy, &crc) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (ReadData(interp, pngPtr, (unsigned char *)&unitSpecifier, 1, &crc) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if ( PPUx > 2147483647 || PPUy > 2147483647
+ || unitSpecifier > 1 ) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "invalid physical size value", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PHYS", NULL);
+ return TCL_ERROR;
+ }
+
+ if (PPUx > 0) {
+ pngPtr->aspect = ((double) PPUy) / ((double) PPUx);
+ }
+ if (1 == unitSpecifier) {
+ pngPtr->DPI = ((double) PPUx) * 0.0254;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Paeth --
*
* Utility function for applying the Paeth filter to a pixel. The Paeth
@@ -1709,9 +1811,9 @@ UnfilterLine(
PNGImage *pngPtr)
{
unsigned char *thisLine =
- Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, (int *)NULL);
+ Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, (Tcl_Size *)NULL);
unsigned char *lastLine =
- Tcl_GetByteArrayFromObj(pngPtr->lastLineObj, (int *)NULL);
+ Tcl_GetByteArrayFromObj(pngPtr->lastLineObj, (Tcl_Size *)NULL);
#define PNG_FILTER_NONE 0
#define PNG_FILTER_SUB 1
@@ -1841,7 +1943,7 @@ DecodeLine(
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, (int *)NULL);
+ unsigned char *p = Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, (Tcl_Size *)NULL);
p++;
if (UnfilterLine(interp, pngPtr) == TCL_ERROR) {
@@ -2098,7 +2200,7 @@ ReadIDAT(
*/
while (chunkSz && !Tcl_ZlibStreamEof(pngPtr->stream)) {
- int len1, len2;
+ Tcl_Size len1, len2;
/*
* Read another block of input into the zlib stream if data remains.
@@ -2115,7 +2217,7 @@ ReadIDAT(
if (Tcl_ZlibStreamEof(pngPtr->stream)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "extra data after end of zlib stream", -1));
+ "extra data after end of zlib stream", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA",
NULL);
return TCL_ERROR;
@@ -2154,7 +2256,7 @@ ReadIDAT(
}
Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, &len2);
- if (len2 == pngPtr->phaseSize) {
+ if (len2 == (Tcl_Size)pngPtr->phaseSize) {
if (pngPtr->phase > 7) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
"extra data after final scan line of final phase",
@@ -2207,7 +2309,7 @@ ReadIDAT(
if (chunkSz != 0) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "compressed data after stream finalize in PNG data", -1));
+ "compressed data after stream finalize in PNG data", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL);
return TCL_ERROR;
}
@@ -2292,11 +2394,11 @@ ParseFormat(
PNGImage *pngPtr)
{
Tcl_Obj **objv = NULL;
- int objc = 0;
+ Tcl_Size objc = 0;
static const char *const fmtOptions[] = {
"-alpha", NULL
};
- enum fmtOptions {
+ enum fmtOptionsEnum {
OPT_ALPHA
};
@@ -2333,7 +2435,7 @@ ParseFormat(
objc--;
objv++;
- switch ((enum fmtOptions) optIndex) {
+ switch ((enum fmtOptionsEnum) optIndex) {
case OPT_ALPHA:
if (Tcl_GetDoubleFromObj(interp, objv[0],
&pngPtr->alpha) == TCL_ERROR) {
@@ -2342,7 +2444,7 @@ ParseFormat(
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));
+ "-alpha value must be between 0.0 and 1.0", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_ALPHA",
NULL);
return TCL_ERROR;
@@ -2388,7 +2490,7 @@ DecodePNG(
{
unsigned long chunkType;
int result;
- int chunkSz;
+ Tcl_Size chunkSz;
unsigned long crc;
/*
@@ -2421,6 +2523,29 @@ DecodePNG(
return TCL_ERROR;
}
+ /*
+ * Physical header may be present here so try to parse it
+ */
+
+ if (CHUNK_pHYs == chunkType) {
+ /*
+ * Finish parsing the PHYS chunk.
+ */
+
+ if (ReadPHYS(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;
+ }
+ }
+
if (CHUNK_PLTE == chunkType) {
/*
* Finish parsing the PLTE chunk.
@@ -2440,7 +2565,7 @@ DecodePNG(
}
} else if (PNG_COLOR_PLTE == pngPtr->colorType) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "PLTE chunk required for indexed color", -1));
+ "PLTE chunk required for indexed color", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_PLTE", NULL);
return TCL_ERROR;
}
@@ -2472,13 +2597,36 @@ DecodePNG(
}
/*
+ * Physical header may be present here so try to parse it
+ */
+
+ if (CHUNK_pHYs == chunkType) {
+ /*
+ * Finish parsing the PHYS chunk.
+ */
+
+ if (ReadPHYS(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));
+ "at least one IDAT chunk is required", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_IDAT", NULL);
return TCL_ERROR;
}
@@ -2529,8 +2677,8 @@ DecodePNG(
pngPtr->block.pixelPtr = (unsigned char *)attemptckalloc(pngPtr->blockLen);
if (!pngPtr->block.pixelPtr) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "memory allocation failed", -1));
- Tcl_SetErrorCode(interp, "TK", "MALLOC", (char *)NULL);
+ "memory allocation failed", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL);
return TCL_ERROR;
}
@@ -2581,7 +2729,7 @@ DecodePNG(
if (!Tcl_ZlibStreamEof(pngPtr->stream)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "unfinalized data stream in PNG data", -1));
+ "unfinalized data stream in PNG data", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL);
return TCL_ERROR;
}
@@ -2607,7 +2755,7 @@ DecodePNG(
if (chunkSz) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "IEND chunk contents must be empty", -1));
+ "IEND chunk contents must be empty", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL);
return TCL_ERROR;
}
@@ -2628,7 +2776,7 @@ DecodePNG(
#if 0
if (ReadData(interp, pngPtr, &c, 1, NULL) != TCL_ERROR) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "extra data following IEND chunk", -1));
+ "extra data following IEND chunk", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL);
return TCL_ERROR;
}
@@ -2672,12 +2820,15 @@ DecodePNG(
static int
FileMatchPNG(
- Tcl_Channel chan,
- const char *fileName,
- Tcl_Obj *fmtObj,
- int *widthPtr,
- int *heightPtr,
- Tcl_Interp *interp)
+ Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
+ Tcl_Channel chan, /* The image file, open for reading. */
+ TCL_UNUSED(const char *), /* The name of the image file. */
+ TCL_UNUSED(Tcl_Obj *), /* User-specified format object, or NULL. */
+ TCL_UNUSED(Tcl_Obj *), /* metadata input, may be NULL */
+ int *widthPtr, int *heightPtr,
+ /* The dimensions of the image are returned
+ * here if the file is a valid raw GIF file. */
+ TCL_UNUSED(Tcl_Obj *)) /* metadata return dict, may be NULL */
{
PNGImage png;
int match = 0;
@@ -2718,15 +2869,17 @@ static int
FileReadPNG(
Tcl_Interp* interp, /* Interpreter to use for reporting errors. */
Tcl_Channel chan, /* The image file, open for reading. */
- const char* fileName, /* The name of the image file. */
+ TCL_UNUSED(const char*), /* The name of the image file. */
Tcl_Obj *fmtObj, /* User-specified format object, or NULL. */
+ TCL_UNUSED(Tcl_Obj*), /* metadata input, may be NULL */
Tk_PhotoHandle imageHandle, /* The photo image to write into. */
int destX, int destY, /* Coordinates of top-left pixel in photo
* image to be written to. */
int width, int height, /* Dimensions of block of photo image to be
* written to. */
- int srcX, int srcY) /* Coordinates of top-left pixel to be used in
+ int srcX, int srcY, /* Coordinates of top-left pixel to be used in
* image being read. */
+ Tcl_Obj* metadataOutObj) /* metadata return dict, may be NULL */
{
PNGImage png;
int result = TCL_ERROR;
@@ -2737,6 +2890,18 @@ FileReadPNG(
result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY, width, height, srcX, srcY);
}
+ if (TCL_OK == result && metadataOutObj != NULL && png.DPI != -1) {
+ result = Tcl_DictObjPut(NULL, metadataOutObj,
+ Tcl_NewStringObj("DPI",-1),
+ Tcl_NewDoubleObj(png.DPI));
+ }
+
+ if (TCL_OK == result && metadataOutObj != NULL && png.aspect != -1) {
+ result = Tcl_DictObjPut(NULL, metadataOutObj,
+ Tcl_NewStringObj("aspect",-1),
+ Tcl_NewDoubleObj(png.aspect));
+ }
+
CleanupPNGImage(&png);
return result;
}
@@ -2761,11 +2926,13 @@ FileReadPNG(
static int
StringMatchPNG(
- Tcl_Obj *pObjData,
- Tcl_Obj *fmtObj,
- int *widthPtr,
- int *heightPtr,
- Tcl_Interp *interp)
+ Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
+ Tcl_Obj *pObjData, /* the object containing the image data */
+ TCL_UNUSED(Tcl_Obj *), /* the image format object, or NULL */
+ TCL_UNUSED(Tcl_Obj *), /* metadata input, may be NULL */
+ int *widthPtr, /* where to put the string width */
+ int *heightPtr, /* where to put the string height */
+ TCL_UNUSED(Tcl_Obj *)) /* metadata return dict, may be NULL */
{
PNGImage png;
int match = 0;
@@ -2807,13 +2974,15 @@ StringReadPNG(
Tcl_Interp* interp, /* Interpreter to use for reporting errors. */
Tcl_Obj *pObjData,
Tcl_Obj *fmtObj, /* User-specified format object, or NULL. */
+ TCL_UNUSED(Tcl_Obj*), /* metadata input, may be NULL */
Tk_PhotoHandle imageHandle, /* The photo image to write into. */
int destX, int destY, /* Coordinates of top-left pixel in photo
* image to be written to. */
int width, int height, /* Dimensions of block of photo image to be
* written to. */
- int srcX, int srcY) /* Coordinates of top-left pixel to be used in
+ int srcX, int srcY, /* Coordinates of top-left pixel to be used in
* image being read. */
+ Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */
{
PNGImage png;
int result = TCL_ERROR;
@@ -2825,6 +2994,18 @@ StringReadPNG(
result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY, width, height, srcX, srcY);
}
+ if (TCL_OK == result && metadataOutObj != NULL && png.DPI != -1) {
+ result = Tcl_DictObjPut(NULL, metadataOutObj,
+ Tcl_NewStringObj("DPI",-1),
+ Tcl_NewDoubleObj(png.DPI));
+ }
+
+ if (TCL_OK == result && metadataOutObj != NULL && png.aspect != -1) {
+ result = Tcl_DictObjPut(NULL, metadataOutObj,
+ Tcl_NewStringObj("aspect",-1),
+ Tcl_NewDoubleObj(png.aspect));
+ }
+
CleanupPNGImage(&png);
return result;
}
@@ -2850,7 +3031,7 @@ WriteData(
Tcl_Interp *interp,
PNGImage *pngPtr,
const unsigned char *srcPtr,
- int srcSz,
+ Tcl_Size srcSz,
unsigned long *crcPtr)
{
if (!srcPtr || srcSz <= 0) {
@@ -2867,14 +3048,14 @@ WriteData(
*/
if (pngPtr->objDataPtr) {
- int objSz;
+ Tcl_Size objSz;
unsigned char *destPtr;
Tcl_GetByteArrayFromObj(pngPtr->objDataPtr, &objSz);
- if (objSz > INT_MAX - srcSz) {
+ if (objSz + srcSz > INT_MAX) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "image too large to store completely in byte array", -1));
+ "image too large to store completely in byte array", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL);
return TCL_ERROR;
}
@@ -2883,13 +3064,13 @@ WriteData(
if (!destPtr) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "memory allocation failed", -1));
+ "memory allocation failed", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL);
return TCL_ERROR;
}
memcpy(destPtr+objSz, srcPtr, srcSz);
- } else if (Tcl_Write(pngPtr->channel, (const char *) srcPtr, srcSz) == -1) {
+ } else if (Tcl_Write(pngPtr->channel, (const char *) srcPtr, srcSz) == TCL_IO_FAILURE) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write to channel failed: %s", Tcl_PosixError(interp)));
return TCL_ERROR;
@@ -2911,6 +3092,34 @@ WriteByte(
/*
*----------------------------------------------------------------------
*
+ * LongToInt32 --
+ *
+ * This function transforms to a 32-bit integer value as
+ * four bytes in network byte order.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Buffer will be modified.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline void
+LongToInt32(
+ unsigned long l,
+ unsigned char *pc)
+{
+ 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);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* WriteInt32 --
*
* This function writes a 32-bit integer value out to the PNG image as
@@ -2933,12 +3142,7 @@ WriteInt32(
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);
-
+ LongToInt32(l,pc);
return WriteData(interp, pngPtr, pc, 4, crcPtr);
}
@@ -2965,7 +3169,7 @@ WriteChunk(
PNGImage *pngPtr,
unsigned long chunkType,
const unsigned char *dataPtr,
- int dataSize)
+ Tcl_Size dataSize)
{
unsigned long crc = Tcl_ZlibCRC32(0, NULL, 0);
int result = TCL_OK;
@@ -3139,7 +3343,7 @@ WriteIDAT(
int rowNum, flush = TCL_ZLIB_NO_FLUSH, result;
Tcl_Obj *outputObj;
unsigned char *outputBytes;
- int outputSize;
+ Tcl_Size outputSize;
/*
* Filter and compress each row one at a time.
@@ -3208,7 +3412,7 @@ WriteIDAT(
if (Tcl_ZlibStreamPut(pngPtr->stream, pngPtr->thisLineObj,
flush) != TCL_OK) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "deflate() returned error", -1));
+ "deflate() returned error", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DEFLATE", NULL);
return TCL_ERROR;
}
@@ -3230,7 +3434,7 @@ WriteIDAT(
*/
outputObj = Tcl_NewObj();
- (void) Tcl_ZlibStreamGet(pngPtr->stream, outputObj, -1);
+ (void) Tcl_ZlibStreamGet(pngPtr->stream, outputObj, TCL_INDEX_NONE);
outputBytes = Tcl_GetByteArrayFromObj(outputObj, &outputSize);
result = WriteChunk(interp, pngPtr, CHUNK_IDAT, outputBytes, outputSize);
Tcl_DecrRefCount(outputObj);
@@ -3257,7 +3461,8 @@ WriteIDAT(
static int
WriteExtraChunks(
Tcl_Interp *interp,
- PNGImage *pngPtr)
+ PNGImage *pngPtr,
+ Tcl_Obj *metadataInObj)
{
static const unsigned char sBIT_contents[] = {
8, 8, 8, 8
@@ -3299,8 +3504,8 @@ WriteExtraChunks(
Tcl_DStringInit(&buf);
Tcl_DStringAppend(&buf, "Software", 9);
- Tcl_DStringAppend(&buf, "Tk Toolkit v", -1);
- Tcl_DStringAppend(&buf, TK_PATCH_LEVEL, -1);
+ Tcl_DStringAppend(&buf, "Tk Toolkit v", TCL_INDEX_NONE);
+ Tcl_DStringAppend(&buf, TK_PATCH_LEVEL, TCL_INDEX_NONE);
if (WriteChunk(interp, pngPtr, CHUNK_tEXt,
(unsigned char *) Tcl_DStringValue(&buf),
Tcl_DStringLength(&buf)) != TCL_OK) {
@@ -3309,6 +3514,79 @@ WriteExtraChunks(
}
Tcl_DStringFree(&buf);
+ /*
+ * Add a pHYs chunk if there is metadata for DPI and/or aspect
+ * aspect = PPUy / PPUx
+ * DPI = PPUx * 0.0254
+ * The physical chunk consists of:
+ * - Points per meter in x direction (32 bit)
+ * - Points per meter in x direction (32 bit)
+ * - Unit specifier: 0: no unit (only aspect), 1: Points per meter
+ */
+
+ if (metadataInObj != NULL) {
+
+ Tcl_Obj *aspectObj, *DPIObj;
+ double aspectValue=-1, DPIValue=-1;
+ unsigned long PPUx = 65536, PPUy = 65536;
+ char unitSpecifier;
+
+ if (TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj,
+ Tcl_NewStringObj("aspect",-1),
+ &aspectObj) ||
+ TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj,
+ Tcl_NewStringObj("DPI",-1),
+ &DPIObj) ) {
+ return TCL_ERROR;
+ }
+ if (DPIObj != NULL) {
+ if (TCL_ERROR == Tcl_GetDoubleFromObj(interp, DPIObj, &DPIValue))
+ {
+ return TCL_ERROR;
+ }
+ PPUx = (unsigned long)floor(DPIValue / 0.0254+0.5);
+ if (aspectObj == NULL) {
+ PPUy = PPUx;
+ }
+ unitSpecifier = 1;
+ }
+ if (aspectObj != NULL) {
+ if (TCL_ERROR == Tcl_GetDoubleFromObj(interp, aspectObj,
+ &aspectValue)) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * aspect = PPUy / PPUx
+ */
+
+ if (DPIObj == NULL) {
+ unitSpecifier = 0;
+ PPUx = 65536;
+ PPUy = (unsigned long)floor(65536.0 * aspectValue+0.5);
+ } else {
+ PPUy = (unsigned long)floor(DPIValue * aspectValue / 0.0254+0.5);
+ }
+ }
+ if (DPIObj != NULL || aspectObj != NULL) {
+ unsigned char buffer[9];
+
+ if ( PPUx > 2147483647 || PPUy > 2147483647 ) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "DPI or aspect out of range", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PHYS", NULL);
+ return TCL_ERROR;
+ }
+
+ LongToInt32(PPUx, buffer);
+ LongToInt32(PPUy, buffer+4);
+ buffer[8] = unitSpecifier;
+ if (WriteChunk(interp, pngPtr, CHUNK_pHYs, buffer, 9)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
return TCL_OK;
}
@@ -3334,7 +3612,8 @@ static int
EncodePNG(
Tcl_Interp *interp,
Tk_PhotoImageBlock *blockPtr,
- PNGImage *pngPtr)
+ PNGImage *pngPtr,
+ Tcl_Obj *metadataInObj)
{
int greenOffset, blueOffset, alphaOffset;
@@ -3384,7 +3663,7 @@ EncodePNG(
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));
+ "image is too large to encode pixel data", TCL_INDEX_NONE));
Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL);
return TCL_ERROR;
}
@@ -3417,7 +3696,7 @@ EncodePNG(
* other programs more than us.
*/
- if (WriteExtraChunks(interp, pngPtr) == TCL_ERROR) {
+ if (WriteExtraChunks(interp, pngPtr, metadataInObj) == TCL_ERROR) {
return TCL_ERROR;
}
@@ -3458,7 +3737,8 @@ static int
FileWritePNG(
Tcl_Interp *interp,
const char *filename,
- Tcl_Obj *fmtObj,
+ TCL_UNUSED(Tcl_Obj *),
+ Tcl_Obj *metadataInObj,
Tk_PhotoImageBlock *blockPtr)
{
Tcl_Channel chan;
@@ -3494,7 +3774,7 @@ FileWritePNG(
* Write the raw PNG data out to the file.
*/
- result = EncodePNG(interp, blockPtr, &png);
+ result = EncodePNG(interp, blockPtr, &png, metadataInObj);
cleanup:
Tcl_Close(interp, chan);
@@ -3523,7 +3803,8 @@ FileWritePNG(
static int
StringWritePNG(
Tcl_Interp *interp,
- Tcl_Obj *fmtObj,
+ TCL_UNUSED(Tcl_Obj *),
+ Tcl_Obj *metadataInObj,
Tk_PhotoImageBlock *blockPtr)
{
Tcl_Obj *resultObj = Tcl_NewObj();
@@ -3544,7 +3825,7 @@ StringWritePNG(
* back to the interpreter if successful.
*/
- result = EncodePNG(interp, blockPtr, &png);
+ result = EncodePNG(interp, blockPtr, &png, metadataInObj);
if (TCL_OK == result) {
Tcl_SetObjResult(interp, png.objDataPtr);