/* 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 #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; } }