diff --git a/crates/codecs/src/lib.rs b/crates/codecs/src/lib.rs index c96c5334a..1ef6f7908 100644 --- a/crates/codecs/src/lib.rs +++ b/crates/codecs/src/lib.rs @@ -245,11 +245,25 @@ pub fn decode_unguarded(bytes: &[u8], format: Format, opts: &DecodeOptions) -> R /// Convert a decode result to linear Rec.2020 D65 (Bradford-adapted), the develop working space. pub fn to_working(d: &Decoded) -> Rgb32f { - if d.space.is_working() { - return d.image.clone(); + let mut image = if d.space.is_working() { + d.image.clone() + } else { + let m = d.space.to_working().to_f32(); + d.image.map(|p| mat_apply(&m, p)) + }; + // Straight alpha onto white. JPEG and contact sheets have no alpha, and a transparent + // pixel is usually stored as black (#1212). + if let Some(alpha) = &d.alpha { + let w = image.width; + lightcraft_raster::par_rows(&mut image.data, w, |y, row| { + let a = alpha.row(y); + for (p, &alpha) in row.iter_mut().zip(a) { + let a = alpha.clamp(0.0, 1.0); + *p = p.map(|c| c * a + (1.0 - a)); + } + }); } - let m = d.space.to_working().to_f32(); - d.image.map(|p| mat_apply(&m, p)) + image } /// Where a thumbnail came from. @@ -323,3 +337,37 @@ pub fn decode_thumbnail_with(bytes: &[u8], opts: &ThumbnailOptions) -> Result