Hacker News new | ask | show | jobs
by moefh 19 days ago
This problem of what exactly a color value means is mostly inconsequential when you have 8 bits per component, the difference in the denominator being either 255 or 256 makes the errors tiny, you must have really good color perception and get really close to the screen to see any difference at all, and your monitor/phone screen is probably not calibrated anyway, so who cares.

It becomes a pain in the ass when you're generating a VGA signal with a microcontroller with 8 color output pins (3 red, 3 green, 2 blue). The meaning of a color value is very real in this setup: it corresponds to a voltage level you must send to the VGA monitor, 0V-0.7V.

So the blue channel will map (0->0V, 1->0.23V, 2->0.47V, 3->0.7V), and the red/green will map (0->0V, 1->0.1V, ..., 7->0.7V). Notice how none of the blue voltages match any of the red/green ones (other than the extremes)? That means you don't get to see any pure grays -- the closest ones will have bit of blue or yellow tint, depending on the direction of the difference.

Not only that, any gradients at all (other than the ones not mixing blue with the other channels) will be noticeable off: for example, the closest colors in the line between pure red to pure white will all be slightly orange or purple.

Code for VGA output in 8-bit color with double-buffered 320x240 framebuffer for the Raspberry Pi Pico 2 here, if anyone cares: https://github.com/moefh/pico-vga-8bit-demo

4 comments

> That means you don't get to see any pure grays -- the closest ones will have bit of blue or yellow tint, depending on the direction of the difference.

OMG I remember as a kid staring at static-y CRT displays, and seeing these faint blue and yellow lines at the borders of them. I’d always wondered why they appeared and why they were specifically blue and yellow. I finally know! (at least, assuming those specific artifacts are due to the same thing)

specifically on the edges? I would guess that is the phosphor layout, that the 'gray' beam is hitting a blue phosphor but not the red & green, or vice-versa.
You forgot about gamma correction. Before converting a value in the range of 0-255 into a voltage, PCs typically raise that value to the power of 2.2. This makes the difference between small values and large values far more apparent:

2^2.2 = 4.595, 255^2.2 = 196,964.699

Differences between small and large values are irrelevant to the point being made here, though. Much more relevant is the difference between nearby values, and the gamma just gets that closer to logarithmic perception, instead of perceptual steps being disproportionately large for small values.

(This may be more apparent when you frame gamma as being applied in the 0-1 range, so it doesn’t really turn 2 into 4.595 and 255 into ~200k; it turns (2/255)≈0.00784 into (2/255)^2.2 ≈ 0.0000233, and leaves (255/255)=1 as is.)

Dithering in time seems like the best solution to this problem. Delta sigma modulation per pixel can be done reasonably easily.

Changing at 30Hz I doubt a human can tell the difference between slightly blue and slightly yellow.

This is called frame rate control (FRC) in display terminology, at least when used for LCD displays where the panel has fewer distinct levels than the manufacturer wants to advertise.
PWM and FRC aka headache generators deluxe
> Notice how none of the blue voltages match any of the red/green ones (other than the extremes)? That means you don't get to see any pure grays

I assume this is why RGBI color was so common in the 80s.