Go Back   Project64 Forums > General Discussion > Open Discussion

Reply
 
Thread Tools Display Modes
  #1  
Old 10th July 2013, 12:02 AM
HatCat's Avatar
HatCat HatCat is offline
Alpha Tester
Project Supporter
Senior Member
 
Join Date: Feb 2007
Location: In my hat.
Posts: 16,236
Default Notes on reversing 256-color palettes

I was unable to find an online web source that would tell me the 256 colors used in a 8-bit color palette. I needed this particularly since there is an 8-bit minimum compatibility color mode built into my driver on Windows, so I thought it would be cool to make zilmar's basic CFB plugin support 8-bit monitor screens.

A while back, I did unintentionally reverse the 256-color VGA BIOS mode 19 palette for MS-DOS using 386 assembler, but it was slightly different from the Windows one....

So since this info wasn't documented, I extracted them off a binary 8-bit BMP file I made by typing in the hex values.
The palette for the 256 colors enumerated into the list I compiled here:
https://dl.dropboxusercontent.com/u/...ttes/8bpp.html

Eventually I noticed the pattern for the palette.
With only two exceptions (i < 10 || i > 246), the RGB varies directly as **BGR** bits reversed, if we divide the 8-bit color into RR GGG BBB.

The basic pattern for converting 8-bit Windows colors, into 32-bit RGB colors, can be expressed in this pseudo-code:
Code:
unsigned long int RGBA[256];
register int i;

for (i = 0; i < 256; i++)
{
    const unsigned char R = (i >> 0) & 0b111;
    const unsigned char G = (i >> 3) & 0b111;
    const unsigned char B = (i >> 6) & 0b11;

    RGBA[i] = (32*R << 16) | (32*G <<  8) | (64*B <<  0) | 0xFF;
}
// BIOS hard-wired exceptions go here:
RGBA[0x02] = 0x008000FF; // dark green (html calls it "green")
RGBA[0x03] = 0x808000FF; // olive
RGBA[0x04] = 0x000080FF; // navy
RGBA[0x05] = 0x800080FF; // purple
RGBA[0x06] = 0x008080FF; // teal
RGBA[0x07] = 0xC0C0C0FF; // silver
RGBA[0x08] = 0xC0DCC0FF;
RGBA[0x09] = 0xD4D0C8FF;
RGBA[0xF6] = 0xFFFBF0FF;
RGBA[0xF7] = 0xA0A0A4FF;
...
RGBA[0xFF] = 0xFFFFFFFF; // white
Next up is going the other way.
From N64 RGBA16 (R5/G5/B5/A1) to Win32 8-bpp (what evidently seems to be B2/G3/R3).
Reply With Quote
  #2  
Old 10th July 2013, 04:25 AM
HatCat's Avatar
HatCat HatCat is offline
Alpha Tester
Project Supporter
Senior Member
 
Join Date: Feb 2007
Location: In my hat.
Posts: 16,236
Default

Got the approximations set.

For some reason though, Win7's "256 colors mode", appears to only give me 16 colors.
But oh well, maybe on someone else's machine the pixels will be processed as they should be.

The edge cases switch is a nasty block of shit; I think I'm officially done writing pixel down-sampling algorithms XD (converting 16-bit color quality to lower bits)

Code:
    for (i = 0; i < 65536; i++)
        Convert16to8[i] = (B/8 << 6) | (G/4 << 3) | (R/4 << 0);
    for (i = 0; i < 65536; i++)
    { /* edge cases for 8-bit Windows bit-map palette */
        if ((Convert16to8[i] >= 10) & (Convert16to8[i] < 246))
            continue;
        switch (Convert16to8[i])
        {
            case 0x02: /* intended 0x400000, actually 0x008000 */
            case 0x03: /* intended 0x600000, actually 0x808000 */
            case 0x04: /* intended 0x800000, actually 0x000080 (correction) */
            case 0x05: /* intended 0xA00000, actually 0x800080 */
                Convert16to8[i] = 0x01; /* approximation:  0x800000 */
                continue;
            case 0x06: /* intended 0xC00000, actually 0x008080 */
            case 0x07: /* intended 0xE00000, actually 0xC0C0C0 */
                Convert16to8[i] = 0xF9; /* approximation:  0xFF0000 */
                continue;
            case 0x08: /* intended 0x002000, actually 0xC0DCC0 */
                Convert16to8[i] = 0x10; /* approximation:  0x004000 */
                continue;
            case 0x09: /* intended 0x202000, actually 0xD4D0C8 */
                Convert16to8[i] = 0x12; /* rescale:  0x404000 */
                continue;
            case 0xF6: /* intended 0xC0C0C0, actually 0xFFFBF0 */
                Convert16to8[i] = 0x07; /* correction:  0xC0C0C0 */
                continue;
            case 0xF7: /* intended 0xE0C0C0, actually 0xA0A0A4 */
                Convert16to8[i] = 0x08; /* approximation:  0xA08080 */
                continue;
            case 0xF8: /* intended 0x00E0C0, actually 0x808080 */
                Convert16to8[i] = 0xA8; /* approximation:  0x00A080 */
                continue;
            case 0xF9: /* intended 0x20E0C0, actually 0xFF0000 */
                Convert16to8[i] = 0xA9; /* approximation:  0x20A080 */
                continue;
            case 0xFA: /* intended 0x40E0C0, actually 0x00FF00 */
                Convert16to8[i] = 0xA9; /* approximation:  0x40A080 */
                continue;
            case 0xFB: /* intended 0x60E0C0, actually 0xFFFF00 */
            case 0xFC: /* intended 0x80E0C0, actually 0x0000FF */
                Convert16to8[i] = 0xF6; /* approximation:  0xFFFBF0 */
                continue;
            case 0xFD: /* intended 0xA0E0C0, actually 0xFF00FF */
                Convert16to8[i] = 0xAB; /* approximation:  0x60A080 */
                continue;
            case 0xFE: /* intended 0xC0E0C0, actually 0x00FFFF */
                Convert16to8[i] = 0xF7; /* approximation:  0xC0DCC0 */
                continue;
        }
    }
Reply With Quote
  #3  
Old 10th July 2013, 05:01 PM
HatCat's Avatar
HatCat HatCat is offline
Alpha Tester
Project Supporter
Senior Member
 
Join Date: Feb 2007
Location: In my hat.
Posts: 16,236
Default

I figured out a similar pattern for the BIOS 4-bit color palette mode.
https://dl.dropboxusercontent.com/u/...ttes/4bpp.html

4-bit colors here can be LUT-voided using the algorithm:
LBGR
(That sounds awfully familiar somehow..."Lesbian Black Gay Rights?")

Where "L" MSB represents the lightness/luminosity.

If the highest bit was set, it was the brightest colors (bright gray, bright red, bright green, bright blue, white, etc.).

The low 3 bits however maintain the same basic element of color.

000 = black
001 = red
010 = green
011 = red + green = yellow
100 = blue
101 = red + blue = purple
110 = green + blue = cyan (stupid HTML color names call this "aqua")
111 = red + green + blue = no saturation (silver/gray/black/white)

I finished implementing color conversion support for all the possible monitor screen color qualities (1 bit per pixel, 4 b/px, 8 b/px, 16 b/px, 24 p/px, and 32 b/px).

Code:
#ifdef BIG_ENDIAN
#define HES(ptr)  (ptr ^ 00)
#define HIS(ptr)  (ptr ^ 00)
#else
#define HES(ptr)  (ptr ^ 02) /* halfword endian swap */
#define HIS(ptr)  (ptr ^ 01) /* endian swaps on halfword array indices */
#endif

...

void RomOpen(void)
{
    PIXELFORMATDESCRIPTOR pfd;
    unsigned char R, G, B, A;
    unsigned short Color16;
    register int i;
...
    for (i = 0; i < 65536; i++)
    {
        Color16 = (unsigned short)(i);
#ifdef BIG_ENDIAN
        Color16 = Flip16(Color16);
#endif
        R = (Color16 >> 11)%32 << 3; /* R{255..32} <-- R{31..0} */
        G = (Color16 >>  6)%32 << 3; /* G{255..32} <-- G{31..0} */
        B = (Color16 >>  1)%32 << 3; /* B{255..32} <-- B{31..0} */
        A = -(Color16 & FALSE) & 0xFF; /* Alpha mixing not supported. */
        Convert16to1[i] = (R + G + B >= 128 + 128 + 128);
        Convert16to4[i]  = (R + G + B > 128 + 128 + 128) << 3;
        Convert16to4[i] |= (B >= 64)<<2 | (G >= 64)<<1 | (R >= 64)<<0;
        Convert16to8[i] = adjustWinVGA[(B/64 << 6) | (G/32 << 3) | (R/32 << 0)];
        Convert16to16[i]   = (R << 8) | (G << 3);
        Convert16to16[i] >>= (pfd.cGreenShift == 5); /* as compared to G6 */
        Convert16to16[i]  |= (B >> 3);
        Convert16to32[i] = (A << 24) | (R << 16) | (G <<  8) | (B <<  0);
    }
    return;
}

void UpdateScreen(void)
{
    void **SFB; /* screen frame buffer, or "surface buffer" */
    int pitch; /* binary distance between scanlines in pixels */
    int limit;
    register int cx;
    const UINT16 *DRAM16 = (UINT16 *)(GFXInfo.RDRAM);
    const UINT16 *FB = DRAM16 + (*GFXInfo.VI_ORIGIN_REG & 0x00FFFFFF)/2;
...
    pitch = (bmi.bmiHeader.biWidth + 0x003) & ~03;
    limit = pitch * -bmi.bmiHeader.biHeight;
    PixelFormat = GetDeviceCaps(CFB, BITSPIXEL);
    switch (PixelFormat)
    {
        case 1:
        { /* Monochrome monitors are untested!  (VI_WIDTH & 07) must be 00. */
            unsigned char *M = (unsigned char *)(SFB);

            for (cx = 0; cx < limit; cx += 8)
            {
                M[cx]  = Convert16to1[FB[cx + HIS(00)]] << 7;
                M[cx] |= Convert16to1[FB[cx + HIS(01)]] << 6;
                M[cx] |= Convert16to1[FB[cx + HIS(02)]] << 5;
                M[cx] |= Convert16to1[FB[cx + HIS(03)]] << 4;
                M[cx] += 8*Convert16to1[FB[cx + HIS(04)]];
                M[cx] += 4*Convert16to1[FB[cx + HIS(05)]];
                M[cx] += 2*Convert16to1[FB[cx + HIS(06)]];
                M[cx] += 1*Convert16to1[FB[cx + HIS(07)]];
            }
            break;
        }
        case 4:
        {
            unsigned char *M = (unsigned char *)(SFB);

            for (cx = 0; cx < limit; cx += 2)
            {
                M[cx]  = Convert16to8[FB[cx + HIS(0)]] << 4;
                M[cx] |= Convert16to8[FB[cx + HIS(1)]] << 0;
            }
            break;
        }
        case 8:
        {
            UINT8 *M = (UINT8 *)(SFB);

            for (cx = 0; cx < limit; cx++)
                M[cx] = Convert16to8[FB[HIS(cx)]];
            break;
        }
        case 16:
        {
            UINT16 *M = (UINT16 *)(SFB);

            for (cx = 0; cx < limit; cx++)
                M[cx] = Convert16to16[FB[HIS(cx)]];
            break;
        }
        case 24:
        {
            unsigned char *M = (unsigned char *)(SFB);

            for (cx = 0; cx < limit; cx++)
            {
                M[3*cx + 0] = (unsigned char)(Convert16to32[FB[HIS(cx)]] >> 16);
                M[3*cx + 1] = (unsigned char)(Convert16to32[FB[HIS(cx)]] >>  8);
                M[3*cx + 2] = (unsigned char)(Convert16to32[FB[HIS(cx)]] >>  0);
            }
            break;
        }
        case 32:
        {
            UINT32 *M = (UINT32 *)(SFB);

            for (cx = 0; cx < limit; cx++)
                M[cx] = Convert16to32[FB[HIS(cx)]];
            break;
        }
    }
    DrawScreen();
    return;
}
Ultimately, the difference in my modifications apart from the original Basic CFB plugin is that now we account for the user who will try to switch monitor screen color depths while the emulation thread is running. Per each iteration of UpdateScreen will the updated pixel color depth be constantly maintained to make sure the screen is drawn right at the cost of very, very little slowdown compared to the real bottleneck (the pixel DMA loops).

Last edited by HatCat; 10th July 2013 at 05:18 PM.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT. The time now is 03:20 PM.


Powered by vBulletin® Version 3.7.3
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.