@@ -2,8 +2,13 @@ use std::fs::File;
22use std:: io:: Cursor ;
33
44use clovers:: Float ;
5+ use exr:: {
6+ image:: { Encoding , Layer , PixelImage } ,
7+ meta:: attribute:: Chromaticities ,
8+ prelude:: { LayerAttributes , WritableImage } ,
9+ } ;
510use humantime:: FormattedDuration ;
6- use image:: { ImageBuffer , ImageFormat , Rgb32FImage , RgbImage } ;
11+ use image:: { ImageBuffer , ImageFormat , RgbImage } ;
712use img_parts:: png:: { Png , PngChunk } ;
813use palette:: { chromatic_adaptation:: AdaptInto , white_point:: E , Xyz } ;
914use tracing:: info;
@@ -60,7 +65,7 @@ pub fn png(
6065 let stats = format ! ( "Rendering finished in {duration}, using {threads} threads." ) ;
6166 let comment = format ! ( "{common}{details} {stats}" ) ;
6267
63- let software = "Software\0 https://github.com/walther/clovers " . to_string ( ) ;
68+ let software = "Software\0 clovers " . to_string ( ) ;
6469
6570 for metadata in [ comment, software] {
6671 let bytes = metadata. as_bytes ( ) . to_owned ( ) ;
@@ -76,35 +81,45 @@ pub fn png(
7681 Ok ( ( ) )
7782}
7883
79- pub fn exr (
80- pixelbuffer : & [ Xyz < E > ] ,
81- target : & String ,
82- _duration : & FormattedDuration ,
83- render_options : & RenderOptions ,
84- ) -> Result < ( ) , String > {
85- let RenderOptions {
86- input : _,
87- output : _,
88- width,
89- height,
90- samples : _,
91- max_depth : _,
92- mode : _,
93- sampler : _,
94- bvh : _,
95- formats : _,
96- } = render_options;
97- // TODO: metadata?
98-
84+ /// Save the pixelbuffer as an OpenEXR file.
85+ ///
86+ /// From the specification:
87+ /// > In an OpenEXR file whose pixels represent CIE XYZ tristimulus values,
88+ /// > the pixels’ X, Y and Z components should be stored in the file’s R, G and B channels.
89+ /// > The file header should contain a chromaticities attribute with the following values:
90+ /// > | | CIE x,y |
91+ /// > |-------|----------|
92+ /// > | red | 1, 0 |
93+ /// > | green | 0, 1 |
94+ /// > | blue | 0, 0 |
95+ /// > | white | 1/3, 1/3 |
96+ /// <cite><https://openexr.com/en/latest/TechnicalIntroduction.html#cie-xyz-color></cite>
97+ pub fn exr ( pixelbuffer : & [ Xyz < E > ] , width : u32 , height : u32 , target : & String ) -> Result < ( ) , String > {
9998 info ! ( "Converting pixelbuffer to an image" ) ;
100- let mut img: Rgb32FImage = ImageBuffer :: new ( * width, * height) ;
101- img. enumerate_pixels_mut ( ) . for_each ( |( x, y, pixel) | {
102- let index = y * width + x;
103- // NOTE: EXR format expects linear rgb
104- let color: palette:: LinSrgb < Float > = pixelbuffer[ index as usize ] . adapt_into ( ) ;
105- * pixel = image:: Rgb ( [ color. red , color. green , color. blue ] ) ;
99+ let dimensions = ( width as usize , height as usize ) ;
100+ let layer_attributes = LayerAttributes {
101+ // capture_date: todo!(),
102+ software_name : Some ( "clovers" . into ( ) ) ,
103+ ..Default :: default ( )
104+ } ;
105+ let encoding = Encoding :: SMALL_FAST_LOSSLESS ;
106+ let channels = exr:: prelude:: SpecificChannels :: build ( )
107+ . with_channel :: < f32 > ( "R" )
108+ . with_channel :: < f32 > ( "G" )
109+ . with_channel :: < f32 > ( "B" )
110+ . with_pixel_fn ( |coord| {
111+ let index = coord. y ( ) * width as usize + coord. x ( ) ;
112+ let pixel: Xyz < E > = pixelbuffer[ index] ;
113+ pixel. into_components ( )
114+ } ) ;
115+ let mut image =
116+ PixelImage :: from_layer ( Layer :: new ( dimensions, layer_attributes, encoding, channels) ) ;
117+ image. attributes . chromaticities = Some ( Chromaticities {
118+ red : ( 1.0 , 0.0 ) . into ( ) ,
119+ green : ( 0.0 , 1.0 ) . into ( ) ,
120+ blue : ( 0.0 , 0.0 ) . into ( ) ,
121+ white : ( 1.0 / 3.0 , 1.0 / 3.0 ) . into ( ) ,
106122 } ) ;
107-
108- img. save_with_format ( target, ImageFormat :: OpenExr )
109- . or ( Err ( "Unable to write to file" . to_owned ( ) ) )
123+ image. write ( ) . to_file ( target) . unwrap ( ) ;
124+ Ok ( ( ) )
110125}
0 commit comments