640x480x8 Display, 8bit RGB to VGA palette.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
joexbayer
Posts: 7
Joined: Sat Feb 25, 2023 2:02 pm

640x480x8 Display, 8bit RGB to VGA palette.

Post by joexbayer »

I have a 640x480x8 display meaning 1 byte per pixel. This is mostly because I want to restrain myself
and use as little memory as possible. Now I have found out that because of the 8bit, I use the VGA palette:
Image
This was "fine" until I started to add icons / pictures that are exported to 8bit RGB.

I have been searching for a while now, through this forum and the reddit and not found a way to map 8bit RGB colors to the VGA palette.
Ive considered going through all the 255 possible colors and map them 1 to 1, but the shades of colors make this really hard.

Is this just an impossible task or have I not been searching hard enough?

I really hope there isn't a obvious answer that I have missed all this time :D
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by Octocontrabass »

This question was also posted elsewhere.

Since 640x480x8 is a VBE mode, you can use VBE functions to set the palette. Using your own palette should make things much easier, but you might still need to research quantization algorithms to map the colors in your image to the colors available on the screen.
alexfru
Member
Member
Posts: 1111
Joined: Tue Mar 04, 2014 5:27 am

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by alexfru »

Change the palette to all combinations of RGBs that fit into 256 colors, e.g. 6 reds * 6 greens * 6 blues = 216 colors.
Then use e.g. Ordered dithering.
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by Gigasoft »

Personally I use a 676 palette, but if you want to map colors to the standard VGA palette, you could find the saturation and value, use a gray if the saturation is below the limit (depending on value) where the color is best approximated by a gray, otherwise use the appropriate row based on which grid square the value and saturation fall into. Might also check if the color is better approximated by one of the 12 non-gray colors in the first row, again by classifying according to HSV coordinates. For dithering, it is not so easy, haven't done it myself but you'll probably need to precompute a map from chroma and value to the closest set of three points in SV space, and then use the hue as well to get six colors to use for the mix.
joexbayer
Posts: 7
Joined: Sat Feb 25, 2023 2:02 pm

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by joexbayer »

Octocontrabass wrote:This question was also posted elsewhere.

Since 640x480x8 is a VBE mode, you can use VBE functions to set the palette. Using your own palette should make things much easier, but you might still need to research quantization algorithms to map the colors in your image to the colors available on the screen.
Yes, posted it here and on reddit at the same time but it wasn't approved here until now! :D
Thanks again for your feedback, managed to solve it at last.
joexbayer
Posts: 7
Joined: Sat Feb 25, 2023 2:02 pm

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by joexbayer »

alexfru wrote:Change the palette to all combinations of RGBs that fit into 256 colors, e.g. 6 reds * 6 greens * 6 blues = 216 colors.
Then use e.g. Ordered dithering.
Thanks! I will have a look at Ordered dithering! :)
User avatar
AndrewAPrice
Member
Member
Posts: 2300
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by AndrewAPrice »

joexbayer wrote:I have been searching for a while now, through this forum and the reddit and not found a way to map 8bit RGB colors to the VGA palette.
Ive considered going through all the 255 possible colors and map them 1 to 1, but the shades of colors make this really hard.
I'll explain a simple method if you don't care about dithering.

Let's assumine you have a palette->RGB lookup table, where you can look up "Palette Color 5" and get the RGB values for it.

Code: Select all

struct Color {
    float red, green, blue;
};

Color palette[256];
I'm defining red/green/blue to be floats from 0.0 to 1.0, but they could be unsigned chars from 0 to 255.

To find the closest Palette Color from RGB color, loop through all your palette colors and return the one with the smallest error.

Code: Select all

int FindClosestPaletteColor(const Color& color) {
   int closestPaletteColor = 0;
   float smallest_error = INT_MAX;

   for (int i = 0; i < 256; i++) {
       float error = DifferenceBetweenColors(pallete[i], color);
      if (error < smallest_error) {
         smallest_error = error;
         closestPaletteColor = i;
      }
   }
   return closestPaletteColor;
}
For calculating how different two colors are, I'm just treating colors as a 3d vector and doing a simple squared distance, but there are other methods too.

Code: Select all

float DifferenceBetweenColors(const Color& a, const Color& b) {
      float delta_red = a.red - b.red;
      float delta_green = a.green - b.green;
      float delta_blue = a.blue - b.blue;

      float error = delta_red * delta_red + delta_green * delta_green + delta_blue * delta_blue;
}
Now, FindClosestPaletteColor() is slow and inefficient to be called in real-time for every color. We can generate a lookup table!

We can very efficiently take a random RGB color, turn it into 5-bit (32 values) per channel RGB color, which gives us 32,768 possible colors. You could construct the lookup table like this:

Code: Select all

unsigned char palette_lookup_table[32768]; // 2^15.

void PopulateLookupTable() {
  for (int red = 0; red < 32; red++) {
    for (int green = 0; green < 32; green++) {
      for (int blue = 0; blue < 32; blue++) {
        palette_lookup_table[red + green * 32 + blue * (32 * 32)] =
            FindClosestPaletteColor({static_cast<float>(red) / 31.0f,
                                 static_cast<float>(green) / 31.0f,
                                 static_cast<float>(blue) / 31.0f, 1.0f});
      }
    }
  }
}
unsigned char lookupTable[32768] would use 32KB of memory. If that's too much, you may get by with your lookup table being 4-bits (16 values) per channel which would only use 4KB of memory. Smaller than that I'd worry you'd be loosing too much precision.

Now that you have your lookup table, we can efficiently lookup the closest palette index of any color:

Code: Select all

int ColorToPaletteIndex(const Color& color) const {
  // Convert the color to 15-bits and look it up.
  int red = static_cast<int>(color.red * 31.0f);
  int green = static_cast<int>(color.green * 31.0f);
  int blue = static_cast<int>(color.blue * 31.0f);

  int color_index =
      std::max(0, std::min(red + green * 32 + blue * 1024, 32767));
  return palette_lookup_table[color_index];
}
FindClosestPaletteColor() and PopulateLookupTable() could exist in some separate program so you only have to construct the table once and you can hardcode the lookup table into your program:

Code: Select all

unsigned char palette_lookup_table[] = { ... };
My OS is Perception.
joexbayer
Posts: 7
Joined: Sat Feb 25, 2023 2:02 pm

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by joexbayer »

AndrewAPrice wrote:
joexbayer wrote:I have been searching for a while now, through this forum and the reddit and not found a way to map 8bit RGB colors to the VGA palette.
Ive considered going through all the 255 possible colors and map them 1 to 1, but the shades of colors make this really hard.
I'll explain a simple method if you don't care about dithering.

Let's assumine you have a palette->RGB lookup table, where you can look up "Palette Color 5" and get the RGB values for it.

Code: Select all

struct Color {
    float red, green, blue;
};

Color palette[256];
I'm defining red/green/blue to be floats from 0.0 to 1.0, but they could be unsigned chars from 0 to 255.

To find the closest Palette Color from RGB color, loop through all your palette colors and return the one with the smallest error.

Code: Select all

int FindClosestPaletteColor(const Color& color) {
   int closestPaletteColor = 0;
   float smallest_error = INT_MAX;

   for (int i = 0; i < 256; i++) {
       float error = DifferenceBetweenColors(pallete[i], color);
      if (error < smallest_error) {
         smallest_error = error;
         closestPaletteColor = i;
      }
   }
   return closestPaletteColor;
}
For calculating how different two colors are, I'm just treating colors as a 3d vector and doing a simple squared distance, but there are other methods too.

Code: Select all

float DifferenceBetweenColors(const Color& a, const Color& b) {
      float delta_red = a.red - b.red;
      float delta_green = a.green - b.green;
      float delta_blue = a.blue - b.blue;

      float error = delta_red * delta_red + delta_green * delta_green + delta_blue * delta_blue;
}
Now, FindClosestPaletteColor() is slow and inefficient to be called in real-time for every color. We can generate a lookup table!

We can very efficiently take a random RGB color, turn it into 5-bit (32 values) per channel RGB color, which gives us 32,768 possible colors. You could construct the lookup table like this:

Code: Select all

unsigned char palette_lookup_table[32768]; // 2^15.

void PopulateLookupTable() {
  for (int red = 0; red < 32; red++) {
    for (int green = 0; green < 32; green++) {
      for (int blue = 0; blue < 32; blue++) {
        palette_lookup_table[red + green * 32 + blue * (32 * 32)] =
            FindClosestPaletteColor({static_cast<float>(red) / 31.0f,
                                 static_cast<float>(green) / 31.0f,
                                 static_cast<float>(blue) / 31.0f, 1.0f});
      }
    }
  }
}
unsigned char lookupTable[32768] would use 32KB of memory. If that's too much, you may get by with your lookup table being 4-bits (16 values) per channel which would only use 4KB of memory. Smaller than that I'd worry you'd be loosing too much precision.

Now that you have your lookup table, we can efficiently lookup the closest palette index of any color:

Code: Select all

int ColorToPaletteIndex(const Color& color) const {
  // Convert the color to 15-bits and look it up.
  int red = static_cast<int>(color.red * 31.0f);
  int green = static_cast<int>(color.green * 31.0f);
  int blue = static_cast<int>(color.blue * 31.0f);

  int color_index =
      std::max(0, std::min(red + green * 32 + blue * 1024, 32767));
  return palette_lookup_table[color_index];
}
FindClosestPaletteColor() and PopulateLookupTable() could exist in some separate program so you only have to construct the table once and you can hardcode the lookup table into your program:

Code: Select all

unsigned char palette_lookup_table[] = { ... };
Thanks for the detailed response!
I will have a look at it, currently I got it working by remapping the palette to 8bit RGB.
Although the limitation of no real gray colors is pushing me back to VGA.
User avatar
eekee
Member
Member
Posts: 891
Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by eekee »

Plan 9 has an interesting color map. color(6) goes into what it is and why, and has code to compute the palette. (Note: The code may come under the Lucent LPL which has an odd legal clause. It might be best to use the numbers it outputs but not include the code itself in your OS.)

I might post again with a screenshot of the colors program; I've been meaning to install a Plan 9 VM.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
User avatar
eekee
Member
Member
Posts: 891
Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:

Re: 640x480x8 Display, 8bit RGB to VGA palette.

Post by eekee »

Here's the Plan 9 color chart as stated. I'm not sure why it's laid out like this, but the greys are on the diagonal.
Attachments
9colors.png
9colors.png (5.51 KiB) Viewed 2867 times
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
Post Reply