Tk Source Code

Changes On Branch rfe-f285ddcd-animated-gif-metadata
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch rfe-f285ddcd-animated-gif-metadata Excluding Merge-Ins

This is equivalent to a diff from 85a539ba to a24d1f5d

2022-10-04
17:48
TIP632 return gif animated metadata: merge implementation, tests and documentation check-in: 832cdddb user: oehhar tags: trunk, main
17:35
TIP632 return gif animated metadata: add documentation Closed-Leaf check-in: a24d1f5d user: oehhar tags: rfe-f285ddcd-animated-gif-metadata
16:41
TIP632 return gif animated metadata: add tests check-in: 257f03d1 user: oehhar tags: rfe-f285ddcd-animated-gif-metadata
2022-09-21
06:05
merge 8.7 check-in: 00981494 user: fvogel tags: rfe-f285ddcd-animated-gif-metadata
2022-09-20
20:17
Merge 8.6 check-in: a39a4298 user: marc_culler tags: trunk, main
18:49
Merge main check-in: 69c8b427 user: marc_culler tags: mac_styles_87
18:12
Merge 8.6 check-in: 85a539ba user: marc_culler tags: trunk, main
18:11
Apply patch [313f102dbc] (parenthesize macro parameters) from Christopher Chavez. check-in: cb9772ac user: marc_culler tags: core-8-6-branch
17:49
Merge 8.6 check-in: adc4932b user: marc_culler tags: trunk, main

Changes to doc/photo.n.

738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
.PP
Each image has a metadata dictionary property.
This dictionary is not relevant to the bitmap representation of the
image, but may contain additional information like resolution or
comments.
Image format drivers may output metadata when image data is
parsed, or may use metadata to be included in image files or formats.
.SS "METADATA KEYS"
.PP
Each image format driver supports an individual set of metadata dictionary
keys. Predefined keys are:
.TP
DPI
.
Horizontal image resolution in DPI as a double value.







|







738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
.PP
Each image has a metadata dictionary property.
This dictionary is not relevant to the bitmap representation of the
image, but may contain additional information like resolution or
comments.
Image format drivers may output metadata when image data is
parsed, or may use metadata to be included in image files or formats.
.SS "METADATA KEYS (MULTIPLE FORMATS)"
.PP
Each image format driver supports an individual set of metadata dictionary
keys. Predefined keys are:
.TP
DPI
.
Horizontal image resolution in DPI as a double value.
760
761
762
763
764
765
766

























767
768
769
770
771
772
773
comment
.
Image text comment.
Supported by formats \fBgif\fR and \fBpng\fR.
.PP
It is valid to set any key in the metadata dict.
A format driver will ignore keys it does not handle.

























.PP
.VE 8.7
.SH CREDITS
.PP
The photo image type was designed and implemented by Paul Mackerras,
based on his earlier photo widget and some suggestions from
John Ousterhout.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
comment
.
Image text comment.
Supported by formats \fBgif\fR and \fBpng\fR.
.PP
It is valid to set any key in the metadata dict.
A format driver will ignore keys it does not handle.
.SS "METADATA KEYS FOR ANIMATED GIF INFORMATION"
.PP
The following metadata keys are reported when reading a \fBgif\fR format file.
They are typically used in conjunction with the \fI-index\fR option of an
animated \fBgif\fR file to properly display the subimage sequence.
The options are linked to each subimage selected by \fI-index\fR.
.TP
\fBdelay time\fR \fItime\fR
.
Update delay time in 10ms unit. This key is only present, if delay time is not 0.
.TP
\fBdisposal method\fR \fImethod\fR
.
Disposal method of the preceeding image, if given for the current image.
Possible values are: \fIdo not dispose\fR, \fIrestore to background color\fR, \fIrestore to previous\fR.
.TP
\fBuser interaction\fR \fIbool\fR
.
The key is present with a value of 1, if user interaction is specified.
Otherwise, the key is not present.
.TP
\fBupdate region\fR \fIX0\fR, \fIY0\fR, \fIwidth\fR, \fIheight\fR
.
Update region of the current subimage, if subimage has not the same size as
the full image. The pixel outside of this box are all fully transparent.
.PP
.VE 8.7
.SH CREDITS
.PP
The photo image type was designed and implemented by Paul Mackerras,
based on his earlier photo widget and some suggestions from
John Ousterhout.

Changes to generic/tkImgGIF.c.

42
43
44
45
46
47
48












49
50
51
52
53
54
55

#define GIF_SPECIAL	(256)
#define GIF_PAD		(GIF_SPECIAL+1)
#define GIF_SPACE	(GIF_SPECIAL+2)
#define GIF_BAD		(GIF_SPECIAL+3)
#define GIF_DONE	(GIF_SPECIAL+4)













/*
 * structure to "mimic" FILE for Mread, so we can look like fread. The decoder
 * state keeps track of which byte we are about to read, or EOF.
 */

typedef struct mFile {
    unsigned char *data;	/* mmencoded source string */







>
>
>
>
>
>
>
>
>
>
>
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

#define GIF_SPECIAL	(256)
#define GIF_PAD		(GIF_SPECIAL+1)
#define GIF_SPACE	(GIF_SPECIAL+2)
#define GIF_BAD		(GIF_SPECIAL+3)
#define GIF_DONE	(GIF_SPECIAL+4)

/*
 * structure to hold the data of a Graphic Control Extension block.
 */

typedef struct {
    int blockPresent;		/* if 1, the block was read and is in scope */
    int transparent;		/* Transparency index */
    int delayTime;		/* update delay time in 10ms */
    int disposalMethod;		/* disposal method 0-3 */
    int userInteraction;	/* user interaction 0/1 */
} GIFGraphicControlExtensionBlock;

/*
 * structure to "mimic" FILE for Mread, so we can look like fread. The decoder
 * state keeps track of which byte we are about to read, or EOF.
 */

typedef struct mFile {
    unsigned char *data;	/* mmencoded source string */
174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
 * Prototypes for local functions defined in this file:
 */

static int		ReadOneByte(Tcl_Interp *interp,
			    GIFImageConfig *gifConfPtr, Tcl_Channel chan);
static int		DoExtension(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, int label, unsigned char *buffer,


			    int *transparent, Tcl_Obj *metadataOutObj);
static int		GetCode(Tcl_Channel chan, int code_size, int flag,
			    GIFImageConfig *gifConfPtr);
static int		GetDataBlock(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, unsigned char *buf);
static int		ReadColorMap(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, int number,
			    unsigned char buffer[MAXCOLORMAPSIZE][4]);







>
>
|







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 * Prototypes for local functions defined in this file:
 */

static int		ReadOneByte(Tcl_Interp *interp,
			    GIFImageConfig *gifConfPtr, Tcl_Channel chan);
static int		DoExtension(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, int label, unsigned char *buffer,
			    GIFGraphicControlExtensionBlock
			    *gifGraphicControlExtensionBlock,
			    Tcl_Obj *metadataOutObj);
static int		GetCode(Tcl_Channel chan, int code_size, int flag,
			    GIFImageConfig *gifConfPtr);
static int		GetDataBlock(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, unsigned char *buf);
static int		ReadColorMap(GIFImageConfig *gifConfPtr,
			    Tcl_Channel chan, int number,
			    unsigned char buffer[MAXCOLORMAPSIZE][4]);
412
413
414
415
416
417
418
419
420
421
422
423
424

425
426
427
428
429
430
431
    int index = 0, argc = 0, i, result = TCL_ERROR;
    Tcl_Obj **objv;
    unsigned char buf[100];
    unsigned char *trashBuffer = NULL;
    int bitPixel;
    int gifLabel;
    unsigned char colorMap[MAXCOLORMAPSIZE][4];
    int transparent = -1;
    static const char *const optionStrings[] = {
	"-index", NULL
    };
    GIFImageConfig gifConf, *gifConfPtr = &gifConf;


    /*
     * Decode the magic used to convey when we're sourcing data from a string
     * source and not a file.
     */

    memset(colorMap, 0, MAXCOLORMAPSIZE*4);
    memset(gifConfPtr, 0, sizeof(GIFImageConfig));







|





>







426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
    int index = 0, argc = 0, i, result = TCL_ERROR;
    Tcl_Obj **objv;
    unsigned char buf[100];
    unsigned char *trashBuffer = NULL;
    int bitPixel;
    int gifLabel;
    unsigned char colorMap[MAXCOLORMAPSIZE][4];
    GIFGraphicControlExtensionBlock gifGraphicControlExtensionBlock;
    static const char *const optionStrings[] = {
	"-index", NULL
    };
    GIFImageConfig gifConf, *gifConfPtr = &gifConf;

    gifGraphicControlExtensionBlock.blockPresent = 0;
    /*
     * Decode the magic used to convey when we're sourcing data from a string
     * source and not a file.
     */

    memset(colorMap, 0, MAXCOLORMAPSIZE*4);
    memset(gifConfPtr, 0, sizeof(GIFImageConfig));
543
544
545
546
547
548
549
550

551
552
553
554
555
556
557
	     * This is a GIF extension.
	     */

	    if (-1 == (gifLabel = ReadOneByte( interp, gifConfPtr, chan ) ) ) {
		goto error;
	    }
	    if (DoExtension(gifConfPtr, chan, gifLabel,
		    gifConfPtr->workingBuffer, &transparent, metadataOutObj)

		    < 0) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"error reading extension in GIF image", -1));
		Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT",
			NULL);
		goto error;
	    }







|
>







558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
	     * This is a GIF extension.
	     */

	    if (-1 == (gifLabel = ReadOneByte( interp, gifConfPtr, chan ) ) ) {
		goto error;
	    }
	    if (DoExtension(gifConfPtr, chan, gifLabel,
		    gifConfPtr->workingBuffer, &gifGraphicControlExtensionBlock,
		    metadataOutObj)
		    < 0) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"error reading extension in GIF image", -1));
		Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT",
			NULL);
		goto error;
	    }
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
		goto error;
	    }

	    /*
	     * This extension starts a new scope, so Graphic control Extension
	     * data should be cleared
	     */
	    transparent = -1;

	    continue;
	}
	break;
    }

    /*







|







651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
		goto error;
	    }

	    /*
	     * This extension starts a new scope, so Graphic control Extension
	     * data should be cleared
	     */
	    gifGraphicControlExtensionBlock.blockPresent = 0;

	    continue;
	}
	break;
    }

    /*
684
685
686
687
688
689
690



691
692
693
694
695
696
697
698
    }
    if (height > imageHeight) {
	height = imageHeight;
    }

    if ((width > 0) && (height > 0)) {
	Tk_PhotoImageBlock block;




	/*
	 * Read the data and put it into the photo buffer for display by the
	 * general image machinery.
	 */

	block.width = width;
	block.height = height;







>
>
>
|







700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
    }
    if (height > imageHeight) {
	height = imageHeight;
    }

    if ((width > 0) && (height > 0)) {
	Tk_PhotoImageBlock block;
	int transparent = -1;
	if (gifGraphicControlExtensionBlock.blockPresent) {
	    transparent = gifGraphicControlExtensionBlock.transparent;
	}
	/*
	 * Read the data and put it into the photo buffer for display by the
	 * general image machinery.
	 */

	block.width = width;
	block.height = height;
723
724
725
726
727
728
729












































































730
731
732
733
734
735
736
	if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY,
		width, height, TK_PHOTO_COMPOSITE_SET) != TCL_OK) {
	    ckfree(block.pixelPtr);
	    goto error;
	}
	ckfree(block.pixelPtr);
    }













































































    /*
     * We've successfully read the GIF frame (or there was nothing to read,
     * which suits as well). We're done.
     */

    while (1) {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
	if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY,
		width, height, TK_PHOTO_COMPOSITE_SET) != TCL_OK) {
	    ckfree(block.pixelPtr);
	    goto error;
	}
	ckfree(block.pixelPtr);
    }
    
    /*
     * Update the metadata dictionary with current image data
     */
    
    if (NULL != metadataOutObj) {

	/*
	 * Save the update box, if not the whole image
	 */

	if ( width != fileWidth || height != fileHeight) {
	    Tcl_Obj *itemList[4];
	    itemList[0] = Tcl_NewIntObj(destX);
	    itemList[1] = Tcl_NewIntObj(destY);
	    itemList[2] = Tcl_NewIntObj(width);
	    itemList[3] = Tcl_NewIntObj(height);
	    if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
		    Tcl_NewStringObj("update region",-1),
		    Tcl_NewListObj(4, itemList) )) {
		result = TCL_ERROR;
		goto error;
	    }
	}

	/*
	 * Copy the Graphic Control Extension Block data to the metadata
	 * dictionary
	 */

	if (gifGraphicControlExtensionBlock.blockPresent) {
	    if ( gifGraphicControlExtensionBlock.delayTime != 0) {
		if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
			Tcl_NewStringObj("delay time",-1),
			Tcl_NewIntObj(gifGraphicControlExtensionBlock.delayTime)
			)) {
		    result = TCL_ERROR;
		    goto error;
		}
	    }
	    switch ( gifGraphicControlExtensionBlock.disposalMethod ) {
	    case 1: /* Do not dispose */
		if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
			Tcl_NewStringObj("disposal method",-1),
			Tcl_NewStringObj("do not dispose",-1))) {
		    result = TCL_ERROR;
		    goto error;
		}
		break;
	    case 2: /* Restore to background color */
		if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
			Tcl_NewStringObj("disposal method",-1),
			Tcl_NewStringObj("restore to background color",-1))) {
		    result = TCL_ERROR;
		    goto error;
		}
		break;
	    case 3: /* Restore to previous */
		if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
			Tcl_NewStringObj("disposal method",-1),
			Tcl_NewStringObj("restore to previous",-1))) {
		    result = TCL_ERROR;
		    goto error;
		}
		break;
	    }
	    if ( gifGraphicControlExtensionBlock.userInteraction != 0) {
		if ( TCL_OK != Tcl_DictObjPut(interp, metadataOutObj,
			Tcl_NewStringObj("user interaction",-1),
			Tcl_NewBooleanObj(1))) {
		    result = TCL_ERROR;
		    goto error;
		}
	    }
	}
    }

    /*
     * We've successfully read the GIF frame (or there was nothing to read,
     * which suits as well). We're done.
     */

    while (1) {
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
	     * This is a GIF extension.
	     */

	    if (-1 == (gifLabel = ReadOneByte( interp, gifConfPtr, chan ) ) ) {
		goto error;
	    }
	    if (DoExtension(gifConfPtr, chan, gifLabel,
		    gifConfPtr->workingBuffer, &transparent, metadataOutObj)

		    < 0) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"error reading extension in GIF image", -1));
		Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT",
			NULL);
		goto error;
	    }







|
>







841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
	     * This is a GIF extension.
	     */

	    if (-1 == (gifLabel = ReadOneByte( interp, gifConfPtr, chan ) ) ) {
		goto error;
	    }
	    if (DoExtension(gifConfPtr, chan, gifLabel,
		    gifConfPtr->workingBuffer, &gifGraphicControlExtensionBlock,
		    metadataOutObj)
		    < 0) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"error reading extension in GIF image", -1));
		Tcl_SetErrorCode(interp, "TK", "IMAGE", "GIF", "BAD_EXT",
			NULL);
		goto error;
	    }
1043
1044
1045
1046
1047
1048
1049

1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088










1089


1090
1091
1092
1093
1094
1095
1096
1097
*	Process a GIF extension block
*
* Results:
*	-1 to trigger an extension read error
*       >= 0 ok
*
* Side effects:

*	The transparent color is set if present in current extensions
*       The data of the following extensions are saved to the metadata dict:
*       - Application extension
*         - Comment extension in key "comment"
*       Plain text extensions are currently ignored.
*
*----------------------------------------------------------------------
*/

static int
DoExtension(
    GIFImageConfig *gifConfPtr,
    Tcl_Channel chan,
    int label,
    unsigned char *buf, /* defined as 280 byte working buffer */
    int *transparent,
    Tcl_Obj *metadataOutObj)
{
    int count;
    /* Prepare extension name
     * Maximum string size: "comment" + Code(3) + trailing zero
     */
    char extensionStreamName[8];
    extensionStreamName[0] = '\0';

    switch (label) {
    case 0x01:			/* Plain Text Extension */
	/*
	 * This extension starts a new scope, so Graphic control Extension
	 * data should be cleared
	 */
	*transparent = -1;
	/* this extension is ignored, skip below */
	break;
    case 0xf9:			/* Graphic Control Extension */
	count = GetDataBlock(gifConfPtr, chan, buf);
	if (count < 0) {
	    return -1;
	}










	if ((buf[0] & 0x1) != 0) {


	    *transparent = buf[3];
	}
	break;
    case 0xfe:			/* Comment Extension */
	strcpy(extensionStreamName,"comment");
        /* copy the extension data below */
	break;
    }







>
|














|















|







>
>
>
>
>
>
>
>
>
>
|
>
>
|







1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
*	Process a GIF extension block
*
* Results:
*	-1 to trigger an extension read error
*       >= 0 ok
*
* Side effects:
*       The gifGraphicControlExtensionBlock is set if present in current
*       extensions
*       The data of the following extensions are saved to the metadata dict:
*       - Application extension
*         - Comment extension in key "comment"
*       Plain text extensions are currently ignored.
*
*----------------------------------------------------------------------
*/

static int
DoExtension(
    GIFImageConfig *gifConfPtr,
    Tcl_Channel chan,
    int label,
    unsigned char *buf, /* defined as 280 byte working buffer */
    GIFGraphicControlExtensionBlock *gifGraphicControlExtensionBlock,
    Tcl_Obj *metadataOutObj)
{
    int count;
    /* Prepare extension name
     * Maximum string size: "comment" + Code(3) + trailing zero
     */
    char extensionStreamName[8];
    extensionStreamName[0] = '\0';

    switch (label) {
    case 0x01:			/* Plain Text Extension */
	/*
	 * This extension starts a new scope, so Graphic control Extension
	 * data should be cleared
	 */
	gifGraphicControlExtensionBlock->blockPresent = 0;
	/* this extension is ignored, skip below */
	break;
    case 0xf9:			/* Graphic Control Extension */
	count = GetDataBlock(gifConfPtr, chan, buf);
	if (count < 0) {
	    return -1;
	}
	gifGraphicControlExtensionBlock->blockPresent=1;
	/* save disposal method */
	gifGraphicControlExtensionBlock->disposalMethod
		= ((buf[0] & 0x1C) >> 2);
	/* save disposal method */
	gifGraphicControlExtensionBlock->userInteraction = ((buf[0] & 2) >> 1);
	/* save delay time */
	gifGraphicControlExtensionBlock->delayTime
		= LM_to_uint(buf[1], buf[2]);
	/* save transparent index if given */
	if ((buf[0] & 0x1) == 0) {
	    gifGraphicControlExtensionBlock->transparent = -1;
	} else {
	    gifGraphicControlExtensionBlock->transparent = buf[3];
	}
	break;
    case 0xfe:			/* Comment Extension */
	strcpy(extensionStreamName,"comment");
        /* copy the extension data below */
	break;
    }

Changes to tests/imgPhoto.test.

2509
2510
2511
2512
2513
2514
2515













































































































































2516
2517
2518
2519
2520
2521
2522
    image create photo gif1 -data $data
    dict size [gif1 cget -metadata]
} -cleanup {
    catch {image delete gif1}
    file delete $path
} -result {0}














































































































































unset -nocomplain gifstart gifdata gifend


catch {rename foreachPixel {}}
catch {rename checkImgTrans {}}
catch {rename checkImgTransLoop {}}
imageFinish







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
    image create photo gif1 -data $data
    dict size [gif1 cget -metadata]
} -cleanup {
    catch {image delete gif1}
    file delete $path
} -result {0}

test imgPhoto-23.21 {GIF delay time metadata} -setup {
    set data $::gifstart
    # Graphic control extension: 10 1/100s delay time
    append data "\x21\xf9\x04\x00\x0a\x00\x00\x00"
    # Trailer
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{delay time} 10}

test imgPhoto-23.22 {GIF disposal method "do not dispose" metadata} -setup {
    set data $::gifstart
    # Graphic control extension: disposdal method: 
    append data "\x21\xf9\x04\x04\x00\x00\x00\x00"
    # Trailer
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{disposal method} {do not dispose}}

test imgPhoto-33.23 {GIF disposal method "restore to background color" metadata} -setup {
    set data $::gifstart
    # Graphic control extension: disposdal method: 
    append data "\x21\xf9\x04\x08\x00\x00\x00\x00"
    # Trailer
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{disposal method} {restore to background color}}

test imgPhoto-43.24 {GIF disposal method "restore to previous" metadata} -setup {
    set data $::gifstart
    # Graphic control extension: disposdal method: 
    append data "\x21\xf9\x04\x0C\x00\x00\x00\x00"
    # Trailer
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{disposal method} {restore to previous}}

test imgPhoto-53.25 {GIF user input flag metadata} -setup {
    set data $::gifstart
    # Graphic control extension: disposdal method: 
    append data "\x21\xf9\x04\x02\x00\x00\x00\x00"
    # Trailer
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{user interaction} 1}

test imgPhoto-63.26 {GIF update region metadata} -setup {
    # size 32x32, global color table size: 8
    set data "GIF89a\x20\x00\x20\x00\xc2\x07\x00"
    # color table
    append data "\x00\x00\x00\x33\x33\xff\xff\x33\x33\xff\x33\xff\x33\xff\x33\x33\xff\xff\xff\xff\x33\xff\xff\xff"
    # Trailer
    # As this is 16x16, we will get an update region setting
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{update region} {0 0 16 16}}

test imgPhoto-63.27 {GIF multiple options metadata} -setup {
    # size 32x32, global color table size: 8
    set data "GIF89a\x20\x00\x20\x00\xc2\x07\x00"
    # color table
    append data "\x00\x00\x00\x33\x33\xff\xff\x33\x33\xff\x33\xff\x33\xff\x33\x33\xff\xff\xff\xff\x33\xff\xff\xff"
    # Graphic control extension: do not dispose, user interaction, transparent color, delay time 10
    append data "\x21\xf9\x04\x07\x0a\x00\x01\x00"
    # Image data and trailer
    # As this is 16x16, we will get an update region setting
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{update region} {0 0 16 16} {delay time} 10 {disposal method} {do not dispose} {user interaction} 1}

test imgPhoto-63.27 {GIF multiple options metadata in -index 0} -setup {
    # size 32x32, global color table size: 8
    set data "GIF89a\x20\x00\x20\x00\xc2\x07\x00"
    # color table
    append data "\x00\x00\x00\x33\x33\xff\xff\x33\x33\xff\x33\xff\x33\xff\x33\x33\xff\xff\xff\xff\x33\xff\xff\xff"
    # Graphic control extension: do not dispose, user interaction, transparent color, delay time 4096
    append data "\x21\xf9\x04\x07\x00\x10\x01\x00"
    # Image data
    # As this is 16x16, we will get an update region setting
    append data $::gifdata
    # Graphic control extension: restore to background, delay time 1
    append data "\x21\xf9\x04\x08\x01\x00\x02\x00"
    # Image data and trailer
    # As this is 16x16, we will get an update region setting
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data -format "gif -index 0"
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{update region} {0 0 16 16} {delay time} 4096 {disposal method} {do not dispose} {user interaction} 1}

test imgPhoto-63.28 {GIF multiple options metadata in -index 1} -setup {
    # size 32x32, global color table size: 8
    set data "GIF89a\x20\x00\x20\x00\xc2\x07\x00"
    # color table
    append data "\x00\x00\x00\x33\x33\xff\xff\x33\x33\xff\x33\xff\x33\xff\x33\x33\xff\xff\xff\xff\x33\xff\xff\xff"
    # Graphic control extension: restore to background, delay time 1
    append data "\x21\xf9\x04\x08\x01\x00\x02\x00"
    # Image data
    # As this is 16x16, we will get an update region setting
    append data $::gifdata
    # Graphic control extension: do not dispose, user interaction, transparent color, delay time 4096
    append data "\x21\xf9\x04\x07\x00\x10\x01\x00"
    # Image data and trailer
    # As this is 16x16, we will get an update region setting
    append data $::gifdata $::gifend
} -body {
    image create photo gif1 -data $data -format "gif -index 1"
    gif1 cget -metadata
} -cleanup {
    catch {image delete gif1}
} -result {{update region} {0 0 16 16} {delay time} 4096 {disposal method} {do not dispose} {user interaction} 1}

unset -nocomplain gifstart gifdata gifend


catch {rename foreachPixel {}}
catch {rename checkImgTrans {}}
catch {rename checkImgTransLoop {}}
imageFinish