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

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

/* These need to be extern since they are called by imfapp.c. */

extern int hasColorQD;
extern CIconHandle mac_create_cicn(Image *img);
extern void mac_add_cicn(ImageFamily *imf, Handle handle);

static PixPatHandle mac_create_ppat(Image *img);
static 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_image_1(ImageFamily *imf, Image *img, Image *subimg, int subi, 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 macrowbytes, int rowbytes);
static void mac_init_cicn_1(Image *img);
static CTabHandle interp_ctab(Image *img);
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;
//	
	macimg->colricon = nil;
	macimg->cicn_inited = FALSE;
	
	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); /* 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);
		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);
		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);
		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);
	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);
	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);
			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 i, subi;
    Image *subimg;

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

/* Interpret a single image/subimage. */

static void
mac_interp_image_1(ImageFamily *imf, Image *img, Image *subimg, int subi, 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(subimg);
	/* Mono icons and masks are very similar; digest both here. */
	monodone = colrdone = maskdone = FALSE;
	if (subimg->monodata != lispnil && (subimg->rawmonodata == NULL || force)) {
		numbytes = h * computed_rowbytes(w, 1);
		subimg->rawmonodata = xmalloc(numbytes);
		interp_bytes(subimg->monodata, numbytes, subimg->rawmonodata, 0);
	}
	if (w == 8 && h == 8 && subimg->rawmonodata != NULL && img->istile) {
		/* Monochrome pattern. */
		datahandle = NewHandle(8);
		memset(*datahandle, 0, 8);
		/* Read exactly 8 bytes. */
		mac_copy_bytes(subimg->rawmonodata, 8, datahandle, rowbytes, 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 && subimg->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(subimg->rawmonodata, 32, sicnhandle, rowbytes, rowbytes);
		macimg->monosicn = sicnhandle;
		monodone = TRUE;
	}
	if (w == 32 && h == 32 && subimg->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(subimg->rawmonodata, 128, iconhandle, rowbytes, rowbytes);
		macimg->monoicon = iconhandle;
		monodone = TRUE;
	}
	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);
	}
	if (w == 16 && h == 16 && subimg->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(subimg->rawmaskdata, 32, sicnhandle, rowbytes, rowbytes);
		macimg->masksicn = sicnhandle;
		maskdone = TRUE;
	}
	if (w == 32 && h == 32 && subimg->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(subimg->rawmaskdata, 128, iconhandle, rowbytes, 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);
		datahandle = NewHandle(8);
		(*ppathandle)->patData = datahandle;
		HLock(datahandle);
		memset(*datahandle, 0, 8);
		HUnlock(datahandle);
		(*ppathandle)->patXValid = -1;
		macimg->colrpat = ppathandle;
		colrdone = maskdone = TRUE;
	}
	if (subimg->colrdata != lispnil && (subimg->rawcolrdata == NULL || force)) {
		numbytes = h * computed_rowbytes(w, img->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);
    }
	/* A sufficiently small tiling color image can be a color pattern. */
	if (hasColorQD && w <= 64 && h <= 64 && img->istile && subimg->rawcolrdata != NULL) {
		macimg->colrpat = mac_create_ppat(subimg);
		colrdone = maskdone = TRUE;
	}
	/* Finally, a color icon covers all other cases. */
	if (hasColorQD && w <= 88 && h <= 96 && !img->istile
	    && (subimg->rawcolrdata != NULL
	        || (macimg->monosicn == nil
	            && macimg->masksicn == nil
	            && macimg->monoicon == nil
	            && macimg->maskicon == nil))) {
		mac_init_cicn(subimg);
		colrdone = TRUE;
	}
	/* This will mostly happen for images that are too large. */
	if (!monodone && !colrdone) {
		run_warning("%dx%d image of \"%s\" could not be interpreted", w, h, imf->name);
	}
}

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);
	numbytes = actualh * rowbytes;
	datahandle = NewHandle(numbytes);
	(*ppathandle)->patData = datahandle;
	mac_copy_bytes(img->rawcolrdata, numbytes, datahandle, macrowbytes, 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. */
	/* Use xmalloc to avoid heap gymnastics. */
	cicnhandle = (CIconHandle) NewEmptyHandle();
	*cicnhandle = (CIcon *) xmalloc(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);
	(*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;
	/* Use xmalloc to avoid heap gymnastics. */
	datahandle = NewEmptyHandle();
	*datahandle = xmalloc(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);
	} 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);
		/* 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);
		/* 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);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *monohandle, numbytes);
		HUnlock(monohandle);
	/* Use mask data if no mono data exists. */
	} else if (macimg->maskicon != nil) {
		HLock(macimg->maskicon);
		memcpy((char *) (*cicnhandle)->iconMaskData + numbytes, *(macimg->maskicon), numbytes);
		HUnlock(macimg->maskicon);
	} else if (macimg->masksicn != nil) {
		HLock(macimg->masksicn);
		memcpy((char *) (*cicnhandle)->iconMaskData + numbytes, *(macimg->masksicn), numbytes);
		HUnlock(macimg->masksicn);
	} else if (img->maskdata != lispnil || img->rawmaskdata != NULL) {
		monohandle = NewHandle(numbytes);
		HLock(monohandle);
		memset(*monohandle, 0, numbytes);
		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, monohandle, macbitrowbytes, bitrowbytes);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *monohandle, numbytes);
		HUnlock(monohandle);
	/* No mono or mask data, make it all 1s. */
	} else {
		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);
		memcpy((char *) (*cicnhandle)->iconMaskData, *maskhandle, numbytes);
		HUnlock(maskhandle);
	/* Use mono data if no mask data exists. */
	} else if (macimg->monoicon != nil) {
		HLock(macimg->monoicon);
		memcpy((char *) (*cicnhandle)->iconMaskData, *(macimg->monoicon), numbytes);
		HUnlock(macimg->monoicon);
	} else if (macimg->monosicn != nil) {
		HLock(macimg->monosicn);
		memcpy((char *) (*cicnhandle)->iconMaskData, *(macimg->monosicn), numbytes);
		HUnlock(macimg->monosicn);
	} else if (img->monodata != lispnil || img->rawmonodata != NULL) {
		maskhandle = NewHandle(numbytes);
		HLock(maskhandle);
		memset(*maskhandle, 0, numbytes);
		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, maskhandle, macbitrowbytes, bitrowbytes);
		memcpy(((char *) (*cicnhandle)->iconMaskData) + numbytes, *maskhandle, numbytes);
		HUnlock(maskhandle);
	/* No mask or mono data, make it be the entire area. */
	} else {
		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);
}

/* Mac bitmap/pixmap data usually has a an even number of bytes per row, while
   generic image data can use the smallest number, whether odd or even.  So
   when we copy from generic to Mac-specific, we may need to insert an empty
   byte at the end of each row. */

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

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

void
mac_init_cicn(Image *img)
{
	int subi;

	if (img->numsubimages > 0) {
		for (subi = 0; subi < img->numsubimages; ++subi) {
			mac_init_cicn_1(img->subimages[subi]);
		}
	} else {
		mac_init_cicn_1(img);
	}
}

	/* This function makes dummy calls to PlotCIcon.  This 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 everywhere in the code. 
	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. */

void
mac_init_cicn_1(Image *img)
{
	MacImage *macimg = (MacImage *) img->hook;
	
	/* First make sure we have a colricon! */
	if (macimg->colricon == NULL) {
		macimg->colricon = (Handle) mac_create_cicn(img);
		macimg->cicn_inited = FALSE;
	}
	/* Init the PixMap pointer by a dummy PlotCIcon call. */
	if (macimg->cicn_inited == FALSE) {
		PlotCIcon(NULL, (CIconHandle) macimg->colricon);
		macimg->cicn_inited = TRUE;
	}
}

/* Given an image, create and return a color table, using either its Lisp palette
   or the raw palette, whichever has colors in it. */

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

	palette = img->palette;
	if (!consp(palette)) {
		/* (should look up named palette) */
	}
	if (palette == lispnil && img->rawpalette == NULL)
	  return nil;
	len = (palette != lispnil ? length(palette) : img->numcolors);
	/* Use xmalloc to avoid heap gymnastics. */
	ctabhandle = (CTabHandle) NewEmptyHandle();
	*ctabhandle = (ColorTable *) xmalloc(8 + len * 8);
	HLock((Handle) ctabhandle);
	(*ctabhandle)->ctFlags = 0;
	(*ctabhandle)->ctSeed = GetCTSeed();
	(*ctabhandle)->ctSize = len - 1;
	ctdata = (ColorSpec *) &((*ctabhandle)->ctTable);
	if (palette != lispnil) {
		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));
				/* Look for predefined color names. */
				if (strcmp(colorname, "white") == 0) {
					r = 65535;  g = 65535;  b = 65535;
				} else if (strcmp(colorname, "black") == 0) {
					r = 0;  g = 0;  b = 0;
				} else if (strcmp(colorname, "red") == 0) {
					r = 65535;  g = 0;  b = 0;
				} else if (strcmp(colorname, "green") == 0) {
					r = 0;  g = 65535;  b = 0;
				} else if (strcmp(colorname, "blue") == 0) {
					r = 0;  g = 0;  b = 65535;
				} else {
					init_warning("No color named \"%s\" found, substituting gray",
								 colorname);
					r = g = b = i * 5000;
				}
			} else if (numberp(car(color))) {
				/* The usual case is a triple of numbers. */
				r = c_number(car(color));
				g = c_number(cadr(color));
				b = c_number(car(cddr(color)));
			} else {
				init_warning("palette color is not a name or set of numbers, substituting black");
				r = g = b = 0;
			}
			ctdata[i].rgb.red   = r;
			ctdata[i].rgb.green = g;
			ctdata[i].rgb.blue  = b;
		}
	} else {
		for (i = 0; i < len; ++i) {
			ctdata[i].value = img->rawpalette[i * 4];
			ctdata[i].rgb.red   = img->rawpalette[i * 4 + 1];
			ctdata[i].rgb.green = img->rawpalette[i * 4 + 2];
			ctdata[i].rgb.blue  = img->rawpalette[i * 4 + 3];
		}
	}
	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;
}
