@@ -415,12 +415,17 @@ impl CalibratedRgb {
415415 // Clear inherited CICP metadata from the sRGB base profile.
416416 profile. cicp = None ;
417417
418- // Use gamma directly as the TRC exponent.
418+ // Use gamma directly as the TRC exponent via a parametric curve
419+ // (ICC type 0: Y = X^gamma). This preserves full s15Fixed16 precision
420+ // when serialised to ICC bytes, matching how the C++ BMP decoder builds
421+ // its skcms_TransferFunction. `curve_from_gamma()` would lose precision
422+ // because it quantises to u8Fixed8 format.
419423 // Guard against zero gamma (degenerate header) by falling back to 1.0.
420424 let safe_gamma = |g : f32 | if g > 0.0 { g } else { 1.0 } ;
421- profile. red_trc = Some ( moxcms:: curve_from_gamma ( safe_gamma ( self . gamma_r ) ) ) ;
422- profile. green_trc = Some ( moxcms:: curve_from_gamma ( safe_gamma ( self . gamma_g ) ) ) ;
423- profile. blue_trc = Some ( moxcms:: curve_from_gamma ( safe_gamma ( self . gamma_b ) ) ) ;
425+ let parametric_trc = |g : f32 | moxcms:: ToneReprCurve :: Parametric ( vec ! [ safe_gamma( g) ] ) ;
426+ profile. red_trc = Some ( parametric_trc ( self . gamma_r ) ) ;
427+ profile. green_trc = Some ( parametric_trc ( self . gamma_g ) ) ;
428+ profile. blue_trc = Some ( parametric_trc ( self . gamma_b ) ) ;
424429 profile
425430 }
426431}
@@ -2475,11 +2480,11 @@ mod test {
24752480 profile. is_some( ) ,
24762481 "pal8v4: should have a synthesized ICC profile from calibrated RGB parameters"
24772482 ) ;
2478- // The profile should be parseable.
2479- let profile_bytes = profile. unwrap ( ) ;
2480- assert ! (
2481- moxcms:: ColorProfile :: new_from_slice ( & profile_bytes ) . is_ok ( ) ,
2482- "synthesized ICC profile should be valid"
2483+ validate_icc_profile (
2484+ & profile. unwrap ( ) ,
2485+ "pal8v4.bmp" ,
2486+ moxcms:: DataColorSpace :: Rgb ,
2487+ moxcms :: ProfileClass :: DisplayDevice ,
24832488 ) ;
24842489
24852490 // pal8v5.bmp uses LCS_sRGB — no ICC profile needed.
0 commit comments