Tk Source Code

Artifact [aa8fb89a]
Login

Artifact aa8fb89a347b7b2733000e616ec0811cd2ac8eec:

Attachment "compositing.patch" to ticket [566765ff] added by dkf 2002-06-10 16:44:47.
Index: ChangeLog
===================================================================
RCS file: /cvsroot/tktoolkit/tk/ChangeLog,v
retrieving revision 1.496
diff -u -r1.496 ChangeLog
--- ChangeLog	10 Jun 2002 00:15:42 -0000	1.496
+++ ChangeLog	10 Jun 2002 09:42:56 -0000
@@ -1,3 +1,49 @@
+2002-06-10  Donal K. Fellows  <[email protected]>
+
+	* tests/imgPhoto.test: Added tests of -compositingrule
+
+	* doc/photo.n: Added documentation for "-compositingrule".
+	* generic/tkImgPhoto.c (ImgPhotoCmd,ParseSubcommandOptions): New
+	"-compositingrule" option for [$photo copy] subcommand, using
+	OPT_COMPOSITE flag and compositingRule field in SubcommandOptions
+	structure.
+
+	* doc/FindPhoto.3: Documented the extra argument for the
+	compositing rule and the action to take if anyone wants to
+	maintain total backward-compatability.
+
+	* generic/tk.h (TK_PHOTO_COMPOSITE_*): Defined values for use as
+	compositing rules. 
+	(USE_OLD_PHOTO_PUT_BLOCK): Added a way for users to select the old
+	interface to Tk_PhotoPutBlock to provide an easier upgrade path.
+
+	* generic/tk.decls: Alter Tk_PhotoPut*Block to Tk_PhotoPut*Block_Old
+	and introduce new slots for the old name of function with an extra
+	argument at the end for the compositing rule.
+
+	* generic/tkImgPhoto.c (ImgPhotoCmd): Updated "transparency set"
+	subcommand to use TkSubtractRegion().
+
+	* win/tkWinRegion.c (TkSubtractRegion): 
+	* mac/tkMacRegion.c (TkSubtractRegion): 
+	* generic/tkInt.decls (TkSubtractRegion): 
+	* unix/tkUnixPort.h (TkSubtractRegion): Added function to perform
+	the set-difference operation on regions; it seems all platforms
+	can support it, and it makes removing rectangular bits from
+	regions much easier.
+
+	* generic/tkImgPPM.c (FileReadPPM): Reading a PPM/PGM always uses
+	the SET compositing rule because it is faster and the format does
+	not have any transparency information.
+
+	* generic/tkImgGIF.c (FileReadGIF): Reading a GIF always uses the
+	SET compositing rule because GIF files model transparency as a
+	single special colour.
+
+	* generic/tkImgPhoto.c (Tk_PhotoPutBlock, Tk_PhotoPutZoomedBlock):
+	Added a compositing rule to allow better control over what happens
+	to transparent pixels when inserting data into a photo image.
+
 2002-06-10  Anton Kovalenko <[email protected]>
 
 	* library/tk.tcl: added utility functions to get "-underline" and 
Index: doc/FindPhoto.3
===================================================================
RCS file: /cvsroot/tktoolkit/tk/doc/FindPhoto.3,v
retrieving revision 1.3
diff -u -r1.3 FindPhoto.3
--- doc/FindPhoto.3	29 Oct 1999 03:57:40 -0000	1.3
+++ doc/FindPhoto.3	10 Jun 2002 09:42:56 -0000
@@ -27,11 +27,11 @@
 .VE
 .sp
 void
-\fBTk_PhotoPutBlock\fR(\fIhandle, blockPtr, x, y, width, height\fR)
+\fBTk_PhotoPutBlock\fR(\fIhandle, blockPtr, x, y, width, height, compRule\fR)
 .sp
 void
 \fBTk_PhotoPutZoomedBlock\fR(\fIhandle, blockPtr, x, y, width, height,\
-zoomX, zoomY, subsampleX, subsampleY\fR)
+zoomX, zoomY, subsampleX, subsampleY, compRule\fR)
 .sp
 int
 \fBTk_PhotoGetImage\fR(\fIhandle, blockPtr\fR)
@@ -69,6 +69,16 @@
 Specifies the width of the image area to be affected (for
 \fBTk_PhotoPutBlock\fR) or the desired image width (for
 \fBTk_PhotoExpand\fR and \fBTk_PhotoSetSize\fR).
+.VS 8.4
+.AP int compRule in
+Specifies the compositing rule used when combining transparent pixels
+in a block of data with a photo image.  Must be one of
+TK_PHOTO_COMPOSITE_OVERLAY (which puts the block of data over the top
+of the existing photo image, with the previous contents showing
+through in the transparent bits) or TK_PHOTO_COMPOSITE_SET (which
+discards the existing photo image contents in the rectangle covered by
+the data block.)
+.VE 8.4
 .AP int height in
 Specifies the height of the image area to be affected (for
 \fBTk_PhotoPutBlock\fR) or the desired image height (for
@@ -135,6 +145,17 @@
 have other values, e.g., for images that are stored as separate red,
 green and blue planes.
 .PP
+.VS 8.4
+The \fIcompRule\fR parameter to \fBTk_PhotoPutBlock\fR specifies a
+compositing rule that says what to do with transparent pixels.  The
+value TK_PHOTO_COMPOSITE_OVERLAY says that the previous contents of
+the photo image should show through, and the value
+TK_PHOTO_COMPOSITE_SET says that the previous contents of the photo
+image should be completely ignored, and the values from the block be
+copied directly across.  The behavior in Tk8.3 and earlier was
+equivalent to having TK_PHOTO_COMPOSITE_OVERLAY as a compositing rule.
+.VE 8.4
+.PP
 The value given for the \fIwidth\fR and \fIheight\fR parameters to
 \fBTk_PhotoPutBlock\fR do not have to correspond to the values specified
 in \fIblock\fR.  If they are smaller, \fBTk_PhotoPutBlock\fR extracts a
@@ -192,6 +213,16 @@
 .PP
 \fBTk_PhotoGetSize\fR returns the dimensions of the image in
 *\fIwidthPtr\fR and *\fIheightPtr\fR.
+
+.SH PORTABILITY
+.VS 8.4
+.PP
+In Tk 8.3 and earlier, \fBTk_PhotoPutBlock\fR and
+\fBTk_PhotoPutZoomedBlock\fR had different signatures. If you want to
+compile code that uses the old interface against 8.4 without updating
+your code, compile it with the flag -DUSE_OLD_PHOTO_PUT_BLOCK.  Code
+linked using Stubs against older versions of Tk will continue to work.
+.VE 8.4
 
 .SH CREDITS
 .PP
Index: doc/photo.n
===================================================================
RCS file: /cvsroot/tktoolkit/tk/doc/photo.n,v
retrieving revision 1.10
diff -u -r1.10 photo.n
--- doc/photo.n	14 Mar 2002 10:19:40 -0000	1.10
+++ doc/photo.n	10 Jun 2002 09:42:56 -0000
@@ -189,6 +189,18 @@
 the Y direction.  Negative values will cause the image to be flipped
 about the Y or X axes, respectively.  If \fIy\fR is not given, the
 default value is the same as \fIx\fR.
+.TP
+\fB\-compositingrule \fIrule\fR
+.VS 8.4
+Specifies how transparent pixels in the source image are combined with
+the destination image.  When a compositing rule of \fIoverlay\fR is
+set, the old contents of the destination image are visible, as if the
+source image were printed on a piece of transparent film and placed
+over the top of the destination.  When a compositing rule of \fIset\R
+is set, the old contents of the destination image are discarded and
+the source image is used as-is.  The default compositing rule is
+\fIoverlay\fR.
+.VE 8.4
 .RE
 .TP
 \fIimageName \fBdata ?\fIoption value(s) ...\fR?
Index: generic/tk.decls
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tk.decls,v
retrieving revision 1.18
diff -u -r1.18 tk.decls
--- generic/tk.decls	5 Apr 2002 08:38:22 -0000	1.18
+++ generic/tk.decls	10 Jun 2002 09:42:56 -0000
@@ -708,13 +708,13 @@
 }
 
 declare 144 generic {
-    void Tk_PhotoPutBlock (Tk_PhotoHandle handle, \
+    void Tk_PhotoPutBlock_Old (Tk_PhotoHandle handle, \
 	    Tk_PhotoImageBlock *blockPtr, int x, int y, \
 	    int width, int height)
 }
 
 declare 145 generic {
-    void Tk_PhotoPutZoomedBlock (Tk_PhotoHandle handle, \
+    void Tk_PhotoPutZoomedBlock_Old (Tk_PhotoHandle handle, \
 	    Tk_PhotoImageBlock *blockPtr, int x, int y, \
 	    int width, int height, int zoomX, int zoomY, \
 	    int subsampleX, int subsampleY)
@@ -1162,6 +1162,18 @@
 #
 declare 245 generic {
     void Tk_SetCaretPos (Tk_Window tkwin, int x, int y, int height)
+}
+
+declare 246 generic {
+    void Tk_PhotoPutBlock (Tk_PhotoHandle handle,
+	    Tk_PhotoImageBlock *blockPtr, int x, int y,
+	    int width, int height, int compRule)
+}
+declare 247 generic {
+    void Tk_PhotoPutZoomedBlock (Tk_PhotoHandle handle,
+	    Tk_PhotoImageBlock *blockPtr, int x, int y,
+	    int width, int height, int zoomX, int zoomY,
+	    int subsampleX, int subsampleY, int compRule)
 }
 
 # Define the platform specific public Tk interface.  These functions are
Index: generic/tk.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tk.h,v
retrieving revision 1.58
diff -u -r1.58 tk.h
--- generic/tk.h	6 Mar 2002 15:36:17 -0000	1.58
+++ generic/tk.h	10 Jun 2002 09:42:57 -0000
@@ -1270,6 +1270,15 @@
 } Tk_PhotoImageBlock;
 
 /*
+ * The following values control how blocks are combined into photo
+ * images when the alpha component of a pixel is not 255, a.k.a. the
+ * compositing rule.
+ */
+
+#define TK_PHOTO_COMPOSITE_OVERLAY	0
+#define TK_PHOTO_COMPOSITE_SET		1
+
+/*
  * Procedure prototypes and structures used in reading and
  * writing photo images:
  */
@@ -1457,6 +1466,26 @@
  */
 
 #include "tkDecls.h"
+
+/*
+ * Allow users to say that they don't want to alter their source to
+ * add the extra argument to Tk_PhotoPutBlock(); DO NOT DEFINE THIS
+ * WHEN BUILDING TK.
+ *
+ * This goes after the inclusion of the stubbed-decls so that the
+ * declarations of what is actually there can be correct.
+ */
+
+#ifdef USE_OLD_PHOTO_PUT_BLOCK
+#   ifdef Tk_PhotoPutBlock
+#	undef Tk_PhotoPutBlock
+#   endif
+#   define Tk_PhotoPutBlock		Tk_PhotoPutBlock_Old
+#   ifdef Tk_PhotoPutZoomedBlock
+#	undef Tk_PhotoPutZoomedBlock
+#   endif
+#   define Tk_PhotoPutZoomedBlock	Tk_PhotoPutZoomedBlock_Old
+#endif /* USE_OLD_PHOTO_PUT_BLOCK */
 
 /*
  * Tcl commands exported by Tk:
Index: generic/tkDecls.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkDecls.h,v
retrieving revision 1.18
diff -u -r1.18 tkDecls.h
--- generic/tkDecls.h	5 Apr 2002 08:38:22 -0000	1.18
+++ generic/tkDecls.h	10 Jun 2002 09:42:57 -0000
@@ -510,11 +510,12 @@
 				CONST84 char ** argv, Tk_ArgvInfo * argTable, 
 				int flags));
 /* 144 */
-EXTERN void		Tk_PhotoPutBlock _ANSI_ARGS_((Tk_PhotoHandle handle, 
+EXTERN void		Tk_PhotoPutBlock_Old _ANSI_ARGS_((
+				Tk_PhotoHandle handle, 
 				Tk_PhotoImageBlock * blockPtr, int x, int y, 
 				int width, int height));
 /* 145 */
-EXTERN void		Tk_PhotoPutZoomedBlock _ANSI_ARGS_((
+EXTERN void		Tk_PhotoPutZoomedBlock_Old _ANSI_ARGS_((
 				Tk_PhotoHandle handle, 
 				Tk_PhotoImageBlock * blockPtr, int x, int y, 
 				int width, int height, int zoomX, int zoomY, 
@@ -846,6 +847,16 @@
 /* 245 */
 EXTERN void		Tk_SetCaretPos _ANSI_ARGS_((Tk_Window tkwin, int x, 
 				int y, int height));
+/* 246 */
+EXTERN void		Tk_PhotoPutBlock _ANSI_ARGS_((Tk_PhotoHandle handle, 
+				Tk_PhotoImageBlock * blockPtr, int x, int y, 
+				int width, int height, int compRule));
+/* 247 */
+EXTERN void		Tk_PhotoPutZoomedBlock _ANSI_ARGS_((
+				Tk_PhotoHandle handle, 
+				Tk_PhotoImageBlock * blockPtr, int x, int y, 
+				int width, int height, int zoomX, int zoomY, 
+				int subsampleX, int subsampleY, int compRule));
 
 typedef struct TkStubHooks {
     struct TkPlatStubs *tkPlatStubs;
@@ -1002,8 +1013,8 @@
     Tk_Window (*tk_NameToWindow) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * pathName, Tk_Window tkwin)); /* 141 */
     void (*tk_OwnSelection) _ANSI_ARGS_((Tk_Window tkwin, Atom selection, Tk_LostSelProc * proc, ClientData clientData)); /* 142 */
     int (*tk_ParseArgv) _ANSI_ARGS_((Tcl_Interp * interp, Tk_Window tkwin, int * argcPtr, CONST84 char ** argv, Tk_ArgvInfo * argTable, int flags)); /* 143 */
-    void (*tk_PhotoPutBlock) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height)); /* 144 */
-    void (*tk_PhotoPutZoomedBlock) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height, int zoomX, int zoomY, int subsampleX, int subsampleY)); /* 145 */
+    void (*tk_PhotoPutBlock_Old) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height)); /* 144 */
+    void (*tk_PhotoPutZoomedBlock_Old) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height, int zoomX, int zoomY, int subsampleX, int subsampleY)); /* 145 */
     int (*tk_PhotoGetImage) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr)); /* 146 */
     void (*tk_PhotoBlank) _ANSI_ARGS_((Tk_PhotoHandle handle)); /* 147 */
     void (*tk_PhotoExpand) _ANSI_ARGS_((Tk_PhotoHandle handle, int width, int height)); /* 148 */
@@ -1104,6 +1115,8 @@
     void (*tk_SetInternalBorderEx) _ANSI_ARGS_((Tk_Window tkwin, int left, int right, int top, int bottom)); /* 243 */
     void (*tk_SetMinimumRequestSize) _ANSI_ARGS_((Tk_Window tkwin, int minWidth, int minHeight)); /* 244 */
     void (*tk_SetCaretPos) _ANSI_ARGS_((Tk_Window tkwin, int x, int y, int height)); /* 245 */
+    void (*tk_PhotoPutBlock) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height, int compRule)); /* 246 */
+    void (*tk_PhotoPutZoomedBlock) _ANSI_ARGS_((Tk_PhotoHandle handle, Tk_PhotoImageBlock * blockPtr, int x, int y, int width, int height, int zoomX, int zoomY, int subsampleX, int subsampleY, int compRule)); /* 247 */
 } TkStubs;
 
 #ifdef __cplusplus
@@ -1696,13 +1709,13 @@
 #define Tk_ParseArgv \
 	(tkStubsPtr->tk_ParseArgv) /* 143 */
 #endif
-#ifndef Tk_PhotoPutBlock
-#define Tk_PhotoPutBlock \
-	(tkStubsPtr->tk_PhotoPutBlock) /* 144 */
-#endif
-#ifndef Tk_PhotoPutZoomedBlock
-#define Tk_PhotoPutZoomedBlock \
-	(tkStubsPtr->tk_PhotoPutZoomedBlock) /* 145 */
+#ifndef Tk_PhotoPutBlock_Old
+#define Tk_PhotoPutBlock_Old \
+	(tkStubsPtr->tk_PhotoPutBlock_Old) /* 144 */
+#endif
+#ifndef Tk_PhotoPutZoomedBlock_Old
+#define Tk_PhotoPutZoomedBlock_Old \
+	(tkStubsPtr->tk_PhotoPutZoomedBlock_Old) /* 145 */
 #endif
 #ifndef Tk_PhotoGetImage
 #define Tk_PhotoGetImage \
@@ -2097,6 +2110,14 @@
 #ifndef Tk_SetCaretPos
 #define Tk_SetCaretPos \
 	(tkStubsPtr->tk_SetCaretPos) /* 245 */
+#endif
+#ifndef Tk_PhotoPutBlock
+#define Tk_PhotoPutBlock \
+	(tkStubsPtr->tk_PhotoPutBlock) /* 246 */
+#endif
+#ifndef Tk_PhotoPutZoomedBlock
+#define Tk_PhotoPutZoomedBlock \
+	(tkStubsPtr->tk_PhotoPutZoomedBlock) /* 247 */
 #endif
 
 #endif /* defined(USE_TK_STUBS) && !defined(USE_TK_STUB_PROCS) */
Index: generic/tkImgGIF.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkImgGIF.c,v
retrieving revision 1.20
diff -u -r1.20 tkImgGIF.c
--- generic/tkImgGIF.c	19 Feb 2002 14:05:43 -0000	1.20
+++ generic/tkImgGIF.c	10 Jun 2002 09:42:57 -0000
@@ -481,7 +481,8 @@
 	break;
     }
 
-    Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height);
+    Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height,
+	    TK_PHOTO_COMPOSITE_SET);
 
     noerror:
     if (block.pixelPtr) {
Index: generic/tkImgPPM.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkImgPPM.c,v
retrieving revision 1.9
diff -u -r1.9 tkImgPPM.c
--- generic/tkImgPPM.c	19 Feb 2002 16:30:26 -0000	1.9
+++ generic/tkImgPPM.c	10 Jun 2002 09:42:57 -0000
@@ -228,7 +228,8 @@
 	    }
 	}
 	block.height = nLines;
-	Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, nLines);
+	Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, nLines,
+		TK_PHOTO_COMPOSITE_SET);
 	destY += nLines;
     }
 
Index: generic/tkImgPhoto.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkImgPhoto.c,v
retrieving revision 1.30
diff -u -r1.30 tkImgPhoto.c
--- generic/tkImgPhoto.c	12 Apr 2002 06:48:58 -0000	1.30
+++ generic/tkImgPhoto.c	10 Jun 2002 09:42:57 -0000
@@ -232,6 +232,7 @@
     int subsampleX, subsampleY;	/* Values specified for -subsample option. */
     Tcl_Obj *format;		/* Value specified for -format option. */
     XColor *background;		/* Value specified for -background option. */
+    int compositingRule;	/* Value specified for -compositingrule opt */
 };
 
 /*
@@ -242,6 +243,7 @@
  * field of the SubcommandOptions structure if that option was specified.
  *
  * OPT_BACKGROUND:		Set if -format option allowed/specified.
+ * OPT_COMPOSITE:		Set if -compositingrule option allowed/spec'd.
  * OPT_FORMAT:			Set if -format option allowed/specified.
  * OPT_FROM:			Set if -from option allowed/specified.
  * OPT_GRAYSCALE:		Set if -grayscale option allowed/specified.
@@ -252,13 +254,14 @@
  */
 
 #define OPT_BACKGROUND	1
-#define OPT_FORMAT	2
-#define OPT_FROM	4
-#define OPT_GRAYSCALE	8
-#define OPT_SHRINK	0x10
-#define OPT_SUBSAMPLE	0x20
-#define OPT_TO		0x40
-#define OPT_ZOOM	0x80
+#define OPT_COMPOSITE	2
+#define OPT_FORMAT	4
+#define OPT_FROM	8
+#define OPT_GRAYSCALE	0x10
+#define OPT_SHRINK	0x20
+#define OPT_SUBSAMPLE	0x40
+#define OPT_TO		0x80
+#define OPT_ZOOM	0x100
 
 /*
  * List of option names.  The order here must match the order of
@@ -267,6 +270,7 @@
 
 static char *optionNames[] = {
     "-background",
+    "-compositingrule",
     "-format",
     "-from",
     "-grayscale",
@@ -775,14 +779,15 @@
 	options.zoomX = options.zoomY = 1;
 	options.subsampleX = options.subsampleY = 1;
 	options.name = NULL;
+	options.compositingRule = TK_PHOTO_COMPOSITE_OVERLAY;
 	if (ParseSubcommandOptions(&options, interp,
-		OPT_FROM | OPT_TO | OPT_ZOOM | OPT_SUBSAMPLE | OPT_SHRINK,
-		&index, objc, objv) != TCL_OK) {
+		OPT_FROM | OPT_TO | OPT_ZOOM | OPT_SUBSAMPLE | OPT_SHRINK |
+		OPT_COMPOSITE, &index, objc, objv) != TCL_OK) {
 	    return TCL_ERROR;
 	}
 	if (options.name == NULL || index < objc) {
 	    Tcl_WrongNumArgs(interp, 2, objv,
-		    "source-image ?-from x1 y1 x2 y2? ?-to x1 y1 x2 y2? ?-zoom x y? ?-subsample x y?");
+		    "source-image ?-compositingrule rule? ?-from x1 y1 x2 y2? ?-to x1 y1 x2 y2? ?-zoom x y? ?-subsample x y?");
 	    return TCL_ERROR;
 	}
 
@@ -858,7 +863,8 @@
 	Tk_PhotoPutZoomedBlock((Tk_PhotoHandle) masterPtr, &block,
 		options.toX, options.toY, options.toX2 - options.toX,
 		options.toY2 - options.toY, options.zoomX, options.zoomY,
-		options.subsampleX, options.subsampleY);
+		options.subsampleX, options.subsampleY,
+		options.compositingRule);
 
 	break;
 
@@ -946,9 +952,9 @@
 	}
 	return result;
 	break;
-      }
+    }
 
-      case PHOTO_GET: {
+    case PHOTO_GET: {
 	/*
 	 * photo get command - first parse and check parameters.
 	 */
@@ -979,9 +985,9 @@
 		pixelPtr[2]);
 	Tcl_AppendResult(interp, string, (char *) NULL);
 	break;
-      }
+    }
 
-      case PHOTO_PUT:
+    case PHOTO_PUT:
 	/*
 	 * photo put command - first parse the options and colors specified.
 	 */
@@ -1104,7 +1110,7 @@
 	block.offset[3] = 0;
 	Tk_PhotoPutBlock((ClientData)masterPtr, &block,
 		options.toX, options.toY, options.toX2 - options.toX,
-		options.toY2 - options.toY);
+		options.toY2 - options.toY, TK_PHOTO_COMPOSITE_SET);
 	ckfree((char *) block.pixelPtr);
 	break;
 
@@ -1326,59 +1332,43 @@
 		return TCL_ERROR;
 	    }
 
+	    setBox.x = x;
+	    setBox.y = y;
+	    setBox.width = 1;
+	    setBox.height = 1;
+	    pixelPtr = masterPtr->pix24 + (y * masterPtr->width + x) * 4;
+
 	    if (transFlag) {
 		/*
-		 * Make pixel transparent.  Do by building a mask for
-		 * all the other pixels in the image and setting the
-		 * validRegion to the intersection of that with the
-		 * old validRegion.  There isn't a neater way to do
-		 * this given the limited set of operations available
-		 * in the platform-independent region operations.
+		 * Make pixel transparent.
 		 */
-		TkRegion setRegion = TkCreateRegion();
+		TkRegion clearRegion = TkCreateRegion();
 
-		if (y > 0) {
-		    setBox.x = 0;
-		    setBox.y = 0;
-		    setBox.width = masterPtr->width;
-		    setBox.height = y;
-		    TkUnionRectWithRegion(&setBox, setRegion, setRegion);
-		}
-		if (x > 0) {
-		    setBox.x = 0;
-		    setBox.y = y;
-		    setBox.width = x;
-		    setBox.height = 1;
-		    TkUnionRectWithRegion(&setBox, setRegion, setRegion);
-		}
-		if (x < masterPtr->width-1) {
-		    setBox.x = x+1;
-		    setBox.y = y;
-		    setBox.width = masterPtr->width-1 - x;
-		    setBox.height = 1;
-		    TkUnionRectWithRegion(&setBox, setRegion, setRegion);
-		}
-		if (y < masterPtr->height-1) {
-		    setBox.x = 0;
-		    setBox.y = y+1;
-		    setBox.width = masterPtr->width;
-		    setBox.height = masterPtr->height-1 - y;
-		    TkUnionRectWithRegion(&setBox, setRegion, setRegion);
-		}
-		TkIntersectRegion(masterPtr->validRegion, setRegion,
+		TkUnionRectWithRegion(&setBox, clearRegion, clearRegion);
+		TkSubtractRegion(masterPtr->validRegion, clearRegion,
 			masterPtr->validRegion);
-		TkDestroyRegion(setRegion);
+		TkDestroyRegion(clearRegion);
+		/*
+		 * Set the alpha value correctly.
+		 */
+		pixelPtr[3] = 0;
 	    } else {
 		/*
 		 * Make pixel opaque.
 		 */
-		setBox.x = x;
-		setBox.y = y;
-		setBox.width = 1;
-		setBox.height = 1;
 		TkUnionRectWithRegion(&setBox, masterPtr->validRegion,
 			masterPtr->validRegion);
+		pixelPtr[3] = 255;
 	    }
+
+	    /*
+	     * Inform the generic image code that the image
+	     * has (potentially) changed.
+	     */
+
+	    Tk_ImageChanged(masterPtr->tkMaster, x, y, 1, 1,
+		    masterPtr->width, masterPtr->height);
+	    masterPtr->flags &= ~IMAGE_CHANGED;
 	}
 
 	}
@@ -1514,7 +1504,8 @@
  *
  *	This procedure is invoked to process one of the options
  *	which may be specified for the photo image subcommands,
- *	namely, -from, -to, -zoom, -subsample, -format, and -shrink.
+ *	namely, -from, -to, -zoom, -subsample, -format, -shrink,
+ *	and -compositingrule.
  *
  * Results:
  *	A standard Tcl result.
@@ -1629,7 +1620,9 @@
 	    }
 	} else if (bit == OPT_FORMAT) {
 	    /*
-	     * The -format option takes a single string value.
+	     * The -format option takes a single string value.  Note
+	     * that parsing this is outside the scope of this
+	     * function.
 	     */
 
 	    if (index + 1 < objc) {
@@ -1640,6 +1633,34 @@
 			"requires a value", (char *) NULL);
 		return TCL_ERROR;
 	    }
+	} else if (bit == OPT_COMPOSITE) {
+	    /*
+	     * The -compositingrule option takes a single value from
+	     * a well-known set.
+	     */
+
+	    if (index + 1 < objc) {
+		/*
+		 * Note that these must match the TK_PHOTO_COMPOSITE_*
+		 * constants.
+		 */
+		static CONST char *compositingRules[] = {
+		    "overlay", "set",
+		    NULL
+		};
+
+		index++;
+		if (Tcl_GetIndexFromObj(interp, objv[index], compositingRules,
+			"compositing rule", 0, &optPtr->compositingRule)
+			!= TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*optIndexPtr = index;
+	    } else {
+		Tcl_AppendResult(interp, "the \"-compositingrule\" option ",
+			"requires a value", (char *) NULL);
+		return TCL_ERROR;
+	    }
 	} else if ((bit != OPT_SHRINK) && (bit != OPT_GRAYSCALE)) {
 	    char *val;
 	    maxValues = ((bit == OPT_FROM) || (bit == OPT_TO))? 4: 2;
@@ -1935,7 +1956,7 @@
 	    && ((masterPtr->dataString != oldData)
 		    || (masterPtr->format != oldFormat))) {
 
-	if (MatchStringFormat(interp, masterPtr->dataString, 
+	if (MatchStringFormat(interp, masterPtr->dataString,
 		masterPtr->format, &imageFormat, &imageWidth,
 		&imageHeight, &oldformat) != TCL_OK) {
 	    return TCL_ERROR;
@@ -2090,6 +2111,8 @@
 	     * to byte-swap any 16 or 32 bit values that we store in the
 	     * image in those situations where the server's endianness
 	     * is different from ours.
+	     *
+	     * Can't we use autoconf to figure this out?
 	     */
 
 	    if (imagePtr != NULL) {
@@ -2660,12 +2683,10 @@
 		masterPtr->ditherX = 0;
 		masterPtr->ditherY = validBox.height;
 	    }
-	} else {
-	    if ((masterPtr->ditherY > 0)
-		    || ((int) validBox.width < masterPtr->ditherX)) {
-		masterPtr->ditherX = validBox.width;
-		masterPtr->ditherY = 0;
-	    }
+	} else if ((masterPtr->ditherY > 0)
+		|| ((int) validBox.width < masterPtr->ditherX)) {
+	    masterPtr->ditherX = validBox.width;
+	    masterPtr->ditherY = 0;
 	}
     }
 
@@ -2721,7 +2742,7 @@
 		(masterPtr->width > 0) ? masterPtr->width: 1,
 		(masterPtr->height > 0) ? masterPtr->height: 1,
 		instancePtr->visualInfo.depth);
-        if(!newPixmap) {
+        if (!newPixmap) {
             panic("Fail to create pixmap with Tk_GetPixmap in ImgPhotoInstanceSetSize.\n");
             return;
         }
@@ -3635,35 +3656,35 @@
 	}
     }
     if (formatPtr == NULL) {
-      useoldformat = 1;
-      for (formatPtr = tsdPtr->oldFormatList; formatPtr != NULL;
-	 formatPtr = formatPtr->nextPtr) {
-	if (formatString != NULL) {
-	    if (strncasecmp(formatString,
-		    formatPtr->name, strlen(formatPtr->name)) != 0) {
-		continue;
-	    }
-	    matched = 1;
-	    if (formatPtr->fileMatchProc == NULL) {
-		Tcl_AppendResult(interp, "-file option isn't supported for ",
-			formatString, " images", (char *) NULL);
-		return TCL_ERROR;
-	    }
-	}
-	if (formatPtr->fileMatchProc != NULL) {
-	    (void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET);
-	    if ((*formatPtr->fileMatchProc)(chan, fileName, (Tcl_Obj *) formatString,
-		    widthPtr, heightPtr, interp)) {
-		if (*widthPtr < 1) {
-		    *widthPtr = 1;
+	useoldformat = 1;
+	for (formatPtr = tsdPtr->oldFormatList; formatPtr != NULL;
+		formatPtr = formatPtr->nextPtr) {
+	    if (formatString != NULL) {
+		if (strncasecmp(formatString,
+			formatPtr->name, strlen(formatPtr->name)) != 0) {
+		    continue;
 		}
-		if (*heightPtr < 1) {
-		    *heightPtr = 1;
+		matched = 1;
+		if (formatPtr->fileMatchProc == NULL) {
+		    Tcl_AppendResult(interp, "-file option isn't supported",
+			    " for ", formatString, " images", (char *) NULL);
+		    return TCL_ERROR;
+		}
+	    }
+	    if (formatPtr->fileMatchProc != NULL) {
+		(void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET);
+		if ((*formatPtr->fileMatchProc)(chan, fileName, (Tcl_Obj *)
+			formatString, widthPtr, heightPtr, interp)) {
+		    if (*widthPtr < 1) {
+			*widthPtr = 1;
+		    }
+		    if (*heightPtr < 1) {
+			*heightPtr = 1;
+		    }
+		    break;
 		}
-		break;
 	    }
 	}
-      }
     }
 
     if (formatPtr == NULL) {
@@ -3760,34 +3781,34 @@
     }
 
     if (formatPtr == NULL) {
-      useoldformat = 1;
-      for (formatPtr = tsdPtr->oldFormatList; formatPtr != NULL;
-	    formatPtr = formatPtr->nextPtr) {
-	if (formatObj != NULL) {
-	    if (strncasecmp(formatString,
-		    formatPtr->name, strlen(formatPtr->name)) != 0) {
-		continue;
+	useoldformat = 1;
+	for (formatPtr = tsdPtr->oldFormatList; formatPtr != NULL;
+		formatPtr = formatPtr->nextPtr) {
+	    if (formatObj != NULL) {
+		if (strncasecmp(formatString,
+			formatPtr->name, strlen(formatPtr->name)) != 0) {
+		    continue;
+		}
+		matched = 1;
+		if (formatPtr->stringMatchProc == NULL) {
+		    Tcl_AppendResult(interp, "-data option isn't supported",
+			    " for ", formatString, " images", (char *) NULL);
+		    return TCL_ERROR;
+		}
 	    }
-	    matched = 1;
-	    if (formatPtr->stringMatchProc == NULL) {
-		Tcl_AppendResult(interp, "-data option isn't supported for ",
-			formatString, " images", (char *) NULL);
-		return TCL_ERROR;
+	    if ((formatPtr->stringMatchProc != NULL)
+		    && (formatPtr->stringReadProc != NULL)
+		    && (*formatPtr->stringMatchProc)(
+			    (Tcl_Obj *) Tcl_GetString(data),
+			    (Tcl_Obj *) formatString,
+			    widthPtr, heightPtr, interp)) {
+		break;
 	    }
 	}
-	if ((formatPtr->stringMatchProc != NULL)
-		&& (formatPtr->stringReadProc != NULL)
-		&& (*formatPtr->stringMatchProc)((Tcl_Obj *) Tcl_GetString(data),
-			(Tcl_Obj *) formatString,
-		widthPtr, heightPtr, interp)) {
-	    break;
-	}
-      }	
     }
     if (formatPtr == NULL) {
 	if ((formatObj != NULL) && !matched) {
-	    Tcl_AppendResult(interp, "image format \"",
-		    formatString,
+	    Tcl_AppendResult(interp, "image format \"", formatString,
 		    "\" is not supported", (char *) NULL);
 	} else {
 	    Tcl_AppendResult(interp, "couldn't recognize image data",
@@ -3854,7 +3875,7 @@
  *---------------------------------------------------------------------- */
 
 void
-Tk_PhotoPutBlock(handle, blockPtr, x, y, width, height)
+Tk_PhotoPutBlock(handle, blockPtr, x, y, width, height, compRule)
     Tk_PhotoHandle handle;	/* Opaque handle for the photo image
 				 * to be updated. */
     register Tk_PhotoImageBlock *blockPtr;
@@ -3864,6 +3885,8 @@
 				 * be updated in the image. */
     int width, height;		/* Dimensions of the area of the image
 				 * to be updated. */
+    int compRule;		/* Compositing rule to use when processing
+				 * transparent pixels. */
 {
     register PhotoMaster *masterPtr;
     int xEnd, yEnd;
@@ -3884,8 +3907,9 @@
 	    && ((y + height) > masterPtr->userHeight)) {
 	height = masterPtr->userHeight - y;
     }
-    if ((width <= 0) || (height <= 0))
+    if ((width <= 0) || (height <= 0)) {
 	return;
+    }
 
     xEnd = x + width;
     yEnd = y + height;
@@ -3937,7 +3961,8 @@
 	    && (greenOffset == 1) && (blueOffset == 2) && (alphaOffset == 3)
 	    && (width <= blockPtr->width) && (height <= blockPtr->height)
 	    && ((height == 1) || ((x == 0) && (width == masterPtr->width)
-		&& (blockPtr->pitch == pitch)))) {
+		&& (blockPtr->pitch == pitch)))
+	    && (compRule == TK_PHOTO_COMPOSITE_SET)) {
 	memcpy((VOID *) destLinePtr,
 		(VOID *) (blockPtr->pixelPtr + blockPtr->offset[0]),
 		(size_t) (height * width * 4));
@@ -3948,35 +3973,70 @@
 	    hCopy = MIN(hLeft, blockPtr->height);
 	    hLeft -= hCopy;
 	    for (; hCopy > 0; --hCopy) {
-		destPtr = destLinePtr;
-		for (wLeft = width; wLeft > 0;) {
-		    wCopy = MIN(wLeft, blockPtr->width);
-		    wLeft -= wCopy;
-		    srcPtr = srcLinePtr;
-		    for (; wCopy > 0; --wCopy) {
-			alpha = srcPtr[alphaOffset];
-			if (!destPtr[3]) {
-			    destPtr[0] = destPtr[1] = destPtr[2] = 0xd9;
-			}
-			if (!alphaOffset || (alpha == 255)) {
-			    /* new solid part of the image */
-			    *destPtr++ = srcPtr[0];
-			    *destPtr++ = srcPtr[greenOffset];
-			    *destPtr++ = srcPtr[blueOffset];
-			    *destPtr++ = 255;
-			} else {
-			    if (alpha) {
-				destPtr[0] += (srcPtr[0] - destPtr[0]) * alpha / 255;
-				destPtr[1] += (srcPtr[greenOffset] - destPtr[1]) * alpha / 255;
-				destPtr[2] += (srcPtr[blueOffset] - destPtr[2]) * alpha / 255;
-				destPtr[3] += (255 - destPtr[3]) * alpha / 255;
+		if ((blockPtr->pixelSize == 4) && (greenOffset == 1)
+		    && (blueOffset == 2) && (alphaOffset == 3)
+		    && (width <= blockPtr->width)
+		    && (compRule == TK_PHOTO_COMPOSITE_SET)) {
+		    memcpy((VOID *) destLinePtr, (VOID *) srcLinePtr,
+			   (size_t) (width * 4));
+		} else {
+		    destPtr = destLinePtr;
+		    for (wLeft = width; wLeft > 0;) {
+			wCopy = MIN(wLeft, blockPtr->width);
+			wLeft -= wCopy;
+			srcPtr = srcLinePtr;
+			for (; wCopy > 0; --wCopy) {
+			    alpha = srcPtr[alphaOffset];
+			    /*
+			     * In the easy case, we can just copy.
+			     */
+			    if (!alphaOffset || (alpha == 255)) {
+				/* new solid part of the image */
+				*destPtr++ = srcPtr[0];
+				*destPtr++ = srcPtr[greenOffset];
+				*destPtr++ = srcPtr[blueOffset];
+				*destPtr++ = 255;
+				srcPtr += blockPtr->pixelSize;
+				continue;
 			    }
+
 			    /*
-			     * else should be empty space
+			     * Combine according to the compositing rule.
 			     */
-			    destPtr += 4;
+			    switch (compRule) {
+			    case TK_PHOTO_COMPOSITE_SET:
+				*destPtr++ = srcPtr[0];
+				*destPtr++ = srcPtr[greenOffset];
+				*destPtr++ = srcPtr[blueOffset];
+				*destPtr++ = alpha;
+				break;
+
+			    case TK_PHOTO_COMPOSITE_OVERLAY:
+				if (!destPtr[3]) {
+				    /*
+				     * There must be a better way to select a
+				     * background colour!
+				     */
+				    destPtr[0] = destPtr[1] = destPtr[2] = 0xd9;
+				}
+
+				if (alpha) {
+				    destPtr[0] += (srcPtr[0] - destPtr[0]) * alpha / 255;
+				    destPtr[1] += (srcPtr[greenOffset] - destPtr[1]) * alpha / 255;
+				    destPtr[2] += (srcPtr[blueOffset] - destPtr[2]) * alpha / 255;
+				    destPtr[3] += (255 - destPtr[3]) * alpha / 255;
+				}
+				/*
+				 * else should be empty space
+				 */
+				destPtr += 4;
+				break;
+
+			    default:
+				panic("unknown compositing rule: %d", compRule);
+			    }
+			    srcPtr += blockPtr->pixelSize;
 			}
-			srcPtr += blockPtr->pixelSize;
 		    }
 		}
 		srcLinePtr += blockPtr->pitch;
@@ -4011,6 +4071,23 @@
 	 * expensive.
 	 */
 
+	if (compRule != TK_PHOTO_COMPOSITE_OVERLAY) {
+	    /*
+	     * Don't need this when using the OVERLAY compositing rule,
+	     * which always strictly increases the valid region.
+	     */
+	    TkRegion workRgn = TkCreateRegion();
+
+	    rect.x = x;
+	    rect.y = y;
+	    rect.width = width;
+	    rect.height = height;
+	    TkUnionRectWithRegion(&rect, workRgn, workRgn);
+	    TkSubtractRegion(masterPtr->validRegion, workRgn,
+		    masterPtr->validRegion);
+	    TkDestroyRegion(workRgn);
+	}
+
 	destLinePtr = masterPtr->pix24 + (y * masterPtr->width + x) * 4 + 3;
 	for (y1 = 0; y1 < height; y1++) {
 	    x1 = 0;
@@ -4018,12 +4095,14 @@
 	    while (x1 < width) {
 		/* search for first non-transparent pixel */
 		while ((x1 < width) && !*destPtr) {
-		    x1++; destPtr += 4;
+		    x1++;
+		    destPtr += 4;
 		}
 		end = x1;
 		/* search for first transparent pixel */
 		while ((end < width) && *destPtr) {
-		    end++; destPtr += 4;
+		    end++;
+		    destPtr += 4;
 		}
 		if (end > x1) {
 		    rect.x = x + x1;
@@ -4080,7 +4159,7 @@
 
 void
 Tk_PhotoPutZoomedBlock(handle, blockPtr, x, y, width, height, zoomX, zoomY,
-	subsampleX, subsampleY)
+	subsampleX, subsampleY, compRule)
     Tk_PhotoHandle handle;	/* Opaque handle for the photo image
 				 * to be updated. */
     register Tk_PhotoImageBlock *blockPtr;
@@ -4092,6 +4171,8 @@
 				 * to be updated. */
     int zoomX, zoomY;		/* Zoom factors for the X and Y axes. */
     int subsampleX, subsampleY;	/* Subsampling factors for the X and Y axes. */
+    int compRule;		/* Compositing rule to use when processing
+				 * transparent pixels. */
 {
     register PhotoMaster *masterPtr;
     int xEnd, yEnd;
@@ -4106,16 +4187,16 @@
     int blockXSkip, blockYSkip;
     XRectangle rect;
 
-    if ((zoomX == 1) && (zoomY == 1) && (subsampleX == 1)
-	    && (subsampleY == 1)) {
-	Tk_PhotoPutBlock(handle, blockPtr, x, y, width, height);
+    if (zoomX==1 && zoomY==1 && subsampleX==1 && subsampleY==1) {
+	Tk_PhotoPutBlock(handle, blockPtr, x, y, width, height, compRule);
 	return;
     }
 
     masterPtr = (PhotoMaster *) handle;
 
-    if ((zoomX <= 0) || (zoomY <= 0))
+    if (zoomX <= 0 || zoomY <= 0) {
 	return;
+    }
     if ((masterPtr->userWidth != 0) && ((x + width) > masterPtr->userWidth)) {
 	width = masterPtr->userWidth - x;
     }
@@ -4123,8 +4204,9 @@
 	    && ((y + height) > masterPtr->userHeight)) {
 	height = masterPtr->userHeight - y;
     }
-    if ((width <= 0) || (height <= 0))
+    if (width <= 0 || height <= 0) {
 	return;
+    }
 
     xEnd = x + width;
     yEnd = y + height;
@@ -4171,18 +4253,20 @@
 
     blockXSkip = subsampleX * blockPtr->pixelSize;
     blockYSkip = subsampleY * blockPtr->pitch;
-    if (subsampleX > 0)
+    if (subsampleX > 0) {
 	blockWid = ((blockPtr->width + subsampleX - 1) / subsampleX) * zoomX;
-    else if (subsampleX == 0)
+    } else if (subsampleX == 0) {
 	blockWid = width;
-    else
+    } else {
 	blockWid = ((blockPtr->width - subsampleX - 1) / -subsampleX) * zoomX;
-    if (subsampleY > 0)
+    }
+    if (subsampleY > 0) {
 	blockHt = ((blockPtr->height + subsampleY - 1) / subsampleY) * zoomY;
-    else if (subsampleY == 0)
+    } else if (subsampleY == 0) {
 	blockHt = height;
-    else
+    } else {
 	blockHt = ((blockPtr->height - subsampleY - 1) / -subsampleY) * zoomY;
+    }
 
     /*
      * Copy the data into our local 24-bit/pixel array.
@@ -4211,23 +4295,43 @@
 		srcPtr = srcLinePtr;
 		for (; wCopy > 0; wCopy -= zoomX) {
 		    for (xRepeat = MIN(wCopy, zoomX); xRepeat > 0; xRepeat--) {
-		      if (!destPtr[3]) {
-			destPtr[0] = destPtr[1] = destPtr[2] = 0xd9;
-		      }
-		      if (!alphaOffset || (srcPtr[alphaOffset] == 255)) {
-			*destPtr++ = srcPtr[0];
-			*destPtr++ = srcPtr[greenOffset];
-			*destPtr++ = srcPtr[blueOffset];
-			*destPtr++ = 255;
-		      } else {
-			if (srcPtr[alphaOffset]) {
-			    destPtr[0] += (srcPtr[0] - destPtr[0]) * srcPtr[alphaOffset] / 255;
-			    destPtr[1] += (srcPtr[greenOffset] - destPtr[1]) * srcPtr[alphaOffset] / 255;
-			    destPtr[2] += (srcPtr[blueOffset] - destPtr[2]) * srcPtr[alphaOffset] / 255;
-			    destPtr[3] += (255 - destPtr[3]) * srcPtr[alphaOffset] / 255;
-		  	}
-			destPtr+=4;
-		      }
+			/*
+			 * Common case (solid pixels) first
+			 */
+			if (!alphaOffset || (srcPtr[alphaOffset] == 255)) {
+			    *destPtr++ = srcPtr[0];
+			    *destPtr++ = srcPtr[greenOffset];
+			    *destPtr++ = srcPtr[blueOffset];
+			    *destPtr++ = 255;
+			    continue;
+ 			}
+
+			switch (compRule) {
+			case TK_PHOTO_COMPOSITE_SET:
+			    *destPtr++ = srcPtr[0];
+			    *destPtr++ = srcPtr[greenOffset];
+			    *destPtr++ = srcPtr[blueOffset];
+			    *destPtr++ = srcPtr[alphaOffset];
+			    break;
+			case TK_PHOTO_COMPOSITE_OVERLAY:
+			    if (!destPtr[3]) {
+				/*
+				 * There must be a better way to select a
+				 * background colour!
+				 */
+				destPtr[0] = destPtr[1] = destPtr[2] = 0xd9;
+			    }
+			    if (srcPtr[alphaOffset]) {
+				destPtr[0] += (srcPtr[0] - destPtr[0]) * srcPtr[alphaOffset] / 255;
+				destPtr[1] += (srcPtr[greenOffset] - destPtr[1]) * srcPtr[alphaOffset] / 255;
+				destPtr[2] += (srcPtr[blueOffset] - destPtr[2]) * srcPtr[alphaOffset] / 255;
+				destPtr[3] += (255 - destPtr[3]) * srcPtr[alphaOffset] / 255;
+			    }
+			    destPtr += 4;
+			    break;
+			default:
+			    panic("unknown compositing rule: %d", compRule);
+			}
 		    }
 		    srcPtr += blockXSkip;
 		}
@@ -4242,12 +4346,29 @@
     }
 
     /*
-     * Add this new block to the region that specifies which data is valid.
+     * Recompute the region of data for which we have valid pixels to plot.
      */
 
     if (alphaOffset) {
 	int x1, y1, end;
 
+	if (compRule != TK_PHOTO_COMPOSITE_OVERLAY) {
+	    /*
+	     * Don't need this when using the OVERLAY compositing rule, which
+	     * always strictly increases the valid region.
+	     */
+	    TkRegion workRgn = TkCreateRegion();
+
+	    rect.x = x;
+	    rect.y = y;
+	    rect.width = width;
+	    rect.height = 1;
+	    TkUnionRectWithRegion(&rect, workRgn, workRgn);
+	    TkSubtractRegion(masterPtr->validRegion, workRgn,
+		    masterPtr->validRegion);
+	    TkDestroyRegion(workRgn);
+	}
+
 	destLinePtr = masterPtr->pix24 + (y * masterPtr->width + x) * 4 + 3;
 	for (y1 = 0; y1 < height; y1++) {
 	    x1 = 0;
@@ -4255,12 +4376,14 @@
 	    while (x1 < width) {
 		/* search for first non-transparent pixel */
 		while ((x1 < width) && !*destPtr) {
-		    x1++; destPtr += 4;
+		    x1++;
+		    destPtr += 4;
 		}
 		end = x1;
 		/* search for first transparent pixel */
 		while ((end < width) && *destPtr) {
-		    end++; destPtr += 4;
+		    end++;
+		    destPtr += 4;
 		}
 		if (end > x1) {
 		    rect.x = x + x1;
@@ -4976,11 +5099,14 @@
 		+ blockPtr->pixelSize - 1;
 	for (x = 0; x < blockPtr->width; x++) {
 	    if (*pixelPtr != 255) {
-		alphaOffset = 3; break;
+		alphaOffset = 3;
+		break;
 	    }
 	    pixelPtr += blockPtr->pixelSize;
 	}
-	if (alphaOffset) break;
+	if (alphaOffset) {
+	    break;
+	}
     }
     if (!alphaOffset) {
 	blockPtr->pixelPtr--;
@@ -5360,13 +5486,13 @@
 static int
 ImgPhotoPostscript(clientData, interp, tkwin, psInfo,
         x, y, width, height, prepass)
-    ClientData clientData;
-    Tcl_Interp *interp;
-    Tk_Window tkwin;
-    Tk_PostscriptInfo psInfo; /* postscript info */
-    int x, y;   /* First pixel to output */
-    int width, height;  /* Width and height of area */
-    int prepass;
+     ClientData clientData;	/* Handle for the photo image */
+    Tcl_Interp *interp;		/* Interpreter */
+    Tk_Window tkwin;		/* (unused) */
+    Tk_PostscriptInfo psInfo;	/* postscript info */
+    int x, y;			/* First pixel to output */
+    int width, height;		/* Width and height of area */
+    int prepass;		/* (unused) */
 {
     Tk_PhotoImageBlock block;
 
@@ -5374,4 +5500,36 @@
     block.pixelPtr += y * block.pitch + x * block.pixelSize;
 
     return Tk_PostscriptPhoto(interp, &block, psInfo, width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_PhotoPutBlock_Old, Tk_PhotoPutZoomedBlock_Old --
+ *
+ * These backward-compatability functions just exist to fill slots in
+ * stubs table.  For the behaviour of *_Old, refer to the
+ * corresponding function without the extra suffix.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Tk_PhotoPutBlock_Old(handle, blockPtr, x, y, width, height)
+     Tk_PhotoHandle handle;
+     Tk_PhotoImageBlock *blockPtr;
+     int x, y, width, height;
+{
+    Tk_PhotoPutBlock(handle, blockPtr, x, y, width, height,
+	    TK_PHOTO_COMPOSITE_OVERLAY);
+}
+
+void
+Tk_PhotoPutZoomedBlock_Old(handle, blockPtr, x, y, width, height,
+			   zoomX, zoomY, subsampleX, subsampleY)
+     Tk_PhotoHandle handle;
+     Tk_PhotoImageBlock *blockPtr;
+     int x, y, width, height, zoomX, zoomY, subsampleX, subsampleY;
+{
+    Tk_PhotoPutZoomedBlock(handle, blockPtr, x, y, width, height,
+	    zoomX, zoomY, subsampleX, subsampleY, TK_PHOTO_COMPOSITE_OVERLAY);
 }
Index: generic/tkInt.decls
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkInt.decls,v
retrieving revision 1.26
diff -u -r1.26 tkInt.decls
--- generic/tkInt.decls	27 May 2002 19:49:32 -0000	1.26
+++ generic/tkInt.decls	10 Jun 2002 09:42:58 -0000
@@ -657,6 +657,10 @@
     void TkGCCleanup(TkDisplay *dispPtr)
 }
 
+declare 145 {mac win} {
+    void TkSubtractRegion (TkRegion sra, TkRegion srcb, TkRegion dr_return)
+}
+
 ##############################################################################
 
 # Define the platform specific internal Tcl interface. These functions are
Index: generic/tkIntDecls.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkIntDecls.h,v
retrieving revision 1.17
diff -u -r1.17 tkIntDecls.h
--- generic/tkIntDecls.h	12 Apr 2002 10:10:48 -0000	1.17
+++ generic/tkIntDecls.h	10 Jun 2002 09:42:58 -0000
@@ -489,6 +489,16 @@
 EXTERN void		TkClipCleanup _ANSI_ARGS_((TkDisplay * dispPtr));
 /* 144 */
 EXTERN void		TkGCCleanup _ANSI_ARGS_((TkDisplay * dispPtr));
+#ifdef __WIN32__
+/* 145 */
+EXTERN void		TkSubtractRegion _ANSI_ARGS_((TkRegion sra, 
+				TkRegion srcb, TkRegion dr_return));
+#endif /* __WIN32__ */
+#ifdef MAC_TCL
+/* 145 */
+EXTERN void		TkSubtractRegion _ANSI_ARGS_((TkRegion sra, 
+				TkRegion srcb, TkRegion dr_return));
+#endif /* MAC_TCL */
 
 typedef struct TkIntStubs {
     int magic;
@@ -719,6 +729,15 @@
     void (*tkFocusFree) _ANSI_ARGS_((TkMainInfo * mainPtr)); /* 142 */
     void (*tkClipCleanup) _ANSI_ARGS_((TkDisplay * dispPtr)); /* 143 */
     void (*tkGCCleanup) _ANSI_ARGS_((TkDisplay * dispPtr)); /* 144 */
+#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
+    void *reserved145;
+#endif /* UNIX */
+#ifdef __WIN32__
+    void (*tkSubtractRegion) _ANSI_ARGS_((TkRegion sra, TkRegion srcb, TkRegion dr_return)); /* 145 */
+#endif /* __WIN32__ */
+#ifdef MAC_TCL
+    void (*tkSubtractRegion) _ANSI_ARGS_((TkRegion sra, TkRegion srcb, TkRegion dr_return)); /* 145 */
+#endif /* MAC_TCL */
 } TkIntStubs;
 
 #ifdef __cplusplus
@@ -1338,6 +1357,18 @@
 #define TkGCCleanup \
 	(tkIntStubsPtr->tkGCCleanup) /* 144 */
 #endif
+#ifdef __WIN32__
+#ifndef TkSubtractRegion
+#define TkSubtractRegion \
+	(tkIntStubsPtr->tkSubtractRegion) /* 145 */
+#endif
+#endif /* __WIN32__ */
+#ifdef MAC_TCL
+#ifndef TkSubtractRegion
+#define TkSubtractRegion \
+	(tkIntStubsPtr->tkSubtractRegion) /* 145 */
+#endif
+#endif /* MAC_TCL */
 
 #endif /* defined(USE_TK_STUBS) && !defined(USE_TK_STUB_PROCS) */
 
Index: generic/tkStubInit.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkStubInit.c,v
retrieving revision 1.33
diff -u -r1.33 tkStubInit.c
--- generic/tkStubInit.c	27 May 2002 19:49:32 -0000	1.33
+++ generic/tkStubInit.c	10 Jun 2002 09:42:58 -0000
@@ -272,6 +272,15 @@
     TkFocusFree, /* 142 */
     TkClipCleanup, /* 143 */
     TkGCCleanup, /* 144 */
+#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
+    NULL, /* 145 */
+#endif /* UNIX */
+#ifdef __WIN32__
+    TkSubtractRegion, /* 145 */
+#endif /* __WIN32__ */
+#ifdef MAC_TCL
+    TkSubtractRegion, /* 145 */
+#endif /* MAC_TCL */
 };
 
 TkIntPlatStubs tkIntPlatStubs = {
@@ -785,8 +794,8 @@
     Tk_NameToWindow, /* 141 */
     Tk_OwnSelection, /* 142 */
     Tk_ParseArgv, /* 143 */
-    Tk_PhotoPutBlock, /* 144 */
-    Tk_PhotoPutZoomedBlock, /* 145 */
+    Tk_PhotoPutBlock_Old, /* 144 */
+    Tk_PhotoPutZoomedBlock_Old, /* 145 */
     Tk_PhotoGetImage, /* 146 */
     Tk_PhotoBlank, /* 147 */
     Tk_PhotoExpand, /* 148 */
@@ -887,6 +896,8 @@
     Tk_SetInternalBorderEx, /* 243 */
     Tk_SetMinimumRequestSize, /* 244 */
     Tk_SetCaretPos, /* 245 */
+    Tk_PhotoPutBlock, /* 246 */
+    Tk_PhotoPutZoomedBlock, /* 247 */
 };
 
 /* !END!: Do not edit above this line. */
Index: mac/tkMacRegion.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/mac/tkMacRegion.c,v
retrieving revision 1.3
diff -u -r1.3 tkMacRegion.c
--- mac/tkMacRegion.c	22 May 1999 06:35:00 -0000	1.3
+++ mac/tkMacRegion.c	10 Jun 2002 09:42:59 -0000
@@ -216,3 +216,33 @@
     rect_return->width = (**rgn).rgnBBox.right - (**rgn).rgnBBox.left;
     rect_return->height = (**rgn).rgnBBox.bottom - (**rgn).rgnBBox.top;
 }
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSubtractRegion --
+ *
+ *	Implements the equivilent of the X window function
+ *	XSubtractRegion.  See X window documentation for more details.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void 
+TkSubtractRegion(
+    TkRegion sra,
+    TkRegion srb,
+    TkRegion dr_return)
+{
+    RgnHandle srcRgnA = (RgnHandle) sra;
+    RgnHandle srcRgnB = (RgnHandle) srb;
+    RgnHandle destRgn = (RgnHandle) dr_return;
+
+    DiffRgn(srcRgnA, srcRgnB, destRgn);
+}
Index: tests/imgPhoto.test
===================================================================
RCS file: /cvsroot/tktoolkit/tk/tests/imgPhoto.test,v
retrieving revision 1.9
diff -u -r1.9 imgPhoto.test
--- tests/imgPhoto.test	1 Feb 2002 14:27:30 -0000	1.9
+++ tests/imgPhoto.test	10 Jun 2002 09:42:59 -0000
@@ -27,7 +27,7 @@
 pack .c
 update
 
-# temporarily copy the README fiel from testsDir to tmpDir
+# temporarily copy the README file from testsDir to tmpDir
 if {![file exists README]} {
     set newREADME [file join $::tcltest::workingDir README]
     file copy [file join $::tcltest::testsDir README] $newREADME
@@ -95,7 +95,7 @@
     eval image delete [image names]
     image create photo image1
     list [info commands image1] [image names] \
-	[image width image1] [image height image1]
+	    [image width image1] [image height image1]
 } {image1 image1 0 0}
 # test imgPhoto-2.3 {ImgPhotoCreate procedure: creation failure} {
 #     image create photo p1
@@ -167,13 +167,13 @@
 } {256 256 {169 117 90}}
 test imgPhoto-4.11 {ImgPhotoCmd procedure: copy option} {
     list [catch {p1 copy} msg] $msg
-} {1 {wrong # args: should be "p1 copy source-image ?-from x1 y1 x2 y2? ?-to x1 y1 x2 y2? ?-zoom x y? ?-subsample x y?"}}
+} {1 {wrong # args: should be "p1 copy source-image ?-compositingrule rule? ?-from x1 y1 x2 y2? ?-to x1 y1 x2 y2? ?-zoom x y? ?-subsample x y?"}}
 test imgPhoto-4.12 {ImgPhotoCmd procedure: copy option} {
     list [catch {p1 copy blah} msg] $msg
 } {1 {image "blah" doesn't exist or is not a photo image}}
 test imgPhoto-4.13 {ImgPhotoCmd procedure: copy option} {
     list [catch {p1 copy p2 -blah} msg] $msg
-} {1 {unrecognized option "-blah": must be -from, -shrink, -subsample, -to, or -zoom}}
+} {1 {unrecognized option "-blah": must be -compositingrule, -from, -shrink, -subsample, -to, or -zoom}}
 test imgPhoto-4.14 {ImgPhotoCmd procedure: copy option} {
     list [catch {p1 copy p2 -from -to} msg] $msg
 } {1 {the "-from" option requires one to four integer values}}
@@ -415,9 +415,46 @@
 test imgPhoto-4.68 {ImgPhotoCmd procedure: transparency set option} {
     checkImgTransLoopResetSet p1 3 3
 } {0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 2 1 0 1 1 1 2 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 1 0 1 1 1 2 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 1 1 2 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 0 1 2 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 0 1 1 2 0 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 0 1 1 1 2 2 1 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 2 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 . 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 , 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 .}
-catch {rename checkImgTrans {}}
 catch {rename checkImgTransLoopSetReset {}}
 catch {rename checkImgTransLoopResetSet {}}
+# Test the compositing rules for copying images
+image create photo p1 -width 3 -height 3
+image create photo p2 -width 2 -height 2
+test imgPhoto-4.68 {ImgPhotoCmd procedure: copy with -compositingrule} {
+    list [catch {p1 copy p2 -to 1 1 -compositingrule} msg] $msg
+} {1 {the "-compositingrule" option requires a value}}
+test imgPhoto-4.69 {ImgPhotoCmd procedure: copy with -compositingrule} {
+    list [catch {p1 copy p2 -to 1 1 -compositingrule BAD} msg] $msg
+} {1 {bad compositing rule "BAD": must be overlay or set}}
+test imgPhoto-4.70 {ImgPhotoCmd procedure: copy with -compositingrule} {
+    # Tests default compositing rule
+    p1 blank
+    p2 blank
+    p1 put white -to 0 0 2 2
+    p2 put white -to 0 0 2 2
+    p2 transparency set 0 0 true
+    p1 copy p2 -to 1 1
+    checkImgTrans p1 3 3
+} {0 2 2 0}
+test imgPhoto-4.71 {ImgPhotoCmd procedure: copy with -compositingrule} {
+    p1 blank
+    p2 blank
+    p1 put white -to 0 0 2 2
+    p2 put white -to 0 0 2 2
+    p2 transparency set 0 0 true
+    p1 copy p2 -to 1 1 -compositingrule overlay
+    checkImgTrans p1 3 3
+} {0 2 2 0}
+test imgPhoto-4.72 {ImgPhotoCmd procedure: copy with -compositingrule} {
+    p1 blank
+    p2 blank
+    p1 put white -to 0 0 2 2
+    p2 put white -to 0 0 2 2
+    p2 transparency set 0 0 true
+    p1 copy p2 -to 1 1 -compositingrule set
+    checkImgTrans p1 3 3
+} {0 2 1 1 2 0}
+catch {rename checkImgTrans {}}
 
 test imgPhoto-5.1 {ImgPhotoGet/Free procedures, shared instances} {
     eval image delete [image names]
Index: unix/tkUnixPort.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixPort.h,v
retrieving revision 1.7
diff -u -r1.7 tkUnixPort.h
--- unix/tkUnixPort.h	25 Sep 2001 16:25:20 -0000	1.7
+++ unix/tkUnixPort.h	10 Jun 2002 09:42:59 -0000
@@ -145,6 +145,8 @@
 	(Region) b, (Region) r)
 #define TkRectInRegion(r, x, y, w, h) XRectInRegion((Region) r, x, y, w, h)
 #define TkSetRegion(d, gc, rgn) XSetRegion(d, gc, (Region) rgn)
+#define TkSubtractRegion(a, b, r) XSubtractRegion((Region) a, \
+	(Region) b, (Region) r)
 #define TkUnionRectWithRegion(rect, src, ret) XUnionRectWithRegion(rect, \
 	(Region) src, (Region) ret)
 
Index: win/tkWinRegion.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/win/tkWinRegion.c,v
retrieving revision 1.2
diff -u -r1.2 tkWinRegion.c
--- win/tkWinRegion.c	14 Sep 1998 18:24:01 -0000	1.2
+++ win/tkWinRegion.c	10 Jun 2002 09:42:59 -0000
@@ -177,3 +177,28 @@
     rect.right = x+width;
     return RectInRegion((HRGN)r, &rect) ? RectanglePart : RectangleOut;
 }
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSubtractRegion --
+ *
+ *	Compute the set-difference of two regions.
+ *
+ * Results:
+ *	Returns the result in the dr_return region.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkSubtractRegion(sra, srb, dr_return)
+    TkRegion sra;
+    TkRegion srb;
+    TkRegion dr_return;
+{
+    CombineRgn((HRGN) dr_return, (HRGN) sra, (HRGN) srb, RGN_DIFF);
+}