# RGB332

RGB332 is a general 256 color palette that encodes one color with 1 byte (i.e. 8 bits): 3 bits (highest) for red, 3 bits for green and 2 bits (lowest) for blue (as human eye is least sensitive to blue we choose to allocate fewest bits to blue). RGB332 is an implicit palette -- it doesn't have to be stored in memory (though doing so also has justifications) because the color index itself determines the color and vice versa. Compared to the classic 24 bit RGB (which assigns 8 bits to each of the RGB components), RGB332 is very "KISS/suckless" and often good enough (especially with dithering) as it saves memory, avoids headaches with endianness and represents each color with just a single number (as opposed to 3), so it is ideal for simple and limited computers such as embedded. It is also in the public domain, unlike some other palettes, so it's additionally a legally safe choice. RGB332 also has a "sister palette" called RGB565 which uses two bytes instead of one and so offers many more colors.

One disadvantage of plain 332 palette lies in the linearity of each component, i.e. lack of gamma correction, resulting in too many almost indistinguishable bright colors and too few darker ones { TODO: does a gamma corrected 332 exist? make it? ~drummyfish }. Another disadvantage is non-alignment of the blue component with red and green ones, i.e. while R/G components have 8 levels of intensity and hence step from 0 to 255 by 36.4, the B component only has 4 levels and steps by exactly 85, making it impossible to create exact shades of grey (which of course have to have all R, G and B components equal).

It's also possible to swap the order of component to get palettes such as BGR223, GBR 323 etc.

Another variant of this format could be an 8bit color value under a different color model than RGB, for example HSV or HSL, which could eliminate some of the shortcomings in color distribution (described above), and would add the advantage of easy manipulation of brightness etc., but it would also somehow have to address the issue of multiple representations of colors such as black.

The RGB values of the 332 palette are following:

```
#000000 #000055 #0000aa #0000ff #002400 #002455 #0024aa #0024ff
#004800 #004855 #0048aa #0048ff #006d00 #006d55 #006daa #006dff
#009100 #009155 #0091aa #0091ff #00b600 #00b655 #00b6aa #00b6ff
#00da00 #00da55 #00daaa #00daff #00ff00 #00ff55 #00ffaa #00ffff
#240000 #240055 #2400aa #2400ff #242400 #242455 #2424aa #2424ff
#244800 #244855 #2448aa #2448ff #246d00 #246d55 #246daa #246dff
#249100 #249155 #2491aa #2491ff #24b600 #24b655 #24b6aa #24b6ff
#24da00 #24da55 #24daaa #24daff #24ff00 #24ff55 #24ffaa #24ffff
#480000 #480055 #4800aa #4800ff #482400 #482455 #4824aa #4824ff
#484800 #484855 #4848aa #4848ff #486d00 #486d55 #486daa #486dff
#489100 #489155 #4891aa #4891ff #48b600 #48b655 #48b6aa #48b6ff
#48da00 #48da55 #48daaa #48daff #48ff00 #48ff55 #48ffaa #48ffff
#6d0000 #6d0055 #6d00aa #6d00ff #6d2400 #6d2455 #6d24aa #6d24ff
#6d4800 #6d4855 #6d48aa #6d48ff #6d6d00 #6d6d55 #6d6daa #6d6dff
#6d9100 #6d9155 #6d91aa #6d91ff #6db600 #6db655 #6db6aa #6db6ff
#6dda00 #6dda55 #6ddaaa #6ddaff #6dff00 #6dff55 #6dffaa #6dffff
#910000 #910055 #9100aa #9100ff #912400 #912455 #9124aa #9124ff
#914800 #914855 #9148aa #9148ff #916d00 #916d55 #916daa #916dff
#919100 #919155 #9191aa #9191ff #91b600 #91b655 #91b6aa #91b6ff
#91da00 #91da55 #91daaa #91daff #91ff00 #91ff55 #91ffaa #91ffff
#b60000 #b60055 #b600aa #b600ff #b62400 #b62455 #b624aa #b624ff
#b64800 #b64855 #b648aa #b648ff #b66d00 #b66d55 #b66daa #b66dff
#b69100 #b69155 #b691aa #b691ff #b6b600 #b6b655 #b6b6aa #b6b6ff
#b6da00 #b6da55 #b6daaa #b6daff #b6ff00 #b6ff55 #b6ffaa #b6ffff
#da0000 #da0055 #da00aa #da00ff #da2400 #da2455 #da24aa #da24ff
#da4800 #da4855 #da48aa #da48ff #da6d00 #da6d55 #da6daa #da6dff
#da9100 #da9155 #da91aa #da91ff #dab600 #dab655 #dab6aa #dab6ff
#dada00 #dada55 #dadaaa #dadaff #daff00 #daff55 #daffaa #daffff
#ff0000 #ff0055 #ff00aa #ff00ff #ff2400 #ff2455 #ff24aa #ff24ff
#ff4800 #ff4855 #ff48aa #ff48ff #ff6d00 #ff6d55 #ff6daa #ff6dff
#ff9100 #ff9155 #ff91aa #ff91ff #ffb600 #ffb655 #ffb6aa #ffb6ff
#ffda00 #ffda55 #ffdaaa #ffdaff #ffff00 #ffff55 #ffffaa #ffffff
```

And here's something that may come in very handy e.g. in pixel art: the APPARENT brightness values of all 332 colors by index (from black to white, 0 being minimum and 1023 maximum): { Don't dare to ask me how I made this. Yes I manually ordered all the colors comparing them one by one. ~drummyfish }

```
0, 118, 170, 233, 130, 140, 186, 251, 245, 253, 271, 324, 366, 364,
381, 432, 464, 467, 492, 508, 602, 604, 611, 636, 756, 767, 771, 782,
786, 788, 793, 799, 111, 131, 184, 248, 144, 159, 193, 267, 254, 258,
276, 330, 369, 377, 385, 435, 468, 488, 494, 519, 605, 606, 612, 637,
755, 768, 774, 781, 787, 789, 794, 800, 185, 201, 231, 306, 203, 210,
238, 310, 285, 292, 314, 368, 389, 393, 405, 448, 495, 501, 505, 552,
621, 626, 634, 645, 770, 773, 775, 785, 795, 790, 797, 804, 250, 269,
289, 336, 266, 275, 300, 344, 333, 341, 362, 409, 428, 433, 446, 487,
531, 535, 542, 577, 643, 646, 660, 693, 779, 780, 784, 798, 801, 802,
806, 811, 311, 317, 365, 404, 313, 347, 371, 413, 403, 407, 421, 463,
476, 485, 499, 523, 568, 575, 588, 613, 671, 676, 692, 712, 791, 792,
803, 812, 809, 810, 820, 844, 430, 437, 447, 466, 434, 440, 451, 475,
458, 461, 474, 514, 526, 538, 545, 571, 619, 622, 631, 661, 711, 727,
725, 750, 807, 819, 832, 845, 842, 850, 857, 867, 524, 532, 539, 556,
528, 536, 543, 557, 549, 553, 573, 592, 616, 623, 630, 663, 686, 687,
690, 710, 765, 769, 772, 783, 851, 858, 866, 888, 886, 890, 900, 923,
547, 550, 554, 581, 548, 551, 555, 582, 558, 576, 578, 625, 658, 655,
662, 678, 694, 696, 699, 744, 776, 777, 778, 805, 868, 870, 881, 896,
895, 901, 909, 1023
```

## Operations

Here are C functions for converting RGB332 to RGB24 and back:

```
unsigned char rgbTo332(unsigned char red, unsigned char green, unsigned char blue)
{
  return ((red / 32) << 5) | ((green / 32) << 2) | (blue / 64);
}

void rgbFrom332(unsigned char colorIndex, unsigned char *red, unsigned char *green, unsigned char *blue)
{
  unsigned char value = (colorIndex >> 5) & 0x07;
  *red = value != 7 ? value * 36 : 255;

  value = (colorIndex >> 2) & 0x07;
  *green = value != 7 ? value * 36 : 255;
  
  value = colorIndex & 0x03;
  *blue = (value != 3) ? value * 72 : 255;
}
```

In practice though it may be better to do something as follows, even if the conversions may be slightly inaccurate:

```
uint32_t rgbFrom332(uint8_t c332)
{
  uint32_t c = ((c332 & 0x03) << 6);
  c = ((c332 & 0xe0) << 16) | ((c332 & 0x1c) << 11) | c | (c >> 2);
  return c | (c >> 3) | (c >> 5);
}

uint8_t rgbTo332(uint32_t c)
{
  return ((c >> 16) & 0xe0) | ((c >> 11) & 0x1c) | ((c >> 6) & 0x03);
}

```

NOTE on `rgbFrom332`: a quick naive idea on getting the 8bit values for R, G and B components out of RGB332 color is to simply take the bits and pad them with zeros from bottom to the 8bit values -- though that will somewhat work, it won't be completely correct; consider e.g. an input value `11100000` where R is `111`, i.e. at maximum -- by padding this to `11100000` we however don't get the desired maximum value `11111111` (if we pad with 1s we'll have the same problem for the zero value). This is why the code isn't as simple as the `rgbTo332` function where we really do just chop off the unneeded bits.

Addition/subtraction of two RGB332 colors can be performed by simply adding/subtracting the two color values as long as no over/underflow occurs in either component -- by adding the values we basically perform a parallel addition/subtraction of all three components with only one operation. Unfortunately checking for when exactly such overflow occurs is not easy to do quickly { Or is it? ~drummyfish }, but to rule out e.g. an overflow with addition we may for example check whether the highest bit of each component in both colors to be added is 0 (i.e. `if (((color1 & 0x92) | (color2 & 0x92)) == 0) newColor = color1 + color2;`). { Code untested. ~drummyfish }

Addition/subtraction of colors can also be approximated in a very fast way using the OR/AND operation instead of arithmetic addition/subtraction -- however this only works sometimes (check visually). For example if you need to quickly brighten/darken all pixels in a 332 image, you can just OR/AND each pixel with these values:

```
brighten by more:  doesn't really work anymore
brighten by 3:     | 0x6d  (011 011 01)
brighten by 2:     | 0x49  (010 010 01)
brighten by 1:     | 0x24  (001 001 00)
darken by 1:       & 0xdb  (110 110 11)
darken by 2:       & 0xb6  (101 101 10)
darken by 3:       & 0x92  (100 100 10)
darken by more:    doesn't really work anymore
```

Division by power of two is also fairly fast, you may simply shift the whole value right and zero appropriate bits, for example to darken a color twice you can just do `(color >> 1) & 0x6d` { Again code untested sorry. ~drummyfish }. Multiplying by power of two is not as simple though (consider e.g. a value `1011` shifted left once will result in a smaller value `0110`; you may still try some tricks like a bitwise or with the previous value but that will already be an approximation);

{ TODO: Would it be possible to accurately add two 332 colors by adding all components in parallel using bitwise operators somehow? I briefly tried but the result seemed too complex to be worth it though. ~drummyfish }

Inverting a 332 color is done simply by inverting all bits in the color value.

TODO: blending?

## See Also

- RGB565
- palette
- grayscale