Explainer

What a gamma table actually does when you dim a display

A brightness slider is not one operation. Between the slider and the panel there are five, and the order matters more than the values.

The five operations, in order

For every entry in the display's ramp, one sample at a time:

x = pow(raw, 1/gamma)
x = (x - 0.5) * contrast + 0.5
x = x * brightness
x = x * tint
x = clamp(x, 0, 1)

Read the second line carefully: contrast pivots around 0.5, not around zero. At contrast 1 it is the identity and the line vanishes. Below 1 it pulls the whole ramp toward the midpoint — which means the bottom of the ramp is lifted off zero. That single term, not the dimming, is what turns true black into dark grey.

Trap 1 — repeated adjustments compound

If each adjustment shapes the current table, the second one shapes a table that is already shaped. The fix is to capture the display's pristine ramp once and always shape that. It is the same class of bug as a scheduler that multiplies an already-dimmed value: it looks fine on the first call and drifts forever after.

Trap 2 — the table can silently drop

A wake, a resolution change or a GPU reset can discard the table without telling anyone. You must not assume the write stuck: read the table back, compare one interior sample against what you asked for, and retry with backoff. A tolerance is needed because the round trip is not bit-exact.

Trap 3 — a warm tint must never brighten the panel

Warming the picture means raising red and lowering blue. Done naively the peak goes above 1 and the display gets brighter than it was. The constraint is one line: normalise the peak down to 1. A colour change should never be a brightness change.

Reference white

Every tint is expressed relative to one reference white, so that the reference temperature is a true identity transform and an untouched display stays untouched. Pick that anchor carelessly and “no change” quietly becomes “slightly warmer”.

Honest limits

Related