RapidRAW Logo RapidRAW
Verified in v1.6.7
Documentation Menu

Tone & Color

A sunset eclipse rendered with the AgX tone mapper, an aerial autumn road with the K-Portra 400 film emulation, and a hotel with teal and orange color grading

All tools on this page run in one GPU compute shader (shaders/shader.wgsl). Slider values are converted to shader values in image_processing.rs. "Linear" means scene-linear RGB with Rec.709 primaries; "display" means the 0 to 1 values after the tone mapper.

Luminance is always Rec.709 luma: Y = 0.2126·R + 0.7152·G + 0.0722·B.

Order of operations

The shader always runs the steps in the same order, whatever order you moved the sliders in.

Show all 27 steps
# Step Works in
1 Chromatic aberration correction input
2 Convert non-RAW input to linear linear
3 Add up mask values
4 Noise reduction square-root luma
5 Sharpening square-root luma
6 Clarity, Structure, Centré clarity log2 luma
7 Dehaze linear
8 Exposure linear
9 Glow, Halation, Flares linear
10 White balance Bradford LMS
11 Centré exposure and color linear
12 Whites, Shadows, Blacks, Contrast gamma 2.2 luma
13 Highlights linear luma
14 Color calibration linear
15 HSL mixer HSV of sRGB values
16 Hue HSV of sRGB values
17 Saturation, Vibrance linear
18 Color grading (global, then each mask) linear
19 Vignette linear
20 Tone mapper (Basic or AgX) linear to display
21 Built-in film emulation V-Log to display
22 Brightness display
23 Curves (global, then each mask) display
24 Custom LUT display
25 Grain display
26 Clipping warning overlay display
27 Dither and output display

Steps 4 to 9 are covered in Detail & Effects.

A few things follow from this order:

  • Exposure comes before the tone mapper, so pushing it never clips hard with AgX; Brightness comes after, so it reshapes midtones without touching black and white.
  • Color grading and vignette work on scene-linear light, so the tone mapper rolls them off naturally.
  • Curves always see the finished, tone-mapped image.

Exposure

rgb = rgb · 2^(slider / 0.8)

The slider goes from −5 to +5, which is ±6.25 EV. It is a pure multiplication in linear light, exactly like changing the shutter speed. Baseline Exposure and highlight-preservation compensation (see RAW Processing) are added on top.

The same drone RAW at Exposure −1, 0 and +1, each with its histogram: the whole histogram shifts left or right, and at +1 a spike of clipped pixels appears at white
The same RAW at Exposure −1.00, 0 and +1.00, rendered by RapidRAW. Exposure moves every tone, so the whole histogram shifts. At +1.00, 7.7% of the pixels are pushed past white and clip (Basic tone mapper); with AgX they would roll off more gently.

White balance

White balance is a von Kries adaptation in Bradford LMS space, the same method used to convert between illuminants in color management:

gains = LMS(as-shot white) / LMS(target white)
rgb'  = LMS→RGB · ( gains · RGB→LMS · rgb ) / luma(adapted white)
  • Dividing by the luma of the adapted white keeps overall brightness constant when you change the temperature.
  • Temperature and tint are converted to a chromaticity with Robertson's method (31 isotherms). Tint moves perpendicular to the Planckian locus.
  • In relative mode, one unit of Temperature is 1.5 mired and one unit of Tint is 1.5 tint units, so ±100 is ±150 mired. Mired steps are perceptually even: the same slider move looks similar whether the photo was shot at 3000 K or 7000 K.
  • Kelvin mode covers 2,000 to 50,000 K and tint ±150.
  • In masks, white balance is a relative shift added in log-gain space.
Left: a 5 by 5 grid of a neutral grey rendered at Temperature and Tint values from −100 to +100, from blue to warm and from green to magenta. Right: the Kelvin value each Temperature setting corresponds to, for photos shot at 3,200, 5,500 and 6,500 K
Left: a neutral grey rendered by RapidRAW at each Temperature and Tint value. Brightness stays about the same, only the color changes. Right: in relative mode the slider moves in even mired steps, so the Kelvin value changes slowly at the left and very quickly at the right. From a photo shot at 6,500 K, about +90 already reaches the 50,000 K limit.

Whites, Shadows and Blacks

These three work on the luminance of an edge-aware base layer and then put the local detail back, so they change tones without flattening texture. The base comes from guided filters on log2 luminance (radius 10 for the tonal base and 40 for the broad base, measured at a 1080 px short side), computed once per photo.

Shadows and Blacks lift or lower the tone curve with two curves that peak at different places, in gamma 2.2 (t = Y^(1/2.2)):

shadow_lift = (slider/120) · t · (1 − t)^4.5
black_lift  = (slider/40)  · t · (1 − t)^12

The (1 − t)^12 term makes Blacks act much closer to black than Shadows. Positive values also stretch contrast around a pivot of 0.2, and add back local detail so lifted shadows don't look flat. Detail is not boosted in near-black areas, so noise isn't amplified. Brightened shadows are desaturated very slightly (up to 12%).

Whites works on the broad base above a pivot of 0.4 (gamma 2.2). Positive values push the upper tones up with a soft shoulder; negative values compress them with a rational curve. Because it moves the base and keeps detail ratios, highlight texture survives.

Highlights has its own section below because it's a separate step: it runs after these three and Contrast, on linear luminance instead of gamma 2.2, with its own recovery for blown areas.

Three charts of how far tones move at −100, −50, +50 and +100: Blacks only near black, Shadows across the lower half with its peak around a quarter, Whites only in the upper half
How far each tone moves, in percent of the histogram width, at −100, −50, +50 and +100. The effect grows smoothly with the slider. Positive Whites stops short of white because the Basic tone mapper clips there.

Contrast

A per-channel S-curve in gamma 2.2 with the pivot at 0.5 (about 0.218 linear, so slightly above middle grey):

p = rgb^(1/2.2)
s = 2^(1.25 · slider/100)                         0.42 to 2.38
p' = 0.5·(2p)^s              if p < 0.5
p' = 1 − 0.5·(2(1 − p))^s    otherwise

Because it works per channel, more contrast also increases saturation a little, like a film curve. Linear values above 1.0 are left alone, so the tone mapper can still compress them.

Left: Contrast tone curves at −100, −50, +50 and +100, all crossing at a pivot at 55% of the histogram. Right: five color patches rendered at Contrast −100 to +100, getting more saturated as contrast rises; the grey patch stays grey
Left: the Contrast curve at four values. Tones above the pivot get brighter, tones below get darker. Right: color patches rendered by RapidRAW. Because the curve is applied to R, G and B separately, colors get more saturated with more contrast and duller with less.

Highlights

Highlights compresses or expands tones above a low pivot (luma 0.06), blended in between 0.06 and 0.28, on the tonal base:

  • Negative (recovery): compressed = d / (1 + 2.2k · d / (1 + 0.35d)), a rational shoulder that compresses very bright (above 1.0) areas strongly while mid highlights move less. Local and mid-scale detail are boosted a little, so cloud texture comes back instead of turning flat grey.
  • Positive: brightens the upper tones with a curve that eases off for very bright values.
  • Blown areas: with recovery, pixels above 1.0 are pulled toward neutral (up to 40%), so areas with no color information left turn white instead of a flat, saturated blob.

The result is applied as a luminance ratio, so hues don't shift.

Chart of how far tones move with Highlights at −100, −50, +50 and +100: nothing in the darkest third of the histogram, the strongest pull-down at the very brightest tones
Highlights leaves roughly the darkest third of the histogram alone. Lowering it pulls the brightest tones down the most (up to 20% of the histogram at −100); raising it stops short of white because the Basic tone mapper clips there.

Highlights vs. Exposure vs. Whites. Exposure scales everything. Highlights works on the bright part of the radius-10 base and keeps texture. Whites works on the broader radius-40 base and mostly sets where the brightest tones end up.

The Basic sliders on the histogram

The charts below follow a neutral tone through the steps above and then through the Basic tone mapper (RAW). The x axis is where the tone sits in the histogram before the change, so you can read off which part of the histogram a slider moves. They're computed from the shader formulas on a flat area (no local detail). A grey ramp rendered through RapidRAW matches them to within one 8-bit level.

Seven small tone curves, one per Basic slider, each at −100, −50, +50 and +100: Exposure scales everything, Contrast is an S-curve, Highlights bends the upper half, Shadows and Blacks the lower part, Whites the top end and Brightness bows the middle while keeping black and white fixed
Each slider as a tone curve. Exposure and Brightness use −1.0, −0.5, +0.5 and +1.0. The Contrast pivot sits at about 55% of the histogram (0.5 in gamma 2.2, slightly above middle grey). With the Basic tone mapper, anything pushed past white clips, which is why the brightening curves flatten at the top.

The Basic adjustments page shows the same data as one overview chart.

Saturation and Vibrance

rgb = mix(Y, rgb, (1 + saturation) · (1 + vibrance_factor))
  • Saturation scales chroma evenly while keeping luma.
  • Positive Vibrance works mainly on low-saturation colors ((1 − S)^1.25) and is halved around orange skin hues (about 25°), so faces don't turn orange.
  • Negative Vibrance reduces the most saturated colors first. Colors below 5% saturation are left alone.
A person leaning on a car in front of an alpine slope, three times with close-ups: the original, Saturation +100 where the slope, hair, skin and sweater turn orange, and Vibrance +100 where they stay much closer to the original
A Sony RAW at Saturation +100 and Vibrance +100. Saturation doubles every color, so the slope turns orange and the cream sweater turns yellow. Vibrance holds back strong colors and skin hues: the average skin saturation goes from 0.52 to 0.70 with Saturation, but only to 0.63 with Vibrance.
Chart of the color strength multiplier against saturation before: Saturation +50 is a flat 1.5x, Vibrance +50 starts at 1.75x for muted colors and falls to 1x for fully saturated colors, lower on skin tones, and Vibrance −50 reduces strong colors more than weak ones
How much each color's strength is multiplied, by how saturated it already is. Vibrance +50 adds up to 75% to muted colors and nothing to fully saturated ones, and only half of that on skin hues (around 25°).

HSL mixer

Hue and saturation are read from the HSV of the sRGB-encoded color; luminance is measured as linear luma. There are eight bands:

Band Center Width
Red 358° 35°
Orange 25° 45°
Yellow 60° 40°
Green 115° 90°
Aqua 180° 60°
Blue 225° 60°
Purple 280° 55°
Magenta 330° 50°
  • Each band's weight is a Gaussian of the hue distance, and the weights are normalized to add up to 1. So there are no gaps between bands, and two neighbouring bands never double up.
  • Hue sliders shift by up to ±60°. Saturation multiplies saturation. Luminance scales luma, and the hue and saturation changes keep luma constant.
  • Near-grey pixels are protected: hue and saturation changes fade in between 5% and 20% saturation, so neutral areas don't pick up color.
Chart of the eight Color Mixer bands as weight curves around the hue circle, from Red at 358 degrees to Magenta at 330 degrees, with Green the widest band
The normalized weight of each band by hue. Where two bands overlap, a color is shared between them, so neighbouring sliders blend smoothly. Green is the widest band (90°), Red the narrowest (35°).

Color calibration

Global only. The hue sliders rotate the camera primaries toward their neighbours (up to 25%), as in a 3x3 primary mixing matrix:

Primary Positive values Negative values
Red toward green toward blue
Green toward blue toward red
Blue toward red toward green

The saturation sliders scale chroma weighted by how much each primary contributes to the pixel. Shadows Tint adds up to ±6.25% magenta or green below luma 0.3.

Color grading

Color grading adds a color offset in linear light to three tonal zones:

shadow edge    = 0.1 + max(0, −balance)·0.5
highlight edge = 0.5 − max(0,  balance)·0.5
feather        = 0.2 · blending

Shadows, midtones and highlights masks are smooth steps around these edges; midtones is whatever is left. Each zone adds (color − 0.5) · saturation and a luminance offset, with zone strengths of 0.1 / 0.6 / 0.8 for color and 0.5 / 0.8 / 1.0 for luminance (shadows / midtones / highlights). Global applies everywhere at full strength.

  • Negative balance extends the shadow zone upward; positive extends the highlight zone downward.
  • Because the offsets are added in linear light before the tone mapper, a strong shadow tint stays subtle in deep blacks and builds up smoothly.
Three charts of the Shadows, Midtones and Highlights zone masks across the histogram: at the default settings, with Balance +50, and with Blending 100
The three zone masks by position in the histogram (RAW, Basic tone mapper). The zones are measured in linear light, so the Highlights zone only starts above about 75% of the histogram. Positive Balance moves it down into the midtones; Blending widens all the overlaps.

Vignette

uv = (position − center) · 2,  shaped by |uv|^(1 − roundness)
m  = smoothstep(midpoint − feather/2, midpoint + feather/2, distance)
negative amount:  rgb · (1 + amount·m)          darkens
positive amount:  mix(rgb, white, amount·m)     fades to white

With roundness 0 the shape is a circle (corrected for the aspect ratio). It's applied in linear light before tone mapping, which makes the falloff look like natural lens vignetting.

Nine grey frames with a strong vignette: Midpoint 25, 50 and 75, Feather 10, 50 and 100, and Roundness −50, 0 and +50
Midpoint sets how far from the center the darkening starts, Feather how soft the transition is, and Roundness the shape: positive values follow the frame edges more, negative values make it rounder.

Tone mappers

The tone mapper converts scene-linear light (which can be far above 1.0) into display values.

Basic (default for new edits)

  • RAW: sRGB encode → ^(1/1.1) → 75% smoothstep S-curve. Middle grey (0.18) lands at 0.49. Anything at or above 1.0 is clipped per channel.
  • Non-RAW: just the sRGB encoding, so an unedited JPEG looks exactly like the original.

AgX (based on Troy Sobotka's AgX)

  1. Negative values are removed by an offset.
  2. An "inset" matrix moves the colors toward the white point in a Rec.2020-based space, with a small rotation of each primary.
  3. Each channel is encoded as log2 from −15.2 to +5 EV around middle grey.
  4. A sigmoid curve is applied, then a 2.4 gamma.
  5. An "outset" matrix restores most of the saturation.
Scene value AgX display value
0.001 0.00
0.18 (middle grey) 0.51
0.5 0.75
1.0 0.92
about 1.40 1.00

AgX has about half a stop of headroom above 1.0 and rolls very bright colors toward white instead of skewing their hue (a blown orange sky stays orange-white instead of turning flat yellow).

Chart of display value against scene brightness in stops for the Basic tone mapper on RAW and on JPEG files, and for AgX
The three curves for a neutral input. Both Basic curves reach white at scene value 1.0 and clip; AgX reaches white about half a stop later and has a longer, softer shoulder.
Two versions of a sunset eclipse: Basic with hard yellow clipping around the sun, AgX with a smooth orange to white roll-off
The same RAW at Exposure +0.3 with Basic (left) and AgX (right, Contrast +15). Basic clips each channel at 1.0; AgX rolls the sun off smoothly. Sample image by Wojciech Wolas.

Film emulation

The built-in film looks are scene-referred 3D LUTs:

  1. The linear image (before tone mapping) is encoded with Panasonic V-Log (0.18 lands at 0.423).
  2. The LUT is sampled with tetrahedral interpolation (the cube is split into six tetrahedra; smoother than trilinear, no hue shifts along the grey axis).
  3. The result is mixed with the tone-mapped image by the LUT intensity. At 100% the film LUT replaces the tone mapper completely.

Your own LUTs (.cube, .3dl, HALD images) are treated as display-referred: they're applied after Curves, to the finished image.

An aerial shot of a road winding through autumn forest, shown without a film look and with each of the six built-in film emulations
A DJI drone RAW with the six built-in film emulations at 100%. The first tile is the edit without a film look.

Brightness

Brightness runs after the tone mapper, on display values. It uses a rational curve that keeps 0 and 1 fixed:

k  = 2^(−1.2 · slider/0.8)
Y' = Y / (Y + (1 − Y)·k)

Chroma is scaled a bit less than luma and is reduced near white, so brightening doesn't produce oversaturated highlights.

Curves

  • Points are in 0 to 255 display values, at most 16 per channel.
  • Interpolation is a monotone cubic (Fritsch–Carlson). A curve that goes up between two points never dips in between, so there's no overshoot or banding from wiggly curves.
  • The Luma curve is a master curve applied to R, G and B separately (like "RGB" in other editors), so it also affects saturation. Then the R, G and B curves are applied.
  • Curves that are a straight line are skipped.

Grain

Two octaves of gradient (Perlin-style) noise, mixed by Roughness, added equally to R, G and B (monochrome grain). Its size scales with the image, so grain looks the same in the preview and the export. It fades out in pure blacks and whites.

Clipping warnings and output

  • The clipping overlay marks any channel above 0.998 red and below 0.002 blue, on the final display values.
  • 8-bit output gets ±½ step of dither noise against banding. 16-bit TIFF exports are written without dither from 16-bit float data.

Slider scaling

How UI values map to shader values:

Slider UI range Shader value
Exposure, Brightness −5 to 5 ÷0.8 (±6.25)
Contrast, Saturation, Vibrance, Dehaze ±100 ÷100
Highlights, Shadows ±100 ÷120
Whites, Blacks ±100 ÷40
Clarity, Structure ±100 ÷125
Temperature, Tint (relative) ±100 1.5 mired per unit
Sharpness ±100 ÷50
HSL hue ±100 up to ±60°
HSL saturation, luminance ±100 ÷100
Color grading saturation, luminance 0 to 100, ±100 ÷500
Color grading balance ±100 ÷200
Calibration hue / saturation ±100 ÷400 / ÷120
Vignette amount, midpoint, roundness, feather ÷100
Grain amount / size 0 to 100 ÷400 / ÷50
Chromatic aberration ±100 ÷10000 (±1% scale of red or blue)

Masks

Most adjustments inside a mask are added to the global value before the operation runs:

value at pixel = global + Σ (mask value × mask influence)

So a mask at 50% influence with Exposure +1 gives exactly +0.5 EV, not a 50% blend of two images, and overlapping masks add up. White balance adds in log-gain space. Color grading and curves are different: each mask's grade or curve is blended over the result (mix(rgb, curve(rgb), influence)), one mask after the other.

Global only: tone mapper, LUTs, vignette, grain, color calibration, chromatic aberration and Centré. Up to 32 masks are used.

See also