/*
 * bltWinop.c --
 *
 *	This module implements simple window commands for the BLT toolkit.
 *
 * Copyright 1991-1998 Lucent Technologies, Inc.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and warranty
 * disclaimer appear in supporting documentation, and that the names
 * of Lucent Technologies any of their entities not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * Lucent Technologies disclaims all warranties with regard to this
 * software, including all implied warranties of merchantability and
 * fitness.  In no event shall Lucent Technologies be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether in
 * an action of contract, negligence or other tortuous action, arising
 * out of or in connection with the use or performance of this
 * software.
 */

#include "bltInt.h"

#ifndef NO_WINOP

#include "bltImage.h"
#include <X11/Xutil.h>
#ifndef _WIN32
#include <X11/Xproto.h>
#endif

static Tcl_CmdProc WinopCmd;

static int
GetRealizedWindow(interp, string, tkwinPtr)
    Tcl_Interp *interp;
    char *string;
    Tk_Window *tkwinPtr;
{
    Tk_Window tkwin;

    tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp));
    if (tkwin == NULL) {
	return TCL_ERROR;
    }
    if (Tk_WindowId(tkwin) == None) {
	Tk_MakeWindowExist(tkwin);
    }
    *tkwinPtr = tkwin;
    return TCL_OK;
}

static Window
StringToWindow(interp, string)
    Tcl_Interp *interp;
    char *string;
{
    int xid;

    if (string[0] == '.') {
	Tk_Window tkwin;

	if (GetRealizedWindow(interp, string, &tkwin) != TCL_OK) {
	    return None;
	}
	if (Tk_IsTopLevel(tkwin)) {
	    return Blt_GetRealWindowId(tkwin);
	} else {
	    return Tk_WindowId(tkwin);
	}
    } else if (Tcl_GetInt(interp, string, &xid) == TCL_OK) {
#ifdef _WIN32
	static TkWinWindow tkWinWindow;

	tkWinWindow.handle = (HWND)xid;
	tkWinWindow.winPtr = NULL;
	tkWinWindow.type = TWD_WINDOW;
	return (Window)&tkWinWindow;
#else
	return (Window)xid;
#endif
    }
    return None;
}

#ifdef _WIN32

static int
GetWindowSize(
    Tcl_Interp *interp,
    Window window,
    int *widthPtr, 
    int *heightPtr)
{
    int result;
    RECT region;
    TkWinWindow *winPtr = (TkWinWindow *)window;

    result = GetWindowRect(winPtr->handle, &region);
    if (result) {
	*widthPtr = region.right - region.left;
	*heightPtr = region.bottom - region.top;
	return TCL_OK;
    }
    return TCL_ERROR;
}

#else

/*
 *----------------------------------------------------------------------
 *
 * XGeometryErrorProc --
 *
 *	Flags errors generated from XGetGeometry calls to the X server.
 *
 * Results:
 *	Always returns 0.
 *
 * Side Effects:
 *	Sets a flag, indicating an error occurred.
 *
 *----------------------------------------------------------------------
 */
/* ARGSUSED */
static int
XGeometryErrorProc(clientData, errEventPtr)
    ClientData clientData;
    XErrorEvent *errEventPtr;
{
    int *errorPtr = clientData;

    *errorPtr = TCL_ERROR;
    return 0;
}

static int
GetWindowSize(interp, window, widthPtr, heightPtr)
    Tcl_Interp *interp;
    Window window;
    int *widthPtr, *heightPtr;
{
    int result;
    int any = -1;
    int x, y, borderWidth, depth;
    Window root;
    Tk_ErrorHandler handler;
    Tk_Window tkwin;
    
    tkwin = Tk_MainWindow(interp);
    handler = Tk_CreateErrorHandler(Tk_Display(tkwin), any, X_GetGeometry, 
	    any, XGeometryErrorProc, &result);
    result = XGetGeometry(Tk_Display(tkwin), window, &root, &x, &y, 
	  (unsigned int *)widthPtr, (unsigned int *)heightPtr,
	  (unsigned int *)&borderWidth, (unsigned int *)&depth);
    Tk_DeleteErrorHandler(handler);
    XSync(Tk_Display(tkwin), False);
    if (result) {
	return TCL_OK;
    }
    return TCL_ERROR;
}
#endif /*_WIN32*/


#ifndef _WIN32
/*ARGSUSED*/
static int
ColormapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    register int i;
    Tk_Window tkwin;
#define MAXCOLORS 256
    register XColor *colorPtr;
    XColor colorArr[MAXCOLORS];
    unsigned long int pixelValues[MAXCOLORS];
    int inUse[MAXCOLORS];
    char string[20];
    unsigned long int *indexPtr;
    int nFree;

    if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
	return TCL_ERROR;
    }
    /* Initially, we assume all color cells are allocated. */
    memset((char *)inUse, 0, sizeof(int) * MAXCOLORS);

    /*
     * Start allocating color cells.  This will tell us which color cells
     * haven't already been allocated in the colormap.  We'll release the
     * cells as soon as we find out how many there are.
     */
    nFree = 0;
    for (indexPtr = pixelValues, i = 0; i < MAXCOLORS; i++, indexPtr++) {
	if (!XAllocColorCells(Tk_Display(tkwin), Tk_Colormap(tkwin),
		False, NULL, 0, indexPtr, 1)) {
	    break;
	}
	inUse[*indexPtr] = TRUE;/* Indicate the cell is unallocated */
	nFree++;
    }
    XFreeColors(Tk_Display(tkwin), Tk_Colormap(tkwin), pixelValues, nFree, 0);
    for (colorPtr = colorArr, i = 0; i < MAXCOLORS; i++, colorPtr++) {
	colorPtr->pixel = i;
    }
    XQueryColors(Tk_Display(tkwin), Tk_Colormap(tkwin), colorArr, MAXCOLORS);
    for (colorPtr = colorArr, i = 0; i < MAXCOLORS; i++, colorPtr++) {
	if (!inUse[colorPtr->pixel]) {
	    sprintf(string, "#%02x%02x%02x", (colorPtr->red >> 8),
		(colorPtr->green >> 8), (colorPtr->blue >> 8));
	    Tcl_AppendElement(interp, string);
	    sprintf(string, "%ld", colorPtr->pixel);
	    Tcl_AppendElement(interp, string);
	}
    }
    return TCL_OK;
}

#endif

/*ARGSUSED*/
static int
LowerOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    register int i;
    Window window;
    Display *display;

    display = Tk_Display(Tk_MainWindow(interp));
    for (i = 2; i < argc; i++) {
	window = StringToWindow(interp, argv[i]);
	if (window == None) {
	    return TCL_ERROR;
	}
	XLowerWindow(display, window);
    }
    return TCL_OK;
}

/*ARGSUSED*/
static int
RaiseOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    register int i;
    Window window;
    Display *display;

    display = Tk_Display(Tk_MainWindow(interp));
    for (i = 2; i < argc; i++) {
	window = StringToWindow(interp, argv[i]);
	if (window == None) {
	    return TCL_ERROR;
	}
	XRaiseWindow(display, window);
    }
    return TCL_OK;
}

/*ARGSUSED*/
static int
MapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    register int i;
    Window window;
    Display *display;

    display = Tk_Display(Tk_MainWindow(interp));
    for (i = 2; i < argc; i++) {
	if (argv[i][0] == '.') {
	    Tk_Window tkwin;
	    Tk_FakeWin *fakePtr;

	    if (GetRealizedWindow(interp, argv[i], &tkwin) != TCL_OK) {
		return TCL_ERROR;
	    }
	    fakePtr = (Tk_FakeWin *) tkwin;
	    fakePtr->flags |= TK_MAPPED;
	    window = Tk_WindowId(tkwin);
	} else {
	    int xid;

	    if (Tcl_GetInt(interp, argv[i], &xid) != TCL_OK) {
		return TCL_ERROR;
	    }
	    window = (Window)xid;
	}
	XMapWindow(display, window);
    }
    return TCL_OK;
}

/*ARGSUSED*/
static int
MoveOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not Used. */
    char **argv;
{
    int x, y;
    Tk_Window tkwin;
    Window window;
    Display *display;

    tkwin = Tk_MainWindow(interp);
    display = Tk_Display(tkwin);
    window = StringToWindow(interp, argv[2]);
    if (window == None) {
	return TCL_ERROR;
    }
    if (Tk_GetPixels(interp, tkwin, argv[3], &x) != TCL_OK) {
	Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
	return TCL_ERROR;
    }
    if (Tk_GetPixels(interp, tkwin, argv[4], &y) != TCL_OK) {
	Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
	return TCL_ERROR;
    }
    XMoveWindow(display, window, x, y);
    return TCL_OK;
}

/*ARGSUSED*/
static int
UnmapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    register int i;
    Window window;
    Display *display;

    display = Tk_Display(Tk_MainWindow(interp));
    for (i = 2; i < argc; i++) {
	if (argv[i][0] == '.') {
	    Tk_Window tkwin;
	    Tk_FakeWin *fakePtr;

	    if (GetRealizedWindow(interp, argv[i], &tkwin) != TCL_OK) {
		return TCL_ERROR;
	    }
	    fakePtr = (Tk_FakeWin *) tkwin;
	    fakePtr->flags &= ~TK_MAPPED;
	    window = Tk_WindowId(tkwin);
	} else {
	    int xid;

	    if (Tcl_GetInt(interp, argv[i], &xid) != TCL_OK) {
		return TCL_ERROR;
	    }
	    window = (Window)xid;
	}
	XMapWindow(display, window);
    }
    return TCL_OK;
}

/* ARGSUSED */
static int
ChangesOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;		/* Not used. */
{
    Tk_Window tkwin;

    if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Tk_IsTopLevel(tkwin)) {
	XSetWindowAttributes attrs;
	Window window;
	unsigned int mask;

	window = Blt_GetRealWindowId(tkwin);
	attrs.backing_store = WhenMapped;
	attrs.save_under = True;
	mask = CWBackingStore | CWSaveUnder;
	XChangeWindowAttributes(Tk_Display(tkwin), window, mask, &attrs);
    }
    return TCL_OK;
}

/* ARGSUSED */
static int
ParentOp(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;			/* Not used. */
char **argv;		/* Not used. */
{
    Window window;
    int value;
    char buf[50];

    if (Tcl_GetInt(interp, argv[2], &value) != TCL_OK) {
        return TCL_ERROR;
    }
    window = Blt_GetParent(Tk_Display(Tk_MainWindow(interp)), (Window)value);
    if (window) {
        sprintf(buf, "0x%x", (int)window);    
        Tcl_AppendResult(interp, buf, 0);
    }
    return TCL_OK;
}

/* ARGSUSED */
static int
QueryOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;		/* Not used. */
{
    int rootX, rootY, childX, childY;
    Window root, child;
    unsigned int mask;
    Tk_Window tkwin = (Tk_Window)clientData;

    /* GetCursorPos */
    if (XQueryPointer(Tk_Display(tkwin), Tk_WindowId(tkwin), &root,
	    &child, &rootX, &rootY, &childX, &childY, &mask)) {
	char string[200];

	sprintf(string, "@%d,%d", rootX, rootY);
	Tcl_SetResult(interp, string, TCL_VOLATILE);
    }
    return TCL_OK;
}

/*ARGSUSED*/
static int
WarpToOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_Window tkwin, mainWindow;

    mainWindow = (Tk_Window)clientData;
    if (argc > 2) {
	if (argv[2][0] == '@') {
	    int x, y;
	    Window root;

	    if (Blt_GetXY(interp, mainWindow, argv[2], &x, &y) != TCL_OK) {
		return TCL_ERROR;
	    }
	    root = RootWindow(Tk_Display(mainWindow), 
		Tk_ScreenNumber(mainWindow));
	    XWarpPointer(Tk_Display(mainWindow), None, root, 0, 0, 0, 0, x, y);
	} else {
	    if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (!Tk_IsMapped(tkwin)) {
		Tcl_AppendResult(interp, "can't warp to unmapped window \"",
		    Tk_PathName(tkwin), "\"", (char *)NULL);
		return TCL_ERROR;
	    }
	    XWarpPointer(Tk_Display(tkwin), None, Tk_WindowId(tkwin),
		0, 0, 0, 0, Tk_Width(tkwin) / 2, Tk_Height(tkwin) / 2);
	}
    }
    return QueryOp(clientData, interp, 0, (char **)NULL);
}

#ifdef notdef
static int
ReparentOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    Tk_Window tkwin;

    if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
	return TCL_ERROR;
    }
    if (argc == 4) {
	Tk_Window newParent;

	if (GetRealizedWindow(interp, argv[3], &newParent) != TCL_OK) {
	    return TCL_ERROR;
	}
	Blt_RelinkWindow2(tkwin, Blt_GetRealWindowId(tkwin), newParent, 0, 0);
    } else {
	Blt_UnlinkWindow(tkwin);
    }
    return TCL_OK;
}
#endif


/*
 * This is a temporary home for these image routines.  They will be
 * moved when a new image type is created for them.
 */
/*ARGSUSED*/
static int
ConvolveOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Blt_ColorImage srcImage, destImage;
    Filter2D filter;
    int nValues;
    char **valueArr;
    double *kernel;
    double value, sum;
    register int i;
    int dim;
    int result = TCL_ERROR;

    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    if (Tcl_SplitList(interp, argv[4], &nValues, &valueArr) != TCL_OK) {
	return TCL_ERROR;
    }
    kernel = NULL;
    if (nValues == 0) {
	Tcl_AppendResult(interp, "empty kernel", (char *)NULL);
	goto error;
    }
    dim = (int)sqrt((double)nValues);
    if ((dim * dim) != nValues) {
	Tcl_AppendResult(interp, "kernel must be square", (char *)NULL);
	goto error;
    }
    kernel = Blt_Malloc(sizeof(double) * nValues);
    sum = 0.0;
    for (i = 0; i < nValues; i++) {
	if (Tcl_GetDouble(interp, valueArr[i], &value) != TCL_OK) {
	    goto error;
	}
	kernel[i] = value;
	sum += value;
    }
    filter.kernel = kernel;
    filter.support = dim * 0.5;
    filter.sum = (sum == 0.0) ? 1.0 : sum;
    filter.scale = 1.0 / nValues;

    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_ConvolveColorImage(srcImage, &filter);
    Blt_FreeColorImage(srcImage);
    Blt_ColorImageToPhoto(destImage, destPhoto);
    Blt_FreeColorImage(destImage);
    result = TCL_OK;
  error:
    if (valueArr != NULL) {
	Blt_Free(valueArr);
    }
    if (kernel != NULL) {
	Blt_Free(kernel);
    }
    return result;
}

/*ARGSUSED*/
static int
QuantizeOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    Blt_ColorImage srcImage, destImage;
    int nColors = 1;
    int result;

    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    if ((dest.width != src.width) || (dest.height != src.height)) {
	Tk_PhotoSetSize(destPhoto, src.width, src.height);
    }
    if (argc>4) {
       if (Tcl_GetInt(interp, argv[4], &nColors) != TCL_OK) {
	return TCL_ERROR;
       }
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_PhotoToColorImage(destPhoto);
    result = Blt_QuantizeColorImage(srcImage, destImage, nColors);
    if (result == TCL_OK) {
	Blt_ColorImageToPhoto(destImage, destPhoto);
    }
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(destImage);
    return result;
}

static int
GetColorPix32(Tcl_Interp *interp, Tk_Window tkwin, char *color, Pix32 *colPtr) {
    XColor *xCol;
    int red, green, blue;
    
    colPtr->Alpha = 255;
    if (*color == '#' && strlen(color) == 7 && 3 ==
        sscanf(color+1, "%02x%02x%02x", &red, &green, &blue)) {
        colPtr->Red = red;
        colPtr->Green = green;
        colPtr->Blue = blue;
        return TCL_OK;
    }
    xCol = Tk_GetColor(interp, tkwin, Tk_GetUid(color));
    if (xCol == NULL) {
        return TCL_ERROR;
    }
    colPtr->Red = (xCol->red >> 8);
    colPtr->Green = (xCol->green >> 8);
    colPtr->Blue = (xCol->blue >> 8);
    return TCL_OK;
}

static int
XColorToPix32(XColor *xCol, Pix32 *colPtr) {
    
    colPtr->Alpha = 255;
    colPtr->Red = (xCol->red >> 8);
    colPtr->Green = (xCol->green >> 8);
    colPtr->Blue = (xCol->blue >> 8);
    return TCL_OK;
}

/*ARGSUSED*/
static int
AlphaOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    Blt_ColorImage srcImage, destImage;
    int result, alpha, anycolor = 0, withAlpha, hasWith = 0;
    Tk_Window tkwin = (Tk_Window)clientData;
    Pix32 oldColor;
    char *string;
    int negate = 0, shift = 0;

    alpha = 0;
    if (!strcmp("-shift", argv[2])) {
        shift = 1;
        argc -= 1;
        argv += 1;
    }
    
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    if (0 && srcPhoto == destPhoto) {
        Tcl_AppendResult(interp, "src and dest images must be different",
            (char *)NULL);
        return TCL_ERROR;
    }
    string = argv[4];
    if (string[0] == '!') {
        negate = 1;
        string++;
    }
    if (!strcmp(string, "*")) {
        anycolor = 1;
        if (argc <= 5) {
            Tcl_AppendResult(interp, "must give an alpha", 0);
            return TCL_ERROR;
        }
            
    } else {
        if (GetColorPix32(interp, tkwin, string, &oldColor) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    if (argc > 5) {
        if (Tcl_GetInt(interp, argv[5], &alpha) != TCL_OK) {
            return TCL_ERROR;
        }
        if (alpha<0 || alpha > 255) {
            Tcl_AppendResult(interp, "alpha must be >= 0 and <= 255", argv[3],
                (char *)NULL);
                return TCL_ERROR;
        }
    }
    if (argc > 6) {
        if (Tcl_GetInt(interp, argv[6], &withAlpha) != TCL_OK) {
            return TCL_ERROR;
        }
        hasWith = 1;
        if (withAlpha<0 || withAlpha > 255) {
            Tcl_AppendResult(interp, "withalpha must be >= 0 and <= 255", argv[3],
                (char *)NULL);
                return TCL_ERROR;
        }
    }
    if ((dest.width != src.width) || (dest.height != src.height)) {
        Tk_PhotoSetSize(destPhoto, src.width, src.height);
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_PhotoToColorImage(destPhoto);
    result = TCL_OK;
    {
    int width, height;
    int count, same;
    Pix32 *srcPtr, *destPtr, *endPtr, *color;
    unsigned char origAlpha;

    width = Blt_ColorImageWidth(srcImage);
    height = Blt_ColorImageHeight(srcImage);
    count = width * height;
    color = &oldColor;
    
    srcPtr = Blt_ColorImageBits(srcImage);
    destPtr = Blt_ColorImageBits(destImage);
    if (shift) {
        for (endPtr = destPtr + count; destPtr < endPtr; srcPtr++, destPtr++) {
            origAlpha = srcPtr->Alpha;
            if (origAlpha == 0) {
                destPtr->value = srcPtr->value;
                continue;
            }
            destPtr->value = color->value;
            destPtr->Alpha = srcPtr->Blue;
        }
    } else if (!anycolor) {
        color = &oldColor;
        for (endPtr = destPtr + count; destPtr < endPtr; srcPtr++, destPtr++) {
            origAlpha = srcPtr->Alpha;
            destPtr->value = srcPtr->value;
            same = (srcPtr->Red == color->Red && srcPtr->Green == color->Green &&
            srcPtr->Blue == color->Blue);
            if (hasWith && origAlpha != withAlpha) {
                
            } else if (negate) {
                if ((!same) && (origAlpha != (unsigned char)-1)) {
                    origAlpha = alpha;
                }
            } else {
                if (same) {
                    origAlpha = alpha;
                }
            }
            destPtr->Alpha = origAlpha;
        }
    } else {
        for (endPtr = destPtr + count; destPtr < endPtr; srcPtr++, destPtr++) {
            origAlpha = srcPtr->Alpha;
            destPtr->value = srcPtr->value;
            if (hasWith && origAlpha == withAlpha) {
                destPtr->Alpha = alpha;
            } else if (origAlpha == (unsigned char)-1) {
                destPtr->Alpha = alpha;
            }
        }
    }
    }

    if (result == TCL_OK) {
	Blt_ColorImageToPhoto(destImage, destPhoto);
    }
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(destImage);
    return result;
}

/*ARGSUSED*/
static int
RecolorOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    Blt_ColorImage srcImage, destImage;
    int result, alpha;
    Tk_Window tkwin = (Tk_Window)clientData;
    Pix32 oldColor, newColor;

    alpha = -1;
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    if (GetColorPix32(interp, tkwin, argv[4], &oldColor) != TCL_OK) {
	return TCL_ERROR;
    }
    if (GetColorPix32(interp, tkwin, argv[5], &newColor) != TCL_OK) {
        return TCL_ERROR;
    }
    if (argc > 6) {
        if (Tcl_GetInt(interp, argv[6], &alpha) != TCL_OK) {
            return TCL_ERROR;
        }
        if (alpha<0 || alpha > 255) {
            Tcl_AppendResult(interp, "alpha must be >= 0 and <= 255", argv[3],
                (char *)NULL);
                return TCL_ERROR;
        }
    }
    if ((dest.width != src.width) || (dest.height != src.height)) {
        Tk_PhotoSetSize(destPhoto, src.width, src.height);
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_PhotoToColorImage(destPhoto);
    result = Blt_RecolorImage(srcImage, destImage, &oldColor, &newColor, alpha);
    if (result == TCL_OK) {
	Blt_ColorImageToPhoto(destImage, destPhoto);
    }
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(destImage);
    return result;
}

/*ARGSUSED*/
static int
ColorsOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto;
    Tk_PhotoImageBlock src;
    Blt_ColorImage srcImage;
    int top, x, y, isalph, iscnt, isNew, cnt;
    int i, rng[4], from = 0;
    register Pix32 *srcPtr;
    Tcl_Obj *listPtr;
    char buf[100];
    Blt_HashEntry *hPtr;
    Blt_HashTable hTbl;
    Blt_HashSearch cursor;

    top = 0;
    isalph = 0;
    iscnt = 0;
    while (argc > 3) {
        if (!strcmp(argv[2], "-alpha")) {
            isalph = 1;
        } else if (!strcmp(argv[2], "-from")) {
            from = 1;
            if (argc<7) {
                Tcl_AppendResult(interp, "expected 4 args: x1 y1 x2 y2", (char *)NULL);
                return TCL_ERROR;
            }
            for (i=0; i<4; i++) {
                if (Tcl_GetInt(interp, argv[i+3], rng+i)) {
                    return TCL_ERROR;
                }
            }
            argc -= 4;
            argv += 4;
        } else if (!strcmp(argv[2], "-count")) {
            iscnt = 1;
        } else {
            Tcl_AppendResult(interp, "expected -from, -alpha or -count", (char *)NULL);
            return TCL_ERROR;
        }
        argc--;
        argv++;
    }
    if (argc != 3) {
        Tcl_AppendResult(interp, "too few arguments", (char *)NULL);
        return TCL_ERROR;
    }
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width < 1) || (src.height < 1)) {
	return TCL_OK;
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    srcPtr = Blt_ColorImageBits(srcImage);

    Blt_InitHashTable(&hTbl, BLT_STRING_KEYS);
    for (y = 0; y < src.height; y++) {
        if (from && (y < rng[1] || y > rng[3])) continue;
        for (x = 0; x < src.width; x++) {
            if (from && (x < rng[0] || x > rng[2])) continue;
            if (!isalph) {
                sprintf(buf, "#%02x%02x%02x", srcPtr->Red, srcPtr->Green, srcPtr->Blue);
            } else {
                sprintf(buf, "#%02x%02x%02x:%02x", srcPtr->Red, srcPtr->Green, srcPtr->Blue, srcPtr->Alpha);
            }
            hPtr = Blt_CreateHashEntry(&hTbl, buf, &isNew);
            if (isNew) {
                Blt_SetHashValue(hPtr, 1);
            } else {
                cnt = (int)Blt_GetHashValue(hPtr);
                cnt++;
                Blt_SetHashValue(hPtr, cnt);
            }
            srcPtr++;
        }
    }
    listPtr = Tcl_NewListObj(0,0);
    for (hPtr = Blt_FirstHashEntry(&hTbl, &cursor);
        hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
            
        Tcl_Obj *objPtr = Tcl_NewStringObj(Blt_GetHashKey(&hTbl, hPtr), -1);
        Tcl_ListObjAppendElement(interp, listPtr, objPtr);
        if (iscnt) {
            cnt = (int)Blt_GetHashValue(hPtr);
            sprintf(buf, "%d", cnt);
            objPtr = Tcl_NewStringObj(buf, -1);
            Tcl_ListObjAppendElement(interp, listPtr, objPtr);
        }
    }
    Tcl_SetObjResult(interp, listPtr);
    Blt_DeleteHashTable(&hTbl);
    return TCL_OK;
}

/*ARGSUSED*/
static int
TransOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto;
    Tk_PhotoImageBlock src;
    Blt_ColorImage srcImage;
    int x, y, alpha,isSet = 0;
    register Pix32 *srcPtr;
    char buf[100];

    if (argc == 6) {
        isSet = 1;
        if (Tcl_GetInt(interp, argv[5], &alpha) != TCL_OK) {
            return TCL_ERROR;
        }
        if (alpha<0 || alpha > 255) {
            Tcl_AppendResult(interp, "alpha must be >= 0 and <= 255", argv[3],
                (char *)NULL);
                return TCL_ERROR;
        }
    }
    if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
        return TCL_ERROR;
    }
    if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
        return TCL_ERROR;
    }
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width < 1) || (src.height < 1)) {
        Tcl_AppendResult(interp, "empty image", (char *)NULL);
        return TCL_ERROR;
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    srcPtr = Blt_ColorImageBits(srcImage);
    if (y < 0 || y >= src.height || x < 0 || x >= src.width) {
        Tcl_AppendResult(interp, "out of range", (char *)NULL);
        return TCL_ERROR;
    } 
    srcPtr = srcPtr + y*src.width + x;
    if (isSet) {
        srcPtr->Alpha = alpha;
        Blt_ColorImageToPhoto(srcImage, srcPhoto);
    } else {
        sprintf(buf, "%d", srcPtr->Alpha);
        Tcl_AppendResult(interp, buf, 0);
    }
    return TCL_OK;
}

#define PD_SRC_OVER(srcColor,srcAlpha,dstColor,dstAlpha) \
	(srcColor*srcAlpha/255) + dstAlpha*(255-srcAlpha)/255*dstColor/255
#define PD_SRC_OVER_ALPHA(srcAlpha,dstAlpha) \
	(srcAlpha + (255-srcAlpha)*dstAlpha/255)

static
void PixBlend(
    Pix32 *srcPtr, Pix32 *src2Ptr, Pix32 *destPtr,
    unsigned char alpha, unsigned char dAlpha
) {
    dAlpha = 255-alpha;
    destPtr->Red = PD_SRC_OVER(srcPtr->Red, alpha, src2Ptr->Red, dAlpha);
    destPtr->Green = PD_SRC_OVER(srcPtr->Green, alpha, src2Ptr->Green, dAlpha);
    destPtr->Blue = PD_SRC_OVER(srcPtr->Blue, alpha, src2Ptr->Blue, dAlpha);
    destPtr->Alpha = 255; /* PD_SRC_OVER_ALPHA(alpha, dAlpha); */
}

static
void PixMerged( Pix32 *srcPtr, Pix32 *src2Ptr, Pix32 *destPtr, Pix32 *colorPtr) {
    unsigned char alpha;
    int isdead;
    
    alpha = srcPtr->Blue;
    isdead = (srcPtr->Red == 0xde && srcPtr->Green == 0xad);
    if (alpha == 0 && isdead) { /* "," inner fill */
        destPtr->value = src2Ptr->value;
    } else if (alpha == 0) {    /* "." none or transparent */
        destPtr->value = 0;
    } else if (isdead == 0) {   /* solid color with alpha */
        destPtr->value = colorPtr->value;
        destPtr->Alpha = alpha;
    } else {                    /* Blend color with inner fill */
        PixBlend( colorPtr, src2Ptr, destPtr, alpha, src2Ptr->Alpha);
    }
}


/* For RGB grab alpha from blue and substitute maskcolor for RG="#DEAD"*/
int Blt_ImageMergeInner(Tcl_Interp *interp, char *srcName, char *src2Name,
    char * destName, XColor *maskColor, int leaveMsg) {
    Tk_PhotoHandle srcPhoto, srcPhoto2, destPhoto;
    Tk_PhotoImageBlock src, src2, dest;
    Blt_ColorImage srcImage, srcImage2, destImage;
    double opacity;
    double opacity2;
    int result, isWc;
    Pix32 withColor;

    isWc = 0;
    opacity = 0.5;
    opacity2 = -1;
    srcPhoto = Blt_FindPhoto(interp, srcName);
    if (srcPhoto == NULL) {
        if (leaveMsg) 
            Tcl_AppendResult(interp, "source image \"", srcName, "\" doesn't",
            " exist or is not a photo image", (char *)NULL);
        return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
        if (leaveMsg) 
            Tcl_AppendResult(interp, "source image \"", srcName, "\" is empty",
               (char *)NULL);
        return TCL_ERROR;
    }

    srcPhoto2 = Blt_FindPhoto(interp, src2Name);
    if (srcPhoto2 == NULL) {
        if (leaveMsg) 
            Tcl_AppendResult(interp, "source image \"", src2Name, "\" doesn't",
                " exist or is not a photo image", (char *)NULL);
        return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto2, &src2);
    if ((src2.width <= 1) || (src2.height <= 1)) {
        if (leaveMsg) 
           Tcl_AppendResult(interp, "source image \"", src2Name, "\" is empty",
                (char *)NULL);
        return TCL_ERROR;
    }
    if (maskColor) {
        XColorToPix32(maskColor, &withColor);
        isWc = 1;
        goto domerge;
    }
    /*
    if (argc>5) {
        
        if (isdigit(argv[5][0]) == 0 && argv[5][0] != '.') {
            Tk_Window tkwin;
            char *colStr;
            
            tkwin = Tk_MainWindow(interp);
            isWc = 1;
            
            colStr = argv[5];
            if (colStr[0] == '!') {
                colStr++;
                isWc = 2;
            }

                return TCL_ERROR;
            }
            if (src.width < 4 || src.height < 4) {
                Tcl_AppendResult(interp, "src image too small ",  0);
                return TCL_ERROR;
            }
            if (src2.width < 4 || src2.height < 4) {
                Tcl_AppendResult(interp, "src2 image too small ",  0);
                return TCL_ERROR;
            }
            if (dest.width < 4 || dest.height < 4) {
                Tcl_AppendResult(interp, "dest image too small ",  0);
                return TCL_ERROR;
            }
            goto domerge;
        }
        
        if (Tcl_GetDouble(interp, argv[5], &opacity) != TCL_OK) {
            return TCL_ERROR;
        }
        if (opacity < 0.0 || opacity > 1.0) {
            Tcl_AppendResult(interp, "opacity must be >= 0.0 and <= 1.0: ", argv[5], (char *)NULL);
            return TCL_ERROR;
        }
    }
    if (argc>6) {
        if (Tcl_GetDouble(interp, argv[6], &opacity2) != TCL_OK) {
            return TCL_ERROR;
        }
        if (opacity2 < 0.0 || opacity2 > 1.0) {
            Tcl_AppendResult(interp, "opacity must be >= 0.0 and <= 1.0: ", argv[6], (char *)NULL);
            return TCL_ERROR;
        }
    }
    */
    if (isWc == 0 && ((src2.width != src.width) || (src2.height != src.height))) {
        int sh, sw;
        sw = (src2.width>src.width?src2.width:src.width);
        sh = (src2.height>src.height?src2.height:src.height);
        Tk_PhotoSetSize(srcPhoto2, sw, sh);
        Tk_PhotoGetImage(srcPhoto2, &src2);
        if (sw != src.width || sh != src.height) {
            Tk_PhotoSetSize(srcPhoto, sw, sh);
            Tk_PhotoGetImage(srcPhoto, &src);
        }
    }

    domerge:
    destPhoto = Blt_FindPhoto(interp, destName);
    if (destPhoto == NULL) {
        if (leaveMsg)
            Tcl_AppendResult(interp, "destination image \"", destName, "\" doesn't",
            " exist or is not a photo image", (char *)NULL);
        return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);

    if ((dest.width != src.width) || (dest.height != src.height)) {
        Tk_PhotoSetSize(destPhoto, src.width, src.height);
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    srcImage2 = Blt_PhotoToColorImage(srcPhoto2);
    destImage = Blt_PhotoToColorImage(destPhoto);

    Tk_PhotoGetImage(destPhoto, &dest);
    if ((dest.width != src.width) || (dest.height != src.height)) {
        Tk_PhotoSetSize(destPhoto, src.width, src.height);
        destImage = Blt_PhotoToColorImage(destPhoto);
    }
    if (isWc != 1) {
        result = Blt_MergeColorImage(srcImage, srcImage2, destImage, opacity, opacity2,
            isWc ? &withColor : NULL);
    } else {
        int xs1, xs2, xd1, xd2, xsc, xdc;
        int ys1, ys2, yd1, yd2, ysc, ydc;
        Pix32 *srcPtr, *src2Ptr, *destPtr;

        srcPtr = Blt_ColorImageBits(srcImage);
        src2Ptr = Blt_ColorImageBits(srcImage2);
        destPtr = Blt_ColorImageBits(destImage);

        xsc = (src2.width/2);
        ysc = (src2.height/2);
        xdc = (dest.width/2);
        ydc = (dest.height/2);
        for (xs1 = xsc-1, xs2=xsc, xd1 = xdc-1, xd2=xd1+1;
        xd1>=0; xd1--, xs1--, xd2++, xs2++) {
            if (xs1<0) { xs1 = xsc-1; xs2 = xs1+1; }
            for (ys1 = ysc-1, ys2 = ysc, yd1 = ydc-1, yd2=yd1+1;
            yd1>=0; yd1--, ys1--, yd2++, ys2++) {
                if (ys1<0) { ys1 = ysc-1; ys2 = ys1+1; }
                
#define DDDC_O(ind,ind2) if ( withColor.value == srcPtr[ind].value) destPtr[ind].value =  src2Ptr[ind2].value
#define DDDC(ind,ind2) PixMerged(srcPtr+(ind), src2Ptr+(ind2), destPtr+(ind), &withColor)

                DDDC(yd1*dest.width+xd1, ys1*src2.width+xs1);
                if (ys2>=src2.height) { ys2--; }
                if (xs2>=src2.width) { xs2--; }
                if (yd2<dest.height && xd2<dest.width) {
                    DDDC(yd2*dest.width+xd2, ys2*src2.width+xs2);
                }
                if (xd2<dest.width) {
                    DDDC(yd1*dest.width+xd2, ys1*src2.width+xs2);
                }
                if (yd2<dest.height) {
                    DDDC(yd2*dest.width+xd1, ys2*src2.width+xs1);
                }
            }
        }
        result = TCL_OK;

    }
    if (result == TCL_OK) {
        Blt_ColorImageToPhoto(destImage, destPhoto);
    }
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(srcImage2);
    Blt_FreeColorImage(destImage);
    return result;
}


/*ARGSUSED*/
static int
MergeOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, srcPhoto2, destPhoto;
    Tk_PhotoImageBlock src, src2, dest;
    Blt_ColorImage srcImage, srcImage2, destImage;
    double opacity;
    double opacity2;
    int result, isWc;
    Pix32 withColor;

    isWc = 0;
    opacity = 0.5;
    opacity2 = -1;
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }

    srcPhoto2 = Blt_FindPhoto(interp, argv[3]);
    if (srcPhoto2 == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto2, &src2);
    if ((src2.width <= 1) || (src2.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[3], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    if (argc>5) {
        
        if (isdigit(argv[5][0]) == 0 && argv[5][0] != '.') {
            Tk_Window tkwin;
            char *colStr;
            
            tkwin = Tk_MainWindow(interp);
            isWc = 1;
            
            colStr = argv[5];
            if (colStr[0] == '!') {
                colStr++;
                isWc = 2;
            }

            if (GetColorPix32(interp, tkwin, colStr, &withColor) != TCL_OK) {
                return TCL_ERROR;
            }
            if (src.width < 4 || src.height < 4) {
                Tcl_AppendResult(interp, "src image too small ",  0);
                return TCL_ERROR;
            }
            if (src2.width < 4 || src2.height < 4) {
                Tcl_AppendResult(interp, "src2 image too small ",  0);
                return TCL_ERROR;
            }
            goto domerge;
        }
        
        if (Tcl_GetDouble(interp, argv[5], &opacity) != TCL_OK) {
            return TCL_ERROR;
        }
        if (opacity < 0.0 || opacity > 1.0) {
            Tcl_AppendResult(interp, "opacity must be >= 0.0 and <= 1.0: ", argv[5], (char *)NULL);
            return TCL_ERROR;
        }
    }
    if (argc>6) {
        if (Tcl_GetDouble(interp, argv[6], &opacity2) != TCL_OK) {
            return TCL_ERROR;
        }
        if (opacity2 < 0.0 || opacity2 > 1.0) {
            Tcl_AppendResult(interp, "opacity must be >= 0.0 and <= 1.0: ", argv[6], (char *)NULL);
            return TCL_ERROR;
        }
    }

    if (isWc == 0 && ((src2.width != src.width) || (src2.height != src.height))) {
	int sh, sw;
	sw = (src2.width>src.width?src2.width:src.width);
	sh = (src2.height>src.height?src2.height:src.height);
	Tk_PhotoSetSize(srcPhoto2, sw, sh);
        Tk_PhotoGetImage(srcPhoto2, &src2);
	if (sw != src.width || sh != src.height) {
	    Tk_PhotoSetSize(srcPhoto, sw, sh);
            Tk_PhotoGetImage(srcPhoto, &src);
	}
    }

domerge:
    destPhoto = Blt_FindPhoto(interp, argv[4]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[4], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);

    srcImage = Blt_PhotoToColorImage(srcPhoto);
    srcImage2 = Blt_PhotoToColorImage(srcPhoto2);
    destImage = Blt_PhotoToColorImage(destPhoto);

    Tk_PhotoGetImage(destPhoto, &dest);
    if ((dest.width != src.width) || (dest.height != src.height)) {
	Tk_PhotoSetSize(destPhoto, src.width, src.height);
        destImage = Blt_PhotoToColorImage(destPhoto);
    }
    if (isWc != 1) {
        result = Blt_MergeColorImage(srcImage, srcImage2, destImage, opacity, opacity2,
            isWc ? &withColor : NULL);
    } else {
        int xs1, xs2, xd1, xd2, xsc, xdc;
        int ys1, ys2, yd1, yd2, ysc, ydc;
        Pix32 *srcPtr, *src2Ptr, *destPtr;

        srcPtr = Blt_ColorImageBits(srcImage);
        src2Ptr = Blt_ColorImageBits(srcImage2);
        destPtr = Blt_ColorImageBits(destImage);

        xsc = (src2.width/2);
        ysc = (src2.height/2);
        xdc = (dest.width/2);
        ydc = (dest.height/2);
        for (xs1 = xsc-1, xs2=xs1+1, xd1 = xdc-1, xd2=xdc;
        xd1>=0; xd1--, xs1--, xd2++, xs2++) {
            if (xs1<0) { xs1 = xsc-1; xs2 = xs1+1; }
            for (ys1 = ysc-1, ys2 = ys1+1, yd1 = ydc-1, yd2=yd1+1;
            yd1>=0; yd1--, ys1--, yd2++, ys2++) {
                if (ys1<0) { ys1 = ysc-1; ys2 = ys1+1; }
                
#define DDC(ind,ind2) destPtr[ind].value = ( withColor.value == srcPtr[ind].value ? src2Ptr[ind2].value : srcPtr[ind].value)

                DDC(yd1*dest.width+xd1, ys1*src2.width+xs1);
                //destPtr[yd1*dest.width+xd1].value = srcPtr[ys1*src2.width+xs1].value;
                if (ys2>=src2.height) { ys2--; }
                if (xs2>=src2.width) { xs2--; }
                if (yd2<dest.height && xd2<dest.width)
                DDC(yd2*dest.width+xd2, ys2*src2.width+xs2);
                // destPtr[yd2*dest.width+xd2].value = srcPtr[ys2*src2.width+xs2].value;
                if (xd2<dest.width)
                DDC(yd1*dest.width+xd2, ys1*src2.width+xs2);
                //destPtr[yd1*dest.width+xd2].value = srcPtr[ys1*src2.width+xs2].value;
                if (yd2<dest.height)
                DDC(yd2*dest.width+xd1, ys2*src2.width+xs1);
                //destPtr[yd2*dest.width+xd1].value = srcPtr[ys2*src2.width+xs1].value;
                //if (ya<src.height && ya2<dest.height && xa<src.width && xa2<dest.width) {}
            }
        }
        result = TCL_OK;

    }
    if (result == TCL_OK) {
	Blt_ColorImageToPhoto(destImage, destPhoto);
    }
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(srcImage2);
    Blt_FreeColorImage(destImage);
    return result;
}


/*ARGSUSED*/
static int
ReadJPEGOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
#if HAVE_JPEGLIB_H
    int result;
    Tcl_DString dString;
    char *fileName;
    Tk_PhotoHandle photo;	/* The photo image to write into. */

    photo = Blt_FindPhoto(interp, argv[3]);
    if (photo == NULL) {
	Tcl_AppendResult(interp, "image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tcl_DStringInit(&dString);
    fileName = Tcl_TranslateFileName(interp, argv[2], &dString);
    
    result = Blt_JPEGToPhoto(interp, fileName, photo);
    Tcl_DStringInit(&dString);
    return result;
#else
    Tcl_AppendResult(interp, "JPEG support not compiled", (char *)NULL);
    return TCL_ERROR;
#endif
}

static double Drand( double val) {
#ifdef HAVE_DRAND48
    return drand48();
#else
    return (double)random()/RAND_MAX;
#endif
}

#define SLANTX(x,y) (doslant==0 || slant == 0.0?x:(((int)(x + (y * slant)))%src.width))
#define ARCX(x,y) (doarc==0 || sineVal == 0.0?x:(((int)(x + (src.width*sqrt(0.5*0.5-((double)y/src.height-0.5)*((double)y/src.height-0.5)) * sineVal)))%src.width))
#define SINEX(x,y) (dosine==0 || sineVal == 0.0?x:(((int)(x + (src.width*(*tfunc)(M_PI *y/src.height) * sineVal)))%src.width))
#ifdef HAVE_DRAND48
#define RANDX(c) (dorand==0 || randVal == 0.0?c:(c+(Drand(0) * randVal - (randVal/2.0))))
#else
#define RANDX(c) c
#endif
#define SKEWX(c) ((doskew==0 || skew == 1.0)?c:(ox>=skew?1.0:c/skew))
#define CLAMP(c)	((((c) < 0.0) ? 0.0 : ((c) > 1.0) ? 1.0 : (c)))

static int
GradientsOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    double range[3];
    double left[3];
    int x, y, width;
    double t;
    XColor C;
    XColor *leftPtr, *rightPtr;
    Tk_Window tkwin;
    
    tkwin = Tk_MainWindow(interp);

    leftPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[2]));
    if (leftPtr == NULL) {
        return TCL_ERROR;
    }
    rightPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[3]));
    if (rightPtr == NULL) {
        return TCL_ERROR;
    }
    if (Tcl_GetInt(interp, argv[4], &width) != TCL_OK) {
        return TCL_ERROR;
    }
    if (width<=2) {
        Tcl_AppendResult(interp, "width must be > 2", 0);
        return TCL_ERROR;
    }
    left[0] = (double)(leftPtr->red >> 8);
    left[1] = (double)(leftPtr->green >> 8);
    left[2] = (double)(leftPtr->blue >> 8);
    range[0] = (double)((rightPtr->red - leftPtr->red) / 257.0);
    range[1] = (double)((rightPtr->green - leftPtr->green) / 257.0);
    range[2] = (double)((rightPtr->blue - leftPtr->blue) / 257.0);

    y =0;
    for (x = 0; x < width; x++) {
        char cbuf[100];
        t = ((double)( sin(M_PI_2 * (double)x / (double)width)));
        t = CLAMP(t);
        C.red = (unsigned short)(left[0] + t * range[0]);
        C.green = (unsigned short)(left[1] + t * range[1]);
        C.blue = (unsigned short)(left[2] + t * range[2]);
        sprintf(cbuf, "#%02x%02x%02x", C.red, C.green, C.blue);
        if (x) { Tcl_AppendResult(interp, " ", 0); }
        Tcl_AppendResult(interp, cbuf, 0);
    }
    return TCL_OK;
}

int
Blt_GetGradient(Tcl_Interp *interp, Tk_Window tkwin, Gradient *gradPtr) {
    XColor *leftPtr, *rightPtr;
    double range[3];
    double left[3];
    int x, y, width;
    double t;
    XColor **destPtr, C;

    leftPtr = gradPtr->color;
    rightPtr = gradPtr->color2;
    left[0] = (double)(leftPtr->red >> 8);
    left[1] = (double)(leftPtr->green >> 8);
    left[2] = (double)(leftPtr->blue >> 8);
    range[0] = (double)((rightPtr->red - leftPtr->red) / 257.0);
    range[1] = (double)((rightPtr->green - leftPtr->green) / 257.0);
    range[2] = (double)((rightPtr->blue - leftPtr->blue) / 257.0);

    y =0;
    width = gradPtr->width;
    if (gradPtr->grads) {
        Blt_FreeGradient(gradPtr);
    }
    destPtr = gradPtr->grads = (XColor**)Blt_Calloc(width+1, sizeof(XColor*));
    for (x = 0; x < width; x++) {
        char cbuf[100];
        t = ((double)( sin(M_PI_2 * (double)x / (double)width)));
        t = CLAMP(t);
        C.red = (unsigned short)(left[0] + t * range[0]);
        C.green = (unsigned short)(left[1] + t * range[1]);
        C.blue = (unsigned short)(left[2] + t * range[2]);
        /**destPtr = Tk_GetColorByValue(tkwin, &C); */
        sprintf(cbuf, "#%02x%02x%02x", C.red, C.green, C.blue);
        *destPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(cbuf));
        if (*destPtr == NULL) break;
        destPtr++;
    }
    gradPtr->origColor = gradPtr->color;
    gradPtr->origColor2 = gradPtr->color2;
    gradPtr->origWidth = gradPtr->width;
    return TCL_OK;
}

int
Blt_FreeGradient(Gradient *gradPtr) {
    XColor **destPtr;
    if (gradPtr->grads) {
        destPtr = gradPtr->grads;
        while (*destPtr) {
            Tk_FreeColor(*destPtr);
            destPtr++;
        }
        Blt_Free( gradPtr->grads);
    }
    gradPtr->grads = NULL;
    return TCL_OK;

}

/*ARGSUSED*/
static int
GradientOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle photo;
    Tk_PhotoImageBlock src;
    XColor *leftPtr, *rightPtr;
    Tk_Window tkwin;
    double range[3];
    double left[3];
    Pix32 *destPtr;
    Blt_ColorImage destImage;
    double skew, randVal, slant, sineVal;
    int dorand, doslant, doskew, doarc, dosine, gtype, result, ox;
    int nWidth = 0, nHeight = 0;
    Tcl_Obj *objPtr;
    static char *types[] = { "linear", "radial", "sine", "halfsine", "rectangular",  "split", "blank", 0 };
    enum ITypeIdx {
        ILinearIdx, IRadialIdx, ISineIdx, IHalfSineIdx, IRectIdx, ISplitIdx,
        IBlankIdx
    };
    double (*tfunc)(double);
    int alpha;
    unsigned char aVal;

    aVal = 255;
    tfunc = &sin;
    dorand = 0;
    doskew = 0;
    doslant = 0;
    dosine = 0;
    doarc = 0;
    skew = 1.0;
    gtype = ISineIdx;
    tkwin = Tk_MainWindow(interp);
    photo = Blt_FindPhoto(interp, argv[2]);
    if (photo == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(photo, &src);
    leftPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[3]));
    if (leftPtr == NULL) {
	return TCL_ERROR;
    }
    rightPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[4]));
    if (rightPtr == NULL) {
	return TCL_ERROR;
    }
    while (argc > 5) {
        if (argc < 7) {
            Tcl_AppendResult(interp, "expected argument", argv[5], (char *)NULL);
            return TCL_ERROR;
        }
        if (!strcmp("-alpha", argv[5])) {
            if (Tcl_GetInt(interp, argv[6], &alpha) != TCL_OK) {
                return TCL_ERROR;
            }
            if (alpha<=0 || alpha > 255) {
                Tcl_AppendResult(interp, "alpha must be > 0 and <= 255", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
            aVal = (unsigned char) alpha;

        } else if (!strcmp("-type", argv[5])) {
            objPtr = Tcl_NewStringObj(argv[6],-1);
            Tcl_IncrRefCount(objPtr);
            result = Tcl_GetIndexFromObj(interp, objPtr, types, "type", 0,
                &gtype);
            Tcl_DecrRefCount(objPtr);
            if (result != TCL_OK) {
                return TCL_ERROR;
            }
                
        } else if (!strcmp("-skew", argv[5])) {
            doskew = 1;
            if (Tcl_GetDouble(interp, argv[6], &skew) != TCL_OK) {
                return TCL_ERROR;
            }
            if (skew<0.0 || skew>1.0) {
                Tcl_AppendResult(interp, "skew must be >=0 && <=1.0: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-width", argv[5])) {
            if (Tk_GetPixels(interp, tkwin, argv[6], &nWidth) != TCL_OK) {
                return TCL_ERROR;
            }
            if (nWidth<1 || nWidth>5000) {
                Tcl_AppendResult(interp, "width must be >=1 && <=5000: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-height", argv[5])) {
            if (Tk_GetPixels(interp, tkwin, argv[6], &nHeight) != TCL_OK) {
                return TCL_ERROR;
            }
            if (nHeight<1 || nHeight>5000) {
                Tcl_AppendResult(interp, "width must be >=1 && <=5000: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-rand", argv[5])) {
            dorand = 1;
            if (Tcl_GetDouble(interp, argv[6], &randVal) != TCL_OK) {
                return TCL_ERROR;
            }
            if (randVal<0.0 || randVal>0.1) {
                Tcl_AppendResult(interp, "randVal must be >= 0.0 && <= 0.1: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-slant", argv[5])) {
            doslant = 1;
            if (Tcl_GetDouble(interp, argv[6], &slant) != TCL_OK) {
                return TCL_ERROR;
            }
            if (slant<-1000.0 || slant>1000.0) {
                Tcl_AppendResult(interp, "slant must be >= -1000.0 && <= 1000.0: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-mathval", argv[5])) {
            dosine = 1;
            if (Tcl_GetDouble(interp, argv[6], &sineVal) != TCL_OK) {
                return TCL_ERROR;
            }
            if (sineVal<-1000.0 || sineVal>1000.0) {
                Tcl_AppendResult(interp, "mathval must be >= -1000.0 && <= 1000.0: ", argv[6],
                    (char *)NULL);
                    return TCL_ERROR;
            }
        } else if (!strcmp("-mathfunc", argv[5])) {
            if (!strcmp("circle", argv[6])) {
                dosine = 0;
                doarc = 1;
            } else if (!strcmp("sin", argv[6])) {
                tfunc = &sin;
            } else if (!strcmp("cos", argv[6])) {
                tfunc = &cos;
            } else if (!strcmp("atan", argv[6])) {
                tfunc = &atan;
            } else if (!strcmp("acos", argv[6])) {
                tfunc = &acos;
            } else if (!strcmp("asin", argv[6])) {
                tfunc = &asin;
            } else if (!strcmp("rand", argv[6])) {
                tfunc = &Drand;
            } else if (!strcmp("cosh", argv[6])) {
                tfunc = &cosh;
            } else if (!strcmp("sinh", argv[6])) {
                tfunc = &sinh;
            } else if (!strcmp("tan", argv[6])) {
                tfunc = &tan;
            } else if (!strcmp("tanh", argv[6])) {
                tfunc = &tanh;
            } else if (!strcmp("log", argv[6])) {
                tfunc = &log;
            } else if (!strcmp("log10", argv[6])) {
                tfunc = &log10;
            } else if (!strcmp("exp", argv[6])) {
                tfunc = &exp;
            } else if (!strcmp("sqrt", argv[6])) {
                tfunc = &sqrt;
            } else {
                Tcl_AppendResult(interp, "mathfunc ", argv[6], " not one of: ",
                    "sin cos tan sinh cosh tanh asin acos atan log log10 exp sqrt rand circle", (char *)NULL);
                return TCL_ERROR;
            }
        } else {
            Tcl_AppendResult(interp, "argument \"", argv[5], "\" not one of: -type -skew -rand -slant -curve -circle",
                (char *)NULL);
                return TCL_ERROR;
        }
        argc -= 2;
        argv += 2;
    }
    if ((nWidth>0 || nHeight>0) && ((nWidth != src.width) || (nHeight != src.height))) {
        if (nWidth<=0) { nWidth = src.width; }
        if (nHeight<=0) { nHeight = src.height; }
        Tk_PhotoSetSize(photo, nWidth, nHeight);
        Tk_PhotoGetImage(photo, &src);
    }
    left[0] = (double)(leftPtr->red >> 8);
    left[1] = (double)(leftPtr->green >> 8);
    left[2] = (double)(leftPtr->blue >> 8);
    range[0] = (double)((rightPtr->red - leftPtr->red) / 257.0);
    range[1] = (double)((rightPtr->green - leftPtr->green) / 257.0);
    range[2] = (double)((rightPtr->blue - leftPtr->blue) / 257.0);

    destImage = Blt_CreateColorImage(src.width, src.height);
    destPtr = Blt_ColorImageBits(destImage);
    
    if (gtype == ILinearIdx) {
	register int x, y, sx;
	double t;

	for (y = 0; y < src.height; y++) {
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
		t = (double)sx / src.width;
		t = SKEWX(t);
		t = RANDX(t);
		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
     } else if (gtype == ISineIdx) {
	register int x, y, sx;
	double t;

	for (y = 0; y < src.height; y++) {
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
		t = ((double)(sin(2*M_PI_2 * (double)sx / (double)src.width)));
		t = SKEWX(t);
		t = RANDX(t);
 		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
    } else if (gtype == IHalfSineIdx) {
	register int x, y, sx;
	double t;

	for (y = 0; y < src.height; y++) {
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
		t = ((double)( sin(M_PI_2 * (double)sx / (double)src.width)));
		t = SKEWX(t);
		t = RANDX(t);
 		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
    } else if (gtype == IRadialIdx) {
	register int x, y, sx;
	register double dx, dy;
	double dy2;
	double t;
	double midX, midY;

	midX = midY = 0.5;
	for (y = 0; y < src.height; y++) {
	    dy = (y / (double)src.height) - midY;
	    dy2 = dy * dy;
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
		dx = (sx / (double)src.width) - midX;
		t = 1.0  - (double)sqrt(dx * dx + dy2);
		t = SKEWX(t);
		t = RANDX(t);
		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
  
    } else if (gtype == IRectIdx) {
	register int x, y, sx;
	register double dx, dy;
	double t, px, py;
	double midX, midY;
	double cosTheta, sinTheta;
	double angle;

	angle = M_PI_2 * -0.3;
	cosTheta = cos(angle);
	sinTheta = sin(angle);

	midX = 0.5, midY = 0.5;
	for (y = 0; y < src.height; y++) {
	    dy = (y / (double)src.height) - midY;
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
		dx = (sx / (double)src.width) - midX;
		px = dx * cosTheta - dy * sinTheta;
		py = dx * sinTheta + dy * cosTheta;
		t = FABS(px) + FABS(py);
		t = SKEWX(t);
		t = RANDX(t);
		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
    } else if (gtype == ISplitIdx) {
	register int x, y, sx, midx;
	double t;

        midx = src.width/2;
	for (y = 0; y < src.height; y++) {
	    for (x = 0; x < src.width; x++) {
		ox = (double)x / src.width;
		sx = SLANTX(x,y);
		sx = ARCX(sx,y);
		sx = SINEX(sx,y);
	        if (x<midx) {
	            sx += (0.1 * src.width);
	        } else {
	            sx -= (0.1 * src.width);
	        }
		t = (double)sx / src.width;
		t = SKEWX(t);
		t = RANDX(t);
		t = CLAMP(t);
		destPtr->Red = (unsigned char)(left[0] + t * range[0]);
		destPtr->Green = (unsigned char)(left[1] + t * range[1]);
		destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
    } else if (gtype == IBlankIdx) {
	register int x, y;

	for (y = 0; y < src.height; y++) {
	    for (x = 0; x < src.width; x++) {
		destPtr->Red = (unsigned char)0xFF;
		destPtr->Green = (unsigned char)0xFF;
		destPtr->Blue = (unsigned char)0xFF;
		destPtr->Alpha = aVal;
		destPtr++;
	    }
	}
    }
    Blt_ColorImageToPhoto(destImage, photo);
    Blt_FreeColorImage(destImage);
    return TCL_OK;
}

/*ARGSUSED*/
static int
ResampleOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    ResampleFilter *filterPtr, *vertFilterPtr, *horzFilterPtr;
    char *filterName;

    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    filterName = (argc > 4) ? argv[4] : "none";
    if (Blt_GetResampleFilter(interp, filterName, &filterPtr) != TCL_OK) {
	return TCL_ERROR;
    }
    vertFilterPtr = horzFilterPtr = filterPtr;
    if ((filterPtr != NULL) && (argc > 5)) {
	if (Blt_GetResampleFilter(interp, argv[5], &filterPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
	vertFilterPtr = filterPtr;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    if ((dest.width <= 1) || (dest.height <= 1)) {
	Tk_PhotoSetSize(destPhoto, src.width, src.height);
	goto copyImage;
    }
    if ((src.width == dest.width) && (src.height == dest.height)) {
      copyImage:
	/* Source and destination image sizes are the same. Don't
	 * resample. Simply make copy of image */
	dest.width = src.width;
	dest.height = src.height;
	dest.pixelPtr = src.pixelPtr;
	dest.pixelSize = src.pixelSize;
	dest.pitch = src.pitch;
	dest.offset[0] = src.offset[0];
	dest.offset[1] = src.offset[1];
	dest.offset[2] = src.offset[2];
	Tk_PhotoPutBlock(destPhoto, &dest, 0, 0, dest.width, dest.height);
	return TCL_OK;
    }
    if (filterPtr == NULL) {
	Blt_ResizePhoto(srcPhoto, 0, 0, src.width, src.height, destPhoto);
    } else {
	Blt_ResamplePhoto(srcPhoto, 0, 0, src.width, src.height, destPhoto,
		horzFilterPtr, vertFilterPtr);
    }
    return TCL_OK;
}

/*ARGSUSED*/
static int
BlurOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    double radius;

    radius = 3;
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    if (argc > 4) {
        if (Tcl_GetDouble(interp, argv[4], &radius) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    Tk_PhotoSetSize(destPhoto, src.width, src.height);
    return Blt_BlurColorImage(srcPhoto, destPhoto, (int)(radius + 0.5) );
}

/*ARGSUSED*/
static int
RotateOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    Blt_ColorImage srcImage, destImage;
    double theta;

    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    if (Tcl_ExprDouble(interp, argv[4], &theta) != TCL_OK) {
	return TCL_ERROR;
    }
    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_RotateColorImage(srcImage, theta);

    Blt_ColorImageToPhoto(destImage, destPhoto);
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(destImage);
    return TCL_OK;
}

/*ARGSUSED*/
int
Blt_ImageMirror(Tcl_Interp *interp, char *srcImg, char *dstImg, int flip, int flags) {
    Tk_PhotoHandle srcPhoto, destPhoto;
    Blt_ColorImage srcImage, destImage;
    int x, y, x1, x2, y2;
    Tk_PhotoImageBlock src, dest;
    Pix32 *destPtr, *srcPtr;

    srcPhoto = Blt_FindPhoto(interp, srcImg);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "image \"", srcImg, "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    destPhoto = Blt_FindPhoto(interp, dstImg);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", dstImg, "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }


    if ((flip == MIRROR_TILE || flip == MIRROR_INNER || flip == MIRROR_OUTER ) && srcPhoto == destPhoto) {
        Tcl_AppendResult(interp, "image must be different", (char *)NULL);
        return TCL_ERROR;
    }
    Tk_PhotoGetImage(destPhoto, &dest);
    if (src.width <= 0 || src.height <= 0) {
        Tcl_AppendResult(interp, "src image empty ",  0);
        return TCL_ERROR;
    }

    if (flip == MIRROR_TILE) {
        if ((dest.width != (src.width *2)) || (dest.height <= (src.height*2))) {
            Tk_PhotoSetSize(destPhoto, 2*src.width, 2*src.height);
            Tk_PhotoGetImage(destPhoto, &dest);
        }
    } else if (flip != MIRROR_OUTER && flip != MIRROR_INNER) {
        if ((dest.width != src.width) || (dest.height <= src.height)) {
            Tk_PhotoSetSize(destPhoto, src.width, src.height);
            Tk_PhotoGetImage(destPhoto, &dest);
        }
    }

    srcImage = Blt_PhotoToColorImage(srcPhoto);
    destImage = Blt_PhotoToColorImage(destPhoto);
    destPtr = Blt_ColorImageBits(destImage);
    srcPtr = Blt_ColorImageBits(srcImage);

    if (flip == MIRROR_INNER) {
        /* Center  */
        int xs1, xs2, xd1, xd2, xsc, xdc;
        int ys1, ys2, yd1, yd2, ysc, ydc;
        
        if (src.width < 4 || src.height < 4) {
            Tcl_AppendResult(interp, "src image too small ",  0);
            return TCL_ERROR;
        }
        if (dest.width < 4 || dest.height < 4) {
            Tcl_AppendResult(interp, "dest image too small ",  0);
            return TCL_ERROR;
        }
        xsc = (src.width/2);
        ysc = (src.height/2);
        xdc = (dest.width/2);
        ydc = (dest.height/2);
        for (xs1 = xsc-1, xs2=xs1+1, xd1 = xdc-1, xd2=xd1+1;
             xd1>=0; xd1--, xs1--, xd2++, xs2++) {
            if (xs1<0) { xs1 = xsc-1; xs2 = xs1+1; }
            for (ys1 = ysc-1, ys2 = ys1+1, yd1 = ydc-1, yd2=yd1+1;
                 yd1>=0; yd1--, ys1--, yd2++, ys2++) {
                if (ys1<0) { ys1 = ysc-1; ys2 = ys1+1; }
                destPtr[yd1*dest.width+xd1].value = srcPtr[ys1*src.width+xs1].value;
                if (ys2>=src.height) { ys2--; }
                if (xs2>=src.width) { xs2--; }
                if (yd2<dest.height && xd2<dest.width)
                    destPtr[yd2*dest.width+xd2].value = srcPtr[ys2*src.width+xs2].value;
                if (xd2<dest.width)
                    destPtr[yd1*dest.width+xd2].value = srcPtr[ys1*src.width+xs2].value;
                if (yd2<dest.height)
                    destPtr[yd2*dest.width+xd1].value = srcPtr[ys2*src.width+xs1].value;
                //if (ya<src.height && ya2<dest.height && xa<src.width && xa2<dest.width) {}
            }
        }
    }
    if (flip == MIRROR_X) {
        /* Flip X */
        for (y = 0; y < src.height; y++) {
            for (x = 0, x1 = src.width*y, x2 = src.width*(y+1)-1;
            x < src.width; 
            x++, x1++, x2--) {
                destPtr[x2].value = srcPtr[x1].value;
            }
        }
    }
    if (flip == MIRROR_Y) {
        /* Flip Y */
        for (x = 0; x < src.width; x++) {
            for (y = 0, y2 = src.height-1; y < src.height; y++, y2--) {
                destPtr[y2*dest.width+x].value = srcPtr[y*src.width+x].value;
            }
        }
    }
    if (flip == MIRROR_XY) {
        /* Flip X and Y */
        for (x = 0, x2 = src.width-1; x < src.width; x++, x2--) {
            for (y = 0, y2 = src.height-1; y < src.height; y++, y2--) {
                destPtr[y2*dest.width+x2].value = srcPtr[y*src.width+x].value;
            }
        }
    }

    if (flip == MIRROR_TILE) {
        for (y = 0; y < src.height; y++) {
            for (x = 0; x < src.width; x++) {
                destPtr[y*dest.width+x].value = srcPtr[y*src.width+x].value;
            }
        }
        /* Flip X */
        for (y = 0; y < src.height; y++) {
            for (x = 0, x1 = src.width*y, x2 = 2*src.width*(y+1)-1;
                 x < src.width; 
                 x++, x1++, x2--) {
                destPtr[x2].value = srcPtr[x1].value;
            }
        }
        /* Flip Y */
        for (x = 0; x < src.width; x++) {
            for (y = 0, y2 = 2*src.height-1; y < src.height; y++, y2--) {
                destPtr[y2*dest.width+x].value = srcPtr[y*src.width+x].value;
            }
        }
        /* Flip X and Y */
        for (x = 0, x2 = 2*src.width-1; x < src.width; x++, x2--) {
            for (y = 0, y2 = 2*src.height-1; y < src.height; y++, y2--) {
                destPtr[y2*dest.width+x2].value = srcPtr[y*src.width+x].value;
            }
        }
    }
    
    if (flip == MIRROR_OUTER) {
        int split = (flags&1);
        int sx1, sx2, sy1, sy2,
            smx = (src.width/2), smy=(src.height/2),
            dmx = (dest.width/2), dmy=(dest.height/2);

        /* Initialize whole background to center pixel. */
        int sind = (src.height/2)*src.width+src.width/2;
        for (x = 0; x < dest.width; x++) {
            for (y = 0; y < dest.height; y++) {
                destPtr[y*dest.width+x].value =
                srcPtr[sind].value;
            }
        }

        /* Copy top & bottom pixels. */        
        for (y = 0, y2 = dest.height-1, sy1 = 0, sy2 = src.height-1;
            y < dmy;
            y++, y2--, sy1++, sy2--) {
            if (sy1 >= smy) { if (split) { sy1--; sy2++; } else { sy1 = sy2 = smy; }}
            for (x = 0, x2 = dest.width-1, sx1 = 0, sx2 = src.width-1;
                x < dmx; x++, x2--, sx1++, sx2--) {
                if (sx1 >= smx) { if (split) { sx1--; sx2++; } else { sx1 = sx2 = smx; }}
                /* Top - left & right */
                destPtr[y*dest.width+x].value = srcPtr[sy1*src.width+sx1].value;
                destPtr[y*dest.width+x2].value = srcPtr[sy1*src.width+sx2].value;
                /* Bottom - left & right */
                destPtr[y2*dest.width+x].value = srcPtr[sy2*src.width+sx1].value;
                destPtr[y2*dest.width+x2].value = srcPtr[sy2*src.width+sx2].value;
            }
        }
        /* Copy left & right pixels. */
        for (x = 0, x2 = dest.width-1, sx1 = 0, sx2 = src.width-1;
            x < smx && x < dmx;
            x++, x2--, sx1++, sx2--) {
            if (sx1 >= smx) { if (split) { sx1--; sx2++; } else { sx1 = sx2 = smx; }}
            for (y = 0, y2 = dest.height-1, sy1 = 0, sy2 = src.height-1;
                y < dmy; y++, y2--, sy1++, sy2--) {
                if (sy1 >= smy) { if (split) { sy1--; sy2++; } else { sy1 = sy2 = smy; }}
                /* Left - top & bottom */
                destPtr[y*dest.width+x].value = srcPtr[sy1*src.width+sx1].value;
                destPtr[y*dest.width+x2].value = srcPtr[sy1*src.width+sx2].value;
                /* Right - top & bottom */
                destPtr[y2*dest.width+x].value = srcPtr[sy2*src.width+sx1].value;
                destPtr[y2*dest.width+x2].value = srcPtr[sy2*src.width+sx2].value;
            }
        }
    }

    Blt_ColorImageToPhoto(destImage, destPhoto);
    Blt_FreeColorImage(srcImage);
    Blt_FreeColorImage(destImage);
    return TCL_OK;
}


/*ARGSUSED*/
static int
MirrorOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Not used. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_PhotoHandle srcPhoto, destPhoto;
    int flip, halo;

    flip = 3;
    halo = 0;
    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    if (argc>4) {
        if (!strcmp(argv[4],"x")) {
            flip = MIRROR_X; /* 1 */
        } else if (!strcmp(argv[4],"y")) {
            flip = MIRROR_Y; /* 2 */
        } else if (!strcmp(argv[4],"xy")) {
            flip = MIRROR_XY; /* 3 */
        } else if (!strcmp(argv[4],"tile")) {
            flip = MIRROR_TILE; /* 4 */
        } else if (!strcmp(argv[4],"outer")) {
            flip = MIRROR_OUTER; /* 5 */
        } else if (!strcmp(argv[4],"inner")) {
            flip = MIRROR_INNER; /* 6 */
        } else {
            Tcl_AppendResult(interp, "direction ", argv[4],
                " must be \"x\", \"y\", \"xy\",\"tile\", \"inner\", or  \"outer\"", (char *)NULL);
            return TCL_ERROR;
        }
    }
    if (argc>5) {
        if (flip != MIRROR_OUTER) {
            Tcl_AppendResult(interp, "halo is for outer only", 0);
            return TCL_ERROR;
        }
        if (Tcl_GetInt(interp, argv[5], &halo) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    return Blt_ImageMirror(interp, argv[2], argv[3], flip, halo);
}
    

/*
 * --------------------------------------------------------------------------
 *
 * SnapOp --
 *
 *	Snaps a picture of a window and stores it in a designated
 *	photo image.  The window must be completely visible or
 *	the snap will fail.
 *
 * Results:
 *	Returns a standard Tcl result.  interp->result contains
 *	the list of the graph coordinates. If an error occurred
 *	while parsing the window positions, TCL_ERROR is returned,
 *	then interp->result will contain an error message.
 *
 * -------------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
SnapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_Window tkwin;
    int width, height, destWidth, destHeight;
    Window window;

    tkwin = Tk_MainWindow(interp);
    window = StringToWindow(interp, argv[2]);
    if (window == None) {
	return TCL_ERROR;
    }
    if (GetWindowSize(interp, window, &width, &height) != TCL_OK) {
	Tcl_AppendResult(interp, "can't get window geometry of \"", argv[2],
		 "\"", (char *)NULL);
	return TCL_ERROR;
    }
    destWidth = width, destHeight = height;
    if ((argc > 4) && (Blt_GetPixels(interp, tkwin, argv[4], PIXELS_POSITIVE,
		&destWidth) != TCL_OK)) {
	return TCL_ERROR;
    }
    if ((argc > 5) && (Blt_GetPixels(interp, tkwin, argv[5], PIXELS_POSITIVE,
		&destHeight) != TCL_OK)) {
	return TCL_ERROR;
    }
    return Blt_SnapPhoto(interp, tkwin, window, 0, 0, width, height, destWidth,
	destHeight, argv[3], GAMMA);
}

/*ARGSUSED*/
static int
SubsampleOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Main window of the interpreter. */
    Tcl_Interp *interp;
    int argc;			/* Not used. */
    char **argv;
{
    Tk_Window tkwin;
    Tk_PhotoHandle srcPhoto, destPhoto;
    Tk_PhotoImageBlock src, dest;
    ResampleFilter *filterPtr, *vertFilterPtr, *horzFilterPtr;
    char *filterName;
    int flag;
    int x, y;
    int width, height;

    srcPhoto = Blt_FindPhoto(interp, argv[2]);
    if (srcPhoto == NULL) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    destPhoto = Blt_FindPhoto(interp, argv[3]);
    if (destPhoto == NULL) {
	Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
	    " exist or is not a photo image", (char *)NULL);
	return TCL_ERROR;
    }
    tkwin = (Tk_Window)clientData;
    flag = PIXELS_NONNEGATIVE;
    if (Blt_GetPixels(interp, tkwin, argv[4], flag, &x) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Blt_GetPixels(interp, tkwin, argv[5], flag, &y) != TCL_OK) {
	return TCL_ERROR;
    }
    flag = PIXELS_POSITIVE;
    if (Blt_GetPixels(interp, tkwin, argv[6], flag, &width) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Blt_GetPixels(interp, tkwin, argv[7], flag, &height) != TCL_OK) {
	return TCL_ERROR;
    }
    filterName = (argc > 8) ? argv[8] : "none";
    if (Blt_GetResampleFilter(interp, filterName, &filterPtr) != TCL_OK) {
	return TCL_ERROR;
    }
    vertFilterPtr = horzFilterPtr = filterPtr;
    if ((filterPtr != NULL) && (argc > 9)) {
	if (Blt_GetResampleFilter(interp, argv[9], &filterPtr) != TCL_OK) {
	    return TCL_ERROR;
	}
	vertFilterPtr = filterPtr;
    }
    Tk_PhotoGetImage(srcPhoto, &src);
    Tk_PhotoGetImage(destPhoto, &dest);
    if ((src.width <= 1) || (src.height <= 1)) {
	Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
	    (char *)NULL);
	return TCL_ERROR;
    }
    if (((x + width) > src.width) || ((y + height) > src.height)) {
	Tcl_AppendResult(interp, "nonsensical dimensions for subregion: x=",
	    argv[4], " y=", argv[5], " width=", argv[6], " height=",
	    argv[7], (char *)NULL);
	return TCL_ERROR;
    }
    if ((dest.width <= 1) || (dest.height <= 1)) {
	Tk_PhotoSetSize(destPhoto, width, height);
    }
    if (filterPtr == NULL) {
	Blt_ResizePhoto(srcPhoto, x, y, width, height, destPhoto);
    } else {
	Blt_ResamplePhoto(srcPhoto, x, y, width, height, destPhoto, 
		horzFilterPtr, vertFilterPtr);
    }
    return TCL_OK;
}

static Blt_OpSpec imageOps[] =
{
    {"alpha", 2, (Blt_Op)AlphaOp, 6, 10, "?-shift? srcPhoto destPhoto Color ?alpha? ?withalpha?",},
    {"blur", 2, (Blt_Op)BlurOp, 5, 6, "srcPhoto destPhoto ?radius?",},
    {"colors", 3, (Blt_Op)ColorsOp, 4, 0, "?-alpha? ?-count? ?-from x1 y1 x2 y2? srcPhoto",},
    {"convolve", 3, (Blt_Op)ConvolveOp, 6, 6, "srcPhoto destPhoto filter",},
    {"gradient", 1, (Blt_Op)GradientOp, 6, 0, "photo left right ?-type lines|normal|radial|rectangular|linear|sine|halfsine? ?-skew val? ?-slant val? ?-rand val? ?-mathval val? ?-mathfunc circle|sin|cos|tan|asin|acos|atan|sinh|cosh|tanh|exp|sqrt|log|log10|rand?",},
    {"merge", 2, (Blt_Op)MergeOp, 6, 7, "srcPhoto1 srcPhoto2 destPhoto ?opacity? ?opacity2?",},
    {"mirror", 3, (Blt_Op)MirrorOp, 5, 7, "srcPhoto destPhoto ?x|y|xy|tile|outer|innert? ?halo?",},
    {"quantize", 2, (Blt_Op)QuantizeOp, 4, 5, "srcPhoto destPhoto ?nColors?",},
    {"readjpeg", 3, (Blt_Op)ReadJPEGOp, 5, 5, "fileName photoName",},
    {"recolor", 3, (Blt_Op)RecolorOp, 7, 8, "srcPhoto destPhoto oldColor newColor ?alpha?",},
    {"resample", 3, (Blt_Op)ResampleOp, 5, 7, 
	"srcPhoto destPhoto ?horzFilter vertFilter?",},
    {"rotate", 2, (Blt_Op)RotateOp, 6, 6, "srcPhoto destPhoto angle",},
    {"subsample", 2, (Blt_Op)SubsampleOp, 9, 11,
	"srcPhoto destPhoto x y width height ?horzFilter? ?vertFilter?",},
    {"trans", 2, (Blt_Op)TransOp, 6, 7, "image x y ?alpha?",},
};

static int nImageOps = sizeof(imageOps) / sizeof(Blt_OpSpec);

/* ARGSUSED */
static int
ImageOp(clientData, interp, argc, argv)
    ClientData clientData;	/* Main window of interpreter. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    Blt_Op proc;
    int result;

    proc = Blt_GetOp(interp, nImageOps, imageOps, BLT_OP_ARG2, argc, argv, 0);
    if (proc == NULL) {
	return TCL_ERROR;
    }
    clientData = (ClientData)Tk_MainWindow(interp);
    result = (*proc) (clientData, interp, argc - 1, argv + 1);
    return result;
}

static Blt_OpSpec winOps[] =
{
    {"changes", 3, (Blt_Op)ChangesOp, 3, 3, "window",},
#ifndef _WIN32
    {"colormap", 3, (Blt_Op)ColormapOp, 3, 3, "window",},
#endif
    {"gradients", 1, (Blt_Op)GradientsOp, 5, 5, "color1 color2 width",},
    {"image", 1, (Blt_Op)ImageOp, 2, 0, "args",},
    {"lower", 1, (Blt_Op)LowerOp, 2, 0, "window ?window?...",},
    {"map", 2, (Blt_Op)MapOp, 2, 0, "window ?window?...",},
    {"move", 2, (Blt_Op)MoveOp, 5, 5, "window x y",},
    {"parent", 3, (Blt_Op)ParentOp, 3, 3, "window",},
    {"query", 3, (Blt_Op)QueryOp, 2, 2, "",},
    {"raise", 2, (Blt_Op)RaiseOp, 2, 0, "window ?window?...",},
#ifdef notdef
    {"reparent", 3, (Blt_Op)ReparentOp, 3, 4, "window ?parent?",},
#endif
    {"snap", 2, (Blt_Op)SnapOp, 4, 8, "window photoName ?width height?",},
    {"unmap", 1, (Blt_Op)UnmapOp, 2, 0, "window ?window?...",},
    {"warpto", 1, (Blt_Op)WarpToOp, 2, 5, "?window?",},
};

static int nWinOps = sizeof(winOps) / sizeof(Blt_OpSpec);

/* ARGSUSED */
static int
WinopCmd(clientData, interp, argc, argv)
    ClientData clientData;	/* Main window of interpreter. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    Blt_Op proc;
    int result;

    proc = Blt_GetOp(interp, nWinOps, winOps, BLT_OP_ARG1,  argc, argv, 0);
    if (proc == NULL) {
	return TCL_ERROR;
    }
    clientData = (ClientData)Tk_MainWindow(interp);
    result = (*proc) (clientData, interp, argc, argv);
    return result;
}

int
Blt_WinopInit(interp)
    Tcl_Interp *interp;
{
    static Blt_CmdSpec cmdSpec = {"winop", WinopCmd,};

    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
	return TCL_ERROR;
    }
    return TCL_OK;
}

#endif /* NO_WINOP */
