summaryrefslogtreecommitdiffstats
path: root/generic/tkImgPNG.c
diff options
context:
space:
mode:
authoroehhar <harald.oehlmann@elmicron.de>2020-06-22 21:20:04 (GMT)
committeroehhar <harald.oehlmann@elmicron.de>2020-06-22 21:20:04 (GMT)
commitb39239f7ef80426cf1583b625f0ab22f94666c2a (patch)
tree08cf6df8e0c65269b34203d44f06b5faf911dd73 /generic/tkImgPNG.c
parent3f0bcf5127f3f554341d43576f656162f4cf5101 (diff)
downloadtk-b39239f7ef80426cf1583b625f0ab22f94666c2a.zip
tk-b39239f7ef80426cf1583b625f0ab22f94666c2a.tar.gz
tk-b39239f7ef80426cf1583b625f0ab22f94666c2a.tar.bz2
TIP529 image metadata: read png DPI and aspect metadata
Diffstat (limited to 'generic/tkImgPNG.c')
-rw-r--r--generic/tkImgPNG.c166
1 files changed, 165 insertions, 1 deletions
diff --git a/generic/tkImgPNG.c b/generic/tkImgPNG.c
index 669bc27..af0684e 100644
--- a/generic/tkImgPNG.c
+++ b/generic/tkImgPNG.c
@@ -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;
/*
@@ -355,6 +364,13 @@ InitPNGImage(
}
return TCL_ERROR;
}
+
+ /*
+ * Initialize physical size pHYS values
+ */
+
+ pngPtr->DPI = -1;
+ pngPtr->aspect = -1;
return TCL_OK;
}
@@ -944,6 +960,7 @@ ReadChunkHeader(
case CHUNK_IDAT:
case CHUNK_IEND:
case CHUNK_IHDR:
+ case CHUNK_pHYs:
case CHUNK_PLTE:
case CHUNK_tRNS:
break;
@@ -962,7 +979,6 @@ ReadChunkHeader(
case CHUNK_iTXt:
case CHUNK_oFFs:
case CHUNK_pCAL:
- case CHUNK_pHYs:
case CHUNK_sBIT:
case CHUNK_sCAL:
case CHUNK_sPLT:
@@ -1654,6 +1670,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", -1));
+ 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, &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", -1));
+ 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
@@ -2426,6 +2520,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.
@@ -2477,6 +2594,29 @@ 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.
*/
@@ -2758,6 +2898,18 @@ FileReadPNG(
result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY);
}
+ 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;
}
@@ -2854,6 +3006,18 @@ StringReadPNG(
result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY);
}
+ 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;
}