diff --git a/crates/effects/src/utility.rs b/crates/effects/src/utility.rs index 2448733b0..65de35cf6 100644 --- a/crates/effects/src/utility.rs +++ b/crates/effects/src/utility.rs @@ -68,7 +68,7 @@ fn hdr_spow(v: f32, e: f32) -> f32 { /// One straight-colour pixel through HDR Highlight Compression at blend `t` (Amount / 100, > 0). /// /// Measured behaviour (16-bit renders of a 0–4 HDR ramp and coloured patches at Amount 100, -/// 90 and 84): every channel is divided by `1 + Y` (`Y` = 0.30 R + 0.59 G + 0.11 B, so a grey +/// 90, 84, 75, 50 and 25): every channel is divided by `1 + Y` (`Y` = 0.30 R + 0.59 G + 0.11 B, so a grey /// value `x` lands on `x / (1 + x)`), clipped at 1, then mixed with the input as `x^0.4` values. fn hdr_compress_pixel(c: [f32; 3], t: f32) -> [f32; 3] { let y = (HDR_LUMA[0] * c[0] + HDR_LUMA[1] * c[1] + HDR_LUMA[2] * c[2]).max(0.0); @@ -600,26 +600,36 @@ mod tests { } /// Our own measurements of After Effects 2026 (32 bpc, no working space; 16-bit PNG renders - /// decoded back to linear): input pixel, then the output at Amount 100, 90 and 84. + /// decoded back to linear): input pixel, then the output at Amount 100, 90, 84, 75, 50 and 25. #[test] fn hdr_compression_matches_measured_reference() { + const AMOUNTS: [f64; 6] = [100.0, 90.0, 84.0, 75.0, 50.0, 25.0]; #[rustfmt::skip] - let table: [([f32; 3], [[f32; 3]; 3]); 9] = [ - ([0.18005371; 3], [[0.1524; 3], [0.1550; 3], [0.1566; 3]]), - ([0.49365234; 3], [[0.3302; 3], [0.3450; 3], [0.3540; 3]]), - ([1.0058594; 3], [[0.5014; 3], [0.5427; 3], [0.5683; 3]]), - ([2.0058594; 3], [[0.6673; 3], [0.7637; 3], [0.8256; 3]]), - ([2.9941406; 3], [[0.7496; 3], [0.8962; 3], [0.9922; 3]]), - ([3.9941406; 3], [[0.7998; 3], [0.9929; 3], [1.1213; 3]]), - ([0.7998047, 0.099975586, 0.099975586], [[0.6101, 0.0762, 0.0762], [0.6279, 0.0784, 0.0784], [0.6386, 0.0798, 0.0798]]), - ([0.099975586, 0.7001953, 0.19995117], [[0.0681, 0.4776, 0.1363], [0.0711, 0.4976, 0.1420], [0.0727, 0.5097, 0.1456]]), - ([0.30004883, 0.5, 2.5], [[0.1807, 0.3012, 0.9998], [0.1911, 0.3184, 1.1147], [0.1972, 0.3288, 1.1860]]), + let table: [([f32; 3], [[f32; 3]; 6]); 9] = [ + ([0.18005371; 3], [[0.1524; 3], [0.1550; 3], [0.1566; 3], [0.1590; 3], [0.1660; 3], [0.1729; 3]]), + ([0.49365234; 3], [[0.3302; 3], [0.3450; 3], [0.3540; 3], [0.3674; 3], [0.4073; 3], [0.4492; 3]]), + ([1.0058594; 3], [[0.5014; 3], [0.5427; 3], [0.5683; 3], [0.6085; 3], [0.7280; 3], [0.8602; 3]]), + ([2.0058594; 3], [[0.6673; 3], [0.7637; 3], [0.8256; 3], [0.9232; 3], [1.2292; 3], [1.5892; 3]]), + ([2.9941406; 3], [[0.7496; 3], [0.8962; 3], [0.9922; 3], [1.1465; 3], [1.6475; 3], [2.2604; 3]]), + ([3.9941406; 3], [[0.7998; 3], [0.9929; 3], [1.1213; 3], [1.3303; 3], [2.0312; 3], [2.9141; 3]]), + ([0.7998047, 0.099975586, 0.099975586], [ + [0.6101, 0.0762, 0.0762], [0.6279, 0.0784, 0.0784], [0.6386, 0.0798, 0.0798], + [0.6547, 0.0817, 0.0817], [0.7012, 0.0877, 0.0877], [0.7496, 0.0937, 0.0937], + ]), + ([0.099975586, 0.7001953, 0.19995117], [ + [0.0681, 0.4776, 0.1363], [0.0711, 0.4976, 0.1420], [0.0727, 0.5097, 0.1456], + [0.0754, 0.5287, 0.1508], [0.0832, 0.5829, 0.1662], [0.0912, 0.6397, 0.1825], + ]), + ([0.30004883, 0.5, 2.5], [ + [0.1807, 0.3012, 0.9998], [0.1911, 0.3184, 1.1147], [0.1972, 0.3288, 1.1860], + [0.2071, 0.3453, 1.2995], [0.2359, 0.3931, 1.6484], [0.2669, 0.4444, 2.0482], + ]), ]; let mut img = Image::new(table.len() as u32, 1); for (x, (c, _)) in table.iter().enumerate() { img.set(x as u32, 0, [c[0], c[1], c[2], 1.0]); } - for (i, amount) in [100.0, 90.0, 84.0].into_iter().enumerate() { + for (i, amount) in AMOUNTS.into_iter().enumerate() { let out = run("ec.utility.hdrcompression", &[("amount", num(amount))], img.clone()); for (x, (input, want)) in table.iter().enumerate() { let got = out.img.data[x];