1 /*
2  * tkImgPNG.c --
3  *
4  *	A Tk photo image file handler for PNG files.
5  *
6  * Copyright © 2006-2008 Muonics, Inc.
7  * Copyright © 2008 Donal K. Fellows
8  *
9  * See the file "license.terms" for information on usage and redistribution of
10  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  */
12 
13 #include "tkInt.h"
14 
15 #define	PNG_INT32(a,b,c,d)	\
16 	(((long)(a) << 24) | ((long)(b) << 16) | ((long)(c) << 8) | (long)(d))
17 #define	PNG_BLOCK_SZ	1024		/* Process up to 1k at a time. */
18 #define PNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
19 
20 /*
21  * Every PNG image starts with the following 8-byte signature.
22  */
23 
24 #define PNG_SIG_SZ	8
25 static const unsigned char pngSignature[] = {
26     137, 80, 78, 71, 13, 10, 26, 10
27 };
28 
29 static const int startLine[8] = {
30     0, 0, 0, 4, 0, 2, 0, 1
31 };
32 
33 /*
34  * Chunk type flags.
35  */
36 
37 #define PNG_CF_ANCILLARY 0x20000000L	/* Non-critical chunk (can ignore). */
38 #define PNG_CF_PRIVATE   0x00100000L	/* Application-specific chunk. */
39 #define PNG_CF_RESERVED  0x00001000L	/* Not used. */
40 #define PNG_CF_COPYSAFE  0x00000010L	/* Opaque data safe for copying. */
41 
42 /*
43  * Chunk types, not all of which have support implemented. Note that there are
44  * others in the official extension set which we will never support (as they
45  * are officially deprecated).
46  */
47 
48 #define CHUNK_IDAT	PNG_INT32('I','D','A','T')	/* Pixel data. */
49 #define CHUNK_IEND	PNG_INT32('I','E','N','D')	/* End of Image. */
50 #define CHUNK_IHDR	PNG_INT32('I','H','D','R')	/* Header. */
51 #define CHUNK_PLTE	PNG_INT32('P','L','T','E')	/* Palette. */
52 
53 #define CHUNK_bKGD	PNG_INT32('b','K','G','D')	/* Background Color */
54 #define CHUNK_cHRM	PNG_INT32('c','H','R','M')	/* Chroma values. */
55 #define CHUNK_gAMA	PNG_INT32('g','A','M','A')	/* Gamma. */
56 #define CHUNK_hIST	PNG_INT32('h','I','S','T')	/* Histogram. */
57 #define CHUNK_iCCP	PNG_INT32('i','C','C','P')	/* Color profile. */
58 #define CHUNK_iTXt	PNG_INT32('i','T','X','t')	/* Internationalized
59 							 * text (comments,
60 							 * etc.) */
61 #define CHUNK_oFFs	PNG_INT32('o','F','F','s')	/* Image offset. */
62 #define CHUNK_pCAL	PNG_INT32('p','C','A','L')	/* Pixel calibration
63 							 * data. */
64 #define CHUNK_pHYs	PNG_INT32('p','H','Y','s')	/* Physical pixel
65 							 * dimensions. */
66 #define CHUNK_sBIT	PNG_INT32('s','B','I','T')	/* Significant bits */
67 #define CHUNK_sCAL	PNG_INT32('s','C','A','L')	/* Physical scale. */
68 #define CHUNK_sPLT	PNG_INT32('s','P','L','T')	/* Suggested
69 							 * palette. */
70 #define CHUNK_sRGB	PNG_INT32('s','R','G','B')	/* Standard RGB space
71 							 * declaration. */
72 #define CHUNK_tEXt	PNG_INT32('t','E','X','t')	/* Plain Latin-1
73 							 * text. */
74 #define CHUNK_tIME	PNG_INT32('t','I','M','E')	/* Time stamp. */
75 #define CHUNK_tRNS	PNG_INT32('t','R','N','S')	/* Transparency. */
76 #define CHUNK_zTXt	PNG_INT32('z','T','X','t')	/* Compressed Latin-1
77 							 * text. */
78 
79 /*
80  * Color flags.
81  */
82 
83 #define PNG_COLOR_INDEXED	1
84 #define PNG_COLOR_USED		2
85 #define PNG_COLOR_ALPHA		4
86 
87 /*
88  * Actual color types.
89  */
90 
91 #define PNG_COLOR_GRAY		0
92 #define PNG_COLOR_RGB		(PNG_COLOR_USED)
93 #define PNG_COLOR_PLTE		(PNG_COLOR_USED | PNG_COLOR_INDEXED)
94 #define PNG_COLOR_GRAYALPHA	(PNG_COLOR_GRAY | PNG_COLOR_ALPHA)
95 #define PNG_COLOR_RGBA		(PNG_COLOR_USED | PNG_COLOR_ALPHA)
96 
97 /*
98  * Compression Methods.
99  */
100 
101 #define PNG_COMPRESS_DEFLATE	0
102 
103 /*
104  * Filter Methods.
105  */
106 
107 #define PNG_FILTMETH_STANDARD	0
108 
109 /*
110  * Interlacing Methods.
111  */
112 
113 #define	PNG_INTERLACE_NONE	0
114 #define PNG_INTERLACE_ADAM7	1
115 
116 /*
117  * State information, used to store everything about the PNG image being
118  * currently parsed or created.
119  */
120 
121 typedef struct {
122     /*
123      * PNG data source/destination channel/object/byte array.
124      */
125 
126     Tcl_Channel channel;	/* Channel for from-file reads. */
127     Tcl_Obj *objDataPtr;
128     unsigned char *strDataBuf;	/* Raw source data for from-string reads. */
129     TkSizeT strDataLen;		/* Length of source data. */
130     unsigned char *base64Data;	/* base64 encoded string data. */
131     unsigned char base64Bits;	/* Remaining bits from last base64 read. */
132     unsigned char base64State;	/* Current state of base64 decoder. */
133     double alpha;		/* Alpha from -format option. */
134 
135     /*
136      * Image header information.
137      */
138 
139     unsigned char bitDepth;	/* Number of bits per pixel. */
140     unsigned char colorType;	/* Grayscale, TrueColor, etc. */
141     unsigned char compression;	/* Compression Mode (always zlib). */
142     unsigned char filter;	/* Filter mode (0 - 3). */
143     unsigned char interlace;	/* Type of interlacing (if any). */
144     unsigned char numChannels;	/* Number of channels per pixel. */
145     unsigned char bytesPerPixel;/* Bytes per pixel in scan line. */
146     int bitScale;		/* Scale factor for RGB/Gray depths < 8. */
147     int currentLine;		/* Current line being unfiltered. */
148     unsigned char phase;	/* Interlacing phase (0..6). */
149     Tk_PhotoImageBlock block;
150     int blockLen;		/* Number of bytes in Tk image pixels. */
151 
152     /*
153      * For containing data read from PLTE (palette) and tRNS (transparency)
154      * chunks.
155      */
156 
157     int paletteLen;		/* Number of PLTE entries (1..256). */
158     int useTRNS;		/* Flag to indicate whether there was a
159 				 * palette given. */
160     struct {
161 	unsigned char red;
162 	unsigned char green;
163 	unsigned char blue;
164 	unsigned char alpha;
165     } palette[256];		/* Palette RGB/Transparency table. */
166     unsigned char transVal[6];	/* Fully-transparent RGB/Gray Value. */
167 
168     /*
169      * For compressing and decompressing IDAT chunks.
170      */
171 
172     Tcl_ZlibStream stream;	/* Inflating or deflating stream; this one is
173 				 * not bound to a Tcl command. */
174     Tcl_Obj *lastLineObj;	/* Last line of pixels, for unfiltering. */
175     Tcl_Obj *thisLineObj;	/* Current line of pixels to process. */
176     int lineSize;		/* Number of bytes in a PNG line. */
177     int phaseSize;		/* Number of bytes/line in current phase. */
178 
179 
180     /*
181      * Physical size: pHYS chunks.
182      */
183 
184     double DPI;
185     double aspect;
186 
187 } PNGImage;
188 
189 /*
190  * Maximum size of various chunks.
191  */
192 
193 #define	PNG_PLTE_MAXSZ 768	/* 3 bytes/RGB entry, 256 entries max */
194 #define	PNG_TRNS_MAXSZ 256	/* 1-byte alpha, 256 entries max */
195 
196 /*
197  * Forward declarations of non-global functions defined in this file:
198  */
199 
200 static void		ApplyAlpha(PNGImage *pngPtr);
201 static int		CheckColor(Tcl_Interp *interp, PNGImage *pngPtr);
202 static inline int	CheckCRC(Tcl_Interp *interp, PNGImage *pngPtr,
203 			    unsigned long calculated);
204 static void		CleanupPNGImage(PNGImage *pngPtr);
205 static int		DecodeLine(Tcl_Interp *interp, PNGImage *pngPtr);
206 static int		DecodePNG(Tcl_Interp *interp, PNGImage *pngPtr,
207 			    Tcl_Obj *fmtObj, Tk_PhotoHandle imageHandle,
208 			    int destX, int destY);
209 static int		EncodePNG(Tcl_Interp *interp,
210 			    Tk_PhotoImageBlock *blockPtr, PNGImage *pngPtr,
211 			    Tcl_Obj *metadataInObj);
212 static int		FileMatchPNG(Tcl_Interp *interp, Tcl_Channel chan,
213 			    const char *fileName, Tcl_Obj *fmtObj,
214 			    Tcl_Obj *metadataInObj, int *widthPtr,
215 			    int *heightPtr, Tcl_Obj *metadataOut);
216 static int		FileReadPNG(Tcl_Interp *interp, Tcl_Channel chan,
217 			    const char *fileName, Tcl_Obj *fmtObj,
218 			    Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle,
219 			    int destX, int destY, int width, int height,
220 			    int srcX, int srcY, Tcl_Obj *metadataOutPtr);
221 static int		FileWritePNG(Tcl_Interp *interp, const char *filename,
222 			    Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
223 			    Tk_PhotoImageBlock *blockPtr);
224 static int		InitPNGImage(Tcl_Interp *interp, PNGImage *pngPtr,
225 			    Tcl_Channel chan, Tcl_Obj *objPtr, int dir);
226 static inline unsigned char Paeth(int a, int b, int c);
227 static int		ParseFormat(Tcl_Interp *interp, Tcl_Obj *fmtObj,
228 			    PNGImage *pngPtr);
229 static int		ReadBase64(Tcl_Interp *interp, PNGImage *pngPtr,
230 			    unsigned char *destPtr, size_t destSz,
231 			    unsigned long *crcPtr);
232 static int		ReadByteArray(Tcl_Interp *interp, PNGImage *pngPtr,
233 			    unsigned char *destPtr, size_t destSz,
234 			    unsigned long *crcPtr);
235 static int		ReadData(Tcl_Interp *interp, PNGImage *pngPtr,
236 			    unsigned char *destPtr, size_t destSz,
237 			    unsigned long *crcPtr);
238 static int		ReadChunkHeader(Tcl_Interp *interp, PNGImage *pngPtr,
239 			    size_t *sizePtr, unsigned long *typePtr,
240 			    unsigned long *crcPtr);
241 static int		ReadIDAT(Tcl_Interp *interp, PNGImage *pngPtr,
242 			    int chunkSz, unsigned long crc);
243 static int		ReadIHDR(Tcl_Interp *interp, PNGImage *pngPtr);
244 static inline int	ReadInt32(Tcl_Interp *interp, PNGImage *pngPtr,
245 			    unsigned long *resultPtr, unsigned long *crcPtr);
246 static int		ReadPLTE(Tcl_Interp *interp, PNGImage *pngPtr,
247 			    int chunkSz, unsigned long crc);
248 static int		ReadTRNS(Tcl_Interp *interp, PNGImage *pngPtr,
249 			    int chunkSz, unsigned long crc);
250 static int		SkipChunk(Tcl_Interp *interp, PNGImage *pngPtr,
251 			    int chunkSz, unsigned long crc);
252 static int		StringMatchPNG(Tcl_Interp *interp, Tcl_Obj *pObjData,
253 			    Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
254 			    int *widthPtr, int *heightPtr,
255 			    Tcl_Obj *metadataOutObj);
256 static int		StringReadPNG(Tcl_Interp *interp, Tcl_Obj *pObjData,
257 			    Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj,
258 			    Tk_PhotoHandle imageHandle,
259 			    int destX, int destY, int width, int height,
260 			    int srcX, int srcY, Tcl_Obj *metadataOutObj);
261 
262 static int		StringWritePNG(Tcl_Interp *interp, Tcl_Obj *fmtObj,
263 			    Tcl_Obj *metadataInObj,
264 			    Tk_PhotoImageBlock *blockPtr);
265 static int		UnfilterLine(Tcl_Interp *interp, PNGImage *pngPtr);
266 static inline int	WriteByte(Tcl_Interp *interp, PNGImage *pngPtr,
267 			    unsigned char c, unsigned long *crcPtr);
268 static inline int	WriteChunk(Tcl_Interp *interp, PNGImage *pngPtr,
269 			    unsigned long chunkType,
270 			    const unsigned char *dataPtr, size_t dataSize);
271 static int		WriteData(Tcl_Interp *interp, PNGImage *pngPtr,
272 			    const unsigned char *srcPtr, size_t srcSz,
273 			    unsigned long *crcPtr);
274 static int		WriteExtraChunks(Tcl_Interp *interp,
275 			    PNGImage *pngPtr, Tcl_Obj *metadataInObj);
276 static int		WriteIHDR(Tcl_Interp *interp, PNGImage *pngPtr,
277 			    Tk_PhotoImageBlock *blockPtr);
278 static int		WriteIDAT(Tcl_Interp *interp, PNGImage *pngPtr,
279 			    Tk_PhotoImageBlock *blockPtr);
280 static inline int	WriteInt32(Tcl_Interp *interp, PNGImage *pngPtr,
281 			    unsigned long l, unsigned long *crcPtr);
282 
283 /*
284  * The format record for the PNG file format:
285  */
286 
287 Tk_PhotoImageFormatVersion3 tkImgFmtPNG = {
288     "png",			/* name */
289     FileMatchPNG,		/* fileMatchProc */
290     StringMatchPNG,		/* stringMatchProc */
291     FileReadPNG,		/* fileReadProc */
292     StringReadPNG,		/* stringReadProc */
293     FileWritePNG,		/* fileWriteProc */
294     StringWritePNG,		/* stringWriteProc */
295     NULL
296 };
297 
298 /*
299  *----------------------------------------------------------------------
300  *
301  * InitPNGImage --
302  *
303  *	This function is invoked by each of the Tk image handler procs
304  *	(MatchStringProc, etc.) to initialize state information used during
305  *	the course of encoding or decoding a PNG image.
306  *
307  * Results:
308  *	TCL_OK, or TCL_ERROR if initialization failed.
309  *
310  * Side effects:
311  *	The reference count of the -data Tcl_Obj*, if any, is incremented.
312  *
313  *----------------------------------------------------------------------
314  */
315 
316 static int
InitPNGImage(Tcl_Interp * interp,PNGImage * pngPtr,Tcl_Channel chan,Tcl_Obj * objPtr,int dir)317 InitPNGImage(
318     Tcl_Interp *interp,
319     PNGImage *pngPtr,
320     Tcl_Channel chan,
321     Tcl_Obj *objPtr,
322     int dir)
323 {
324     memset(pngPtr, 0, sizeof(PNGImage));
325 
326     pngPtr->channel = chan;
327     pngPtr->alpha = 1.0;
328 
329     /*
330      * If decoding from a -data string object, increment its reference count
331      * for the duration of the decode and get its length and byte array for
332      * reading with ReadData().
333      */
334 
335     if (objPtr) {
336 	Tcl_IncrRefCount(objPtr);
337 	pngPtr->objDataPtr = objPtr;
338 	pngPtr->strDataBuf =
339 		Tcl_GetByteArrayFromObj(objPtr, &pngPtr->strDataLen);
340     }
341 
342     /*
343      * Initialize the palette transparency table to fully opaque.
344      */
345 
346     memset(pngPtr->palette, 255, sizeof(pngPtr->palette));
347 
348     /*
349      * Initialize Zlib inflate/deflate stream.
350      */
351 
352     if (Tcl_ZlibStreamInit(NULL, dir, TCL_ZLIB_FORMAT_ZLIB,
353 	    TCL_ZLIB_COMPRESS_DEFAULT, NULL, &pngPtr->stream) != TCL_OK) {
354 	if (interp) {
355 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
356 		    "zlib initialization failed", -1));
357 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "ZLIB_INIT", NULL);
358 	}
359 	if (objPtr) {
360 	    Tcl_DecrRefCount(objPtr);
361 	}
362 	return TCL_ERROR;
363     }
364 
365     /*
366      * Initialize physical size pHYS values
367      */
368 
369     pngPtr->DPI = -1;
370     pngPtr->aspect = -1;
371 
372     return TCL_OK;
373 }
374 
375 /*
376  *----------------------------------------------------------------------
377  *
378  * CleanupPNGImage --
379  *
380  *	This function is invoked by each of the Tk image handler procs
381  *	(MatchStringProc, etc.) prior to returning to Tcl in order to clean up
382  *	any allocated memory and call other cleanup handlers such as zlib's
383  *	inflateEnd/deflateEnd.
384  *
385  * Results:
386  *	None.
387  *
388  * Side effects:
389  *	The reference count of the -data Tcl_Obj*, if any, is decremented.
390  *	Buffers are freed, streams are closed. The PNGImage should not be used
391  *	for any purpose without being reinitialized post-cleanup.
392  *
393  *----------------------------------------------------------------------
394  */
395 
396 static void
CleanupPNGImage(PNGImage * pngPtr)397 CleanupPNGImage(
398     PNGImage *pngPtr)
399 {
400     /*
401      * Don't need the object containing the -data value anymore.
402      */
403 
404     if (pngPtr->objDataPtr) {
405 	Tcl_DecrRefCount(pngPtr->objDataPtr);
406     }
407 
408     /*
409      * Discard pixel buffer.
410      */
411 
412     if (pngPtr->stream) {
413 	Tcl_ZlibStreamClose(pngPtr->stream);
414     }
415 
416     if (pngPtr->block.pixelPtr) {
417 	ckfree(pngPtr->block.pixelPtr);
418     }
419     if (pngPtr->thisLineObj) {
420 	Tcl_DecrRefCount(pngPtr->thisLineObj);
421     }
422     if (pngPtr->lastLineObj) {
423 	Tcl_DecrRefCount(pngPtr->lastLineObj);
424     }
425 
426     memset(pngPtr, 0, sizeof(PNGImage));
427 }
428 
429 /*
430  *----------------------------------------------------------------------
431  *
432  * ReadBase64 --
433  *
434  *	This function is invoked to read the specified number of bytes from
435  *	base-64 encoded image data.
436  *
437  *	Note: It would be better if the Tk_PhotoImage stuff handled this by
438  *	creating a channel from the -data value, which would take care of
439  *	base64 decoding and made the data readable as if it were coming from a
440  *	file.
441  *
442  * Results:
443  *	TCL_OK, or TCL_ERROR if an I/O error occurs.
444  *
445  * Side effects:
446  *	The file position will change. The running CRC is updated if a pointer
447  *	to it is provided.
448  *
449  *----------------------------------------------------------------------
450  */
451 
452 static int
ReadBase64(Tcl_Interp * interp,PNGImage * pngPtr,unsigned char * destPtr,size_t destSz,unsigned long * crcPtr)453 ReadBase64(
454     Tcl_Interp *interp,
455     PNGImage *pngPtr,
456     unsigned char *destPtr,
457     size_t destSz,
458     unsigned long *crcPtr)
459 {
460     static const unsigned char from64[] = {
461 	0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80, 0x80,
462 	0x83, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
463 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80,
464 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x3e,
465 	0x83, 0x83, 0x83, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
466 	0x3b, 0x3c, 0x3d, 0x83, 0x83, 0x83, 0x81, 0x83, 0x83, 0x83, 0x00,
467 	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
468 	0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
469 	0x17, 0x18, 0x19, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x1a, 0x1b,
470 	0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
471 	0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
472 	0x32, 0x33, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
473 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
474 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
475 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
476 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
477 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
478 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
479 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
480 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
481 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
482 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
483 	0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
484 	0x83, 0x83
485     };
486 
487     /*
488      * Definitions for the base-64 decoder.
489      */
490 
491 #define PNG64_SPECIAL	0x80	/* Flag bit */
492 #define PNG64_SPACE	0x80	/* Whitespace */
493 #define PNG64_PAD	0x81	/* Padding */
494 #define PNG64_DONE	0x82	/* End of data */
495 #define PNG64_BAD	0x83	/* Ooooh, naughty! */
496 
497     while (destSz && pngPtr->strDataLen) {
498 	unsigned char c = 0;
499 	unsigned char c64 = from64[*pngPtr->strDataBuf++];
500 
501 	pngPtr->strDataLen--;
502 
503 	if (PNG64_SPACE == c64) {
504 	    continue;
505 	}
506 
507 	if (c64 & PNG64_SPECIAL) {
508 	    c = (unsigned char) pngPtr->base64Bits;
509 	} else {
510 	    switch (pngPtr->base64State++) {
511 	    case 0:
512 		pngPtr->base64Bits = c64 << 2;
513 		continue;
514 	    case 1:
515 		c = (unsigned char) (pngPtr->base64Bits | (c64 >> 4));
516 		pngPtr->base64Bits = (c64 & 0xF) << 4;
517 		break;
518 	    case 2:
519 		c = (unsigned char) (pngPtr->base64Bits | (c64 >> 2));
520 		pngPtr->base64Bits = (c64 & 0x3) << 6;
521 		break;
522 	    case 3:
523 		c = (unsigned char) (pngPtr->base64Bits | c64);
524 		pngPtr->base64State = 0;
525 		pngPtr->base64Bits = 0;
526 		break;
527 	    }
528 	}
529 
530 	if (crcPtr) {
531 	    *crcPtr = Tcl_ZlibCRC32(*crcPtr, &c, 1);
532 	}
533 
534 	if (destPtr) {
535 	    *destPtr++ = c;
536 	}
537 
538 	destSz--;
539 
540 	if (c64 & PNG64_SPECIAL) {
541 	    break;
542 	}
543     }
544 
545     if (destSz) {
546 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
547 		"unexpected end of image data", -1));
548 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL);
549 	return TCL_ERROR;
550     }
551 
552     return TCL_OK;
553 }
554 
555 /*
556  *----------------------------------------------------------------------
557  *
558  * ReadByteArray --
559  *
560  *	This function is invoked to read the specified number of bytes from a
561  *	non-base64-encoded byte array provided via the -data option.
562  *
563  *	Note: It would be better if the Tk_PhotoImage stuff handled this by
564  *	creating a channel from the -data value and made the data readable as
565  *	if it were coming from a file.
566  *
567  * Results:
568  *	TCL_OK, or TCL_ERROR if an I/O error occurs.
569  *
570  * Side effects:
571  *	The file position will change. The running CRC is updated if a pointer
572  *	to it is provided.
573  *
574  *----------------------------------------------------------------------
575  */
576 
577 static int
ReadByteArray(Tcl_Interp * interp,PNGImage * pngPtr,unsigned char * destPtr,size_t destSz,unsigned long * crcPtr)578 ReadByteArray(
579     Tcl_Interp *interp,
580     PNGImage *pngPtr,
581     unsigned char *destPtr,
582     size_t destSz,
583     unsigned long *crcPtr)
584 {
585     /*
586      * Check to make sure the number of requested bytes are available.
587      */
588 
589     if ((size_t)pngPtr->strDataLen < destSz) {
590 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
591 		"unexpected end of image data", -1));
592 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EARLY_END", NULL);
593 	return TCL_ERROR;
594     }
595 
596     while (destSz) {
597 	size_t blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
598 
599 	memcpy(destPtr, pngPtr->strDataBuf, blockSz);
600 
601 	pngPtr->strDataBuf += blockSz;
602 	pngPtr->strDataLen -= blockSz;
603 
604 	if (crcPtr) {
605 	    *crcPtr = Tcl_ZlibCRC32(*crcPtr, destPtr, blockSz);
606 	}
607 
608 	destPtr += blockSz;
609 	destSz -= blockSz;
610     }
611 
612     return TCL_OK;
613 }
614 
615 /*
616  *----------------------------------------------------------------------
617  *
618  * ReadData --
619  *
620  *	This function is invoked to read the specified number of bytes from
621  *	the image file or data. It is a wrapper around the choice of byte
622  *	array Tcl_Obj or Tcl_Channel which depends on whether the image data
623  *	is coming from a file or -data.
624  *
625  * Results:
626  *	TCL_OK, or TCL_ERROR if an I/O error occurs.
627  *
628  * Side effects:
629  *	The file position will change. The running CRC is updated if a pointer
630  *	to it is provided.
631  *
632  *----------------------------------------------------------------------
633  */
634 
635 static int
ReadData(Tcl_Interp * interp,PNGImage * pngPtr,unsigned char * destPtr,size_t destSz,unsigned long * crcPtr)636 ReadData(
637     Tcl_Interp *interp,
638     PNGImage *pngPtr,
639     unsigned char *destPtr,
640     size_t destSz,
641     unsigned long *crcPtr)
642 {
643     if (pngPtr->base64Data) {
644 	return ReadBase64(interp, pngPtr, destPtr, destSz, crcPtr);
645     } else if (pngPtr->strDataBuf) {
646 	return ReadByteArray(interp, pngPtr, destPtr, destSz, crcPtr);
647     }
648 
649     while (destSz) {
650 	TkSizeT blockSz = PNG_MIN(destSz, PNG_BLOCK_SZ);
651 
652 	blockSz = Tcl_Read(pngPtr->channel, (char *)destPtr, blockSz);
653 	if (blockSz == TCL_IO_FAILURE) {
654 	    /* TODO: failure info... */
655 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
656 		    "channel read failed: %s", Tcl_PosixError(interp)));
657 	    return TCL_ERROR;
658 	}
659 
660 	/*
661 	 * Update CRC, pointer, and remaining count if anything was read.
662 	 */
663 
664 	if (blockSz) {
665 	    if (crcPtr) {
666 		*crcPtr = Tcl_ZlibCRC32(*crcPtr, destPtr, blockSz);
667 	    }
668 
669 	    destPtr += blockSz;
670 	    destSz -= blockSz;
671 	}
672 
673 	/*
674 	 * Check for EOF before all desired data was read.
675 	 */
676 
677 	if (destSz && Tcl_Eof(pngPtr->channel)) {
678 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
679 		    "unexpected end of file", -1));
680 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EOF", NULL);
681 	    return TCL_ERROR;
682 	}
683     }
684 
685     return TCL_OK;
686 }
687 
688 /*
689  *----------------------------------------------------------------------
690  *
691  * ReadInt32 --
692  *
693  *	This function is invoked to read a 32-bit integer in network byte
694  *	order from the image data and return the value in host byte order.
695  *	This is used, for example, to read the 32-bit CRC value for a chunk
696  *	stored in the image file for comparison with the calculated CRC value.
697  *
698  * Results:
699  *	TCL_OK, or TCL_ERROR if an I/O error occurs.
700  *
701  * Side effects:
702  *	The file position will change. The running CRC is updated if a pointer
703  *	to it is provided.
704  *
705  *----------------------------------------------------------------------
706  */
707 
708 static inline int
ReadInt32(Tcl_Interp * interp,PNGImage * pngPtr,unsigned long * resultPtr,unsigned long * crcPtr)709 ReadInt32(
710     Tcl_Interp *interp,
711     PNGImage *pngPtr,
712     unsigned long *resultPtr,
713     unsigned long *crcPtr)
714 {
715     unsigned char p[4];
716 
717     if (ReadData(interp, pngPtr, p, 4, crcPtr) == TCL_ERROR) {
718 	return TCL_ERROR;
719     }
720 
721     *resultPtr = PNG_INT32(p[0], p[1], p[2], p[3]);
722 
723     return TCL_OK;
724 }
725 
726 /*
727  *----------------------------------------------------------------------
728  *
729  * CheckCRC --
730  *
731  *	This function is reads the final 4-byte integer CRC from a chunk and
732  *	compares it to the running CRC calculated over the chunk type and data
733  *	fields.
734  *
735  * Results:
736  *	TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs.
737  *
738  * Side effects:
739  *	The file position will change.
740  *
741  *----------------------------------------------------------------------
742  */
743 
744 static inline int
CheckCRC(Tcl_Interp * interp,PNGImage * pngPtr,unsigned long calculated)745 CheckCRC(
746     Tcl_Interp *interp,
747     PNGImage *pngPtr,
748     unsigned long calculated)
749 {
750     unsigned long chunked;
751 
752     /*
753      * Read the CRC field at the end of the chunk.
754      */
755 
756     if (ReadInt32(interp, pngPtr, &chunked, NULL) == TCL_ERROR) {
757 	return TCL_ERROR;
758     }
759 
760     /*
761      * Compare the read CRC to what we calculate to make sure they match.
762      */
763 
764     if (calculated != chunked) {
765 	Tcl_SetObjResult(interp, Tcl_NewStringObj("CRC check failed", -1));
766 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "CRC", NULL);
767 	return TCL_ERROR;
768     }
769 
770     return TCL_OK;
771 }
772 
773 /*
774  *----------------------------------------------------------------------
775  *
776  * SkipChunk --
777  *
778  *	This function is used to skip a PNG chunk that is not used by this
779  *	implementation. Given the input stream has had the chunk length and
780  *	chunk type fields already read, this function will read the number of
781  *	bytes indicated by the chunk length, plus four for the CRC, and will
782  *	verify that CRC is correct for the skipped data.
783  *
784  * Results:
785  *	TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs.
786  *
787  * Side effects:
788  *	The file position will change.
789  *
790  *----------------------------------------------------------------------
791  */
792 
793 static int
SkipChunk(Tcl_Interp * interp,PNGImage * pngPtr,int chunkSz,unsigned long crc)794 SkipChunk(
795     Tcl_Interp *interp,
796     PNGImage *pngPtr,
797     int chunkSz,
798     unsigned long crc)
799 {
800     unsigned char buffer[PNG_BLOCK_SZ];
801 
802     /*
803      * Skip data in blocks until none is left. Read up to PNG_BLOCK_SZ bytes
804      * at a time, rather than trusting the claimed chunk size, which may not
805      * be trustworthy.
806      */
807 
808     while (chunkSz) {
809 	int blockSz = PNG_MIN(chunkSz, PNG_BLOCK_SZ);
810 
811 	if (ReadData(interp, pngPtr, buffer, blockSz, &crc) == TCL_ERROR) {
812 	    return TCL_ERROR;
813 	}
814 
815 	chunkSz -= blockSz;
816     }
817 
818     if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
819 	return TCL_ERROR;
820     }
821 
822     return TCL_OK;
823 }
824 
825 /*
826  * 4.3. Summary of standard chunks
827  *
828  * This table summarizes some properties of the standard chunk types.
829  *
830  *	Critical chunks (must appear in this order, except PLTE is optional):
831  *
832  *		Name   Multiple	 Ordering constraints OK?
833  *
834  *		IHDR	No	 Must be first
835  *		PLTE	No	 Before IDAT
836  *		IDAT	Yes	 Multiple IDATs must be consecutive
837  *		IEND	No	 Must be last
838  *
839  *	Ancillary chunks (need not appear in this order):
840  *
841  *		Name   Multiple	 Ordering constraints OK?
842  *
843  *		cHRM	No	 Before PLTE and IDAT
844  *		gAMA	No	 Before PLTE and IDAT
845  *		iCCP	No	 Before PLTE and IDAT
846  *		sBIT	No	 Before PLTE and IDAT
847  *		sRGB	No	 Before PLTE and IDAT
848  *		bKGD	No	 After PLTE; before IDAT
849  *		hIST	No	 After PLTE; before IDAT
850  *		tRNS	No	 After PLTE; before IDAT
851  *		pHYs	No	 Before IDAT
852  *		sPLT	Yes	 Before IDAT
853  *		tIME	No	 None
854  *		iTXt	Yes	 None
855  *		tEXt	Yes	 None
856  *		zTXt	Yes	 None
857  *
858  *	[From the PNG specification.]
859  */
860 
861 /*
862  *----------------------------------------------------------------------
863  *
864  * ReadChunkHeader --
865  *
866  *	This function is used at the start of each chunk to extract the
867  *	four-byte chunk length and four-byte chunk type fields. It will
868  *	continue reading until it finds a chunk type that is handled by this
869  *	implementation, checking the CRC of any chunks it skips.
870  *
871  * Results:
872  *	TCL_OK, or TCL_ERROR if an I/O error occurs or an unknown critical
873  *	chunk type is encountered.
874  *
875  * Side effects:
876  *	The file position will change. The running CRC is updated.
877  *
878  *----------------------------------------------------------------------
879  */
880 
881 static int
ReadChunkHeader(Tcl_Interp * interp,PNGImage * pngPtr,size_t * sizePtr,unsigned long * typePtr,unsigned long * crcPtr)882 ReadChunkHeader(
883     Tcl_Interp *interp,
884     PNGImage *pngPtr,
885     size_t *sizePtr,
886     unsigned long *typePtr,
887     unsigned long *crcPtr)
888 {
889     unsigned long chunkType = 0;
890     int chunkSz = 0;
891     unsigned long crc = 0;
892 
893     /*
894      * Continue until finding a chunk type that is handled.
895      */
896 
897     while (!chunkType) {
898 	unsigned long temp;
899 	unsigned char pc[4];
900 	int i;
901 
902 	/*
903 	 * Read the 4-byte length field for the chunk. The length field is not
904 	 * included in the CRC calculation, so the running CRC must be reset
905 	 * afterward. Limit chunk lengths to INT_MAX, to align with the
906 	 * maximum size for Tcl_Read, Tcl_GetByteArrayFromObj, etc.
907 	 */
908 
909 	if (ReadData(interp, pngPtr, pc, 4, NULL) == TCL_ERROR) {
910 	    return TCL_ERROR;
911 	}
912 
913 	temp = PNG_INT32(pc[0], pc[1], pc[2], pc[3]);
914 
915 	if (temp > INT_MAX) {
916 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
917 		    "chunk size is out of supported range on this architecture",
918 		    -1));
919 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "OUTSIZE", NULL);
920 	    return TCL_ERROR;
921 	}
922 
923 	chunkSz = (int) temp;
924 	crc = Tcl_ZlibCRC32(0, NULL, 0);
925 
926 	/*
927 	 * Read the 4-byte chunk type.
928 	 */
929 
930 	if (ReadData(interp, pngPtr, pc, 4, &crc) == TCL_ERROR) {
931 	    return TCL_ERROR;
932 	}
933 
934 	/*
935 	 * Convert it to a host-order integer for simple comparison.
936 	 */
937 
938 	chunkType = PNG_INT32(pc[0], pc[1], pc[2], pc[3]);
939 
940 	/*
941 	 * Check to see if this is a known/supported chunk type. Note that the
942 	 * PNG specs require non-critical (i.e., ancillary) chunk types that
943 	 * are not recognized to be ignored, rather than be treated as an
944 	 * error. It does, however, recommend that an unknown critical chunk
945 	 * type be treated as a failure.
946 	 *
947 	 * This switch/loop acts as a filter of sorts for undesired chunk
948 	 * types. The chunk type should still be checked elsewhere for
949 	 * determining it is in the correct order.
950 	 */
951 
952 	switch (chunkType) {
953 	    /*
954 	     * These chunk types are required and/or supported.
955 	     */
956 
957 	case CHUNK_IDAT:
958 	case CHUNK_IEND:
959 	case CHUNK_IHDR:
960 	case CHUNK_pHYs:
961 	case CHUNK_PLTE:
962 	case CHUNK_tRNS:
963 	    break;
964 
965 	    /*
966 	     * These chunk types are part of the standard, but are not used by
967 	     * this implementation (at least not yet). Note that these are all
968 	     * ancillary chunks (lowercase first letter).
969 	     */
970 
971 	case CHUNK_bKGD:
972 	case CHUNK_cHRM:
973 	case CHUNK_gAMA:
974 	case CHUNK_hIST:
975 	case CHUNK_iCCP:
976 	case CHUNK_iTXt:
977 	case CHUNK_oFFs:
978 	case CHUNK_pCAL:
979 	case CHUNK_sBIT:
980 	case CHUNK_sCAL:
981 	case CHUNK_sPLT:
982 	case CHUNK_sRGB:
983 	case CHUNK_tEXt:
984 	case CHUNK_tIME:
985 	case CHUNK_zTXt:
986 	    /*
987 	     * TODO: might want to check order here.
988 	     */
989 
990 	    if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
991 		return TCL_ERROR;
992 	    }
993 
994 	    chunkType = 0;
995 	    break;
996 
997 	default:
998 	    /*
999 	     * Unknown chunk type. If it's critical, we can't continue.
1000 	     */
1001 
1002 	    if (!(chunkType & PNG_CF_ANCILLARY)) {
1003 		if (chunkType & PNG_INT32(128,128,128,128)) {
1004 		    /*
1005 		     * No nice ASCII conversion; shouldn't happen either, but
1006 		     * we'll be doubly careful.
1007 		     */
1008 
1009 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1010 			    "encountered an unsupported critical chunk type",
1011 			    -1));
1012 		} else {
1013 		    char typeString[5];
1014 
1015 		    typeString[0] = (char) ((chunkType >> 24) & 255);
1016 		    typeString[1] = (char) ((chunkType >> 16) & 255);
1017 		    typeString[2] = (char) ((chunkType >> 8) & 255);
1018 		    typeString[3] = (char) (chunkType & 255);
1019 		    typeString[4] = '\0';
1020 		    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1021 			    "encountered an unsupported critical chunk type"
1022 			    " \"%s\"", typeString));
1023 		}
1024 		Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG",
1025 			"UNSUPPORTED_CRITICAL", NULL);
1026 		return TCL_ERROR;
1027 	    }
1028 
1029 	    /*
1030 	     * Check to see if the chunk type has legal bytes.
1031 	     */
1032 
1033 	    for (i=0 ; i<4 ; i++) {
1034 		if ((pc[i] < 65) || (pc[i] > 122) ||
1035 			((pc[i] > 90) && (pc[i] < 97))) {
1036 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1037 			    "invalid chunk type", -1));
1038 		    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG",
1039 			    "INVALID_CHUNK", NULL);
1040 		    return TCL_ERROR;
1041 		}
1042 	    }
1043 
1044 	    /*
1045 	     * It seems to be an otherwise legally labelled ancillary chunk
1046 	     * that we don't want, so skip it after at least checking its CRC.
1047 	     */
1048 
1049 	    if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
1050 		return TCL_ERROR;
1051 	    }
1052 
1053 	    chunkType = 0;
1054 	}
1055     }
1056 
1057     /*
1058      * Found a known chunk type that's handled, albiet possibly not in the
1059      * right order. Send back the chunk type (for further checking or
1060      * handling), the chunk size and the current CRC for the rest of the
1061      * calculation.
1062      */
1063 
1064     *typePtr = chunkType;
1065     *sizePtr = chunkSz;
1066     *crcPtr = crc;
1067 
1068     return TCL_OK;
1069 }
1070 
1071 /*
1072  *----------------------------------------------------------------------
1073  *
1074  * CheckColor --
1075  *
1076  *	Do validation on color type, depth, and related information, and
1077  *	calculates storage requirements and offsets based on image dimensions
1078  *	and color.
1079  *
1080  * Results:
1081  *	TCL_OK, or TCL_ERROR if color information is invalid or some other
1082  *	failure occurs.
1083  *
1084  * Side effects:
1085  *	None
1086  *
1087  *----------------------------------------------------------------------
1088  */
1089 
1090 static int
CheckColor(Tcl_Interp * interp,PNGImage * pngPtr)1091 CheckColor(
1092     Tcl_Interp *interp,
1093     PNGImage *pngPtr)
1094 {
1095     int offset;
1096 
1097     /*
1098      * Verify the color type is valid and the bit depth is allowed.
1099      */
1100 
1101     switch (pngPtr->colorType) {
1102     case PNG_COLOR_GRAY:
1103 	pngPtr->numChannels = 1;
1104 	if ((1 != pngPtr->bitDepth) && (2 != pngPtr->bitDepth) &&
1105 		(4 != pngPtr->bitDepth) && (8 != pngPtr->bitDepth) &&
1106 		(16 != pngPtr->bitDepth)) {
1107 	    goto unsupportedDepth;
1108 	}
1109 	break;
1110 
1111     case PNG_COLOR_RGB:
1112 	pngPtr->numChannels = 3;
1113 	if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) {
1114 	    goto unsupportedDepth;
1115 	}
1116 	break;
1117 
1118     case PNG_COLOR_PLTE:
1119 	pngPtr->numChannels = 1;
1120 	if ((1 != pngPtr->bitDepth) && (2 != pngPtr->bitDepth) &&
1121 		(4 != pngPtr->bitDepth) && (8 != pngPtr->bitDepth)) {
1122 	    goto unsupportedDepth;
1123 	}
1124 	break;
1125 
1126     case PNG_COLOR_GRAYALPHA:
1127 	pngPtr->numChannels = 2;
1128 	if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) {
1129 	    goto unsupportedDepth;
1130 	}
1131 	break;
1132 
1133     case PNG_COLOR_RGBA:
1134 	pngPtr->numChannels = 4;
1135 	if ((8 != pngPtr->bitDepth) && (16 != pngPtr->bitDepth)) {
1136 	unsupportedDepth:
1137 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1138 		    "bit depth is not allowed for given color type", -1));
1139 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_DEPTH", NULL);
1140 	    return TCL_ERROR;
1141 	}
1142 	break;
1143 
1144     default:
1145 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1146 		"unknown color type field %d", pngPtr->colorType));
1147 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "UNKNOWN_COLOR", NULL);
1148 	return TCL_ERROR;
1149     }
1150 
1151     /*
1152      * Set up the Tk photo block's pixel size and channel offsets. offset
1153      * array elements should already be 0 from the memset during InitPNGImage.
1154      */
1155 
1156     offset = (pngPtr->bitDepth > 8) ? 2 : 1;
1157 
1158     if (pngPtr->colorType & PNG_COLOR_USED) {
1159 	pngPtr->block.pixelSize = offset * 4;
1160 	pngPtr->block.offset[1] = offset;
1161 	pngPtr->block.offset[2] = offset * 2;
1162 	pngPtr->block.offset[3] = offset * 3;
1163     } else {
1164 	pngPtr->block.pixelSize = offset * 2;
1165 	pngPtr->block.offset[3] = offset;
1166     }
1167 
1168     /*
1169      * Calculate the block pitch, which is the number of bytes per line in the
1170      * image, given image width and depth of color. Make sure that it it isn't
1171      * larger than Tk can handle.
1172      */
1173 
1174     if (pngPtr->block.width > INT_MAX / pngPtr->block.pixelSize) {
1175 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1176 		"image pitch is out of supported range on this architecture",
1177 		-1));
1178 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PITCH", NULL);
1179 	return TCL_ERROR;
1180     }
1181 
1182     pngPtr->block.pitch = pngPtr->block.pixelSize * pngPtr->block.width;
1183 
1184     /*
1185      * Calculate the total size of the image as represented to Tk given pitch
1186      * and image height. Make sure that it isn't larger than Tk can handle.
1187      */
1188 
1189     if (pngPtr->block.height > INT_MAX / pngPtr->block.pitch) {
1190 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1191 		"image total size is out of supported range on this architecture",
1192 		-1));
1193 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "SIZE", NULL);
1194 	return TCL_ERROR;
1195     }
1196 
1197     pngPtr->blockLen = pngPtr->block.height * pngPtr->block.pitch;
1198 
1199     /*
1200      * Determine number of bytes per pixel in the source for later use.
1201      */
1202 
1203     switch (pngPtr->colorType) {
1204     case PNG_COLOR_GRAY:
1205 	pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 2 : 1;
1206 	break;
1207     case PNG_COLOR_RGB:
1208 	pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 6 : 3;
1209 	break;
1210     case PNG_COLOR_PLTE:
1211 	pngPtr->bytesPerPixel = 1;
1212 	break;
1213     case PNG_COLOR_GRAYALPHA:
1214 	pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 4 : 2;
1215 	break;
1216     case PNG_COLOR_RGBA:
1217 	pngPtr->bytesPerPixel = (pngPtr->bitDepth > 8) ? 8 : 4;
1218 	break;
1219     default:
1220 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1221 		"unknown color type %d", pngPtr->colorType));
1222 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "UNKNOWN_COLOR", NULL);
1223 	return TCL_ERROR;
1224     }
1225 
1226     /*
1227      * Calculate scale factor for bit depths less than 8, in order to adjust
1228      * them to a minimum of 8 bits per pixel in the Tk image.
1229      */
1230 
1231     if (pngPtr->bitDepth < 8) {
1232 	pngPtr->bitScale = 255 / (int)(pow(2, pngPtr->bitDepth) - 1);
1233     } else {
1234 	pngPtr->bitScale = 1;
1235     }
1236 
1237     return TCL_OK;
1238 }
1239 
1240 /*
1241  *----------------------------------------------------------------------
1242  *
1243  * ReadIHDR --
1244  *
1245  *	This function reads the PNG header from the beginning of a PNG file
1246  *	and returns the dimensions of the image.
1247  *
1248  * Results:
1249  *	The return value is 1 if file "f" appears to start with a valid PNG
1250  *	header, 0 otherwise. If the header is valid, then *widthPtr and
1251  *	*heightPtr are modified to hold the dimensions of the image.
1252  *
1253  * Side effects:
1254  *	The access position in f advances.
1255  *
1256  *----------------------------------------------------------------------
1257  */
1258 
1259 static int
ReadIHDR(Tcl_Interp * interp,PNGImage * pngPtr)1260 ReadIHDR(
1261     Tcl_Interp *interp,
1262     PNGImage *pngPtr)
1263 {
1264     unsigned char sigBuf[PNG_SIG_SZ];
1265     unsigned long chunkType;
1266     size_t chunkSz;
1267     unsigned long crc;
1268     unsigned long width, height;
1269     int mismatch;
1270 
1271     /*
1272      * Read the appropriate number of bytes for the PNG signature.
1273      */
1274 
1275     if (ReadData(interp, pngPtr, sigBuf, PNG_SIG_SZ, NULL) == TCL_ERROR) {
1276 	return TCL_ERROR;
1277     }
1278 
1279     /*
1280      * Compare the read bytes to the expected signature.
1281      */
1282 
1283     mismatch = memcmp(sigBuf, pngSignature, PNG_SIG_SZ);
1284 
1285     /*
1286      * If reading from string, reset position and try base64 decode.
1287      */
1288 
1289     if (mismatch && pngPtr->strDataBuf) {
1290 	pngPtr->strDataBuf = Tcl_GetByteArrayFromObj(pngPtr->objDataPtr,
1291 		&pngPtr->strDataLen);
1292 	pngPtr->base64Data = pngPtr->strDataBuf;
1293 
1294 	if (ReadData(interp, pngPtr, sigBuf, PNG_SIG_SZ, NULL) == TCL_ERROR) {
1295 	    return TCL_ERROR;
1296 	}
1297 
1298 	mismatch = memcmp(sigBuf, pngSignature, PNG_SIG_SZ);
1299     }
1300 
1301     if (mismatch) {
1302 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1303 		"data stream does not have a PNG signature", -1));
1304 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NO_SIG", NULL);
1305 	return TCL_ERROR;
1306     }
1307 
1308     if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
1309 	    &crc) == TCL_ERROR) {
1310 	return TCL_ERROR;
1311     }
1312 
1313     /*
1314      * Read in the IHDR (header) chunk for width, height, etc.
1315      *
1316      * The first chunk in the file must be the IHDR (headr) chunk.
1317      */
1318 
1319     if (chunkType != CHUNK_IHDR) {
1320 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1321 		"expected IHDR chunk type", -1));
1322 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NO_IHDR", NULL);
1323 	return TCL_ERROR;
1324     }
1325 
1326     if (chunkSz != 13) {
1327 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1328 		"invalid IHDR chunk size", -1));
1329 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IHDR", NULL);
1330 	return TCL_ERROR;
1331     }
1332 
1333     /*
1334      * Read and verify the image width and height to be sure Tk can handle its
1335      * dimensions. The PNG specification does not permit zero-width or
1336      * zero-height images.
1337      */
1338 
1339     if (ReadInt32(interp, pngPtr, &width, &crc) == TCL_ERROR) {
1340 	return TCL_ERROR;
1341     }
1342 
1343     if (ReadInt32(interp, pngPtr, &height, &crc) == TCL_ERROR) {
1344 	return TCL_ERROR;
1345     }
1346 
1347     if (!width || !height || (width > INT_MAX) || (height > INT_MAX)) {
1348 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1349 		"image dimensions are invalid or beyond architecture limits",
1350 		-1));
1351 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DIMENSIONS", NULL);
1352 	return TCL_ERROR;
1353     }
1354 
1355     /*
1356      * Set height and width for the Tk photo block.
1357      */
1358 
1359     pngPtr->block.width = (int) width;
1360     pngPtr->block.height = (int) height;
1361 
1362     /*
1363      * Read and the Bit Depth and Color Type.
1364      */
1365 
1366     if (ReadData(interp, pngPtr, &pngPtr->bitDepth, 1, &crc) == TCL_ERROR) {
1367 	return TCL_ERROR;
1368     }
1369 
1370     if (ReadData(interp, pngPtr, &pngPtr->colorType, 1, &crc) == TCL_ERROR) {
1371 	return TCL_ERROR;
1372     }
1373 
1374     /*
1375      * Verify that the color type is valid, the bit depth is allowed for the
1376      * color type, and calculate the number of channels and pixel depth (bits
1377      * per pixel * channels). Also set up offsets and sizes in the Tk photo
1378      * block for the pixel data.
1379      */
1380 
1381     if (CheckColor(interp, pngPtr) == TCL_ERROR) {
1382 	return TCL_ERROR;
1383     }
1384 
1385     /*
1386      * Only one compression method is currently defined by the standard.
1387      */
1388 
1389     if (ReadData(interp, pngPtr, &pngPtr->compression, 1, &crc) == TCL_ERROR) {
1390 	return TCL_ERROR;
1391     }
1392 
1393     if (pngPtr->compression != PNG_COMPRESS_DEFLATE) {
1394 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1395 		"unknown compression method %d", pngPtr->compression));
1396 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_COMPRESS", NULL);
1397 	return TCL_ERROR;
1398     }
1399 
1400     /*
1401      * Only one filter method is currently defined by the standard; the method
1402      * has five actual filter types associated with it.
1403      */
1404 
1405     if (ReadData(interp, pngPtr, &pngPtr->filter, 1, &crc) == TCL_ERROR) {
1406 	return TCL_ERROR;
1407     }
1408 
1409     if (pngPtr->filter != PNG_FILTMETH_STANDARD) {
1410 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1411 		"unknown filter method %d", pngPtr->filter));
1412 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_FILTER", NULL);
1413 	return TCL_ERROR;
1414     }
1415 
1416     if (ReadData(interp, pngPtr, &pngPtr->interlace, 1, &crc) == TCL_ERROR) {
1417 	return TCL_ERROR;
1418     }
1419 
1420     switch (pngPtr->interlace) {
1421     case PNG_INTERLACE_NONE:
1422     case PNG_INTERLACE_ADAM7:
1423 	break;
1424 
1425     default:
1426 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1427 		"unknown interlace method %d", pngPtr->interlace));
1428 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_INTERLACE", NULL);
1429 	return TCL_ERROR;
1430     }
1431 
1432     return CheckCRC(interp, pngPtr, crc);
1433 }
1434 
1435 /*
1436  *----------------------------------------------------------------------
1437  *
1438  * ReadPLTE --
1439  *
1440  *	This function reads the PLTE (indexed color palette) chunk data from
1441  *	the PNG file and populates the palette table in the PNGImage
1442  *	structure.
1443  *
1444  * Results:
1445  *	TCL_OK, or TCL_ERROR if an I/O error occurs or the PLTE chunk is
1446  *	invalid.
1447  *
1448  * Side effects:
1449  *	The access position in f advances.
1450  *
1451  *----------------------------------------------------------------------
1452  */
1453 
1454 static int
ReadPLTE(Tcl_Interp * interp,PNGImage * pngPtr,int chunkSz,unsigned long crc)1455 ReadPLTE(
1456     Tcl_Interp *interp,
1457     PNGImage *pngPtr,
1458     int chunkSz,
1459     unsigned long crc)
1460 {
1461     unsigned char buffer[PNG_PLTE_MAXSZ];
1462     int i, c;
1463 
1464     /*
1465      * This chunk is mandatory for color type 3 and forbidden for 2 and 6.
1466      */
1467 
1468     switch (pngPtr->colorType) {
1469     case PNG_COLOR_GRAY:
1470     case PNG_COLOR_GRAYALPHA:
1471 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1472 		"PLTE chunk type forbidden for grayscale", -1));
1473 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PLTE_UNEXPECTED",
1474 		NULL);
1475 	return TCL_ERROR;
1476 
1477     default:
1478 	break;
1479     }
1480 
1481     /*
1482      * The palette chunk contains from 1 to 256 palette entries. Each entry
1483      * consists of a 3-byte RGB value. It must therefore contain a non-zero
1484      * multiple of 3 bytes, up to 768.
1485      */
1486 
1487     if (!chunkSz || (chunkSz > PNG_PLTE_MAXSZ) || (chunkSz % 3)) {
1488 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1489 		"invalid palette chunk size", -1));
1490 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PLTE", NULL);
1491 	return TCL_ERROR;
1492     }
1493 
1494     /*
1495      * Read the palette contents and stash them for later, possibly.
1496      */
1497 
1498     if (ReadData(interp, pngPtr, buffer, chunkSz, &crc) == TCL_ERROR) {
1499 	return TCL_ERROR;
1500     }
1501 
1502     if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
1503 	return TCL_ERROR;
1504     }
1505 
1506     /*
1507      * Stash away the palette entries and entry count for later mapping each
1508      * pixel's palette index to its color.
1509      */
1510 
1511     for (i=0, c=0 ; c<chunkSz ; i++) {
1512 	pngPtr->palette[i].red = buffer[c++];
1513 	pngPtr->palette[i].green = buffer[c++];
1514 	pngPtr->palette[i].blue = buffer[c++];
1515     }
1516 
1517     pngPtr->paletteLen = i;
1518     return TCL_OK;
1519 }
1520 
1521 /*
1522  *----------------------------------------------------------------------
1523  *
1524  * ReadTRNS --
1525  *
1526  *	This function reads the tRNS (transparency) chunk data from the PNG
1527  *	file and populates the alpha field of the palette table in the
1528  *	PNGImage structure or the single color transparency, as appropriate
1529  *	for the color type.
1530  *
1531  * Results:
1532  *	TCL_OK, or TCL_ERROR if an I/O error occurs or the tRNS chunk is
1533  *	invalid.
1534  *
1535  * Side effects:
1536  *	The access position in f advances.
1537  *
1538  *----------------------------------------------------------------------
1539  */
1540 
1541 static int
ReadTRNS(Tcl_Interp * interp,PNGImage * pngPtr,int chunkSz,unsigned long crc)1542 ReadTRNS(
1543     Tcl_Interp *interp,
1544     PNGImage *pngPtr,
1545     int chunkSz,
1546     unsigned long crc)
1547 {
1548     unsigned char buffer[PNG_TRNS_MAXSZ];
1549     int i;
1550 
1551     if (pngPtr->colorType & PNG_COLOR_ALPHA) {
1552 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1553 		"tRNS chunk not allowed color types with a full alpha channel",
1554 		-1));
1555 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "INVALID_TRNS", NULL);
1556 	return TCL_ERROR;
1557     }
1558 
1559     /*
1560      * For indexed color, there is up to one single-byte transparency value
1561      * per palette entry (thus a max of 256).
1562      */
1563 
1564     if (chunkSz > PNG_TRNS_MAXSZ) {
1565 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1566 		"invalid tRNS chunk size", -1));
1567 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL);
1568 	return TCL_ERROR;
1569     }
1570 
1571     /*
1572      * Read in the raw transparency information.
1573      */
1574 
1575     if (ReadData(interp, pngPtr, buffer, chunkSz, &crc) == TCL_ERROR) {
1576 	return TCL_ERROR;
1577     }
1578 
1579     if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
1580 	return TCL_ERROR;
1581     }
1582 
1583     switch (pngPtr->colorType) {
1584     case PNG_COLOR_GRAYALPHA:
1585     case PNG_COLOR_RGBA:
1586 	break;
1587 
1588     case PNG_COLOR_PLTE:
1589 	/*
1590 	 * The number of tRNS entries must be less than or equal to the number
1591 	 * of PLTE entries, and consists of a single-byte alpha level for the
1592 	 * corresponding PLTE entry.
1593 	 */
1594 
1595 	if (chunkSz > pngPtr->paletteLen) {
1596 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1597 		    "size of tRNS chunk is too large for the palette", -1));
1598 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TRNS_SIZE", NULL);
1599 	    return TCL_ERROR;
1600 	}
1601 
1602 	for (i=0 ; i<chunkSz ; i++) {
1603 	    pngPtr->palette[i].alpha = buffer[i];
1604 	}
1605 	break;
1606 
1607     case PNG_COLOR_GRAY:
1608 	/*
1609 	 * Grayscale uses a single 2-byte gray level, which we'll store in
1610 	 * palette index 0, since we're not using the palette.
1611 	 */
1612 
1613 	if (chunkSz != 2) {
1614 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1615 		    "invalid tRNS chunk size - must 2 bytes for grayscale",
1616 		    -1));
1617 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL);
1618 	    return TCL_ERROR;
1619 	}
1620 
1621 	/*
1622 	 * According to the PNG specs, if the bit depth is less than 16, then
1623 	 * only the lower byte is used.
1624 	 */
1625 
1626 	if (16 == pngPtr->bitDepth) {
1627 	    pngPtr->transVal[0] = buffer[0];
1628 	    pngPtr->transVal[1] = buffer[1];
1629 	} else {
1630 	    pngPtr->transVal[0] = buffer[1];
1631 	}
1632 	pngPtr->useTRNS = 1;
1633 	break;
1634 
1635     case PNG_COLOR_RGB:
1636 	/*
1637 	 * TrueColor uses a single RRGGBB triplet.
1638 	 */
1639 
1640 	if (chunkSz != 6) {
1641 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
1642 		    "invalid tRNS chunk size - must 6 bytes for RGB", -1));
1643 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_TRNS", NULL);
1644 	    return TCL_ERROR;
1645 	}
1646 
1647 	/*
1648 	 * According to the PNG specs, if the bit depth is less than 16, then
1649 	 * only the lower byte is used. But the tRNS chunk still contains two
1650 	 * bytes per channel.
1651 	 */
1652 
1653 	if (16 == pngPtr->bitDepth) {
1654 	    memcpy(pngPtr->transVal, buffer, 6);
1655 	} else {
1656 	    pngPtr->transVal[0] = buffer[1];
1657 	    pngPtr->transVal[1] = buffer[3];
1658 	    pngPtr->transVal[2] = buffer[5];
1659 	}
1660 	pngPtr->useTRNS = 1;
1661 	break;
1662     }
1663 
1664     return TCL_OK;
1665 }
1666 
1667 /*
1668  *----------------------------------------------------------------------
1669  *
1670  * ReadPHYS --
1671  *
1672  *	This function reads the PHYS (physical size) chunk data from
1673  *	the PNG file and populates the fields in the PNGImage
1674  *	structure.
1675  *
1676  * Results:
1677  *	TCL_OK, or TCL_ERROR if an I/O error occurs or the PHYS chunk is
1678  *	invalid.
1679  *
1680  * Side effects:
1681  *	The access position in f advances.
1682  *
1683  *----------------------------------------------------------------------
1684  */
1685 
1686 static int
ReadPHYS(Tcl_Interp * interp,PNGImage * pngPtr,int chunkSz,unsigned long crc)1687 ReadPHYS(
1688     Tcl_Interp *interp,
1689     PNGImage *pngPtr,
1690     int chunkSz,
1691     unsigned long crc)
1692 {
1693     unsigned long PPUx, PPUy;
1694     char unitSpecifier;
1695 
1696     /*
1697      * Check chunk size equal 9 bytes
1698      */
1699 
1700     if (chunkSz != 9) {
1701 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1702 		"invalid physical chunk size", -1));
1703 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PHYS", NULL);
1704 	return TCL_ERROR;
1705     }
1706 
1707     /*
1708      * Read the chunk data
1709      * 4 bytes: Pixels per unit, x axis
1710      * 4 bytes: Pixels per unit, y axis
1711      * 1 byte: unit specifier
1712      */
1713 
1714     if (ReadInt32(interp, pngPtr, &PPUx, &crc) == TCL_ERROR) {
1715 	return TCL_ERROR;
1716     }
1717     if (ReadInt32(interp, pngPtr, &PPUy, &crc) == TCL_ERROR) {
1718 	return TCL_ERROR;
1719     }
1720     if (ReadData(interp, pngPtr, (unsigned char *)&unitSpecifier, 1, &crc) == TCL_ERROR) {
1721 	return TCL_ERROR;
1722     }
1723 
1724     if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
1725 	return TCL_ERROR;
1726     }
1727 
1728     if ( PPUx > 2147483647 || PPUy > 2147483647
1729 	    || unitSpecifier > 1 ) {
1730 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
1731 		"invalid physical size value", -1));
1732 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_PHYS", NULL);
1733 	return TCL_ERROR;
1734     }
1735 
1736     if (PPUx > 0) {
1737 	pngPtr->aspect = ((double) PPUy) / ((double) PPUx);
1738     }
1739     if (1 == unitSpecifier) {
1740 	pngPtr->DPI = ((double) PPUx) * 0.0254;
1741     }
1742     return TCL_OK;
1743 }
1744 
1745 /*
1746  *----------------------------------------------------------------------
1747  *
1748  * Paeth --
1749  *
1750  *	Utility function for applying the Paeth filter to a pixel. The Paeth
1751  *	filter is a linear function of the pixel to be filtered and the pixels
1752  *	to the left, above, and above-left of the pixel to be unfiltered.
1753  *
1754  * Results:
1755  *	Result of the Paeth function for the left, above, and above-left
1756  *	pixels.
1757  *
1758  * Side effects:
1759  *	None
1760  *
1761  *----------------------------------------------------------------------
1762  */
1763 
1764 static inline unsigned char
Paeth(int a,int b,int c)1765 Paeth(
1766     int a,
1767     int b,
1768     int c)
1769 {
1770     int pa = abs(b - c);
1771     int pb = abs(a - c);
1772     int pc = abs(a + b - c - c);
1773 
1774     if ((pa <= pb) && (pa <= pc)) {
1775 	return (unsigned char) a;
1776     }
1777 
1778     if (pb <= pc) {
1779 	return (unsigned char) b;
1780     }
1781 
1782     return (unsigned char) c;
1783 }
1784 
1785 /*
1786  *----------------------------------------------------------------------
1787  *
1788  * UnfilterLine --
1789  *
1790  *	Applies the filter algorithm specified in first byte of a line to the
1791  *	line of pixels being read from a PNG image.
1792  *
1793  *	PNG specifies four filter algorithms (Sub, Up, Average, and Paeth)
1794  *	that combine a pixel's value with those of other pixels in the same
1795  *	and/or previous lines. Filtering is intended to make an image more
1796  *	compressible.
1797  *
1798  * Results:
1799  *	TCL_OK, or TCL_ERROR if the filter type is not recognized.
1800  *
1801  * Side effects:
1802  *	Pixel data in thisLineObj are modified.
1803  *
1804  *----------------------------------------------------------------------
1805  */
1806 
1807 static int
UnfilterLine(Tcl_Interp * interp,PNGImage * pngPtr)1808 UnfilterLine(
1809     Tcl_Interp *interp,
1810     PNGImage *pngPtr)
1811 {
1812     unsigned char *thisLine =
1813 	    Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, (int *)NULL);
1814     unsigned char *lastLine =
1815 	    Tcl_GetByteArrayFromObj(pngPtr->lastLineObj, (int *)NULL);
1816 
1817 #define	PNG_FILTER_NONE		0
1818 #define	PNG_FILTER_SUB		1
1819 #define	PNG_FILTER_UP		2
1820 #define	PNG_FILTER_AVG		3
1821 #define	PNG_FILTER_PAETH	4
1822 
1823     switch (*thisLine) {
1824     case PNG_FILTER_NONE:	/* Nothing to do */
1825 	break;
1826     case PNG_FILTER_SUB: {	/* Sub(x) = Raw(x) - Raw(x-bpp) */
1827 	unsigned char *rawBpp = thisLine + 1;
1828 	unsigned char *raw = rawBpp + pngPtr->bytesPerPixel;
1829 	unsigned char *end = thisLine + pngPtr->phaseSize;
1830 
1831 	while (raw < end) {
1832 	    *raw++ += *rawBpp++;
1833 	}
1834 	break;
1835     }
1836     case PNG_FILTER_UP:		/* Up(x) = Raw(x) - Prior(x) */
1837 	if (pngPtr->currentLine > startLine[pngPtr->phase]) {
1838 	    unsigned char *prior = lastLine + 1;
1839 	    unsigned char *raw = thisLine + 1;
1840 	    unsigned char *end = thisLine + pngPtr->phaseSize;
1841 
1842 	    while (raw < end) {
1843 		*raw++ += *prior++;
1844 	    }
1845 	}
1846 	break;
1847     case PNG_FILTER_AVG:
1848 	/* Avg(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) */
1849 	if (pngPtr->currentLine > startLine[pngPtr->phase]) {
1850 	    unsigned char *prior = lastLine + 1;
1851 	    unsigned char *rawBpp = thisLine + 1;
1852 	    unsigned char *raw = rawBpp;
1853 	    unsigned char *end = thisLine + pngPtr->phaseSize;
1854 	    unsigned char *end2 = raw + pngPtr->bytesPerPixel;
1855 
1856 	    while ((raw < end2) && (raw < end)) {
1857 		*raw++ += *prior++ / 2;
1858 	    }
1859 
1860 	    while (raw < end) {
1861 		*raw++ += (unsigned char)
1862 			(((int) *rawBpp++ + (int) *prior++) / 2);
1863 	    }
1864 	} else {
1865 	    unsigned char *rawBpp = thisLine + 1;
1866 	    unsigned char *raw = rawBpp + pngPtr->bytesPerPixel;
1867 	    unsigned char *end = thisLine + pngPtr->phaseSize;
1868 
1869 	    while (raw < end) {
1870 		*raw++ += *rawBpp++ / 2;
1871 	    }
1872 	}
1873 	break;
1874     case PNG_FILTER_PAETH:
1875 	/* Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) */
1876 	if (pngPtr->currentLine > startLine[pngPtr->phase]) {
1877 	    unsigned char *priorBpp = lastLine + 1;
1878 	    unsigned char *prior = priorBpp;
1879 	    unsigned char *rawBpp = thisLine + 1;
1880 	    unsigned char *raw = rawBpp;
1881 	    unsigned char *end = thisLine + pngPtr->phaseSize;
1882 	    unsigned char *end2 = rawBpp + pngPtr->bytesPerPixel;
1883 
1884 	    while ((raw < end) && (raw < end2)) {
1885 		*raw++ += *prior++;
1886 	    }
1887 
1888 	    while (raw < end) {
1889 		*raw++ += Paeth(*rawBpp++, *prior++, *priorBpp++);
1890 	    }
1891 	} else {
1892 	    unsigned char *rawBpp = thisLine + 1;
1893 	    unsigned char *raw = rawBpp + pngPtr->bytesPerPixel;
1894 	    unsigned char *end = thisLine + pngPtr->phaseSize;
1895 
1896 	    while (raw < end) {
1897 		*raw++ += *rawBpp++;
1898 	    }
1899 	}
1900 	break;
1901     default:
1902 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1903 		"invalid filter type %d", *thisLine));
1904 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_FILTER", NULL);
1905 	return TCL_ERROR;
1906     }
1907 
1908     return TCL_OK;
1909 }
1910 
1911 /*
1912  *----------------------------------------------------------------------
1913  *
1914  * DecodeLine --
1915  *
1916  *	Unfilters a line of pixels from the PNG source data and decodes the
1917  *	data into the Tk_PhotoImageBlock for later copying into the Tk image.
1918  *
1919  * Results:
1920  *	TCL_OK, or TCL_ERROR if the filter type is not recognized.
1921  *
1922  * Side effects:
1923  *	Pixel data in thisLine and block are modified and state information
1924  *	updated.
1925  *
1926  *----------------------------------------------------------------------
1927  */
1928 
1929 static int
DecodeLine(Tcl_Interp * interp,PNGImage * pngPtr)1930 DecodeLine(
1931     Tcl_Interp *interp,
1932     PNGImage *pngPtr)
1933 {
1934     unsigned char *pixelPtr = pngPtr->block.pixelPtr;
1935     int colNum = 0;		/* Current pixel column */
1936     unsigned char chan = 0;	/* Current channel (0..3) = (R, G, B, A) */
1937     unsigned char readByte = 0;	/* Current scan line byte */
1938     int haveBits = 0;		/* Number of bits remaining in current byte */
1939     unsigned char pixBits = 0;	/* Extracted bits for current channel */
1940     int shifts = 0;		/* Number of channels extracted from byte */
1941     int offset = 0;		/* Current offset into pixelPtr */
1942     int colStep = 1;		/* Column increment each pass */
1943     int pixStep = 0;		/* extra pixelPtr increment each pass */
1944     unsigned char lastPixel[6];
1945     unsigned char *p = Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, (int *)NULL);
1946 
1947     p++;
1948     if (UnfilterLine(interp, pngPtr) == TCL_ERROR) {
1949 	return TCL_ERROR;
1950     }
1951     if (pngPtr->currentLine >= pngPtr->block.height) {
1952 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1953 		"PNG image data overflow"));
1954 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DATA_OVERFLOW", NULL);
1955 	return TCL_ERROR;
1956     }
1957 
1958 
1959     if (pngPtr->interlace) {
1960 	switch (pngPtr->phase) {
1961 	case 1:			/* Phase 1: */
1962 	    colStep = 8;	/* 1 pixel per block of 8 per line */
1963 	    break;		/* Start at column 0 */
1964 	case 2:			/* Phase 2: */
1965 	    colStep = 8;	/* 1 pixels per block of 8 per line */
1966 	    colNum = 4;		/* Start at column 4 */
1967 	    break;
1968 	case 3:			/* Phase 3: */
1969 	    colStep = 4;	/* 2 pixels per block of 8 per line */
1970 	    break;		/* Start at column 0 */
1971 	case 4:			/* Phase 4: */
1972 	    colStep = 4;	/* 2 pixels per block of 8 per line */
1973 	    colNum = 2;		/* Start at column 2 */
1974 	    break;
1975 	case 5:			/* Phase 5: */
1976 	    colStep = 2;	/* 4 pixels per block of 8 per line */
1977 	    break;		/* Start at column 0 */
1978 	case 6:			/* Phase 6: */
1979 	    colStep = 2;	/* 4 pixels per block of 8 per line */
1980 	    colNum = 1;		/* Start at column 1 */
1981 	    break;
1982 				/* Phase 7: */
1983 				/* 8 pixels per block of 8 per line */
1984 				/* Start at column 0 */
1985 	}
1986     }
1987 
1988     /*
1989      * Calculate offset into pixelPtr for the first pixel of the line.
1990      */
1991 
1992     offset = pngPtr->currentLine * pngPtr->block.pitch;
1993 
1994     /*
1995      * Adjust up for the starting pixel of the line.
1996      */
1997 
1998     offset += colNum * pngPtr->block.pixelSize;
1999 
2000     /*
2001      * Calculate the extra number of bytes to skip between columns.
2002      */
2003 
2004     pixStep = (colStep - 1) * pngPtr->block.pixelSize;
2005 
2006     for ( ; colNum < pngPtr->block.width ; colNum += colStep) {
2007 	if (haveBits < (pngPtr->bitDepth * pngPtr->numChannels)) {
2008 	    haveBits = 0;
2009 	}
2010 
2011 	for (chan = 0 ; chan < pngPtr->numChannels ; chan++) {
2012 	    if (!haveBits) {
2013 		shifts = 0;
2014 		readByte = *p++;
2015 		haveBits += 8;
2016 	    }
2017 
2018 	    if (16 == pngPtr->bitDepth) {
2019 		pngPtr->block.pixelPtr[offset++] = readByte;
2020 
2021 		if (pngPtr->useTRNS) {
2022 		    lastPixel[chan * 2] = readByte;
2023 		}
2024 
2025 		readByte = *p++;
2026 
2027 		if (pngPtr->useTRNS) {
2028 		    lastPixel[(chan * 2) + 1] = readByte;
2029 		}
2030 
2031 		pngPtr->block.pixelPtr[offset++] = readByte;
2032 
2033 		haveBits = 0;
2034 		continue;
2035 	    }
2036 
2037 	    switch (pngPtr->bitDepth) {
2038 	    case 1:
2039 		pixBits = (unsigned char)((readByte >> (7-shifts)) & 0x01);
2040 		break;
2041 	    case 2:
2042 		pixBits = (unsigned char)((readByte >> (6-shifts*2)) & 0x03);
2043 		break;
2044 	    case 4:
2045 		pixBits = (unsigned char)((readByte >> (4-shifts*4)) & 0x0f);
2046 		break;
2047 	    case 8:
2048 		pixBits = readByte;
2049 		break;
2050 	    }
2051 
2052 	    if (PNG_COLOR_PLTE == pngPtr->colorType) {
2053 		pixelPtr[offset++] = pngPtr->palette[pixBits].red;
2054 		pixelPtr[offset++] = pngPtr->palette[pixBits].green;
2055 		pixelPtr[offset++] = pngPtr->palette[pixBits].blue;
2056 		pixelPtr[offset++] = pngPtr->palette[pixBits].alpha;
2057 		chan += 2;
2058 	    } else {
2059 		pixelPtr[offset++] = (unsigned char)
2060 			(pixBits * pngPtr->bitScale);
2061 
2062 		if (pngPtr->useTRNS) {
2063 		    lastPixel[chan] = pixBits;
2064 		}
2065 	    }
2066 
2067 	    haveBits -= pngPtr->bitDepth;
2068 	    shifts++;
2069 	}
2070 
2071 	/*
2072 	 * Apply boolean transparency via tRNS data if necessary (where
2073 	 * necessary means a tRNS chunk was provided and we're not using an
2074 	 * alpha channel or indexed alpha).
2075 	 */
2076 
2077 	if ((PNG_COLOR_PLTE != pngPtr->colorType) &&
2078 		!(pngPtr->colorType & PNG_COLOR_ALPHA)) {
2079 	    unsigned char alpha;
2080 
2081 	    if (pngPtr->useTRNS) {
2082 		if (memcmp(lastPixel, pngPtr->transVal,
2083 			pngPtr->bytesPerPixel) == 0) {
2084 		    alpha = 0x00;
2085 		} else {
2086 		    alpha = 0xff;
2087 		}
2088 	    } else {
2089 		alpha = 0xff;
2090 	    }
2091 
2092 	    pixelPtr[offset++] = alpha;
2093 
2094 	    if (16 == pngPtr->bitDepth) {
2095 		pixelPtr[offset++] = alpha;
2096 	    }
2097 	}
2098 
2099 	offset += pixStep;
2100     }
2101 
2102     if (pngPtr->interlace) {
2103 	/* Skip lines */
2104 
2105 	switch (pngPtr->phase) {
2106 	case 1: case 2: case 3:
2107 	    pngPtr->currentLine += 8;
2108 	    break;
2109 	case 4: case 5:
2110 	    pngPtr->currentLine += 4;
2111 	    break;
2112 	case 6: case 7:
2113 	    pngPtr->currentLine += 2;
2114 	    break;
2115 	}
2116 
2117 	/*
2118 	 * Start the next phase if there are no more lines to do.
2119 	 */
2120 
2121 	if (pngPtr->currentLine >= pngPtr->block.height) {
2122 	    unsigned long pixels = 0;
2123 
2124 	    while ((!pixels || (pngPtr->currentLine >= pngPtr->block.height))
2125 		    && (pngPtr->phase < 7)) {
2126 		pngPtr->phase++;
2127 
2128 		switch (pngPtr->phase) {
2129 		case 2:
2130 		    pixels = (pngPtr->block.width + 3) >> 3;
2131 		    pngPtr->currentLine = 0;
2132 		    break;
2133 		case 3:
2134 		    pixels = (pngPtr->block.width + 3) >> 2;
2135 		    pngPtr->currentLine = 4;
2136 		    break;
2137 		case 4:
2138 		    pixels = (pngPtr->block.width + 1) >> 2;
2139 		    pngPtr->currentLine = 0;
2140 		    break;
2141 		case 5:
2142 		    pixels = (pngPtr->block.width + 1) >> 1;
2143 		    pngPtr->currentLine = 2;
2144 		    break;
2145 		case 6:
2146 		    pixels = pngPtr->block.width >> 1;
2147 		    pngPtr->currentLine = 0;
2148 		    break;
2149 		case 7:
2150 		    pngPtr->currentLine = 1;
2151 		    pixels = pngPtr->block.width;
2152 		    break;
2153 		}
2154 	    }
2155 
2156 	    if (16 == pngPtr->bitDepth) {
2157 		pngPtr->phaseSize = 1 + (pngPtr->numChannels * pixels * 2);
2158 	    } else {
2159 		pngPtr->phaseSize = 1 + ((pngPtr->numChannels * pixels *
2160 			pngPtr->bitDepth + 7) >> 3);
2161 	    }
2162 	}
2163     } else {
2164 	pngPtr->currentLine++;
2165     }
2166 
2167     return TCL_OK;
2168 }
2169 
2170 /*
2171  *----------------------------------------------------------------------
2172  *
2173  * ReadIDAT --
2174  *
2175  *	This function reads the IDAT (pixel data) chunk from the PNG file to
2176  *	build the image. It will continue reading until all IDAT chunks have
2177  *	been processed or an error occurs.
2178  *
2179  * Results:
2180  *	TCL_OK, or TCL_ERROR if an I/O error occurs or an IDAT chunk is
2181  *	invalid.
2182  *
2183  * Side effects:
2184  *	The access position in f advances. Memory may be allocated by zlib
2185  *	through PNGZAlloc.
2186  *
2187  *----------------------------------------------------------------------
2188  */
2189 
2190 static int
ReadIDAT(Tcl_Interp * interp,PNGImage * pngPtr,int chunkSz,unsigned long crc)2191 ReadIDAT(
2192     Tcl_Interp *interp,
2193     PNGImage *pngPtr,
2194     int chunkSz,
2195     unsigned long crc)
2196 {
2197     /*
2198      * Process IDAT contents until there is no more in this chunk.
2199      */
2200 
2201     while (chunkSz && !Tcl_ZlibStreamEof(pngPtr->stream)) {
2202 	TkSizeT len1, len2;
2203 
2204 	/*
2205 	 * Read another block of input into the zlib stream if data remains.
2206 	 */
2207 
2208 	if (chunkSz) {
2209 	    Tcl_Obj *inputObj = NULL;
2210 	    int blockSz = PNG_MIN(chunkSz, PNG_BLOCK_SZ);
2211 	    unsigned char *inputPtr = NULL;
2212 
2213 	    /*
2214 	     * Check for end of zlib stream.
2215 	     */
2216 
2217 	    if (Tcl_ZlibStreamEof(pngPtr->stream)) {
2218 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
2219 			"extra data after end of zlib stream", -1));
2220 		Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA",
2221 			NULL);
2222 		return TCL_ERROR;
2223 	    }
2224 
2225 	    inputObj = Tcl_NewObj();
2226 	    Tcl_IncrRefCount(inputObj);
2227 	    inputPtr = Tcl_SetByteArrayLength(inputObj, blockSz);
2228 
2229 	    /*
2230 	     * Read the next bit of IDAT chunk data, up to read buffer size.
2231 	     */
2232 
2233 	    if (ReadData(interp, pngPtr, inputPtr, blockSz,
2234 		    &crc) == TCL_ERROR) {
2235 		Tcl_DecrRefCount(inputObj);
2236 		return TCL_ERROR;
2237 	    }
2238 
2239 	    chunkSz -= blockSz;
2240 
2241 	    Tcl_ZlibStreamPut(pngPtr->stream, inputObj, TCL_ZLIB_NO_FLUSH);
2242 	    Tcl_DecrRefCount(inputObj);
2243 	}
2244 
2245 	/*
2246 	 * Inflate, processing each output buffer's worth as a line of pixels,
2247 	 * until we cannot fill the buffer any more.
2248 	 */
2249 
2250     getNextLine:
2251 	Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, &len1);
2252 	if (Tcl_ZlibStreamGet(pngPtr->stream, pngPtr->thisLineObj,
2253 		pngPtr->phaseSize - len1) == TCL_ERROR) {
2254 	    return TCL_ERROR;
2255 	}
2256 	Tcl_GetByteArrayFromObj(pngPtr->thisLineObj, &len2);
2257 
2258 	if (len2 == (TkSizeT)pngPtr->phaseSize) {
2259 	    if (pngPtr->phase > 7) {
2260 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
2261 			"extra data after final scan line of final phase",
2262 			-1));
2263 		Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA",
2264 			NULL);
2265 		return TCL_ERROR;
2266 	    }
2267 
2268 	    if (DecodeLine(interp, pngPtr) == TCL_ERROR) {
2269 		return TCL_ERROR;
2270 	    }
2271 
2272 	    /*
2273 	     * Swap the current/last lines so that we always have the last
2274 	     * line processed available, which is necessary for filtering.
2275 	     */
2276 
2277 	    {
2278 		Tcl_Obj *temp = pngPtr->lastLineObj;
2279 
2280 		pngPtr->lastLineObj = pngPtr->thisLineObj;
2281 		pngPtr->thisLineObj = temp;
2282 	    }
2283 	    Tcl_SetByteArrayLength(pngPtr->thisLineObj, 0);
2284 
2285 	    /*
2286 	     * Try to read another line of pixels out of the buffer
2287 	     * immediately, but don't allow write past end of block.
2288 	     */
2289 
2290 	    if (pngPtr->currentLine < pngPtr->block.height) {
2291 		goto getNextLine;
2292 	    }
2293 
2294 	}
2295 
2296 	/*
2297 	 * Got less than a whole buffer-load of pixels. Either we're going to
2298 	 * be getting more data from the next IDAT, or we've done what we can
2299 	 * here.
2300 	 */
2301     }
2302 
2303     /*
2304      * Ensure that if we've got to the end of the compressed data, we've
2305      * also got to the end of the compressed stream. This sanity check is
2306      * enforced by most PNG readers.
2307      */
2308 
2309     if (chunkSz != 0) {
2310 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2311 		"compressed data after stream finalize in PNG data", -1));
2312 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL);
2313 	return TCL_ERROR;
2314     }
2315 
2316     return CheckCRC(interp, pngPtr, crc);
2317 }
2318 
2319 /*
2320  *----------------------------------------------------------------------
2321  *
2322  * ApplyAlpha --
2323  *
2324  *	Applies an overall alpha value to a complete image that has been read.
2325  *	This alpha value is specified using the -format option to [image
2326  *	create photo].
2327  *
2328  * Results:
2329  *	N/A
2330  *
2331  * Side effects:
2332  *	The access position in f may change.
2333  *
2334  *----------------------------------------------------------------------
2335  */
2336 
2337 static void
ApplyAlpha(PNGImage * pngPtr)2338 ApplyAlpha(
2339     PNGImage *pngPtr)
2340 {
2341     if (pngPtr->alpha != 1.0) {
2342 	unsigned char *p = pngPtr->block.pixelPtr;
2343 	unsigned char *endPtr = p + pngPtr->blockLen;
2344 	int offset = pngPtr->block.offset[3];
2345 
2346 	p += offset;
2347 
2348 	if (16 == pngPtr->bitDepth) {
2349 	    unsigned int channel;
2350 
2351 	    while (p < endPtr) {
2352 		channel = (unsigned int)
2353 			(((p[0] << 8) | p[1]) * pngPtr->alpha);
2354 
2355 		*p++ = (unsigned char) (channel >> 8);
2356 		*p++ = (unsigned char) (channel & 0xff);
2357 
2358 		p += offset;
2359 	    }
2360 	} else {
2361 	    while (p < endPtr) {
2362 		p[0] = (unsigned char) (pngPtr->alpha * p[0]);
2363 		p += 1 + offset;
2364 	    }
2365 	}
2366     }
2367 }
2368 
2369 /*
2370  *----------------------------------------------------------------------
2371  *
2372  * ParseFormat --
2373  *
2374  *	This function parses the -format string that can be specified to the
2375  *	[image create photo] command to extract options for postprocessing of
2376  *	loaded images. Currently, this just allows specifying and applying an
2377  *	overall alpha value to the loaded image (for example, to make it
2378  *	entirely 50% as transparent as the actual image file).
2379  *
2380  * Results:
2381  *	TCL_OK, or TCL_ERROR if the format specification is invalid.
2382  *
2383  * Side effects:
2384  *	None
2385  *
2386  *----------------------------------------------------------------------
2387  */
2388 
2389 static int
ParseFormat(Tcl_Interp * interp,Tcl_Obj * fmtObj,PNGImage * pngPtr)2390 ParseFormat(
2391     Tcl_Interp *interp,
2392     Tcl_Obj *fmtObj,
2393     PNGImage *pngPtr)
2394 {
2395     Tcl_Obj **objv = NULL;
2396     int objc = 0;
2397     static const char *const fmtOptions[] = {
2398 	"-alpha", NULL
2399     };
2400     enum fmtOptionsEnum {
2401 	OPT_ALPHA
2402     };
2403 
2404     /*
2405      * Extract elements of format specification as a list.
2406      */
2407 
2408     if (fmtObj &&
2409 	    Tcl_ListObjGetElements(interp, fmtObj, &objc, &objv) != TCL_OK) {
2410 	return TCL_ERROR;
2411     }
2412 
2413     for (; objc>0 ; objc--, objv++) {
2414 	int optIndex;
2415 
2416 	/*
2417 	 * Ignore the "png" part of the format specification.
2418 	 */
2419 
2420 	if (!strcasecmp(Tcl_GetString(objv[0]), "png")) {
2421 	    continue;
2422 	}
2423 
2424 	if (Tcl_GetIndexFromObjStruct(interp, objv[0], fmtOptions,
2425 		sizeof(char *), "option", 0, &optIndex) == TCL_ERROR) {
2426 	    return TCL_ERROR;
2427 	}
2428 
2429 	if (objc < 2) {
2430 	    Tcl_WrongNumArgs(interp, 1, objv, "value");
2431 	    return TCL_ERROR;
2432 	}
2433 
2434 	objc--;
2435 	objv++;
2436 
2437 	switch ((enum fmtOptionsEnum) optIndex) {
2438 	case OPT_ALPHA:
2439 	    if (Tcl_GetDoubleFromObj(interp, objv[0],
2440 		    &pngPtr->alpha) == TCL_ERROR) {
2441 		return TCL_ERROR;
2442 	    }
2443 
2444 	    if ((pngPtr->alpha < 0.0) || (pngPtr->alpha > 1.0)) {
2445 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
2446 			"-alpha value must be between 0.0 and 1.0", -1));
2447 		Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_ALPHA",
2448 			NULL);
2449 		return TCL_ERROR;
2450 	    }
2451 	    break;
2452 	}
2453     }
2454 
2455     return TCL_OK;
2456 }
2457 
2458 /*
2459  *----------------------------------------------------------------------
2460  *
2461  * DecodePNG --
2462  *
2463  *	This function handles the entirety of reading a PNG file (or data)
2464  *	from the first byte to the last.
2465  *
2466  * Results:
2467  *	TCL_OK, or TCL_ERROR if an I/O error occurs or any problems are
2468  *	detected in the PNG file.
2469  *
2470  * Side effects:
2471  *	The access position in f advances. Memory may be allocated and image
2472  *	dimensions and contents may change.
2473  *
2474  *----------------------------------------------------------------------
2475  */
2476 
2477 static int
DecodePNG(Tcl_Interp * interp,PNGImage * pngPtr,Tcl_Obj * fmtObj,Tk_PhotoHandle imageHandle,int destX,int destY)2478 DecodePNG(
2479     Tcl_Interp *interp,
2480     PNGImage *pngPtr,
2481     Tcl_Obj *fmtObj,
2482     Tk_PhotoHandle imageHandle,
2483     int destX,
2484     int destY)
2485 {
2486     unsigned long chunkType;
2487     size_t chunkSz;
2488     unsigned long crc;
2489 
2490     /*
2491      * Parse the PNG signature and IHDR (header) chunk.
2492      */
2493 
2494     if (ReadIHDR(interp, pngPtr) == TCL_ERROR) {
2495 	return TCL_ERROR;
2496     }
2497 
2498     /*
2499      * Extract alpha value from -format object, if specified.
2500      */
2501 
2502     if (ParseFormat(interp, fmtObj, pngPtr) == TCL_ERROR) {
2503 	return TCL_ERROR;
2504     }
2505 
2506     /*
2507      * The next chunk may either be a PLTE (Palette) chunk or the first of at
2508      * least one IDAT (data) chunks. It could also be one of a number of
2509      * ancillary chunks, but those are skipped for us by the switch in
2510      * ReadChunkHeader().
2511      *
2512      * PLTE is mandatory for color type 3 and forbidden for 2 and 6
2513      */
2514 
2515     if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2516 	    &crc) == TCL_ERROR) {
2517 	return TCL_ERROR;
2518     }
2519 
2520     /*
2521      * Physical header may be present here so try to parse it
2522      */
2523 
2524     if (CHUNK_pHYs == chunkType) {
2525 	/*
2526 	 * Finish parsing the PHYS chunk.
2527 	 */
2528 
2529 	if (ReadPHYS(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2530 	    return TCL_ERROR;
2531 	}
2532 
2533 	/*
2534 	 * Begin the next chunk.
2535 	 */
2536 
2537 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2538 		&crc) == TCL_ERROR) {
2539 	    return TCL_ERROR;
2540 	}
2541     }
2542 
2543     if (CHUNK_PLTE == chunkType) {
2544 	/*
2545 	 * Finish parsing the PLTE chunk.
2546 	 */
2547 
2548 	if (ReadPLTE(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2549 	    return TCL_ERROR;
2550 	}
2551 
2552 	/*
2553 	 * Begin the next chunk.
2554 	 */
2555 
2556 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2557 		&crc) == TCL_ERROR) {
2558 	    return TCL_ERROR;
2559 	}
2560     } else if (PNG_COLOR_PLTE == pngPtr->colorType) {
2561 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2562 		"PLTE chunk required for indexed color", -1));
2563 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_PLTE", NULL);
2564 	return TCL_ERROR;
2565     }
2566 
2567     /*
2568      * The next chunk may be a tRNS (palette transparency) chunk, depending on
2569      * the color type. It must come after the PLTE chunk and before the IDAT
2570      * chunk, but can be present if there is no PLTE chunk because it can be
2571      * used for Grayscale and TrueColor in lieu of an alpha channel.
2572      */
2573 
2574     if (CHUNK_tRNS == chunkType) {
2575 	/*
2576 	 * Finish parsing the tRNS chunk.
2577 	 */
2578 
2579 	if (ReadTRNS(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2580 	    return TCL_ERROR;
2581 	}
2582 
2583 	/*
2584 	 * Begin the next chunk.
2585 	 */
2586 
2587 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2588 		&crc) == TCL_ERROR) {
2589 	    return TCL_ERROR;
2590 	}
2591     }
2592 
2593     /*
2594      * Physical header may be present here so try to parse it
2595      */
2596 
2597     if (CHUNK_pHYs == chunkType) {
2598 	/*
2599 	 * Finish parsing the PHYS chunk.
2600 	 */
2601 
2602 	if (ReadPHYS(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2603 	    return TCL_ERROR;
2604 	}
2605 
2606 	/*
2607 	 * Begin the next chunk.
2608 	 */
2609 
2610 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2611 		&crc) == TCL_ERROR) {
2612 	    return TCL_ERROR;
2613 	}
2614     }
2615 
2616     /*
2617      * Other ancillary chunk types could appear here, but for now we're only
2618      * interested in IDAT. The others should have been skipped.
2619      */
2620 
2621     if (chunkType != CHUNK_IDAT) {
2622 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2623 		"at least one IDAT chunk is required", -1));
2624 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "NEED_IDAT", NULL);
2625 	return TCL_ERROR;
2626     }
2627 
2628     /*
2629      * Expand the photo size (if not set by the user) to provide enough space
2630      * for the image being parsed. It does not matter if width or height wrap
2631      * to negative here: Tk will not shrink the image.
2632      */
2633 
2634     if (Tk_PhotoExpand(interp, imageHandle, destX + pngPtr->block.width,
2635 	    destY + pngPtr->block.height) == TCL_ERROR) {
2636 	return TCL_ERROR;
2637     }
2638 
2639     /*
2640      * A scan line consists of one byte for a filter type, plus the number of
2641      * bits per color sample times the number of color samples per pixel.
2642      */
2643 
2644     if (pngPtr->block.width > ((INT_MAX - 1) / (pngPtr->numChannels * 2))) {
2645 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2646 		"line size is out of supported range on this architecture",
2647 		-1));
2648 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "LINE_SIZE", NULL);
2649 	return TCL_ERROR;
2650     }
2651 
2652     if (16 == pngPtr->bitDepth) {
2653 	pngPtr->lineSize = 1 + (pngPtr->numChannels * pngPtr->block.width*2);
2654     } else {
2655 	pngPtr->lineSize = 1 + ((pngPtr->numChannels * pngPtr->block.width) /
2656 		(8 / pngPtr->bitDepth));
2657 	if (pngPtr->block.width % (8 / pngPtr->bitDepth)) {
2658 	    pngPtr->lineSize++;
2659 	}
2660     }
2661 
2662     /*
2663      * Allocate space for decoding the scan lines.
2664      */
2665 
2666     pngPtr->lastLineObj = Tcl_NewObj();
2667     Tcl_IncrRefCount(pngPtr->lastLineObj);
2668     pngPtr->thisLineObj = Tcl_NewObj();
2669     Tcl_IncrRefCount(pngPtr->thisLineObj);
2670 
2671     pngPtr->block.pixelPtr = (unsigned char *)attemptckalloc(pngPtr->blockLen);
2672     if (!pngPtr->block.pixelPtr) {
2673 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2674 		"memory allocation failed", -1));
2675 	Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL);
2676 	return TCL_ERROR;
2677     }
2678 
2679     /*
2680      * Determine size of the first phase if interlaced. Phase size should
2681      * always be <= line size, so probably not necessary to check for
2682      * arithmetic overflow here: should be covered by line size check.
2683      */
2684 
2685     if (pngPtr->interlace) {
2686 	/*
2687 	 * Only one pixel per block of 8 per line in the first phase.
2688 	 */
2689 
2690 	unsigned int pixels = (pngPtr->block.width + 7) >> 3;
2691 
2692 	pngPtr->phase = 1;
2693 	if (16 == pngPtr->bitDepth) {
2694 	    pngPtr->phaseSize = 1 + pngPtr->numChannels*pixels*2;
2695 	} else {
2696 	    pngPtr->phaseSize = 1 +
2697 		    ((pngPtr->numChannels*pixels*pngPtr->bitDepth + 7) >> 3);
2698 	}
2699     } else {
2700 	pngPtr->phaseSize = pngPtr->lineSize;
2701     }
2702 
2703     /*
2704      * All of the IDAT (data) chunks must be consecutive.
2705      */
2706 
2707     while (CHUNK_IDAT == chunkType) {
2708 	if (ReadIDAT(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2709 	    return TCL_ERROR;
2710 	}
2711 
2712 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2713 		&crc) == TCL_ERROR) {
2714 	    return TCL_ERROR;
2715 	}
2716     }
2717 
2718     /*
2719      * Ensure that we've got to the end of the compressed stream now that
2720      * there are no more IDAT segments. This sanity check is enforced by most
2721      * PNG readers.
2722      */
2723 
2724     if (!Tcl_ZlibStreamEof(pngPtr->stream)) {
2725 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2726 		"unfinalized data stream in PNG data", -1));
2727 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "EXTRA_DATA", NULL);
2728 	return TCL_ERROR;
2729     }
2730 
2731     /*
2732      * Now skip the remaining chunks which we're also not interested in.
2733      */
2734 
2735     while (CHUNK_IEND != chunkType) {
2736 	if (SkipChunk(interp, pngPtr, chunkSz, crc) == TCL_ERROR) {
2737 	    return TCL_ERROR;
2738 	}
2739 
2740 	if (ReadChunkHeader(interp, pngPtr, &chunkSz, &chunkType,
2741 		&crc) == TCL_ERROR) {
2742 	    return TCL_ERROR;
2743 	}
2744     }
2745 
2746     /*
2747      * Got the IEND (end of image) chunk. Do some final checks...
2748      */
2749 
2750     if (chunkSz) {
2751 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2752 		"IEND chunk contents must be empty", -1));
2753 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL);
2754 	return TCL_ERROR;
2755     }
2756 
2757     /*
2758      * Check the CRC on the IEND chunk.
2759      */
2760 
2761     if (CheckCRC(interp, pngPtr, crc) == TCL_ERROR) {
2762 	return TCL_ERROR;
2763     }
2764 
2765     /*
2766      * TODO: verify that nothing else comes after the IEND chunk, or do we
2767      * really care?
2768      */
2769 
2770 #if 0
2771     if (ReadData(interp, pngPtr, &c, 1, NULL) != TCL_ERROR) {
2772 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
2773 		"extra data following IEND chunk", -1));
2774 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "BAD_IEND", NULL);
2775 	return TCL_ERROR;
2776     }
2777 #endif
2778 
2779     /*
2780      * Apply overall image alpha if specified.
2781      */
2782 
2783     ApplyAlpha(pngPtr);
2784 
2785     /*
2786      * Copy the decoded image block into the Tk photo image.
2787      */
2788 
2789     if (Tk_PhotoPutBlock(interp, imageHandle, &pngPtr->block, destX, destY,
2790 	    pngPtr->block.width, pngPtr->block.height,
2791 	    TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
2792 	return TCL_ERROR;
2793     }
2794 
2795     return TCL_OK;
2796 }
2797 
2798 /*
2799  *----------------------------------------------------------------------
2800  *
2801  * FileMatchPNG --
2802  *
2803  *	This function is invoked by the photo image type to see if a file
2804  *	contains image data in PNG format.
2805  *
2806  * Results:
2807  *	The return value is 1 if the first characters in file f look like PNG
2808  *	data, and 0 otherwise.
2809  *
2810  * Side effects:
2811  *	The access position in f may change.
2812  *
2813  *----------------------------------------------------------------------
2814  */
2815 
2816 static int
FileMatchPNG(Tcl_Interp * interp,Tcl_Channel chan,TCL_UNUSED (const char *),TCL_UNUSED (Tcl_Obj *),TCL_UNUSED (Tcl_Obj *),int * widthPtr,int * heightPtr,TCL_UNUSED (Tcl_Obj *))2817 FileMatchPNG(
2818     Tcl_Interp *interp,		/* Interpreter to use for reporting errors. */
2819     Tcl_Channel chan,		/* The image file, open for reading. */
2820     TCL_UNUSED(const char *),	/* The name of the image file. */
2821     TCL_UNUSED(Tcl_Obj *),	/* User-specified format object, or NULL. */
2822     TCL_UNUSED(Tcl_Obj *),	/* metadata input, may be NULL */
2823     int *widthPtr, int *heightPtr,
2824 				/* The dimensions of the image are returned
2825 				 * here if the file is a valid raw GIF file. */
2826     TCL_UNUSED(Tcl_Obj *))	/* metadata return dict, may be NULL */
2827 {
2828     PNGImage png;
2829     int match = 0;
2830 
2831     InitPNGImage(NULL, &png, chan, NULL, TCL_ZLIB_STREAM_INFLATE);
2832 
2833     if (ReadIHDR(interp, &png) == TCL_OK) {
2834 	*widthPtr = png.block.width;
2835 	*heightPtr = png.block.height;
2836 	match = 1;
2837     }
2838 
2839     CleanupPNGImage(&png);
2840 
2841     return match;
2842 }
2843 
2844 /*
2845  *----------------------------------------------------------------------
2846  *
2847  * FileReadPNG --
2848  *
2849  *	This function is called by the photo image type to read PNG format
2850  *	data from a file and write it into a given photo image.
2851  *
2852  * Results:
2853  *	A standard TCL completion code. If TCL_ERROR is returned then an error
2854  *	message is left in the interp's result.
2855  *
2856  * Side effects:
2857  *	The access position in file f is changed, and new data is added to the
2858  *	image given by imageHandle.
2859  *
2860  *----------------------------------------------------------------------
2861  */
2862 
2863 static int
FileReadPNG(Tcl_Interp * interp,Tcl_Channel chan,TCL_UNUSED (const char *),Tcl_Obj * fmtObj,TCL_UNUSED (Tcl_Obj *),Tk_PhotoHandle imageHandle,int destX,int destY,TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),Tcl_Obj * metadataOutObj)2864 FileReadPNG(
2865     Tcl_Interp *interp,		/* Interpreter to use for reporting errors. */
2866     Tcl_Channel chan,		/* The image file, open for reading. */
2867     TCL_UNUSED(const char *),	/* The name of the image file. */
2868     Tcl_Obj *fmtObj,		/* User-specified format object, or NULL. */
2869     TCL_UNUSED(Tcl_Obj *),	/* metadata input, may be NULL */
2870     Tk_PhotoHandle imageHandle,	/* The photo image to write into. */
2871     int destX, int destY,	/* Coordinates of top-left pixel in photo
2872 				 * image to be written to. */
2873     TCL_UNUSED(int),		/* Dimensions of block of photo image to be
2874 				 * written to. */
2875     TCL_UNUSED(int),
2876     TCL_UNUSED(int),		/* Coordinates of top-left pixel to be used in
2877 				 * image being read. */
2878     TCL_UNUSED(int),
2879     Tcl_Obj *metadataOutObj)	/* metadata return dict, may be NULL */
2880 {
2881     PNGImage png;
2882     int result = TCL_ERROR;
2883 
2884     result = InitPNGImage(interp, &png, chan, NULL, TCL_ZLIB_STREAM_INFLATE);
2885 
2886     if (TCL_OK == result) {
2887 	result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY);
2888     }
2889 
2890     if (TCL_OK == result && metadataOutObj != NULL && png.DPI != -1) {
2891 	result = Tcl_DictObjPut(NULL, metadataOutObj,
2892 		Tcl_NewStringObj("DPI",-1),
2893 		Tcl_NewDoubleObj(png.DPI));
2894     }
2895 
2896     if (TCL_OK == result && metadataOutObj != NULL && png.aspect != -1) {
2897 	result = Tcl_DictObjPut(NULL, metadataOutObj,
2898 		Tcl_NewStringObj("aspect",-1),
2899 		Tcl_NewDoubleObj(png.aspect));
2900     }
2901 
2902     CleanupPNGImage(&png);
2903     return result;
2904 }
2905 
2906 /*
2907  *----------------------------------------------------------------------
2908  *
2909  * StringMatchPNG --
2910  *
2911  *	This function is invoked by the photo image type to see if an object
2912  *	contains image data in PNG format.
2913  *
2914  * Results:
2915  *	The return value is 1 if the first characters in the data are like PNG
2916  *	data, and 0 otherwise.
2917  *
2918  * Side effects:
2919  *	The size of the image is placed in widthPre and heightPtr.
2920  *
2921  *----------------------------------------------------------------------
2922  */
2923 
2924 static int
StringMatchPNG(Tcl_Interp * interp,Tcl_Obj * pObjData,TCL_UNUSED (Tcl_Obj *),TCL_UNUSED (Tcl_Obj *),int * widthPtr,int * heightPtr,TCL_UNUSED (Tcl_Obj *))2925 StringMatchPNG(
2926     Tcl_Interp *interp,		/* Interpreter to use for reporting errors. */
2927     Tcl_Obj *pObjData,		/* the object containing the image data */
2928     TCL_UNUSED(Tcl_Obj *),	/* the image format object, or NULL */
2929     TCL_UNUSED(Tcl_Obj *),	/* metadata input, may be NULL */
2930     int *widthPtr,		/* where to put the string width */
2931     int *heightPtr,		/* where to put the string height */
2932     TCL_UNUSED(Tcl_Obj *))	/* metadata return dict, may be NULL */
2933 {
2934     PNGImage png;
2935     int match = 0;
2936 
2937     InitPNGImage(NULL, &png, NULL, pObjData, TCL_ZLIB_STREAM_INFLATE);
2938 
2939     png.strDataBuf = Tcl_GetByteArrayFromObj(pObjData, &png.strDataLen);
2940 
2941     if (ReadIHDR(interp, &png) == TCL_OK) {
2942 	*widthPtr = png.block.width;
2943 	*heightPtr = png.block.height;
2944 	match = 1;
2945     }
2946 
2947     CleanupPNGImage(&png);
2948     return match;
2949 }
2950 
2951 /*
2952  *----------------------------------------------------------------------
2953  *
2954  * StringReadPNG --
2955  *
2956  *	This function is called by the photo image type to read PNG format
2957  *	data from an object and give it to the photo image.
2958  *
2959  * Results:
2960  *	A standard TCL completion code. If TCL_ERROR is returned then an error
2961  *	message is left in the interp's result.
2962  *
2963  * Side effects:
2964  *	New data is added to the image given by imageHandle.
2965  *
2966  *----------------------------------------------------------------------
2967  */
2968 
2969 static int
StringReadPNG(Tcl_Interp * interp,Tcl_Obj * pObjData,Tcl_Obj * fmtObj,TCL_UNUSED (Tcl_Obj *),Tk_PhotoHandle imageHandle,int destX,int destY,TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),Tcl_Obj * metadataOutObj)2970 StringReadPNG(
2971     Tcl_Interp *interp,		/* interpreter for reporting errors in */
2972     Tcl_Obj *pObjData,		/* object containing the image */
2973     Tcl_Obj *fmtObj,		/* format object, or NULL */
2974     TCL_UNUSED(Tcl_Obj *),	/* metadata input, may be NULL */
2975     Tk_PhotoHandle imageHandle,	/* the image to write this data into */
2976     int destX, int destY,	/* The rectangular region of the */
2977     TCL_UNUSED(int),		/* image to copy */
2978     TCL_UNUSED(int),
2979     TCL_UNUSED(int),
2980     TCL_UNUSED(int),
2981     Tcl_Obj *metadataOutObj)	/* metadata return dict, may be NULL */
2982 {
2983     PNGImage png;
2984     int result = TCL_ERROR;
2985 
2986     result = InitPNGImage(interp, &png, NULL, pObjData,
2987 	    TCL_ZLIB_STREAM_INFLATE);
2988 
2989     if (TCL_OK == result) {
2990 	result = DecodePNG(interp, &png, fmtObj, imageHandle, destX, destY);
2991     }
2992 
2993     if (TCL_OK == result && metadataOutObj != NULL && png.DPI != -1) {
2994 	result = Tcl_DictObjPut(NULL, metadataOutObj,
2995 		Tcl_NewStringObj("DPI",-1),
2996 		Tcl_NewDoubleObj(png.DPI));
2997     }
2998 
2999     if (TCL_OK == result && metadataOutObj != NULL && png.aspect != -1) {
3000 	result = Tcl_DictObjPut(NULL, metadataOutObj,
3001 		Tcl_NewStringObj("aspect",-1),
3002 		Tcl_NewDoubleObj(png.aspect));
3003     }
3004 
3005     CleanupPNGImage(&png);
3006     return result;
3007 }
3008 
3009 /*
3010  *----------------------------------------------------------------------
3011  *
3012  * WriteData --
3013  *
3014  *	This function writes a bytes from a buffer out to the PNG image.
3015  *
3016  * Results:
3017  *	TCL_OK, or TCL_ERROR if the write fails.
3018  *
3019  * Side effects:
3020  *	File or buffer will be modified.
3021  *
3022  *----------------------------------------------------------------------
3023  */
3024 
3025 static int
WriteData(Tcl_Interp * interp,PNGImage * pngPtr,const unsigned char * srcPtr,size_t srcSz,unsigned long * crcPtr)3026 WriteData(
3027     Tcl_Interp *interp,
3028     PNGImage *pngPtr,
3029     const unsigned char *srcPtr,
3030     size_t srcSz,
3031     unsigned long *crcPtr)
3032 {
3033     if (!srcPtr || !srcSz) {
3034 	return TCL_OK;
3035     }
3036 
3037     if (crcPtr) {
3038 	*crcPtr = Tcl_ZlibCRC32(*crcPtr, srcPtr, srcSz);
3039     }
3040 
3041     /*
3042      * TODO: is Tcl_AppendObjToObj faster here? i.e., does Tcl join the
3043      * objects immediately or store them in a multi-object rep?
3044      */
3045 
3046     if (pngPtr->objDataPtr) {
3047 	TkSizeT objSz;
3048 	unsigned char *destPtr;
3049 
3050 	Tcl_GetByteArrayFromObj(pngPtr->objDataPtr, &objSz);
3051 
3052 	if (objSz + srcSz > INT_MAX) {
3053 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
3054 		    "image too large to store completely in byte array", -1));
3055 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL);
3056 	    return TCL_ERROR;
3057 	}
3058 
3059 	destPtr = Tcl_SetByteArrayLength(pngPtr->objDataPtr, objSz + srcSz);
3060 
3061 	if (!destPtr) {
3062 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
3063 		    "memory allocation failed", -1));
3064 	    Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL);
3065 	    return TCL_ERROR;
3066 	}
3067 
3068 	memcpy(destPtr+objSz, srcPtr, srcSz);
3069     } else if (Tcl_Write(pngPtr->channel, (const char *) srcPtr, srcSz) == TCL_IO_FAILURE) {
3070 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
3071 		"write to channel failed: %s", Tcl_PosixError(interp)));
3072 	return TCL_ERROR;
3073     }
3074 
3075     return TCL_OK;
3076 }
3077 
3078 static inline int
WriteByte(Tcl_Interp * interp,PNGImage * pngPtr,unsigned char c,unsigned long * crcPtr)3079 WriteByte(
3080     Tcl_Interp *interp,
3081     PNGImage *pngPtr,
3082     unsigned char c,
3083     unsigned long *crcPtr)
3084 {
3085     return WriteData(interp, pngPtr, &c, 1, crcPtr);
3086 }
3087 
3088 /*
3089  *----------------------------------------------------------------------
3090  *
3091  * LongToInt32 --
3092  *
3093  *	This function transforms to a 32-bit integer value as
3094  *	four bytes in network byte order.
3095  *
3096  * Results:
3097  *	None
3098  *
3099  * Side effects:
3100  *	Buffer will be modified.
3101  *
3102  *----------------------------------------------------------------------
3103  */
3104 
3105 static inline void
LongToInt32(unsigned long l,unsigned char * pc)3106 LongToInt32(
3107     unsigned long l,
3108     unsigned char *pc)
3109 {
3110     pc[0] = (unsigned char) ((l & 0xff000000) >> 24);
3111     pc[1] = (unsigned char) ((l & 0x00ff0000) >> 16);
3112     pc[2] = (unsigned char) ((l & 0x0000ff00) >> 8);
3113     pc[3] = (unsigned char) ((l & 0x000000ff) >> 0);
3114 }
3115 
3116 /*
3117  *----------------------------------------------------------------------
3118  *
3119  * WriteInt32 --
3120  *
3121  *	This function writes a 32-bit integer value out to the PNG image as
3122  *	four bytes in network byte order.
3123  *
3124  * Results:
3125  *	TCL_OK, or TCL_ERROR if the write fails.
3126  *
3127  * Side effects:
3128  *	File or buffer will be modified.
3129  *
3130  *----------------------------------------------------------------------
3131  */
3132 
3133 static inline int
WriteInt32(Tcl_Interp * interp,PNGImage * pngPtr,unsigned long l,unsigned long * crcPtr)3134 WriteInt32(
3135     Tcl_Interp *interp,
3136     PNGImage *pngPtr,
3137     unsigned long l,
3138     unsigned long *crcPtr)
3139 {
3140     unsigned char pc[4];
3141     LongToInt32(l,pc);
3142     return WriteData(interp, pngPtr, pc, 4, crcPtr);
3143 }
3144 
3145 /*
3146  *----------------------------------------------------------------------
3147  *
3148  * WriteChunk --
3149  *
3150  *	Writes a complete chunk to the PNG image, including chunk type,
3151  *	length, contents, and CRC.
3152  *
3153  * Results:
3154  *	TCL_OK, or TCL_ERROR if the write fails.
3155  *
3156  * Side effects:
3157  *	None
3158  *
3159  *----------------------------------------------------------------------
3160  */
3161 
3162 static inline int
WriteChunk(Tcl_Interp * interp,PNGImage * pngPtr,unsigned long chunkType,const unsigned char * dataPtr,size_t dataSize)3163 WriteChunk(
3164     Tcl_Interp *interp,
3165     PNGImage *pngPtr,
3166     unsigned long chunkType,
3167     const unsigned char *dataPtr,
3168     size_t dataSize)
3169 {
3170     unsigned long crc = Tcl_ZlibCRC32(0, NULL, 0);
3171     int result = TCL_OK;
3172 
3173     /*
3174      * Write the length field for the chunk.
3175      */
3176 
3177     result = WriteInt32(interp, pngPtr, dataSize, NULL);
3178 
3179     /*
3180      * Write the Chunk Type.
3181      */
3182 
3183     if (TCL_OK == result) {
3184 	result = WriteInt32(interp, pngPtr, chunkType, &crc);
3185     }
3186 
3187     /*
3188      * Write the contents (if any).
3189      */
3190 
3191     if (TCL_OK == result) {
3192 	result = WriteData(interp, pngPtr, dataPtr, dataSize, &crc);
3193     }
3194 
3195     /*
3196      * Write out the CRC at the end of the chunk.
3197      */
3198 
3199     if (TCL_OK == result) {
3200 	result = WriteInt32(interp, pngPtr, crc, NULL);
3201     }
3202 
3203     return result;
3204 }
3205 
3206 /*
3207  *----------------------------------------------------------------------
3208  *
3209  * WriteIHDR --
3210  *
3211  *	This function writes the PNG header at the beginning of a PNG file,
3212  *	which includes information such as dimensions and color type.
3213  *
3214  * Results:
3215  *	TCL_OK, or TCL_ERROR if the write fails.
3216  *
3217  * Side effects:
3218  *	File or buffer will be modified.
3219  *
3220  *----------------------------------------------------------------------
3221  */
3222 
3223 static int
WriteIHDR(Tcl_Interp * interp,PNGImage * pngPtr,Tk_PhotoImageBlock * blockPtr)3224 WriteIHDR(
3225     Tcl_Interp *interp,
3226     PNGImage *pngPtr,
3227     Tk_PhotoImageBlock *blockPtr)
3228 {
3229     unsigned long crc = Tcl_ZlibCRC32(0, NULL, 0);
3230     int result = TCL_OK;
3231 
3232     /*
3233      * The IHDR (header) chunk has a fixed size of 13 bytes.
3234      */
3235 
3236     result = WriteInt32(interp, pngPtr, 13, NULL);
3237 
3238     /*
3239      * Write the IHDR Chunk Type.
3240      */
3241 
3242     if (TCL_OK == result) {
3243 	result = WriteInt32(interp, pngPtr, CHUNK_IHDR, &crc);
3244     }
3245 
3246     /*
3247      * Write the image width, height.
3248      */
3249 
3250     if (TCL_OK == result) {
3251 	result = WriteInt32(interp, pngPtr, (unsigned long) blockPtr->width,
3252 		&crc);
3253     }
3254 
3255     if (TCL_OK == result) {
3256 	result = WriteInt32(interp, pngPtr, (unsigned long) blockPtr->height,
3257 		&crc);
3258     }
3259 
3260     /*
3261      * Write bit depth. Although the PNG format supports 16 bits per channel,
3262      * Tk supports only 8 in the internal representation, which blockPtr
3263      * points to.
3264      */
3265 
3266     if (TCL_OK == result) {
3267 	result = WriteByte(interp, pngPtr, 8, &crc);
3268     }
3269 
3270     /*
3271      * Write out the color type, previously determined.
3272      */
3273 
3274     if (TCL_OK == result) {
3275 	result = WriteByte(interp, pngPtr, pngPtr->colorType, &crc);
3276     }
3277 
3278     /*
3279      * Write compression method (only one method is defined).
3280      */
3281 
3282     if (TCL_OK == result) {
3283 	result = WriteByte(interp, pngPtr, PNG_COMPRESS_DEFLATE, &crc);
3284     }
3285 
3286     /*
3287      * Write filter method (only one method is defined).
3288      */
3289 
3290     if (TCL_OK == result) {
3291 	result = WriteByte(interp, pngPtr, PNG_FILTMETH_STANDARD, &crc);
3292     }
3293 
3294     /*
3295      * Write interlace method as not interlaced.
3296      *
3297      * TODO: support interlace through -format?
3298      */
3299 
3300     if (TCL_OK == result) {
3301 	result = WriteByte(interp, pngPtr, PNG_INTERLACE_NONE, &crc);
3302     }
3303 
3304     /*
3305      * Write out the CRC at the end of the chunk.
3306      */
3307 
3308     if (TCL_OK == result) {
3309 	result = WriteInt32(interp, pngPtr, crc, NULL);
3310     }
3311 
3312     return result;
3313 }
3314 
3315 /*
3316  *----------------------------------------------------------------------
3317  *
3318  * WriteIDAT --
3319  *
3320  *	Writes the IDAT (data) chunk to the PNG image, containing the pixel
3321  *	channel data. Currently, image lines are not filtered and writing
3322  *	interlaced pixels is not supported.
3323  *
3324  * Results:
3325  *	TCL_OK, or TCL_ERROR if the write fails.
3326  *
3327  * Side effects:
3328  *	None
3329  *
3330  *----------------------------------------------------------------------
3331  */
3332 
3333 static int
WriteIDAT(Tcl_Interp * interp,PNGImage * pngPtr,Tk_PhotoImageBlock * blockPtr)3334 WriteIDAT(
3335     Tcl_Interp *interp,
3336     PNGImage *pngPtr,
3337     Tk_PhotoImageBlock *blockPtr)
3338 {
3339     int rowNum, flush = TCL_ZLIB_NO_FLUSH, result;
3340     Tcl_Obj *outputObj;
3341     unsigned char *outputBytes;
3342     TkSizeT outputSize;
3343 
3344     /*
3345      * Filter and compress each row one at a time.
3346      */
3347 
3348     for (rowNum=0 ; rowNum < blockPtr->height ; rowNum++) {
3349 	int colNum;
3350 	unsigned char *srcPtr, *destPtr;
3351 
3352 	srcPtr = blockPtr->pixelPtr + (rowNum * blockPtr->pitch);
3353 	destPtr = Tcl_SetByteArrayLength(pngPtr->thisLineObj,
3354 		pngPtr->lineSize);
3355 
3356 	/*
3357 	 * TODO: use Paeth filtering.
3358 	 */
3359 
3360 	*destPtr++ = PNG_FILTER_NONE;
3361 
3362 	/*
3363 	 * Copy each pixel into the destination buffer after the filter type
3364 	 * before filtering.
3365 	 */
3366 
3367 	for (colNum = 0 ; colNum < blockPtr->width ; colNum++) {
3368 	    /*
3369 	     * Copy red or gray channel.
3370 	     */
3371 
3372 	    *destPtr++ = srcPtr[blockPtr->offset[0]];
3373 
3374 	    /*
3375 	     * If not grayscale, copy the green and blue channels.
3376 	     */
3377 
3378 	    if (pngPtr->colorType & PNG_COLOR_USED) {
3379 		*destPtr++ = srcPtr[blockPtr->offset[1]];
3380 		*destPtr++ = srcPtr[blockPtr->offset[2]];
3381 	    }
3382 
3383 	    /*
3384 	     * Copy the alpha channel, if used.
3385 	     */
3386 
3387 	    if (pngPtr->colorType & PNG_COLOR_ALPHA) {
3388 		*destPtr++ = srcPtr[blockPtr->offset[3]];
3389 	    }
3390 
3391 	    /*
3392 	     * Point to the start of the next pixel.
3393 	     */
3394 
3395 	    srcPtr += blockPtr->pixelSize;
3396 	}
3397 
3398 	/*
3399 	 * Compress the line of pixels into the destination. If this is the
3400 	 * last line, finalize the compressor at the same time. Note that this
3401 	 * can't be just a flush; that leads to a file that some PNG readers
3402 	 * choke on. [Bug 2984787]
3403 	 */
3404 
3405 	if (rowNum + 1 == blockPtr->height) {
3406 	    flush = TCL_ZLIB_FINALIZE;
3407 	}
3408 	if (Tcl_ZlibStreamPut(pngPtr->stream, pngPtr->thisLineObj,
3409 		flush) != TCL_OK) {
3410 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
3411 		    "deflate() returned error", -1));
3412 	    Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "DEFLATE", NULL);
3413 	    return TCL_ERROR;
3414 	}
3415 
3416 	/*
3417 	 * Swap line buffers to keep the last around for filtering next.
3418 	 */
3419 
3420 	{
3421 	    Tcl_Obj *temp = pngPtr->lastLineObj;
3422 
3423 	    pngPtr->lastLineObj = pngPtr->thisLineObj;
3424 	    pngPtr->thisLineObj = temp;
3425 	}
3426     }
3427 
3428     /*
3429      * Now get the compressed data and write it as one big IDAT chunk.
3430      */
3431 
3432     outputObj = Tcl_NewObj();
3433     (void) Tcl_ZlibStreamGet(pngPtr->stream, outputObj, -1);
3434     outputBytes = Tcl_GetByteArrayFromObj(outputObj, &outputSize);
3435     result = WriteChunk(interp, pngPtr, CHUNK_IDAT, outputBytes, outputSize);
3436     Tcl_DecrRefCount(outputObj);
3437     return result;
3438 }
3439 
3440 /*
3441  *----------------------------------------------------------------------
3442  *
3443  * WriteExtraChunks --
3444  *
3445  *	Writes an sBIT and a tEXt chunks to the PNG image, describing a bunch
3446  *	of not very important metadata that many readers seem to need anyway.
3447  *
3448  * Results:
3449  *	TCL_OK, or TCL_ERROR if the write fails.
3450  *
3451  * Side effects:
3452  *	None
3453  *
3454  *----------------------------------------------------------------------
3455  */
3456 
3457 static int
WriteExtraChunks(Tcl_Interp * interp,PNGImage * pngPtr,Tcl_Obj * metadataInObj)3458 WriteExtraChunks(
3459     Tcl_Interp *interp,
3460     PNGImage *pngPtr,
3461     Tcl_Obj *metadataInObj)
3462 {
3463     static const unsigned char sBIT_contents[] = {
3464 	8, 8, 8, 8
3465     };
3466     int sBIT_length = 4;
3467     Tcl_DString buf;
3468 
3469     /*
3470      * Each byte of each channel is always significant; we always write RGBA
3471      * images with 8 bits per channel as that is what the photo image's basic
3472      * data model is.
3473      */
3474 
3475     switch (pngPtr->colorType) {
3476     case PNG_COLOR_GRAY:
3477 	sBIT_length = 1;
3478 	break;
3479     case PNG_COLOR_GRAYALPHA:
3480 	sBIT_length = 2;
3481 	break;
3482     case PNG_COLOR_RGB:
3483     case PNG_COLOR_PLTE:
3484 	sBIT_length = 3;
3485 	break;
3486     case PNG_COLOR_RGBA:
3487 	sBIT_length = 4;
3488 	break;
3489     }
3490     if (WriteChunk(interp, pngPtr, CHUNK_sBIT, sBIT_contents, sBIT_length)
3491 	    != TCL_OK) {
3492 	return TCL_ERROR;
3493     }
3494 
3495     /*
3496      * Say that it is Tk that made the PNG. Note that we *need* the NUL at the
3497      * end of "Software" to be transferred; do *not* change the length
3498      * parameter to -1 there!
3499      */
3500 
3501     Tcl_DStringInit(&buf);
3502     Tcl_DStringAppend(&buf, "Software", 9);
3503     Tcl_DStringAppend(&buf, "Tk Toolkit v", -1);
3504     Tcl_DStringAppend(&buf, TK_PATCH_LEVEL, -1);
3505     if (WriteChunk(interp, pngPtr, CHUNK_tEXt,
3506 	    (unsigned char *) Tcl_DStringValue(&buf),
3507 	    Tcl_DStringLength(&buf)) != TCL_OK) {
3508 	Tcl_DStringFree(&buf);
3509 	return TCL_ERROR;
3510     }
3511     Tcl_DStringFree(&buf);
3512 
3513     /*
3514      * Add a pHYs chunk if there is metadata for DPI and/or aspect
3515      * aspect = PPUy / PPUx
3516      * DPI = PPUx * 0.0254
3517      * The physical chunk consists of:
3518      * - Points per meter in x direction (32 bit)
3519      * - Points per meter in x direction (32 bit)
3520      * - Unit specifier: 0: no unit (only aspect), 1: Points per meter
3521      */
3522 
3523     if (metadataInObj != NULL) {
3524 
3525 	Tcl_Obj *aspectObj, *DPIObj;
3526 	double aspectValue=-1, DPIValue=-1;
3527 	unsigned long PPUx = 65536, PPUy = 65536;
3528 	char unitSpecifier;
3529 
3530 	if (TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj,
3531 		Tcl_NewStringObj("aspect",-1),
3532 		&aspectObj) ||
3533 	    TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj,
3534 		Tcl_NewStringObj("DPI",-1),
3535 		&DPIObj) ) {
3536 	    return TCL_ERROR;
3537 	}
3538 	if (DPIObj != NULL) {
3539 	    if (TCL_ERROR == Tcl_GetDoubleFromObj(interp, DPIObj, &DPIValue))
3540 	    {
3541 		return TCL_ERROR;
3542 	    }
3543 	    PPUx = (unsigned long)floor(DPIValue / 0.0254+0.5);
3544 	    if (aspectObj == NULL) {
3545 		PPUy = PPUx;
3546 	    }
3547 	    unitSpecifier = 1;
3548 	}
3549 	if (aspectObj != NULL) {
3550 	    if (TCL_ERROR == Tcl_GetDoubleFromObj(interp, aspectObj,
3551 		    &aspectValue)) {
3552 		return TCL_ERROR;
3553 	    }
3554 
3555 	    /*
3556 	     * aspect = PPUy / PPUx
3557 	     */
3558 
3559 	    if (DPIObj == NULL) {
3560 		unitSpecifier = 0;
3561 		PPUx = 65536;
3562 		PPUy = (unsigned long)floor(65536.0 * aspectValue+0.5);
3563 	    } else {
3564 		PPUy = (unsigned long)floor(DPIValue * aspectValue / 0.0254+0.5);
3565 	    }
3566 	}
3567 	if (DPIObj != NULL || aspectObj != NULL) {
3568 	    unsigned char buffer[9];
3569 
3570 	    if ( PPUx > 2147483647 || PPUy > 2147483647 ) {
3571 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
3572 			"DPI or aspect out of range", -1));
3573 		Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "PHYS", NULL);
3574 		return TCL_ERROR;
3575 	    }
3576 
3577 	    LongToInt32(PPUx, buffer);
3578 	    LongToInt32(PPUy, buffer+4);
3579 	    buffer[8] = unitSpecifier;
3580 	    if (WriteChunk(interp, pngPtr, CHUNK_pHYs, buffer, 9)
3581 		    != TCL_OK) {
3582 		return TCL_ERROR;
3583 	    }
3584 	}
3585     }
3586     return TCL_OK;
3587 }
3588 
3589 /*
3590  *----------------------------------------------------------------------
3591  *
3592  * EncodePNG --
3593  *
3594  *	This function handles the entirety of writing a PNG file (or data)
3595  *	from the first byte to the last. No effort is made to optimize the
3596  *	image data for best compression.
3597  *
3598  * Results:
3599  *	TCL_OK, or TCL_ERROR if an I/O or memory error occurs.
3600  *
3601  * Side effects:
3602  *	None
3603  *
3604  *----------------------------------------------------------------------
3605  */
3606 
3607 static int
EncodePNG(Tcl_Interp * interp,Tk_PhotoImageBlock * blockPtr,PNGImage * pngPtr,Tcl_Obj * metadataInObj)3608 EncodePNG(
3609     Tcl_Interp *interp,
3610     Tk_PhotoImageBlock *blockPtr,
3611     PNGImage *pngPtr,
3612     Tcl_Obj *metadataInObj)
3613 {
3614     int greenOffset, blueOffset, alphaOffset;
3615 
3616     /*
3617      * Determine appropriate color type based on color usage (e.g., only red
3618      * and maybe alpha channel = grayscale).
3619      *
3620      * TODO: Check whether this is doing any good; Tk might just be pushing
3621      * full RGBA data all the time through here, even though the actual image
3622      * doesn't need it...
3623      */
3624 
3625     greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
3626     blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
3627     alphaOffset = blockPtr->offset[3];
3628     if ((alphaOffset >= blockPtr->pixelSize) || (alphaOffset < 0)) {
3629 	alphaOffset = 0;
3630     } else {
3631 	alphaOffset -= blockPtr->offset[0];
3632     }
3633 
3634     if ((greenOffset != 0) || (blueOffset != 0)) {
3635 	if (alphaOffset) {
3636 	    pngPtr->colorType = PNG_COLOR_RGBA;
3637 	    pngPtr->bytesPerPixel = 4;
3638 	} else {
3639 	    pngPtr->colorType = PNG_COLOR_RGB;
3640 	    pngPtr->bytesPerPixel = 3;
3641 	}
3642     } else {
3643 	if (alphaOffset) {
3644 	    pngPtr->colorType = PNG_COLOR_GRAYALPHA;
3645 	    pngPtr->bytesPerPixel = 2;
3646 	} else {
3647 	    pngPtr->colorType = PNG_COLOR_GRAY;
3648 	    pngPtr->bytesPerPixel = 1;
3649 	}
3650     }
3651 
3652     /*
3653      * Allocate buffers for lines for filtering and compressed data.
3654      */
3655 
3656     pngPtr->lineSize = 1 + (pngPtr->bytesPerPixel * blockPtr->width);
3657     pngPtr->blockLen = pngPtr->lineSize * blockPtr->height;
3658 
3659     if ((blockPtr->width > (INT_MAX - 1) / (pngPtr->bytesPerPixel)) ||
3660 	    (blockPtr->height > INT_MAX / pngPtr->lineSize)) {
3661 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
3662 		"image is too large to encode pixel data", -1));
3663 	Tcl_SetErrorCode(interp, "TK", "IMAGE", "PNG", "TOO_LARGE", NULL);
3664 	return TCL_ERROR;
3665     }
3666 
3667     pngPtr->lastLineObj = Tcl_NewObj();
3668     Tcl_IncrRefCount(pngPtr->lastLineObj);
3669     pngPtr->thisLineObj = Tcl_NewObj();
3670     Tcl_IncrRefCount(pngPtr->thisLineObj);
3671 
3672     /*
3673      * Write out the PNG Signature that all PNGs begin with.
3674      */
3675 
3676     if (WriteData(interp, pngPtr, pngSignature, PNG_SIG_SZ,
3677 	    NULL) == TCL_ERROR) {
3678 	return TCL_ERROR;
3679     }
3680 
3681     /*
3682      * Write out the IHDR (header) chunk containing image dimensions, color
3683      * type, etc.
3684      */
3685 
3686     if (WriteIHDR(interp, pngPtr, blockPtr) == TCL_ERROR) {
3687 	return TCL_ERROR;
3688     }
3689 
3690     /*
3691      * Write out the extra chunks containing metadata that is of interest to
3692      * other programs more than us.
3693      */
3694 
3695     if (WriteExtraChunks(interp, pngPtr, metadataInObj) == TCL_ERROR) {
3696 	return TCL_ERROR;
3697     }
3698 
3699     /*
3700      * Write out the image pixels in the IDAT (data) chunk.
3701      */
3702 
3703     if (WriteIDAT(interp, pngPtr, blockPtr) == TCL_ERROR) {
3704 	return TCL_ERROR;
3705     }
3706 
3707     /*
3708      * Write out the IEND chunk that all PNGs end with.
3709      */
3710 
3711     return WriteChunk(interp, pngPtr, CHUNK_IEND, NULL, 0);
3712 }
3713 
3714 /*
3715  *----------------------------------------------------------------------
3716  *
3717  * FileWritePNG --
3718  *
3719  *	This function is called by the photo image type to write PNG format
3720  *	data to a file.
3721  *
3722  * Results:
3723  *	A standard TCL completion code. If TCL_ERROR is returned then an error
3724  *	message is left in the interp's result.
3725  *
3726  * Side effects:
3727  *	The specified file is overwritten.
3728  *
3729  *----------------------------------------------------------------------
3730  */
3731 
3732 static int
FileWritePNG(Tcl_Interp * interp,const char * filename,TCL_UNUSED (Tcl_Obj *),Tcl_Obj * metadataInObj,Tk_PhotoImageBlock * blockPtr)3733 FileWritePNG(
3734     Tcl_Interp *interp,
3735     const char *filename,
3736     TCL_UNUSED(Tcl_Obj *),
3737     Tcl_Obj *metadataInObj,
3738     Tk_PhotoImageBlock *blockPtr)
3739 {
3740     Tcl_Channel chan;
3741     PNGImage png;
3742     int result = TCL_ERROR;
3743 
3744     /*
3745      * Open a Tcl file channel where the image data will be stored. Tk ought
3746      * to take care of this, and just provide a channel, but it doesn't.
3747      */
3748 
3749     chan = Tcl_OpenFileChannel(interp, filename, "w", 0644);
3750 
3751     if (!chan) {
3752 	return TCL_ERROR;
3753     }
3754 
3755     /*
3756      * Initalize PNGImage instance for encoding.
3757      */
3758 
3759     if (InitPNGImage(interp, &png, chan, NULL,
3760 	    TCL_ZLIB_STREAM_DEFLATE) == TCL_ERROR) {
3761 	goto cleanup;
3762     }
3763 
3764     /*
3765      * Set the translation mode to binary so that CR and LF are not to the
3766      * platform's EOL sequence.
3767      */
3768 
3769     if (Tcl_SetChannelOption(interp, chan, "-translation",
3770 	    "binary") != TCL_OK) {
3771 	goto cleanup;
3772     }
3773 
3774     /*
3775      * Write the raw PNG data out to the file.
3776      */
3777 
3778     result = EncodePNG(interp, blockPtr, &png, metadataInObj);
3779 
3780   cleanup:
3781     Tcl_Close(interp, chan);
3782     CleanupPNGImage(&png);
3783     return result;
3784 }
3785 
3786 /*
3787  *----------------------------------------------------------------------
3788  *
3789  * StringWritePNG --
3790  *
3791  *	This function is called by the photo image type to write PNG format
3792  *	data to a Tcl object and return it in the result.
3793  *
3794  * Results:
3795  *	A standard TCL completion code. If TCL_ERROR is returned then an error
3796  *	message is left in the interp's result.
3797  *
3798  * Side effects:
3799  *	None
3800  *
3801  *----------------------------------------------------------------------
3802  */
3803 
3804 static int
StringWritePNG(Tcl_Interp * interp,TCL_UNUSED (Tcl_Obj *),Tcl_Obj * metadataInObj,Tk_PhotoImageBlock * blockPtr)3805 StringWritePNG(
3806     Tcl_Interp *interp,
3807     TCL_UNUSED(Tcl_Obj *),
3808     Tcl_Obj *metadataInObj,
3809     Tk_PhotoImageBlock *blockPtr)
3810 {
3811     Tcl_Obj *resultObj = Tcl_NewObj();
3812     PNGImage png;
3813     int result = TCL_ERROR;
3814 
3815     /*
3816      * Initalize PNGImage instance for encoding.
3817      */
3818 
3819     if (InitPNGImage(interp, &png, NULL, resultObj,
3820 	    TCL_ZLIB_STREAM_DEFLATE) == TCL_ERROR) {
3821 	goto cleanup;
3822     }
3823 
3824     /*
3825      * Write the raw PNG data into the prepared Tcl_Obj buffer. Set the result
3826      * back to the interpreter if successful.
3827      */
3828 
3829     result = EncodePNG(interp, blockPtr, &png, metadataInObj);
3830 
3831     if (TCL_OK == result) {
3832 	Tcl_SetObjResult(interp, png.objDataPtr);
3833     }
3834 
3835   cleanup:
3836     CleanupPNGImage(&png);
3837     return result;
3838 }
3839 
3840 /*
3841  * Local Variables:
3842  * c-basic-offset: 4
3843  * fill-column: 78
3844  * End:
3845  */
3846