Merge branch 'colour-managed-export'
This commit is contained in:
@@ -1260,6 +1260,84 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_output_colour_space_renders_what_the_colorimetry_predicts() {
|
||||
// TRACES: FR-EXP-2
|
||||
// The shader carries constants generated from `dr_types::colour`; this
|
||||
// recomputes the same conversion on the CPU and demands the GPU agree.
|
||||
// A transposed matrix, a transfer function applied before the
|
||||
// primaries, or a clip in the wrong place all compile perfectly and
|
||||
// simply produce the wrong colour — none of which a "did it compile"
|
||||
// test would notice.
|
||||
//
|
||||
// A saturated patch, deliberately: every one of these spaces maps a
|
||||
// neutral to itself, so a grey would agree with all four.
|
||||
let Some(ctx) = ctx() else { return };
|
||||
let mut pass = AdjustPass::new(&ctx);
|
||||
let source = [200u8, 90, 40];
|
||||
let img = jpeg_image(&ctx, source);
|
||||
|
||||
// The JPEG path linearises with the sRGB curve, so this is the value
|
||||
// reaching the output stage.
|
||||
let linear: Vec<f32> = source
|
||||
.iter()
|
||||
.map(|&v| dr_types::Transfer::Srgb.decode(f32::from(v) / 255.0))
|
||||
.collect();
|
||||
|
||||
for space in dr_types::ColourSpace::ALL {
|
||||
let shader = EditGraph::default_chain().compose_for(space);
|
||||
let t = pass.render(&img, &shader, 16, 16).expect("render");
|
||||
let got = read_centre(&ctx, t);
|
||||
|
||||
let m = space.from_linear_srgb();
|
||||
for channel in 0..3 {
|
||||
let converted = m[channel * 3] * linear[0]
|
||||
+ m[channel * 3 + 1] * linear[1]
|
||||
+ m[channel * 3 + 2] * linear[2];
|
||||
let want = space.transfer().encode(converted.clamp(0.0, 1.0)) * 255.0;
|
||||
let delta = (f32::from(got[channel]) - want).abs();
|
||||
// Two levels: the pipeline stores its intermediate in f16 and
|
||||
// the source itself came from an 8-bit texel, so exactness is
|
||||
// not on offer. A wrong matrix is out by tens.
|
||||
assert!(
|
||||
delta <= 2.0,
|
||||
"{space:?} channel {channel}: rendered {} against a predicted {want:.1} \
|
||||
(whole pixel {got:?})",
|
||||
got[channel]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_wide_gamut_render_differs_from_an_srgb_one() {
|
||||
// The companion to the test above, and the one that would fail if the
|
||||
// output space were accepted and then ignored: predicted values that
|
||||
// happened to match sRGB's would prove nothing. A saturated red is
|
||||
// several tens of levels apart in P3.
|
||||
let Some(ctx) = ctx() else { return };
|
||||
let mut pass = AdjustPass::new(&ctx);
|
||||
let img = jpeg_image(&ctx, [230, 30, 20]);
|
||||
|
||||
let srgb = {
|
||||
let shader = EditGraph::default_chain().compose_for(dr_types::ColourSpace::Srgb);
|
||||
let t = pass.render(&img, &shader, 16, 16).expect("render");
|
||||
read_centre(&ctx, t)
|
||||
};
|
||||
let p3 = {
|
||||
let shader = EditGraph::default_chain().compose_for(dr_types::ColourSpace::DisplayP3);
|
||||
let t = pass.render(&img, &shader, 16, 16).expect("render");
|
||||
read_centre(&ctx, t)
|
||||
};
|
||||
|
||||
// Less red and more green: the same colour expressed against wider
|
||||
// primaries needs smaller numbers to reach it.
|
||||
assert!(
|
||||
p3[0] < srgb[0] && p3[1] > srgb[1],
|
||||
"sRGB rendered {srgb:?} and Display P3 {p3:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_jpeg_and_sensor_data_agree_on_the_same_scene_value() {
|
||||
// The two producers must be interchangeable. A mid-grey that is
|
||||
|
||||
Reference in New Issue
Block a user