/* Tk-specific functions for image families in Xconq.
   Copyright (C) 1998, 1999 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

#include "config.h"
#include "misc.h"
#include "lisp.h"
#include "module.h"
#include "system.h"
#include "imf.h"
extern void make_image_from_file_image(ImageFamily *imf, Image *img,
				       Image *subimg, int subi);

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <tcl.h>
#include <tk.h>

#include "tkimf.h"

/* Lifted from tk sources - XPutImage is not available to Windows port. */
#ifdef UNIX
#define TkPutImage(colors, ncolors, display, pixels, gc, image, destx, desty, srcx, srcy, width, height) \
	XPutImage(display, pixels, gc, image, destx, desty, srcx, \
	srcy, width, height);
#else
extern void TkPutImage(unsigned long *colors, int ncolors,
		       Display* display, Drawable d, GC gc, XImage* image,
		       int src_x, int src_y, int dest_x, int dest_y,
		       unsigned int width, unsigned int height);
#endif

extern Tcl_Interp *interp;

int tmp_valid;

Tk_Window tmp_root_window;

static Pixmap tk_load_bitmap(Tk_Window rootwin, char *name, char *ext,
			     int *w, int *h);
static void tk_interp_image(ImageFamily *imf, Image *img, int force);
static void tk_interp_image_1(ImageFamily *imf, Image *img, Image *subimg,
			      int subi, int force);
static void tk_make_color_pixmap(Tk_Window rootwin, ImageFamily *imf,
				 Image *img);
static void tk_make_mono_pixmap(Tk_Window rootwin, ImageFamily *imf,
				Image *img);
static void tk_make_mask_pixmap(Tk_Window rootwin, ImageFamily *imf,
				Image *img);

static void blacken_masked_area(ImageFamily *imf, Image *img);
static void blacken_mono_masked_area(ImageFamily *imf, Image *img);

static void make_raw_palette(Image *img);

/* Stash for cloned image families. */

static ImageFamily *tkimages[1000];

int numtkimages;

TkImage *
init_tk_image(Image *img)
{
    TkImage *tkimg;

    tkimg = (TkImage *) xmalloc(sizeof(TkImage));
    /* Point to the generic image. */
    tkimg->generic = img;
    tkimg->mono = None;
    tkimg->mask = None;
    tkimg->colr = None;
    tkimg->solid = NULL;
    return tkimg;
}

TkImage *
get_tk_image(Image *img)
{
    TkImage *tkimg;

    if (img->hook)
      return (TkImage *) img->hook;
    tkimg = init_tk_image(img);
    img->hook = (char *) tkimg;
    return tkimg;
}

/* This tries to fill in the given image family by looking for and loading
   standard X11 bitmap files. */

ImageFamily *
tk_load_imf(ImageFamily *imf)
{
    int w, h;
    Pixmap pic;
    Image *img;
    Tk_Window rootwin = None;
    TkImageFamily *tkimf;
    TkImage *tkimg;

    /* If no imf or no name, don't even try. */
    if (imf == NULL || imf->name == NULL)
      return NULL;
    if (strcmp(imf->name, "none") == 0)
      return imf;
    if (tmp_valid) {
	if (imf->hook == NULL) {
	    imf->hook = xmalloc(sizeof(TkImageFamily));
	    tkimf = (TkImageFamily *) imf->hook;
	    tkimf->rootwin = tmp_root_window;
	    /* Record the cloned family for later lookup. */
	    tkimages[numtkimages++] = imf;
	}
    }
    tkimf = (TkImageFamily *) imf->hook;
    if (tkimf) {
	rootwin = tkimf->rootwin;
    }
    /* Grab at plausibly-named bitmaps. */
    pic = tk_load_bitmap(rootwin, imf->name, "b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "8.b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "8.m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "32.b", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mono = pic;
    }
    pic = tk_load_bitmap(rootwin, imf->name, "32.m", &w, &h);
    if (pic != None) {
	img = get_img(imf, w, h);
	tkimg = get_tk_image(img);
	tkimg->mask = pic;
    }
    return imf;
}

/* Try to load a bitmap of the given name, looking in both the current dir
   and the library dir. */

static Pixmap
tk_load_bitmap(rootwin, name, ext, wp, hp)
Tk_Window rootwin;
char *name, *ext;
int *wp, *hp;
{
#if 0
    int hotx, hoty;
    unsigned int w, h;
    Pixmap rslt;
    static char sbuf[1000];

    if (ext != NULL) {
	make_pathname(NULL, name, ext, sbuf);
	if (XReadBitmapFile(dpy, rootwin, sbuf,
			    &w, &h, &rslt, &hotx, &hoty) == BitmapSuccess) {
	    DGprintf("Loaded bitmap \"%s\"\n", sbuf);
	    *wp = w;  *hp = h;
	    return rslt;
	}
    }
#endif
    return None;
}

/* if (force): prefer data over rawdata; always re-create pixmaps */

ImageFamily *
tk_interp_imf(ImageFamily *imf, Image *img, int force)
{
    TkImageFamily *tkimf;

    if (tmp_valid) {
	if (imf->hook == NULL) {
	    imf->hook = xmalloc(sizeof(TkImageFamily));
	    tkimf = (TkImageFamily *) imf->hook;
	    tkimf->rootwin = tmp_root_window;
	    /* Record the cloned family for later lookup. */
	    tkimages[numtkimages++] = imf;
	}
    }
    if (img == NULL) {
	for_all_images(imf, img) {
	    tk_interp_image(imf, img, force);
	}
    } else {
	tk_interp_image(imf, img, force);
    }
    return imf;
}

/* Interpret an image and any possible subimages. */

static void
tk_interp_image(ImageFamily *imf, Image *img, int force)
{
    int subi;

    /* Iterate through any subimages. */
    if (img->isborder && img->subimages != NULL) {
	for (subi = 0; subi < 16; ++subi) {
	    tk_interp_image_1(imf, img, img->subimages[subi], subi, force);
	}
    } else if (img->isconnection && img->subimages != NULL) {
	for (subi = 0; subi < 64; ++subi) {
	    tk_interp_image_1(imf, img, img->subimages[subi], subi, force);
	}
    } else if (img->istransition && img->subimages != NULL) {
	for (subi = 0; subi < 4 * 4; ++subi) {
	    tk_interp_image_1(imf, img, img->subimages[subi], subi, force);
	}
    } else if (img->numsubimages > 0 && img->subimages != NULL) {
	for (subi = 0; subi < img->numsubimages; ++subi) {
	    tk_interp_image_1(imf, img, img->subimages[subi], subi, force);
	}
    } else {
	tk_interp_image_1(imf, img, img, 0, force);
    }
}

/* Interpret a single image/subimage. */

static void
tk_interp_image_1(ImageFamily *imf, Image *img, Image *subimg, int subi,
		  int force)
{
    char namebuf[BUFSIZE];
    int w, h, numbytes;
    int dummy, red, grn, blu;
    TkImageFamily *tkimf;
    TkImage *tkimg;
    Tk_Window rootwin;
    Tk_Uid bitmapid;
    XColor col;

    w = img->w;  h = img->h;
    tkimg = get_tk_image(subimg);
    /* Collect the root window, failing if none exists. */
    tkimf = (TkImageFamily *) imf->hook;
    if (tkimf == NULL)
      return;
    rootwin = tkimf->rootwin;

    /* A 1x1 image is just a color - make it into a solid color. */
    if (w == 1 && h == 1 && img->palette != lispnil) {
	/* force this? */
	img->numcolors = 1;
	parse_lisp_palette_entry(car(img->palette), &dummy, &red, &grn, &blu);
	col.red = red;  col.green = grn;  col.blue = blu;
	tkimg->solid = Tk_GetColorByValue(rootwin, &col);
	if (tkimg->solid == NULL)
	  init_warning("Cannot get color #%2.2x%2.2x%2.2x for %s",
		       col.red, col.green, col.blue, imf->name);
	return;
    }
    if (subimg->colrdata != lispnil
	&& (subimg->rawcolrdata == NULL || force)) {
	numbytes = h * computed_rowbytes(w, subimg->pixelsize);
	subimg->rawcolrdata = xmalloc(numbytes);
	interp_bytes(subimg->colrdata, numbytes, subimg->rawcolrdata, 0);
    }
    if (subimg->rawcolrdata == NULL && img->filedata != lispnil) {
	make_image_from_file_image(imf, img, subimg, subi);
    }
    make_raw_mono_data(subimg, force);
    if (subimg->maskdata != lispnil
	&& (subimg->rawmaskdata == NULL || force)) {
	numbytes = h * computed_rowbytes(w, 1);
	subimg->rawmaskdata = xmalloc(numbytes);
	interp_bytes(subimg->maskdata, numbytes, subimg->rawmaskdata, 0);
    }
    /* At this point our data is known to be in "raw" form, ready to
       turn into Tk-specific objects. */
    if (use_clip_mask) {
	/* If clip masks work (real X11), then we can create bitmaps for
	   the mono and mask data. */
	if (subimg->rawmonodata != NULL && (tkimg->mono == None || force)) {
	    numbytes = h * computed_rowbytes(w, 1);
	    reverse_bit_endianness(subimg->rawmonodata, numbytes);
	    sprintf(namebuf, "%s.%d.%d.%d.mono", imf->name, w, h, subi);
	    bitmapid = Tk_GetUid(namebuf);
	    Tk_DefineBitmap(interp, bitmapid, subimg->rawmonodata, w, h);
	    tkimg->mono = Tk_GetBitmap(interp, rootwin, bitmapid);
	    reverse_bit_endianness(subimg->rawmonodata, numbytes);
	}
	if (subimg->rawmaskdata != NULL	&& (tkimg->mask == None || force)) {
	    numbytes = h * computed_rowbytes(w, 1);
	    reverse_bit_endianness(subimg->rawmaskdata, numbytes);
	    sprintf(namebuf, "%s.%d.%d.%d.mask", imf->name, w, h, subi);
	    bitmapid = Tk_GetUid(namebuf);
	    Tk_DefineBitmap(interp, bitmapid, subimg->rawmaskdata, w, h);
	    tkimg->mask = Tk_GetBitmap(interp, rootwin, bitmapid);
	    reverse_bit_endianness(subimg->rawmaskdata, numbytes);
	}
    } else {
	/* Logical operations depend on the to-be-masked areas of images
	   being all black. */
	if (subimg->rawmaskdata != NULL) {
	    if (subimg->rawcolrdata != NULL)
	      blacken_masked_area(imf, subimg);
	    if (subimg->rawmonodata != NULL)
	      blacken_mono_masked_area(imf, subimg);
	}
	/* Make mono and mask data into b/w pixmaps of full depth, to
	   use with AND/OR operations. */
	if (subimg->rawmonodata	&& (tkimg->mono == None || force)) {
	    tk_make_mono_pixmap(rootwin, imf, subimg);
	}
	if (subimg->rawmaskdata	&& (tkimg->mask == None || force)) {
	    tk_make_mask_pixmap(rootwin, imf, subimg);
	}
    }
    /* Whether using clip masks or logical ops, color pixmap creation
       works the same. */
    if (subimg->rawcolrdata && (tkimg->colr == None || force)) {
	tk_make_color_pixmap(rootwin, imf, subimg);
    }
}

/* TK bitmaps are always in little-endian bit order, while IMF images
   are always big-endian in bit order, so we must reverse the bits
   in each byte individually. */

void
reverse_bit_endianness(char *rawdata, int numbytes)
{
    int i, j, byte, byte2;

    for (i = 0; i < numbytes; ++i) {
	byte = rawdata[i];
	byte2 = 0;
	for (j = 0; j < 8; ++j) {
	    byte2 = (byte2 << 1) | (byte & 1);
	    byte >>= 1;
	}
	rawdata[i] = byte2;
    }
}

static int try_images = 1;

static int use_images;

#ifdef UNIX
static XImage *test_image;
#endif
static int imdepth;

static char *ximbuf;
static XImage *xim;

static void
tk_make_color_pixmap(Tk_Window rootwin, ImageFamily *imf, Image *img)
{
    int r, ri, rc, depth, c, rmask, pix, use_images_here;
    XColor col, *color;
    char *rp;
    GC gc;
    TkImage *tkimg = (TkImage *) img->hook;
    Display *dpy = Tk_Display(rootwin);

    if (tkimg == NULL || img->rawcolrdata == NULL)
      return;
    /* Can't make color pixmaps if we don't have any colors. */
    if (img->palette == lispnil && img->rawpalette == NULL)
      return;
    DGprintf("Starting %dx%d colr pixmap for %s\n", img->w, img->h, imf->name);
    if (img->rawpalette == NULL)
      make_raw_palette(img);
    Tk_MakeWindowExist(rootwin);
    depth = DefaultDepthOfScreen(Tk_Screen(rootwin));
    if (img->numcolors <= 0) {
	run_warning("No colors?");
	return;
    }
    /* Allocate colors. */
    tkimg->cmap = (XColor **) xmalloc(256 /*img->numcolors*/ * sizeof(XColor *));
    for (c = 0; c < img->numcolors; c++) {
	col.red   = img->rawpalette[4 * c + 1];
	col.green = img->rawpalette[4 * c + 2];
	col.blue  = img->rawpalette[4 * c + 3];
	color = Tk_GetColorByValue(rootwin, &col);
	if (color == NULL)
	  init_warning("Cannot get color #%2.2x%2.2x%2.2x for %s",
		       col.red, col.green, col.blue, imf->name);
	tkimg->cmap[img->rawpalette[4 * c + 0]] = color;
    }
    tkimg->colr = Tk_GetPixmap(dpy, Tk_WindowId(rootwin), img->w, img->h,
			       depth);
    if (tkimg->colr == None) {
	init_warning("mono pixmap creation failed");
	return;
    }
    /* Draw the image by plotting each point separately. */
    rmask = (1 << img->pixelsize) - 1;
    rp = img->rawcolrdata;
    gc = Tk_GetGC(rootwin, None, NULL);
    XSetClipMask(dpy, gc, None);
    XSetFillStyle(dpy, gc, FillSolid);
    if (try_images) {
	/* (no XGetImage for color images on Windows) */
#ifdef UNIX
	if (test_image == NULL) {
	    test_image = XGetImage(dpy, tkimg->colr, 0, 0, img->w, img->h,
				   AllPlanes, ZPixmap);
	    /* Only use images with 16-bit color for now. */
	    if (test_image->depth == 16) {
		use_images = TRUE;
		imdepth = 16;
	    }
	}
#else
	/* On Windows, we always seem to have 32-bit images. */
	use_images = TRUE;
	imdepth = 32;
#endif
    }
    use_images_here = use_images;
    if ((img->w & 1) == 1 || (img->h & 1) == 1)
      use_images_here = FALSE;
    if (use_images_here) {
#ifdef UNIX
	ximbuf = (char *) xmalloc(img->w * img->h * (imdepth / 8));
#else
	/* (should do this for both Unix and Windows...) */
	Visual *vis;
	vis = Tk_Visual(rootwin);
	ximbuf = (char *) xmalloc(img->w * img->h * (imdepth / 8));
	xim = XCreateImage(dpy, vis, imdepth, ZPixmap, 0, ximbuf,
			   img->w, img->h, 32, 0);
#endif
    }
    for (r = 0; r < img->h; r++) {
	ri = 8 - img->pixelsize;
	for (c = 0; c < img->w; c++) {
	    /* imf decoding stuff */
	    rc = ((int) (*rp >> ri)) & rmask;
	    if (ri) {
		ri -= img->pixelsize;
	    } else {
		ri = 8 - img->pixelsize;
		rp++;
	    }
	    pix = 0;
	    if (tkimg->cmap[rc] && rc < 256)
	      pix = tkimg->cmap[rc]->pixel;
	    if (use_images_here) {
		if (imdepth == 16) {
#if 1
		    *(((short *) ximbuf) + (img->w * r + c)) = pix;
#endif
#if 0
		    ximbuf[(img->w * r + c) * 2 + 1] = pix & 0xff;
		    ximbuf[(img->w * r + c) * 2 + 0] = pix >> 8;
#endif
		} else {
		    XPutPixel(xim, c, r, pix);
		}
	    } else {
		XSetForeground(dpy, gc, pix);
		XFillRectangle(dpy, tkimg->colr, gc, c, r, 1, 1);
	    }
	}
	if ((img->pixelsize * img->w) % 8) {
	    rp++;
	}
    }
    if (use_images_here) {
#if 0 /* this is probably the right approach to use someday */
	int numvisuals;
	XVisualInfo visinfo, *visinfoptr;

	visinfo.screen = Tk_ScreenNumber(rootwin);
	visinfo.visualid = XVisualIDFromVisual(Tk_Visual(rootwin));
	visinfoptr = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask,
				    &visinfo, &numvisuals);
#endif
#ifdef UNIX
	Visual *vis;
	vis = Tk_Visual(rootwin);
	xim = XCreateImage(dpy, vis, imdepth, ZPixmap, 0, ximbuf,
			   img->w, img->h, 32, 0);
#endif
	/* Move the image's bits into the pixmap. */
	TkPutImage(NULL, 0, dpy, tkimg->colr, gc, xim, 0, 0, 0, 0,
		   img->w, img->h);
    }
    Tk_FreeGC(dpy, gc);
    DGprintf("   Finished\n");
}

static void
tk_make_mono_pixmap(Tk_Window rootwin, ImageFamily *imf, Image *img)
{
    int r, ri, rc, depth, c, rmask, pix, use_images_here;
    XColor col;
    char *rp;
    GC gc;
    TkImage *tkimg = (TkImage *) img->hook;
    Display *dpy = Tk_Display(rootwin);
    XColor *whitecolor, *blackcolor;

    if (tkimg == NULL || img->rawmonodata == NULL)
      return;
    DGprintf("Starting %dx%d mono pixmap for %s\n", img->w, img->h, imf->name);
    Tk_MakeWindowExist(rootwin);
    depth = DefaultDepthOfScreen(Tk_Screen(rootwin));
    /* Note that depth is expected to be > 1 - we're going to paint black
       and white into a color image. */
    col.red = col.green = col.blue = 65535;
    whitecolor = Tk_GetColorByValue(rootwin, &col);
    col.red = col.green = col.blue = 0;
    blackcolor = Tk_GetColorByValue(rootwin, &col);
    tkimg->mono = Tk_GetPixmap(dpy, Tk_WindowId(rootwin), img->w, img->h,
			       depth);
    if (tkimg->mono == None) {
	init_warning("mono pixmap creation failed");
	return;
    }
    rmask = 1;
    rp = img->rawmonodata;
    gc = Tk_GetGC(rootwin, None, NULL);
    XSetClipMask(dpy, gc, None);
    XSetFillStyle(dpy, gc, FillSolid);
    if (try_images) {
#ifdef UNIX
	if (test_image == NULL) {
	    test_image = XGetImage(dpy, tkimg->mono, 0, 0, img->w, img->h,
				   AllPlanes, ZPixmap);
	    /* Only use images with 16-bit color for now. */
	    if (test_image->depth == 16) {
		use_images = TRUE;
	    }
	}
#else
	/* (should create b/w image) */
	use_images = TRUE;
#endif
    }
    use_images_here = use_images;
    if ((img->w & 1) == 1 || (img->h & 1) == 1)
      use_images_here = FALSE;
    if (use_images_here) {
	ximbuf = (char *) xmalloc(img->w * img->h * 2 * sizeof(char));
    }
    for (r = 0; r < img->h; r++) {
	ri = 7;
	for (c = 0; c < img->w; c++) {
	    /* imf decoding stuff */
	    rc = ((int) (*rp >> ri)) & rmask;
	    if (ri) {
		ri -= 1;
	    } else {
		ri = 7;
		rp++;
	    }
	    pix = (rc ? blackcolor : whitecolor)->pixel;
	    if (use_images_here) {
		ximbuf[(img->w * r + c) * 2 + 0] = pix & 0xff;
		ximbuf[(img->w * r + c) * 2 + 1] = pix >> 8;
	    } else {
		XSetForeground(dpy, gc, pix);
		XFillRectangle(dpy, tkimg->mono, gc, c, r, 1, 1);
	    }
	}
	if (img->w % 8) {
	    rp++;
	}
    }
    if (use_images_here) {
	Visual *vis;

	vis = Tk_Visual(rootwin);
	xim = XCreateImage(dpy, vis, 16, ZPixmap, 0, ximbuf, img->w, img->h,
			   32, 0);

	/* Move the image's bits into the pixmap. */
	TkPutImage(NULL, 0, dpy, tkimg->mono, gc, xim, 0, 0, 0, 0,
		   img->w, img->h);
    }
    Tk_FreeGC(dpy, gc);
    DGprintf("    Finished\n");
}

static void
tk_make_mask_pixmap(Tk_Window rootwin, ImageFamily *imf, Image *img)
{
    int r, ri, rc, depth, c, rmask, pix, use_images_here;
    XColor col;
    char *rp;
    GC gc;
    TkImage *tkimg = (TkImage *) img->hook;
    Display *dpy = Tk_Display(rootwin);
    XColor *whitecolor, *blackcolor;

    if (tkimg == NULL || img->rawmaskdata == NULL)
      return;
    DGprintf("Starting %dx%d mask pixmap for %s\n", img->w, img->h, imf->name);
    Tk_MakeWindowExist(rootwin);
    depth = DefaultDepthOfScreen(Tk_Screen(rootwin));
    col.red = col.green = col.blue = 65535;
    whitecolor = Tk_GetColorByValue(rootwin, &col);
    col.red = col.green = col.blue = 0;
    blackcolor = Tk_GetColorByValue(rootwin, &col);
    tkimg->mask = Tk_GetPixmap(dpy, Tk_WindowId(rootwin), img->w, img->h,
			       depth);
    if (tkimg->mask == None) {
	init_warning("mask pixmap creation failed");
	return;
    }
    rmask = 1;
    rp = img->rawmaskdata;
    gc = Tk_GetGC(rootwin, None, NULL);
    XSetClipMask(dpy, gc, None);
    XSetFillStyle(dpy, gc, FillSolid);
    if (try_images) {
#ifdef UNIX
	if (test_image == NULL) {
	    test_image = XGetImage(dpy, tkimg->mask, 0, 0, img->w, img->h,
				   AllPlanes, ZPixmap);
	    /* Only use images with 16-bit color for now. */
	    if (test_image->depth == 16) {
		use_images = TRUE;
	    }
	}
#else
	use_images = TRUE;
#endif
    }
    use_images_here = use_images;
    if ((img->w & 1) == 1 || (img->h & 1) == 1)
      use_images_here = FALSE;
    if (use_images_here) {
	ximbuf = (char *) xmalloc(img->w * img->h * 2 * sizeof(char));
    }
    for (r = 0; r < img->h; r++) {
	ri = 7;
	for (c = 0; c < img->w; c++) {
	    /* imf decoding stuff */
	    rc = ((int) (*rp >> ri)) & rmask;
	    if (ri) {
		ri -= 1;
	    } else {
		ri = 7;
		rp++;
	    }
	    pix = (rc ? blackcolor : whitecolor)->pixel;
	    if (use_images_here) {
		ximbuf[(img->w * r + c) * 2 + 0] = pix & 0xff;
		ximbuf[(img->w * r + c) * 2 + 1] = pix >> 8;
	    } else {
		XSetForeground(dpy, gc, pix);
		XFillRectangle(dpy, tkimg->mask, gc, c, r, 1, 1);
	    }
	}
	if (img->w % 8) {
	    rp++;
	}
    }
    if (use_images_here) {
	Visual *vis;

	vis = Tk_Visual(rootwin);
	xim = XCreateImage(dpy, vis, 16, ZPixmap, 0, ximbuf, img->w, img->h,
			   32, 0);

	/* Move the image's bits into the pixmap. */
	TkPutImage(NULL, 0, dpy, tkimg->mask, gc, xim, 0, 0, 0, 0,
		   img->w, img->h);
    }
    Tk_FreeGC(dpy, gc);
    DGprintf("    Finished\n");
}

/* (should make generic? any point?) */
static void
blacken_masked_area(ImageFamily *imf, Image *img)
{
    int r, ri, rc, c, rmask;
    int rmi, rmc, rmmask;
    char *rp, *rmp;
    int black = -1;

    if (img->rawpalette == NULL)
      make_raw_palette(img);
    for (c = 0; c < img->numcolors; c++) {
	if (img->rawpalette[4 * c + 1] == 0
	    && img->rawpalette[4 * c + 2] == 0
	    && img->rawpalette[4 * c + 3] == 0) {
	    black = c;
	    break;
	}
    }
    if (black < 0
	&& (1 << img->pixelsize) == img->numcolors
	&& img->pixelsize < 8) {
	char *newdata, *nrp;
	int newpsize, nrmask, nri;

	newpsize = img->pixelsize * 2;
	/* Don't need to mess with palette because it already has an
	   additional spot allocated, and we only need the one. */
	rmask = (1 << img->pixelsize) - 1;
	ri = 8 - img->pixelsize;
	rp = img->rawcolrdata;
	newdata = xmalloc(img->h * computed_rowbytes(img->w, newpsize));
	nrmask = (1 << newpsize) - 1;
	nri = 8 - newpsize;
	nrp = newdata;
	for (r = 0; r < img->h; r++) {
	  for (c = 0; c < img->w; c++) {
	    rc = ((int) (*rp >> ri)) & rmask;
	    /* OR the color data into its new location. */
	    *nrp |= (char) (rc << nri);
	    if (ri) {
		ri -= img->pixelsize;
	    } else {
		ri = 8 - img->pixelsize;
		++rp;
	    }
	    if (nri) {
		nri -= newpsize;
	    } else {
		nri = 8 - newpsize;
		++nrp;
	    }
	  }
	  if ((img->pixelsize * img->w) % 8) {
	    ri = 8 - img->pixelsize;
	    ++rp;
	  }
	  if ((newpsize * img->w) % 8) {
	    nri = 8 - newpsize;
	    ++nrp;
	  }
	}
	img->orig_pixelsize = img->pixelsize;
	img->pixelsize = newpsize;
	img->rawcolrdata = newdata;
    }
    /* If a spare color is available, put black there. */
    if (black < 0 && (1 << img->pixelsize) > img->numcolors) {
	black = img->numcolors;
	img->rawpalette[4 * black + 0] = black;
	img->rawpalette[4 * black + 1] = 0;
	img->rawpalette[4 * black + 2] = 0;
	img->rawpalette[4 * black + 3] = 0;
	++(img->numcolors);
    }
    rmask = (1 << img->pixelsize) - 1;
    ri = 8 - img->pixelsize;
    rp = img->rawcolrdata;
    /* OK, now we're getting desperate; use the color in the upper left
       corner and pretend it's a transparent color. */
    if (black < 0) {
	black = ((int) *rp >> ri) & rmask;
	img->rawpalette[4 * black + 1] = 0;
	img->rawpalette[4 * black + 2] = 0;
	img->rawpalette[4 * black + 3] = 0;
    }
    rmmask = 1;
    rmi = 7;
    rmp = img->rawmaskdata;
    for (r = 0; r < img->h; r++) {
	for (c = 0; c < img->w; c++) {
	    rc = ((int) (*rp >> ri)) & rmask;
	    rmc = ((int) (*rmp >> rmi)) & rmmask;
	    if (rmc == 0) {
		/* Mask off the old value. */
		*rp &= (char) (~ (rmask << ri));
		/* Insert the color for black. */
		*rp |= (char) (black << ri);
	    }
	    if (ri) {
		ri -= img->pixelsize;
	    } else {
		ri = 8 - img->pixelsize;
		++rp;
	    }
	    if (rmi) {
		rmi -= 1;
	    } else {
		rmi = 7;
		++rmp;
	    }
	}
	if ((img->pixelsize * img->w) % 8) {
	    ri = 8 - img->pixelsize;
	    ++rp;
	}
	if (img->w % 8) {
	    rmi = 7;
	    ++rmp;
	}
    }
}

static void
blacken_mono_masked_area(ImageFamily *imf, Image *img)
{
    int r, ri, rc, c, rmask;
    int rmi, rmc, rmmask;
    char *rp, *rmp;

    rmask = 1;
    ri = 7;
    rp = img->rawmonodata;
    rmmask = 1;
    rmi = 7;
    rmp = img->rawmaskdata;
    for (r = 0; r < img->h; r++) {
	for (c = 0; c < img->w; c++) {
	    rc = ((int) (*rp >> ri)) & rmask;
	    rmc = ((int) (*rmp >> rmi)) & rmmask;
	    if (rmc == 0) {
		/* Mask off the old value. */
		*rp &= (char) (~ (rmask << ri));
		/* Insert the color for black. */
		*rp |= (char) (1 << ri);
	    }
	    if (ri) {
		ri -= 1;
	    } else {
		ri = 7;
		++rp;
	    }
	    if (rmi) {
		rmi -= 1;
	    } else {
		rmi = 7;
		++rmp;
	    }
	}
	if (img->w % 8) {
	    ri = 7;
	    ++rp;
	}
	if (img->w % 8) {
	    rmi = 7;
	    ++rmp;
	}
    }
}

static void
make_raw_palette(Image *img)
{
    int ipal[4][256];
    int c, ln;
    Obj *pal;

    /* Parse the Lispified palette. */
    /* (should allocate and store directly instead of using ipal) */
    c = 0;
    for_all_list(img->palette, pal) {
	parse_lisp_palette_entry(car(pal), &ipal[0][c],
				 &ipal[1][c], &ipal[2][c], &ipal[3][c]);
	c++;
    }
    img->numcolors = c;
    if (c == 0)
      return;
    /* store palette */
    img->rawpalette = (int *) xmalloc((img->numcolors + 1) * 4 * sizeof(int));
    for (c = 0; c < img->numcolors; c++) {
	for (ln = 0; ln < 4; ln++) {
	    img->rawpalette[4 * c + ln] = ipal[ln][c];
	}
    }
}

void
make_generic_image_data(ImageFamily *imf)
{
    /* (should write impl?) */
}

ImageFamily *
tk_find_imf(char *name)
{
    int i;
    ImageFamily *imf;
    Tk_Window tkwin;

    for (i = 0; i < numtkimages; ++i)
      if (strcmp(tkimages[i]->name, name) == 0)
	return tkimages[i];
    tkwin = Tk_MainWindow(interp);
    tmp_root_window = tkwin;
    tmp_valid = TRUE;
    imf = get_generic_images(name);
    tmp_valid = FALSE;
    return imf;
}
