executor/src/qColorMgr.c

1148 lines
30 KiB
C

/* Copyright 1994, 1996 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_qColorMgr[] =
"$Id: qColorMgr.c 63 2004-12-24 18:19:43Z ctm $";
#endif
/* color manager */
#include "rsys/common.h"
#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "MemoryMgr.h"
#include "rsys/smash.h"
#include "rsys/mman.h"
#include "rsys/cquick.h"
#include "rsys/rgbutil.h"
#include <syn68k_public.h>
#include "rsys/blockinterrupts.h"
#include "rsys/host.h"
#include "rsys/vdriver.h"
#include "rsys/dirtyrect.h"
INTEGER ROMlib_qd_error;
P0 (PUBLIC pascal trap, INTEGER, QDError)
{
/* #warning "make QDError work for real" */
return ROMlib_qd_error;
}
P0 (PUBLIC pascal trap, LONGINT, GetCTSeed)
{
static LONGINT seed = minSeed;
return seed ++;
}
PRIVATE uint32
itab_base_size (int res)
{
uint32 retval;
retval = offsetof (ITab, iTTable) + ((1 << (3 * res)) *
sizeof ((ITab *) 0)->iTTable[0]);
return retval;
}
#define ITABLE_HASH_SIZE 512
#if defined (ITABLE_HASH_SIZE)
/* TODO: look for better hash function */
PRIVATE int
itable_hash (RGBColor *rgbp, int resolution)
{
uint32 hash;
int retval;
uint16 red, green, blue;
red = CW (rgbp->red) >> (16 - resolution);
green = CW (rgbp->green) >> (16 - resolution);
blue = CW (rgbp->blue) >> (16 - resolution);
switch (resolution)
{
case 3:
#if ITABLE_HASH_SIZE != 512
#error fix hash funciton
#endif
hash = (red << 6) | (green << 3) | blue;
break;
default:
warning_unexpected ("resolution = %d", resolution);
/* FALL THROUGH */
case 4:
hash = ((blue&8) << 5) | ((red^((blue>>1)&1)) << 4) | (green ^ (blue&3));
break;
case 5:
hash = ((red&0xf << 5) | green) ^ ((blue<<2)|((red&8)>>2));
break;
}
retval = hash;
return retval;
}
/*
*/
PRIVATE uint32
rgb_diff (RGBColor *rgb1p, RGBColor *rgb2p)
{
uint32 retval;
/*
* NOTE: the surprising "& 0xff" below was determined empirically by
* using NIH Image to plot histograms of the "Bone Marrow" image that
* is on the CD-ROM that Chris Russ gave to me. Chris is the person
* who pointed out that histograms weren't working, and it took a while
* to see that not only did we need to fix Color2Index in general, but
* that we had to be careful to match like a Mac matches. Bone Marrow
* uses a grayscale clut whose low byte of each rgb component is the
* same as the high byte, instead of zero. If we don't mask that info
* off, then we wind up with a different histogram than the Mac produces.
* From my point of view, the whole way NIH Image and the Mac work
* together is questionable, since the conversion from the non-standard
* CLUT that Bone Marrow uses to a standard gray scale clut is dicey.
* After all, 0x8080 is just as far away from 0x8000 as it is from
* 0x8100, so counting on it going to a particular value seems risky.
*/
retval = (ABS (CW (rgb1p->red & 0xff) - CW (rgb2p->red & 0xff)) +
ABS (CW (rgb1p->green & 0xff) - CW (rgb2p->green & 0xff)) +
ABS (CW (rgb1p->blue & 0xff) - CW (rgb2p->blue & 0xff)));
return retval;
}
#endif
/* FIXME: may have to round instead of cleave off the low bits */
#define RGB_TO_ITAB_INDEX(rgb, resolution) \
(((CW ((rgb)->red) >> (16 - (resolution))) << (2 * (resolution))) \
| ((CW ((rgb)->green) >> (16 - (resolution))) << (resolution)) \
| ((CW ((rgb)->blue) >> (16 - (resolution)))))
static uint32
ROMlib_search_proc (RGBColor *rgb)
{
GDHandle gd;
int pixel_size;
uint32 retval;
gd = MR (TheGDevice);
pixel_size = PIXMAP_PIXEL_SIZE (GD_PMAP (gd));
if (pixel_size > 8)
{
const rgb_spec_t *rgb_spec;
rgb_spec = pixel_size == 16 ? &mac_16bpp_rgb_spec
: &mac_32bpp_rgb_spec;
retval = (*rgb_spec->rgbcolor_to_pixel) (rgb_spec, rgb, TRUE);
if (pixel_size == 16)
retval = CW (retval);
else if (pixel_size == 32)
retval = CL (retval);
else
gui_fatal ("unknown pixel size `%d'", pixel_size);
}
else
{
ITabHandle inverse_table;
int resolution;
inverse_table = GD_ITABLE (gd);
resolution = ITAB_RES (inverse_table);
retval = ITAB_TABLE (inverse_table)[RGB_TO_ITAB_INDEX (rgb, resolution)];
#if defined (ITABLE_HASH_SIZE)
{
CTabHandle ctabh;
ColorSpec *cspecp;
ctabh = PIXMAP_TABLE ( GD_PMAP (gd));
cspecp = CTAB_TABLE (ctabh);
if (memcmp (rgb, &cspecp[retval].rgb, sizeof *rgb) != 0)
{
int i;
uint8 *hash_table;
uint32 candidate;
uint32 cur_diff, new_diff;
boolean_t done_zero;
hash_table = ((uint8 *) STARH (inverse_table)
+ itab_base_size (resolution));
i = itable_hash (rgb, resolution);
cur_diff = rgb_diff (rgb, &cspecp[retval].rgb);
new_diff = 1;
done_zero = FALSE;
while (new_diff && (candidate = hash_table[i]) != 0)
{
if (candidate == 1 && !done_zero)
{
candidate = 0;
--i;
done_zero = TRUE;
}
new_diff = rgb_diff (rgb, &cspecp[candidate].rgb);
if (new_diff < cur_diff)
{
retval = candidate;
cur_diff = new_diff;
}
i = (i + 1) % ITABLE_HASH_SIZE;
}
}
}
#endif
}
return retval;
}
P1 (PUBLIC pascal trap, LONGINT, Color2Index,
RGBColor *, rgb)
{
SProcHndl t;
LONGINT success_p;
/* default */
LONGINT position = 0;
GDHandle gdev;
ITabHandle gd_itab;
CTabHandle gd_ctab;
/* rebuild the inverse table for the current graphics device
if the color table is newer than the inverse table */
gdev = MR (TheGDevice);
gd_ctab = PIXMAP_TABLE (GD_PMAP (gdev));
gd_itab = GD_ITABLE (gdev);
if (CTAB_SEED_X (gd_ctab) != ITAB_SEED_X (gd_itab))
MakeITable (gd_ctab, gd_itab, GD_RES_PREF (gdev));
for (t = GD_SEARCH_PROC (MR (TheGDevice)), success_p = FALSE;
t && !success_p;)
{
BOOLEAN (*search_fn) ();
search_fn = (BOOLEAN (*) ()) HxP (t, srchProc);
/* fetch the next before calling the searchproc,
since it can relocate the current `t' */
t = HxP (t, nxtSrch);
{
M68kReg save_regs[16];
memcpy (save_regs, cpu_state.regs, 16 * sizeof cpu_state.regs[0]);
EM_A7 -= 128;
PUSHADDR ((LONGINT) US_TO_SYN68K(rgb));
PUSHADDR ((LONGINT) US_TO_SYN68K(&position));
CALL_EMULATOR ((syn68k_addr_t) US_TO_SYN68K(search_fn));
/* success_p = EM_D0; */
success_p = POPUB ();
memcpy (cpu_state.regs, save_regs, 16 * sizeof cpu_state.regs[0]);
}
#if 0
/* return value from the search procedure is a BOOLEAN, ignore
all but the low byte */
success_p &= 0xFF;
#endif
}
if (! success_p)
position = ROMlib_search_proc (rgb);
else
position = CL (position); /* They filled this in in big endian order. */
return position;
}
P2 (PUBLIC pascal trap, void, Index2Color,
LONGINT, index,
RGBColor *, rgb)
{
CTabHandle ctab = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
*rgb = CTAB_TABLE (ctab)[index].rgb;
}
P1 (PUBLIC pascal trap, void, InvertColor,
RGBColor *, rgb)
{
/* #warning "only use default InvertColor complement procedure" */
/* one's complement */
rgb->red = ~rgb->red;
rgb->green = ~rgb->green;
rgb->blue = ~rgb->blue;
}
P1 (PUBLIC pascal trap, BOOLEAN, RealColor,
RGBColor *, rgb)
{
GDHandle gdev;
ITabHandle inverse_table;
CTabHandle table;
RGBColor *closest;
int resolution;
unsigned short mask;
int index;
/* rebuild the inverse table for the current graphics device
if the color table is newer than the inverse table */
gdev = MR (TheGDevice);
table = PIXMAP_TABLE (GD_PMAP (gdev));
inverse_table = GD_ITABLE (gdev);
if (CTAB_SEED_X (table) != ITAB_SEED_X (inverse_table))
MakeITable (table, inverse_table, GD_RES_PREF (gdev));
resolution = ITAB_RES (inverse_table);
index = ITAB_TABLE (inverse_table)[RGB_TO_ITAB_INDEX (rgb, resolution)];
closest = &(CTAB_TABLE (table)[index].rgb);
/* high `resolution' bits */
mask = CW (((1 << resolution) - 1) << (16 - resolution));
return !(((rgb->red ^ closest->red) & mask)
|| ((rgb->green ^ closest->green) & mask)
|| ((rgb->blue ^ closest->blue) & mask));
}
P3 (PUBLIC pascal trap, void, GetSubTable,
CTabHandle, in_ctab,
INTEGER, resolution,
CTabHandle, target_ctab)
{
/* cached itable from the last transaction */
static ITabHandle cached_itab = NULL;
GDHandle gdev;
ITabHandle itab = NULL;
PixMapHandle gd_pmap;
int i;
gdev = MR (TheGDevice);
gd_pmap = GD_PMAP (gdev);
if (!target_ctab)
{
ITabHandle t = GD_ITABLE (gdev);
target_ctab = PIXMAP_TABLE (gd_pmap);
if (resolution == ITAB_RES (t))
{
/* rebuild the inverse table for the current graphics device
if the color table is newer than the inverse table */
if (CTAB_SEED_X (target_ctab) != ITAB_SEED_X (t))
MakeITable (target_ctab, t, GD_RES_PREF (gdev));
itab = t;
}
}
if (! itab)
{
if (cached_itab
&& ITAB_SEED_X (cached_itab) == CTAB_SEED_X (target_ctab)
&& resolution == ITAB_RES (cached_itab))
itab = cached_itab;
else
{
/* if we haven't allocated a cached itab; do so now. make
sure to do it out of the system zone, since we will keep
this itab around forever */
if (cached_itab == NULL)
ZONE_SAVE_EXCURSION
(SysZone,
{
cached_itab = (ITabHandle) NewHandle ((Size) sizeof (ITab));
});
itab = cached_itab;
MakeITable (target_ctab, itab, resolution);
}
}
LOCK_HANDLE_EXCURSION_2
(in_ctab, target_ctab,
{
CTabHandle gdev_ctab_save = PIXMAP_TABLE_X (gd_pmap);
ITabHandle gdev_itab_save = GD_ITABLE_X (gdev);
/* pull tables into locals for easy access */
ColorSpec *in_ctab_table = CTAB_TABLE (in_ctab);
/* ColorSpec *target_ctab_table = CTAB_TABLE (target_ctab); */
/* int gd_ctab_p = CTAB_FLAGS_X (target_ctab) & CTAB_GDEVICE_BIT_X; */
PIXMAP_TABLE_X (gd_pmap) = RM (target_ctab);
GD_ITABLE_X (gdev) = RM (itab);
for (i = CTAB_SIZE (in_ctab); i >= 0; i --)
{
ColorSpec *color = &in_ctab_table[i];
INTEGER ctab_index;
ctab_index = Color2Index (&color->rgb);
/* inverse table maps rgb color space to an index
within the target color table, not to a value in the
color table */
/*
if (gd_ctab_p)
color->value = CW (ctab_index);
else
color->value = target_ctab_table[ctab_index].value;
color->rgb = target_ctab_table[ctab_index].rgb;
*/
color->value = CW (ctab_index);
}
PIXMAP_TABLE_X (gd_pmap) = gdev_ctab_save;
GD_ITABLE_X (gdev) = gdev_itab_save;
});
}
int
average_color (GDHandle gd,
RGBColor *c1, RGBColor *c2, int ratio,
RGBColor *out)
{
RGBColor in_between;
int c1_index, c2_index, in_between_index;
PixMapHandle gd_pmap;
int gd_pixel_size;
gd_pmap = GD_PMAP (gd);
gd_pixel_size = PIXMAP_PIXEL_SIZE (gd_pmap);
in_between.red = CW (((CW (c1->red) * ratio)
+ (CW (c2->red) * (65535 - ratio))) / 65535);
in_between.green = CW (((CW (c1->green) * ratio)
+ (CW (c2->green) * (65535 - ratio))) / 65535);
in_between.blue = CW (((CW (c1->blue) * ratio)
+ (CW (c2->blue) * (65535 - ratio))) / 65535);
if (gd_pixel_size <= 8)
{
CTabHandle gd_ctab;
ITabHandle gd_itab;
unsigned char *itab_table;
int itab_res;
gd_ctab = PIXMAP_TABLE (gd_pmap);
gd_itab = GD_ITABLE (gd);
if (CTAB_SEED_X (gd_ctab) != ITAB_SEED_X (gd_itab))
MakeITable (gd_ctab, gd_itab, GD_RES_PREF (gd));
itab_res = ITAB_RES (gd_itab);
itab_table = ITAB_TABLE (gd_itab);
c1_index = itab_table[RGB_TO_ITAB_INDEX (c1, itab_res)];
c2_index = itab_table[RGB_TO_ITAB_INDEX (c2, itab_res)];
in_between_index = itab_table[RGB_TO_ITAB_INDEX (&in_between, itab_res)];
}
else
{
const rgb_spec_t *rgb_spec;
rgb_spec = pixmap_rgb_spec (STARH (gd_pmap));
c1_index = (*rgb_spec->rgbcolor_to_pixel) (rgb_spec, c1, TRUE);
c2_index = (*rgb_spec->rgbcolor_to_pixel) (rgb_spec, c2, TRUE);
in_between_index = (*rgb_spec->rgbcolor_to_pixel) (rgb_spec, &in_between,
TRUE);
}
if (c1_index != c2_index
&& (in_between_index == c1_index
|| in_between_index == c2_index))
return FALSE;
else
{
*out = in_between;
return TRUE;
}
}
P3 (PUBLIC pascal trap, BOOLEAN, GetGray,
GDHandle, gdev,
RGBColor *, bk, RGBColor *, fg)
{
return average_color (gdev, bk, fg, 0x8000, fg);
}
#define ENQUEUE(x) ({ const unsigned _q_elt_ = (x); \
if (! index_in_queue_p[_q_elt_]) \
{ \
queue[head] = _q_elt_; \
index_in_queue_p[_q_elt_] = TRUE; \
head = (head + 1) & (itab_elt_count - 1); \
} \
})
#define DEQUEUE() ({ const unsigned _q_elt_ = queue[tail]; \
tail = (tail + 1) & (itab_elt_count - 1); \
index_in_queue_p[_q_elt_] = FALSE; \
_q_elt_; \
})
#define SET_RGB_ERROR(ix, err) (rgb_error[ix] = (err))
/* These are the parameters to rgb_error_for_res_*. Because they
* remain fixed during loops, we put them in globals.
*/
static unsigned current_red, current_green, current_blue;
#define RGB_ERROR_FOR_RESOLUTION_FUNC(RES) \
static ULONGINT \
rgb_error_for_res_##RES (unsigned components) \
{ \
unsigned rc, bc, gc; \
ULONGINT error; \
const unsigned mask = (1 << (RES)) - 1; \
const unsigned mid_bit = (0x8000 >> (RES)); \
\
bc = ((components & mask) << (16 - (RES))) | mid_bit; \
error = ABS ((long) (bc - current_blue)); \
\
gc = ((components & (mask << (RES))) << (16 - (RES) * 2)) | mid_bit; \
error += ABS ((long) (gc - current_green)); \
\
rc = ((components & (mask << ((RES) * 2))) << (16 - (RES) * 3)) | mid_bit; \
error += ABS ((long) (rc - current_red)); \
\
return error; \
}
RGB_ERROR_FOR_RESOLUTION_FUNC (3)
RGB_ERROR_FOR_RESOLUTION_FUNC (4)
RGB_ERROR_FOR_RESOLUTION_FUNC (5)
static int offsets[3][26];
static const int basis_offsets[26][3] =
{
{ -1, -1, -1 },
{ -1, -1, 0 },
{ -1, -1, 1 },
{ -1, 0, -1 },
{ -1, 0, 0 },
{ -1, 0, 1 },
{ -1, 1, -1 },
{ -1, 1, 0 },
{ -1, 1, 1 },
{ 0, -1, -1 },
{ 0, -1, 0 },
{ 0, -1, 1 },
{ 0, 0, -1 },
#if 0
{ 0, 0, 0 },
#endif
{ 0, 0, 1 },
{ 0, 1, -1 },
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 1, -1, -1 },
{ 1, -1, 0 },
{ 1, -1, 1 },
{ 1, 0, -1 },
{ 1, 0, 0 },
{ 1, 0, 1 },
{ 1, 1, -1 },
{ 1, 1, 0 },
{ 1, 1, 1 },
};
static void
init_offsets (void)
{
int i, resolution;
for (resolution = 3; resolution <= 5; resolution ++)
{
int *offset;
offset = offsets[resolution - 3];
for (i = 0; i < 26; i ++)
{
const int *basis_offset;
basis_offset = basis_offsets[i];
offset[i] = (basis_offset[0]
+ (basis_offset[1] << resolution)
+ (basis_offset[2] << (2 * resolution)));
}
}
}
#if defined (ITABLE_HASH_SIZE)
PRIVATE void
add_hash_table (CTabHandle color_table, ITabHandle inverse_table,
int resolution)
{
int ctab_size;
uint8 *hash_tablep;
int i;
hash_tablep = ((uint8 *) STARH (inverse_table)
+ itab_base_size (resolution));
memset (hash_tablep, 0, ITABLE_HASH_SIZE);
ctab_size = CTAB_SIZE (color_table);
for (i = 0; i < ctab_size; ++i)
{
RGBColor *ctabcolorp, *itabcolorp;
int itab_index, index;
ctabcolorp = &CTAB_TABLE (color_table)[i].rgb;
itab_index = RGB_TO_ITAB_INDEX (ctabcolorp, resolution);
index = ITAB_TABLE (inverse_table)[itab_index];
itabcolorp = &CTAB_TABLE (color_table)[index].rgb;
if (memcmp (ctabcolorp, itabcolorp, sizeof *ctabcolorp) != 0)
{
int j;
j = itable_hash (ctabcolorp, resolution);
while (hash_tablep [j])
j = (j + 1) % ITABLE_HASH_SIZE;
hash_tablep [j] = i ? i : 1; /* don't ever put in zero */
}
}
}
#endif
P3 (PUBLIC pascal trap, void, MakeITable,
CTabHandle, color_table,
ITabHandle, inverse_table,
INTEGER, resolution)
{
/* this also counts the number of elements in the queue */
int itab_elt_count;
unsigned short *queue;
char *index_in_queue_p;
ULONGINT *rgb_error;
boolean_t queue_starts_empty_p;
int head, tail;
int i;
int ctab_size;
ULONGINT (*rgb_error_func) (unsigned);
unsigned char *itab_table;
const int *offset;
ColorSpec color_for_index[256];
{
static boolean_t been_here_p = FALSE;
if (! been_here_p)
{
init_offsets ();
been_here_p = TRUE;
}
}
/* We are supposed to override a resolution of zero with the
* resolution of TheGDevice (IMV-142). */
if (resolution == 0)
resolution = GD_RES_PREF (MR (TheGDevice));
/* We are supposed to override a color_table of NULL with the color
* table of TheGDevice (IMV-142). */
if (color_table == NULL)
color_table = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
/* We are supposed to override an inverse_table of NULL with the
* inverse table of TheGDevice (IMV-142). */
if (inverse_table == NULL)
{
inverse_table = GD_ITABLE (MR (TheGDevice));
if (!inverse_table)
{
inverse_table = (ITabHandle) NewHandle (0);
GD_ITABLE_X (MR (TheGDevice)) = RM (inverse_table);
}
}
gui_assert (resolution >= 3 && resolution <= 5);
/* Set up the function to compute rgb deltas. */
if (resolution == 3)
rgb_error_func = rgb_error_for_res_3;
else if (resolution == 4)
rgb_error_func = rgb_error_for_res_4;
else if (resolution == 5)
rgb_error_func = rgb_error_for_res_5;
else
gui_abort ();
itab_elt_count = 1 << (3 * resolution);
offset = offsets[resolution - 3];
/* reallocate `inverse_table' to the appropriate size for the
current resolution */
{
Size new_size;
new_size = itab_base_size (resolution);
#if defined(ITABLE_HASH_SIZE)
new_size += ITABLE_HASH_SIZE;
#endif
SetHandleSize ((Handle) inverse_table, new_size);
}
ITAB_RES_X (inverse_table) = CW (resolution);
itab_table = ITAB_TABLE (inverse_table);
queue = (unsigned short *) alloca (sizeof (unsigned short) * itab_elt_count);
head = tail = 0;
index_in_queue_p = alloca (sizeof *index_in_queue_p * itab_elt_count);
memset (index_in_queue_p, '\000', sizeof *index_in_queue_p * itab_elt_count);
/* Allocate an array for RGB errors and set all entries to the maximum. */
rgb_error = (ULONGINT *) alloca (sizeof (ULONGINT) * itab_elt_count);
memset (rgb_error, ~0, sizeof (ULONGINT) * itab_elt_count);
/* Initialize to a non-0 and non-0xFF value. */
memset (color_for_index, 0x80, sizeof color_for_index);
queue_starts_empty_p = TRUE;
ctab_size = CTAB_SIZE (color_table);
for (i = ctab_size; i >= 0; i --)
{
ColorSpec *color = &CTAB_TABLE (color_table)[i];
int inverse_table_index;
ULONGINT error;
unsigned char new_value;
/* don't include reserved colors in the inverse table */
if (color->value & CTAB_RESERVED_BIT_X)
continue;
/* We always want to enqueue this color, even if it isn't the
* lowest error match for the starting itab entry. It may become
* the lowest error color for neighboring itab entries.
*/
inverse_table_index = RGB_TO_ITAB_INDEX (&(color->rgb), resolution);
ENQUEUE (inverse_table_index);
queue_starts_empty_p = FALSE;
/* Figure out what the color index is. */
if (CTAB_FLAGS_X (color_table) & CTAB_GDEVICE_BIT_X)
new_value = i;
else
new_value = CW (color->value);
/* Save away the color for this index, in native endian byte order. */
color_for_index[new_value].rgb.red = CW (color->rgb.red);
color_for_index[new_value].rgb.green = CW (color->rgb.green);
color_for_index[new_value].rgb.blue = CW (color->rgb.blue);
current_red = color_for_index[new_value].rgb.red;
current_blue = color_for_index[new_value].rgb.blue;
current_green = color_for_index[new_value].rgb.green;
/* Compute the error for this match. */
error = (*rgb_error_func) (inverse_table_index);
/* If this beats the best error for this square so far, make
* that be the new error value.
*/
if (error < rgb_error[inverse_table_index])
{
itab_table[inverse_table_index] = new_value;
SET_RGB_ERROR (inverse_table_index, error);
}
}
if (queue_starts_empty_p)
{
/* Some degenerate color tables can cause us to enqueue nothing
* at all. It's not clear what to do in the case, but we
* do _not_ want to start in on the do...while loop below.
* That can cause mystery smashage deaths, because we'd look
* at uninitialized queue elements. This was happening
* when we were letting FattyBearDemo try a SetDepth to 0 bpp.
*/
warning_unexpected ("Empty queue!");
}
else
{
do
{
int inverse_table_index;
unsigned char ctab_index;
const RGBColor *current_color;
int off;
inverse_table_index = DEQUEUE ();
ctab_index = itab_table[inverse_table_index];
/* Stash the current color away in some globals, outside the loop. */
current_color = &color_for_index[ctab_index].rgb;
current_red = current_color->red; /* native endian! */
current_green = current_color->green; /* native endian! */
current_blue = current_color->blue; /* native endian! */
/* Find the adjacent rgb coordinates to this pixel. If we can
* beat the error found at each neighbor, then we spread out to
* that neighbor.
*/
for (off = 25; off >= 0; off--)
{
int next_itab_index;
ULONGINT error;
/* compute the next adjacent index */
next_itab_index = inverse_table_index + offset[off];
if ((unsigned) next_itab_index < (unsigned) itab_elt_count)
{
/* Compute the error we'll create by taking this step. */
error = (*rgb_error_func) (next_itab_index);
/* Did we improve on the best error for that entry? */
if (error < rgb_error[next_itab_index])
{
ENQUEUE (next_itab_index);
itab_table[next_itab_index] = ctab_index;
rgb_error[next_itab_index] = error;
}
}
}
check_virtual_interrupt ();
}
while (tail != head);
}
/* If the first or last entries in the table are black and white,
* make sure their entries map to them. This wouldn't be necessary
* if we were able to do a more fine-grained resolution; however, some
* programs (like FattyBearDemo) expect that black will definitely
* map to the last entry in the color table, even if there's more
* than one black or near-black color.
*/
for (i = 0; i <= ctab_size; i += ctab_size ?: 1)
{
ULONGINT color_sum;
color_sum = (color_for_index[i].rgb.red
+ color_for_index[i].rgb.green
+ color_for_index[i].rgb.blue);
if (color_sum == 0xFFFF * 3)
itab_table[itab_elt_count - 1] = i;
else if (color_sum == 0x0000 * 3)
itab_table[0] = i;
}
#if defined (ITABLE_HASH_SIZE)
add_hash_table (color_table, inverse_table, resolution);
#endif
ITAB_SEED_X (inverse_table) = CTAB_SEED_X (color_table);
}
P2 (PUBLIC pascal trap, void, ProtectEntry,
INTEGER, index, BOOLEAN, protect)
{
GDHandle gdev;
ColorSpec *entry;
gdev = MR (TheGDevice);
entry = &CTAB_TABLE (PIXMAP_TABLE (GD_PMAP (gdev)))[index];
if (protect)
{
if (entry->value & CTAB_PROTECTED_BIT_X)
/* #warning "set error bit here" */
return;
/* mark this entry as protected; and set the
low byte of the value field with the current
device id */
entry->value = CTAB_PROTECTED_BIT_X | (GD_ID_X (gdev) & CWC (0xFF));
}
else
{
/* clear the protected bit and id fields of the value */
entry->value = 0;
}
/* this does not effect color lookup, so the seed of the
current graphics device's colortable is unchanged */
}
P2 (PUBLIC pascal trap, void, ReserveEntry,
INTEGER, index, BOOLEAN, reserve)
{
GDHandle gdev;
CTabHandle ctab;
ColorSpec *entry;
INTEGER old_value;
gdev = MR (TheGDevice);
ctab = PIXMAP_TABLE (GD_PMAP (gdev));
entry = &CTAB_TABLE (ctab)[index];
old_value = entry->value;
if (reserve)
{
if (entry->value & CTAB_RESERVED_BIT_X)
{
ROMlib_qd_error = cProtectErr;
return;
}
/* mark this entry as reserved; and set the
low byte of the value field with the current
device id */
entry->value = CTAB_RESERVED_BIT_X | (GD_ID_X (gdev) & CWC (0xFF));
}
else
{
/* clear the entry */
entry->value &= ~CTAB_RESERVED_BIT_X;
}
/* success */
ROMlib_qd_error = noErr;
/* Only change the seed when necessary. */
if (old_value != entry->value)
CTAB_SEED_X (ctab) = CL (GetCTSeed ());
}
P3 (PUBLIC pascal trap, void, SetEntries,
INTEGER, start, INTEGER, count,
ColorSpec *, atable /* cSpecArray, atable */)
{
/* ##### figure out exactly what the mac does */
GDHandle gd;
CTabHandle ctab;
ColorSpec *ctab_table;
int ctab_changed_p, i;
RGBColor *r1, *r2;
/* for calling `vdriver_set_colors ()' */
int first_color, num_colors;
gd = MR (TheGDevice);
if (GD_TYPE_X (gd) != CWC (clutType))
/* #### return error code? */
return;
ctab = PIXMAP_TABLE (GD_PMAP (gd));
ctab_table = CTAB_TABLE (ctab);
ctab_changed_p = FALSE;
if (start >= 0)
{
for (i = 0; i <= count; i ++)
{
r1 = &ctab_table[start + i].rgb;
r2 = &atable[i].rgb;
if (r1->red != r2->red
|| r1->green != r2->green
|| r1->blue != r2->blue)
{
*r1 = *r2;
ctab_changed_p = TRUE;
}
}
first_color = start;
num_colors = count + 1;
}
else
{
int min = 10000, max = -10000;
for (i = 0; i <= count; i ++)
{
int index = CW (atable[i].value);
if (index < min)
min = index;
if (index > max)
max = index;
r1 = &ctab_table[index].rgb;
r2 = &atable[i].rgb;
if (r1->red != r2->red
|| r1->green != r2->green
|| r1->blue != r2->blue)
{
*r1 = *r2;
ctab_changed_p = TRUE;
}
}
first_color = min;
num_colors = max - min + 1;
}
/* We need this hack in here so that Wolfenstein 3D will work.
* If we don't always update the real color table, the colors
* are simply wrong.
*/
ctab_changed_p = TRUE;
if (ctab_changed_p)
{
CTAB_SEED_X (ctab) = CL (GetCTSeed ());
if (gd == MR (MainDevice))
{
if (num_colors > 0)
{
dirty_rect_update_screen ();
vdriver_set_colors (first_color, num_colors,
&ctab_table[first_color]);
}
}
}
}
P1 (PUBLIC pascal trap, void, AddSearch,
ProcPtr, searchProc)
{
GDHandle gdev;
SProcHndl search_list_elt;
gdev = MR (TheGDevice);
search_list_elt = (SProcHndl) NewHandle (sizeof (SProcRec));
HxX (search_list_elt, srchProc) = RM (searchProc);
HxX (search_list_elt, nxtSrch) = GD_SEARCH_PROC_X (gdev);
GD_SEARCH_PROC_X (gdev) = RM (search_list_elt);
/* Invalidate all color conversion tables. */
ROMlib_invalidate_conversion_tables ();
}
P1 (PUBLIC pascal trap, void, AddComp,
ProcPtr, compProc)
{
warning_unimplemented (NULL_STRING);
}
P1 (PUBLIC pascal trap, void, DelSearch,
ProcPtr, searchProc)
{
GDHandle gdev;
SProcHndl s, prev;
gdev = MR (TheGDevice);
prev = NULL;
for (s = GD_SEARCH_PROC (gdev); s != NULL; s = HxP (s, nxtSrch))
{
if (HxX (s, srchProc) == RM (searchProc))
{
if (prev == NULL)
GD_SEARCH_PROC_X (gdev) = HxX (s, nxtSrch);
else
HxX (prev, nxtSrch) = HxX (s, nxtSrch);
DisposHandle ((Handle) s);
/* Invalidate all color conversion tables. */
ROMlib_invalidate_conversion_tables ();
break;
}
else
prev = s;
}
if (s == NULL)
warning_unimplemented (NULL_STRING);
}
P1 (PUBLIC pascal trap, void, DelComp,
ProcPtr, compProc)
{
warning_unimplemented (NULL_STRING);
}
P1 (PUBLIC pascal trap, void, SetClientID,
INTEGER, id)
{
GD_ID_X (MR (TheGDevice)) = CW (id);
}
P3 (PUBLIC pascal trap, void, SaveEntries, CTabHandle, src, CTabHandle, result,
ReqListRec *, selection)
{
int req_size, src_ctab_size;
int req_error_p = FALSE;
int i;
if (src == NULL)
src = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
src_ctab_size = CTAB_SIZE (src);
req_size = CW (selection->reqLSize);
SetHandleSize ((Handle) result,
CTAB_STORAGE_FOR_SIZE (req_size));
CTAB_SIZE_X (result) = CW (req_size);
/* #### should this set the color table seed? */
CTAB_SEED_X (result) = CL (GetCTSeed ());
CTAB_FLAGS_X (result) = CW (0);
for (i = 0; i <= req_size; i ++)
{
int req_index = CW (selection->reqLData[i]);
if (req_index >= 0
&& req_index <= src_ctab_size)
CTAB_TABLE (result)[i] = CTAB_TABLE (src)[req_index];
else
{
selection->reqLData[i] = CWC (colReqErr);
req_error_p = TRUE;
}
}
if (req_error_p)
{
/* #### does SaveEntries return colReqErr if errors occured */
ROMlib_qd_error = colReqErr;
}
}
P3 (PUBLIC pascal trap, void, RestoreEntries, CTabHandle, src, CTabHandle, dst,
ReqListRec *, selection)
{
int req_size, dst_ctab_size;
int req_error_p = FALSE;
int i;
if (dst == NULL)
dst = PIXMAP_TABLE (GD_PMAP (MR (TheGDevice)));
dst_ctab_size = CTAB_SIZE (dst);
req_size = CW (selection->reqLSize);
for (i = 0; i < req_size; i ++)
{
int req_index = CW (selection->reqLData[i]);
if (req_index >= 0
&& req_index <= dst_ctab_size)
CTAB_TABLE (dst)[req_index] = CTAB_TABLE (src)[i];
else
{
selection->reqLData[i] = CWC (colReqErr);
req_error_p = TRUE;
}
}
if (req_error_p)
{
/* #warning does SaveEntries return colReqErr if errors occured */
ROMlib_qd_error = colReqErr;
}
}