aboutsummaryrefslogtreecommitdiff
path: root/vendor/exr/examples
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/exr/examples
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/exr/examples')
-rw-r--r--vendor/exr/examples/0a_write_rgba.rs36
-rw-r--r--vendor/exr/examples/0b_read_meta.rs22
-rw-r--r--vendor/exr/examples/0c_read_rgba.rs30
-rw-r--r--vendor/exr/examples/1a_write_rgba_with_metadata.rs64
-rw-r--r--vendor/exr/examples/1b_convert_exr_to_png.rs53
-rw-r--r--vendor/exr/examples/2_rgba_adjust_exposure.rs80
-rw-r--r--vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs78
-rw-r--r--vendor/exr/examples/3b_read_all_channels_with_metadata.rs35
-rw-r--r--vendor/exr/examples/4a_write_custom_fixed_channels.rs41
-rw-r--r--vendor/exr/examples/4b_read_custom_fixed_channels.rs49
-rw-r--r--vendor/exr/examples/5a_write_multiple_layers.rs45
-rw-r--r--vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs74
-rw-r--r--vendor/exr/examples/5c_write_mip_maps.rs73
-rw-r--r--vendor/exr/examples/5d_write_legacy_layers.rs73
-rw-r--r--vendor/exr/examples/6_extract_mip_map_pngs.rs74
-rw-r--r--vendor/exr/examples/7_crop_alpha_any_image.rs46
-rw-r--r--vendor/exr/examples/7_crop_alpha_rgba.rs48
-rw-r--r--vendor/exr/examples/7_write_raw_blocks.rs115
-rw-r--r--vendor/exr/examples/8_read_raw_blocks.rs129
-rw-r--r--vendor/exr/examples/README.md24
20 files changed, 1189 insertions, 0 deletions
diff --git a/vendor/exr/examples/0a_write_rgba.rs b/vendor/exr/examples/0a_write_rgba.rs
new file mode 100644
index 0000000..daa4fcd
--- /dev/null
+++ b/vendor/exr/examples/0a_write_rgba.rs
@@ -0,0 +1,36 @@
+extern crate exr;
+
+
+/// `exr` offers a few simplified functions for the most basic use cases.
+/// `write_rgb_f32_file` is a such a function, which writes a plain rgba exr file.
+///
+/// To write your image data, you need to specify how to retrieve a single pixel from it.
+/// The closure may capture variables or generate data on the fly.
+fn main() {
+ use exr::prelude::*;
+
+ // write a file, with 32-bit float precision per channel
+ write_rgba_file(
+
+ // this accepts paths or &str
+ "minimal_rgba.exr",
+
+ // image resolution is 2k
+ 2048, 2048,
+
+ // generate (or lookup in your own image)
+ // an f32 rgb color for each of the 2048x2048 pixels
+ // (you could also create f16 values here to save disk space)
+ |x,y| {
+ (
+ x as f32 / 2048.0, // red
+ y as f32 / 2048.0, // green
+ 1.0 - (y as f32 / 2048.0), // blue
+ 1.0 // alpha
+ )
+ }
+
+ ).unwrap();
+
+ println!("created file minimal_rgb.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/0b_read_meta.rs b/vendor/exr/examples/0b_read_meta.rs
new file mode 100644
index 0000000..a20527f
--- /dev/null
+++ b/vendor/exr/examples/0b_read_meta.rs
@@ -0,0 +1,22 @@
+
+
+// exr imports
+extern crate exr;
+
+/// Print the custom meta data of a file, excluding technical encoding meta data.
+/// Prints compression method and tile size, but not purely technical data like chunk count.
+fn main() {
+ use exr::prelude::*;
+
+ let meta_data = MetaData::read_from_file(
+ "generated_rgba_with_meta.exr",
+ false // do not throw an error for invalid or missing attributes, skipping them instead
+ ).expect("run example `1_write_rgba_with_metadata` to generate the required file");
+
+ for (layer_index, image_layer) in meta_data.headers.iter().enumerate() {
+ println!(
+ "custom meta data of layer #{}:\n{:#?}",
+ layer_index, image_layer.own_attributes
+ );
+ }
+} \ No newline at end of file
diff --git a/vendor/exr/examples/0c_read_rgba.rs b/vendor/exr/examples/0c_read_rgba.rs
new file mode 100644
index 0000000..a2126f8
--- /dev/null
+++ b/vendor/exr/examples/0c_read_rgba.rs
@@ -0,0 +1,30 @@
+extern crate exr;
+
+/// `exr` offers a few simplified functions for the most basic use cases.
+/// `read_first_rgba_layer_from_file` is a such a function, which loads rgba exr files.
+/// To load the pixel data, you need to specify
+/// how to create and how to set the pixels of your image.
+fn main() {
+ let image = exr::prelude::read_first_rgba_layer_from_file(
+ "generated_rgba.exr",
+
+ // instantiate your image type with the size of the image in file
+ |resolution, _| {
+ let default_pixel = [0.0, 0.0, 0.0, 0.0];
+ let empty_line = vec![ default_pixel; resolution.width() ];
+ let empty_image = vec![ empty_line; resolution.height() ];
+ empty_image
+ },
+
+ // transfer the colors from the file to your image type,
+ // requesting all values to be converted to f32 numbers (you can also directly use f16 instead)
+ // and you could also use `Sample` instead of `f32` to keep the original data type from the file
+ |pixel_vector, position, (r,g,b, a): (f32, f32, f32, f32)| {
+ pixel_vector[position.y()][position.x()] = [r, g, b, a]
+ },
+
+ ).expect("run the `1_write_rgba` example to generate the required file");
+
+ // printing all pixels might kill the console, so only print some meta data about the image
+ println!("opened file generated_rgba.exr: {:#?}", image.layer_data.attributes);
+} \ No newline at end of file
diff --git a/vendor/exr/examples/1a_write_rgba_with_metadata.rs b/vendor/exr/examples/1a_write_rgba_with_metadata.rs
new file mode 100644
index 0000000..394410f
--- /dev/null
+++ b/vendor/exr/examples/1a_write_rgba_with_metadata.rs
@@ -0,0 +1,64 @@
+
+// exr imports
+extern crate exr;
+
+/// Write an rgba exr file, generating the pixel values on the fly.
+/// This streams the generated pixel directly to the file,
+/// never allocating the actual total pixel memory of the image.
+fn main() {
+ use exr::prelude::*;
+ use exr::meta::attribute::*;
+
+ // this function can generate a color for any pixel
+ let generate_pixels = |position: Vec2<usize>| (
+ position.x() as f32 / 2048.0, // red
+ position.y() as f32 / 2048.0, // green
+ 1.0 - (position.y() as f32 / 2048.0), // blue
+ 1.0 // alpha
+ );
+
+ let mut layer_attributes = LayerAttributes::named("generated rgba main layer");
+ layer_attributes.comments = Some(Text::from("This image was generated as part of an example"));
+ layer_attributes.owner = Some(Text::from("The holy lambda function"));
+ layer_attributes.software_name = Some(Text::from("EXRS Project"));
+ layer_attributes.exposure = Some(1.0);
+ layer_attributes.focus = Some(12.4);
+ layer_attributes.frames_per_second = Some((60, 1));
+ layer_attributes.other.insert(
+ Text::from("Layer Purpose (Custom Layer Attribute)"),
+ AttributeValue::Text(Text::from("This layer contains the rgb pixel data"))
+ );
+
+ let layer = Layer::new(
+ (2*2048, 2*2048),
+ layer_attributes,
+ Encoding::SMALL_FAST_LOSSLESS, // use fast but lossy compression
+
+ SpecificChannels::rgba(generate_pixels)
+ );
+
+ // crop away black and transparent pixels from the border, if any
+ let layer = layer
+ .crop_where_eq((0.0, 0.0, 0.0, 0.0))
+ .or_crop_to_1x1_if_empty();
+
+ let mut image = Image::from_layer(layer);
+ image.attributes.pixel_aspect = 1.0;
+
+ image.attributes.time_code = Some(TimeCode {
+ hours: 0,
+ minutes: 1,
+ seconds: 59,
+ frame: 29,
+ ..TimeCode::default()
+ });
+
+ image.attributes.other.insert(
+ Text::from("Mice Count (Custom Image Attribute)"),
+ AttributeValue::I32(23333)
+ );
+
+ // write it to a file with all cores in parallel
+ image.write().to_file("generated_rgba_with_meta.exr").unwrap();
+ println!("created file generated_rgba_with_meta.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/1b_convert_exr_to_png.rs b/vendor/exr/examples/1b_convert_exr_to_png.rs
new file mode 100644
index 0000000..c78716e
--- /dev/null
+++ b/vendor/exr/examples/1b_convert_exr_to_png.rs
@@ -0,0 +1,53 @@
+
+extern crate image as png;
+
+// exr imports
+extern crate exr;
+
+/// Converts one rgba exr with one layer to one png, or fail.
+fn main() {
+ use exr::prelude::*;
+ use exr::prelude as exrs;
+
+ // read from the exr file directly into a new `png::RgbaImage` image without intermediate buffers
+ let reader = exrs::read()
+ .no_deep_data()
+ .largest_resolution_level()
+ .rgba_channels(
+ |resolution, _channels: &RgbaChannels| -> png::RgbaImage {
+ png::ImageBuffer::new(
+ resolution.width() as u32,
+ resolution.height() as u32
+ )
+ },
+
+ // set each pixel in the png buffer from the exr file
+ |png_pixels, position, (r,g,b,a): (f32,f32,f32,f32)| { // TODO implicit argument types!
+ png_pixels.put_pixel(
+ position.x() as u32, position.y() as u32,
+ png::Rgba([tone_map(r), tone_map(g), tone_map(b), (a * 255.0) as u8])
+ );
+ }
+ )
+ .first_valid_layer()
+ .all_attributes();
+
+ // an image that contains a single layer containing an png rgba buffer
+ let image: Image<Layer<SpecificChannels<png::RgbaImage, RgbaChannels>>> = reader
+ .from_file("generated_rgba.exr")
+ .expect("run the `1_write_rgba` example to generate the required file");
+
+
+ /// compress any possible f32 into the range of [0,1].
+ /// and then convert it to an unsigned byte.
+ fn tone_map(linear: f32) -> u8 {
+ // TODO does the `image` crate expect gamma corrected data?
+ let clamped = (linear - 0.5).tanh() * 0.5 + 0.5;
+ (clamped * 255.0) as u8
+ }
+
+ // save the png buffer to a png file
+ let png_buffer = &image.layer_data.channel_data.pixels;
+ png_buffer.save("rgb.png").unwrap();
+ println!("created image rgb.png")
+} \ No newline at end of file
diff --git a/vendor/exr/examples/2_rgba_adjust_exposure.rs b/vendor/exr/examples/2_rgba_adjust_exposure.rs
new file mode 100644
index 0000000..c4e6097
--- /dev/null
+++ b/vendor/exr/examples/2_rgba_adjust_exposure.rs
@@ -0,0 +1,80 @@
+
+// exr imports
+extern crate exr;
+
+/// Read an rgba image, increase the exposure, and then write it back.
+/// Uses multi-core compression where appropriate.
+///
+/// All non-rgba channels and all layers except the first rgba layers will not be present in the new file.
+fn main() {
+ use exr::prelude::*;
+
+ /// This is an example of a custom image type.
+ /// You use your own image struct here.
+ // This struct trades sub-optimal memory-efficiency for clarity,
+ // because this is an example, and does not have to be perfectly efficient.
+ #[derive(Debug, PartialEq)]
+ struct CustomPixels { lines: Vec<Vec<RgbaF32Pixel>> }
+ type RgbaF32Pixel = (f32, f32, f32, f32);
+
+ // read the image from a file
+ let mut image = read().no_deep_data()
+ .largest_resolution_level()
+ .rgba_channels(
+ // create our custom image based on the file info
+ |resolution, _channels| -> CustomPixels {
+ let default_rgba_pixel = (0.0, 0.0, 0.0, 0.0);
+ let default_line = vec![default_rgba_pixel; resolution.width()];
+ let lines = vec![default_line; resolution.height()];
+ CustomPixels { lines }
+ },
+
+ // request pixels with red, green, blue, and optionally and alpha values.
+ // transfer each pixel from the file to our image
+ |image, position, (r,g,b,a): RgbaF32Pixel| {
+
+ // insert the values into our custom image
+ image.lines[position.y()][position.x()] = (r,g,b,a);
+ }
+ )
+ .first_valid_layer()
+ .all_attributes()
+ .from_file("generated_rgba.exr")
+ .expect("run the `1_write_rgba` example to generate the required file");
+
+ let exposure_multiplier = 2.0;
+
+ { // increase exposure of all pixels
+ for line in &mut image.layer_data.channel_data.pixels.lines {
+ for (r,g,b,_) in line {
+ // you should probably check the color space and white points
+ // for high quality color adjustments
+ *r *= exposure_multiplier;
+ *g *= exposure_multiplier;
+ *b *= exposure_multiplier;
+ }
+ }
+
+ // also update meta data after modifying the image
+ if let Some(exposure) = &mut image.layer_data.attributes.exposure {
+ println!("increased exposure from {}s to {}s", exposure, *exposure * exposure_multiplier);
+ *exposure *= exposure_multiplier;
+ }
+ }
+
+ // enable writing our custom pixel storage to a file
+ // FIXME this should be passed as a closure to the `write_with(|x| y)` call
+ impl GetPixel for CustomPixels {
+ type Pixel = RgbaF32Pixel;
+ fn get_pixel(&self, position: Vec2<usize>) -> Self::Pixel {
+ self.lines[position.y()][position.x()]
+ }
+ }
+
+ // write the image to a file
+ image
+ .write().to_file("rgba_exposure_adjusted.exr")
+ .unwrap();
+
+ println!("created file rgba_exposure_adjusted.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs b/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs
new file mode 100644
index 0000000..aedbb75
--- /dev/null
+++ b/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs
@@ -0,0 +1,78 @@
+
+#[macro_use]
+extern crate smallvec;
+extern crate rand;
+extern crate half;
+
+use rand::Rng;
+
+// exr imports
+extern crate exr;
+
+
+/// Generate a noisy image and write it to a file,
+/// also attaching some meta data.
+fn main() {
+ use exr::prelude::*;
+
+ fn generate_f16_vector(size: Vec2<usize>) -> Vec<f16> {
+ let mut values = vec![ f16::from_f32(0.5); size.area() ];
+
+ for _ in 0..(1024*1024/3)/4 {
+ let index = rand::thread_rng().gen_range(0 .. values.len());
+ let value = 1.0 / rand::random::<f32>() - 1.0;
+ let value = if !value.is_normal() || value > 1000.0 { 1000.0 } else { value };
+ values[index] = f16::from_f32(value);
+ }
+
+ values
+ }
+
+ let size = (1024, 512);
+
+ let r = AnyChannel::new(
+ "R", FlatSamples::F16(generate_f16_vector(size.into()))
+ );
+
+ let g = AnyChannel::new(
+ "G", FlatSamples::F16(generate_f16_vector(size.into()))
+ );
+
+ let b = AnyChannel::new(
+ "B", FlatSamples::F32(generate_f16_vector(size.into()).into_iter().map(f16::to_f32).collect())
+ );
+
+ let a = AnyChannel::new(
+ "A", FlatSamples::F32(generate_f16_vector(size.into()).into_iter().map(f16::to_f32).collect())
+ );
+
+ let mut layer_attributes = LayerAttributes::named("test-image");
+ layer_attributes.owner = Some(Text::from("It's you!"));
+ layer_attributes.comments = Some(Text::from("This image was procedurally generated"));
+
+ let layer = Layer::new(
+ size,
+ layer_attributes,
+ Encoding::default(),
+ AnyChannels::sort(smallvec![ r, g, b, a ]),
+ );
+
+ // crop away transparent pixels from the border
+ let layer = layer
+
+ // channel order is (a,b,g,r), as channels are already sorted
+ .crop_where(|samples| samples[0].is_zero())
+
+ // throw error if the image is 100% transparent pixels and should be removed
+ .or_none_if_empty().expect("image is empty and cannot be cropped");
+
+ let image = Image::from_layer(layer);
+
+ println!("writing image {:#?}", image);
+
+ image.write()
+ .on_progress(|progress| println!("progress: {:.1}", progress*100.0))
+ .to_file("noisy.exr").unwrap();
+
+ println!("created file noisy.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/3b_read_all_channels_with_metadata.rs b/vendor/exr/examples/3b_read_all_channels_with_metadata.rs
new file mode 100644
index 0000000..cd9fc04
--- /dev/null
+++ b/vendor/exr/examples/3b_read_all_channels_with_metadata.rs
@@ -0,0 +1,35 @@
+
+// exr imports
+extern crate exr;
+
+/// Read an image and print information about the image into the console.
+/// This example shows how to read an image with multiple layers and arbitrary channels.
+/// For example, a layer with XYZ channels, and additionally a separate Depth layer.
+/// This example does not include resolution levels (mipmaps or ripmaps).
+fn main() {
+ use exr::prelude::*;
+
+ let image = read().no_deep_data()
+ .largest_resolution_level().all_channels().all_layers().all_attributes()
+ .on_progress(|progress| println!("progress: {:.1}", progress*100.0))
+ .from_file("generated_rgba_with_meta.exr")
+ .expect("run example `1_write_rgba_with_metadata` to generate this image file");
+
+ println!("image was read: {:#?}", image);
+
+ // output the average value for each channel of each layer
+ for layer in &image.layer_data {
+ for channel in &layer.channel_data.list {
+
+ let sample_vec = &channel.sample_data;
+ let average = sample_vec.values_as_f32().sum::<f32>() / sample_vec.len() as f32;
+
+ if let Some(layer_name) = &layer.attributes.layer_name {
+ println!("Channel `{}` of Layer `{}` has an average value of {}", channel.name, layer_name, average);
+ }
+ else {
+ println!("Channel `{}` has an average value of {}", channel.name, average);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/exr/examples/4a_write_custom_fixed_channels.rs b/vendor/exr/examples/4a_write_custom_fixed_channels.rs
new file mode 100644
index 0000000..4eb6f8d
--- /dev/null
+++ b/vendor/exr/examples/4a_write_custom_fixed_channels.rs
@@ -0,0 +1,41 @@
+
+// exr imports
+extern crate exr;
+
+/// Create an image with strange channels and write it to a file.
+fn main() {
+ use exr::prelude::*;
+
+ let pixels = SpecificChannels::build()
+ .with_channel("Kharthanasus Korthus")
+ .with_channel("Y")
+ .with_channel("11023")
+ .with_channel("*?!")
+ .with_channel("`--\"")
+ .with_channel("\r\r\r\n\n")
+ .with_pixel_fn(|position|{
+ if position.0 < 1000 {
+ (f16::from_f32(0.2), 0.666_f32, 4_u32, 1532434.0213_f32, 0.99999_f32, 3.142594_f32/4.0)
+ }
+ else {
+ (f16::from_f32(0.4), 0.777_f32, 8_u32, 102154.3_f32, 0.00001_f32, 3.142594_f32/4.0)
+ }
+ });
+
+ let image = Image::from_channels((2000, 1400), pixels);
+
+ // print progress only if it advances more than 1%
+ let mut current_progress_percentage = 0;
+
+ image.write()
+ .on_progress(|progress| {
+ let new_progress = (progress * 100.0) as usize;
+ if new_progress != current_progress_percentage {
+ current_progress_percentage = new_progress;
+ println!("progress: {}%", current_progress_percentage)
+ }
+ })
+ .to_file("custom_channels.exr").unwrap();
+
+ println!("created file custom_channels.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/4b_read_custom_fixed_channels.rs b/vendor/exr/examples/4b_read_custom_fixed_channels.rs
new file mode 100644
index 0000000..a5fd273
--- /dev/null
+++ b/vendor/exr/examples/4b_read_custom_fixed_channels.rs
@@ -0,0 +1,49 @@
+
+// exr imports
+extern crate exr;
+
+/// Read an image and print information about the image into the console.
+/// This example shows how to read an image with multiple layers and specific channels.
+/// This example does not include resolution levels (mipmaps or ripmaps).
+fn main() {
+ use exr::prelude::*;
+
+ let image = read().no_deep_data()
+ .largest_resolution_level()
+
+ .specific_channels()
+ .optional("A", f16::ONE)
+ .required("Y") // TODO also accept a closure with a detailed selection mechanism
+ .optional("right.Y", 0.0)
+ .collect_pixels(
+ |resolution, (a_channel, y_channel, y_right_channel)| {
+ println!("image contains alpha channel? {}", a_channel.is_some());
+ println!("image contains stereoscopic luma channel? {}", y_right_channel.is_some());
+ println!("the type of luma samples is {:?}", y_channel.sample_type);
+
+ vec![vec![(f16::ZERO, 0.0, 0.0); resolution.width()]; resolution.height()]
+ },
+
+ // all samples will be converted to f32 (you can also use the enum `Sample` instead of `f32` here to retain the original data type from the file)
+ |vec, position, (a,y,yr): (f16, f32, f32)| {
+ vec[position.y()][position.x()] = (a, y, yr)
+ }
+ )
+
+ .all_layers()
+ .all_attributes()
+ .on_progress(|progress| println!("progress: {:.1}", progress*100.0))
+ .from_file("custom_channels.exr")
+ .expect("run example `4_write_custom_fixed_channels` to generate this image file");
+
+ // output a random color of each channel of each layer
+ for layer in &image.layer_data {
+ let (alpha, luma, luma_right) = layer.channel_data.pixels.first().unwrap().first().unwrap();
+
+ println!(
+ "top left color of layer `{}`: (a, y, yr) = {:?}",
+ layer.attributes.layer_name.clone().unwrap_or_default(),
+ (alpha.to_f32(), luma, luma_right)
+ )
+ }
+} \ No newline at end of file
diff --git a/vendor/exr/examples/5a_write_multiple_layers.rs b/vendor/exr/examples/5a_write_multiple_layers.rs
new file mode 100644
index 0000000..51e2aed
--- /dev/null
+++ b/vendor/exr/examples/5a_write_multiple_layers.rs
@@ -0,0 +1,45 @@
+
+extern crate smallvec;
+extern crate rand;
+extern crate half;
+
+
+// exr imports
+extern crate exr;
+
+/// Writes multiple layers into one exr file
+/// Note: this may not be supported by legacy software
+fn main() {
+ use exr::prelude::*;
+ let size = Vec2(512, 512);
+
+
+ let layer1 = Layer::new(
+ size,
+ LayerAttributes::named("teal rgb"),
+ Encoding::FAST_LOSSLESS,
+ SpecificChannels::rgb(|_pos| (0_f32, 0.4_f32, 0.4_f32)),
+ );
+
+ let layer2 = Layer::new(
+ size,
+ LayerAttributes::named("orange rgba"),
+ Encoding::FAST_LOSSLESS,
+ SpecificChannels::rgba(|_pos| (0.8_f32, 0.5_f32, 0.1_f32, 1.0_f32)),
+ );
+
+ // define the visible area of the canvas
+ let attributes = ImageAttributes::new(
+ // the pixel section that should be shown
+ IntegerBounds::from_dimensions(size)
+ );
+
+ let image = Image::empty(attributes)
+ .with_layer(layer1) // add an rgb layer of type `SpecificChannels<ClosureA>`
+ .with_layer(layer2); // add an rgba layer of different type, `SpecificChannels<ClosureB>`, not possible with a vector
+
+ println!("writing image...");
+ image.write().to_file("layers.exr").unwrap();
+
+ println!("created file layers.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs b/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs
new file mode 100644
index 0000000..1f14ea7
--- /dev/null
+++ b/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs
@@ -0,0 +1,74 @@
+extern crate image as png;
+use std::cmp::Ordering;
+
+extern crate exr;
+
+/// For each layer in the exr file,
+/// extract each channel as grayscale png,
+/// including all multi-resolution levels.
+//
+// FIXME throws "access denied" sometimes, simply trying again usually works.
+//
+pub fn main() {
+ use exr::prelude::*;
+
+ let path = "layers.exr";
+ let now = ::std::time::Instant::now();
+
+ // load the exr file from disk with multi-core decompression
+ let image = read()
+ .no_deep_data().largest_resolution_level().all_channels().all_layers().all_attributes()
+ .from_file(path).expect("run example `5a_write_multiple_layers` to generate this image file");
+
+ // warning: highly unscientific benchmarks ahead!
+ println!("\nloaded file in {:?}s", now.elapsed().as_secs_f32());
+ let _ = std::fs::create_dir_all("pngs/");
+ println!("writing images...");
+
+ for (layer_index, layer) in image.layer_data.iter().enumerate() {
+ let layer_name = layer.attributes.layer_name.as_ref()
+ .map_or(String::from("main_layer"), Text::to_string);
+
+ for channel in &layer.channel_data.list {
+ let data : Vec<f32> = channel.sample_data.values_as_f32().collect();
+ save_f32_image_as_png(&data, layer.size, format!(
+ "pngs/{} ({}) {}_{}x{}.png",
+ layer_index, layer_name, channel.name,
+ layer.size.width(), layer.size.height(),
+ ))
+ }
+ }
+
+ /// Save raw float data to a PNG file, doing automatic brightness adjustments per channel
+ fn save_f32_image_as_png(data: &[f32], size: Vec2<usize>, name: String) {
+ let mut png_buffer = png::GrayImage::new(size.width() as u32, size.height() as u32);
+ let mut sorted = Vec::from(data);
+ sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));
+
+ // percentile normalization
+ let max = sorted[7 * sorted.len() / 8];
+ let min = sorted[1 * sorted.len() / 8];
+
+ // primitive tone mapping
+ let tone = |v: f32| (v - 0.5).tanh() * 0.5 + 0.5;
+ let max_toned = tone(*sorted.last().unwrap());
+ let min_toned = tone(*sorted.first().unwrap());
+
+ // for each pixel, tone map the value
+ for (x, y, pixel) in png_buffer.enumerate_pixels_mut() {
+ let v = data[y as usize * size.0 + x as usize];
+ let v = (v - min) / (max - min);
+ let v = tone(v);
+
+ let v = (v - min_toned) / (max_toned - min_toned);
+
+ // TODO does the `image` crate expect gamma corrected data?
+ *pixel = png::Luma([(v.max(0.0).min(1.0) * 255.0) as u8]);
+ }
+
+ png_buffer.save(&name).unwrap();
+ }
+
+ println!("extracted all layers to folder `./pngs/*.png`");
+}
+
diff --git a/vendor/exr/examples/5c_write_mip_maps.rs b/vendor/exr/examples/5c_write_mip_maps.rs
new file mode 100644
index 0000000..1096c4e
--- /dev/null
+++ b/vendor/exr/examples/5c_write_mip_maps.rs
@@ -0,0 +1,73 @@
+
+extern crate smallvec;
+extern crate rand;
+extern crate half;
+
+
+// exr imports
+extern crate exr;
+
+
+
+/// Writes two layers, each with multiple mip maps.
+/// All mip maps have solid color for brevity.
+fn main() {
+ use exr::prelude::*;
+ use exr::math::RoundingMode;
+ use smallvec::smallvec;
+
+ let full_size = Vec2(512, 512);
+ let size_rounding = RoundingMode::Up;
+
+ let mip_levels_sizes = exr::meta::mip_map_levels(
+ size_rounding, full_size
+ ).collect::<Vec<_>>();
+
+ let red_mip_levels = mip_levels_sizes.iter()
+ .map(|(_index, level_size)|{
+ FlatSamples::F32(vec![0.1_f32; level_size.area() ])
+ })
+ .collect();
+
+ let green_mip_levels = mip_levels_sizes.iter()
+ .map(|(_index, level_size)|{
+ FlatSamples::F32(vec![0.6_f32; level_size.area() ])
+ })
+ .collect();
+
+ let blue_mip_levels = mip_levels_sizes.iter()
+ .map(|(_index, level_size)|{
+ FlatSamples::F32(vec![1.0_f32; level_size.area() ])
+ })
+ .collect();
+
+ let rgb_mip_maps = AnyChannels::sort(smallvec![
+ AnyChannel::new("R", Levels::Mip { level_data: red_mip_levels, rounding_mode: size_rounding }),
+ AnyChannel::new("G", Levels::Mip { level_data: green_mip_levels, rounding_mode: size_rounding }),
+ AnyChannel::new("B", Levels::Mip { level_data: blue_mip_levels, rounding_mode: size_rounding }),
+ ]);
+
+ let layer1 = Layer::new(
+ full_size,
+ LayerAttributes::named("teal rgb"),
+ Encoding::FAST_LOSSLESS,
+ rgb_mip_maps
+ );
+
+ let mut layer2 = layer1.clone();
+ layer2.attributes.layer_name = Some("Copied Layer".into());
+ layer2.encoding = Encoding::SMALL_FAST_LOSSLESS;
+
+ // define the visible area of the canvas
+ let image_attributes = ImageAttributes::new(
+ IntegerBounds::from_dimensions(full_size)
+ );
+
+ let image = Image::empty(image_attributes)
+ .with_layer(layer1).with_layer(layer2);
+
+ println!("writing image...");
+ image.write().to_file("mip_maps.exr").unwrap();
+
+ println!("created file mip_maps.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/5d_write_legacy_layers.rs b/vendor/exr/examples/5d_write_legacy_layers.rs
new file mode 100644
index 0000000..7276c7f
--- /dev/null
+++ b/vendor/exr/examples/5d_write_legacy_layers.rs
@@ -0,0 +1,73 @@
+
+#[macro_use]
+extern crate smallvec;
+extern crate rand;
+extern crate half;
+
+
+// exr imports
+extern crate exr;
+
+// TODO create a dedicated reader and writer for this scenario
+
+/// Generate an image with channel groups and write it to a file.
+/// Some legacy software may group layers that contain a `.` in the layer name.
+///
+/// Note: This is an OpenEXR legacy strategy. OpenEXR supports layers natively since 2013.
+/// Use the natively supported exrs `Layer` types instead, if possible.
+///
+fn main() {
+ use exr::prelude::*;
+ // TODO simplify handling these types of layers using read() and write()
+
+ let size = Vec2(512, 512);
+
+ let create_channel = |name: &str| -> AnyChannel<FlatSamples> {
+ let color: f16 = f16::from_bits(rand::random::<u16>());
+
+ AnyChannel::new(
+ name,
+ FlatSamples::F16(vec![color; size.area() ])
+ )
+ };
+
+
+ // The channels have the following structure:
+ //
+ // - Object
+ // - Red
+ // - Green
+ // - Blue
+ // - Alpha
+
+ // - Background
+ // - Red
+ // - Green
+ // - Blue
+
+ let foreground_r = create_channel("Object.R");
+ let foreground_g = create_channel("Object.G");
+ let foreground_b = create_channel("Object.B");
+ let foreground_a = create_channel("Object.A");
+
+ let background_r = create_channel("Background.R");
+ let background_g = create_channel("Background.G");
+ let background_b = create_channel("Background.B");
+
+ let layer = Layer::new(
+ size,
+ LayerAttributes::named("test-image"),
+ Encoding::FAST_LOSSLESS,
+ AnyChannels::sort(smallvec![ // the order does not actually matter
+ foreground_r, foreground_g, foreground_b, foreground_a,
+ background_r, background_g, background_b
+ ]),
+ );
+
+ let image = Image::from_layer(layer);
+
+ println!("writing image {:#?}", image);
+ image.write().to_file("groups.exr").unwrap();
+
+ println!("created file groups.exr");
+} \ No newline at end of file
diff --git a/vendor/exr/examples/6_extract_mip_map_pngs.rs b/vendor/exr/examples/6_extract_mip_map_pngs.rs
new file mode 100644
index 0000000..152c7c1
--- /dev/null
+++ b/vendor/exr/examples/6_extract_mip_map_pngs.rs
@@ -0,0 +1,74 @@
+
+extern crate image as png;
+use std::cmp::Ordering;
+
+extern crate exr;
+
+/// Extract all exr pixel information into pngs.
+/// Writes each channel of each mip map of each layer as one grayscale png.
+/// May appear black for single-color images.
+pub fn main() {
+ use exr::prelude::*;
+
+ let path = "mip_maps.exr";
+ let start_time = ::std::time::Instant::now();
+
+ // load the exr file from disk with multi-core decompression
+ let image = read_all_data_from_file(path)
+ .expect("run example `5c_write_mip_maps` to generate this image file");
+
+ // warning: highly unscientific benchmarks ahead!
+ println!("\nloaded file in {:?}s", start_time.elapsed().as_secs_f32());
+ let _ = std::fs::create_dir_all("pngs/");
+ println!("writing images...");
+
+ for (layer_index, layer) in image.layer_data.iter().enumerate() {
+ let layer_name = layer.attributes.layer_name.as_ref()
+ .map_or(String::from("1"), Text::to_string);
+
+ for channel in &layer.channel_data.list {
+ for (level, level_size) in layer.levels_with_resolution(&channel.sample_data) {
+ let data : Vec<f32> = level.values_as_f32().collect();
+
+ save_f32_image_as_png(&data, level_size, format!(
+ "pngs/{} ({}) {}.{}x{}.png",
+ layer_index, layer_name, channel.name,
+ level_size.width(), level_size.height(),
+ ))
+ }
+ }
+ }
+
+ /// Save raw float data to a PNG file, doing automatic brightness adjustments per channel
+ fn save_f32_image_as_png(data: &[f32], size: Vec2<usize>, name: String) {
+ let mut png_buffer = png::GrayImage::new(size.width() as u32, size.height() as u32);
+ let mut sorted = Vec::from(data);
+ sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));
+
+ // percentile normalization
+ let max = sorted[7 * sorted.len() / 8];
+ let min = sorted[1 * sorted.len() / 8];
+
+ // primitive tone mapping
+ let tone = |v: f32| (v - 0.5).tanh() * 0.5 + 0.5;
+ let max_toned = tone(*sorted.last().unwrap());
+ let min_toned = tone(*sorted.first().unwrap());
+
+ // for each pixel, tone map the value
+ for (x, y, pixel) in png_buffer.enumerate_pixels_mut() {
+ let v = data[y as usize * size.0 + x as usize];
+ let v = (v - min) / (max - min);
+ let v = tone(v);
+
+ let v = (v - min_toned) / (max_toned - min_toned);
+
+ // TODO does the `image` crate expect gamma corrected data?
+ *pixel = png::Luma([(v.max(0.0).min(1.0) * 255.0) as u8]);
+ }
+
+ png_buffer.save(&name).unwrap();
+ }
+
+ println!("extracted all layers to folder `./pngs/*.png`");
+}
+
diff --git a/vendor/exr/examples/7_crop_alpha_any_image.rs b/vendor/exr/examples/7_crop_alpha_any_image.rs
new file mode 100644
index 0000000..726beda
--- /dev/null
+++ b/vendor/exr/examples/7_crop_alpha_any_image.rs
@@ -0,0 +1,46 @@
+
+extern crate image as png;
+
+extern crate exr;
+
+/// Read an arbitrary image, crop away transparent pixels,
+/// then write the cropped result to another file.
+pub fn main() {
+ use exr::prelude::*;
+
+ let path = "tests/images/valid/custom/oh crop.exr";
+
+ // loads any image (excluding deep data)
+ let image: FlatImage = read_all_flat_layers_from_file(path)
+ .expect("this file exists in the exrs repository. download that?");
+
+ // construct a cropped image
+ let image = Image {
+ attributes: image.attributes,
+
+ // crop each layer
+ layer_data: image.layer_data.into_iter().map(|layer|{
+ println!("cropping layer {:#?}", layer);
+
+ // find the alpha channel of the layer
+ let alpha_channel_index = layer.channel_data.list.iter()
+ .position(|channel| channel.name.eq_case_insensitive("A"));
+
+ // if has alpha, crop it where alpha is zero
+ if let Some(alpha_channel_index) = alpha_channel_index {
+ layer.crop_where(|pixel: FlatSamplesPixel| pixel[alpha_channel_index].is_zero())
+ .or_crop_to_1x1_if_empty() // do not remove empty layers from image, because it could result in an image without content
+ .reallocate_cropped() // actually perform the crop operation
+ }
+ else {
+ // return the original layer, as no alpha channel can be used for cropping
+ layer
+ }
+
+ }).collect::<Layers<_>>(),
+ };
+
+ image.write().to_file("cropped.exr").unwrap();
+ println!("cropped file to cropped.exr");
+}
+
diff --git a/vendor/exr/examples/7_crop_alpha_rgba.rs b/vendor/exr/examples/7_crop_alpha_rgba.rs
new file mode 100644
index 0000000..18478d3
--- /dev/null
+++ b/vendor/exr/examples/7_crop_alpha_rgba.rs
@@ -0,0 +1,48 @@
+
+extern crate image as png;
+
+extern crate exr;
+
+
+/// Read an rgba image, or fail if none can be found.
+/// Then crop away transparent pixels,
+/// and write the cropped result to another file.
+/// This retains only the rgb pixels, and no other layers.
+pub fn main() {
+ use exr::prelude::*;
+ use exr::image::pixel_vec::*; // import predefined pixel storage
+
+ let path = "tests/images/valid/custom/oh crop.exr";
+
+ type DynamicRgbaPixel = (Sample, Sample, Sample, Sample); // `Sample` is an enum containing the original data type (f16,f32, or u32)
+
+ // load an rgba image
+ // this specific example discards all but the first valid rgb layers and converts all pixels to f32 values
+ // TODO optional alpha channel!
+ let image: PixelImage<PixelVec<DynamicRgbaPixel>, RgbaChannels> = read_first_rgba_layer_from_file(
+ path,
+ PixelVec::<DynamicRgbaPixel>::constructor,
+
+ // use this predefined rgba pixel container from the exr crate, requesting any type of pixels with 3 or 4 values
+ PixelVec::set_pixel
+ ).expect("this file exists in the exrs repository. download that?");
+
+ // construct a ~simple~ cropped image
+ let image: Image<Layer<CroppedChannels<SpecificChannels<PixelVec<DynamicRgbaPixel>, RgbaChannels>>>> = Image {
+ attributes: image.attributes,
+
+ // crop each layer
+ layer_data: {
+ println!("cropping layer {:#?}", image.layer_data);
+
+ // if has alpha, crop it where alpha is zero
+ image.layer_data
+ .crop_where(|(_r, _g, _b, alpha)| alpha.is_zero())
+ .or_crop_to_1x1_if_empty() // do not remove empty layers from image, because it could result in an image without content
+ },
+ };
+
+ image.write().to_file("cropped_rgba.exr").unwrap();
+ println!("cropped file to cropped_rgba.exr");
+}
+
diff --git a/vendor/exr/examples/7_write_raw_blocks.rs b/vendor/exr/examples/7_write_raw_blocks.rs
new file mode 100644
index 0000000..4cb2646
--- /dev/null
+++ b/vendor/exr/examples/7_write_raw_blocks.rs
@@ -0,0 +1,115 @@
+
+#[macro_use]
+extern crate smallvec;
+extern crate rand;
+extern crate half;
+
+use std::convert::TryInto;
+use std::io::BufWriter;
+use std::fs::File;
+use exr::block::{UncompressedBlock};
+use exr::block::writer::ChunksWriter;
+
+// exr imports
+extern crate exr;
+
+/// Generate a striped image on the fly and directly write that to a file without allocating the whole image at once.
+/// On my machine, this program produces a 3GB file while only ever allocating 4MB memory (takes a while though).
+fn main() {
+ use exr::prelude::*;
+ use attribute::*;
+ use exr::math::*;
+
+ // pre-compute a list of random values
+ let random_values: Vec<f32> = (0..64)
+ .map(|_| rand::random::<f32>())
+ .collect();
+
+ // resulting resolution (268 megapixels for 3GB files)
+ let size = (2048*8, 2048*8);
+
+ // define meta data header that will be written
+ let header = exr::meta::header::Header::new(
+ "test-image".try_into().unwrap(),
+ size,
+ smallvec![
+ attribute::ChannelDescription::new("B", SampleType::F32, true),
+ attribute::ChannelDescription::new("G", SampleType::F32, true),
+ attribute::ChannelDescription::new("R", SampleType::F32, true),
+ attribute::ChannelDescription::new("Z", SampleType::F32, true),
+ ],
+ );
+
+ // define encoding that will be written
+ let mut header = header.with_encoding(
+ Compression::Uncompressed,
+
+ exr::meta::BlockDescription::Tiles(TileDescription {
+ tile_size: Vec2(64, 64),
+ level_mode: LevelMode::Singular,
+ rounding_mode: RoundingMode::Down
+ }),
+
+ LineOrder::Increasing
+ );
+
+ // add some random meta data
+ header.own_attributes.exposure = Some(1.0);
+
+
+ let headers = smallvec![ header ];
+
+ // specify output path, and buffer it for better performance
+ let file = BufWriter::new(File::create("3GB.exr").unwrap());
+
+ let start_time = ::std::time::Instant::now();
+
+ // finally write the image
+ exr::block::write(
+ file, headers, true,
+ |meta_data, chunk_writer|{
+
+
+ let blocks = meta_data.collect_ordered_blocks(|block_index|{
+ let channel_description = &meta_data.headers[block_index.layer].channels;
+
+ // fill the image file contents with one of the precomputed random values,
+ // picking a different one per channel
+ UncompressedBlock::from_lines(channel_description, block_index, |line_mut|{
+ // TODO iterate mut instead??
+
+ let chan = line_mut.location.channel;
+
+ if chan == 3 { // write time as depth (could also check for _meta.channels[chan].name == "Z")
+ line_mut.write_samples(|_| start_time.elapsed().as_secs_f32())
+ .expect("write to line bug");
+ }
+
+ else { // write rgba color
+ line_mut
+ .write_samples(|sample_index| random_values[(sample_index + chan) % random_values.len()])
+ .expect("write to line bug");
+ }
+ })
+ });
+
+ // print progress only if it advances more than 1%
+ let mut current_progress_percentage = 0;
+
+ chunk_writer
+ .on_progress(|progress|{
+ let new_progress = (progress * 100.0) as usize;
+ if new_progress != current_progress_percentage {
+ current_progress_percentage = new_progress;
+ println!("progress: {}%", current_progress_percentage)
+ }
+ })
+ .compress_all_blocks_parallel(&meta_data, blocks)?;
+
+ Ok(())
+ }
+ ).unwrap();
+
+ // warning: highly unscientific benchmarks ahead!
+ println!("\ncreated file 3GB.exr in {:?}s", start_time.elapsed().as_secs_f32());
+}
diff --git a/vendor/exr/examples/8_read_raw_blocks.rs b/vendor/exr/examples/8_read_raw_blocks.rs
new file mode 100644
index 0000000..4864c98
--- /dev/null
+++ b/vendor/exr/examples/8_read_raw_blocks.rs
@@ -0,0 +1,129 @@
+
+extern crate rand;
+extern crate half;
+
+use std::io::{BufReader};
+use std::fs::File;
+use exr::block::reader::ChunksReader;
+
+// exr imports
+extern crate exr;
+
+
+/// Collects the average pixel value for each channel.
+/// Does not load the whole image into memory at once: only processes the image block by block.
+/// On my machine, this program analyzes a 3GB file while only allocating 1.1MB.
+fn main() {
+ use exr::prelude::*;
+
+ let file = BufReader::new(
+ File::open("3GB.exr")
+ .expect("run example `7_write_raw_blocks` to generate this image file")
+ );
+
+
+ // -- the following structs will hold the collected data from the image --
+
+ /// Collect averages for each layer in the image
+ #[derive(Debug)]
+ struct Layer {
+ #[allow(unused)] // note: is used in Debug impl
+ layer_name: Option<Text>,
+
+ data_window: IntegerBounds,
+
+ /// Collect one average float per channel in the layer
+ channels: Vec<Channel>,
+ }
+
+ /// A single channel in the layer, holds a single average value
+ #[derive(Debug)]
+ struct Channel {
+ #[allow(unused)] // note: is used in Debug impl
+ channel_name: Text,
+
+ sample_type: SampleType, // f32, u32, or f16
+ average: f32,
+ }
+
+ let start_time = ::std::time::Instant::now();
+
+
+ // -- read the file, summing up the average pixel values --
+
+ // start reading the file, extracting the meta data of the image
+ let reader = exr::block::read(file, true).unwrap();
+
+ // print progress only if it advances more than 1%
+ let mut current_progress_percentage = 0;
+
+ // create the empty data structure that will collect the analyzed results,
+ // based on the extracted meta data of the file
+ let mut averages = reader.headers().iter()
+ // create a layer for each header in the file
+ .map(|header| Layer {
+ layer_name: header.own_attributes.layer_name.clone(),
+ data_window: header.data_window(),
+
+ // create a averaging channel for each channel in the file
+ channels: header.channels.list.iter()
+ .map(|channel| Channel {
+ channel_name: channel.name.clone(),
+ sample_type: channel.sample_type,
+ average: 0.0
+ })
+ .collect()
+ })
+ .collect::<Vec<_>>();
+
+ // create a reader that loads only relevant chunks from the file, and also prints something on progress
+ let reader = reader
+
+ // do not worry about multi-resolution levels or deep data
+ .filter_chunks(true, |meta_data, tile, block| {
+ let header = &meta_data.headers[block.layer];
+ !header.deep && tile.is_largest_resolution_level()
+ }).unwrap()
+
+ .on_progress(|progress|{
+ let new_progress = (progress * 100.0) as usize;
+ if new_progress != current_progress_percentage {
+ current_progress_percentage = new_progress;
+ println!("progress: {}%", current_progress_percentage)
+ }
+ });
+
+ // read all pixel blocks from the image, decompressing in parallel
+ reader.decompress_parallel(true, |meta_data, block|{
+ let header = &meta_data.headers[block.index.layer];
+
+ // collect all pixel values from the pixel block
+ for line in block.lines(&header.channels) {
+ let layer = &mut averages[line.location.layer];
+ let channel = &mut layer.channels[line.location.channel];
+ let channel_sample_count = layer.data_window.size.area() as f32;
+
+ // now sum the average based on the values in this line section of pixels
+ match channel.sample_type {
+ SampleType::F16 => for value in line.read_samples::<f16>() {
+ channel.average += value?.to_f32() / channel_sample_count;
+ },
+
+ SampleType::F32 => for value in line.read_samples::<f32>() {
+ channel.average += value? / channel_sample_count;
+ },
+
+ SampleType::U32 => for value in line.read_samples::<u32>() {
+ channel.average += (value? as f32) / channel_sample_count;
+ },
+ }
+ }
+
+ Ok(())
+ }).unwrap();
+
+ println!("average values: {:#?}", averages);
+
+ // warning: highly unscientific benchmarks ahead!
+ println!("\nprocessed file in {:?}s", start_time.elapsed().as_secs_f32());
+} \ No newline at end of file
diff --git a/vendor/exr/examples/README.md b/vendor/exr/examples/README.md
new file mode 100644
index 0000000..7e9453e
--- /dev/null
+++ b/vendor/exr/examples/README.md
@@ -0,0 +1,24 @@
+# Examples
+
+These are examples that demonstrate how to use `exrs`.
+
+Some of these examples read image files
+that can be generated by running a different example beforehand.
+The examples are named such that running all examples alphabetically
+will generate any image before it is used.
+
+Only the cropping examples use images from the source repository's test folder.
+
+## Things you can find in the examples:
+- Read image data into a custom data structure, without loosing any meta information:
+ `2_rgba_adjust_exposure`
+- Access all pixel information in a file, fully dynamic:
+ `6_extract_mip_map_pngs`
+
+
+## Older Versions
+The examples for any specific `exrs` version can be found on the `docs.rs` page:
+- [docs.rs/crate/exr/1.71.0/source/examples/](https://docs.rs/crate/exr/1.7.0/source/examples/)
+- [docs.rs/crate/exr/1.7.0/source/examples/](https://docs.rs/crate/exr/1.7.0/source/examples/)
+- [docs.rs/crate/exr/1.6.5/source/examples/](https://docs.rs/crate/exr/1.6.5/source/examples/)
+- ... \ No newline at end of file