/***************************************************************************** * * Inverse color map code, from Graphics Gems II. * * this code builds a inverse map table, used for color mapping a * hi color (bpp > 8) image down to 4bpp or 8bpp * * a inverse table is a 32K table that mapps a RGB 555 value (a WORD) to * a palette index (a BYTE) * * public functions: * MakeITable - makes a inverse table to any palette * * non-public functions: * inv_cmap - work horse for MakeITable * MakeITableVGA - makes a inverse table to the VGA colors * MakeITable256 - makes a inverse table to a uniform 6 level palette * * Created: Feb 20 1994, ToddLa * * Copyright (c) 1994-1999 Microsoft Corporation *****************************************************************************/ #include "precomp.hxx" #ifdef WIN32 #define _huge #undef GlobalAllocPtr #undef GlobalFreePtr #define GlobalAllocPtr(f, cb) (LPVOID)GlobalAlloc(((f) & ~GMEM_MOVEABLE) | GMEM_FIXED, cb) #define GlobalFreePtr(p) GlobalFree((HGLOBAL)(p)) #endif typedef DWORD _huge *PDIST; typedef BYTE FAR *PIMAP; typedef struct {BYTE r,g,b,x;} RGBX; void inv_cmap( int colors, RGBX FAR *colormap, int bits, PDIST dist_buf, PIMAP rgbmap ); BOOL MakeITableMono(PIMAP lpITable); BOOL MakeITable256(PIMAP lpITable); BOOL MakeITableVGA(PIMAP lpITable); BOOL MakeITableDEF(PIMAP lpITable); PBYTE gpDefITable = NULL; /***************************************************************************** * * MakeITable * * build a 32k color inverse table to a palette * * lpITable points to a 32K table to hold inverse table * prgb points to the palette RGBs to build table from. * nColors number of RGBs * * if prgb is NULL build a special inverse table, to a fixed color table * * nColors = 2 build to mono colors (black,white) * nColors = 16 build to VGA colors. * nColors = 20 build to GDI DEFAULT_PALETTE * nColors = 216 build to 6*6*6 colors. * *****************************************************************************/ BOOL MakeITable(PIMAP lpITable, RGBX FAR *prgb, int nColors) { PDIST lpDistBuf; PIMAP iTable = lpITable; // // handle special color tables. // if (prgb == NULL) { switch (nColors) { case 2: return MakeITableMono(lpITable); case 16: return MakeITableVGA(lpITable); case 20: return MakeITableDEF(lpITable); case 256: return MakeITable256(lpITable); default: return FALSE; } } // // We need to grab the global palette semaphore here // because inv_cmap is not thread-safe (it uses all // kinds of global variables) // BOOL result = FALSE; SEMOBJ semo(ghsemPalette); // // Check if palette is default palette. If it is, use the // cached default palette iTable. Create cache as appropriate. // if(nColors >= 20) { int i; ULONG * pulDef = (ULONG *) logDefaultPal.palPalEntry; ULONG * pulSrc = (ULONG *) prgb; for(i = 0; i < nColors; i++) if(pulSrc[i] != pulDef[i%20]) break; if(i == nColors) { if(gpDefITable != NULL) { RtlCopyMemory(lpITable, gpDefITable, 32768); return TRUE; } iTable = (PBYTE)PALLOCNOZ(32768,'tidG'); if(iTable == NULL) iTable = lpITable; nColors = 20; } } // // not a special table // lpDistBuf = (PDIST)PALLOCNOZ(32768l * sizeof(DWORD),'pmtG'); if (lpDistBuf != NULL) { inv_cmap(nColors,prgb,5,lpDistBuf, iTable); VFREEMEM(lpDistBuf); result = TRUE; if(iTable != lpITable) { RtlCopyMemory(lpITable, iTable, 32768); gpDefITable = iTable; } } else { if(iTable != lpITable) VFREEMEM(iTable); } return result; } /***************************************************************************** * * MakeITable256 * * build a 32k color inverse table to a 3-3-2 palette * *****************************************************************************/ BOOL MakeITable256(PIMAP lpITable) { PIMAP pb; int r,g,b; pb = lpITable; for (r=0;r<32;r++) for (g=0;g<32;g++) for (b=0;b<32;b++) *pb++ = (((r & 0x1c) << 3)| (g & 0x1c) | ((b & 0x18) >> 3)); return TRUE; } //;----------------------------------------------------------------------------- //; vga_map //; ; //; Subdividing the RGB color cube twice along each axis yields 64 smaller ; //; cubes. A maximum of three VGA colors, and often only one VGA color, ; //; match (Euclidean distance) into each of the subdivided cubes. Therefore, ; //; this adaptive Eudclidean match is must faster than the traditional ; //; Euclidean match. ; //; ; //; Note: This table was built according to the VGA palette. The ; //; indices it returns will not be appropriate for all devices. Use a ; //; VgaTranslateMap to produce the final physical color index. ; //; (Example: GDI has indices 7 and 8 reversed. To use this code in GDI, ; //; enable the VgaTranslateMap and swap indices 7 and 8.) ; //; ; //; The index to this map is computed as follows given a 24-bit RGB. ; //; ; //; index = ((Red & 0xC0) >> 2) | ; //; ((Green & 0xC0) >> 4) | ; //; ((Blue & 0xC0) >> 6); ; //; ; //; Each entry is a word made up of four nibbles. The first nibble always ; //; contains a valid GDI VGA color index. The second and third nibbles ; //; contain valid GDI VGA color indices if they are non-zero. ; //; The fourth nibble is an optimization for sub-cubes 42 and 63. ; //; ; //; History: ; //; 23-February-1994 -by- Raymond E. Endres [rayen] ; //; Wrote it. ; //;----------------------------------------------------------------------------- static WORD vga_map[] = { 0x0000, // Index 0 r=0 g=0 b=0 0x0004, // r=1 g=0 b=0 0x0004, // r=2 g=0 b=0 0x000C, // r=3 g=0 b=0 0x0002, // r=0 g=1 b=0 0x0006, // r=1 g=1 b=0 0x0006, // r=2 g=1 b=0 0x00C6, // r=3 g=1 b=0 0x0002, // Index 8 r=0 g=2 b=0 0x0006, // r=1 g=2 b=0 0x0006, // r=2 g=2 b=0 0x00E6, // r=3 g=2 b=0 0x000A, // r=0 g=3 b=0 0x00A6, // r=1 g=3 b=0 0x00E6, // r=2 g=3 b=0 0x000E, // r=3 g=3 b=0 0x0001, // Index 16 r=0 g=0 b=1 0x0005, // r=1 g=0 b=1 0x0005, // r=2 g=0 b=1 0x00C5, // r=3 g=0 b=1 0x0003, // r=0 g=1 b=1 0x0008, // r=1 g=1 b=1 0x0008, // r=2 g=1 b=1 0x0C78, // r=3 g=1 b=1 0x0003, // Index 24 r=0 g=2 b=1 0x0008, // r=1 g=2 b=1 0x0078, // r=2 g=2 b=1 0x0E78, // r=3 g=2 b=1 0x00A3, // r=0 g=3 b=1 0x0A78, // r=1 g=3 b=1 0x0E78, // r=2 g=3 b=1 0x0E78, // r=3 g=3 b=1 0x0001, // Index 32 r=0 g=0 b=2 0x0005, // r=1 g=0 b=2 0x0005, // r=2 g=0 b=2 0x00D5, // r=3 g=0 b=2 0x0003, // r=0 g=1 b=2 0x0008, // r=1 g=1 b=2 0x0078, // r=2 g=1 b=2 0x0D78, // r=3 g=1 b=2 0x0003, // Index 40 r=0 g=2 b=2 0x0078, // r=1 g=2 b=2 0x0078, // r=2 g=2 b=2 1 0x0078, // r=3 g=2 b=2 0x00B3, // r=0 g=3 b=2 0x0B78, // r=1 g=3 b=2 0x0078, // r=2 g=3 b=2 0x00F7, // r=3 g=3 b=2 0x0009, // Index 48 r=0 g=0 b=3 0x0095, // r=1 g=0 b=3 0x00D5, // r=2 g=0 b=3 0x000D, // r=3 g=0 b=3 0x0093, // r=0 g=1 b=3 0x0978, // r=1 g=1 b=3 0x0D78, // r=2 g=1 b=3 0x0D78, // r=3 g=1 b=3 0x00B3, // Index 56 r=0 g=2 b=3 0x0B78, // r=1 g=2 b=3 0x0078, // r=2 g=2 b=3 0x00F7, // r=3 g=2 b=3 0x000B, // r=0 g=3 b=3 0x0B78, // r=1 g=3 b=3 0x00F7, // r=2 g=3 b=3 0x00F7 // r=3 g=3 b=3 1 }; static RGBX VGAColors[] = { 00, 00, 00, 00, 16, 00, 00, 00, 00, 16, 00, 00, 16, 16, 00, 00, 00, 00, 16, 00, 16, 00, 16, 00, 00, 16, 16, 00, 24, 24, 24, 00, 16, 16, 16, 00, 31, 00, 00, 00, 00, 31, 00, 00, 31, 31, 00, 00, 00, 00, 31, 00, 31, 00, 31, 00, 00, 31, 31, 00, 31, 31, 31, 00 }; /***************************************************************************** * * MapVGA * *****************************************************************************/ __inline BYTE MapVGA(BYTE r, BYTE g, BYTE b) { int i; // // build index into vga_map (r,g,b in range of 0-31) // i = ((b & 0x18) >> 3) | ((g & 0x18) >> 1) | ((r & 0x18) << 1); // // lookup in our "quick map" table // i = (int)vga_map[i]; if (i & 0xFFF0) { // // more than one color is close, do a eclidian search of // at most three colors. // int e1,e,n,n1; e1 = 0x7fffffff; while (i) { n = i & 0x000F; e = ((int)VGAColors[n].r - r) * ((int)VGAColors[n].r - r) + ((int)VGAColors[n].g - g) * ((int)VGAColors[n].g - g) + ((int)VGAColors[n].b - b) * ((int)VGAColors[n].b - b) ; if (e < e1) { n1 = n; e1 = e; } i = i >> 4; } return (BYTE)n1; } else { // // one one color matchs, we are done // return (BYTE)(i & 0x000F); } } /***************************************************************************** * * MakeITableVGA * * build a 32k color inverse table to the VGA colors * *****************************************************************************/ BOOL MakeITableVGA(PIMAP lpITable) { PIMAP pb; BYTE r,g,b; pb = lpITable; for (r=0;r<32;r++) for (g=0;g<32;g++) for (b=0;b<32;b++) *pb++ = (BYTE)MapVGA(r,g,b); return TRUE; } /***************************************************************************** * * MakeITableDEF * * build a 32k color inverse table to the DEFAULT_PALETTE * just builds a VGA ITable then bumps up values above 7 * *****************************************************************************/ BOOL MakeITableDEF(PIMAP pb) { int i; MakeITableVGA(pb); for (i=0;i<0x8000;i++) if (pb[i] >= 8) pb[i] += 240; return TRUE; } /***************************************************************************** * * MakeITableMono * * build a 32k color inverse table to black/white * *****************************************************************************/ BOOL MakeITableMono(PIMAP lpITable) { PIMAP pb; BYTE r,g,b; pb = lpITable; for (r=0;r<32;r++) for (g=0;g<32;g++) for (b=0;b<32;b++) *pb++ = (g/2 + (r+b)/4) > 15; return TRUE; } /***************************************************************** * TAG( inv_cmap ) * * Compute an inverse colormap efficiently. * Inputs: * colors: Number of colors in the forward colormap. * colormap: The forward colormap. * bits: Number of quantization bits. The inverse * colormap will have (2^bits)^3 entries. * dist_buf: An array of (2^bits)^3 long integers to be * used as scratch space. * Outputs: * rgbmap: The output inverse colormap. The entry * rgbmap[(r<<(2*bits)) + (g<> nbits; gcenter = colormap[cindex].g >> nbits; bcenter = colormap[cindex].b >> nbits; rdist = colormap[cindex].r - (rcenter * x + x/2); gdist = colormap[cindex].g - (gcenter * x + x/2); cdist = colormap[cindex].b - (bcenter * x + x/2); cdist = rdist*rdist + gdist*gdist + cdist*cdist; crinc = 2 * ((rcenter + 1) * xsqr - (colormap[cindex].r * x)); cginc = 2 * ((gcenter + 1) * xsqr - (colormap[cindex].g * x)); cbinc = 2 * ((bcenter + 1) * xsqr - (colormap[cindex].b * x)); /* Array starting points. */ cdp = dist_buf + rcenter * rstride + gcenter * gstride + bcenter; crgbp = rgbmap + rcenter * rstride + gcenter * gstride + bcenter; (void)redloop(); } } /* redloop -- loop up and down from red center. */ int redloop() { int detect; int r, i = cindex; int first; long txsqr = xsqr + xsqr; static long rxx; detect = 0; /* Basic loop up. */ for ( r = rcenter, rdist = cdist, rxx = crinc, rdp = cdp, rrgbp = crgbp, first = 1; r < (int) colormax; r++, rdp += rstride, rrgbp += rstride, rdist += rxx, rxx += txsqr, first = 0 ) { if ( greenloop( first ) ) detect = 1; else if ( detect ) break; } /* Basic loop down. */ for ( r = rcenter - 1, rxx = crinc - txsqr, rdist = cdist - rxx, rdp = cdp - rstride, rrgbp = crgbp - rstride, first = 1; r >= 0; r--, rdp -= rstride, rrgbp -= rstride, rxx -= txsqr, rdist -= rxx, first = 0 ) { if ( greenloop( first ) ) detect = 1; else if ( detect ) break; } return detect; } /* greenloop -- loop up and down from green center. */ int greenloop( int restart ) { int detect; int g, i = cindex; int first; long txsqr = xsqr + xsqr; static int here, min, max; #ifdef MINMAX_TRACK static int prevmax, prevmin; int thismax, thismin; #endif static long ginc, gxx, gcdist; /* "gc" variables maintain correct */ static PDIST gcdp; /* values for bcenter position, */ static PIMAP gcrgbp; /* despite modifications by blueloop */ /* to gdist, gdp, grgbp. */ if ( restart ) { here = gcenter; min = 0; max = (int) colormax - 1; ginc = cginc; #ifdef MINMAX_TRACK prevmax = 0; prevmin = (int) colormax; #endif } #ifdef MINMAX_TRACK thismin = min; thismax = max; #endif detect = 0; /* Basic loop up. */ for ( g = here, gcdist = gdist = rdist, gxx = ginc, gcdp = gdp = rdp, gcrgbp = grgbp = rrgbp, first = 1; g <= max; g++, gdp += gstride, gcdp += gstride, grgbp += gstride, gcrgbp += gstride, gdist += gxx, gcdist += gxx, gxx += txsqr, first = 0 ) { if ( blueloop( first ) ) { if ( !detect ) { /* Remember here and associated data! */ if ( g > here ) { here = g; rdp = gcdp; rrgbp = gcrgbp; rdist = gcdist; ginc = gxx; #ifdef MINMAX_TRACK thismin = here; #endif } detect = 1; } } else if ( detect ) { #ifdef MINMAX_TRACK thismax = g - 1; #endif break; } } /* Basic loop down. */ for ( g = here - 1, gxx = ginc - txsqr, gcdist = gdist = rdist - gxx, gcdp = gdp = rdp - gstride, gcrgbp = grgbp = rrgbp - gstride, first = 1; g >= min; g--, gdp -= gstride, gcdp -= gstride, grgbp -= gstride, gcrgbp -= gstride, gxx -= txsqr, gdist -= gxx, gcdist -= gxx, first = 0 ) { if ( blueloop( first ) ) { if ( !detect ) { /* Remember here! */ here = g; rdp = gcdp; rrgbp = gcrgbp; rdist = gcdist; ginc = gxx; #ifdef MINMAX_TRACK thismax = here; #endif detect = 1; } } else if ( detect ) { #ifdef MINMAX_TRACK thismin = g + 1; #endif break; } } #ifdef MINMAX_TRACK /* If we saw something, update the edge trackers. For now, only * tracks edges that are "shrinking" (min increasing, max * decreasing. */ if ( detect ) { if ( thismax < prevmax ) max = thismax; prevmax = thismax; if ( thismin > prevmin ) min = thismin; prevmin = thismin; } #endif return detect; } /* blueloop -- loop up and down from blue center. */ int blueloop( int restart ) { int detect; register PDIST dp; register PIMAP rgbp; register long bdist, bxx; register int b, i = cindex; register long txsqr = xsqr + xsqr; register int lim; static int here, min, max; #ifdef MINMAX_TRACK static int prevmin, prevmax; int thismin, thismax; #endif /* MINMAX_TRACK */ static long binc; if ( restart ) { here = bcenter; min = 0; max = (int) colormax - 1; binc = cbinc; #ifdef MINMAX_TRACK prevmin = (int) colormax; prevmax = 0; #endif /* MINMAX_TRACK */ } detect = 0; #ifdef MINMAX_TRACK thismin = min; thismax = max; #endif /* Basic loop up. */ /* First loop just finds first applicable cell. */ for ( b = here, bdist = gdist, bxx = binc, dp = gdp, rgbp = grgbp, lim = max; b <= lim; b++, dp++, rgbp++, bdist += bxx, bxx += txsqr ) { if ( *dp > (DWORD)bdist ) { /* Remember new 'here' and associated data! */ if ( b > here ) { here = b; gdp = dp; grgbp = rgbp; gdist = bdist; binc = bxx; #ifdef MINMAX_TRACK thismin = here; #endif } detect = 1; break; } } /* Second loop fills in a run of closer cells. */ for ( ; b <= lim; b++, dp++, rgbp++, bdist += bxx, bxx += txsqr ) { if ( *dp > (DWORD)bdist ) { *dp = bdist; *rgbp = (BYTE) i; } else { #ifdef MINMAX_TRACK thismax = b - 1; #endif break; } } /* Basic loop down. */ /* Do initializations here, since the 'find' loop might not get * executed. */ lim = min; b = here - 1; bxx = binc - txsqr; bdist = gdist - bxx; dp = gdp - 1; rgbp = grgbp - 1; /* The 'find' loop is executed only if we didn't already find * something. */ if ( !detect ) for ( ; b >= lim; b--, dp--, rgbp--, bxx -= txsqr, bdist -= bxx ) { if ( *dp > (DWORD)bdist ) { /* Remember here! */ /* No test for b against here necessary because b < * here by definition. */ here = b; gdp = dp; grgbp = rgbp; gdist = bdist; binc = bxx; #ifdef MINMAX_TRACK thismax = here; #endif detect = 1; break; } } /* The 'update' loop. */ for ( ; b >= lim; b--, dp--, rgbp--, bxx -= txsqr, bdist -= bxx ) { if ( *dp > (DWORD)bdist ) { *dp = bdist; *rgbp = (BYTE) i; } else { #ifdef MINMAX_TRACK thismin = b + 1; #endif break; } } /* If we saw something, update the edge trackers. */ #ifdef MINMAX_TRACK if ( detect ) { /* Only tracks edges that are "shrinking" (min increasing, max * decreasing. */ if ( thismax < prevmax ) max = thismax; if ( thismin > prevmin ) min = thismin; /* Remember the min and max values. */ prevmax = thismax; prevmin = thismin; } #endif /* MINMAX_TRACK */ return detect; } void maxfill(PDIST buffer, long side) { register unsigned long maxv = (unsigned long)~0L; register long i; register PDIST bp; for ( i = colormax * colormax * colormax, bp = buffer; i > 0; i--, bp++ ) *bp = maxv; }