Project

General

Profile

Statistics
| Branch: | Revision:

root / src / tkenv / tkImgPNG.c @ e1750c09

History | View | Annotate | Download (61.5 KB)

1
/*
2
 * tkImgPNG.c --
3
 *
4
 *                A Tk photo image file handler for PNG files.  Requires zlib.
5
 *
6
 * Copyright (c) 2005 Michael Kirkham <mikek@muonics.com> & Muonics
7
 *
8
 * See the file "license.terms" for information on usage and redistribution
9
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10
 *
11
 * RCS: @(#) $Id: tkImgPNG.c,v 1.6 2005/03/26 22:22:13 mikek Exp $
12
 */
13

    
14
#include <stdlib.h>
15
#include <memory.h>
16
#include <limits.h>
17

    
18
#include <zlib.h>
19
#include <math.h>
20
#include "tcl.h"
21
#include "tk.h"
22

    
23
/*
24
 * Change by Andras:
25
 *
26
 * Handle Tk_PhotoPutBlock signature change in Tk 8.5:
27
 * "Added interp argument to these functions and made them return
28
 * a standard Tcl result, with error indicating memory allocation
29
 * failure instead of panic()ing."
30
 * See Tk ChangeLog entry from 2003-03-06 by Donal K. Fellows
31
 * --
32
 */
33
#if TK_MAJOR_VERSION>8 || (TK_MAJOR_VERSION==8 && TK_MINOR_VERSION>=5)
34
#define INTERP_IF_TK85 interp,
35
#else
36
#define INTERP_IF_TK85
37
#endif
38

    
39
/* Every PNG image starts with the following 8-byte signature */
40

    
41
static const Byte        gspPNGSignature[]        = { 137, 80, 78, 71, 13, 10, 26, 10 };
42
#define        gsSigSz        8
43

    
44
static const int        gspStartLine[8] = {0, 0, 0, 4, 0, 2, 0, 1};
45

    
46
#define        PNG_LONG(a,b,c,d)        ((a << 24) | (b << 16) | (c << 8) | d)
47

    
48
#define        PNG_BLOCK_SZ                1024                /* Process up to 1k at a time */
49

    
50
/* Chunk type flags */
51

    
52
#define PNG_CF_ANCILLARY        0x10000000L        /* Non-critical chunk (can ignore) */
53
#define PNG_CF_PRIVATE                0x00100000L        /* Application-specific chunk */
54
#define PNG_CF_RESERVED                0x00001000L        /* Not used */
55
#define PNG_CF_COPYSAFE                0x00000010L        /* Opaque data safe for copying */
56

    
57
/* Chunk types */
58

    
59
#define CHUNK_IDAT        PNG_LONG(  73,  68,  65,  84 )        /* Pixel data */
60
#define CHUNK_IEND        PNG_LONG(  73,  69,  78,  68 )        /* End of Image */
61
#define CHUNK_IHDR        PNG_LONG(  73,  72,  68,  82 )        /* Header */
62
#define CHUNK_PLTE        PNG_LONG(  80,  76,  84,  69 )        /* Palette */
63
#define CHUNK_bKGD        PNG_LONG(  98,  75,  71,  68 )        /* Background Color */
64
#define CHUNK_cHRM        PNG_LONG(  99,  72,  82,  77 )
65
#define CHUNK_gAMA        PNG_LONG( 103,  65,  77,  65 )        /* Gamma */
66
#define CHUNK_hIST        PNG_LONG( 104,  73,  83,  84 )        /* Histogram */
67
#define CHUNK_iCCP        PNG_LONG( 105,  67,  67,  80 )
68
#define CHUNK_iTXt        PNG_LONG( 105,  84,  88, 116 )        /* Text (comments etc.) */
69
#define CHUNK_oFFs        PNG_LONG( 111,  70,  70, 115 )
70
#define CHUNK_pCAL        PNG_LONG( 112,  67,  65,  76 )
71
#define CHUNK_pHYs        PNG_LONG( 112,  72,  89, 115 )
72
#define CHUNK_sBIT        PNG_LONG( 115,  66,  73,  84 )
73
#define CHUNK_sCAL        PNG_LONG( 115,  67,  65,  76 )
74
#define CHUNK_sPLT        PNG_LONG( 115,  80,  76,  84 )
75
#define CHUNK_sRGB        PNG_LONG( 115,  82,  71,  66 )
76
#define CHUNK_tEXt        PNG_LONG( 116,  69,  88, 116 )        /* More text */
77
#define CHUNK_tIME        PNG_LONG( 116,  73,  77,  69 )        /* Time stamp */
78
#define CHUNK_tRNS        PNG_LONG( 116,  82,  78,  83 )        /* Transparency */
79
#define CHUNK_zTXt        PNG_LONG( 122,  84,  88, 116 )        /* More text */
80

    
81
/* Color flags */
82

    
83
#define PNG_COLOR_INDEXED                1
84
#define PNG_COLOR_USED                        2
85
#define PNG_COLOR_ALPHA                        4
86

    
87
/* Actual color types */
88

    
89
#define PNG_COLOR_GRAY                        0
90
#define PNG_COLOR_RGB                        (PNG_COLOR_USED)
91
#define PNG_COLOR_PLTE                        (PNG_COLOR_USED | PNG_COLOR_INDEXED)
92
#define PNG_COLOR_GRAYALPHA                (PNG_COLOR_GRAY | PNG_COLOR_ALPHA)
93
#define PNG_COLOR_RGBA                        (PNG_COLOR_USED | PNG_COLOR_ALPHA)
94

    
95
/* Compression Methods */
96

    
97
#define PNG_COMPRESS_DEFLATE        0
98

    
99
/* Filter Methods */
100

    
101
#define PNG_FILTMETH_STANDARD        0
102

    
103
/* Interlacing Methods */
104

    
105
#define        PNG_INTERLACE_NONE                0
106
#define PNG_INTERLACE_ADAM7                1
107

    
108
#define        PNG_ENCODE        0
109
#define PNG_DECODE        1
110

    
111
typedef struct
112
{
113
        Byte        mRed;
114
        Byte        mGrn;
115
        Byte        mBlu;
116
        Byte        mAlpha;
117
} PNG_RGBA;
118

    
119
/* State information */
120

    
121
typedef struct
122
{
123
        /* PNG Data Source/Destination channel/object/byte array */
124

    
125
        Tcl_Channel                mFile;                        /* Channel for from-file reads */
126
        Tcl_Obj*                mpObjData;
127
        Byte*                        mpStrData;                /* Raw source data for from-string reads */
128
        int                                mStrDataSz;                /* Length of source data */
129
        Byte*                        mpBase64Data;        /* base64 encoded string data */
130
        Byte                        mBase64Bits;        /* Remaining bits from last base64 read */
131
        Byte                        mBase64State;        /* Current state of base64 decoder */
132

    
133
        uLong                        mChunks;                /* Number of chunks read */
134

    
135
        /* State information for zlib compression/decompression */
136

    
137
        z_stream                mZStream;                /* Zlib inflate/deflate stream state */
138
        int                                mZStreamInit;        /* Stream has been initialized */
139
        int                                mZStreamDir;        /* PNG_ENCODE/PNG_DECODE */
140

    
141
        /* Image Header Information */
142

    
143
        uLong                        mWidth;                        /* Width of the PNG image in pixels */
144
        uLong                        mHeight;                /* Height of the PNG image in pixels */
145
        Byte                        mBitDepth;                /* Number of bits per pixel */
146
        Byte                        mColorType;                /* Grayscale, TrueColor, etc. */
147
        Byte                        mCompression;        /* Compression Mode (always zlib) */
148
        Byte                        mFilter;                /* Filter mode (0 - 3) */
149
        Byte                        mInterlace;                /* Type of interlacing (if any) */
150

    
151
        Byte                        mChannels;                /* Number of channels per pixel */
152
        Byte                        mPixelDepth;        /* Number of total bits per pixel */
153
        Byte                        mBPP;                        /* Bytes per pixel in scan line */
154

    
155
        uLong                        mCurrLine;                /* Current line being unfiltered */
156
        Byte                        mPhase;                        /* Interlacing phase (0..6) */
157

    
158
        Tk_PhotoImageBlock        mBlock;
159
        uLong                                mBlockSz;                /* Number of bytes in Tk image pixels */
160

    
161
        /* PLTE Palette and tRNS Transparency Entries */
162

    
163
        int                                mPalEntries;        /* Number of PLTE entries (1..256) */
164
        int                                mUseTRNS;
165
        PNG_RGBA                mpPalette[256];        /* Palette RGB/Transparency table */
166
        Byte                        mpTrans[6];                /* Fully-transparent RGB/Gray Value */
167

    
168
        /* PNG and Tk Photo pixel data */
169

    
170
        Byte*                        mpLastLine;                /* Last line of pixels, for unfiltering */
171
        Byte*                        mpThisLine;                /* Current line of pixels to process */
172
        uLong                        mLineSz;                /* Number of bytes in a PNG line */
173
        uLong                        mPhaseSz;                /* Number of bytes/line in current phase */
174
} PNGImage;
175

    
176

    
177
/*
178
 *----------------------------------------------------------------------
179
 *
180
 * PNGZAlloc --
181
 *
182
 *                This function is invoked by zlib to allocate memory it needs.
183
 *
184
 * Results:
185
 *                A pointer to the allocated buffer or NULL if allocation fails.
186
 *
187
 * Side effects:
188
 *                Memory is allocated.
189
 *
190
 *----------------------------------------------------------------------
191
 */
192

    
193
static
194
voidpf PNGZAlloc(voidpf opaque, uInt items, uInt itemSz)
195
{
196
        uLong        blockSz        = items * itemSz;
197
        void*        pBlock        = attemptckalloc(blockSz);
198

    
199
        if (!pBlock)
200
                return Z_NULL;
201

    
202
        memset(pBlock, 0, blockSz);
203

    
204
        return pBlock;
205
}
206

    
207

    
208
/*
209
 *----------------------------------------------------------------------
210
 *
211
 * PNGZFree --
212
 *
213
 *                This function is invoked by zlib to free memory it previously
214
 *                allocated using PNGZAlloc.
215
 *
216
 * Results:
217
 *                None.
218
 *
219
 * Side effects:
220
 *                Memory is freed.
221
 *
222
 *----------------------------------------------------------------------
223
 */
224

    
225
static
226
void PNGZFree(voidpf opaque, voidpf ptr)
227
{
228
        if (ptr) ckfree((char *)ptr);
229
}
230

    
231

    
232
/*
233
 *----------------------------------------------------------------------
234
 *
235
 * PNGInit --
236
 *
237
 *                This function is invoked by each of the Tk image handler
238
 *                procs (MatchStringProc, etc.) to initialize state information
239
 *                used during the course of encoding or decoding an PNG image.
240
 *
241
 * Results:
242
 *                TCL_OK, or TCL_ERROR if initialization failed.
243
 *
244
 * Side effects:
245
 *                The reference count of the -data Tcl_Obj*, if any, is
246
 *                incremented.
247
 *
248
 *----------------------------------------------------------------------
249
 */
250

    
251
static int
252
PNGInit(Tcl_Interp* interp, PNGImage* pPNG,
253
        Tcl_Channel chan, Tcl_Obj* pObj, int dir)
254
{
255
        int ret;
256

    
257
        memset(pPNG, 0, sizeof(*pPNG));
258

    
259
        pPNG -> mFile = chan;
260

    
261
        /*
262
         * If decoding from a -data string object, increment its reference
263
         * count for the duration of the decode and get its length and
264
         * byte array for reading with PNGRead().
265
         */
266

    
267
        if (pObj)
268
        {
269
                Tcl_IncrRefCount(pObj);
270
                pPNG -> mpObjData = pObj;
271
                pPNG -> mpStrData = Tcl_GetByteArrayFromObj(pObj,
272
                        &pPNG->mStrDataSz);
273
        }
274

    
275
        /* Initialize the palette transparency table to fully opaque */
276

    
277
        memset(pPNG -> mpPalette, 0xff, sizeof(pPNG -> mpPalette));
278

    
279
        /* Initialize Zlib inflate/deflate stream */
280

    
281
        pPNG -> mZStream.zalloc        = PNGZAlloc;        /* Memory allocation */
282
        pPNG -> mZStream.zfree        = PNGZFree;                /* Memory deallocation */
283

    
284
        if (PNG_DECODE == dir) {
285
                ret = inflateInit(&pPNG -> mZStream);
286
        } else {
287
                ret = deflateInit(&pPNG -> mZStream, Z_DEFAULT_COMPRESSION);
288
        }
289

    
290
        /* Make sure that Zlib stream initialization was successful */
291

    
292
        if (Z_OK != ret)
293
        {
294
                if (pPNG -> mZStream.msg)
295
                        Tcl_SetResult(interp, pPNG -> mZStream.msg, TCL_VOLATILE);
296
                else
297
                        Tcl_SetResult(interp, "zlib initialization failed", TCL_STATIC);
298

    
299
                return TCL_ERROR;
300
        }
301

    
302
        pPNG -> mZStreamInit = 1;
303

    
304
        return TCL_OK;
305
}
306

    
307

    
308
/*
309
 *----------------------------------------------------------------------
310
 *
311
 * PNGCleanup --
312
 *
313
 *                This function is invoked by each of the Tk image handler
314
 *                procs (MatchStringProc, etc.) prior to returning to Tcl
315
 *                in order to clean up any allocated memory and call other
316
 *                other cleanup handlers such as zlib's inflateEnd/deflateEnd.
317
 *
318
 * Results:
319
 *                None.
320
 *
321
 * Side effects:
322
 *                The reference count of the -data Tcl_Obj*, if any, is
323
 *                decremented.  Buffers are freed, zstreams are closed.
324
 *
325
 *----------------------------------------------------------------------
326
 */
327

    
328
static void
329
PNGCleanup(PNGImage* pPNG)
330
{
331
        /* Don't need the object containing the -data .. data anymore. */
332

    
333
        if (pPNG -> mpObjData)
334
                Tcl_DecrRefCount(pPNG -> mpObjData);
335

    
336
        /* Discard pixel buffer */
337

    
338
        if (pPNG -> mZStreamInit)
339
        {
340
                if (PNG_ENCODE == pPNG -> mZStreamDir)
341
                        inflateEnd(&pPNG -> mZStream);
342
                else
343
                        deflateEnd(&pPNG -> mZStream);
344
        }
345

    
346
        if (pPNG -> mBlock.pixelPtr)
347
                ckfree((char *)pPNG -> mBlock.pixelPtr);
348
        if (pPNG -> mpThisLine)
349
                ckfree((char *)pPNG -> mpThisLine);
350
        if (pPNG -> mpLastLine)
351
                ckfree((char *)pPNG -> mpLastLine);
352
}
353

    
354

    
355
/*
356
 *----------------------------------------------------------------------
357
 *
358
 * char64 --
359
 *
360
 *                This procedure converts a base64 ascii character into its binary
361
 *                equivalent.  This code is a slightly modified version of the
362
 *                char64 proc in N. Borenstein's metamail decoder.
363
 *
364
 * Results:
365
 *                The binary value, or an error code.
366
 *
367
 * Side effects:
368
 *                None.
369
 *----------------------------------------------------------------------
370
 */
371

    
372
#define PNG64_SPECIAL     0x80
373
#define PNG64_SPACE       0x80
374
#define PNG64_PAD         0x81
375
#define PNG64_DONE        0x82
376
#define PNG64_BAD         0x83
377

    
378
static Byte gspFrom64[] =
379
{
380
        0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80, 0x80, 0x83,
381
        0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
382
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x80, 0x83, 0x83, 0x83,
383
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x3e, 0x83, 0x83, 0x83, 0x3f,
384
        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x83, 0x83,
385
        0x83, 0x81, 0x83, 0x83, 0x83, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
386
        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
387
        0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x83, 0x83, 0x83, 0x83, 0x83,
388
        0x83, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
389
        0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
390
        0x31, 0x32, 0x33, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
391
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
392
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
393
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
394
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
395
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
396
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
397
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
398
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
399
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
400
        0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
401
        0x83, 0x83, 0x83
402
};
403

    
404

    
405
/*
406
 *----------------------------------------------------------------------
407
 *
408
 * PNGRead --
409
 *
410
 *                This function is invoked to read the specified number of bytes
411
 *                from the image file or data.  It is a wrapper around the
412
 *                choice of byte array Tcl_Obj or Tcl_Channel which depends on
413
 *                whether the image data is coming from a file or -data.
414
 *
415
 * Results:
416
 *                TCL_OK, or TCL_ERROR if an I/O error occurs.
417
 *
418
 * Side effects:
419
 *                The file position will change.  The running CRC is updated
420
 *                if a pointer to it is provided.
421
 *
422
 *----------------------------------------------------------------------
423
 */
424

    
425
static int
426
PNGRead(Tcl_Interp* interp, PNGImage* pPNG,
427
        Byte* pDest, uLong destSz, uLong* pCRC)
428
{
429
        if (pPNG -> mpBase64Data)
430
        {
431
                while (destSz && pPNG -> mStrDataSz)
432
                {
433
                        Byte        c;
434
                        Byte        c64 = gspFrom64[*pPNG -> mpStrData++];
435

    
436
                        pPNG -> mStrDataSz--;
437

    
438
                        if (PNG64_SPACE == c64)
439
                                continue;
440

    
441
                        if (c64 & PNG64_SPECIAL)
442
                        {
443
                                c = pPNG -> mBase64Bits;
444
                        }
445
                        else
446
                        {
447
                                if (0 == pPNG -> mBase64State)
448
                                {
449
                                        pPNG -> mBase64Bits = c64 << 2;
450
                                        pPNG -> mBase64State++;
451
                                        continue;
452
                                }
453

    
454
                                switch (pPNG -> mBase64State++)
455
                                {
456
                                case 1:
457
                                        c = (Byte)(pPNG -> mBase64Bits | (c64 >> 4));
458
                                        pPNG -> mBase64Bits = (c64 & 0xF) << 4;
459
                                        break;
460
                                case 2:
461
                                        c = (Byte)(pPNG -> mBase64Bits | (c64 >> 2));
462
                                        pPNG -> mBase64Bits = (c64 & 0x3) << 6;
463
                                        break;
464
                                case 3:
465
                                        c = (Byte)(pPNG -> mBase64Bits | c64);
466
                                        pPNG -> mBase64State = 0;
467
                                        pPNG -> mBase64Bits = 0;
468
                                        break;
469
                                }
470
                        }
471

    
472
                        if (pCRC)
473
                                *pCRC = crc32(*pCRC, &c, 1);
474

    
475
                        if (pDest)
476
                                *pDest++ = c;
477

    
478
                        destSz--;
479

    
480
                        if (c64 & PNG64_SPECIAL)
481
                                break;
482
                }
483

    
484
                if (destSz)
485
                {
486
                        Tcl_SetResult(interp, "Unexpected end of image data", TCL_STATIC);
487
                        return TCL_ERROR;
488
                }
489
        }
490
        else
491
        if (pPNG -> mpStrData)
492
        {
493
                if (pPNG -> mStrDataSz < destSz)
494
                {
495
                        Tcl_SetResult(interp, "Unexpected end of image data", TCL_STATIC);
496
                        return TCL_ERROR;
497
                }
498

    
499
                if (pDest)
500
                        memcpy(pDest, pPNG -> mpStrData, destSz);
501

    
502
                pPNG -> mpStrData                += destSz;
503
                pPNG -> mStrDataSz        -= destSz;
504

    
505
                if (pCRC)
506
                        *pCRC = crc32(*pCRC, pDest, destSz);
507
        }
508
        else
509
        {
510
                while (destSz)
511
                {
512
                        int         blockSz = Tcl_Read(pPNG -> mFile, pDest, destSz);
513

    
514
                        if (blockSz < 0)
515
                        {
516
                                /* TODO: failure info... */
517
                                Tcl_SetResult(interp, "Channel read failed", TCL_STATIC);
518
                                return TCL_ERROR;
519
                        }
520

    
521
                        if (blockSz)
522
                        {
523
                                if (pCRC)
524
                                        *pCRC = crc32(*pCRC, pDest, blockSz);
525

    
526
                                pDest += blockSz;
527
                                destSz -= blockSz;
528
                        }
529

    
530
                        if (destSz && Tcl_Eof(pPNG -> mFile))
531
                        {
532
                                Tcl_SetResult(interp, "Unexpected end of file ", TCL_STATIC);
533
                                return TCL_ERROR;
534
                        }
535
                }
536
        }
537

    
538
        return TCL_OK;
539
}
540

    
541

    
542
/*
543
 *----------------------------------------------------------------------
544
 *
545
 * PNGReadLong --
546
 *
547
 *                This function is invoked to read a 4-byte long integer in
548
 *                network byte order from the image data and return the value in
549
 *                host byte order.
550
 *
551
 *
552
 * Results:
553
 *                TCL_OK, or TCL_ERROR if an I/O error occurs.
554
 *
555
 * Side effects:
556
 *                The file position will change.  The running CRC is updated
557
 *                if a pointer to it is provided.
558
 *
559
 *----------------------------------------------------------------------
560
 */
561

    
562
static int
563
PNGReadLong(Tcl_Interp* interp, PNGImage* pPNG, uLong* pResult, uLong* pCRC)
564
{
565
        Byte p[4];
566

    
567
        if (PNGRead(interp, pPNG, p, 4, pCRC) == TCL_ERROR)
568
                return TCL_ERROR;
569

    
570
        *pResult = PNG_LONG(p[0],p[1],p[2],p[3]);
571

    
572
        return TCL_OK;
573
}
574

    
575

    
576
/*
577
 *----------------------------------------------------------------------
578
 *
579
 * CheckCRC --
580
 *
581
 *                This function is reads the final 4-byte integer CRC from a
582
 *                chunk and compares it to the running CRC calculated over the
583
 *                chunk type and data fields.
584
 *
585
 * Results:
586
 *                TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs.
587
 *
588
 * Side effects:
589
 *                The file position will change.
590
 *
591
 *----------------------------------------------------------------------
592
 */
593

    
594
static
595
int CheckCRC(Tcl_Interp* interp, PNGImage* pPNG, uLong calculated)
596
{
597
        uLong        chunked;
598

    
599
        /* Read the CRC field at the end of the chunk */
600

    
601
        if (PNGReadLong(interp, pPNG, &chunked, NULL) == TCL_ERROR)
602
                return TCL_ERROR;
603

    
604
        /* Compare the read CRC to what we calculate to make sure they match. */
605

    
606
        if (calculated != chunked)
607
        {
608
//                Tcl_SetResult(interp, "CRC check failed", TCL_STATIC);
609
//                return TCL_ERROR;
610
        }
611

    
612
        /* Done parsing this chunk. Reset the CRC counter for the next chunk. */
613

    
614
        pPNG -> mChunks++;
615

    
616
        return TCL_OK;
617
}
618

    
619

    
620
/*
621
 *----------------------------------------------------------------------
622
 *
623
 * SkipChunk --
624
 *
625
 *                This function is used to skip a PNG chunk that is not used
626
 *                by this implementation.  Given the input stream has had the
627
 *                chunk length and chunk type fields already read, this function
628
 *                will read the number of bytes indicated by the chunk length,
629
 *                plus four for the CRC, and will verify that CRC is
630
 *                correct for the skipped data.
631
 *
632
 * Results:
633
 *                TCL_OK, or TCL_ERROR if an I/O error or CRC mismatch occurs.
634
 *
635
 * Side effects:
636
 *                The file position will change.
637
 *
638
 *----------------------------------------------------------------------
639
 */
640

    
641
static int
642
SkipChunk(Tcl_Interp* interp, PNGImage* pPNG, uLong chunkSz, uLong crc)
643
{
644
        Byte        pBuffer[PNG_BLOCK_SZ];
645

    
646
        /*
647
         * Skip data in blocks until none is left.  Read up to PNG_BLOCK_SZ
648
         * bytes at a time, rather than trusting the claimed chunk size,
649
         * which may not be trustworthy.
650
         */
651

    
652
        while (chunkSz)
653
        {
654
                int blockSz = (chunkSz > PNG_BLOCK_SZ) ? PNG_BLOCK_SZ : chunkSz;
655

    
656
                if (PNGRead(interp, pPNG, pBuffer, blockSz, &crc) == TCL_ERROR)
657
                        return TCL_ERROR;
658

    
659
                chunkSz -= blockSz;
660
        }
661

    
662
        if (CheckCRC(interp, pPNG, crc) == TCL_ERROR)
663
                return TCL_ERROR;
664

    
665
        return TCL_OK;
666
}
667

    
668

    
669
/*
670
4.3. Summary of standard chunks
671

672
This table summarizes some properties of the standard chunk types.
673

674
   Critical chunks (must appear in this order, except PLTE
675
                                        is optional):
676

677
                   Name  Multiple  Ordering constraints
678
                                   OK?
679

680
                   IHDR        No          Must be first
681
                   PLTE        No          Before IDAT
682
                   IDAT        Yes         Multiple IDATs must be consecutive
683
                   IEND        No          Must be last
684

685
   Ancillary chunks (need not appear in this order):
686

687
                   Name  Multiple  Ordering constraints
688
                                   OK?
689

690
                   cHRM        No          Before PLTE and IDAT
691
                   gAMA        No          Before PLTE and IDAT
692
                   iCCP        No          Before PLTE and IDAT
693
                   sBIT        No          Before PLTE and IDAT
694
                   sRGB        No          Before PLTE and IDAT
695
                   bKGD        No          After PLTE; before IDAT
696
                   hIST        No          After PLTE; before IDAT
697
                   tRNS        No          After PLTE; before IDAT
698
                   pHYs        No          Before IDAT
699
                   sPLT        Yes         Before IDAT
700
                   tIME        No          None
701
                   iTXt        Yes         None
702
                   tEXt        Yes         None
703
                   zTXt        Yes         None
704

705
        [From the PNG specification.]
706
*/
707

    
708

    
709
/*
710
 *----------------------------------------------------------------------
711
 *
712
 * ReadChunkHeader --
713
 *
714
 *                This function is used at the start of each chunk to extract
715
 *                the four-byte chunk length and four-byte chunk type fields.
716
 *                It will continue reading until it finds a chunk type that is
717
 *                handled by this implementation, checking the CRC of any chunks
718
 *                it skips.
719
 *
720
 * Results:
721
 *                TCL_OK, or TCL_ERROR if an I/O error occurs or an unknown
722
 *                critical chunk type is encountered.
723
 *
724
 * Side effects:
725
 *                The file position will change.  The running CRC is updated.
726
 *
727
 *----------------------------------------------------------------------
728
 */
729

    
730
static int
731
ReadChunkHeader(Tcl_Interp* interp, PNGImage* pPNG,
732
        uLong* pSize, uLong* pType, uLong* pCRC)
733
{
734
        uLong        chunkType        = 0;
735
        uLong        chunkSz                = 0;
736
        uLong        crc;
737
        Byte        pc[4];
738

    
739
        /* Continue until finding a chunk type that is handled. */
740

    
741
        while (!chunkType)
742
        {
743
                int i;
744

    
745
                /*
746
                 * Read the 4-byte length field for the chunk, which is not
747
                 * included in the CRC calculation.
748
                 */
749

    
750
                if (PNGReadLong(interp, pPNG, &chunkSz, NULL) == TCL_ERROR)
751
                        return TCL_ERROR;
752

    
753
                /* Read the 4-byte chunk type */
754

    
755
                crc = crc32(0, NULL, 0);
756

    
757
                if (PNGRead(interp, pPNG, pc, 4, &crc) == TCL_ERROR)
758
                        return TCL_ERROR;
759

    
760
                /* Convert it to a host-order long for integer comparison */
761

    
762
                chunkType = PNG_LONG(pc[0], pc[1], pc[2], pc[3]);
763

    
764
                /*
765
                 * Check to see if this is a known/supported chunk type.  Note
766
                 * that the PNG specs require non-critical (i.e., ancillary)
767
                 * chunk types that are not recognized to be ignored, rather
768
                 * than be treated as an error.  It does, however, recommend
769
                 * that an unknown critical chunk type be treated as a failure.
770
                 *
771
                 * This switch/loop acts as a filter of sorts for undesired
772
                 * chunk types.  The chunk type should still be checked
773
                 * elsewhere for determining it is in the correct order.
774
                 */
775

    
776
                switch (chunkType)
777
                {
778
                case CHUNK_IDAT:
779
                case CHUNK_IEND:
780
                case CHUNK_IHDR:
781
                case CHUNK_PLTE:
782
                case CHUNK_tRNS:
783
                        break;
784

    
785
                /*
786
                 * These chunk types are part of the standard, but are not used by
787
                 * this implementation (at least not yet).  Note that these are
788
                 * all ancillary chunks (nowercase first letter).
789
                 */
790

    
791
                case CHUNK_bKGD:
792
                case CHUNK_cHRM:
793
                case CHUNK_gAMA:
794
                case CHUNK_hIST:
795
                case CHUNK_iCCP:
796
                case CHUNK_iTXt:
797
                case CHUNK_oFFs:
798
                case CHUNK_pCAL:
799
                case CHUNK_pHYs:
800
                case CHUNK_sBIT:
801
                case CHUNK_sCAL:
802
                case CHUNK_sPLT:
803
                case CHUNK_sRGB:
804
                case CHUNK_tEXt:
805
                case CHUNK_tIME:
806
                case CHUNK_zTXt:
807
                        /* TODO: might want to check order here. */
808
                        if (SkipChunk(interp, pPNG, chunkSz, crc) == TCL_ERROR)
809
                                return TCL_ERROR;
810

    
811
                        chunkType = 0;
812
                        break;
813

    
814
                default:
815
                        /* Unknown chunk type. If it's critical, we can't continue. */
816

    
817
                        if (!(chunkType & PNG_CF_ANCILLARY))
818
                        {
819
                                Tcl_SetResult(interp,
820
                                        "Encountered an unsupported criticial chunk type",
821
                                        TCL_STATIC);
822
                                return TCL_ERROR;
823
                        }
824

    
825
                        /* Check to see if the chunk type has legal bytes */
826

    
827
                        for (i = 0 ; i < 4 ; i++)
828
                        {
829
                                if ((pc[i] < 65) || (pc[i] > 122) ||
830
                                        ((pc[i] > 90) && (pc[i] < 97)))
831
                                {
832
                                        Tcl_SetResult(interp, "Invalid chunk type", TCL_STATIC);
833
                                        return TCL_ERROR;
834
                                }
835
                        }
836

    
837
                        /*
838
                         * It seems to be an otherwise legally labelled ancillary chunk
839
                         * that we don't want, so skip it after at least checking its CRC.
840
                         */
841

    
842
                        if (SkipChunk(interp, pPNG, chunkSz, crc) == TCL_ERROR)
843
                                return TCL_ERROR;
844

    
845
                        chunkType = 0;
846
                }
847
        }
848

    
849
        /*
850
         * Found a known chunk type that's handled, albiet possibly not in
851
         * the right order.  Send back the chunk type (for further checking
852
         * or handling), the chunk size and the current CRC for the rest
853
         * of the calculation.
854
         */
855

    
856
        *pType        = chunkType;
857
        *pSize        = chunkSz;
858
        *pCRC        = crc;
859

    
860
        return TCL_OK;
861
}
862

    
863
static int
864
PNGCheckColor(Tcl_Interp* interp, PNGImage* pPNG)
865
{
866
        int result = TCL_OK;
867
        int        offset;
868

    
869
        /* Verify the color type is valid and the bit depth is allowed */
870

    
871
//        if (pPNG -> mBitDepth == 16)
872
//                return TCL_ERROR;
873

    
874
        switch (pPNG -> mColorType)
875
        {
876
        case PNG_COLOR_GRAY:
877
                pPNG -> mChannels = 1;
878
                if ((1 != pPNG->mBitDepth) && (2 != pPNG->mBitDepth) &&
879
                        (4 != pPNG->mBitDepth) && (8 != pPNG->mBitDepth) &&
880
                        (16 != pPNG -> mBitDepth))
881
                        result = TCL_ERROR;
882
                break;
883

    
884
        case PNG_COLOR_RGB:
885
                pPNG -> mChannels = 3;
886
                if ((8 != pPNG->mBitDepth) && (16 != pPNG->mBitDepth))
887
                        result = TCL_ERROR;
888
                break;
889

    
890
        case PNG_COLOR_PLTE:
891
                pPNG -> mChannels = 1;
892
                if ((1 != pPNG->mBitDepth) && (2 != pPNG->mBitDepth) &&
893
                        (4 != pPNG->mBitDepth) && (8 != pPNG->mBitDepth))
894
                                result = TCL_ERROR;
895
                break;
896

    
897
        case PNG_COLOR_GRAYALPHA:
898
                pPNG -> mChannels = 2;
899
                if ((8 != pPNG->mBitDepth) && (16 != pPNG->mBitDepth))
900
                        result = TCL_ERROR;
901
                break;
902

    
903
        case PNG_COLOR_RGBA:
904
                pPNG -> mChannels = 4;
905
                if ((8 != pPNG->mBitDepth) && (16 != pPNG->mBitDepth))
906
                        result = TCL_ERROR;
907
                break;
908

    
909
        default:
910
                Tcl_SetResult(interp, "Unknown Color Type field", TCL_STATIC);
911
                return TCL_ERROR;
912
        }
913

    
914
        if (TCL_ERROR == result)
915
        {
916
                Tcl_SetResult(interp, "Bit depth is not allowed for given color type",
917
                        TCL_STATIC);
918
                return TCL_ERROR;
919
        }
920

    
921
        pPNG -> mPixelDepth = pPNG -> mChannels * pPNG -> mBitDepth;
922

    
923
        /*
924
         * Set up the Tk photo block's pixel size and channel offsets.
925
         * offset array elements should already be 0 from the memset
926
         * during PNGInit().
927
         */
928

    
929
        offset = (pPNG -> mBitDepth > 8) ? 2 : 1;
930

    
931
        if (pPNG -> mColorType & PNG_COLOR_USED)
932
        {
933
                pPNG -> mBlock.pixelSize        = offset * 4;
934
                pPNG -> mBlock.offset[1]        = offset;
935
                pPNG -> mBlock.offset[2]        = offset * 2;
936
                pPNG -> mBlock.offset[3]        = offset * 3;
937
        }
938
        else
939
        {
940
                pPNG -> mBlock.pixelSize        = offset * 2;
941
                pPNG -> mBlock.offset[3]        = offset;
942
        }
943

    
944
        pPNG -> mBlock.pitch= pPNG -> mBlock.pixelSize * pPNG -> mBlock.width;
945
        pPNG -> mBlockSz        = pPNG -> mBlock.height * pPNG -> mBlock.pitch;
946

    
947
        switch (pPNG -> mColorType)
948
        {
949
        case PNG_COLOR_GRAY:
950
                pPNG -> mBPP = (pPNG -> mBitDepth > 8) ? 2 : 1;
951
                break;
952
        case PNG_COLOR_RGB:
953
                pPNG -> mBPP = (pPNG -> mBitDepth > 8) ? 6 : 3;
954
                break;
955
        case PNG_COLOR_PLTE:
956
                pPNG -> mBPP = 1;
957
                break;
958
        case PNG_COLOR_GRAYALPHA:
959
                pPNG -> mBPP = (pPNG -> mBitDepth > 8) ? 4 : 2;
960
                break;
961
        case PNG_COLOR_RGBA:
962
                pPNG -> mBPP = (pPNG -> mBitDepth > 8) ? 8 : 4;
963
                break;
964
        default:
965
                Tcl_SetResult(interp, "internal error - unknown color type",
966
                        TCL_STATIC);
967
                return TCL_ERROR;
968
        }
969

    
970
        return TCL_OK;
971
}
972

    
973

    
974
/*
975
 *----------------------------------------------------------------------
976
 *
977
 * ReadIHDR --
978
 *
979
 *                This function reads the PNG header from the beginning of a
980
 *                PNG file and returns the dimensions of the image.
981
 *
982
 * Results:
983
 *                The return value is 1 if file "f" appears to start with
984
 *                a valid PNG header, 0 otherwise.  If the header is valid,
985
 *                then *widthPtr and *heightPtr are modified to hold the
986
 *                dimensions of the image.
987
 *
988
 * Side effects:
989
 *                The access position in f advances.
990
 *
991
 *----------------------------------------------------------------------
992
 */
993

    
994
static int
995
ReadIHDR(Tcl_Interp* interp, PNGImage* pPNG)
996
{
997
        Byte        pSig[gsSigSz];
998
        uLong        chunkType;
999
        uLong        chunkSz;
1000
        uLong        crc;
1001
        int                mismatch;
1002

    
1003
        /* Read the appropriate number of bytes for the PNG signature */
1004

    
1005
        if (PNGRead(interp, pPNG, pSig, gsSigSz, NULL) == TCL_ERROR)
1006
                return TCL_ERROR;
1007

    
1008
        /* Compare the read bytes to the expected signature. */
1009

    
1010
        mismatch = memcmp(pSig, gspPNGSignature, gsSigSz);
1011

    
1012
        /* If reading from string, reset position and try base64 decode */
1013

    
1014
        if (mismatch && pPNG -> mpStrData)
1015
        {
1016
                pPNG -> mpStrData = Tcl_GetByteArrayFromObj(pPNG -> mpObjData,
1017
                        &pPNG -> mStrDataSz);
1018
                pPNG -> mpBase64Data = pPNG -> mpStrData;
1019

    
1020
                if (PNGRead(interp, pPNG, pSig, gsSigSz, NULL) == TCL_ERROR)
1021
                        return TCL_ERROR;
1022

    
1023
                mismatch = memcmp(pSig, gspPNGSignature, gsSigSz);
1024
        }
1025

    
1026
        if (mismatch)
1027
        {
1028
                Tcl_SetResult(interp, "Data stream does not have a PNG signature",
1029
                        TCL_STATIC);
1030
                return TCL_ERROR;
1031
        }
1032

    
1033
        if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
1034
                        &crc) == TCL_ERROR)
1035
                return TCL_ERROR;
1036

    
1037
        /* Read in the IHDR (header) chunk for width, height, etc. */
1038
        /* The first chunk in the file must be the IHDR (headr) chunk */
1039

    
1040
        if (chunkType != CHUNK_IHDR)
1041
        {
1042
                Tcl_SetResult(interp, "Expected IHDR chunk type", TCL_STATIC);
1043
                return TCL_ERROR;
1044
        }
1045

    
1046
        if (chunkSz != 13)
1047
        {
1048
                Tcl_SetResult(interp, "Invalid IHDR chunk size", TCL_STATIC);
1049
                return TCL_ERROR;
1050
        }
1051

    
1052
        /* Read and verify the image width to be sure we can handle it */
1053

    
1054
        if (PNGReadLong(interp, pPNG, &pPNG->mWidth, &crc) == TCL_ERROR)
1055
                return TCL_ERROR;
1056

    
1057
        if (!pPNG -> mWidth || (pPNG -> mWidth > INT_MAX))
1058
        {
1059
                Tcl_SetResult(interp,
1060
                        "Image width is invalid or out of supported range",
1061
                        TCL_STATIC);
1062
                return TCL_ERROR;
1063
        }
1064

    
1065
        /* Read and verify the image height to be sure we can handle it */
1066

    
1067
        if (PNGReadLong(interp, pPNG, &pPNG->mHeight, &crc) == TCL_ERROR)
1068
                return TCL_ERROR;
1069

    
1070
        if (!pPNG -> mHeight || (pPNG -> mHeight > INT_MAX))
1071
        {
1072
                Tcl_SetResult(interp,
1073
                        "Image height is invalid or out of supported range",
1074
                        TCL_STATIC);
1075
                return TCL_ERROR;
1076
        }
1077

    
1078
        /* Set height and width for the Tk photo block */
1079

    
1080
        pPNG -> mBlock.width = (int)pPNG -> mWidth;
1081
        pPNG -> mBlock.height = (int)pPNG -> mHeight;
1082

    
1083
        /* Read and the Bit Depth and Color Type */
1084

    
1085
        if (PNGRead(interp, pPNG, &pPNG->mBitDepth, 1, &crc) == TCL_ERROR)
1086
                return TCL_ERROR;
1087

    
1088
        if (PNGRead(interp, pPNG, &pPNG->mColorType, 1, &crc) == TCL_ERROR)
1089
                return TCL_ERROR;
1090

    
1091
        /*
1092
         * Verify that the color type is valid, the bit depth is allowed
1093
         * for the color type, and calculate the number of channels and
1094
         * pixel depth (bits per pixel * channels).  Also set up offsets
1095
         * and sizes in the Tk photo block for the pixel data.
1096
         */
1097

    
1098
        if (PNGCheckColor(interp, pPNG) == TCL_ERROR)
1099
                return TCL_ERROR;
1100

    
1101
        /* Only one compression method is currently defined by the standard */
1102

    
1103
        if (PNGRead(interp, pPNG, &pPNG->mCompression, 1, &crc) == TCL_ERROR)
1104
                return TCL_ERROR;
1105

    
1106
        if (PNG_COMPRESS_DEFLATE != pPNG -> mCompression)
1107
        {
1108
                Tcl_SetResult(interp, "Unknown compression method", TCL_STATIC);
1109
                return TCL_ERROR;
1110
        }
1111

    
1112
        /*
1113
         * Only one filter method is currently defined by the standard; the
1114
         * method has five actual filter types associated with it.
1115
         */
1116

    
1117
        if (PNGRead(interp, pPNG, &pPNG->mFilter, 1, &crc) == TCL_ERROR)
1118
                return TCL_ERROR;
1119

    
1120
        if (PNG_FILTMETH_STANDARD != pPNG -> mFilter)
1121
        {
1122
                Tcl_SetResult(interp, "Unknown filter method", TCL_STATIC);
1123
                return TCL_ERROR;
1124
        }
1125

    
1126
        if (PNGRead(interp, pPNG, &pPNG->mInterlace, 1, &crc) == TCL_ERROR)
1127
                return TCL_ERROR;
1128

    
1129
        switch (pPNG -> mInterlace)
1130
        {
1131
        case PNG_INTERLACE_NONE:
1132
        case PNG_INTERLACE_ADAM7:
1133
                break;
1134

    
1135
        default:
1136
                Tcl_SetResult(interp, "Unknown interlace method", TCL_STATIC);
1137
                return TCL_ERROR;
1138
        }
1139

    
1140
        return CheckCRC(interp, pPNG, crc);
1141
}
1142

    
1143

    
1144
/*
1145
 *----------------------------------------------------------------------
1146
 *
1147
 * ReadPLTE --
1148
 *
1149
 *                This function reads the PLTE (indexed color palette) chunk
1150
 *                data from the PNG file and populates the palette table in the
1151
 *                PNGImage structure.
1152
 *
1153
 * Results:
1154
 *                TCL_OK, or TCL_ERROR if an I/O error occurs or the PLTE
1155
 *                chunk is invalid.
1156
 *
1157
 * Side effects:
1158
 *                The access position in f advances.
1159
 *
1160
 *----------------------------------------------------------------------
1161
 */
1162

    
1163
static int
1164
ReadPLTE(Tcl_Interp* interp, PNGImage* pPNG, uLong chunkSz,
1165
        uLong chunkType, uLong crc)
1166
{
1167
        Byte        pBuffer[768];
1168
        int                i, c;
1169

    
1170
        /* This chunk is mandatory for color type 3 and forbidden for 2 and 6 */
1171

    
1172
        switch (pPNG -> mColorType)
1173
        {
1174
        case PNG_COLOR_GRAY:
1175
        case PNG_COLOR_GRAYALPHA:
1176
                Tcl_SetResult(interp, "PLTE chunk type forbidden for grayscale",
1177
                        TCL_STATIC);
1178
                return TCL_ERROR;
1179

    
1180
        default:
1181
                break;
1182
        }
1183

    
1184
        /*
1185
         * The palette chunk contains from 1 to 256 palette entries.
1186
         * Each entry consists of a 3 byte RGB value.  It must therefore
1187
         * contain a non-zero multiple of 3 bytes, up to 768.
1188
         */
1189

    
1190
        if (!chunkSz || (chunkSz > 768) || (chunkSz % 3))
1191
        {
1192
                Tcl_SetResult(interp, "Invalid palette chunk size", TCL_STATIC);
1193
                return TCL_ERROR;
1194
        }
1195

    
1196
        /* Read the palette contents and stash them for later, possibly */
1197

    
1198
        if (PNGRead(interp, pPNG, pBuffer, chunkSz, &crc) == TCL_ERROR)
1199
                return TCL_ERROR;
1200

    
1201
        if (CheckCRC(interp, pPNG, crc) == TCL_ERROR)
1202
                return TCL_ERROR;
1203

    
1204
        /* Stash away the number of palette entries and start the next chunk. */
1205

    
1206
        pPNG -> mPalEntries = chunkSz / 3;
1207

    
1208
        for (i = 0, c = 0 ; c < chunkSz ; i++)
1209
        {
1210
                pPNG -> mpPalette[i].mRed = pBuffer[c++];
1211
                pPNG -> mpPalette[i].mGrn = pBuffer[c++];
1212
                pPNG -> mpPalette[i].mBlu = pBuffer[c++];
1213
        }
1214

    
1215
        return TCL_OK;
1216
}
1217

    
1218

    
1219
/*
1220
 *----------------------------------------------------------------------
1221
 *
1222
 * ReadtRNS --
1223
 *
1224
 *                This function reads the tRNS (transparency) chunk data from the
1225
 *                PNG file and populates the alpha field of the palette table in
1226
 *                the PNGImage structure or the single color transparency, as
1227
 *                appropriate for the color type.
1228
 *
1229
 * Results:
1230
 *                TCL_OK, or TCL_ERROR if an I/O error occurs or the tRNS
1231
 *                chunk is invalid.
1232
 *
1233
 * Side effects:
1234
 *                The access position in f advances.
1235
 *
1236
 *----------------------------------------------------------------------
1237
 */
1238

    
1239
static int
1240
ReadtRNS(Tcl_Interp* interp, PNGImage* pPNG, uLong chunkSz, uLong crc)
1241
{
1242
        Byte        pBuffer[256];
1243
        int                i;
1244

    
1245
        /* First a sanity check to prevent buffer overrun */
1246

    
1247
        if (chunkSz > 256)
1248
        {
1249
                Tcl_SetResult(interp, "Invalid tRNS chunk size", TCL_STATIC);
1250
                return TCL_ERROR;
1251
        }
1252

    
1253
        /* Read in the raw transparency information */
1254

    
1255
        if (PNGRead(interp, pPNG, pBuffer, chunkSz, &crc) == TCL_ERROR)
1256
                return TCL_ERROR;
1257

    
1258
        if (CheckCRC(interp, pPNG, crc) == TCL_ERROR)
1259
                return TCL_ERROR;
1260

    
1261
        if (pPNG -> mColorType & PNG_COLOR_ALPHA)
1262
        {
1263
                Tcl_SetResult(interp,
1264
                        "tRNS chunk not allowed color types with a full alpha channel",
1265
                        TCL_STATIC);
1266
                return TCL_ERROR;
1267
        }
1268

    
1269
        switch (pPNG -> mColorType)
1270
        {
1271
        case PNG_COLOR_GRAYALPHA:
1272
        case PNG_COLOR_RGBA:
1273
                break;
1274

    
1275
        case PNG_COLOR_PLTE:
1276
                /*
1277
                 * The number of tRNS entries must be less than or equal to
1278
                 * the number of PLTE entries, and consists of a single-byte
1279
                 * alpha level for the corresponding PLTE entry.
1280
                 */
1281
                if ((chunkSz / 3) > pPNG -> mPalEntries)
1282
                {
1283
                        Tcl_SetResult(interp,
1284
                                "Size of tRNS chunk is too large for the palette",
1285
                                TCL_STATIC);
1286
                        return TCL_ERROR;
1287
                }
1288

    
1289
                for (i = 0 ; i < chunkSz ; i++)
1290
                {
1291
                        pPNG -> mpPalette[i].mAlpha = pBuffer[i];
1292
                }
1293
                break;
1294

    
1295
        case PNG_COLOR_GRAY:
1296
                /*
1297
                 * Grayscale uses a single 2-byte gray level, which we'll
1298
                 * store in palette index 0, since we're not using the palette.
1299
                 */
1300

    
1301
                if (chunkSz != 2)
1302
                {
1303
                        Tcl_SetResult(interp,
1304
                                "Invalid tRNS chunk size - must 2 bytes for grayscale",
1305
                                TCL_STATIC);
1306
                        return TCL_ERROR;
1307
                }
1308

    
1309
                /*
1310
                 * According to the PNG specs, if the bit depth is less than 16,
1311
                 * then only the lower byte is used.
1312
                 */
1313

    
1314
                if (16 == pPNG -> mBitDepth)
1315
                {
1316
                        pPNG -> mpTrans[0] = pBuffer[0];
1317
                        pPNG -> mpTrans[1] = pBuffer[1];
1318
                }
1319
                else
1320
                {
1321
                        pPNG -> mpTrans[0] = pBuffer[1];
1322
                }
1323
                pPNG -> mUseTRNS = 1;
1324
                break;
1325

    
1326
        case PNG_COLOR_RGB:
1327
                /* TrueColor uses a single RRGGBB triplet. */
1328

    
1329
                if (chunkSz != 6)
1330
                {
1331
                        Tcl_SetResult(interp,
1332
                                "Invalid tRNS chunk size - must 2 bytes for grayscale",
1333
                                TCL_STATIC);
1334
                        return TCL_ERROR;
1335
                }
1336

    
1337
                /*
1338
                 * According to the PNG specs, if the bit depth is less than 16,
1339
                 * then only the lower byte is used.
1340
                 */
1341

    
1342
                if (16 == pPNG -> mBitDepth)
1343
                {
1344
                        memcpy(pPNG -> mpTrans, pBuffer, 6);
1345
                }
1346
                else
1347
                {
1348
                        pPNG -> mpTrans[0] = pBuffer[1];
1349
                        pPNG -> mpTrans[1] = pBuffer[3];
1350
                        pPNG -> mpTrans[2] = pBuffer[5];
1351
                }
1352
                pPNG -> mUseTRNS = 1;
1353
                break;
1354
        }
1355

    
1356
        return TCL_OK;
1357
}
1358

    
1359

    
1360
/*
1361
 *----------------------------------------------------------------------
1362
 *
1363
 * DecodeLine --
1364
 *
1365
 *
1366
 * Results:
1367
 *
1368
 * Side effects:
1369
 *
1370
 *----------------------------------------------------------------------
1371
 */
1372

    
1373
#define        PNG_FILTER_NONE                0
1374
#define        PNG_FILTER_SUB                1
1375
#define        PNG_FILTER_UP                2
1376
#define        PNG_FILTER_AVG                3
1377
#define        PNG_FILTER_PAETH        4
1378

    
1379
static Byte
1380
Paeth(int a, int b, int c)
1381
{
1382
        int                pa        = abs(b - c);
1383
        int                pb        = abs(a - c);
1384
        int                pc        = abs(a + b - c - c);
1385

    
1386
        if ((pa <= pb) && (pa <= pc))
1387
                return (Byte)a;
1388

    
1389
        if (pb <= pc)
1390
                return (Byte)b;
1391

    
1392
        return (Byte)c;
1393
}
1394

    
1395
static int
1396
UnfilterLine(Tcl_Interp* interp, PNGImage* pPNG)
1397
{
1398
        switch (*pPNG->mpThisLine)
1399
        {
1400
        case PNG_FILTER_NONE:        /* Nothing to do */
1401
                break;
1402
        case PNG_FILTER_SUB:        /* Sub(x) = Raw(x) - Raw(x-bpp) */
1403
                {
1404
                        Byte*        pRaw_bpp= pPNG -> mpThisLine + 1;
1405
                        Byte*        pRaw        = pRaw_bpp + pPNG -> mBPP;
1406
                        Byte*        pEnd        = pPNG->mpThisLine + pPNG->mPhaseSz;
1407

    
1408
                        while (pRaw < pEnd)
1409
                        {
1410
                                *pRaw++ += *pRaw_bpp++;
1411
                        }
1412
                }
1413
                break;
1414
        case PNG_FILTER_UP:                /* Up(x) = Raw(x) - Prior(x) */
1415
                if (pPNG -> mCurrLine > gspStartLine[pPNG -> mPhase])
1416
                {
1417
                        Byte*        pPrior        = pPNG -> mpLastLine + 1;
1418
                        Byte*        pRaw        = pPNG -> mpThisLine + 1;
1419
                        Byte*        pEnd        = pPNG->mpThisLine + pPNG->mPhaseSz;
1420

    
1421
                        while (pRaw < pEnd)
1422
                        {
1423
                                *pRaw++ += *pPrior++;
1424
                        }
1425
                }
1426
                break;
1427
        case PNG_FILTER_AVG:
1428
                /* Avg(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) */
1429
                if (pPNG -> mCurrLine > gspStartLine[pPNG -> mPhase])
1430
                {
1431
                        Byte*        pPrior        = pPNG -> mpLastLine + 1;
1432
                        Byte*        pRaw_bpp= pPNG -> mpThisLine + 1;
1433
                        Byte*        pRaw        = pRaw_bpp;
1434
                        Byte*        pEnd        = pPNG->mpThisLine + pPNG->mPhaseSz;
1435
                        Byte*        pEnd2        = pRaw + pPNG -> mBPP;
1436

    
1437
                        while ((pRaw < pEnd2) && (pRaw < pEnd))
1438
                        {
1439
                                *pRaw++ += (*pPrior++/2);
1440
                        }
1441

    
1442
                        while (pRaw < pEnd)
1443
                        {
1444
                                *pRaw++ += (Byte)(((int)*pRaw_bpp++ + (int)*pPrior++)/2);
1445
                        }
1446
                }
1447
                else
1448
                {
1449
                        Byte*        pRaw_bpp= pPNG -> mpThisLine + 1;
1450
                        Byte*        pRaw        = pRaw_bpp + pPNG -> mBPP;
1451
                        Byte*        pEnd        = pPNG->mpThisLine + pPNG->mPhaseSz;
1452

    
1453
                        while (pRaw < pEnd)
1454
                        {
1455
                                *pRaw++ += (*pRaw_bpp++/2);
1456
                        }
1457
                }
1458
                break;
1459
        case PNG_FILTER_PAETH:
1460
                /* Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x),
1461
                                Prior(x-bpp)) */
1462
                if (pPNG -> mCurrLine > gspStartLine[pPNG -> mPhase])
1463
                {
1464
                        Byte*        pPrior_bpp        = pPNG -> mpLastLine + 1;
1465
                        Byte*        pPrior                = pPrior_bpp;
1466
                        Byte*        pRaw_bpp        = pPNG -> mpThisLine + 1;
1467
                        Byte*        pRaw                = pRaw_bpp;
1468
                        Byte*        pEnd                = pPNG->mpThisLine + pPNG->mPhaseSz;
1469
                        Byte*        pEnd2                = pRaw_bpp + pPNG -> mBPP;
1470

    
1471
                        while ((pRaw < pEnd) && (pRaw < pEnd2))
1472
                        {
1473
                                *pRaw++ += *pPrior++;
1474
                        }
1475

    
1476
                        while (pRaw < pEnd)
1477
                        {
1478
                                *pRaw++ += Paeth(*pRaw_bpp++, *pPrior++, *pPrior_bpp++);
1479
                        }
1480
                }
1481
                else
1482
                {
1483
                        Byte*        pRaw_bpp= pPNG -> mpThisLine + 1;
1484
                        Byte*        pRaw        = pRaw_bpp + pPNG -> mBPP;
1485
                        Byte*        pEnd        = pPNG->mpThisLine + pPNG->mPhaseSz;
1486

    
1487
                        while (pRaw < pEnd)
1488
                        {
1489
                                *pRaw++ += *pRaw_bpp++;
1490
                        }
1491
                }
1492
                break;
1493
        default:
1494
                Tcl_SetResult(interp, "Invalid filter type", TCL_STATIC);
1495
                return TCL_ERROR;
1496
        }
1497

    
1498
        return TCL_OK;
1499
}
1500

    
1501
static int
1502
DecodeLine(Tcl_Interp* interp, PNGImage* pPNG)
1503
{
1504
        Byte*        pixelPtr        = pPNG -> mBlock.pixelPtr;
1505
        int                bitScale        = 1;        /* Scale factor for RGB/Gray bit depths < 8 */
1506
        uLong        colNum                = 0;        /* Current pixel column */
1507
        Byte        chan                = 0;        /* Current channel (0..3) = (R, G, B, A) */
1508
        Byte        readByte        = 0;        /* Current scan line byte */
1509
        int                haveBits        = 0;        /* Number of bits remaining in current byte */
1510
        Byte        pixBits                = 0;        /* Extracted bits for current channel */
1511
        int                shifts                = 0;        /* Number of channels extracted from byte */
1512
        uLong        offset                = 0;        /* Current offset into pixelPtr */
1513
        Byte        colStep                = 1;        /* Column increment each pass */
1514
        Byte        pixStep                = 0;        /* extra pixelPtr increment each pass */
1515
        Byte        pLastPixel[6];
1516
        Byte*        p                        = pPNG -> mpThisLine + 1;
1517

    
1518
        if (UnfilterLine(interp, pPNG) == TCL_ERROR)
1519
                return TCL_ERROR;
1520

    
1521
        /*
1522
         * Calculate scale factor for bit depths less than 8, in order to
1523
         * adjust them to a minimum of 8 bits per pixel in the Tk image.
1524
         */
1525

    
1526
        if (pPNG -> mBitDepth < 8)
1527
                bitScale = 255/(pow(2, pPNG -> mBitDepth)-1);
1528

    
1529
        if (pPNG -> mInterlace)
1530
        {
1531
                switch (pPNG -> mPhase)
1532
                {
1533
                case 1:                                /* Phase 1: */
1534
                        colStep        = 8;        /* 1 pixel per block of 8 per line */
1535
                        break;                        /* Start at column 0 */
1536
                case 2:                                /* Phase 2: */
1537
                        colStep        = 8;        /* 1 pixels per block of 8 per line */
1538
                        colNum        = 4;        /* Start at column 4 */
1539
                        break;
1540
                case 3:                                /* Phase 3: */
1541
                        colStep        = 4;        /* 2 pixels per block of 8 per line */
1542
                        break;                        /* Start at column 0 */
1543
                case 4:                                /* Phase 4: */
1544
                        colStep        = 4;        /* 2 pixels per block of 8 per line */
1545
                        colNum        = 2;        /* Start at column 2 */
1546
                        break;
1547
                case 5:                                /* Phase 5: */
1548
                        colStep        = 2;        /* 4 pixels per block of 8 per line */
1549
                        break;                        /* Start at column 0 */
1550
                case 6:                                /* Phase 6: */
1551
                        colStep        = 2;        /* 4 pixels per block of 8 per line */
1552
                        colNum        = 1;        /* Start at column 1 */
1553
                        break;
1554
                                                        /* Phase 7: */
1555
                                                        /* 8 pixels per block of 8 per line */
1556
                                                        /* Start at column 0 */
1557
                }
1558
        }
1559

    
1560
        /* Calculate offset into pixelPtr for the first pixel of the line */
1561

    
1562
        offset        = pPNG -> mCurrLine * pPNG -> mBlock.pitch;
1563

    
1564
        /* Adjust up for the starting pixel of the line */
1565

    
1566
        offset += colNum * pPNG -> mBlock.pixelSize;
1567

    
1568
        /* Calculate the extra number of bytes to skip between columns */
1569

    
1570
        pixStep = (colStep - 1) * pPNG -> mBlock.pixelSize;
1571

    
1572
        for ( ; colNum < pPNG -> mWidth ; colNum += colStep)
1573
        {
1574
                if (haveBits < (pPNG -> mBitDepth * pPNG -> mChannels))
1575
                        haveBits = 0;
1576

    
1577
                for (chan = 0 ; chan < pPNG -> mChannels ; chan++)
1578
                {
1579
                        if (!haveBits)
1580
                        {
1581
                                shifts = 0;
1582

    
1583
                                readByte = *p++;
1584

    
1585
                                haveBits += 8;
1586
                        }
1587

    
1588
                        if (16 == pPNG -> mBitDepth)
1589
                        {
1590
                                pPNG->mBlock.pixelPtr[offset++] = readByte;
1591

    
1592
                                if (pPNG -> mUseTRNS)
1593
                                        pLastPixel[chan * 2] = readByte;
1594

    
1595
                                readByte = *p++;
1596

    
1597
                                if (pPNG -> mUseTRNS)
1598
                                        pLastPixel[(chan * 2) + 1] = readByte;
1599

    
1600
                                pPNG->mBlock.pixelPtr[offset++] = readByte;
1601

    
1602
                                haveBits = 0;
1603
                                continue;
1604
                        }
1605

    
1606
                        switch (pPNG -> mBitDepth)
1607
                        {
1608
                        case 1:
1609
                                pixBits = (readByte >> (7 - shifts)) & 0x01;
1610
                                break;
1611
                        case 2:
1612
                                pixBits = (readByte >> (6 - shifts*2)) & 0x03;
1613
                                break;
1614
                        case 4:
1615
                                pixBits = (readByte >> (4 - shifts*4)) & 0x0f;
1616
                                break;
1617
                        case 8:
1618
                                pixBits = readByte;
1619
                                break;
1620
                        }
1621

    
1622
                        if (PNG_COLOR_PLTE == pPNG -> mColorType)
1623
                        {
1624
                                pixelPtr[offset++] = pPNG -> mpPalette[pixBits].mRed;
1625
                                pixelPtr[offset++] = pPNG -> mpPalette[pixBits].mGrn;
1626
                                pixelPtr[offset++] = pPNG -> mpPalette[pixBits].mBlu;
1627
                                pixelPtr[offset++] = pPNG -> mpPalette[pixBits].mAlpha;
1628
                                chan += 2;
1629
                        }
1630
                        else
1631
                        {
1632
                                pixelPtr[offset++] = pixBits * bitScale;
1633

    
1634
                                if (pPNG -> mUseTRNS)
1635
                                        pLastPixel[chan] = pixBits;
1636
                        }
1637

    
1638
                        haveBits -= pPNG -> mBitDepth;
1639
                        shifts++;
1640
                }
1641

    
1642
                /*
1643
                 * Apply boolean transparency via tRNS data if necessary
1644
                 * (where necessary means a tRNS chunk was provided and
1645
                 * we're not using an alpha channel or indexed alpha).
1646
                 */
1647

    
1648
                if ((PNG_COLOR_PLTE != pPNG -> mColorType) &&
1649
                        ((pPNG -> mColorType & PNG_COLOR_ALPHA) == 0))
1650
                {
1651
                        Byte alpha;
1652

    
1653
                        if (pPNG -> mUseTRNS)
1654
                        {
1655
                                if (memcmp(pLastPixel, pPNG -> mpTrans, pPNG -> mBPP) == 0)
1656
                                        alpha = 0x00;
1657
                                else
1658
                                        alpha = 0xff;
1659
                        }
1660
                        else
1661
                        {
1662
                                alpha = 0xff;
1663
                        }
1664

    
1665
                        pixelPtr[offset++] = alpha;
1666

    
1667
                        if (16 == pPNG -> mBitDepth)
1668
                                pixelPtr[offset++] = alpha;
1669
                }
1670

    
1671
                offset += pixStep;
1672
        }
1673

    
1674
        if (pPNG -> mInterlace)
1675
        {
1676
                /* Skip lines */
1677

    
1678
                switch (pPNG -> mPhase)
1679
                {
1680
                case 1: case 2: case 3:
1681
                        pPNG -> mCurrLine += 8;
1682
                        break;
1683
                case 4: case 5:
1684
                        pPNG -> mCurrLine += 4;
1685
                        break;
1686
                case 6: case 7:
1687
                        pPNG -> mCurrLine += 2;
1688
                        break;
1689
                }
1690

    
1691
                /* Start the next phase if there are no more lines to do */
1692

    
1693
                if (pPNG -> mCurrLine >= pPNG -> mHeight)
1694
                {
1695
                        uLong pixels        = 0;
1696

    
1697
                        while ((!pixels || (pPNG -> mCurrLine >= pPNG -> mHeight)) &&
1698
                                (pPNG->mPhase<7))
1699
                        {
1700
                                pPNG -> mPhase++;
1701

    
1702
                                switch (pPNG -> mPhase)
1703
                                {
1704
                                case 2:
1705
                                        pixels = (pPNG -> mWidth + 3) >> 3;
1706
                                        pPNG -> mCurrLine        = 0;
1707
                                        break;
1708
                                case 3:
1709
                                        pixels = (pPNG -> mWidth + 3) >> 2;
1710
                                        pPNG -> mCurrLine        = 4;
1711
                                        break;
1712
                                case 4:
1713
                                        pixels = (pPNG -> mWidth + 1) >> 2;
1714
                                        pPNG -> mCurrLine        = 0;
1715
                                        break;
1716
                                case 5:
1717
                                        pixels = (pPNG -> mWidth + 1) >> 1;
1718
                                        pPNG -> mCurrLine        = 2;
1719
                                        break;
1720
                                case 6:
1721
                                        pixels = (pPNG -> mWidth) >> 1;
1722
                                        pPNG -> mCurrLine        = 0;
1723
                                        break;
1724
                                case 7:
1725
                                        pPNG -> mCurrLine        = 1;
1726
                                        pixels                                = pPNG -> mWidth;
1727
                                        break;
1728
                                }
1729
                        }
1730

    
1731
                        if (16 == pPNG -> mBitDepth)
1732
                        {
1733
                                pPNG -> mPhaseSz = 1 + (pPNG -> mChannels * pixels * 2);
1734
                        }
1735
                        else
1736
                        {
1737
                                pPNG -> mPhaseSz = 1 + (((pPNG -> mChannels * pixels *
1738
                                        pPNG -> mBitDepth) + 7) >> 3);
1739
                        }
1740
                }
1741
        }
1742
        else
1743
        {
1744
                pPNG -> mCurrLine++;
1745
        }
1746

    
1747
        return TCL_OK;
1748
}
1749

    
1750

    
1751
/*
1752
 *----------------------------------------------------------------------
1753
 *
1754
 * ReadIDAT --
1755
 *
1756
 *                This function reads the IDAT (pixel data) chunk from the
1757
 *                PNG file to build the image.  It will continue reading until
1758
 *                all IDAT chunks have been processed or an error occurs.
1759
 *
1760
 * Results:
1761
 *                TCL_OK, or TCL_ERROR if an I/O error occurs or an IDAT
1762
 *                chunk is invalid.
1763
 *
1764
 * Side effects:
1765
 *                The access position in f advances.  Memory may be allocated
1766
 *                by zlib through PNGZAlloc.
1767
 *
1768
 *----------------------------------------------------------------------
1769
 */
1770

    
1771
static int
1772
ReadIDAT(Tcl_Interp* interp, PNGImage* pPNG, uLong chunkSz, uLong crc)
1773
{
1774
        Byte        pInput[PNG_BLOCK_SZ];
1775
        uLong        lineCount        = 0;
1776

    
1777
        /* Process IDAT contents until there is no more in this chunk */
1778

    
1779
        while (chunkSz)
1780
        {
1781
                int blockSz = (chunkSz > PNG_BLOCK_SZ) ? PNG_BLOCK_SZ : chunkSz;
1782
                int ret;
1783

    
1784
                /* Read the next bit of IDAT chunk data, up to read buffer size */
1785

    
1786
                if (PNGRead(interp, pPNG, pInput, blockSz, &crc) == TCL_ERROR)
1787
                        return TCL_ERROR;
1788

    
1789
                chunkSz -= blockSz;
1790

    
1791
                /* Run inflate() until output buffer is not full. */
1792

    
1793
                pPNG -> mZStream.avail_in = blockSz;
1794
                pPNG -> mZStream.next_in = pInput;
1795

    
1796
                do {
1797
                        ret = inflate(&pPNG -> mZStream, Z_NO_FLUSH);
1798

    
1799
                        switch (ret)
1800
                        {
1801
                        case Z_STREAM_ERROR:
1802
                                break;
1803
                        case Z_NEED_DICT:
1804
                        case Z_DATA_ERROR:
1805
                        case Z_MEM_ERROR:
1806
                                Tcl_SetResult(interp, "zlib inflation failed", TCL_STATIC);
1807
                                return TCL_ERROR;
1808
                        }
1809

    
1810
                        /* Process pixels when a full scan line has been obtained */
1811

    
1812
                        if (!pPNG -> mZStream.avail_out)
1813
                        {
1814
                                Byte*        temp;
1815
                                lineCount++;
1816

    
1817
                                if (pPNG -> mPhase > 7)
1818
                                {
1819
                                        Tcl_SetResult(interp,
1820
                                                "Extra data after final scan line of final phase",
1821
                                                TCL_STATIC);
1822
                                        return TCL_ERROR;
1823
                                }
1824

    
1825
                                if (DecodeLine(interp, pPNG) == TCL_ERROR)
1826
                                        return TCL_ERROR;
1827

    
1828
                                /*
1829
                                 * Swap the current/last lines so that we always have the last
1830
                                 * line processed available, which is necessary for filtering.
1831
                                 */
1832

    
1833
                                temp = pPNG -> mpLastLine;
1834
                                pPNG -> mpLastLine = pPNG -> mpThisLine;
1835
                                pPNG -> mpThisLine = temp;
1836

    
1837
                                /* Next pass through, inflate into the new current line */
1838

    
1839
                                pPNG -> mZStream.avail_out        = pPNG -> mPhaseSz;
1840
                                pPNG -> mZStream.next_out        = pPNG -> mpThisLine;
1841
                        }
1842
                } while (pPNG -> mZStream.avail_in);
1843

    
1844
                /* Check for end of zlib stream */
1845

    
1846
                if (ret == Z_STREAM_END)
1847
                {
1848
                        if (chunkSz)
1849
                        {
1850
                                Tcl_SetResult(interp, "Extra data after end of zlib stream",
1851
                                        TCL_STATIC);
1852
                                return TCL_ERROR;
1853
                        }
1854

    
1855
                        break;
1856
                }
1857
        }
1858

    
1859
        if (CheckCRC(interp, pPNG, crc) == TCL_ERROR)
1860
                return TCL_ERROR;
1861

    
1862
        return TCL_OK;
1863
}
1864

    
1865

    
1866
/*
1867
 *----------------------------------------------------------------------
1868
 *
1869
 * PNGDecode --
1870
 *
1871
 *                This function handles the entirety of reading a PNG file (or
1872
 *                data) from the first byte to the last.
1873
 *
1874
 * Results:
1875
 *                TCL_OK, or TCL_ERROR if an I/O error occurs or any problems
1876
 *                are detected in the PNG file.
1877
 *
1878
 * Side effects:
1879
 *                The access position in f advances.  Memory may be allocated
1880
 *                and image dimensions and contents may change.
1881
 *
1882
 *----------------------------------------------------------------------
1883
 */
1884

    
1885
static int
1886
PNGDecode(Tcl_Interp* interp, PNGImage* pPNG, Tcl_Obj* format,
1887
        Tk_PhotoHandle imageHandle, int destX, int destY)
1888
{
1889
        double                alpha                = 1.0;
1890
        Tcl_Obj**        objv                = NULL;
1891
        int                        objc                = 0;
1892
        uLong                chunkType;
1893
        uLong                chunkSz;
1894
        uLong                crc;
1895

    
1896
        static const char* fmtOptions[] = {
1897
                "png", "-alpha", (char *)NULL
1898
        };
1899

    
1900
        enum fmtOptions {
1901
                OPT_PNG, OPT_ALPHA
1902
        };
1903

    
1904
        /* Parse the PNG signature and IHDR (header) chunk */
1905

    
1906
        if (ReadIHDR(interp, pPNG) == TCL_ERROR)
1907
                return TCL_ERROR;
1908

    
1909
        if (format && (Tcl_ListObjGetElements(interp, format,
1910
                        &objc, &objv) == TCL_ERROR))
1911
                return TCL_ERROR;
1912

    
1913
        while (objc)
1914
        {
1915
            int optIndex;
1916

    
1917
        if (Tcl_GetIndexFromObj(interp, objv[0], fmtOptions, "option", 0,
1918
                                &optIndex) == TCL_ERROR)
1919
            return TCL_ERROR;
1920

    
1921
                if (OPT_PNG == optIndex)
1922
                {
1923
                        objc--; objv++;
1924
                        continue;
1925
                }
1926

    
1927
            if (objc < 2)
1928
            {
1929
                Tcl_WrongNumArgs(interp, 1, objv, "value");
1930
                return TCL_ERROR;
1931
            }
1932

    
1933
                objc--; objv++;
1934

    
1935
                switch ((enum fmtOptions) optIndex)
1936
                {
1937
                case OPT_PNG:
1938
                        break;
1939

    
1940
                case OPT_ALPHA:
1941
                        if (Tcl_GetDoubleFromObj(interp, objv[0], &alpha) == TCL_ERROR)
1942
                                return TCL_ERROR;
1943

    
1944
                        if ((alpha < 0.0) || (alpha > 1.0))
1945
                        {
1946
                                Tcl_SetResult(interp,
1947
                                        "-alpha value must be between 0.0 and 1.0",
1948
                                        TCL_STATIC);
1949
                                return TCL_ERROR;
1950
                        }
1951
                        break;
1952
                }
1953

    
1954
                objc--; objv++;
1955
        }
1956

    
1957
        /*
1958
         * The next chunk may either be a PLTE (Palette) chunk or the first
1959
         * of at least one IDAT (data) chunks.  It could also be one of
1960
         * a number of ancillary chunks, but those are skipped by for us by
1961
         * the switch in ReadChunkHeader().
1962
         *
1963
         * This chunk is mandatory for color type 3 and forbidden for 2 and 6
1964
         */
1965

    
1966
        if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
1967
                        &crc) == TCL_ERROR)
1968
                return TCL_ERROR;
1969

    
1970
        if (CHUNK_PLTE == chunkType)
1971
        {
1972
                /* Finish parsing the PLTE chunk */
1973

    
1974
                if (ReadPLTE(interp, pPNG, chunkSz, chunkType, crc) == TCL_ERROR)
1975
                        return TCL_ERROR;
1976

    
1977
                /* Begin the next chunk */
1978

    
1979
                if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
1980
                                &crc) == TCL_ERROR)
1981
                        return TCL_ERROR;
1982
        }
1983
        else
1984
        if (PNG_COLOR_PLTE == pPNG -> mColorType)
1985
        {
1986
                Tcl_SetResult(interp, "PLTE chunk required for indexed color",
1987
                        TCL_STATIC);
1988
                return TCL_ERROR;
1989
        }
1990

    
1991
        /*
1992
         * The next chunk may be a tRNS (palette transparency) chunk,
1993
         * depending on the color type. It must come after the PLTE
1994
         * chunk and before the IDAT chunk, but can be present if there
1995
         * is no PLTE chunk because it can be used for Grayscale and
1996
         * TrueColor in lieu of an alpha channel.
1997
         */
1998

    
1999
        if (CHUNK_tRNS == chunkType)
2000
        {
2001
                if (ReadtRNS(interp, pPNG, chunkSz, crc) == TCL_ERROR)
2002
                        return TCL_ERROR;
2003

    
2004
                if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
2005
                                &crc) == TCL_ERROR)
2006
                        return TCL_ERROR;
2007
        }
2008

    
2009
        /*
2010
         * Other ancillary chunk types could appear here, but for now we're
2011
         * only interested in IDAT.  The others should have been skipped.
2012
         */
2013

    
2014
        if (CHUNK_IDAT != chunkType)
2015
        {
2016
                Tcl_SetResult(interp, "At least one IDAT chunk is required",
2017
                        TCL_STATIC);
2018
                return TCL_ERROR;
2019
        }
2020

    
2021
        /*
2022
         * Expand the photo size (if not set by the user) to provide enough
2023
         * space for the image being parsed.
2024
         */
2025

    
2026
        Tk_PhotoExpand(INTERP_IF_TK85 imageHandle, destX + pPNG -> mWidth,
2027
                destY + pPNG -> mHeight);
2028

    
2029
        /*
2030
         * A scan line consists of one byte for a filter type, plus
2031
         * the number of bits per color sample times the number of
2032
         * color samples per pixel.
2033
         */
2034

    
2035
        if (16 == pPNG -> mBitDepth)
2036
        {
2037
                pPNG -> mLineSz = 1 + (pPNG -> mChannels * pPNG -> mWidth * 2);
2038
        }
2039
        else
2040
        {
2041
                pPNG -> mLineSz = 1 + ((pPNG -> mChannels * pPNG -> mWidth) /
2042
                        (8 / pPNG -> mBitDepth));
2043
                if (pPNG -> mWidth % (8 / pPNG -> mBitDepth))
2044
                        pPNG -> mLineSz++;
2045
        }
2046

    
2047
        /* Allocate space for decoding the scan lines */
2048

    
2049
        pPNG -> mpLastLine        = (Byte*) attemptckalloc(pPNG -> mLineSz);
2050
        pPNG -> mpThisLine        = (Byte*) attemptckalloc(pPNG -> mLineSz);
2051
        pPNG -> mBlock.pixelPtr        = (Byte*) attemptckalloc(pPNG -> mBlockSz);
2052

    
2053
        if (!pPNG -> mpLastLine || !pPNG -> mpThisLine || !pPNG -> mBlock.pixelPtr)
2054
        {
2055
                Tcl_SetResult(interp, "Memory allocation failed", TCL_STATIC);
2056
                return TCL_ERROR;
2057
        }
2058

    
2059
        if (pPNG -> mInterlace)
2060
        {       uLong pixels;
2061
                /* Only one pixel per block of 8 per line in the first phase */
2062
                pPNG -> mPhase                = 1;
2063

    
2064
                pixels = (pPNG -> mWidth + 7) >> 3;
2065

    
2066
                if (16 == pPNG -> mBitDepth)
2067
                {
2068
                        pPNG -> mPhaseSz = 1 + (pPNG -> mChannels * pixels * 2);
2069
                }
2070
                else
2071
                {
2072
                        pPNG -> mPhaseSz = 1 +
2073
                                (((pPNG -> mChannels * pixels * pPNG -> mBitDepth) + 7) >> 3);
2074
                }
2075
        }
2076
        else
2077
        {
2078
                pPNG -> mPhaseSz = pPNG -> mLineSz;
2079
        }
2080
        pPNG -> mZStream.avail_out        = pPNG -> mPhaseSz;
2081
        pPNG -> mZStream.next_out        = pPNG -> mpThisLine;
2082

    
2083
        /* All of the IDAT (data) chunks must be consecutive */
2084

    
2085
        while (CHUNK_IDAT == chunkType)
2086
        {
2087
                if (ReadIDAT(interp, pPNG, chunkSz, crc) == TCL_ERROR)
2088
                        return TCL_ERROR;
2089

    
2090
                if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
2091
                                &crc) == TCL_ERROR)
2092
                        return TCL_ERROR;
2093
        }
2094

    
2095
        /* Now skip the remaining chunks which we're also not interested in. */
2096

    
2097
        while (CHUNK_IEND != chunkType)
2098
        {
2099
                if (SkipChunk(interp, pPNG, chunkSz, crc) == TCL_ERROR)
2100
                        return TCL_ERROR;
2101

    
2102
                if (ReadChunkHeader(interp, pPNG, &chunkSz, &chunkType,
2103
                                &crc) == TCL_ERROR)
2104
                        return TCL_ERROR;
2105
        }
2106

    
2107
        /* Got the IEND (end of image) chunk.  Do some final checks... */
2108

    
2109
        if (chunkSz)
2110
        {
2111
                Tcl_SetResult(interp, "IEND chunk contents must be empty", TCL_STATIC);
2112
                return TCL_ERROR;
2113
        }
2114

    
2115
        /* Check the CRC on the IEND chunk */
2116

    
2117
        if (CheckCRC(interp, pPNG, crc) == TCL_ERROR)
2118
                return TCL_ERROR;
2119

    
2120
        /*
2121
         * TODO: verify that nothing else comes after the IEND chunk, or do
2122
         * we really care?
2123
         */
2124

    
2125
#if 0
2126
        if (PNGRead(interp, pPNG, &c, 1, NULL) != TCL_ERROR)
2127
        {
2128
                Tcl_SetResult(interp, "Extra data following IEND chunk", TCL_STATIC);
2129
                return TCL_ERROR;
2130
        }
2131
#endif
2132

    
2133
        /* Apply overall image alpha if specified */
2134

    
2135
        if (alpha != 1.0)
2136
        {
2137
                unsigned char*        p                = pPNG -> mBlock.pixelPtr;
2138
                unsigned char*        pEnd        = p + pPNG -> mBlockSz;
2139
                int                                offset        = pPNG -> mBlock.offset[3];
2140

    
2141
                p += offset;
2142

    
2143
                if (16 == pPNG -> mBitDepth)
2144
                {
2145
                        int channel;
2146

    
2147
                        while (p < pEnd)
2148
                        {
2149
                                channel = ((p[0] << 8) | p[1]) * alpha;
2150

    
2151
                                *p++ = channel >> 8;
2152
                                *p++ = channel & 0xff;
2153

    
2154
                                p += offset;
2155
                        }
2156
                }
2157
                else
2158
                {
2159
                        while (p < pEnd)
2160
                        {
2161
                                *p++ *= alpha;
2162

    
2163
                                p += offset;
2164
                        }
2165
                }
2166
        }
2167

    
2168
        Tk_PhotoPutBlock(INTERP_IF_TK85 imageHandle, &pPNG -> mBlock, destX, destY,
2169
                pPNG -> mBlock.width, pPNG -> mBlock.height, TK_PHOTO_COMPOSITE_SET);
2170

    
2171
        return TCL_OK;
2172
}
2173

    
2174

    
2175
/*
2176
 *----------------------------------------------------------------------
2177
 *
2178
 * FileMatchPNG --
2179
 *
2180
 *                This function is invoked by the photo image type to see if
2181
 *                a file contains image data in PNG format.
2182
 *
2183
 * Results:
2184
 *                The return value is 1 if the first characters in file f look
2185
 *                like PNG data, and 0 otherwise.
2186
 *
2187
 * Side effects:
2188
 *                The access position in f may change.
2189
 *
2190
 *----------------------------------------------------------------------
2191
 */
2192

    
2193
static int
2194
FileMatchPNG(chan, fileName, format, widthPtr, heightPtr, interp)
2195
        Tcl_Channel chan;                        /* The image file, open for reading. */
2196
        CONST char *fileName;                /* The name of the image file. */
2197
        Tcl_Obj *format;                        /* User-specified format object, or NULL. */
2198
        int *widthPtr, *heightPtr;        /* The dimensions of the image are
2199
                                                                 * returned here if the file is a valid
2200
                                                                 * raw PNG file. */
2201
        Tcl_Interp *interp;                        /* not used */
2202
{
2203
        PNGImage                png;
2204
        int                                match = 0;
2205
        Tcl_SavedResult        sya;
2206

    
2207
        Tcl_SaveResult(interp, &sya);
2208

    
2209
        PNGInit(interp, &png, chan, NULL, PNG_DECODE);
2210

    
2211
        if (ReadIHDR(interp, &png) == TCL_OK)
2212
        {
2213
                *widthPtr        = (int)png.mWidth;
2214
                *heightPtr        = (int)png.mHeight;
2215
                match                = 1;
2216
        }
2217

    
2218
        PNGCleanup(&png);
2219
        Tcl_RestoreResult(interp, &sya);
2220

    
2221
        return match;
2222
}
2223

    
2224

    
2225
/*
2226
 *----------------------------------------------------------------------
2227
 *
2228
 * FileReadPNG --
2229
 *
2230
 *                This function is called by the photo image type to read
2231
 *                PNG format data from a file and write it into a given
2232
 *                photo image.
2233
 *
2234
 * Results:
2235
 *                A standard TCL completion code.  If TCL_ERROR is returned
2236
 *                then an error message is left in the interp's result.
2237
 *
2238
 * Side effects:
2239
 *                The access position in file f is changed, and new data is
2240
 *                added to the image given by imageHandle.
2241
 *
2242
 *----------------------------------------------------------------------
2243
 */
2244

    
2245
static int
2246
FileReadPNG(interp, chan, fileName, format, imageHandle, destX, destY,
2247
                width, height, srcX, srcY)
2248
        Tcl_Interp *interp;                        /* Interpreter to use for reporting errors. */
2249
        Tcl_Channel chan;                        /* The image file, open for reading. */
2250
        CONST char *fileName;                /* The name of the image file. */
2251
        Tcl_Obj *format;                        /* User-specified format object, or NULL. */
2252
        Tk_PhotoHandle imageHandle;        /* The photo image to write into. */
2253
        int destX, destY;                        /* Coordinates of top-left pixel in
2254
                                                                 * photo image to be written to. */
2255
        int width, height;                        /* Dimensions of block of photo image to
2256
                                                                 * be written to. */
2257
        int srcX, srcY;                                /* Coordinates of top-left pixel to be used
2258
                                                                 * in image being read. */
2259
{
2260
        PNGImage                png;
2261
        int                                result                = TCL_ERROR;
2262

    
2263
        result = PNGInit(interp, &png, chan, NULL, PNG_DECODE);
2264

    
2265
        if (TCL_OK == result)
2266
                result = PNGDecode(interp, &png, format, imageHandle, destX, destY);
2267

    
2268
        PNGCleanup(&png);
2269
        return result;
2270
}
2271

    
2272

    
2273
/*
2274
 *----------------------------------------------------------------------
2275
 *
2276
 * StringMatchPNG --
2277
 *
2278
 *  This function is invoked by the photo image type to see if
2279
 *  an object contains image data in PNG format.
2280
 *
2281
 * Results:
2282
 *  The return value is 1 if the first characters in the data are
2283
 *  like PNG data, and 0 otherwise.
2284
 *
2285
 * Side effects:
2286
 *  the size of the image is placed in widthPre and heightPtr.
2287
 *
2288
 *----------------------------------------------------------------------
2289
 */
2290

    
2291
static int
2292
StringMatchPNG(dataObj, format, widthPtr, heightPtr, interp)
2293
        Tcl_Obj *dataObj;                        /* the object containing the image data */
2294
        Tcl_Obj *format;                        /* the image format object, or NULL */
2295
        int *widthPtr;                                /* where to put the string width */
2296
        int *heightPtr;                                /* where to put the string height */
2297
        Tcl_Interp *interp;                        /* not used */
2298
{
2299
        PNGImage                png;
2300
        int                                match                = 0;
2301
        Tcl_SavedResult        sya;
2302

    
2303
        Tcl_SaveResult(interp, &sya);
2304
        PNGInit(interp, &png, (Tcl_Channel)NULL, dataObj, PNG_DECODE);
2305

    
2306
        png.mpStrData = Tcl_GetByteArrayFromObj(dataObj, &png.mStrDataSz);
2307

    
2308
        if (ReadIHDR(interp, &png) == TCL_OK)
2309
        {
2310
                *widthPtr        = (int)png.mWidth;
2311
                *heightPtr        = (int)png.mHeight;
2312
                match                = 1;
2313
        }
2314

    
2315
        PNGCleanup(&png);
2316
        Tcl_RestoreResult(interp, &sya);
2317

    
2318
        return match;
2319
}
2320

    
2321

    
2322
/*
2323
 *----------------------------------------------------------------------
2324
 *
2325
 * StringReadPNG -- --
2326
 *
2327
 *                This function is called by the photo image type to read
2328
 *                PNG format data from an object and give it to the photo image.
2329
 *
2330
 * Results:
2331
 *                A standard TCL completion code.  If TCL_ERROR is returned
2332
 *                then an error message is left in the interp's result.
2333
 *
2334
 * Side effects:
2335
 *                new data is added to the image given by imageHandle.
2336
 *
2337
 *----------------------------------------------------------------------
2338
 */
2339

    
2340
static int
2341
StringReadPNG(interp, dataObj, format, imageHandle,
2342
                destX, destY, width, height, srcX, srcY)
2343
        Tcl_Interp *interp;                                /* interpreter for reporting errors in */
2344
        Tcl_Obj *dataObj;                                /* object containing the image */
2345
        Tcl_Obj *format;                                /* format object, or NULL */
2346
        Tk_PhotoHandle imageHandle;                /* the image to write this data into */
2347
        int destX, destY;                                /* The rectangular region of the  */
2348
        int width, height;                                /*   image to copy */
2349
        int srcX, srcY;
2350
{
2351
        PNGImage        png;
2352
        int                        result                = TCL_ERROR;
2353

    
2354
        result = PNGInit(interp, &png, (Tcl_Channel)NULL,
2355
                dataObj, PNG_DECODE);
2356

    
2357
        if (TCL_OK == result)
2358
                result = PNGDecode(interp, &png, format, imageHandle, destX, destY);
2359

    
2360
        PNGCleanup(&png);
2361
        return result;
2362
}
2363

    
2364
/* Write is far from done yet.  Don't enable this code. */
2365
#if 0
2366
static int
2367
WriteByte(Tcl_Interp* interp, PNGImage* pPNG, Byte c, uLong* pCRC)
2368
{
2369
        return WriteBytes(interp, pPNG, &c, 1, pCRC);
2370
}
2371

2372
static int
2373
WriteBytes(Tcl_Interp* interp, PNGImage* pPNG,
2374
        const void* pSrc, uLong srcSz, uLong* pCRC)
2375
{
2376
        if (pCRC && pSrc && srcSz)
2377
                *pCRC = crc32(*pCRC, pSrc, srcSz);
2378

2379
        /*
2380
         * TODO: is Tcl_AppendObjToObj faster here? i.e., does Tcl join
2381
         * the objects immediately or store them in a multi-object rep?
2382
         */
2383

2384
        if (pPNG -> mpObjData)        /* ugh, wanna check result, not panic! */
2385
                Tcl_AppendToObj(pPNG -> mpObjData, pSrc, srcSz);
2386
        else
2387
        if (Tcl_WriteChars(pPNG -> mChannel, pSrc, srcSz) < 0)
2388
        {
2389
                /* TODO: reason */
2390

2391
                Tcl_SetResult(interp, "Write to channel failed", TCL_STATIC);
2392
                return TCL_ERROR;
2393
        }
2394

2395
        return TCL_OK;
2396
}
2397

2398
static int
2399
WriteLong(Tcl_Interp* interp, PNGImage* pPNG, uLong L, uLong* pCRC)
2400
{
2401
        Byte        pc[4];
2402

2403
        pc[0] = (Byte)((L & 0xff000000) >> 24);
2404
        pc[1] = (Byte)((L & 0x00ff0000) >> 16);
2405
        pc[2] = (Byte)((L & 0x0000ff00) >> 8);
2406
        pc[3] = (Byte)((L & 0x000000ff) >> 0);
2407

2408
        return WriteBytes(interp, pPNG, pc, 4, pCRC);
2409
}
2410

2411
static int
2412
WritePNG(Tcl_Interp* interp, Tk_PhotoImageBlock *blockPtr, PNGImage* pPNG)
2413
{
2414
        /* Write out the PNG Signature */
2415

2416
        if (WriteBytes(interp, pPNG, gspPNGSignature, gsSigSz, NULL) == TCL_ERROR)
2417
                return TCL_ERROR;
2418

2419
        if (WriteIHDR(interp, pPNG, blockPtr) == TCL_ERROR)
2420
                return TCL_ERROR;
2421

2422
        if (WriteChunk(interp, pPNG, CHUNK_IDAT, NULL, 0) == TCL_ERROR)
2423
                return TCL_ERROR;
2424

2425
        return WriteIEND(interp, pPNG);;
2426
}
2427

2428
static int
2429
WriteIHDR(Tcl_Interp* interp, PNGImage* pPNG, Tk_PhotoImageBlock *blockPtr)
2430
{
2431
        uLong        crc                = crc32(0, NULL, 0);
2432
        int                result        = TCL_OK;
2433

2434
        /* The IHDR (header) chunk has a fixed size of 13 bytes */
2435

2436
        result = WriteLong(interp, pPNG, 13, NULL);
2437

2438
        /* Write the IHDR Chunk Type */
2439

2440
        if (TCL_OK == result)
2441
                result = WriteLong(interp, pPNG, CHUNK_IHDR, &crc);
2442

2443
        /* Write the image width, height */
2444

2445
        if (TCL_OK == result)
2446
                result = WriteLong(interp, pPNG, blockPtr->width, &crc);
2447

2448
        if (TCL_OK == result)
2449
                result = WriteLong(interp, pPNG, blockPtr->height, &crc);
2450

2451
        /*
2452
         * TODO: Figure out appropriate bit depth, color type.  For now,
2453
         * just going to say it's 8bpp TrueColor.
2454
         */
2455

2456
        if (TCL_OK == result)
2457
                result = WriteByte(interp, pPNG, 8, &crc);
2458

2459
        if (TCL_OK == result)
2460
                result = WriteByte(interp, pPNG, PNG_COLOR_RGBA, &crc);
2461

2462
        if (TCL_OK == result)
2463
                result = WriteByte(interp, pPNG, PNG_COMPRESS_DEFLATE, &crc);
2464

2465
        if (TCL_OK == result)
2466
                result = WriteByte(interp, pPNG, PNG_FILTMETH_STANDARD, &crc);
2467

2468
        /* TODO: support interlace through -format */
2469

2470
        if (TCL_OK == result)
2471
                result = WriteByte(interp, pPNG, PNG_INTERLACE_NONE, &crc);
2472

2473
        /* Write out the CRC at the end of the chunk */
2474

2475
        if (TCL_OK == result)
2476
                result = WriteLong(interp, pPNG, crc, NULL);
2477

2478
        return result;
2479
}
2480

2481
static int
2482
WriteIEND(Tcl_Interp* interp, PNGImage* pPNG)
2483
{
2484
        return WriteChunk(interp, pPNG, CHUNK_IEND, NULL, 0);
2485
}
2486

2487
static int
2488
WriteChunk(Tcl_Interp* interp, PNGImage* pPNG,
2489
        uLong chunkType, const void* pData, uLong dataSz)
2490
{
2491
        uLong        crc                = crc32(0, NULL, 0);
2492
        int                result        = TCL_OK;
2493

2494
        /* Write the length field for the chunk */
2495

2496
        result = WriteLong(interp, pPNG, dataSz, NULL);
2497

2498
        /* Write the Chunk Type */
2499

2500
        if (TCL_OK == result)
2501
                result = WriteLong(interp, pPNG, chunkType, &crc);
2502

2503
        /* Write the contents (if any) */
2504

2505
        if (TCL_OK == result)
2506
                result = WriteBytes(interp, pPNG, pData, dataSz, &crc);
2507

2508
        /* Write out the CRC at the end of the chunk */
2509

2510
        if (TCL_OK == result)
2511
                result = WriteLong(interp, pPNG, crc, NULL);
2512

2513
        return result;
2514
}
2515

2516
static int
2517
FileWritePNG(Tcl_Interp* interp, CONST char* filename, Tcl_Obj* format,
2518
        Tk_PhotoImageBlock *blockPtr)
2519
{
2520
        Tcl_Channel        chan;
2521
        PNGImage        png;
2522
        int                        result                = TCL_ERROR;
2523

2524
        /* Open a Tcl file channel where the image data will be stored. */
2525

2526
        /* TODO: Support other channel types through format option? */
2527
        /* TODO: Support file mode through format option? */
2528

2529
        /* This is silly.  image photo ought to take care of this stuff. */
2530

2531
        chan = Tcl_OpenFileChannel(interp, filename, "w", 0644);
2532

2533
        if (!chan)
2534
                return TCL_ERROR;
2535

2536
        PNGInit(interp, &png, chan, NULL, PNG_ENCODE);
2537

2538
        /*
2539
     * Set the translation mode to binary so that CR and LF are not
2540
         * to the platform's EOL sequence.
2541
         */
2542

2543
        if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK)
2544
                goto cleanup;
2545

2546
        if (Tcl_SetChannelOption(interp, chan, "-encoding", "binary") != TCL_OK)
2547
                goto cleanup;
2548

2549
        /* Write the raw PNG data out to the file */
2550

2551
        result = WritePNG(interp, blockPtr, &png);
2552

2553
cleanup:
2554
        Tcl_Close(interp, chan);
2555
        PNGCleanup(&png);
2556
        return result;
2557
}
2558

2559
static int
2560
StringWritePNG(Tcl_Interp* interp, Tcl_Obj* format,
2561
        Tk_PhotoImageBlock *blockPtr)
2562
{
2563
        Tcl_Obj*        pObjResult        = Tcl_NewObj();
2564
        PNGImage        png;
2565
        int                        result                = TCL_ERROR;
2566

2567
        PNGInit(interp, &png, NULL, pObjResult, PNG_ENCODE);
2568

2569
        /*
2570
         * Write the raw PNG data into the prepared Tcl_Obj buffer
2571
         * Set the result back to the interpreter if successful.
2572
         */
2573

2574
        result = WritePNG(interp, blockPtr, &png);
2575

2576
        if (TCL_OK == result)
2577
                Tcl_SetObjResult(interp, png.mpObjData);
2578

2579
        PNGCleanup(&png);
2580
        return result;
2581
}
2582

2583
#endif
2584

    
2585
/* The format record for the PNG file format: */
2586

    
2587
Tk_PhotoImageFormat tkImgFmtPNG = {
2588
        "png",                                /* name */
2589
        FileMatchPNG,                /* fileMatchProc */
2590
        StringMatchPNG,                /* stringMatchProc */
2591
        FileReadPNG,                /* fileReadProc */
2592
        StringReadPNG,                /* stringReadProc */
2593
        NULL, /* FileWritePNG        */        /* fileWriteProc */
2594
        NULL  /* StringWritePNG        */        /* stringWriteProc */
2595
};
2596