summaryrefslogtreecommitdiff
path: root/vendor/exr/src/image/write
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/exr/src/image/write')
-rw-r--r--vendor/exr/src/image/write/channels.rs407
-rw-r--r--vendor/exr/src/image/write/layers.rs188
-rw-r--r--vendor/exr/src/image/write/mod.rs184
-rw-r--r--vendor/exr/src/image/write/samples.rs205
4 files changed, 984 insertions, 0 deletions
diff --git a/vendor/exr/src/image/write/channels.rs b/vendor/exr/src/image/write/channels.rs
new file mode 100644
index 0000000..2450f09
--- /dev/null
+++ b/vendor/exr/src/image/write/channels.rs
@@ -0,0 +1,407 @@
+//! How to read arbitrary channels and rgb channels.
+
+use crate::prelude::*;
+use crate::io::*;
+use crate::math::*;
+use crate::meta::{header::*, attribute::*};
+use crate::block::*;
+use crate::image::recursive::*;
+use crate::block::samples::*;
+use crate::image::write::samples::*;
+
+use std::marker::PhantomData;
+
+
+/// Enables an image containing this list of channels to be written to a file.
+pub trait WritableChannels<'slf> {
+
+ /// Generate the file meta data for this list of channel
+ fn infer_channel_list(&self) -> ChannelList;
+
+ /// Generate the file meta data of whether and how resolution levels should be stored in the file
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode);
+
+ /// The type of temporary writer
+ type Writer: ChannelsWriter;
+
+ /// Create a temporary writer for this list of channels
+ fn create_writer(&'slf self, header: &Header) -> Self::Writer;
+}
+
+/// A temporary writer for a list of channels
+pub trait ChannelsWriter: Sync {
+
+ /// Deliver a block of pixels, containing all channel data, to be stored in the file
+ fn extract_uncompressed_block(&self, header: &Header, block: BlockIndex) -> Vec<u8>; // TODO return uncompressed block?
+}
+
+
+/// Define how to get a pixel from your custom pixel storage.
+/// Can be a closure of type [`Sync + Fn(Vec2<usize>) -> YourPixel`].
+pub trait GetPixel: Sync {
+
+ /// The pixel tuple containing `f32`, `f16`, `u32` and `Sample` values.
+ /// The length of the tuple must match the number of channels in the image.
+ type Pixel;
+
+ /// Inspect a single pixel at the requested position.
+ /// Will be called exactly once for each pixel in the image.
+ /// The position will not exceed the image dimensions.
+ /// Might be called from multiple threads at the same time.
+ fn get_pixel(&self, position: Vec2<usize>) -> Self::Pixel;
+}
+
+impl<F, P> GetPixel for F where F: Sync + Fn(Vec2<usize>) -> P {
+ type Pixel = P;
+ fn get_pixel(&self, position: Vec2<usize>) -> P { self(position) }
+}
+
+impl<'samples, Samples> WritableChannels<'samples> for AnyChannels<Samples>
+ where Samples: 'samples + WritableSamples<'samples>
+{
+ fn infer_channel_list(&self) -> ChannelList {
+ ChannelList::new(self.list.iter().map(|channel| ChannelDescription {
+ name: channel.name.clone(),
+ sample_type: channel.sample_data.sample_type(),
+ quantize_linearly: channel.quantize_linearly,
+ sampling: channel.sampling
+ }).collect())
+ }
+
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode) {
+ let mode = self.list.iter().next().expect("zero channels in list").sample_data.infer_level_modes();
+
+ debug_assert!(
+ std::iter::repeat(mode).zip(self.list.iter().skip(1))
+ .all(|(first, other)| other.sample_data.infer_level_modes() == first),
+
+ "level mode must be the same across all levels (do not nest resolution levels!)"
+ );
+
+ mode
+ }
+
+ type Writer = AnyChannelsWriter<Samples::Writer>;
+ fn create_writer(&'samples self, header: &Header) -> Self::Writer {
+ let channels = self.list.iter()
+ .map(|chan| chan.sample_data.create_samples_writer(header))
+ .collect();
+
+ AnyChannelsWriter { channels }
+ }
+}
+
+/// A temporary writer for an arbitrary list of channels
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct AnyChannelsWriter<SamplesWriter> {
+ channels: SmallVec<[SamplesWriter; 4]>
+}
+
+impl<Samples> ChannelsWriter for AnyChannelsWriter<Samples> where Samples: SamplesWriter {
+ fn extract_uncompressed_block(&self, header: &Header, block_index: BlockIndex) -> Vec<u8> {
+ UncompressedBlock::collect_block_data_from_lines(&header.channels, block_index, |line_ref| {
+ self.channels[line_ref.location.channel].extract_line(line_ref)
+ })
+ }
+}
+
+
+
+
+
+
+impl<'c, Channels, Storage>
+WritableChannels<'c> for SpecificChannels<Storage, Channels>
+where
+ Storage: 'c + GetPixel,
+ Storage::Pixel: IntoRecursive,
+ Channels: 'c + Sync + Clone + IntoRecursive,
+ <Channels as IntoRecursive>::Recursive: WritableChannelsDescription<<Storage::Pixel as IntoRecursive>::Recursive>,
+{
+ fn infer_channel_list(&self) -> ChannelList {
+ let mut vec = self.channels.clone().into_recursive().channel_descriptions_list();
+ vec.sort_unstable_by_key(|channel:&ChannelDescription| channel.name.clone()); // TODO no clone?
+
+ debug_assert!(
+ // check for equal neighbors in sorted vec
+ vec.iter().zip(vec.iter().skip(1)).all(|(prev, next)| prev.name != next.name),
+ "specific channels contain duplicate channel names"
+ );
+
+ ChannelList::new(vec)
+ }
+
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode) {
+ (LevelMode::Singular, RoundingMode::Down) // TODO
+ }
+
+ type Writer = SpecificChannelsWriter<
+ 'c,
+ <<Channels as IntoRecursive>::Recursive as WritableChannelsDescription<<Storage::Pixel as IntoRecursive>::Recursive>>::RecursiveWriter,
+ Storage,
+ Channels
+ >;
+
+ fn create_writer(&'c self, header: &Header) -> Self::Writer {
+ SpecificChannelsWriter {
+ channels: self,
+ recursive_channel_writer: self.channels.clone().into_recursive().create_recursive_writer(&header.channels),
+ }
+ }
+}
+
+
+
+/// A temporary writer for a layer of channels, alpha being optional
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct SpecificChannelsWriter<'channels, PixelWriter, Storage, Channels> {
+ channels: &'channels SpecificChannels<Storage, Channels>, // TODO this need not be a reference?? impl writer for specific_channels directly?
+ recursive_channel_writer: PixelWriter,
+}
+
+
+impl<'channels, PxWriter, Storage, Channels> ChannelsWriter
+for SpecificChannelsWriter<'channels, PxWriter, Storage, Channels>
+ where
+ Channels: Sync,
+ Storage: GetPixel,
+ Storage::Pixel: IntoRecursive,
+ PxWriter: Sync + RecursivePixelWriter<<Storage::Pixel as IntoRecursive>::Recursive>,
+{
+ fn extract_uncompressed_block(&self, header: &Header, block_index: BlockIndex) -> Vec<u8> {
+ let block_bytes = block_index.pixel_size.area() * header.channels.bytes_per_pixel;
+ let mut block_bytes = vec![0_u8; block_bytes];
+
+ let width = block_index.pixel_size.0;
+ let line_bytes = width * header.channels.bytes_per_pixel;
+ let byte_lines = block_bytes.chunks_exact_mut(line_bytes);
+ assert_eq!(byte_lines.len(), block_index.pixel_size.height(), "invalid block line splits");
+
+ //dbg!(width, line_bytes, header.channels.bytes_per_pixel, byte_lines.len());
+
+ let mut pixel_line = Vec::with_capacity(width);
+
+ for (y, line_bytes) in byte_lines.enumerate() {
+ pixel_line.clear();
+ pixel_line.extend((0 .. width).map(|x|
+ self.channels.pixels.get_pixel(block_index.pixel_position + Vec2(x, y)).into_recursive()
+ ));
+
+ self.recursive_channel_writer.write_pixels(line_bytes, pixel_line.as_slice(), |px| px);
+ }
+
+ block_bytes
+ }
+}
+
+/// A tuple containing either `ChannelsDescription` or `Option<ChannelsDescription>` entries.
+/// Use an `Option` if you want to dynamically omit a single channel (probably only for roundtrip tests).
+/// The number of entries must match the number of channels.
+pub trait WritableChannelsDescription<Pixel>: Sync {
+
+ /// A type that has a recursive entry for each channel in the image,
+ /// which must accept the desired pixel type.
+ type RecursiveWriter: RecursivePixelWriter<Pixel>;
+
+ /// Create the temporary writer, accepting the sorted list of channels from `channel_descriptions_list`.
+ fn create_recursive_writer(&self, channels: &ChannelList) -> Self::RecursiveWriter;
+
+ /// Return all the channels that should actually end up in the image, in any order.
+ fn channel_descriptions_list(&self) -> SmallVec<[ChannelDescription; 5]>;
+}
+
+impl WritableChannelsDescription<NoneMore> for NoneMore {
+ type RecursiveWriter = NoneMore;
+ fn create_recursive_writer(&self, _: &ChannelList) -> Self::RecursiveWriter { NoneMore }
+ fn channel_descriptions_list(&self) -> SmallVec<[ChannelDescription; 5]> { SmallVec::new() }
+}
+
+impl<InnerDescriptions, InnerPixel, Sample: IntoNativeSample>
+ WritableChannelsDescription<Recursive<InnerPixel, Sample>>
+ for Recursive<InnerDescriptions, ChannelDescription>
+ where InnerDescriptions: WritableChannelsDescription<InnerPixel>
+{
+ type RecursiveWriter = RecursiveWriter<InnerDescriptions::RecursiveWriter, Sample>;
+
+ fn create_recursive_writer(&self, channels: &ChannelList) -> Self::RecursiveWriter {
+ // this linear lookup is required because the order of the channels changed, due to alphabetical sorting
+ let (start_byte_offset, target_sample_type) = channels.channels_with_byte_offset()
+ .find(|(_offset, channel)| channel.name == self.value.name)
+ .map(|(offset, channel)| (offset, channel.sample_type))
+ .expect("a channel has not been put into channel list");
+
+ Recursive::new(self.inner.create_recursive_writer(channels), SampleWriter {
+ start_byte_offset, target_sample_type,
+ px: PhantomData::default()
+ })
+ }
+
+ fn channel_descriptions_list(&self) -> SmallVec<[ChannelDescription; 5]> {
+ let mut inner_list = self.inner.channel_descriptions_list();
+ inner_list.push(self.value.clone());
+ inner_list
+ }
+}
+
+impl<InnerDescriptions, InnerPixel, Sample: IntoNativeSample>
+WritableChannelsDescription<Recursive<InnerPixel, Sample>>
+for Recursive<InnerDescriptions, Option<ChannelDescription>>
+ where InnerDescriptions: WritableChannelsDescription<InnerPixel>
+{
+ type RecursiveWriter = OptionalRecursiveWriter<InnerDescriptions::RecursiveWriter, Sample>;
+
+ fn create_recursive_writer(&self, channels: &ChannelList) -> Self::RecursiveWriter {
+ // this linear lookup is required because the order of the channels changed, due to alphabetical sorting
+
+ let channel = self.value.as_ref().map(|required_channel|
+ channels.channels_with_byte_offset()
+ .find(|(_offset, channel)| channel == &required_channel)
+ .map(|(offset, channel)| (offset, channel.sample_type))
+ .expect("a channel has not been put into channel list")
+ );
+
+ Recursive::new(
+ self.inner.create_recursive_writer(channels),
+ channel.map(|(start_byte_offset, target_sample_type)| SampleWriter {
+ start_byte_offset, target_sample_type,
+ px: PhantomData::default(),
+ })
+ )
+ }
+
+ fn channel_descriptions_list(&self) -> SmallVec<[ChannelDescription; 5]> {
+ let mut inner_list = self.inner.channel_descriptions_list();
+ if let Some(value) = &self.value { inner_list.push(value.clone()); }
+ inner_list
+ }
+}
+
+/// Write pixels to a slice of bytes. The top level writer contains all the other channels,
+/// the most inner channel is `NoneMore`.
+pub trait RecursivePixelWriter<Pixel>: Sync {
+
+ /// Write pixels to a slice of bytes. Recursively do this for all channels.
+ fn write_pixels<FullPixel>(&self, bytes: &mut [u8], pixels: &[FullPixel], get_pixel: impl Fn(&FullPixel) -> &Pixel);
+}
+
+type RecursiveWriter<Inner, Sample> = Recursive<Inner, SampleWriter<Sample>>;
+type OptionalRecursiveWriter<Inner, Sample> = Recursive<Inner, Option<SampleWriter<Sample>>>;
+
+/// Write the pixels of a single channel, unconditionally. Generic over the concrete sample type (f16, f32, u32).
+#[derive(Debug, Clone)]
+pub struct SampleWriter<Sample> {
+ target_sample_type: SampleType,
+ start_byte_offset: usize,
+ px: PhantomData<Sample>,
+}
+
+impl<Sample> SampleWriter<Sample> where Sample: IntoNativeSample {
+ fn write_own_samples(&self, bytes: &mut [u8], samples: impl ExactSizeIterator<Item=Sample>) {
+ let byte_start_index = samples.len() * self.start_byte_offset;
+ let byte_count = samples.len() * self.target_sample_type.bytes_per_sample();
+ let ref mut byte_writer = &mut bytes[byte_start_index..byte_start_index + byte_count];
+
+ let write_error_msg = "invalid memory buffer length when writing";
+
+ // match outside the loop to avoid matching on every single sample
+ match self.target_sample_type {
+ // TODO does this boil down to a `memcpy` where the sample type equals the type parameter?
+ SampleType::F16 => for sample in samples { sample.to_f16().write(byte_writer).expect(write_error_msg); },
+ SampleType::F32 => for sample in samples { sample.to_f32().write(byte_writer).expect(write_error_msg); },
+ SampleType::U32 => for sample in samples { sample.to_u32().write(byte_writer).expect(write_error_msg); },
+ };
+
+ debug_assert!(byte_writer.is_empty(), "all samples are written, but more were expected");
+ }
+}
+
+impl RecursivePixelWriter<NoneMore> for NoneMore {
+ fn write_pixels<FullPixel>(&self, _: &mut [u8], _: &[FullPixel], _: impl Fn(&FullPixel) -> &NoneMore) {}
+}
+
+impl<Inner, InnerPixel, Sample: IntoNativeSample>
+ RecursivePixelWriter<Recursive<InnerPixel, Sample>>
+ for RecursiveWriter<Inner, Sample>
+ where Inner: RecursivePixelWriter<InnerPixel>
+{
+ // TODO impl exact size iterator <item = Self::Pixel>
+ fn write_pixels<FullPixel>(&self, bytes: &mut [u8], pixels: &[FullPixel], get_pixel: impl Fn(&FullPixel) -> &Recursive<InnerPixel, Sample>){
+ self.value.write_own_samples(bytes, pixels.iter().map(|px| get_pixel(px).value));
+ self.inner.write_pixels(bytes, pixels, |px| &get_pixel(px).inner);
+ }
+}
+
+impl<Inner, InnerPixel, Sample> RecursivePixelWriter<Recursive<InnerPixel, Sample>>
+ for OptionalRecursiveWriter<Inner, Sample>
+ where Inner: RecursivePixelWriter<InnerPixel>,
+ Sample: IntoNativeSample
+{
+ fn write_pixels<FullPixel>(&self, bytes: &mut [u8], pixels: &[FullPixel], get_pixel: impl Fn(&FullPixel) -> &Recursive<InnerPixel, Sample>) {
+ if let Some(writer) = &self.value {
+ writer.write_own_samples(bytes, pixels.iter().map(|px| get_pixel(px).value));
+ }
+
+ self.inner.write_pixels(bytes, pixels, |px| &get_pixel(px).inner);
+ }
+}
+
+
+
+
+
+
+
+#[cfg(test)]
+pub mod test {
+ use crate::image::write::channels::WritableChannels;
+ use crate::image::SpecificChannels;
+ use crate::prelude::{f16};
+ use crate::meta::attribute::{ChannelDescription, SampleType};
+ use crate::image::pixel_vec::PixelVec;
+
+ #[test]
+ fn compiles(){
+ let x = 3_f32;
+ let y = f16::from_f32(4.0);
+ let z = 2_u32;
+ let s = 1.3_f32;
+ let px = (x,y,z,s);
+
+ assert_is_writable_channels(
+ SpecificChannels::rgba(|_pos| px)
+ );
+
+ assert_is_writable_channels(SpecificChannels::rgba(
+ PixelVec::new((3, 2), vec![px, px, px, px, px, px])
+ ));
+
+ let px = (2333_u32, 4_f32);
+ assert_is_writable_channels(
+ SpecificChannels::build()
+ .with_channel("A")
+ .with_channel("C")
+ .with_pixels(PixelVec::new((3, 2), vec![px, px, px, px, px, px]))
+ );
+
+ let px = (3_f32, f16::ONE, 2333_u32, 4_f32);
+ assert_is_writable_channels(SpecificChannels::new(
+ (
+ ChannelDescription::named("x", SampleType::F32),
+ ChannelDescription::named("y", SampleType::F16),
+ Some(ChannelDescription::named("z", SampleType::U32)),
+ Some(ChannelDescription::named("p", SampleType::F32)),
+ ),
+
+ PixelVec::new((3, 2), vec![px, px, px, px, px, px])
+ ));
+
+
+
+ fn assert_is_writable_channels<'s>(_channels: impl WritableChannels<'s>){}
+
+ }
+}
+
+
+
+
diff --git a/vendor/exr/src/image/write/layers.rs b/vendor/exr/src/image/write/layers.rs
new file mode 100644
index 0000000..85648ff
--- /dev/null
+++ b/vendor/exr/src/image/write/layers.rs
@@ -0,0 +1,188 @@
+//! How to write either a single or a list of layers.
+
+use crate::meta::header::{ImageAttributes, Header};
+use crate::meta::{Headers, compute_chunk_count};
+use crate::block::BlockIndex;
+use crate::image::{Layers, Layer};
+use crate::meta::attribute::{TileDescription};
+use crate::prelude::{SmallVec};
+use crate::image::write::channels::{WritableChannels, ChannelsWriter};
+use crate::image::recursive::{Recursive, NoneMore};
+
+/// Enables an image containing this list of layers to be written to a file.
+pub trait WritableLayers<'slf> {
+
+ /// Generate the file meta data for this list of layers
+ fn infer_headers(&self, image_attributes: &ImageAttributes) -> Headers;
+
+ /// The type of temporary writer
+ type Writer: LayersWriter;
+
+ /// Create a temporary writer for this list of layers
+ fn create_writer(&'slf self, headers: &[Header]) -> Self::Writer;
+}
+
+/// A temporary writer for a list of channels
+pub trait LayersWriter: Sync {
+
+ /// Deliver a block of pixels from a single layer to be stored in the file
+ fn extract_uncompressed_block(&self, headers: &[Header], block: BlockIndex) -> Vec<u8>;
+}
+
+/// A temporary writer for an arbitrary list of layers
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct AllLayersWriter<ChannelsWriter> {
+ layers: SmallVec<[LayerWriter<ChannelsWriter>; 2]>
+}
+
+/// A temporary writer for a single layer
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct LayerWriter<ChannelsWriter> {
+ channels: ChannelsWriter, // impl ChannelsWriter
+}
+
+// impl for smallvec
+impl<'slf, Channels: 'slf> WritableLayers<'slf> for Layers<Channels> where Channels: WritableChannels<'slf> {
+ fn infer_headers(&self, image_attributes: &ImageAttributes) -> Headers {
+ slice_infer_headers(self.as_slice(), image_attributes)
+ }
+
+ type Writer = AllLayersWriter<Channels::Writer>;
+ fn create_writer(&'slf self, headers: &[Header]) -> Self::Writer {
+ slice_create_writer(self.as_slice(), headers)
+ }
+}
+
+fn slice_infer_headers<'slf, Channels:'slf + WritableChannels<'slf>>(
+ slice: &[Layer<Channels>], image_attributes: &ImageAttributes
+) -> Headers
+{
+ slice.iter().map(|layer| layer.infer_headers(image_attributes).remove(0)).collect() // TODO no array-vs-first
+}
+
+fn slice_create_writer<'slf, Channels:'slf + WritableChannels<'slf>>(
+ slice: &'slf [Layer<Channels>], headers: &[Header]
+) -> AllLayersWriter<Channels::Writer>
+{
+ AllLayersWriter {
+ layers: slice.iter().zip(headers.chunks_exact(1)) // TODO no array-vs-first
+ .map(|(layer, header)| layer.create_writer(header))
+ .collect()
+ }
+}
+
+
+impl<'slf, Channels: WritableChannels<'slf>> WritableLayers<'slf> for Layer<Channels> {
+ fn infer_headers(&self, image_attributes: &ImageAttributes) -> Headers {
+ let blocks = match self.encoding.blocks {
+ crate::image::Blocks::ScanLines => crate::meta::BlockDescription::ScanLines,
+ crate::image::Blocks::Tiles(tile_size) => {
+ let (level_mode, rounding_mode) = self.channel_data.infer_level_modes();
+ crate::meta::BlockDescription::Tiles(TileDescription { level_mode, rounding_mode, tile_size, })
+ },
+ };
+
+ let chunk_count = compute_chunk_count(
+ self.encoding.compression, self.size, blocks
+ );
+
+ let header = Header {
+ channels: self.channel_data.infer_channel_list(),
+ compression: self.encoding.compression,
+
+ blocks,
+ chunk_count,
+
+ line_order: self.encoding.line_order,
+ layer_size: self.size,
+ shared_attributes: image_attributes.clone(),
+ own_attributes: self.attributes.clone(),
+
+
+ deep: false, // TODO deep data
+ deep_data_version: None,
+ max_samples_per_pixel: None,
+ };
+
+ smallvec![ header ]// TODO no array-vs-first
+ }
+
+ type Writer = LayerWriter</*'l,*/ Channels::Writer>;
+ fn create_writer(&'slf self, headers: &[Header]) -> Self::Writer {
+ let channels = self.channel_data
+ .create_writer(headers.first().expect("inferred header error")); // TODO no array-vs-first
+
+ LayerWriter { channels }
+ }
+}
+
+impl<C> LayersWriter for AllLayersWriter<C> where C: ChannelsWriter {
+ fn extract_uncompressed_block(&self, headers: &[Header], block: BlockIndex) -> Vec<u8> {
+ self.layers[block.layer].extract_uncompressed_block(std::slice::from_ref(&headers[block.layer]), block) // TODO no array-vs-first
+ }
+}
+
+impl<C> LayersWriter for LayerWriter<C> where C: ChannelsWriter {
+ fn extract_uncompressed_block(&self, headers: &[Header], block: BlockIndex) -> Vec<u8> {
+ self.channels.extract_uncompressed_block(headers.first().expect("invalid inferred header"), block) // TODO no array-vs-first
+ }
+}
+
+
+
+
+
+impl<'slf> WritableLayers<'slf> for NoneMore {
+ fn infer_headers(&self, _: &ImageAttributes) -> Headers { SmallVec::new() }
+
+ type Writer = NoneMore;
+ fn create_writer(&'slf self, _: &[Header]) -> Self::Writer { NoneMore }
+}
+
+impl<'slf, InnerLayers, Channels> WritableLayers<'slf> for Recursive<InnerLayers, Layer<Channels>>
+ where InnerLayers: WritableLayers<'slf>, Channels: WritableChannels<'slf>
+{
+ fn infer_headers(&self, image_attributes: &ImageAttributes) -> Headers {
+ let mut headers = self.inner.infer_headers(image_attributes);
+ headers.push(self.value.infer_headers(image_attributes).remove(0)); // TODO no unwrap
+ headers
+ }
+
+ type Writer = RecursiveLayersWriter<InnerLayers::Writer, Channels::Writer>;
+
+ fn create_writer(&'slf self, headers: &[Header]) -> Self::Writer {
+ let (own_header, inner_headers) = headers.split_last()
+ .expect("header has not been inferred correctly");
+
+ let layer_index = inner_headers.len();
+ RecursiveLayersWriter {
+ inner: self.inner.create_writer(inner_headers),
+ value: (layer_index, self.value.create_writer(std::slice::from_ref(own_header))) // TODO no slice
+ }
+ }
+}
+
+type RecursiveLayersWriter<InnerLayersWriter, ChannelsWriter> = Recursive<InnerLayersWriter, (usize, LayerWriter<ChannelsWriter>)>;
+
+impl LayersWriter for NoneMore {
+ fn extract_uncompressed_block(&self, _: &[Header], _: BlockIndex) -> Vec<u8> {
+ panic!("recursive length mismatch bug");
+ }
+}
+
+impl<InnerLayersWriter, Channels> LayersWriter for RecursiveLayersWriter<InnerLayersWriter, Channels>
+ where InnerLayersWriter: LayersWriter, Channels: ChannelsWriter
+{
+ fn extract_uncompressed_block(&self, headers: &[Header], block: BlockIndex) -> Vec<u8> {
+ let (layer_index, layer) = &self.value;
+ if *layer_index == block.layer {
+ let header = headers.get(*layer_index).expect("layer index bug");
+ layer.extract_uncompressed_block(std::slice::from_ref(header), block) // TODO no slice?
+ }
+ else {
+ self.inner.extract_uncompressed_block(headers, block)
+ }
+ }
+}
+
+
diff --git a/vendor/exr/src/image/write/mod.rs b/vendor/exr/src/image/write/mod.rs
new file mode 100644
index 0000000..3c20060
--- /dev/null
+++ b/vendor/exr/src/image/write/mod.rs
@@ -0,0 +1,184 @@
+
+//! Write an exr image to a file.
+//!
+//! First, call `my_image.write()`. The resulting value can be customized, like this:
+//! ```no_run
+//! use exr::prelude::*;
+//! # let my_image: FlatImage = unimplemented!();
+//!
+//! my_image.write()
+//! .on_progress(|progress| println!("progress: {:.1}", progress*100.0))
+//! .to_file("image.exr").unwrap();
+//! ```
+//!
+
+pub mod layers;
+pub mod samples;
+pub mod channels;
+
+
+
+use crate::meta::Headers;
+use crate::error::UnitResult;
+use std::io::{Seek, BufWriter};
+use crate::io::Write;
+use crate::image::{Image, ignore_progress, SpecificChannels, IntoSample};
+use crate::image::write::layers::{WritableLayers, LayersWriter};
+use crate::math::Vec2;
+use crate::block::writer::ChunksWriter;
+
+/// An oversimplified function for "just write the damn file already" use cases.
+/// Have a look at the examples to see how you can write an image with more flexibility (it's not that hard).
+/// Use `write_rgb_file` if you do not need an alpha channel.
+///
+/// Each of `R`, `G`, `B` and `A` can be either `f16`, `f32`, `u32`, or `Sample`.
+// TODO explain pixel tuple f32,f16,u32
+pub fn write_rgba_file<R,G,B,A>(
+ path: impl AsRef<std::path::Path>, width: usize, height: usize,
+ colors: impl Sync + Fn(usize, usize) -> (R, G, B, A)
+) -> UnitResult
+ where R: IntoSample, G: IntoSample, B: IntoSample, A: IntoSample,
+{
+ let channels = SpecificChannels::rgba(|Vec2(x,y)| colors(x,y));
+ Image::from_channels((width, height), channels).write().to_file(path)
+}
+
+/// An oversimplified function for "just write the damn file already" use cases.
+/// Have a look at the examples to see how you can write an image with more flexibility (it's not that hard).
+/// Use `write_rgb_file` if you do not need an alpha channel.
+///
+/// Each of `R`, `G`, and `B` can be either `f16`, `f32`, `u32`, or `Sample`.
+// TODO explain pixel tuple f32,f16,u32
+pub fn write_rgb_file<R,G,B>(
+ path: impl AsRef<std::path::Path>, width: usize, height: usize,
+ colors: impl Sync + Fn(usize, usize) -> (R, G, B)
+) -> UnitResult
+ where R: IntoSample, G: IntoSample, B: IntoSample
+{
+ let channels = SpecificChannels::rgb(|Vec2(x,y)| colors(x,y));
+ Image::from_channels((width, height), channels).write().to_file(path)
+}
+
+
+
+/// Enables an image to be written to a file. Call `image.write()` where this trait is implemented.
+pub trait WritableImage<'img, WritableLayers>: Sized {
+
+ /// Create a temporary writer which can be configured and used to write the image to a file.
+ fn write(self) -> WriteImageWithOptions<'img, WritableLayers, fn(f64)>;
+}
+
+impl<'img, WritableLayers> WritableImage<'img, WritableLayers> for &'img Image<WritableLayers> {
+ fn write(self) -> WriteImageWithOptions<'img, WritableLayers, fn(f64)> {
+ WriteImageWithOptions {
+ image: self,
+ check_compatibility: true,
+ parallel: true,
+ on_progress: ignore_progress
+ }
+ }
+}
+
+/// A temporary writer which can be configured and used to write an image to a file.
+// temporary writer with options
+#[derive(Debug, Clone, PartialEq)]
+pub struct WriteImageWithOptions<'img, Layers, OnProgress> {
+ image: &'img Image<Layers>,
+ on_progress: OnProgress,
+ check_compatibility: bool,
+ parallel: bool,
+}
+
+
+impl<'img, L, F> WriteImageWithOptions<'img, L, F>
+ where L: WritableLayers<'img>, F: FnMut(f64)
+{
+ /// Generate file meta data for this image. The meta data structure is close to the data in the file.
+ pub fn infer_meta_data(&self) -> Headers { // TODO this should perform all validity checks? and none after that?
+ self.image.layer_data.infer_headers(&self.image.attributes)
+ }
+
+ /// Do not compress multiple pixel blocks on multiple threads at once.
+ /// Might use less memory and synchronization, but will be slower in most situations.
+ pub fn non_parallel(self) -> Self { Self { parallel: false, ..self } }
+
+ /// Skip some checks that ensure a file can be opened by other exr software.
+ /// For example, it is no longer checked that no two headers or two attributes have the same name,
+ /// which might be an expensive check for images with an exorbitant number of headers.
+ ///
+ /// If you write an uncompressed file and need maximum speed, it might save a millisecond to disable the checks,
+ /// if you know that your file is not invalid any ways. I do not recommend this though,
+ /// as the file might not be readably by any other exr library after that.
+ /// __You must care for not producing an invalid file yourself.__
+ pub fn skip_compatibility_checks(self) -> Self { Self { check_compatibility: false, ..self } }
+
+ /// Specify a function to be called regularly throughout the writing process.
+ /// Replaces all previously specified progress functions in this reader.
+ pub fn on_progress<OnProgress>(self, on_progress: OnProgress) -> WriteImageWithOptions<'img, L, OnProgress>
+ where OnProgress: FnMut(f64)
+ {
+ WriteImageWithOptions {
+ on_progress,
+ image: self.image,
+ check_compatibility: self.check_compatibility,
+ parallel: self.parallel
+ }
+ }
+
+ /// Write the exr image to a file.
+ /// Use `to_unbuffered` instead, if you do not have a file.
+ /// If an error occurs, attempts to delete the partially written file.
+ #[inline]
+ #[must_use]
+ pub fn to_file(self, path: impl AsRef<std::path::Path>) -> UnitResult {
+ crate::io::attempt_delete_file_on_write_error(path.as_ref(), move |write|
+ self.to_unbuffered(write)
+ )
+ }
+
+ /// Buffer the writer and then write the exr image to it.
+ /// Use `to_buffered` instead, if your writer is an in-memory buffer.
+ /// Use `to_file` instead, if you have a file path.
+ /// If your writer cannot seek, you can write to an in-memory vector of bytes first, using `to_buffered`.
+ #[inline]
+ #[must_use]
+ pub fn to_unbuffered(self, unbuffered: impl Write + Seek) -> UnitResult {
+ self.to_buffered(BufWriter::new(unbuffered))
+ }
+
+ /// Write the exr image to a writer.
+ /// Use `to_file` instead, if you have a file path.
+ /// Use `to_unbuffered` instead, if this is not an in-memory writer.
+ /// If your writer cannot seek, you can write to an in-memory vector of bytes first.
+ #[must_use]
+ pub fn to_buffered(self, write: impl Write + Seek) -> UnitResult {
+ let headers = self.infer_meta_data();
+ let layers = self.image.layer_data.create_writer(&headers);
+
+ crate::block::write(
+ write, headers, self.check_compatibility,
+ move |meta, chunk_writer|{
+
+ let blocks = meta.collect_ordered_block_data(|block_index|
+ layers.extract_uncompressed_block(&meta.headers, block_index)
+ );
+
+ let chunk_writer = chunk_writer.on_progress(self.on_progress);
+ if self.parallel { chunk_writer.compress_all_blocks_parallel(&meta, blocks)?; }
+ else { chunk_writer.compress_all_blocks_sequential(&meta, blocks)?; }
+ /*let blocks_writer = chunk_writer.as_blocks_writer(&meta);
+
+ // TODO propagate send requirement further upwards
+ if self.parallel {
+ blocks_writer.compress_all_blocks_parallel(blocks)?;
+ }
+ else {
+ blocks_writer.compress_all_blocks_sequential(blocks)?;
+ }*/
+
+ Ok(())
+ }
+ )
+ }
+}
+
diff --git a/vendor/exr/src/image/write/samples.rs b/vendor/exr/src/image/write/samples.rs
new file mode 100644
index 0000000..e74105b
--- /dev/null
+++ b/vendor/exr/src/image/write/samples.rs
@@ -0,0 +1,205 @@
+//! How to write samples (a grid of `f32`, `f16` or `u32` values).
+
+use crate::meta::attribute::{LevelMode, SampleType, TileDescription};
+use crate::meta::header::Header;
+use crate::block::lines::LineRefMut;
+use crate::image::{FlatSamples, Levels, RipMaps};
+use crate::math::{Vec2, RoundingMode};
+use crate::meta::{rip_map_levels, mip_map_levels, rip_map_indices, mip_map_indices, BlockDescription};
+
+/// Enable an image with this sample grid to be written to a file.
+/// Also can contain multiple resolution levels.
+/// Usually contained within `Channels`.
+pub trait WritableSamples<'slf> {
+ // fn is_deep(&self) -> bool;
+
+ /// Generate the file meta data regarding the number type of this storage
+ fn sample_type(&self) -> SampleType;
+
+ /// Generate the file meta data regarding resolution levels
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode);
+
+ /// The type of the temporary writer for this sample storage
+ type Writer: SamplesWriter;
+
+ /// Create a temporary writer for this sample storage
+ fn create_samples_writer(&'slf self, header: &Header) -> Self::Writer;
+}
+
+/// Enable an image with this single level sample grid to be written to a file.
+/// Only contained within `Levels`.
+pub trait WritableLevel<'slf> {
+
+ /// Generate the file meta data regarding the number type of these samples
+ fn sample_type(&self) -> SampleType;
+
+ /// The type of the temporary writer for this single level of samples
+ type Writer: SamplesWriter;
+
+ /// Create a temporary writer for this single level of samples
+ fn create_level_writer(&'slf self, size: Vec2<usize>) -> Self::Writer;
+}
+
+/// A temporary writer for one or more resolution levels containing samples
+pub trait SamplesWriter: Sync {
+
+ /// Deliver a single short horizontal list of samples for a specific channel.
+ fn extract_line(&self, line: LineRefMut<'_>);
+}
+
+/// A temporary writer for a predefined non-deep sample storage
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct FlatSamplesWriter<'samples> {
+ resolution: Vec2<usize>, // respects resolution level
+ samples: &'samples FlatSamples
+}
+
+
+
+// used if no layers are used and the flat samples are directly inside the channels
+impl<'samples> WritableSamples<'samples> for FlatSamples {
+ fn sample_type(&self) -> SampleType {
+ match self {
+ FlatSamples::F16(_) => SampleType::F16,
+ FlatSamples::F32(_) => SampleType::F32,
+ FlatSamples::U32(_) => SampleType::U32,
+ }
+ }
+
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode) { (LevelMode::Singular, RoundingMode::Down) }
+
+ type Writer = FlatSamplesWriter<'samples>; //&'s FlatSamples;
+ fn create_samples_writer(&'samples self, header: &Header) -> Self::Writer {
+ FlatSamplesWriter {
+ resolution: header.layer_size,
+ samples: self
+ }
+ }
+}
+
+// used if layers are used and the flat samples are inside the levels
+impl<'samples> WritableLevel<'samples> for FlatSamples {
+ fn sample_type(&self) -> SampleType {
+ match self {
+ FlatSamples::F16(_) => SampleType::F16,
+ FlatSamples::F32(_) => SampleType::F32,
+ FlatSamples::U32(_) => SampleType::U32,
+ }
+ }
+
+ type Writer = FlatSamplesWriter<'samples>;
+ fn create_level_writer(&'samples self, size: Vec2<usize>) -> Self::Writer {
+ FlatSamplesWriter {
+ resolution: size,
+ samples: self
+ }
+ }
+}
+
+impl<'samples> SamplesWriter for FlatSamplesWriter<'samples> {
+ fn extract_line(&self, line: LineRefMut<'_>) {
+ let image_width = self.resolution.width(); // header.layer_size.width();
+ debug_assert_ne!(image_width, 0, "image width calculation bug");
+
+ let start_index = line.location.position.y() * image_width + line.location.position.x();
+ let end_index = start_index + line.location.sample_count;
+
+ debug_assert!(
+ start_index < end_index && end_index <= self.samples.len(),
+ "for resolution {:?}, this is an invalid line: {:?}",
+ self.resolution, line.location
+ );
+
+ match self.samples {
+ FlatSamples::F16(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
+ FlatSamples::F32(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
+ FlatSamples::U32(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
+ }.expect("writing line bytes failed");
+ }
+}
+
+
+impl<'samples, LevelSamples> WritableSamples<'samples> for Levels<LevelSamples>
+ where LevelSamples: WritableLevel<'samples>
+{
+ fn sample_type(&self) -> SampleType {
+ let sample_type = self.levels_as_slice().first().expect("no levels found").sample_type();
+
+ debug_assert!(
+ self.levels_as_slice().iter().skip(1).all(|ty| ty.sample_type() == sample_type),
+ "sample types must be the same across all levels"
+ );
+
+ sample_type
+ }
+
+ fn infer_level_modes(&self) -> (LevelMode, RoundingMode) {
+ match self {
+ Levels::Singular(_) => (LevelMode::Singular, RoundingMode::Down),
+ Levels::Mip { rounding_mode, .. } => (LevelMode::MipMap, *rounding_mode),
+ Levels::Rip { rounding_mode, .. } => (LevelMode::RipMap, *rounding_mode),
+ }
+ }
+
+ type Writer = LevelsWriter<LevelSamples::Writer>;
+ fn create_samples_writer(&'samples self, header: &Header) -> Self::Writer {
+ let rounding = match header.blocks {
+ BlockDescription::Tiles(TileDescription { rounding_mode, .. }) => Some(rounding_mode),
+ BlockDescription::ScanLines => None,
+ };
+
+ LevelsWriter {
+ levels: match self {
+ Levels::Singular(level) => Levels::Singular(level.create_level_writer(header.layer_size)),
+ Levels::Mip { level_data, rounding_mode } => {
+ debug_assert_eq!(
+ level_data.len(),
+ mip_map_indices(rounding.expect("mip maps only with tiles"), header.layer_size).count(),
+ "invalid mip map count"
+ );
+
+ Levels::Mip { // TODO store level size in image??
+ rounding_mode: *rounding_mode,
+ level_data: level_data.iter()
+ .zip(mip_map_levels(rounding.expect("mip maps only with tiles"), header.layer_size))
+ // .map(|level| level.create_samples_writer(header))
+ .map(|(level, (_level_index, level_size))| level.create_level_writer(level_size))
+ .collect()
+ }
+ },
+ Levels::Rip { level_data, rounding_mode } => {
+ debug_assert_eq!(level_data.map_data.len(), level_data.level_count.area(), "invalid rip level count");
+ debug_assert_eq!(
+ level_data.map_data.len(),
+ rip_map_indices(rounding.expect("rip maps only with tiles"), header.layer_size).count(),
+ "invalid rip map count"
+ );
+
+ Levels::Rip {
+ rounding_mode: *rounding_mode,
+ level_data: RipMaps {
+ level_count: level_data.level_count,
+ map_data: level_data.map_data.iter()
+ .zip(rip_map_levels(rounding.expect("rip maps only with tiles"), header.layer_size))
+ .map(|(level, (_level_index, level_size))| level.create_level_writer(level_size))
+ .collect(),
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/// A temporary writer for multiple resolution levels
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct LevelsWriter<SamplesWriter> {
+ levels: Levels<SamplesWriter>,
+}
+
+impl<Samples> SamplesWriter for LevelsWriter<Samples> where Samples: SamplesWriter {
+ fn extract_line(&self, line: LineRefMut<'_>) {
+ self.levels.get_level(line.location.level).expect("invalid level index") // TODO compute level size from line index??
+ .extract_line(line)
+ }
+}