# Color

Color (also *colour*, from *celare*, "to cover") is the perceived visual quality of light that's associated with its wavelength/frequency (or mixture of several); for example red, blue and yellow are colors. Electromagnetic waves with wavelength from about 380 to 750 nm (about 400 to 790 THz) form the **visible spectrum**, i.e. waves our eyes can see -- combining such waves with different intensities and letting them fall on the retina of our eyes gives rise to the perception of color in our brain. Without a question colors play immensely important role in our daily lives and thus the study of such an essential physical quality is very profound: there is a deep and complex *color theory* concerned with the concept of color (its definition, description, reproduction, psychological effect etc.). Needless to say colors are intimately related to any visual information such as art, computer graphics, astrophysics, various visualizations or just everyday perception of our world. Color support is sometimes used as the opposite of systems that are extremely limited in the number of colors they can handle, which may be called monochromatic, 1bit (distinguishing only two colors), black&white or grayscale. Color can be thought of as having a similar relationship to visual information as pitch has to auditory information.

Fun fact: in the past some colors were officially called nigger.

**How many colors are there?** The total count of colors humans can distinguish is of course individual (color blindness makes people see fewer colors but there are also conditions that make some people be able to perceive more colors), then also we can ask what color really means (see below) but -- approximately speaking -- various sources state we are able to distinguish millions or even over 10 million different colors on average. In computer technology we talk about **color depth** which says the number of bits we use to represent color -- the more bits, the more colors we can represent. 24 bits are nowadays mostly used to record color (8 bits for each red, green and blue component, so called *true color*), which allows for 16777216 distinct colors, though even something like 16 bits (65536 colors) is mostly enough for many use cases. Some advanced systems however support many more colors than true color, especially extremely bright and dim ones -- see HDR.

**What gives physical objects their color?** Most everyday objects get their color from reflecting only specific parts of the white light (usually sunlight), while absorbing the opposite part of the spectrum, i.e. for example a white object reflects all incoming light, a black one absorbs all incoming light (that's why black things get hot in sunlight), a red one reflects the red light and absorbs the rest etc. This is determined by the qualities of the object's surface, such as the structure of its atoms or its microscopic geometry.

## What Is Color?

This is actually a non-trivial question, or rather there exist many varying definitions of it and furthermore it is a matter of subjective experience, perception of colors may differ between people. When asking what color really is, consider the following:

- Are non-primary colors true colors, or just mixtures of the primary colors? Red, green and blue are the three primary colors, the ones we can mix all other colors from. Many will say yes, non-primary colors are colors. But hold on.
- Are non-spectral colors colors or just mixtures of spectral colors? Spectral colors are the colors with a single wavelength (e.g. red, orange or violet), other colors (like pink) are just mixtures of these. Again, probably yes.
- Is saturation part of color, or a separate attribute? I.e. are e.g. green and greenish gray different colors, or same colors with different saturation? Now it depends.
- Is black a color, or rather a lack of a color? E.g. in computers it is usually treated just as another color, but real world black is really the absence of any light.
- Is white a color? If we are using a subtractive color model, the argument is the same as for black (white paper is really just lack of any color on it).
- Is e.g. gold a color? Or just yellow with a lot of specular reflection? In the real world many things may be called to have a gold color, but in computer graphics we would likely separate the color from the light reflective attribute (such as metalicity).
- Is transparent a color?
- Is intensity part of color (especially in context of e.g. HDR)? For example we might say both Sun and paper are white, but still Sun's color is much "stronger" -- is it therefore a "whiter white" than that of a paper?
- Are colors not perceivable by average human colors? Many animals see colors we can't see (e.g. those in infrared spectrum), but there are also rare cases of humans (so called tetrachromats) who see many more colors than usual thanks to a mutation.
- Are impossible colors colors? Interestingly there exist colors perceivable by average humans which however cannot naturally be seen due to "physics" -- they can however be seen with "eye hacks". Do we count these too?
- ...

## Color In Math/Programming

Provided it's so hard to even define color, it's no surprise that color theory is kind of complicated as fuck. This section will only poke on essential stuff, mostly in relation to programming.

To preface we must briefly mention that colors are divided in many ways, for example there are spectral colors (ones that can be describes by a single wavelength, i.e. those found in the rainbow), primary colors (a set of a few basic colors whose mixing can produce other colors, for example red, green and blue; this depends on color model), secondary colors (equal mixing of two primary ones), tertiary colors (mix of primary and secondary) etc.

**How do our eyes perceive color?** Even if by chance we hated biology, it's useful to know the basics of what's going on in our eyes and brains. In they eye (on the retina) we have two kinds of bitches (cells): rods and cones. Rods perceive "brightness", i.e. "how many photons per second" there are -- these bitches can't see color, only "intensity" of light, but they're useful at night when there is low light. Cones on the other hand make us see actual colors, and they are separated to three types: ones detecting red, green and blue light. Each type of cone gets excited when bombarded with photons of its respective desired FREQUENCY (i.e. color), and it also reacts a little less to frequencies close to that key frequency. So for example the "green color" cones react violently when a photon with the frequency of 555 * 10^12 Hz (wavelength of 540 nm) hits them, and they also somewhat react to 600 * 10^12 Hz, but they remain very chill when hit by 680 * 10^12 Hz wave -- but here the blue cone gets excited very much. The brain sees the amount of excitement for each of the three cone types and based on that decides what exact color we are seeing.

Now to begin with the programming theory, let's start with the term **color model**. As could be guessed by the name, it means "mathematical model of color", i.e. a way of representing colors with numbers. Even though this is probably not 100% correct, as programmers we can more or less equate color model with "color representation in memory". There exist several widely used color models, most of which consist of 3 numeric components. This means that color to us can be abstractly though of as a point in 3D space (unless we're dealing with more limited colors, for example shades of gray, which is then of course a 1 dimensional value). Color models can be further divided into additive, subtractive etc.

Let's list some of the most common color models/representations (conversions between then will be shown later):

- **RGB** (red, green, blue): Most basic model, native to most full color display devices (each pixel on a monitor is composed of three tiny subpixels: red, green and blue), based on biological function of human eye (cone cells on retina are of three types depending on which color they sense: red, green and blue). This is therefore the most common representation of color in programming, it's the "default" one. The model is *additive*, which means the primary colors (red, green and blue) are mixed by being ADDED together (converging towards white color). The disadvantage of RGB is for instance that it's not very straightforward to manipulate things such as saturation etc. An RGB color can be imagines as a point inside a 1x1x1 cube (one dimension is red, one green and one blue, each going from 0 to 1).
- **HSV** (hue, saturation, value, also HSB, with brightness): Another common representation. Hue says the "pigment" of the color (red, blue, green, yellow, orange, pink, ...), saturation the "fullness" or "richness" (from dull gray to "very colorful") and value is the "brightness". Compared to RGB this is an alternative way of representing the same colors, the dimensions are just defined differently. The main advantage of HSV against RGB is more natural manipulation of colors: for example to brighten a color we only increase the value component of each pixel; or to make an image more vivid, we fire up the saturation component. Unlike with RGB however, HSV in 3D is depicted as a cylinder wherein the vertical dimension is value, horizontal distance from the center is saturation (decreasing saturation of any color takes it to the same center value, which lies on a gray scale) and hue is the angle of rotation about vertical axis going through the center (it's a cyclical value, commonly expressed in degrees). A major disadvantage is that black color has many different representations (any color with saturation and value zero, regardless of hue), which wastes values.
- **HSL** (hue, saturation, lightness): Very similar to HSV, just with a slightly different meaning of the last component. Lightness also represents a sort of "brightness", but, unlike with value, cranking lightness to the max will make every color pure white. The difference is essentially this: considering a color's RGB representation, *value* is the largest of the R, G and B components, whereas *lightness* is an average of the largest and smallest of these values. This further worsens the disadvantage mentioned above: both black and white colors now have several representations.
- **HCV** and **HCL**: like HSV and HSL but using chroma instead of saturation. Chroma is very similar to saturation, but defined a little bit differently.
- **CMY** (cyan, magenta, yellow): Subtractive model, used mainly in print. The primary colors here are complementary to red, green and blue, and they get mixed by SUBTRACTING -- this is because in printing white color is the default and black results as a mix of all the primary colors.
- **CMYK** (cyan, magenta, yellow, key): CMY with added *key* component -- this is usually pure black color and it's there basically to save cost. Normally black (and shades of gray) could be produced by mixing cyan, magenta and yellow, but that would be wasteful, so there is one cartridge with just pure black.
- **YUV**: Represents color as one "brightness" value (Y) and two "chroma" values (U and V). Taking only the Y channel would give us a grayscale picture, and the additional U and V components give it full color. This has many advantages, for example if we have a three-wire cable carrying YUV signal, black-and-white screens can just take the Y channel and ignore the other two.
- **YCbCr**: Similar to YUV, mostly used in encoding of digital video because the separation of intensity and chroma channels allows for good lossy compression by subsampling (using lower resolution for) chroma, while keeping good resolution for intensity (to which our sight is more sensitive).
- **grayscale**: Straightforward model for "black and white" (or otherwise monochrome) images in which each pixel just has a single *intensity* ("brightness") value.
- **indexed** (palette): Not exactly a "model" but rather a computer representation. Indexed color means that we have some given palette and then each color is represented simply as a whole number that points to the palette (i.e. color is an index). Colors in the palette can be represented in any model and color depth, for example 24 bit RGB -- the advantage is that representation of a color (i.e. each pixel) can take relatively small space, depending on how many colors there are in the palette (e.g. with a 256 color palette each pixel will only require 8 bits).
- other ones: BGR (RGB with different order of components), HCL (hue, chroma, luminance), HCV (hue, chroma, value), ...

Now given a model such as RGB, a mathematician will like to represent each of the components as a real number in the range between 0 and 1, i.e. for example the red color would be represented as [1,0,0]. As programmers, however, we'll eventually have to quantize the values and thus we have to also talk about so called **color depth**, a value saying how many bits we allocate for a color representation -- the term *bits per pixel* (BPP) is frequently encountered as a unit here. For example the standard for the RGB model is nowadays 8 bits per component, i.e. 24 bits in total, and so it is sometimes called RGB24 (this frequently gets extended to RGB32 by adding another 8bit alpha component, which expresses transparency; this is convenient as 32bit values nicely align in memory). 24bit RGB values are commonly expressed in hexadecimal where, very conveniently, each pair of digits represents one component: for example the color green might be written as `#00ff00` (sometimes even shorter forms are allowed, e.g. CSS also supports `#0f0`). Color depth, naturally, will imply how many colors in total we'll be able to represent. Some devices possess higher color depth (see mainly HDR) and some have lower (e.g. RGB332 uses 8, RGB565 uses 16 etc.). In case we can't split the number of bits evenly, we should allocate more bits for the components that "matter more" in terms of human vision -- for example RGB565 allocates 5 bits to red and blue and 6 bits to green, as human eye is most sensitive to green. Especially with lower color depths tricks such as dithering can be used to visually simulate more colors.

Another essential term is **color space**, practically denoting a set of "physical" colors. Color space is oftentimes related to and/or confused with color model, but they are different things: whereas color model says how we represent color, color space just defines a set of colors in the real world. Color space may also define a correspondence with some specific color model(s), i.e. for example the color of the Sun in the sky may correspond to the RGB value [1.0, 1.0, 1.0] or something -- this is why they may get confused. There exist standardized color spaces such as sRGB. Colors spaces such as oklab aim for perceptual uniformity, e.g. that of lightness and hue. Then there also exists the term **gamut** which signifies a subset of given colorspace. Gamut is used in context of physical devices such as monitors, printers or cameras, to express which exact physical colors the device can reproduce and/or capture. Color spaces and gamuts are important when you're calibrating devices, for example if you want the colors on your monitor match colors that come out of your printer etc.

Finally let's quickly go over other concepts related to colors. **Gamma correction** is a non-linear function very often used on the "brightness" component of recorded colors -- this is because human sight is more sensitive to darker colors than lighter ones, and so to increase perceptual image quality it is good to allocate more bits for darker tones on the detriment of lighter ones. **HDR** (high dynamic range) means that a device is capable of handling "brightness" values in very wide range, i.e. for instance an HDR camera will be able to simultaneously capture details in both very bright and very dark areas of a scene (whereas in traditional cameras a bright sky will for example turn out all white) -- for this a very high color depth is used (typically the RGB components are represented as floating point values). **Complementary color** to given color is one that "cancels" it out when mixed with it.

The following is a table of some common colors:

| color name   | red |green|blue |cyan|magenta|yellow| hue |chroma|sat.(V)|sat.(L)|value|light.|grayscale| RGB24  |RGB565|RGB332| comment                                                                   |
| ------------ |-----|-----|-----|----| ----- | ---- |-----| ---- | ----- | ----- |-----|------| ------- |--------|------|------| ------------------------------------------------------------------------- |
| white        | 1   | 1   | 1   | 0  | 0     | 0    | 0   | 0    | 0     | 0     | 1   | 1    | 1       |`ffffff`|`ffff`| `ff` | all frequencies, complementary to black                                   |
| light gray   | 0.75| 0.75| 0.75|0.25| 0.25  | 0.25 | 0   | 0    | 0     | 0     | 0.75| 0.75 | 0.75    |`c0c0c0`|`c618`| `db` | complementary to dark gray                                                |
| gray         | 0.5 | 0.5 | 0.5 |0.5 | 0.5   | 0.5  | 0   | 0    | 0     | 0     | 0.5 | 0.5  | 0.5     |`808080`|`8410`| `92` | complementary to self                                                     |
| dark gray    | 0.25| 0.25| 0.25|0.75| 0.75  | 0.75 | 0   | 0    | 0     | 0     | 0.25| 0.25 | 0.25    |`404040`|`4208`| `49` | complementary to light gray                                               |
| black        | 0   | 0   | 0   | 1  | 1     | 1    | 0   | 0    | 0     | 0     | 0   | 0    | 0       |`000000`|`0000`| `00` | lack of light, complementary to white                                     |
| red          | 1   | 0   | 0   | 0  | 1     | 1    | 0   | 1    | 1     | 1     | 1   | 0.5  | 0.29    |`ff0000`|`f800`| `e0` | ~685 nm, RGB primary, complementary to cyan                               |
| orange       | 1   | 0.5 | 0   | 0  | 0.5   | 1    | 0.08| 1    | 1     | 1     | 1   | 0.5  | 0.59    |`ff8000`|`fc00`| `f0` | ~605 nm, RGB tertiary, AKA light brown                                    |
| azure        | 0   | 0.5 | 1   | 1  | 0.5   | 0    | 0.57| 1    | 1     | 1     | 1   | 0.5  | 0.44    |`0080ff`|`041f`| `13` |                                                                           |
| yellow       | 1   | 1   | 0   | 0  | 0     | 1    | 0.16| 1    | 1     | 1     | 1   | 0.5  | 0.88    |`ffff00`|`ffe0`| `fc` | ~580 nm, RGB secondary, complementary to blue                             |
| green        | 0   | 1   | 0   | 1  | 0     | 1    | 0.33| 1    | 1     | 1     | 1   | 0.5  | 0.58    |`00ff00`|`07e0`| `1c` | ~532 nm, RGB primary, complementary to pink                               |
| cyan         | 0   | 1   | 1   | 1  | 0     | 0    | 0.5 | 1    | 1     | 1     | 1   | 0.5  | 0.7     |`00ffff`|`07ff`| `1f` | ~512 nm, RGB secondary, complementary to red                              |
| blue         | 0   | 0   | 1   | 1  | 1     | 0    | 0.66| 1    | 1     | 1     | 1   | 0.5  | 0.11    |`0000ff`|`001f`| `03` | ~472 nm, RGB primary, complementary to yellow, most favorite in the world |
| violet       | 0.5 | 0   | 1   | 0.5| 1     | 0    | 0.75| 1    | 1     | 1     | 1   | 0.5  | 0.26    |`8000ff`|`801f`| `83` | ~415 nm, RGB tertiary                                                     |
| pink         | 1   | 0   | 1   | 0  | 1     | 0    | 0.83| 1    | 1     | 1     | 1   | 0.5  | 0.41    |`ff00ff`|`f81f`| `e3` | RGB secondary, complementary to green                                     |
| light red    | 1   | 0.5 | 0.5 | 0  | 0.5   | 0.5  | 0   | 0.5  | 0.5   | 1     | 1   | 0.75 | 0.64    |`ff8080`|`fc10`| `f2` |                                                                           |
| light orange | 1   | 0.75| 0.5 | 0  | 0.25  | 0.5  | 0.08| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.79    |`ffc040`|`fe10`| `fa` |                                                                           |
| light yellow | 1   | 1   | 0.5 | 0  | 0     | 0.5  | 0.16| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.94    |`ffff80`|`fff0`| `fe` |                                                                           |
| light green  | 0.5 | 1   | 0.5 | 0.5| 0     | 0.5  | 0.33| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.79    |`80ff80`|`87f0`| `9e` |                                                                           |
| light cyan   | 0.5 | 1   | 1   | 0.5| 0     | 0    | 0.5 | 0.5  | 0.5   | 1     | 1   | 0.75 | 0.85    |`80ffff`|`87ff`| `9f` |                                                                           |
| light blue   | 0.5 | 0.5 | 1   | 0.5| 0.5   | 0    | 0.66| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.55    |`8080ff`|`841f`| `93` |                                                                           |
| light violet | 0.75| 0.5 | 1   |0.25| 0.5   | 0    | 0.75| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.63    |`c08040`|`c41f`| `d3` |                                                                           |
| light pink   | 1   | 0.5 | 1   | 0  | 0.5   | 0    | 0.83| 0.5  | 0.5   | 1     | 1   | 0.75 | 0.7     |`ff80ff`|`fc1f`| `f3` |                                                                           |
| dark red     | 0.5 | 0   | 0   | 0.5| 1     | 1    | 0   | 0.5  | 1     | 1     | 0.5 | 0.25 | 0.14    |`800000`|`8000`| `80` |                                                                           |
| brown        | 0.5 | 0.25| 0   | 0.5| 0.75  | 1    | 0.08| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.29    |`804000`|`8200`| `88` | AKA dark orange                                                           |
| dark yellow  | 0.5 | 0.5 | 0   | 0.5| 0.5   | 1    | 0.16| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.44    |`808000`|`8400`| `90` |                                                                           |
| dark green   | 0   | 0.5 | 0   | 1  | 0.5   | 1    | 0.33| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.29    |`008000`|`0400`| `10` |                                                                           |
| teal         | 0   | 0.5 | 0.5 | 1  | 0.5   | 0.5  | 0.5 | 0.5  | 1     | 1     | 0.5 | 0.25 | 0.35    |`008080`|`0410`| `12` | AKA dark cyan                                                             |
| dark blue    | 0   | 0   | 0.5 | 1  | 1     | 0.5  | 0.66| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.05    |`000080`|`0010`| `02` |                                                                           |
| dark violet  | 0.25| 0   | 0.5 |0.75| 1     | 0.5  | 0.75| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.13    |`c00080`|`4010`| `42` |                                                                           |
| purple       | 0.5 | 0   | 0.5 | 0.5| 1     | 0.5  | 0.83| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.2     |`800080`|`8010`| `82` | AKA dark pink                                                             |
| indigo       | 0.3 | 0   | 0.5 | 0.7| 1     | 0.5  | 0.75| 0.5  | 1     | 1     | 0.5 | 0.25 | 0.18    |`4b0082`|`4810`| `42` |                                                                           |

## Code And Conversions

Below is a C code implementing some functions for conversion between different color representations, may it serve as a reference of how to convert between them.

```
#define u8 unsigned char

u8 rgbToGray(u8 r, u8 g, u8 b)
{
  // eye has diff. sens. to components, formula: gray ~= 0.3 R + 0.6 G + 0.1 B
  return (5 * ((unsigned) r) + 8 * ((unsigned) g) + 3 * ((unsigned) b)) / 16;
  // however this is still only approximate, the exact formula seems to be non-linear,
  // see the article on RGB 332 to get a brightness-sorted list of colors
}

void rgbToCmy(u8 r, u8 g, u8 b, u8 *c, u8 *m, u8 *y)
{
  *c = 255 - r; *m = 255 - g; *y = 255 - b;
}

void cmyToRgb(u8 c, u8 m, u8 y, u8 *r, u8 *g, u8 *b)
{
  *r = 255 - c; *g = 255 - m; *b = 255 - y;
}

void hsvlToRgb(u8 h, u8 s, u8 vl, u8 *r, u8 *g, u8 *b, u8 hsv)
{
  *r = hsv ? ((((int) vl) * s) / 256) :
    (((int) (256 - 2 * ((vl > 127) ? (vl - 127) : (127 - vl)))) * s) / 256;

  int c = (((((int) h) * 256) / 42) % 512) - 255;
  *g = (((int) *r) * (255 + (c >= 0 ? -1 * c : c))) / 256;
  *b = hsv ? (vl - *r) : (((int) vl) - *r / 2);
  *r += *b;
  *g += *b;

  switch (h / 42)
  {
    case 0:  break;
    case 1:  r ^= g; g ^= r; r ^= g; break; // swap
    case 2:  r ^= g; g ^= r; r ^= g; r ^= b; b ^= r; r ^= b; break; 
    case 3:  r ^= b; b ^= r; r ^= b; break;
    case 4:  g ^= b; b ^= g; g ^= b; r ^= b; b ^= r; r ^= b; break;
    default: g ^= b; b ^= g; g ^= b; break;
  }
}

void rgbToHcsvl(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *sv, u8 *sl, u8 *v, u8 *l)
{
  int min = r < g ? (r < b ? r : b) : (g < b ? g : b); // min of 3
  *v = r > g ? (r > b ? r : b) : (g > b ? g : b);      // max of 3
  *c = *v - min;

  *l = (min + *v) / 2;

  *sl = (*l != 0 && *l != 255) ?
    ((511 * (((int) *v) - *l)) / (256 - 2 * ((*l > 127) ?
      (*l - 127) : (127 - *l)))) : 0;

  min = *c;
  *sv = (*v != 0) ? (255 * min) / *v : 0;

  if (*c == 0)
    *h = 0;
  else if (*v == r)
    *h = (256 + (42 * (((int) g) - b)) / min) % 256;
  else if (*v == g)
    *h = ((42 * ((2 * min + b) - r)) / min) % 256;
  else if (*v == b)
    *h = ((42 * ((4 * min + r) - g)) / min) % 256;
}

void hsvToRgb(u8 h, u8 s, u8 v, u8 *r, u8 *g, u8 *b)
{
  hsvlToRgb(h,s,v,r,g,b,1);
}

void hslToRgb(u8 h, u8 s, u8 l, u8 *r, u8 *g, u8 *b)
{
  hsvlToRgb(h,s,l,r,g,b,0);
}

void rgbToHsv(u8 r, u8 g, u8 b, u8 *h, u8 *s, u8 *v)
{
  u8 sl, l, c;
  rgbToHcsvl(r,g,b,h,&c,s,&sl,v,&sl);
}

void rgbToHsl(u8 r, u8 g, u8 b, u8 *h, u8 *s, u8 *l)
{
  u8 sv, v, c;
  rgbToHcsvl(r,g,b,h,&c,&sv,s,&v,l);
}

void rgbToHcv(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *v)
{
  u8 sv, sl, l;
  rgbToHcsvl(r,g,b,h,c,&sv,&sl,v,&l);
}

void rgbToHcl(u8 r, u8 g, u8 b, u8 *h, u8 *c, u8 *l)
{
  u8 sv, sl, v;
  rgbToHcsvl(r,g,b,h,c,&sv,&sl,&v,l);
}
```

## See Also

- RGB332
- RGB565