/* Image families for the Mac interface to Xconq.
   Copyright (C) 1992-1998 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.  */

/* Note!  This file does not use the standard "conq.h" header, so can't assume
   all the usual definitions. */
 
#include "config.h"
#include "misc.h"
#include "lisp.h"
#include "imf.h"

#ifdef THINK_C
#include <MacHeaders>
#else /* assume MPW */
#ifdef NEW_HEADERS
#include <MacTypes.h>
#else
#include <Types.h>
#endif
#ifdef NEW_HEADERS
#include <MacMemory.h>
#else
#include <Memory.h>
#endif
#include <Resources.h>
#include <Quickdraw.h>
#include <Icons.h>
#endif /* THINK_C */

#include "macimf.h"

#define mac_computed_rowbytes(w, pixelsize) (((w * pixelsize + 15) / 16) * 2)

extern int hasColorQD;

extern PixPatHandle mac_create_ppat(Image *img);
extern CIconHandle mac_create_cicn(Image *img);
extern void mac_init_cicn(Image *img);
extern void mac_add_cicn(ImageFamily *imf, Handle handle);
extern void mac_add_ppat(ImageFamily *imf, Handle handle);

static MacImage *init_mac_image(Image *img);
static void mac_interp_image(ImageFamily *imf, Image *img, int force);
static void mac_interp_bytes(Obj *datalist, int numbytes, Handle desthandle, int jump);
static void mac_copy_bytes(char *data, int numbytes, Handle desthandle, int jump, int rowbytes);
static CTabHandle interp_ctab(Obj *palette);
static CTabHandle synth_ctab(void);
static void convert_ppat(Image *img, PixPatHandle colrpat);
static void convert_cicn(Image *img, CIconHandle colricon, int *hasmono, int *hasmask);
static Obj *convert_ctab(CTabHandle ctabhandle);

static MacImage *
init_mac_image(Image *img)
{
	int j;
	MacImage *macimg;

	macimg = (MacImage *) xmalloc(sizeof(MacImage));
	for (j = 0; j < 8; ++j)
	  SET_IMG_PAT (macimg, j, '\F');
	macimg->generic = img;
	return macimg;
}

MacImage *
get_mac_image(Image *img)
{
	MacImage *macimg;

	if (img->hook)
	  return (MacImage *) img->hook;
	macimg = init_mac_image(img);
	img->hook = (char *) macimg;
	return macimg;
}

/* This tries to fill in the given image family from various resources.  The order
   should be "best first", so as to improve search time. */

ImageFamily *
mac_load_imf(ImageFamily *imf)
{
    int w, h, i, rsize, startlineno = 0, endlineno = 0;
    char tmpstrbuf[100];
    Obj *imfspec;
    Image *img;
    MacImage *macimg;
    Handle imfhandle, pathandle, sicnhandle, iconhandle, maskhandle, handle;
    PicHandle pichandle;
    Rect bounds;
    Str255 namestr, maskstr, im32x32str, im16x16str, im8x8str, im1x1str;
	short resid;  ResType restype;  Str255 resname;

	/* Can't do anything without a name for the image family. */
	if (imf == NULL || imf->name == NULL)
	  return NULL;
	c2p(imf->name, namestr);
	/* The name of the mask is always formed by appending " mask". */
	sprintf(tmpstrbuf, "%s mask", imf->name);
	c2p(tmpstrbuf, maskstr);
	/* The name of the 32x32 cicn/ppat is always formed by appending " 32x32". */
	sprintf(tmpstrbuf, "%s 32x32", imf->name);
	c2p(tmpstrbuf, im32x32str);
	/* The name of the 16x16 cicn/ppat is always formed by appending " 16x16". */
	sprintf(tmpstrbuf, "%s 16x16", imf->name);
	c2p(tmpstrbuf, im16x16str);
	/* The name of the 8x8 cicn is always formed by appending " 8x8". */
	sprintf(tmpstrbuf, "%s 8x8", imf->name);
	c2p(tmpstrbuf, im8x8str);
	/* The name of the solid color is always formed by appending " 1x1". */
	sprintf(tmpstrbuf, "%s 1x1", imf->name);
	c2p(tmpstrbuf, im1x1str);
	/* Look for and load the image family specification resource first. */
	imfhandle = (Handle) GetNamedResource('XCif', namestr);
	if (imfhandle != nil) {
		imfspec = read_form_from_string(copy_string(*imfhandle), &startlineno, &endlineno, NULL);
		interp_imf_contents(imf, imfspec);
	}
	pichandle = (PicHandle) GetNamedResource('PICT', namestr);
	if (pichandle != nil) {
		img = get_img(imf, 16, 16, 0); /* should get real bounds */
		if (img != NULL) {
			macimg = get_mac_image(img);
			/* (should distinguish mono and color picts somehow) */
			macimg->monopict = pichandle;
			/* Look for a mask too. */
			pichandle = (PicHandle) GetNamedResource('PICT', maskstr);
			if (pichandle != nil) {
				macimg->maskpict = pichandle;
			}
			img->istile = 0;
		}
	}
	/* (should also be able to pick up picts with rows and columns of images, but
	    would need separate resource to identify which is which) */
	/* Pick up cicns, if we can do color. */
	if (hasColorQD) {
		handle = GetNamedResource('cicn', namestr);
		mac_add_cicn(imf, handle);
		handle = GetNamedResource('cicn', im16x16str);
		mac_add_cicn(imf, handle);
		handle = GetNamedResource('cicn', im8x8str);
		mac_add_cicn(imf, handle);
	} else {
		/* (should at least try to get the mono part, without using CQD traps) */
	}
	/* Pick up ICONs. */
	iconhandle = (Handle) GetNamedResource('ICON', namestr);
	if (iconhandle != nil) {
		img = get_img(imf, 32, 32, 0);
		if (img != NULL) {
			macimg = get_mac_image(img);
			macimg->monoicon = iconhandle;
			/* Look for a mask too. */
			iconhandle = (Handle) GetNamedResource('ICON', maskstr);
			if (iconhandle != nil) {
				macimg->maskicon = iconhandle;
			}
		}
	}
	/* Pick up SICNs. */
	sicnhandle = (Handle) GetNamedResource('SICN', namestr);
	if (sicnhandle != nil) {
		img = get_img(imf, 16, 16, 0);
		if (img != NULL) {
			macimg = get_mac_image(img);
			/* Image itself is just the first 32 bytes, mask is second 32 if present. */
			macimg->monosicn = sicnhandle;
			rsize = SizeResource(sicnhandle);
			if (rsize >= 64) {
				maskhandle = (Handle) NewHandle(32);
				for (i = 0; i < 32; ++i) {
					(*maskhandle)[i] = (*sicnhandle)[i+32];
				}
				macimg->masksicn = maskhandle;
			} else {
				/* Mask could be separate resource, so look for it. */
				iconhandle = GetNamedResource('SICN', maskstr);
				if (iconhandle != nil) {
					macimg->masksicn = sicnhandle;
				} else {
					/* no mask to be found */
				}
			}
			img->istile = 0;
		}
	}
	/* Pick up color patterns, if we're capable of doing color. */
	if (hasColorQD) {
		handle = GetNamedResource('ppat', namestr);
		mac_add_ppat(imf, handle);
		handle = GetNamedResource('ppat', im1x1str);
		mac_add_ppat(imf, handle);
		handle = GetNamedResource('ppat', im16x16str);
		mac_add_ppat(imf, handle);
		handle = GetNamedResource('ppat', im32x32str);
		mac_add_ppat(imf, handle);
	}
	/* Load a pattern, which can be used for any size area, but whose "natural" size
	   is always 8x8. */
	pathandle = GetNamedResource('PAT ', namestr);
	if (pathandle != nil) {
		img = get_img(imf, 8, 8, 0);
		if (img != NULL) {
			img->istile = TRUE;
			macimg = get_mac_image(img);
			if (macimg->patdefined) {
				int allzeros = TRUE;

				for (i = 0; i < 8; ++i) {
					if (((char *) &(macimg->monopat))[i] != 0) {
						allzeros = FALSE;
						break;
					}
				}
				/* A mono pattern of all zeros is a default pat from a ppat; overwrite
				   it silently. */
				if (!allzeros) {
					for (i = 0; i < 8; ++i) {
						if (((char *) &(macimg->monopat))[i] != ((char *) *pathandle)[i]) {
							run_warning("ppat/PAT mismatch for \"%s\", overwriting ppat",
										imf->name);
							break;
						}
					}
				}
				for (i = 0; i < 8; ++i)
				  SET_IMG_PAT(macimg, i, ((char *) *pathandle)[i]);
			} else {
				/* Set the monopat. */
				for (i = 0; i < 8; ++i)
				  SET_IMG_PAT(macimg, i, ((char *) *pathandle)[i]);
				macimg->patdefined = 1;
			}
		}
	}
	return imf;
}

void
mac_add_cicn(ImageFamily *imf, Handle handle)
{
    int n, i, w, h, newcicn;
    Image *img;
    MacImage *macimg;
    CIconHandle cicnhandle;
    PixMap pmap;
    Rect bounds;
	short resid;  ResType restype;  Str255 resname;

	if (handle == nil)
	  return;
	newcicn = FALSE;
	/* Need to get id so we can use special trap. */
	GetResInfo(handle, &resid, &restype, resname);
	cicnhandle = GetCIcon(resid);
	HLock((Handle) cicnhandle);
	pmap = (*cicnhandle)->iconPMap;
	bounds = pmap.bounds;
	w = bounds.right - bounds.left;  h = bounds.bottom - bounds.top;
	img = get_img(imf, w, h, 0);
	if (img != NULL) {
		macimg = get_mac_image(img);
		if (!macimg->cicn_inited) {
			macimg->colricon = (Handle) cicnhandle;
			newcicn = TRUE;
			/* Mask is built in, don't need to load separately. */
		}
	}

	/* Always unlock this handle or the heap will be messed up beyond all recognition!
	Moreover, if get_mac_image returns an already loaded cicn, the new cicnhandle is 
	never used, as macimg->colricon still points to the old cicnhandle at the top of the 
	heap. The new cicnhandle should therefore not only be unlocked but also trashed. */	

	HUnlock((Handle) cicnhandle);
	if (!newcicn)
		DisposeCIcon(cicnhandle);
}

void
mac_add_ppat(ImageFamily *imf, Handle handle)
{
    int n, i, w, h, val, constpat;
    Image *img;
    MacImage *macimg;
    PixPatHandle ppathandle;
	PixMapHandle pmhandle;
    PixMap pmap;
	CTabHandle ctabhandle;
	ColorSpec *ctdata;
    Rect bounds;
	short resid;  ResType restype;  Str255 resname;
	if (handle == nil)
	  return;
	HLock(handle);
	/* Need to get the id of the ppat so we can use special trap. */
	GetResInfo(handle, &resid, &restype, resname);
	ppathandle = GetPixPat(resid);
	HLock((Handle) ppathandle);
	pmhandle = (*ppathandle)->patMap;
	switch ((*ppathandle)->patType) {
		case 0:
			/* (should put something in monodata?) */
			w = h = 8;
			break;
		case 1:
			/* Compute the actual size of the pattern. */
			bounds = (*pmhandle)->bounds;
			w = bounds.right - bounds.left;  h = bounds.bottom - bounds.top;
			/* Decide if this pattern is a pattern or just a color; color
			   part must be one color and mono part must be all zeros. */
			ctabhandle = (*pmhandle)->pmTable;
			if ((*ctabhandle)->ctSize == 0) {
				/* See if anything nonzero in mono pattern. */
				constpat = TRUE;
				for (i = 0; i < 8; ++i) {
					val = QD_PAT_ADDR((*ppathandle)->pat1Data)[i];
					if (val != 0)
					  constpat = FALSE;
				}
				if (constpat) {
					w = h = 1;
				}
			}
	}
	img = get_img(imf, w, h, 0);
	if (img != NULL) {
		/* Indicate that we can use this pattern for any size area. */
		img->istile = TRUE;
		macimg = get_mac_image(img);
		if (w == 1 && h == 1) {
			/* Collect the one color and record it. */
			ctabhandle = (*pmhandle)->pmTable;
			ctdata = (ColorSpec *) &((*ctabhandle)->ctTable);
			macimg->color.red   = ctdata[0].rgb.red;
			macimg->color.green = ctdata[0].rgb.green;
			macimg->color.blue  = ctdata[0].rgb.blue;
			macimg->colordefined = TRUE;
		} else {
			macimg->colrpat = ppathandle;
			/* A ppat always has an 8x8 mono pattern defined. */
			img = get_img(imf, 8, 8, 0);
			if (img != NULL) {
				img->istile = TRUE;
				macimg = get_mac_image(img);
				/* Set the monopat. */
				for (i = 0; i < 8; ++i)
				  SET_IMG_PAT(macimg, i, QD_PAT_ADDR((*ppathandle)->pat1Data)[i]);
				macimg->patdefined = 1;
			}
		}
	}
	HUnlock((Handle) ppathandle);
	HUnlock(handle);
}

/* Given an image family that already has data for it, in Lisp form,
   make platform-specific images. */

ImageFamily *
mac_interp_imf(imf, img, force)
ImageFamily *imf;
Image *img;
int force;
{
	if (img == NULL) {
		for_all_images(imf, img) {
			mac_interp_image(imf, img, force);
		}
	} else {
		mac_interp_image(imf, img, force);
	}
	return imf;
}

void
mac_interp_image(ImageFamily *imf, Image *img, int force)
{
	int w, h, i, numbytes, bitrowbytes, actualw, actualh, pixelsize, rowbytes;
	int macrowbytes;
	int monodone, colrdone, maskdone;
	MacImage *macimg;
	Handle sicnhandle, iconhandle, datahandle;
    PixPatHandle ppathandle;
    CIconHandle cicnhandle;
    PixMapHandle pmhandle;

	w = img->w;  h = img->h;
	actualw = img->actualw;  actualh = img->actualh;
	pixelsize = img->pixelsize;
	rowbytes = computed_rowbytes(actualw, pixelsize);
	macimg = get_mac_image(img);
	/* Mono icons and masks are very similar; digest both here. */
	monodone = colrdone = maskdone = FALSE;
	if (img->monodata != lispnil && (img->rawmonodata == NULL || force)) {
		numbytes = h * computed_rowbytes(w, 1);
		img->rawmonodata = xmalloc(numbytes);
		interp_bytes(img->monodata, numbytes, img->rawmonodata, 0);
	}
	if (w == 8 && h == 8 && img->rawmonodata != NULL && img->istile) {
		/* Monochrome pattern. */
		datahandle = NewHandle(8);
		memset(*datahandle, 0, 8);
		/* Read exactly 8 bytes. */
		mac_copy_bytes(img->rawmonodata, 8, datahandle, 0, rowbytes);
		/* Fill in the monopat. */
		for (i = 0; i < 8; ++i)
		  SET_IMG_PAT(macimg, i, (*datahandle)[i]);
		macimg->patdefined = TRUE;
		/* Patterns have no masks, but defeat subsequent mask hacking. */
		monodone = maskdone = TRUE;
	}
	if (w == 16 && h == 16 && img->rawmonodata != NULL) {
		/* Shape is appropriate for a small icon, make one. */
		sicnhandle = NewHandle(32);
		memset(*sicnhandle, 0, 32);
		/* Read exactly 32 bytes. */
		mac_copy_bytes(img->rawmonodata, 32, sicnhandle, 0, rowbytes);
		macimg->monosicn = sicnhandle;
		monodone = TRUE;
	}
	if (w == 32 && h == 32 && img->rawmonodata != NULL) {
		/* Shape is appropriate for a standard icon, make one. */
		iconhandle = NewHandle(128);
		memset(*iconhandle, 0, 128);
		/* Read exactly 128 bytes. */
		mac_copy_bytes(img->rawmonodata, 128, iconhandle, 0, rowbytes);
		macimg->monoicon = iconhandle;
		monodone = TRUE;
	}
	if (img->maskdata != lispnil && (img->rawmaskdata == NULL || force)) {
		numbytes = h * computed_rowbytes(w, 1);
		img->rawmaskdata = xmalloc(numbytes);
		interp_bytes(img->maskdata, numbytes, img->rawmaskdata, 0);
	}
	if (w == 16 && h == 16 && img->rawmaskdata != NULL) {
		/* Shape is appropriate for a small icon mask, make one. */
		sicnhandle = NewHandle(32);
		memset(*sicnhandle, 0, 32);
		/* Read exactly 32 bytes. */
		mac_copy_bytes(img->rawmaskdata, 32, sicnhandle, 0, rowbytes);
		macimg->masksicn = sicnhandle;
		maskdone = TRUE;
	}
	if (w == 32 && h == 32 && img->rawmaskdata != NULL) {
		/* Shape is appropriate for a standard icon, make one. */
		iconhandle = NewHandle(128);
		memset(*iconhandle, 0, 128);
		/* Read exactly 128 bytes. */
		mac_copy_bytes(img->rawmaskdata, 128, iconhandle, 0, rowbytes);
		macimg->maskicon = iconhandle;
		maskdone = TRUE;
	}
	/* A 1x1 image is just a color - make it into a small color pattern. */
	if (hasColorQD && w == 1 && h == 1 && img->istile) {
		/* Make a simple color pattern. */
		ppathandle = NewPixPat();
		pmhandle = (*ppathandle)->patMap;
		SetRect(&((*pmhandle)->bounds), 0, 0, 8, 8);
		(*pmhandle)->rowBytes = 1;
		(*pmhandle)->pixelType = 0;
		(*pmhandle)->pixelSize = 1;
		(*pmhandle)->cmpCount = 1;
		(*pmhandle)->cmpSize = pixelsize;
		(*pmhandle)->pmTable = interp_ctab(img->palette);
		datahandle = NewHandle(8);
		(*ppathandle)->patData = datahandle;
		HLock(datahandle);
		memset(*datahandle, 0, 8);
		HUnlock(datahandle);
		(*ppathandle)->patXValid = -1;
		macimg->colrpat = ppathandle;
		colrdone = maskdone = TRUE;
	}
	if (img->colrdata != lispnil && (img->rawcolrdata == NULL || force)) {
		numbytes = h * computed_rowbytes(w, img->pixelsize);
		img->rawcolrdata = xmalloc(numbytes);
		interp_bytes(img->colrdata, numbytes, img->rawcolrdata, 0);
	}
	/* A sufficiently small tiling color image can be a color pattern. */
	if (hasColorQD && w <= 64 && h <= 64 && img->istile && img->rawcolrdata != NULL) {
		macimg->colrpat = mac_create_ppat(img);
		colrdone = maskdone = TRUE;
	}
	/* Finally, a color icon covers all other cases. */
	if (hasColorQD && w <= 64 && h <= 64 && !img->istile
	    && (img->rawcolrdata != NULL
	        || (macimg->monosicn == nil
	            && macimg->masksicn == nil
	            && macimg->monoicon == nil
	            && macimg->maskicon == nil))) {
	    macimg->colricon = (Handle) mac_create_cicn(img);
	    macimg->cicn_inited = FALSE;
		mac_init_cicn(img);
		colrdone = TRUE;
	}
	if (!monodone && !colrdone) {
		run_warning("%dx%d image of \"%s\" could not be interpreted", w, h, imf->name);
	}
#if 0 /* broken */
    /* If we have a color pattern of random size, and an 8x8 mono pattern, then we should
       glue the mono pattern into the color pattern's mono slot.  This requires searching
       all pairs of images in the family. */
	for (img = imf->images; img != NULL; img = img->next) {
		Image *img2;
		MacImage *macimg2;
		macimg = get_mac_image(img);
		if (macimg->colrpat != nil) {
			for (img2 = imf->images; img2 != NULL; img2 = img2->next) {
				macimg2 = get_mac_image(img2);
				if (macimg2->patdefined) {
					memcpy((char *) &(*(macimg->colrpat)->pat1Data), (char *) &(macimg2->monopat), 8);
				}
			}
		}
	}
#endif
}

PixPatHandle
mac_create_ppat(Image *img)
{
	int numbytes, actualw, actualh, pixelsize, rowbytes, macrowbytes;
	Handle iconhandle, datahandle;
    PixPatHandle ppathandle;
    PixMapHandle pmhandle;

	actualw = img->actualw;  actualh = img->actualh;
	pixelsize = img->pixelsize;
	rowbytes = computed_rowbytes(actualw, pixelsize);
	/* Limit bits per pixel to reasonable values. */
	if (pixelsize < 1 || pixelsize > 8)
	  pixelsize = 8;
	macrowbytes = mac_computed_rowbytes(actualw, pixelsize);
	/* As a special exception, PixPats can have a rowbytes of 1. */
	if (actualw <= 8 && pixelsize == 1)
	  macrowbytes = 1;
	/* Make a color pattern. */
	ppathandle = NewPixPat();
	pmhandle = (*ppathandle)->patMap;
	SetRect(&((*pmhandle)->bounds), 0, 0, actualw, actualh);
	(*pmhandle)->rowBytes = macrowbytes;
	(*pmhandle)->pixelType = 0;
	(*pmhandle)->pixelSize = pixelsize;
	(*pmhandle)->cmpCount = 1;
	(*pmhandle)->cmpSize = pixelsize;
	(*pmhandle)->pmTable = interp_ctab(img->palette);
	numbytes = actualh * rowbytes;
	datahandle = NewHandle(numbytes);
	(*ppathandle)->patData = datahandle;
	mac_copy_bytes(img->rawcolrdata, numbytes, datahandle, macrowbytes - rowbytes, rowbytes);
	(*ppathandle)->patXValid = -1;
	return ppathandle;
}

/* Given an image, make a color icon out of its data. */

CIconHandle
mac_create_cicn(Image *img)
{
	int w, h, i, numbytes, bitrowbytes, macbitrowbytes, actualw, actualh;
	int pixelsize, rowbytes, macrowbytes;
	MacImage *macimg;
	Handle sicnhandle, iconhandle, datahandle, monohandle, maskhandle;
    CIconHandle cicnhandle;
    PixMapHandle pmhandle;

	w = img->w;  h = img->h;
	actualw = img->actualw;  actualh = img->actualh;
	pixelsize = img->pixelsize;
    /* If no color data in evidence, prepare to use mono or mask instead. */
    if (img->rawcolrdata == NULL)
	  pixelsize = 1;
	rowbytes = computed_rowbytes(actualw, pixelsize);
	macrowbytes = mac_computed_rowbytes(actualw, pixelsize);
	bitrowbytes = computed_rowbytes(actualw, 1);
	macbitrowbytes = mac_computed_rowbytes(actualw, 1);
	macimg = get_mac_image(img);
	/* Make a full color icon. */
	/* Allocate enough space for the icon and its data/mask bitmaps. */
	cicnhandle = (CIconHandle) NewHandle(sizeof(CIcon) + 2 * actualh * macbitrowbytes);
	if (cicnhandle == nil)
	  run_error("out of memory");
	HLock((Handle) cicnhandle);
	(*cicnhandle)->iconPMap.baseAddr = 0;
	(*cicnhandle)->iconPMap.rowBytes = macrowbytes | 0x8000;
	SetRect(&((*cicnhandle)->iconPMap.bounds), 0, 0, actualw, actualh);
	(*cicnhandle)->iconPMap.pmVersion = 0;
	(*cicnhandle)->iconPMap.packType = 0;
	(*cicnhandle)->iconPMap.packSize = 0;
	(*cicnhandle)->iconPMap.hRes = 0;
	(*cicnhandle)->iconPMap.vRes = 0;
	(*cicnhandle)->iconPMap.pixelType = 0;
	(*cicnhandle)->iconPMap.pixelSize = pixelsize;
	(*cicnhandle)->iconPMap.cmpCount = 1;
	(*cicnhandle)->iconPMap.cmpSize = pixelsize;
	(*cicnhandle)->iconPMap.planeBytes = 0;
	(*cicnhandle)->iconPMap.pmTable = interp_ctab(img->palette);
	(*cicnhandle)->iconPMap.pmReserved = 0;
	/* Configure the monochrome icon. */
	SetRect(&((*cicnhandle)->iconBMap.bounds), 0, 0, actualw, actualh);
	(*cicnhandle)->iconBMap.rowBytes = 0;
	(*cicnhandle)->iconBMap.baseAddr = NULL;
	SetRect(&((*cicnhandle)->iconMask.bounds), 0, 0, actualw, actualh);
	/* Configure the mask bitmap. */
	(*cicnhandle)->iconMask.rowBytes = macbitrowbytes;
	(*cicnhandle)->iconMask.baseAddr = NULL;
	numbytes = actualh * macrowbytes;
	datahandle = NewHandle(numbytes);
	(*cicnhandle)->iconData = datahandle;
	/* Fill up the datahandle with the color data, or else use mono/mask data
	   if the color data is missing. */
	if (img->colrdata != lispnil || img->rawcolrdata != NULL) {
		if (img->rawcolrdata == NULL) {
			int xnumbytes = img->h * computed_rowbytes(img->w, img->pixelsize);
			img->rawcolrdata = xmalloc(xnumbytes);
			interp_bytes(img->colrdata, xnumbytes, img->rawcolrdata, 0);
		}
		mac_copy_bytes(img->rawcolrdata, numbytes, datahandle, macrowbytes - rowbytes, rowbytes);
	} else if (img->monodata != lispnil || img->rawmonodata != NULL) {
		if (img->rawmonodata == NULL) {
			int xnumbytes = h * computed_rowbytes(w, 1);
			img->rawmonodata = xmalloc(xnumbytes);
			interp_bytes(img->monodata, xnumbytes, img->rawmonodata, 0);
		}
		mac_copy_bytes(img->rawmonodata, numbytes, datahandle, macrowbytes - rowbytes, rowbytes);
		/* Make an ersatz color table. */
		(*cicnhandle)->iconPMap.pmTable = synth_ctab();
	} else if (img->maskdata != lispnil || img->rawmaskdata != NULL) {
		if (img->rawmaskdata == NULL) {
			int xnumbytes = h * computed_rowbytes(w, 1);
			img->rawmaskdata = xmalloc(xnumbytes);
			interp_bytes(img->maskdata, xnumbytes, img->rawmaskdata, 0);
		}
		mac_copy_bytes(img->rawmaskdata, numbytes, datahandle, macrowbytes - rowbytes, rowbytes);
		/* Make an ersatz color table. */
		(*cicnhandle)->iconPMap.pmTable = synth_ctab();
	} else {
		/* (should do something) */
	}
	/* Set up rowbytes and numbytes for both mono and mask parts. */
    (*cicnhandle)->iconBMap.rowBytes = macbitrowbytes;
	numbytes = actualh * macbitrowbytes;
	if (macimg->monoicon != nil) {
		/* If a mono icon is already set up, use it as the icon's bitmap. */
		HLock(macimg->monoicon);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *(macimg->monoicon), numbytes);
		HUnlock(macimg->monoicon);
	} else if (macimg->monosicn != nil) {
		/* If a mono sicn is already set up, use it as the icon's bitmap. */
		HLock(macimg->monosicn);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *(macimg->monosicn), numbytes);
		HUnlock(macimg->monosicn);
	} else if (img->monodata != lispnil || img->rawmonodata != NULL) {
		/* (should use static temp space here?) */
		monohandle = NewHandle(numbytes);
		HLock(monohandle);
		memset(*monohandle, 0, numbytes);
		/* Read as many bytes as directed. */
		if (img->rawmonodata == NULL) {
			int xnumbytes = h * computed_rowbytes(w, 1);
			img->rawmonodata = xmalloc(xnumbytes);
			interp_bytes(img->monodata, xnumbytes, img->rawmonodata, 0);
		}
		mac_copy_bytes(img->rawmonodata, numbytes, monohandle, macbitrowbytes - bitrowbytes, bitrowbytes);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *monohandle, numbytes);
		HUnlock(monohandle);
	} else {
		/* No mono data, make it all 1s. */
		monohandle = NewHandle(numbytes);
		HLock(monohandle);
		memset(*monohandle, 0xff, numbytes);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *monohandle, numbytes);
		HUnlock(monohandle);
	}
	if (macimg->maskicon != nil) {
		/* If a mask icon is already set up, use it as the icon's mask. */
		HLock(macimg->maskicon);
		memcpy((char *) (*cicnhandle)->iconMaskData, *(macimg->maskicon), numbytes);
		HUnlock(macimg->maskicon);
	} else if (macimg->masksicn != nil) {
		/* If a mask sicn is already set up, use it as the icon's mask. */
		HLock(macimg->masksicn);
		memcpy((char *) (*cicnhandle)->iconMaskData, *(macimg->masksicn), numbytes);
		HUnlock(macimg->masksicn);
	} else if (img->maskdata != lispnil || img->rawmaskdata != NULL) {
		maskhandle = NewHandle(numbytes);
		HLock(maskhandle);
		memset(*maskhandle, 0, numbytes);
		/* Read as many bytes as directed. */
		if (img->rawmaskdata == NULL) {
			int xnumbytes = h * computed_rowbytes(w, 1);
			img->rawmaskdata = xmalloc(xnumbytes);
			interp_bytes(img->maskdata, xnumbytes, img->rawmaskdata, 0);
		}
		mac_copy_bytes(img->rawmaskdata, numbytes, maskhandle, macbitrowbytes - bitrowbytes, bitrowbytes);
		memcpy((char *) (*cicnhandle)->iconMaskData, *maskhandle, numbytes);
		HUnlock(maskhandle);
	} else {
		/* No mask data, make it be the entire area. */
		maskhandle = NewHandle(numbytes);
		HLock(maskhandle);
		memset(*maskhandle, 0xff, numbytes);
		memcpy((char *) (*cicnhandle)->iconMaskData, *maskhandle, numbytes);
		HUnlock(maskhandle);
	}
	HUnlock((Handle) cicnhandle);
	return cicnhandle;
}

static void
mac_interp_bytes(Obj *datalist, int numbytes, Handle desthandle, int jump)
{
    HLock(desthandle);
    interp_bytes(datalist, numbytes, *desthandle, jump);
    HUnlock(desthandle);
}

static void
mac_copy_bytes(char *data, int numbytes, Handle desthandle, int jump, int rowbytes)
{
	int i, j;

    HLock(desthandle);
    if (jump == 0) {
		memcpy(*desthandle, data, numbytes);
    } else {
		j = 0;
		for (i = 0; i < numbytes; ++i) {
			(*desthandle)[i] = data[j++];
			if (jump == 1) {
				++i;
				(*desthandle)[i] = 0;
			}
		}
    }
    HUnlock(desthandle);
}

/* This function makes dummy calls to PlotCIcon.  The dummy call is absolutely
   essential if one wishes to speed up and/or modify plotting by direct access
   of the cicn PixMap and BitMap, as is now done in draw_unit_image and draw_side_emblem. 
   The reason for this is that PlotCIcon not only plots the cicn, but also sets up
   a pointer to the PixMap data at offset zero. If this has not been done at least
   once, CopyBits cannot handle the cicn. Moreover, since this is a pointer and not
   a handle, it gets trashed whenever the cicn or PixMap data is moved in memory.
   This is why a dummy call to PlotCIcon is needed before every access of the PixMap.
   To avoid this, the cicn, PixMap and CTable are now locked in memory right after
   the first (and only) PlotCIcon call. To prevent fragmentation, the handles are
   also moved to the top of the heap zone before this is done. */

void
mac_init_cicn(Image *img)
{
	CIconHandle cicnhandle;
	MacImage *macimg;

	macimg = (MacImage *) img->hook;
	if (macimg->colricon != NULL && !macimg->cicn_inited) {
		cicnhandle = (CIconHandle) macimg->colricon;

		/* Hang 'em High! */
		HLockHi((Handle) cicnhandle);
		HLockHi((Handle) (*cicnhandle)->iconData);
		HLockHi((Handle) (*cicnhandle)->iconPMap.pmTable);

		/* Initialize PixMap pointer by this dummy call. */
		PlotCIcon(NULL, cicnhandle);

		/* Lock down stuff that got unlocked by PlotCIcon. */
		HLock((Handle) (*cicnhandle)->iconData);
		HLock((Handle) cicnhandle);
		macimg->cicn_inited = TRUE;
	}
}

/* Given a list of color table entries, create and return a color table. */

static CTabHandle
interp_ctab(Obj *palette)
{
	int len, i, r, g, b;
	char *colorname;
	Obj *head, *rest, *color;
	ColorSpec *ctdata;
	CTabHandle ctabhandle;

	if (!consp(palette)) {
		/* (should look up named palette) */
	}
	len = length(palette);
	if (len == 0)
	  return nil;
	ctabhandle = (CTabHandle) NewHandle(8 + len * 8);
	HLock((Handle) ctabhandle);
	(*ctabhandle)->ctFlags = 0;
	(*ctabhandle)->ctSeed = GetCTSeed();
	(*ctabhandle)->ctSize = len - 1;
	ctdata = (ColorSpec *) &((*ctabhandle)->ctTable);
	for (i = 0, rest = palette; i < len; ++i, rest = cdr(rest)) {
		head = car(rest);
		ctdata[i].value = c_number(car(head));
		color = cdr(head);
		if (symbolp(car(color)) || stringp(car(color))) {
			colorname = c_string(car(color));
			if (strcmp(colorname, "white") == 0) {
				ctdata[i].rgb.red   = 65535;
				ctdata[i].rgb.green = 65535;
				ctdata[i].rgb.blue  = 65535;
			} else if (strcmp(colorname, "black") == 0) {
				ctdata[i].rgb.red   = 0;
				ctdata[i].rgb.green = 0;
				ctdata[i].rgb.blue  = 0;
			} else if (strcmp(colorname, "red") == 0) {
				ctdata[i].rgb.red   = 65535;
				ctdata[i].rgb.green = 0;
				ctdata[i].rgb.blue  = 0;
			} else if (strcmp(colorname, "green") == 0) {
				ctdata[i].rgb.red   = 0;
				ctdata[i].rgb.green = 65535;
				ctdata[i].rgb.blue  = 0;
			} else if (strcmp(colorname, "blue") == 0) {
				ctdata[i].rgb.red   = 0;
				ctdata[i].rgb.green = 0;
				ctdata[i].rgb.blue  = 65535;
			} else {
				init_warning("No color named \"%s\" found, substituting gray",
							 colorname);
				ctdata[i].rgb.red   = i * 5000;
				ctdata[i].rgb.green = i * 5000;
				ctdata[i].rgb.blue  = i * 5000;
			}
		} else if (numberp(car(color))) {
			ctdata[i].rgb.red   = c_number(car(color));
			ctdata[i].rgb.green = c_number(cadr(color));
			ctdata[i].rgb.blue  = c_number(car(cddr(color)));
		} else {
			init_warning("palette color is not a name or set of numbers, ignoring");
		}
	}
	HUnlock((Handle) ctabhandle);
	return ctabhandle;
}

/* Make up a two-color (white and black) color table. */

static CTabHandle
synth_ctab()
{
	int len;
	ColorSpec *ctdata;
	CTabHandle ctabhandle;

	len = 2;
	ctabhandle = (CTabHandle) NewHandle(8 + len * 8);
	HLock((Handle) ctabhandle);
	(*ctabhandle)->ctFlags = 0;
	(*ctabhandle)->ctSeed = GetCTSeed();
	(*ctabhandle)->ctSize = len - 1;
	ctdata = (ColorSpec *) &((*ctabhandle)->ctTable);
	ctdata[0].value = 0;
	ctdata[0].rgb.red = 65535;
	ctdata[0].rgb.green = 65535;
	ctdata[0].rgb.blue = 65535;
	ctdata[1].value = 1;
	ctdata[1].rgb.red = 0;
	ctdata[1].rgb.green = 0;
	ctdata[1].rgb.blue = 0;
	HUnlock((Handle) ctabhandle);
	return ctabhandle;
}

void
make_generic_image_data(ImageFamily *imf)
{
	int i, hasmono, hasmask;
	char *colorname;
	Image *img;
	MacImage *macimg;
	Obj *colorcomp;

	for_all_images(imf, img) {
		hasmono = hasmask = FALSE;
		macimg = get_mac_image(img);
		if (hasColorQD && macimg->colricon != nil)
		  convert_cicn(img, (CIconHandle) macimg->colricon, &hasmono, &hasmask);
		if (hasColorQD && macimg->colrpat != nil)
		  convert_ppat(img, macimg->colrpat);
		if (hasColorQD && img->w == 1 && img->h == 1 && macimg->colordefined) {
			colorname = find_color_name(macimg->color.red, macimg->color.green, macimg->color.blue);
			if (colorname != NULL) {
				colorcomp = cons(new_string(colorname), lispnil);
			} else {
				colorcomp = cons(new_number(macimg->color.red),
								 cons(new_number(macimg->color.green),
									  cons(new_number(macimg->color.blue),
									       lispnil)));
			}
			img->palette = cons(cons(new_number(0), colorcomp), lispnil);
		}
		if (macimg->monoicon != nil && !hasmono) {
			img->rawmonodata = xmalloc(128);
			for (i = 0; i < 128; ++i)
			  img->rawmonodata[i] = ((char *) *(macimg->monoicon))[i];
		}
		if (macimg->maskicon != nil && !hasmask) {
			img->rawmaskdata = xmalloc(128);
			for (i = 0; i < 128; ++i)
			  img->rawmaskdata[i] = ((char *) *(macimg->maskicon))[i];
		}
		if (macimg->monosicn != nil && !hasmono) {
			img->rawmonodata = xmalloc(32);
			for (i = 0; i < 32; ++i)
			  img->rawmonodata[i] = ((char *) *(macimg->monosicn))[i];
		}
		if (macimg->masksicn != nil && !hasmask) {
			img->rawmaskdata = xmalloc(32);
			for (i = 0; i < 32; ++i)
			  img->rawmaskdata[i] = ((char *) *(macimg->masksicn))[i];
		}
		if (macimg->patdefined) {
			img->rawmonodata = xmalloc(8);
			for (i = 0; i < 8; ++i)
			  img->rawmonodata[i] = ((char *) &(macimg->monopat))[i];
		}
	}
    compute_image_bboxes(imf);
}

/* Generify a color pattern. */

static void
convert_ppat(Image *img, PixPatHandle pat)
{
	int w, h, i, j, numbytes, rowbytes, macrowbytes, anynonzero;
	Rect bounds;
	PixMapHandle pmhandle = (*pat)->patMap;
	CTabHandle ctabhandle = (*pmhandle)->pmTable;
	Handle patdata = (*pat)->patData;

	switch ((*pat)->patType) {
		case 0:
			/* (should put something in monodata?) */
			break;
		case 1:
			/* Compute the actual size of the pattern. */
			bounds = (*pmhandle)->bounds;
			w = bounds.right - bounds.left;  h = bounds.bottom - bounds.top;
			if (w != img->w || h != img->h) {
				img->actualw = w;  img->actualh = h;
			}
			img->pixelsize = (*pmhandle)->pixelSize;
			img->palette = convert_ctab(ctabhandle);
			rowbytes = computed_rowbytes(w, img->pixelsize);
			macrowbytes = (*pmhandle)->rowBytes & 0x3fff;
			numbytes = h * rowbytes;
			img->rawcolrdata = xmalloc(numbytes);
			j = 0;
			for (i = 0; i < numbytes; ++i) {
				if (rowbytes > 1 && i > 0 && i % rowbytes == 0)
				  j += (macrowbytes - rowbytes);
				img->rawcolrdata[i] = (*patdata)[j++];
			}
			break;
		case 2:
			run_warning("Type 2 (RGB) pattern, ignoring");
			break;
	}
}

/* Generify the data in a color icon. */

static void
convert_cicn(Image *img, CIconHandle cicnhandle, int *hasmono, int *hasmask)
{
	int w, h, i, j, rowbytes, macrowbytes, bitrowbytes, macbitrowbytes, numbytes;
	Rect bounds;
	char *baseaddr;
	PixMap pmap = (*cicnhandle)->iconPMap;
	CTabHandle ctabhandle = pmap.pmTable;
	Handle datahandle;

	*hasmono = *hasmask = FALSE;
	HLock((Handle) cicnhandle);
	bounds = pmap.bounds;
	w = bounds.right - bounds.left;  h = bounds.bottom - bounds.top;
	if (w != img->w || h != img->h) {
		img->actualw = w;  img->actualh = h;
	}
	img->pixelsize = pmap.pixelSize;
	img->palette = convert_ctab(ctabhandle);
	rowbytes = computed_rowbytes(w, img->pixelsize);
	macrowbytes = pmap.rowBytes & 0x3fff;
	numbytes = h * rowbytes;
	img->rawcolrdata = xmalloc(numbytes);
	datahandle = (*cicnhandle)->iconData;
	j = 0;
	for (i = 0; i < numbytes; ++i) {
		if (i > 0 && (rowbytes == 1 || i % rowbytes == 0))
		  j += (macrowbytes - rowbytes);
		img->rawcolrdata[i] = (*datahandle)[j++];
	}
	/* Convert the cicn's monochrome icon if defined. */
	bitrowbytes = computed_rowbytes(w, 1);
	macbitrowbytes = (*cicnhandle)->iconBMap.rowBytes;
	if (macbitrowbytes > 0) {
		baseaddr = ((char *) (*cicnhandle)->iconMaskData) + h * macbitrowbytes;
		numbytes = h * bitrowbytes;
		img->rawmonodata = xmalloc(numbytes);
		j = 0;
		for (i = 0; i < numbytes; ++i) {
			if (i > 0 && (bitrowbytes == 1 || i % bitrowbytes == 0))
			  j += (macbitrowbytes - bitrowbytes);
			img->rawmonodata[i] = baseaddr[j++];
		}
		*hasmono = TRUE;
	}
	/* Convert the cicn's mask if one is defined. */
	macbitrowbytes = (*cicnhandle)->iconMask.rowBytes;
	if (macbitrowbytes > 0) {
		baseaddr = (char *) (*cicnhandle)->iconMaskData;
		numbytes = h * bitrowbytes;
		img->rawmaskdata = xmalloc(numbytes);
		j = 0;
		for (i = 0; i < numbytes; ++i) {
			if (i > 0 && (bitrowbytes == 1 || i % bitrowbytes == 0))
			  j += (macbitrowbytes - bitrowbytes);
			img->rawmaskdata[i] = baseaddr[j++];
		}
		*hasmask = TRUE;
	}
	HUnlock((Handle) cicnhandle);
}

/* Generify the data in a color table. */

static Obj *
convert_ctab(CTabHandle ctabhandle)
{
	int c, ctsize;
	ColorSpec cspec;
	Obj *rslt = lispnil, *tmp, *rest, *restprev, *tmp2;

	if (ctabhandle == nil)
	  return lispnil;
	ctsize = (*ctabhandle)->ctSize;
	if (ctsize >= 0) {
		HLock((Handle) ctabhandle);
		for (c = 0; c <= ctsize; ++c) {
			cspec = (*ctabhandle)->ctTable[c];
			tmp = cons(new_number(cspec.value),
					   cons(new_number(cspec.rgb.red),
					   		cons(new_number(cspec.rgb.green),
					   			 cons(new_number(cspec.rgb.blue),
					   			 	  lispnil))));
			/* Splice the new entry into the list so that all are sorted by index. */
			restprev = lispnil;
			for (rest = rslt;
				 cspec.value > (numberp(car(car(rest))) ? c_number(car(car(rest))) : 1000000);
				 rest = cdr(rest)) {
				restprev = rest;
			}
			tmp2 = cons(tmp, rest);
			if (restprev != lispnil)
			  set_cdr(restprev, tmp2);
			else
			  rslt = tmp2;
		}
		HUnlock((Handle) ctabhandle);
	}
	return rslt;
}
