/*
 * 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

#ifdef __STDC__
static Tcl_CmdProc WinopCmd;
#endif


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(interp, window, widthPtr, heightPtr)
    Tcl_Interp *interp;
    Window window;
    int *widthPtr, *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 = (int *)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, (ClientData)&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

static int
LowerOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    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;
}

static int
RaiseOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    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;
}

static int
MapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    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;			/* Unused */
    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;
}

static int
UnmapOp(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;
    int argc;
    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
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);
}

/*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
    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;
    }
    return Blt_JPEGToPhoto(interp, argv[2], photo);
#else
    Tcl_AppendResult(interp, "JPEG support not compiled", (char *)NULL);
    return TCL_ERROR;
#endif
}


/*
 * --------------------------------------------------------------------------
 *
 * 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;
    Display *display;
    Window window;

    tkwin = Tk_MainWindow(interp);
    display = Tk_Display(tkwin);
    
    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, width, height, destWidth, 
	destHeight, argv[3]);
}

/*
 * 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;
    Colorimage srcImage, destImage;
    Image2DFilter filter;
    int nValues;
    char **valueArr;
    float *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 = (float *)malloc(sizeof(float) * nValues);
    sum = 0.0;
    for (i = 0; i < nValues; i++) {
	if (Tcl_GetDouble(interp, valueArr[i], &value) != TCL_OK) {
	    goto error;
	}
	kernel[i] = (float)value;
	sum += value;
    }
    filter.kernel = kernel;
    filter.support = dim / (float)2.0;
    filter.sum = (sum == 0) ? (float)1.0 : (float)sum;
    filter.scale = (float)1.0 / nValues;

    srcImage = Blt_PhotoToColorimage(srcPhoto, NULL);
    destImage = Blt_ConvolveColorimage(srcImage, &filter);
    Blt_FreeColorimage(srcImage);
    Blt_ColorimageToPhoto(destImage, destPhoto);
    Blt_FreeColorimage(destImage);
    result = TCL_OK;
  error:
    if (valueArr != NULL) {
	free((char *)valueArr);
    }
    if (kernel != NULL) {
	free((char *)kernel);
    }
    return result;
}

/*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;
    Image1DFilter filter, vertFilter, horzFilter;
    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_FindImage1DFilter(interp, filterName, &filter) != TCL_OK) {
	return TCL_ERROR;
    }
    vertFilter = horzFilter = filter;
    if ((filter != NULL) && (argc > 5)) {
	if (Blt_FindImage1DFilter(interp, argv[5], &filter) != TCL_OK) {
	    return TCL_ERROR;
	}
	vertFilter = filter;
    }
    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 (filter == NULL) {
	Blt_ResizePhoto(srcPhoto, destPhoto, (Region2D *)NULL);
    } else {
	Blt_ResamplePhoto(srcPhoto, destPhoto, (Region2D *)NULL, 
		horzFilter, vertFilter);
    }
    return TCL_OK;
}

/*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;
    Region2D region;
    Image1DFilter filter, vFilter, hFilter;
    char *filterName;
    int flag;
    int x, y;

    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;
    }
    region.left = x;
    if (Blt_GetPixels(interp, tkwin, argv[5], flag, &y) != TCL_OK) {
	return TCL_ERROR;
    }
    region.top = y;
    flag = PIXELS_POSITIVE;
    if (Blt_GetPixels(interp, tkwin, argv[6], flag, &x) != TCL_OK) {
	return TCL_ERROR;
    }
    region.right = region.left + x - 1;
    if (Blt_GetPixels(interp, tkwin, argv[7], flag, &y) != TCL_OK) {
	return TCL_ERROR;
    }
    region.bottom = region.top + y - 1;

    filterName = (argc > 8) ? argv[8] : "box";
    if (Blt_FindImage1DFilter(interp, filterName, &filter) != TCL_OK) {
	return TCL_ERROR;
    }
    vFilter = hFilter = filter;
    if ((filter != NULL) && (argc > 9)) {
	if (Blt_FindImage1DFilter(interp, argv[9], &filter) != TCL_OK) {
	    return TCL_ERROR;
	}
	vFilter = filter;
    }
    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 ((region.right >= src.width) || (region.bottom >= 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, REGION_WIDTH(&region), 
		REGION_HEIGHT(&region));
    }
    if (filter == NULL) {
	Blt_ResizePhoto(srcPhoto, destPhoto, &region);
    } else {
	Blt_ResamplePhoto(srcPhoto, destPhoto, &region, hFilter, vFilter);
    }
    return TCL_OK;
}

#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

static Blt_OpSpec winOps[] =
{
    {"changes", 3, (Blt_OpProc)ChangesOp, 3, 3, "window",},
#ifndef WIN32
    {"colormap", 3, (Blt_OpProc)ColormapOp, 3, 3, "window",},
#endif
    {"convolve", 3, (Blt_OpProc)ConvolveOp, 5, 5,
	"srcPhoto destPhoto filter",},
    {"lower", 1, (Blt_OpProc)LowerOp, 2, 0, "window ?window?...",},
    {"map", 2, (Blt_OpProc)MapOp, 2, 0, "window ?window?...",},
    {"move", 2, (Blt_OpProc)MoveOp, 5, 5, "window x y",},
    {"query", 1, (Blt_OpProc)QueryOp, 2, 2, "",},
    {"raise", 2, (Blt_OpProc)RaiseOp, 2, 0, "window ?window?...",},
    {"readjpeg", 3, (Blt_OpProc)ReadJPEGOp, 4, 4, "fileName photoName",},
#ifdef notdef
    {"reparent", 3, (Blt_OpProc)ReparentOp, 3, 4, "window ?parent?",},
#endif
    {"resample", 3, (Blt_OpProc)ResampleOp, 4, 6,
	"srcPhoto destPhoto ?horzFilter vertFilter?",},
    {"snap", 2, (Blt_OpProc)SnapOp, 4, 6,
	"window photoName ?width height?",},
    {"subsample", 2, (Blt_OpProc)SubsampleOp, 8, 10,
	"srcPhoto destPhoto x y width height ?horzFilter? ?vertFilter?",},
    {"unmap", 1, (Blt_OpProc)UnmapOp, 2, 0, "window ?window?...",},
    {"warpto", 1, (Blt_OpProc)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_OpProc opProc;
    int result;

    opProc = Blt_GetOperation(interp, nWinOps, winOps, BLT_OPER_ARG1,
	argc, argv, 0);
    if (opProc == NULL) {
	return TCL_ERROR;
    }
    clientData = (ClientData)Tk_MainWindow(interp);
    result = (*opProc) (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 */
