@@ -224,6 +224,13 @@ impl DynamicImage {
224224 }
225225
226226 /// Encodes a dynamic image into a buffer.
227+ ///
228+ /// **WARNING**: Conversion between RGB and Luma is not aware of the color space and always
229+ /// uses sRGB coefficients to determine a non-constant luminance from an RGB color (and
230+ /// conversely).
231+ ///
232+ /// This unfortunately owes to the public bounds of `T` which does not allow for passing a
233+ /// color space as a parameter. This function will likely be deprecated and replaced.
227234 #[ inline]
228235 #[ must_use]
229236 pub fn to <
@@ -247,73 +254,103 @@ impl DynamicImage {
247254 /// Returns a copy of this image as an RGB image.
248255 #[ must_use]
249256 pub fn to_rgb8 ( & self ) -> RgbImage {
250- self . to ( )
257+ match self {
258+ DynamicImage :: ImageRgb8 ( x) => x. clone ( ) ,
259+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
260+ }
251261 }
252262
253263 /// Returns a copy of this image as an RGB image.
254264 #[ must_use]
255265 pub fn to_rgb16 ( & self ) -> Rgb16Image {
256- self . to ( )
266+ match self {
267+ DynamicImage :: ImageRgb16 ( x) => x. clone ( ) ,
268+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
269+ }
257270 }
258271
259272 /// Returns a copy of this image as an RGB image.
260273 #[ must_use]
261274 pub fn to_rgb32f ( & self ) -> Rgb32FImage {
262- self . to ( )
275+ match self {
276+ DynamicImage :: ImageRgb32F ( x) => x. clone ( ) ,
277+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
278+ }
263279 }
264280
265281 /// Returns a copy of this image as an RGBA image.
266282 #[ must_use]
267283 pub fn to_rgba8 ( & self ) -> RgbaImage {
268- self . to ( )
284+ match self {
285+ DynamicImage :: ImageRgba8 ( x) => x. clone ( ) ,
286+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
287+ }
269288 }
270289
271290 /// Returns a copy of this image as an RGBA image.
272291 #[ must_use]
273292 pub fn to_rgba16 ( & self ) -> Rgba16Image {
274- self . to ( )
293+ match self {
294+ DynamicImage :: ImageRgba16 ( x) => x. clone ( ) ,
295+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
296+ }
275297 }
276298
277299 /// Returns a copy of this image as an RGBA image.
278300 #[ must_use]
279301 pub fn to_rgba32f ( & self ) -> Rgba32FImage {
280- self . to ( )
302+ match self {
303+ DynamicImage :: ImageRgba32F ( x) => x. clone ( ) ,
304+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
305+ }
281306 }
282307
283308 /// Returns a copy of this image as a Luma image.
284309 #[ must_use]
285310 pub fn to_luma8 ( & self ) -> GrayImage {
286- self . to ( )
311+ match self {
312+ DynamicImage :: ImageLuma8 ( x) => x. clone ( ) ,
313+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
314+ }
287315 }
288316
289317 /// Returns a copy of this image as a Luma image.
290318 #[ must_use]
291319 pub fn to_luma16 ( & self ) -> Gray16Image {
292- self . to ( )
320+ match self {
321+ DynamicImage :: ImageLuma16 ( x) => x. clone ( ) ,
322+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
323+ }
293324 }
294325
295326 /// Returns a copy of this image as a Luma image.
296327 #[ must_use]
297328 pub fn to_luma32f ( & self ) -> ImageBuffer < Luma < f32 > , Vec < f32 > > {
298- self . to ( )
329+ dynamic_map ! ( self , ref p , p . cast_in_color_space ( ) )
299330 }
300331
301332 /// Returns a copy of this image as a `LumaA` image.
302333 #[ must_use]
303334 pub fn to_luma_alpha8 ( & self ) -> GrayAlphaImage {
304- self . to ( )
335+ match self {
336+ DynamicImage :: ImageLumaA8 ( x) => x. clone ( ) ,
337+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
338+ }
305339 }
306340
307341 /// Returns a copy of this image as a `LumaA` image.
308342 #[ must_use]
309343 pub fn to_luma_alpha16 ( & self ) -> GrayAlpha16Image {
310- self . to ( )
344+ match self {
345+ DynamicImage :: ImageLumaA16 ( x) => x. clone ( ) ,
346+ x => dynamic_map ! ( x, ref p, p. cast_in_color_space( ) ) ,
347+ }
311348 }
312349
313350 /// Returns a copy of this image as a `LumaA` image.
314351 #[ must_use]
315352 pub fn to_luma_alpha32f ( & self ) -> ImageBuffer < LumaA < f32 > , Vec < f32 > > {
316- self . to ( )
353+ dynamic_map ! ( self , ref p , p . cast_in_color_space ( ) )
317354 }
318355
319356 /// Consume the image and returns a RGB image.
@@ -662,7 +699,7 @@ impl DynamicImage {
662699 dynamic_map ! (
663700 * self ,
664701 ref image_buffer,
665- bytemuck:: cast_slice( image_buffer. as_raw( ) . as_ref ( ) )
702+ bytemuck:: cast_slice( image_buffer. as_raw( ) )
666703 )
667704 }
668705
@@ -1112,7 +1149,12 @@ impl DynamicImage {
11121149 // Try to no-op this transformation, we may be lucky..
11131150 if self . color_space ( ) == other. color_space ( ) {
11141151 // Nothing to transform, just rescale samples and type cast.
1115- dynamic_map ! ( self , ref mut p, * p = other. to( ) ) ;
1152+ dynamic_map ! (
1153+ self ,
1154+ ref mut p,
1155+ * p = dynamic_map!( other, ref o, o. cast_in_color_space( ) )
1156+ ) ;
1157+
11161158 return Ok ( ( ) ) ;
11171159 }
11181160
@@ -1951,6 +1993,42 @@ mod test {
19511993 assert_eq ! ( target[ ( 0 , 0 ) ] , Rgb ( [ 234u8 , 51 , 35 ] ) ) ;
19521994 }
19531995
1996+ #[ test]
1997+ fn into_luma_is_color_space_aware ( ) {
1998+ let mut buffer = super :: DynamicImage :: ImageRgb16 ( {
1999+ ImageBuffer :: from_fn ( 128 , 128 , |_, _| Rgb ( [ u16:: MAX , 0 , 0 ] ) )
2000+ } ) ;
2001+
2002+ buffer. set_rgb_primaries ( Cicp :: DISPLAY_P3 . primaries ) ;
2003+ buffer. set_transfer_function ( Cicp :: DISPLAY_P3 . transfer ) ;
2004+
2005+ let luma8 = buffer. clone ( ) . into_luma8 ( ) ;
2006+ assert_eq ! ( luma8[ ( 0 , 0 ) ] , Luma ( [ 58u8 ] ) ) ;
2007+
2008+ buffer. set_rgb_primaries ( Cicp :: SRGB . primaries ) ;
2009+
2010+ let luma8 = buffer. clone ( ) . into_luma8 ( ) ;
2011+ assert_eq ! ( luma8[ ( 0 , 0 ) ] , Luma ( [ 54u8 ] ) ) ;
2012+ }
2013+
2014+ #[ test]
2015+ fn from_luma_is_color_space_aware ( ) {
2016+ let mut buffer = super :: DynamicImage :: ImageLuma16 ( {
2017+ ImageBuffer :: from_fn ( 128 , 128 , |_, _| Luma ( [ u16:: MAX ] ) )
2018+ } ) ;
2019+
2020+ buffer. set_rgb_primaries ( Cicp :: DISPLAY_P3 . primaries ) ;
2021+ buffer. set_transfer_function ( Cicp :: DISPLAY_P3 . transfer ) ;
2022+
2023+ let rgb8 = buffer. clone ( ) . into_rgb8 ( ) ;
2024+ assert_eq ! ( rgb8[ ( 0 , 0 ) ] , Rgb ( [ 58u8 , 176u8 , 20u8 ] ) ) ;
2025+
2026+ buffer. set_rgb_primaries ( Cicp :: SRGB . primaries ) ;
2027+
2028+ let rgb8 = buffer. clone ( ) . into_rgb8 ( ) ;
2029+ assert_eq ! ( rgb8[ ( 0 , 0 ) ] , Rgb ( [ 54u8 , 182u8 , 18u8 ] ) ) ;
2030+ }
2031+
19542032 #[ test]
19552033 fn convert_color_space_coverage ( ) {
19562034 const TYPES : [ ColorType ; 10 ] = [
0 commit comments