// Where this frame's sensor actually ran out, in the plane's own counts. // // `maximum` is the format's full scale, not the sensor's: a frame that has blown // plateaus at 1.89-2.00x it (measured, as ratios of `maximum - black`: Sony // ILCE-5100 2.002, Ricoh GR III 1.964, Fuji X-T3 / X100V / X100S 1.892) and the // white level the shader divides by has to be that plateau, or the frame lands // short of white and the highlight desaturation — a smoothstep that starts at // 0.95 of the *sensor* — never fires on a body that sits at 0.946. Reading it // off the frame is what makes one develop right for every body. // // A plateau, not a max, and a pile rather than the frame's largest sample: a hot // pixel sits hundreds of counts above the level the sensor stops at — measured on // a Panasonic DMC-LX10 RW2, one sample at 15993 and one at 14665 over a pile of // 3.1M at 13855 — and a window hung off that stray holds the stray alone, finds // no pile in it, and hands back the factor two. There the frame's own level is // 1.75x that fall-back, so the develop opened 0.8 of a stop bright, clipped to // flat the sky the sensor had held, and the fit to the camera's preview could // only pull the exposure back down: the highlight detail was already gone. // // A plateau, not a max: the frame is read over the nine counts at its top, and // the white level is the highest of those that is a **cliff** — a count the one // below it does not come near. A clipped region piles at the level the sensor // stops at, so the count under that level is the sensor's own noise floor and // orders of magnitude smaller; a smooth bright sky hands its top count to the // next one down in similar numbers and is left alone. A frame that has not // clipped — and one that never reaches the format's own scale — stays on the old // factor two, the population mean of the ratios above and the best guess when // there is nothing to measure. // // One count down, not the largest of the three: a sensor clips its channels at // counts a couple apart, so the plane carries a second pile just under the top // one, and asking the top pile to beat *that* pile refused every frame whose // channels do not run out together. Measured on a Fujifilm XF10 RAF: 433819 // samples at 30993 over 30 at 30992 — a cliff — and 83954 at 30990 three counts // under it, which the old rule read as the ground the top pile had to clear eight // times over (it clears 44, not 83954) and answered "not clipped" for a frame // holding 6.7% of its plane at the level. Read that way the develop divided by // 2.000x instead of 1.953x, its plateau landed on 0.976 of the sensor, the // desaturation fired at half strength, and the highlights kept the magenta the // white balance gains make of them (36.7% of the frame's bright blocks red and // blue of green against 0.0% in the camera's own JPEG of the same shot). // // The pile is still asked to be a few dozen samples and not a share of the plane: // what a blown region is has nothing to do with how many the plane holds, and the // frame that has blown one lamp is the frame whose highlights need the level most. export function sensorWhite(data: ArrayLike, maximum: number, black: number): number { const legacy = 2 * (maximum - black); const n = data.length; const floor = Math.max(32, Math.ceil(n * 1e-6)); // Whole-plane counts, so the window can be hung off the highest count the frame // piled at rather than off its largest sample — the one a hot pixel owns. const counts = new Uint32Array(65536); for (let i = 0; i < n; i++) { const v = data[i]; counts[v > 65535 ? 65535 : v]++; } let top = 0; for (let v = 65535; v >= 0; v--) if (counts[v] >= floor) { top = v; break; } if (top < maximum - black) return legacy; const lo = top - 8; const near = new Uint32Array(9); for (let k = 0; k < 9; k++) near[k] = counts[lo + k]; // The scan takes the first count in the window that is both a pile and a cliff // over the count below it. for (let k = 8; k >= 1; k--) { if (near[k] >= floor && near[k] >= near[k - 1] * 8) return lo + k; } return legacy; }