diff options
| author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 | 
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 | 
| commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
| tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/exr/src/image | |
| parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
| download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip | |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/exr/src/image')
| -rw-r--r-- | vendor/exr/src/image/channel_groups.rs | 267 | ||||
| -rw-r--r-- | vendor/exr/src/image/crop.rs | 801 | ||||
| -rw-r--r-- | vendor/exr/src/image/mod.rs | 1326 | ||||
| -rw-r--r-- | vendor/exr/src/image/pixel_vec.rs | 97 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/any_channels.rs | 128 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/image.rs | 209 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/layers.rs | 204 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/levels.rs | 219 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/mod.rs | 207 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/samples.rs | 122 | ||||
| -rw-r--r-- | vendor/exr/src/image/read/specific_channels.rs | 463 | ||||
| -rw-r--r-- | vendor/exr/src/image/recursive.rs | 178 | ||||
| -rw-r--r-- | vendor/exr/src/image/write/channels.rs | 407 | ||||
| -rw-r--r-- | vendor/exr/src/image/write/layers.rs | 188 | ||||
| -rw-r--r-- | vendor/exr/src/image/write/mod.rs | 184 | ||||
| -rw-r--r-- | vendor/exr/src/image/write/samples.rs | 205 | 
16 files changed, 5205 insertions, 0 deletions
| diff --git a/vendor/exr/src/image/channel_groups.rs b/vendor/exr/src/image/channel_groups.rs new file mode 100644 index 0000000..7d74375 --- /dev/null +++ b/vendor/exr/src/image/channel_groups.rs @@ -0,0 +1,267 @@ + +use std::collections::HashMap; +use crate::image::write::channels::{WritableChannels, ChannelsWriter}; +use crate::meta::attribute::{LevelMode, ChannelList, Text, TextSlice, ChannelInfo}; +use crate::meta::header::Header; +use crate::image::read::layers::{ReadChannels, ChannelsReader}; +use crate::block::{BlockIndex, UncompressedBlock}; +use crate::block::lines::{collect_uncompressed_block_from_lines, LineIndex}; +use std::io::{Cursor, Read}; +use crate::error::{Result, UnitResult}; +use crate::block::chunk::TileCoordinates; +use crate::prelude::SmallVec; + + + + + +pub struct ChannelGroups<ChannelGroup> { +    channel_group: Option<ChannelGroup>, +    children: HashMap<Text, Self> +} + + +impl<ChannelGroup> ChannelGroups<ChannelGroup>  { + + +    // pub fn visit_groups_mut(&mut self, visitor: impl Fn(&mut Channels)) { +    // } + + + +    pub fn groups(&self) -> SmallVec<[&ChannelGroup; 12]> { +        let children = self.children.iter().flat_map(|group| group.groups()); +        self.channel_group.iter().chain(children).collect() +    } + +    pub fn lookup_group(&self, group_name: &TextSlice) -> Option<&ChannelGroup> { +        let dot_index = group_name.iter().position('.'); +        if let Some(dot_index) = dot_index { +            let group_name = &group_name[.. dot_index]; +            let child_name = &group_name[dot_index + 1 ..]; +            self.children.get(group_name) +                .and_then(|child| child.lookup(child_name)) +        } +        else { +            self.channel_group.lookup(name) +        } +    } + + +    /*pub fn insert_group(&mut self, full_name: &TextSlice, value: ChannelGroup) { +        let dot_index = full_name.iter().position('.'); +        if let Some(dot_index) = dot_index { +            let group_name = &group_name[.. dot_index]; +            let name_rest = &group_name[dot_index + 1 ..]; + +            self.children.entry(Text::from_slice_unchecked(group_name)) +                .or_insert(|| ); + +            // self.children.insert(Text::from_slice_unchecked(group_name), value) +            //     .and_then(|child| child.lookup(name_rest)); +        } +        else { +            self.channel_group.lookup(name); +        } +    }*/ + +    pub fn map<T>(self, mapper: impl FnMut(ChannelGroup) -> T) -> ChannelGroups<T> { +        ChannelGroups { +            children: self.channel_group.iter().map(&mapper).collect(), +            channel_group: self.channel_group.map(mapper), +        } +    } +} + + +pub fn parse_channel_list_groups<T>(channels: impl Iterator<Item=(Text, T)>) +    -> ChannelGroups<SmallVec<(Text, T)>> +{ +    fn insert_into_groups(groups: &mut ChannelGroups<SmallVec<(Text, T)>>, name: Text, value: T) { +        let dot_index = name.as_slice().iter().position('.'); + +        if let Some(dot_index) = dot_index { +            // insert into child group + +            let group_name = Text::from_slice_unchecked(&name.as_slice()[.. dot_index]); +            let child_channel = Text::from_slice_unchecked(&name.as_slice()[dot_index + 1 ..]); + +            let child_group = groups.children.entry(group_name) +                .or_insert(ChannelGroups { channel_group: None, children: Default::default() }); + +            insert_into_groups(child_group, child_channel, value); +        } + +        else { +            // insert directly into group + +            if groups.channel_group.is_none() { +                groups.channel_group = Some(SmallVec::new()); +            } + +            groups.channel_group.unwrap().push(value); +        } +    } + +    let mut result = ChannelGroups { channel_group: None, children: HashMap::default() }; +    for (name, value) in channels { insert_into_groups(&mut result, name, value); } +    result +} + + +impl<'slf, ChannelGroup> WritableChannels<'slf> for ChannelGroups<ChannelGroup> +    where ChannelGroup: WritableChannels<'slf> +{ +    fn infer_channel_list(&self) -> ChannelList { +        // TODO what about empty groups with NO channels?? + +        let child_channels = self.children.iter().flat_map(|(group_name, child)| { +            let mut child_channels = child.infer_channel_list().list; +            for channel in &mut child_channels { channel.name.push_front(group_name) }; +            child_channels +        }); + +        let mut own_channels = self.channel_group +            .map(|chans| chans.infer_channel_list().list) +            .unwrap_or_default(); + +        own_channels.extend(child_channels); +        own_channels.sort_unstable(); // TODO only once at end +        ChannelList::new(own_channels) // might be empty, but will be checked in MetaData::validate() +    } + +    fn level_mode(&self) -> LevelMode { +        fn find_mode_or_none(channels: &Self) -> Option<LevelMode> { +            channels.channel_group.map(WritableChannels::level_mode).or_else(|| { +                channels.children.iter().map(find_mode_or_none).next() +            }) +        } + +        let mode = find_mode_or_none(self) +            .expect("empty channel groups (check failed)"); // TODO only happens for empty channels, right? panic maybe? + +        if let Some(chans) = self.channel_group.as_ref() { +            debug_assert_eq!(chans.level_mode(), mode, "level mode must be equal for all legacy channel groups") +        } + +        debug_assert!( +            self.children.values() +                .flat_map(find_mode_or_none) +                .all(|child_mode| child_mode == mode), + +            "level mode must be equal for all legacy channel groups" +        ); + +        mode +    } + +    type Writer = GroupChannelsWriter<'slf, ChannelGroup>; + +    fn create_writer(&'slf self, header: &Header) -> Self::Writer { +        let channels = header.channels.list.iter() +            .map(|channel_info|{ +                // hashmap order is not guaranteed? so look up each channel group manually instead of generating new +                let channels = self.lookup_group(channel_info.name.as_slice()) +                    .expect("channels not found bug"); + +                channels.create_writer(header) // channel_info.name.clone() +            }) +            .collect(); + +        GroupChannelsWriter { channels_list: channels } +    } +} + +struct GroupChannelsWriter<'c, ChannelGroupWriter> { +    channels_list: Vec<&'c ChannelGroupWriter>, +} + +impl<'c, Channels> ChannelsWriter for GroupChannelsWriter<'c, Channels> where Channels: ChannelsWriter { +    fn extract_uncompressed_block(&self, header: &Header, block: BlockIndex) -> Vec<u8> { +        let mut blocks_per_channel: Vec<Cursor<Vec<u8>>> = self +            .channels_list.iter() +            .map(|channels| Cursor::new(channels.extract_uncompressed_block(header, block))) +            .collect(); + +        UncompressedBlock::uncompressed_block_from_lines(header, block, |line|{ +            let channel_reader = &mut blocks_per_channel[line.location.channel]; // TODO subsampling + +            // read from specific channel into total byte block +            // this assumes that the lines in the callback are iterated in strictly increasing order +            // because each channel reader is consumed +            channel_reader.read_exact(line.value) +                .expect("collecting grouped channel byte block failed"); +        }) +    } +} + + +struct ReadChannelGroups<ReadChannelGroup> { +    read_channels: ReadChannelGroup +} + +struct ChannelGroupsReader<ChannelGroupReader> { +    channels: ChannelGroups<usize>, +    indexed_channels: Vec<ChannelGroupReader>, +} + +impl<'s, ReadChannelGroup> ReadChannels<'s> for ReadChannelGroups<ReadChannelGroup> +    where ReadChannelGroup: ReadChannels<'s> +{ +    type Reader = ChannelGroupsReader<ReadChannelGroup::Reader>; + +    fn create_channels_reader(&'s self, header: &Header) -> Result<Self::Reader> { +        let swap = |(a,b)| (b,a); +        let channel_groups = parse_channel_list_groups( +            header.channels.list.iter().enumerate().map(swap) +        ); + +        let mut indexed_channels = Vec::new(); +        let channel_groups = channel_groups.map(|channels| { + +            let mut channels_header = header.clone(); // TODO no clone? +            channels_header.channels = ChannelList::new(channels.iter().map(|(name, index)|{ +                let mut channel_info = header.channels.list[index].clone(); +                channel_info.name = name; +                channel_info +            }).collect()); // FIXME does not comply to `header.chunk_count` and that stuff?? change ReadChannels fn signature? + +            indexed_channels.push(self.read_channels.create_channels_reader(&channels_header)); + +            // FIXME this is not the original order indexed_channels.len() - 1 +            indexed_channels[] +        }); + +        Ok(ChannelGroupsReader { +            channels: channel_groups, +            indexed_channels, +        }) + +        /*Ok(ChannelGroupsReader { +            channels: header.channels.list.iter().map(|channel| { +                let mut channels_header = header.clone(); + +                let reader = self.read_channels.create_channels_reader(&channels_header); +                (channels_header, reader) +            }).collect(), +        })*/ +    } +} + +impl<ChannelGroupReader> ChannelsReader for ChannelGroupsReader<ChannelGroupReader> where ChannelGroupReader: ChannelsReader { +    type Channels = ChannelGroups<ChannelGroupReader::Channels>; + +    fn filter_block(&self, tile: (usize, &TileCoordinates)) -> bool { +        self.indexed_channels.iter().any(|channel| channel.filter_block(tile)) +    } + +    fn read_block(&mut self, header: &Header, block: UncompressedBlock) -> UnitResult { +        block.for_lines(|line|{ + +        }) +    } + +    fn into_channels(self) -> Self::Channels { + +    } +}
\ No newline at end of file diff --git a/vendor/exr/src/image/crop.rs b/vendor/exr/src/image/crop.rs new file mode 100644 index 0000000..63aadbf --- /dev/null +++ b/vendor/exr/src/image/crop.rs @@ -0,0 +1,801 @@ +//! Crop away unwanted pixels. Includes automatic detection of bounding rectangle. +//! Currently does not support deep data and resolution levels. + +use crate::meta::attribute::{IntegerBounds, LevelMode, ChannelList}; +use crate::math::{Vec2, RoundingMode}; +use crate::image::{Layer, FlatSamples, SpecificChannels, AnyChannels, FlatSamplesPixel, AnyChannel}; +use crate::image::write::channels::{GetPixel, WritableChannels, ChannelsWriter}; +use crate::meta::header::{LayerAttributes, Header}; +use crate::block::BlockIndex; + +/// Something that has a two-dimensional rectangular shape +pub trait GetBounds { + +    /// The bounding rectangle of this pixel grid. +    fn bounds(&self) -> IntegerBounds; +} + +/// Inspect the pixels in this image to determine where to crop some away +pub trait InspectSample: GetBounds { + +    /// The type of pixel in this pixel grid. +    type Sample; + +    /// Index is not in world coordinates, but within the data window. +    /// Position `(0,0)` always represents the top left pixel. +    fn inspect_sample(&self, local_index: Vec2<usize>) -> Self::Sample; +} + +/// Crop some pixels ways when specifying a smaller rectangle +pub trait Crop: Sized { + +    /// The type of  this image after cropping (probably the same as before) +    type Cropped; + +    /// Crop the image to exclude unwanted pixels. +    /// Panics for invalid (larger than previously) bounds. +    /// The bounds are specified in absolute coordinates. +    /// Does not reduce allocation size of the current image, but instead only adjust a few boundary numbers. +    /// Use `reallocate_cropped()` on the return value to actually reduce the memory footprint. +    fn crop(self, bounds: IntegerBounds) -> Self::Cropped; + +    /// Reduce your image to a smaller part, usually to save memory. +    /// Crop if bounds are specified, return the original if no bounds are specified. +    /// Does not reduce allocation size of the current image, but instead only adjust a few boundary numbers. +    /// Use `reallocate_cropped()` on the return value to actually reduce the memory footprint. +    fn try_crop(self, bounds: Option<IntegerBounds>) -> CropResult<Self::Cropped, Self> { +        match bounds { +            Some(bounds) => CropResult::Cropped(self.crop(bounds)), +            None => CropResult::Empty { original: self }, +        } +    } +} + +/// Cropping an image fails if the image is fully transparent. +/// Use [`or_crop_to_1x1_if_empty`] or [`or_none_if_empty`] to obtain a normal image again. +#[must_use] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum CropResult<Cropped, Old> { + +    /// The image contained some pixels and has been cropped or left untouched +    Cropped (Cropped), + +    /// All pixels in the image would be discarded, removing the whole image +    Empty { + +        /// The fully discarded image which caused the cropping to fail +        original: Old +    } +} + +/// Crop away unwanted pixels from the border if they match the specified rule. +pub trait CropWhere<Sample>: Sized { + +    /// The type of the cropped image (probably the same as the original image). +    type Cropped; + +    /// Crop away unwanted pixels from the border if they match the specified rule. +    /// Does not reduce allocation size of the current image, but instead only adjust a few boundary numbers. +    /// Use `reallocate_cropped()` on the return value to actually reduce the memory footprint. +    fn crop_where(self, discard_if: impl Fn(Sample) -> bool) -> CropResult<Self::Cropped, Self>; + +    /// Crop away unwanted pixels from the border if they match the specified color. +    /// If you want discard based on a rule, use `crop_where` with a closure instead. +    /// Does not reduce allocation size of the current image, but instead only adjust a few boundary numbers. +    /// Use `reallocate_cropped()` on the return value to actually reduce the memory footprint. +    fn crop_where_eq(self, discard_color: impl Into<Sample>) -> CropResult<Self::Cropped, Self> where Sample: PartialEq; + +    /// Convert this data to cropped data without discarding any pixels. +    fn crop_nowhere(self) -> Self::Cropped; +} + +impl<Channels> Crop for Layer<Channels> { +    type Cropped = Layer<CroppedChannels<Channels>>; + +    fn crop(self, bounds: IntegerBounds) -> Self::Cropped { +        CroppedChannels::crop_layer(bounds, self) +    } +} + +impl<T> CropWhere<T::Sample> for T where T: Crop + InspectSample { +    type Cropped = <Self as Crop>::Cropped; + +    fn crop_where(self, discard_if: impl Fn(T::Sample) -> bool) -> CropResult<Self::Cropped, Self> { +        let smaller_bounds = { +            let keep_if = |position| !discard_if(self.inspect_sample(position)); +            try_find_smaller_bounds(self.bounds(), keep_if) +        }; + +        self.try_crop(smaller_bounds) +    } + +    fn crop_where_eq(self, discard_color: impl Into<T::Sample>) -> CropResult<Self::Cropped, Self> where T::Sample: PartialEq { +        let discard_color: T::Sample = discard_color.into(); +        self.crop_where(|sample| sample == discard_color) +    } + +    fn crop_nowhere(self) -> Self::Cropped { +        let current_bounds = self.bounds(); +        self.crop(current_bounds) +    } +} + +/// A smaller window into an existing pixel storage +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CroppedChannels<Channels> { + +    /// The uncropped pixel storage +    pub full_channels: Channels, + +    /// The uncropped pixel storage bounds +    pub full_bounds: IntegerBounds, + +    /// The cropped pixel storage bounds +    pub cropped_bounds: IntegerBounds, +} + +impl<Channels> CroppedChannels<Channels> { + +    /// Wrap a layer in a cropped view with adjusted bounds, but without reallocating your pixels +    pub fn crop_layer(new_bounds: IntegerBounds, layer: Layer<Channels>) -> Layer<CroppedChannels<Channels>> { +        Layer { +            channel_data: CroppedChannels { +                cropped_bounds: new_bounds, +                full_bounds: layer.absolute_bounds(), +                full_channels: layer.channel_data, +            }, + +            size: new_bounds.size, + +            attributes: LayerAttributes { +                layer_position: new_bounds.position, +                .. layer.attributes +            }, + +            encoding: layer.encoding +        } +    } +} + +// TODO make cropped view readable if you only need a specific section of the image? + +// make cropped view writable: + +impl<'slf, Channels:'slf> WritableChannels<'slf> for CroppedChannels<Channels> where Channels: WritableChannels<'slf> { +    fn infer_channel_list(&self) -> ChannelList { +        self.full_channels.infer_channel_list() // no need for adjustments, as the layer content already reflects the changes +    } + +    fn infer_level_modes(&self) -> (LevelMode, RoundingMode) { +        self.full_channels.infer_level_modes() +    } + +    type Writer = CroppedWriter<Channels::Writer>; + +    fn create_writer(&'slf self, header: &Header) -> Self::Writer { +        let offset = (self.cropped_bounds.position - self.full_bounds.position) +            .to_usize("invalid cropping bounds for cropped view").unwrap(); + +        CroppedWriter { channels: self.full_channels.create_writer(header), offset } +    } +} + +/// A writer for the cropped view layer +#[derive(Debug, Clone, PartialEq)] +pub struct CroppedWriter<ChannelsWriter> { +    channels: ChannelsWriter, +    offset: Vec2<usize> +} + +impl<'c, Channels> ChannelsWriter for CroppedWriter<Channels> where Channels: ChannelsWriter { +    fn extract_uncompressed_block(&self, header: &Header, block: BlockIndex) -> Vec<u8> { +        let block = BlockIndex { +            pixel_position: block.pixel_position + self.offset, +            .. block +        }; + +        self.channels.extract_uncompressed_block(header, block) +    } +} + +impl<Samples, Channels> InspectSample for Layer<SpecificChannels<Samples, Channels>> where Samples: GetPixel { +    type Sample = Samples::Pixel; +    fn inspect_sample(&self, local_index: Vec2<usize>) -> Samples::Pixel { +        self.channel_data.pixels.get_pixel(local_index) +    } +} + +impl InspectSample for Layer<AnyChannels<FlatSamples>> { +    type Sample = FlatSamplesPixel; + +    fn inspect_sample(&self, local_index: Vec2<usize>) -> FlatSamplesPixel { +        self.sample_vec_at(local_index) +    } +} + +// ALGORITHM IDEA: for arbitrary channels, find the most desired channel, +// and process that first, keeping the processed bounds as starting point for the other layers + +/// Realize a cropped view of the original data, +/// by actually removing the unwanted original pixels, +/// reducing the memory consumption. +/// Currently not supported for `SpecificChannels`. +pub trait ApplyCroppedView { + +    /// The simpler type after cropping is realized +    type Reallocated; + +    /// Make the cropping real by reallocating the underlying storage, +    /// with the goal of reducing total memory usage. +    /// Currently not supported for `SpecificChannels`. +    fn reallocate_cropped(self) -> Self::Reallocated; +} + +impl ApplyCroppedView for Layer<CroppedChannels<AnyChannels<FlatSamples>>> { +    type Reallocated = Layer<AnyChannels<FlatSamples>>; + +    fn reallocate_cropped(self) -> Self::Reallocated { +        let cropped_absolute_bounds = self.channel_data.cropped_bounds; +        let cropped_relative_bounds = cropped_absolute_bounds.with_origin(-self.channel_data.full_bounds.position); + +        assert!(self.absolute_bounds().contains(cropped_absolute_bounds), "bounds not valid for layer dimensions"); +        assert!(cropped_relative_bounds.size.area() > 0, "the cropped image would be empty"); + +        Layer { +            channel_data: if cropped_relative_bounds.size == self.channel_data.full_bounds.size { +                assert_eq!(cropped_absolute_bounds.position, self.channel_data.full_bounds.position, "crop bounds size equals, but position does not"); + +                // the cropping would not remove any pixels +                self.channel_data.full_channels +            } +            else { +                let start_x = cropped_relative_bounds.position.x() as usize; // safe, because just checked above +                let start_y = cropped_relative_bounds.position.y() as usize; // safe, because just checked above +                let x_range = start_x .. start_x + cropped_relative_bounds.size.width(); +                let old_width = self.channel_data.full_bounds.size.width(); +                let new_height = cropped_relative_bounds.size.height(); + +                let channels = self.channel_data.full_channels.list.into_iter().map(|channel: AnyChannel<FlatSamples>| { +                    fn crop_samples<T:Copy>(samples: Vec<T>, old_width: usize, new_height: usize, x_range: std::ops::Range<usize>, y_start: usize) -> Vec<T> { +                        let filtered_lines = samples.chunks_exact(old_width).skip(y_start).take(new_height); +                        let trimmed_lines = filtered_lines.map(|line| &line[x_range.clone()]); +                        trimmed_lines.flatten().map(|x|*x).collect() // TODO does this use memcpy? +                    } + +                    let samples = match channel.sample_data { +                        FlatSamples::F16(samples) => FlatSamples::F16(crop_samples( +                            samples, old_width, new_height, x_range.clone(), start_y +                        )), + +                        FlatSamples::F32(samples) => FlatSamples::F32(crop_samples( +                            samples, old_width, new_height, x_range.clone(), start_y +                        )), + +                        FlatSamples::U32(samples) => FlatSamples::U32(crop_samples( +                            samples, old_width, new_height, x_range.clone(), start_y +                        )), +                    }; + +                    AnyChannel { sample_data: samples, ..channel } +                }).collect(); + +                AnyChannels { list: channels } +            }, + +            attributes: self.attributes, +            encoding: self.encoding, +            size: self.size, +        } +    } +} + + + +/// Return the smallest bounding rectangle including all pixels that satisfy the predicate. +/// Worst case: Fully transparent image, visits each pixel once. +/// Best case: Fully opaque image, visits two pixels. +/// Returns `None` if the image is fully transparent. +/// Returns `[(0,0), size]` if the image is fully opaque. +/// Designed to be cache-friendly linear search. Optimized for row-major image vectors. +pub fn try_find_smaller_bounds(current_bounds: IntegerBounds, pixel_at: impl Fn(Vec2<usize>) -> bool) -> Option<IntegerBounds> { +    assert_ne!(current_bounds.size.area(), 0, "cannot find smaller bounds of an image with zero width or height"); +    let Vec2(width, height) = current_bounds.size; + +    // scans top to bottom (left to right) +    let first_top_left_pixel = (0 .. height) +        .flat_map(|y| (0 .. width).map(move |x| Vec2(x,y))) +        .find(|&position| pixel_at(position))?; // return none if no pixel should be kept + +    // scans bottom to top (right to left) +    let first_bottom_right_pixel = (first_top_left_pixel.y() + 1 .. height) // excluding the top line +        .flat_map(|y| (0 .. width).map(move |x| Vec2(x, y))) // x search cannot start at first_top.x, because this must catch all bottom pixels +        .rev().find(|&position| pixel_at(position)) +        .unwrap_or(first_top_left_pixel); // did not find any at bottom, but we know top has some pixel + +    // now we know exactly how much we can throw away top and bottom, +    // but we don't know exactly about left or right +    let top = first_top_left_pixel.y(); +    let bottom = first_bottom_right_pixel.y(); + +    // we only now some arbitrary left and right bounds which we need to refine. +    // because the actual image contents might be wider than the corner points. +    // we know that we do not need to look in the center between min x and max x, +    // as these must be included in any case. +    let mut min_left_x = first_top_left_pixel.x().min(first_bottom_right_pixel.x()); +    let mut max_right_x = first_bottom_right_pixel.x().max(first_top_left_pixel.x()); + +    // requires for loop, because bounds change while searching +    for y in top ..= bottom { + +        // escape the loop if there is nothing left to crop +        if min_left_x == 0 && max_right_x == width - 1 { break; } + +        // search from right image edge towards image center, until known max x, for existing pixels, +        // possibly including some pixels that would have been cropped otherwise +        if max_right_x != width - 1 { +            max_right_x = (max_right_x + 1 .. width).rev() // excluding current max +                .find(|&x| pixel_at(Vec2(x, y))) +                .unwrap_or(max_right_x); +        } + +        // search from left image edge towards image center, until known min x, for existing pixels, +        // possibly including some pixels that would have been cropped otherwise +        if min_left_x != 0 { +            min_left_x = (0 .. min_left_x) // excluding current min +                .find(|&x| pixel_at(Vec2(x, y))) +                .unwrap_or(min_left_x); +        } +    } + +    // TODO add 1px margin to avoid interpolation issues? +    let local_start = Vec2(min_left_x, top); +    let local_end = Vec2(max_right_x + 1, bottom + 1); +    Some(IntegerBounds::new( +        current_bounds.position + local_start.to_i32(), +        local_end - local_start +    )) +} + +impl<S> GetBounds for Layer<S> { +    fn bounds(&self) -> IntegerBounds { +        self.absolute_bounds() +    } +} + +impl<Cropped, Original> CropResult<Cropped, Original> { + +    /// If the image was fully empty, return `None`, otherwise return `Some(cropped_image)`. +    pub fn or_none_if_empty(self) -> Option<Cropped> { +        match self { +            CropResult::Cropped (cropped) => Some(cropped), +            CropResult::Empty { .. } => None, +        } +    } + +    /// If the image was fully empty, crop to one single pixel of all the transparent pixels instead, +    /// leaving the layer intact while reducing memory usage. +    pub fn or_crop_to_1x1_if_empty(self) -> Cropped where Original: Crop<Cropped=Cropped> + GetBounds { +        match self { +            CropResult::Cropped (cropped) => cropped, +            CropResult::Empty { original } => { +                let bounds = original.bounds(); +                if bounds.size == Vec2(0,0) { panic!("layer has width and height of zero") } +                original.crop(IntegerBounds::new(bounds.position, Vec2(1,1))) +            }, +        } +    } +} + + + +#[cfg(test)] +mod test { +    use super::*; + +    #[test] +    fn find_bounds() { +        fn find_bounds(offset: Vec2<i32>, lines: &Vec<Vec<i32>>) -> IntegerBounds { +            if let Some(first_line) = lines.first() { +                assert!(lines.iter().all(|line| line.len() == first_line.len()), "invalid test input"); +                IntegerBounds::new(offset, (first_line.len(), lines.len())) +            } +            else { +                IntegerBounds::new(offset, (0,0)) +            } +        } + +        fn assert_found_smaller_bounds(offset: Vec2<i32>, uncropped_lines: Vec<Vec<i32>>, expected_cropped_lines: Vec<Vec<i32>>) { +            let old_bounds = find_bounds(offset, &uncropped_lines); + +            let found_bounds = try_find_smaller_bounds( +                old_bounds, +                |position| uncropped_lines[position.y()][position.x()] != 0 +            ).unwrap(); + +            let found_bounds = found_bounds.with_origin(-offset); // make indices local + +            let cropped_lines: Vec<Vec<i32>> = +                uncropped_lines[found_bounds.position.y() as usize .. found_bounds.end().y() as usize] +                .iter().map(|uncropped_line|{ +                    uncropped_line[found_bounds.position.x() as usize .. found_bounds.end().x() as usize].to_vec() +                }).collect(); + +            assert_eq!(cropped_lines, expected_cropped_lines); +        } + +        assert_found_smaller_bounds( +            Vec2(-3,-3), + +            vec![ +                vec![ 2, 3, 4 ], +                vec![ 2, 3, 4 ], +            ], + +            vec![ +                vec![ 2, 3, 4 ], +                vec![ 2, 3, 4 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-3,-3), + +            vec![ +                vec![ 2 ], +            ], + +            vec![ +                vec![ 2 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-3,-3), + +            vec![ +                vec![ 0 ], +                vec![ 2 ], +                vec![ 0 ], +                vec![ 0 ], +            ], + +            vec![ +                vec![ 2 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-3,-3), + +            vec![ +                vec![ 0, 0, 0, 3, 0 ], +            ], + +            vec![ +                vec![ 3 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(3,3), + +            vec![ +                vec![ 0, 1, 1, 2, 1, 0 ], +                vec![ 0, 1, 3, 1, 1, 0 ], +                vec![ 0, 1, 1, 1, 1, 0 ], +            ], + +            vec![ +                vec![ 1, 1, 2, 1 ], +                vec![ 1, 3, 1, 1 ], +                vec![ 1, 1, 1, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(3,3), + +            vec![ +                vec![ 0, 0, 0, 0 ], +                vec![ 1, 1, 2, 1 ], +                vec![ 1, 3, 1, 1 ], +                vec![ 1, 1, 1, 1 ], +                vec![ 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1, 1, 2, 1 ], +                vec![ 1, 3, 1, 1 ], +                vec![ 1, 1, 1, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(3,3), + +            vec![ +                vec![ 0, 1, 1, 2, 1, 0 ], +                vec![ 0, 0, 3, 1, 0, 0 ], +                vec![ 0, 1, 1, 1, 1, 0 ], +            ], + +            vec![ +                vec![ 1, 1, 2, 1 ], +                vec![ 0, 3, 1, 0 ], +                vec![ 1, 1, 1, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(3,3), + +            vec![ +                vec![ 0, 0, 1, 2, 0, 0 ], +                vec![ 0, 1, 3, 1, 1, 0 ], +                vec![ 0, 0, 1, 1, 0, 0 ], +            ], + +            vec![ +                vec![ 0, 1, 2, 0 ], +                vec![ 1, 3, 1, 1 ], +                vec![ 0, 1, 1, 0 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(1,3), + +            vec![ +                vec![ 1, 0, 0, 0, ], +                vec![ 0, 0, 0, 0, ], +                vec![ 0, 0, 0, 0, ], +            ], + +            vec![ +                vec![ 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(1,3), + +            vec![ +                vec![ 0, 0, 0, 0, ], +                vec![ 0, 1, 0, 0, ], +                vec![ 0, 0, 0, 0, ], +            ], + +            vec![ +                vec![ 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-1,-3), + +            vec![ +                vec![ 0, 0, 0, 0, ], +                vec![ 0, 0, 0, 1, ], +                vec![ 0, 0, 0, 0, ], +            ], + +            vec![ +                vec![ 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-1,-3), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 1, 1, 0, 0 ], +                vec![ 0, 0, 1, 1, 1, 0, 0 ], +                vec![ 0, 0, 1, 1, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1, 1, 1 ], +                vec![ 1, 1, 1 ], +                vec![ 1, 1, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(1000,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 1, 1, 0, 0 ], +                vec![ 0, 1, 1, 1, 1, 1, 0 ], +                vec![ 0, 0, 1, 1, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 0, 1, 1, 1, 0 ], +                vec![ 1, 1, 1, 1, 1 ], +                vec![ 0, 1, 1, 1, 0 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1, 0, 1 ], +                vec![ 0, 0, 0 ], +                vec![ 1, 0, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1, 0, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 1, 0, 0, 0 ], +                vec![ 0, 0, 0, 2, 0, 0, 0 ], +                vec![ 0, 0, 3, 3, 3, 0, 0 ], +                vec![ 0, 0, 0, 4, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 0, 1, 0 ], +                vec![ 0, 2, 0 ], +                vec![ 3, 3, 3 ], +                vec![ 0, 4, 0 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 1, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 0, 0, 1 ], +                vec![ 0, 0, 0 ], +                vec![ 0, 0, 0 ], +                vec![ 1, 0, 0 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 1, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1, 0, 0, 0 ], +                vec![ 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 1 ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-10,-300), + +            vec![ +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +                vec![ 0, 0, 1, 0, 0, 0, 0 ], +                vec![ 0, 0, 0, 0, 0, 0, 0 ], +            ], + +            vec![ +                vec![ 1 ], +                vec![ 0 ], +                vec![ 0 ], +                vec![ 1 ], +            ] +        ); + + +        assert_found_smaller_bounds( +            Vec2(-1,-3), + +            vec![ +                vec![ 0, 0, 1, 0, ], +                vec![ 0, 0, 0, 1, ], +                vec![ 0, 0, 0, 0, ], +            ], + +            vec![ +                vec![ 1, 0, ], +                vec![ 0, 1, ], +            ] +        ); + +        assert_found_smaller_bounds( +            Vec2(-1,-3), + +            vec![ +                vec![ 1, 0, 0, 0, ], +                vec![ 0, 1, 0, 0, ], +                vec![ 0, 0, 0, 0, ], +                vec![ 0, 0, 0, 0, ], +            ], + +            vec![ +                vec![ 1, 0, ], +                vec![ 0, 1, ], +            ] +        ); +    } + + +    #[test] +    fn find_no_bounds() { +        let pixels = vec![ +            vec![ 0, 0, 0, 0 ], +            vec![ 0, 0, 0, 0 ], +            vec![ 0, 0, 0, 0 ], +        ]; + +        let bounds = try_find_smaller_bounds( +            IntegerBounds::new((0,0), (4,3)), +            |position| pixels[position.y()][position.x()] != 0 +        ); + +        assert_eq!(bounds, None) +    } + +} + + + + diff --git a/vendor/exr/src/image/mod.rs b/vendor/exr/src/image/mod.rs new file mode 100644 index 0000000..db75050 --- /dev/null +++ b/vendor/exr/src/image/mod.rs @@ -0,0 +1,1326 @@ + +//! Data structures that represent a complete exr image. +//! Contains generic structs that must be nested to obtain a complete image type. +//! +//! +//! For example, an rgba image containing multiple layers +//! can be represented using `Image<Layers<SpecificChannels<MyPixelStorage>>>`. +//! An image containing a single layer with arbitrary channels and no deep data +//! can be represented using `Image<Layer<AnyChannels<FlatSamples>>>`. +//! +//! +//! These and other predefined types are included in this module as +//! 1. `PixelImage`: A single layer, fixed set of arbitrary channels. +//! 1. `PixelLayersImage`: Multiple layers, fixed set of arbitrary channels. +//! 1. `RgbaImage`: A single layer, fixed set of channels: rgb, optional a. +//! 1. `RgbaLayersImage`: Multiple layers, fixed set of channels: rgb, optional a. +//! 1. `FlatImage`: Multiple layers, any channels, no deep data. +//! 1. `AnyImage`: All supported data (multiple layers, arbitrary channels, no deep data yet) +//! +//! You can also use your own types inside an image, +//! for example if you want to use a custom sample storage. +//! +//! This is the high-level interface for the pixels of an image. +//! See `exr::blocks` module for a low-level interface. + +pub mod read; +pub mod write; +pub mod crop; +pub mod pixel_vec; +pub mod recursive; +// pub mod channel_groups; + + +use crate::meta::header::{ImageAttributes, LayerAttributes}; +use crate::meta::attribute::{Text, LineOrder}; +use half::f16; +use crate::math::{Vec2, RoundingMode}; +use crate::compression::Compression; +use smallvec::{SmallVec}; +use crate::error::Error; + +/// Don't do anything +pub(crate) fn ignore_progress(_progress: f64){} + +/// This image type contains all supported exr features and can represent almost any image. +/// It currently does not support deep data yet. +pub type AnyImage = Image<Layers<AnyChannels<Levels<FlatSamples>>>>; + +/// This image type contains the most common exr features and can represent almost any plain image. +/// Does not contain resolution levels. Does not support deep data. +pub type FlatImage = Image<Layers<AnyChannels<FlatSamples>>>; + +/// This image type contains multiple layers, with each layer containing a user-defined type of pixels. +pub type PixelLayersImage<Storage, Channels> = Image<Layers<SpecificChannels<Storage, Channels>>>; + +/// This image type contains a single layer containing a user-defined type of pixels. +pub type PixelImage<Storage, Channels> = Image<Layer<SpecificChannels<Storage, Channels>>>; + +/// This image type contains multiple layers, with each layer containing a user-defined type of rgba pixels. +pub type RgbaLayersImage<Storage> = PixelLayersImage<Storage, RgbaChannels>; + +/// This image type contains a single layer containing a user-defined type of rgba pixels. +pub type RgbaImage<Storage> = PixelImage<Storage, RgbaChannels>; + +/// Contains information about the channels in an rgba image, in the order `(red, green, blue, alpha)`. +/// The alpha channel is not required. May be `None` if the image did not contain an alpha channel. +pub type RgbaChannels = (ChannelDescription, ChannelDescription, ChannelDescription, Option<ChannelDescription>); + +/// Contains information about the channels in an rgb image, in the order `(red, green, blue)`. +pub type RgbChannels = (ChannelDescription, ChannelDescription, ChannelDescription); + +/// The complete exr image. +/// `Layers` can be either a single `Layer` or `Layers`. +#[derive(Debug, Clone, PartialEq)] +pub struct Image<Layers> { + +    /// Attributes that apply to the whole image file. +    /// These attributes appear in each layer of the file. +    /// Excludes technical meta data. +    /// Each layer in this image also has its own attributes. +    pub attributes: ImageAttributes, + +    /// The layers contained in the image file. +    /// Can be either a single `Layer` or a list of layers. +    pub layer_data: Layers, +} + +/// A list of layers. `Channels` can be `SpecificChannels` or `AnyChannels`. +pub type Layers<Channels> = SmallVec<[Layer<Channels>; 2]>; + +/// A single Layer, including fancy attributes and compression settings. +/// `Channels` can be either `SpecificChannels` or `AnyChannels` +#[derive(Debug, Clone, PartialEq)] +pub struct Layer<Channels> { + +    /// The actual pixel data. Either `SpecificChannels` or `AnyChannels` +    pub channel_data: Channels, + +    /// Attributes that apply to this layer. +    /// May still contain attributes that should be considered global for an image file. +    /// Excludes technical meta data: Does not contain data window size, line order, tiling, or compression attributes. +    /// The image also has attributes, which do not differ per layer. +    pub attributes: LayerAttributes, + +    /// The pixel resolution of this layer. +    /// See `layer.attributes` for more attributes, like for example layer position. +    pub size: Vec2<usize>, + +    /// How the pixels are split up and compressed. +    pub encoding: Encoding +} + +/// How the pixels are split up and compressed. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Encoding { + +    /// How the pixel data of all channels in this layer is compressed. May be `Compression::Uncompressed`. +    /// See `layer.attributes` for more attributes. +    pub compression: Compression, + +    /// Describes how the pixels of this layer are divided into smaller blocks. +    /// Either splits the image into its scan lines or splits the image into tiles of the specified size. +    /// A single block can be loaded without processing all bytes of a file. +    pub blocks: Blocks, + +    /// In what order the tiles of this header occur in the file. +    /// Does not change any actual image orientation. +    /// See `layer.attributes` for more attributes. +    pub line_order: LineOrder, +} + +/// How the image pixels are split up into separate blocks. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Blocks { + +    /// The image is divided into scan line blocks. +    /// The number of scan lines in a block depends on the compression method. +    ScanLines, + +    /// The image is divided into tile blocks. +    /// Also specifies the size of each tile in the image +    /// and whether this image contains multiple resolution levels. +    /// +    /// The inner `Vec2` describes the size of each tile. +    /// Stays the same number of pixels across all levels. +    Tiles (Vec2<usize>) +} + + +/// A grid of pixels. The pixels are written to your custom pixel storage. +/// `PixelStorage` can be anything, from a flat `Vec<f16>` to `Vec<Vec<AnySample>>`, as desired. +/// In order to write this image to a file, your `PixelStorage` must implement [`GetPixel`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SpecificChannels<Pixels, ChannelsDescription> { + +    /// A description of the channels in the file, as opposed to the channels in memory. +    /// Should always be a tuple containing `ChannelDescription`s, one description for each channel. +    pub channels: ChannelsDescription, // TODO this is awkward. can this be not a type parameter please? maybe vec<option<chan_info>> ?? + +    /// Your custom pixel storage +    // TODO should also support `Levels<YourStorage>`, where levels are desired! +    pub pixels: Pixels, // TODO rename to "pixels"? +} + + +/// A dynamic list of arbitrary channels. +/// `Samples` can currently only be `FlatSamples` or `Levels<FlatSamples>`. +#[derive(Debug, Clone, PartialEq)] +pub struct AnyChannels<Samples> { + +    /// This list must be sorted alphabetically, by channel name. +    /// Use `AnyChannels::sorted` for automatic sorting. +    pub list: SmallVec<[AnyChannel<Samples>; 4]> +} + +/// A single arbitrary channel. +/// `Samples` can currently only be `FlatSamples` or `Levels<FlatSamples>` +#[derive(Debug, Clone, PartialEq)] +pub struct AnyChannel<Samples> { + +    /// One of "R", "G", or "B" most of the time. +    pub name: Text, + +    /// The actual pixel data. +    /// Can be `FlatSamples` or `Levels<FlatSamples>`. +    pub sample_data: Samples, + +    /// This attribute only tells lossy compression methods +    /// whether this value should be quantized exponentially or linearly. +    /// +    /// Should be `false` for red, green, blue and luma channels, as they are not perceived linearly. +    /// Should be `true` for hue, chroma, saturation, and alpha channels. +    pub quantize_linearly: bool, + +    /// How many of the samples are skipped compared to the other channels in this layer. +    /// +    /// Can be used for chroma subsampling for manual lossy data compression. +    /// Values other than 1 are allowed only in flat, scan-line based images. +    /// If an image is deep or tiled, the sampling rates for all of its channels must be 1. +    pub sampling: Vec2<usize>, +} + +/// One or multiple resolution levels of the same image. +/// `Samples` can be `FlatSamples`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Levels<Samples> { + +    /// A single image without smaller versions of itself. +    /// If you only want to handle exclusively this case, use `Samples` directly, and not `Levels<Samples>`. +    Singular(Samples), + +    /// Contains uniformly scaled smaller versions of the original. +    Mip +    { +        /// Whether to round up or down when calculating Mip/Rip levels. +        rounding_mode: RoundingMode, + +        /// The smaller versions of the original. +        level_data: LevelMaps<Samples> +    }, + +    /// Contains any possible combination of smaller versions of the original. +    Rip +    { +        /// Whether to round up or down when calculating Mip/Rip levels. +        rounding_mode: RoundingMode, + +        /// The smaller versions of the original. +        level_data: RipMaps<Samples> +    }, +} + +/// A list of resolution levels. `Samples` can currently only be `FlatSamples`. +// or `DeepAndFlatSamples` (not yet implemented). +pub type LevelMaps<Samples> = Vec<Samples>; + +/// In addition to the full resolution image, +/// this layer also contains smaller versions, +/// and each smaller version has further versions with varying aspect ratios. +/// `Samples` can currently only be `FlatSamples`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RipMaps<Samples> { + +    /// A flattened list containing the individual levels +    pub map_data: LevelMaps<Samples>, + +    /// The number of levels that were generated along the x-axis and y-axis. +    pub level_count: Vec2<usize>, +} + + +// TODO deep data +/*#[derive(Clone, PartialEq)] +pub enum DeepAndFlatSamples { +    Deep(DeepSamples), +    Flat(FlatSamples) +}*/ + +/// A vector of non-deep values (one value per pixel per channel). +/// Stores row after row in a single vector. +/// The precision of all values is either `f16`, `f32` or `u32`. +/// +/// Since this is close to the pixel layout in the byte file, +/// this will most likely be the fastest storage. +/// Using a different storage, for example `SpecificChannels`, +/// will probably be slower. +#[derive(Clone, PartialEq)] // debug is implemented manually +pub enum FlatSamples { + +    /// A vector of non-deep `f16` values. +    F16(Vec<f16>), + +    /// A vector of non-deep `f32` values. +    F32(Vec<f32>), + +    /// A vector of non-deep `u32` values. +    U32(Vec<u32>), +} + + +/*#[derive(Clone, PartialEq)] +pub enum DeepSamples { +    F16(Vec<Vec<f16>>), +    F32(Vec<Vec<f32>>), +    U32(Vec<Vec<u32>>), +}*/ + +use crate::block::samples::*; +use crate::meta::attribute::*; +use crate::error::Result; +use crate::block::samples::Sample; +use crate::image::write::channels::*; +use crate::image::write::layers::WritableLayers; +use crate::image::write::samples::{WritableSamples}; +use crate::meta::{mip_map_levels, rip_map_levels}; +use crate::io::Data; +use crate::image::recursive::{NoneMore, Recursive, IntoRecursive}; +use std::marker::PhantomData; +use std::ops::Not; +use crate::image::validate_results::{ValidationOptions}; + + +impl<Channels> Layer<Channels> { +    /// Sometimes called "data window" +    pub fn absolute_bounds(&self) -> IntegerBounds { +        IntegerBounds::new(self.attributes.layer_position, self.size) +    } +} + + +impl<SampleStorage, Channels> SpecificChannels<SampleStorage, Channels> { +    /// Create some pixels with channel information. +    /// The `Channels` must be a tuple containing either `ChannelDescription` or `Option<ChannelDescription>`. +    /// The length of the tuple dictates the number of channels in the sample storage. +    pub fn new(channels: Channels, source_samples: SampleStorage) -> Self +        where +            SampleStorage: GetPixel, +            SampleStorage::Pixel: IntoRecursive, +            Channels: Sync + Clone + IntoRecursive, +            <Channels as IntoRecursive>::Recursive: WritableChannelsDescription<<SampleStorage::Pixel as IntoRecursive>::Recursive>, +    { +        SpecificChannels { channels, pixels: source_samples } +    } +} + +/// Convert this type into one of the known sample types. +/// Also specify the preferred native type, which dictates the default sample type in the image. +pub trait IntoSample: IntoNativeSample { + +    /// The native sample types that this type should be converted to. +    const PREFERRED_SAMPLE_TYPE: SampleType; +} + +impl IntoSample for f16 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::F16; } +impl IntoSample for f32 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::F32; } +impl IntoSample for u32 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::U32; } + +/// Used to construct a `SpecificChannels`. +/// Call `with_named_channel` as many times as desired, +/// and then call `with_pixels` to define the colors. +#[derive(Debug)] +pub struct SpecificChannelsBuilder<RecursiveChannels, RecursivePixel> { +    channels: RecursiveChannels, +    px: PhantomData<RecursivePixel> +} + +/// This check can be executed at compile time +/// if the channel names are `&'static str` and the compiler is smart enough. +pub trait CheckDuplicates { + +    /// Check for duplicate channel names. +    fn already_contains(&self, name: &Text) -> bool; +} + +impl CheckDuplicates for NoneMore { +    fn already_contains(&self, _: &Text) -> bool { false } +} + +impl<Inner: CheckDuplicates> CheckDuplicates for Recursive<Inner, ChannelDescription> { +    fn already_contains(&self, name: &Text) -> bool { +        &self.value.name == name || self.inner.already_contains(name) +    } +} + +impl SpecificChannels<(),()> +{ +    /// Start building some specific channels. On the result of this function, +    /// call `with_named_channel` as many times as desired, +    /// and then call `with_pixels` to define the colors. +    pub fn build() -> SpecificChannelsBuilder<NoneMore, NoneMore> { +        SpecificChannelsBuilder { channels: NoneMore, px: Default::default() } +    } +} + +impl<RecursiveChannels: CheckDuplicates, RecursivePixel> SpecificChannelsBuilder<RecursiveChannels, RecursivePixel> +{ +    /// Add another channel to this image. Does not add the actual pixels, +    /// but instead only declares the presence of the channel. +    /// Panics if the name contains unsupported characters. +    /// Panics if a channel with the same name already exists. +    /// Use `Text::new_or_none()` to manually handle these cases. +    /// Use `with_channel_details` instead if you want to specify more options than just the name of the channel. +    /// The generic parameter can usually be inferred from the closure in `with_pixels`. +    pub fn with_channel<Sample: IntoSample>(self, name: impl Into<Text>) +                                            -> SpecificChannelsBuilder<Recursive<RecursiveChannels, ChannelDescription>, Recursive<RecursivePixel, Sample>> +    { +        self.with_channel_details::<Sample>(ChannelDescription::named(name, Sample::PREFERRED_SAMPLE_TYPE)) +    } + +    /// Add another channel to this image. Does not add the actual pixels, +    /// but instead only declares the presence of the channel. +    /// Use `with_channel` instead if you only want to specify the name of the channel. +    /// Panics if a channel with the same name already exists. +    /// The generic parameter can usually be inferred from the closure in `with_pixels`. +    pub fn with_channel_details<Sample: Into<Sample>>(self, channel: ChannelDescription) +        -> SpecificChannelsBuilder<Recursive<RecursiveChannels, ChannelDescription>, Recursive<RecursivePixel, Sample>> +    { +        // duplicate channel names are checked later, but also check now to make sure there are no problems with the `SpecificChannelsWriter` +        assert!(self.channels.already_contains(&channel.name).not(), "channel name `{}` is duplicate", channel.name); + +        SpecificChannelsBuilder { +            channels: Recursive::new(self.channels, channel), +            px: PhantomData::default() +        } +    } + +    /// Specify the actual pixel contents of the image. +    /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> Pixel`), +    /// or you can pass your own image if it implements `GetPixel`. +    /// The pixel type must be a tuple with the correct number of entries, depending on the number of channels. +    /// The tuple entries can be either `f16`, `f32`, `u32` or `Sample`. +    /// Use `with_pixel_fn` instead of this function, to get extra type safety for your pixel closure. +    pub fn with_pixels<Pixels>(self, get_pixel: Pixels) -> SpecificChannels<Pixels, RecursiveChannels> +        where Pixels: GetPixel, <Pixels as GetPixel>::Pixel: IntoRecursive<Recursive=RecursivePixel>, +    { +        SpecificChannels { +            channels: self.channels, +            pixels: get_pixel +        } +    } + +    /// Specify the contents of the image. +    /// The pixel type must be a tuple with the correct number of entries, depending on the number of channels. +    /// The tuple entries can be either `f16`, `f32`, `u32` or `Sample`. +    /// Use `with_pixels` instead of this function, if you want to pass an object that is not a closure. +    /// +    /// Usually, the compiler can infer the type of the pixel (for example, `f16,f32,f32`) from the closure. +    /// If that's not possible, you can specify the type of the channels +    /// when declaring the channel (for example, `with_named_channel::<f32>("R")`). +    pub fn with_pixel_fn<Pixel, Pixels>(self, get_pixel: Pixels) -> SpecificChannels<Pixels, RecursiveChannels> +        where Pixels: Sync + Fn(Vec2<usize>) -> Pixel, Pixel: IntoRecursive<Recursive=RecursivePixel>, +    { +        SpecificChannels { +            channels: self.channels, +            pixels: get_pixel +        } +    } +} + +impl<SampleStorage> SpecificChannels< +    SampleStorage, (ChannelDescription, ChannelDescription, ChannelDescription, ChannelDescription) +> +{ + +    /// Create an image with red, green, blue, and alpha channels. +    /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> (R,G,B,A)`), +    /// or you can pass your own image if it implements `GetPixel<Pixel=(R,G,B,A)>`. +    /// Each of `R`, `G`, `B` and `A` can be either `f16`, `f32`, `u32`, or `Sample`. +    pub fn rgba<R, G, B, A>(source_samples: SampleStorage) -> Self +        where R: IntoSample, G: IntoSample, +              B: IntoSample, A: IntoSample, +              SampleStorage: GetPixel<Pixel=(R, G, B, A)> +    { +        SpecificChannels { +            channels: ( +                ChannelDescription::named("R", R::PREFERRED_SAMPLE_TYPE), +                ChannelDescription::named("G", G::PREFERRED_SAMPLE_TYPE), +                ChannelDescription::named("B", B::PREFERRED_SAMPLE_TYPE), +                ChannelDescription::named("A", A::PREFERRED_SAMPLE_TYPE), +            ), +            pixels: source_samples +        } +    } +} + +impl<SampleStorage> SpecificChannels< +    SampleStorage, (ChannelDescription, ChannelDescription, ChannelDescription) +> +{ + +    /// Create an image with red, green, and blue channels. +    /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> (R,G,B)`), +    /// or you can pass your own image if it implements `GetPixel<Pixel=(R,G,B)>`. +    /// Each of `R`, `G` and `B` can be either `f16`, `f32`, `u32`, or `Sample`. +    pub fn rgb<R, G, B>(source_samples: SampleStorage) -> Self +        where R: IntoSample, G: IntoSample, B: IntoSample, +              SampleStorage: GetPixel<Pixel=(R, G, B)> +    { +        SpecificChannels { +            channels: ( +                ChannelDescription::named("R", R::PREFERRED_SAMPLE_TYPE), +                ChannelDescription::named("G", G::PREFERRED_SAMPLE_TYPE), +                ChannelDescription::named("B", B::PREFERRED_SAMPLE_TYPE), +            ), +            pixels: source_samples +        } +    } +} + + +/// A list of samples representing a single pixel. +/// Does not heap allocate for images with 8 or fewer channels. +pub type FlatSamplesPixel = SmallVec<[Sample; 8]>; + +// TODO also deep samples? +impl Layer<AnyChannels<FlatSamples>> { + +    /// Use `samples_at` if you can borrow from this layer +    pub fn sample_vec_at(&self, position: Vec2<usize>) -> FlatSamplesPixel { +        self.samples_at(position).collect() +    } + +    /// Lookup all channels of a single pixel in the image +    pub fn samples_at(&self, position: Vec2<usize>) -> FlatSampleIterator<'_> { +        FlatSampleIterator { +            layer: self, +            channel_index: 0, +            position +        } +    } +} + +/// Iterate over all channels of a single pixel in the image +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct FlatSampleIterator<'s> { +    layer: &'s Layer<AnyChannels<FlatSamples>>, +    channel_index: usize, +    position: Vec2<usize>, +} + +impl Iterator for FlatSampleIterator<'_> { +    type Item = Sample; + +    fn next(&mut self) -> Option<Self::Item> { +        if self.channel_index < self.layer.channel_data.list.len() { +            let channel = &self.layer.channel_data.list[self.channel_index]; +            let sample = channel.sample_data.value_by_flat_index(self.position.flat_index_for_size(self.layer.size)); +            self.channel_index += 1; +            Some(sample) +        } +        else { None } +    } + +    fn nth(&mut self, pos: usize) -> Option<Self::Item> { +        self.channel_index += pos; +        self.next() +    } + +    fn size_hint(&self) -> (usize, Option<usize>) { +        let remaining = self.layer.channel_data.list.len().saturating_sub(self.channel_index); +        (remaining, Some(remaining)) +    } +} + +impl ExactSizeIterator for FlatSampleIterator<'_> {} + +impl<SampleData> AnyChannels<SampleData>{ + +    /// A new list of arbitrary channels. Sorts the list to make it alphabetically stable. +    pub fn sort(mut list: SmallVec<[AnyChannel<SampleData>; 4]>) -> Self { +        list.sort_unstable_by_key(|channel| channel.name.clone()); // TODO no clone? +        Self { list } +    } +} + +// FIXME check content size of layer somewhere??? before writing? +impl<LevelSamples> Levels<LevelSamples> { + +    /// Get a resolution level by index, sorted by size, decreasing. +    pub fn get_level(&self, level: Vec2<usize>) -> Result<&LevelSamples> { +        match self { +            Levels::Singular(block) => { +                debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug"); +                Ok(block) +            }, + +            Levels::Mip { level_data, .. } => { +                debug_assert_eq!(level.x(), level.y(), "mip map levels must be equal on x and y bug"); +                level_data.get(level.x()).ok_or(Error::invalid("block mip level index")) +            }, + +            Levels::Rip { level_data, .. } => { +                level_data.get_by_level(level).ok_or(Error::invalid("block rip level index")) +            } +        } +    } + +    /// Get a resolution level by index, sorted by size, decreasing. +    // TODO storage order for RIP maps? +    pub fn get_level_mut(&mut self, level: Vec2<usize>) -> Result<&mut LevelSamples> { +        match self { +            Levels::Singular(ref mut block) => { +                debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug"); +                Ok(block) +            }, + +            Levels::Mip { level_data, .. } => { +                debug_assert_eq!(level.x(), level.y(), "mip map levels must be equal on x and y bug"); +                level_data.get_mut(level.x()).ok_or(Error::invalid("block mip level index")) +            }, + +            Levels::Rip { level_data, .. } => { +                level_data.get_by_level_mut(level).ok_or(Error::invalid("block rip level index")) +            } +        } +    } + +    /// Get a slice of all resolution levels, sorted by size, decreasing. +    pub fn levels_as_slice(&self) -> &[LevelSamples] { +        match self { +            Levels::Singular(data) => std::slice::from_ref(data), +            Levels::Mip { level_data, .. } => level_data, +            Levels::Rip { level_data, .. } => &level_data.map_data, +        } +    } + +    /// Get a mutable slice of all resolution levels, sorted by size, decreasing. +    pub fn levels_as_slice_mut(&mut self) -> &mut [LevelSamples] { +        match self { +            Levels::Singular(data) => std::slice::from_mut(data), +            Levels::Mip { level_data, .. } => level_data, +            Levels::Rip { level_data, .. } => &mut level_data.map_data, +        } +    } + +    // TODO simplify working with levels in general! like level_size_by_index and such + +    /*pub fn levels_with_size(&self, rounding: RoundingMode, max_resolution: Vec2<usize>) -> Vec<(Vec2<usize>, &S)> { +        match self { +            Levels::Singular(ref data) => vec![ (max_resolution, data) ], +            Levels::Mip(ref maps) => mip_map_levels(rounding, max_resolution).map(|(_index, size)| size).zip(maps).collect(), +            Levels::Rip(ref rip_maps) => rip_map_levels(rounding, max_resolution).map(|(_index, size)| size).zip(&rip_maps.map_data).collect(), +        } +    }*/ + +    /// Whether this stores multiple resolution levels. +    pub fn level_mode(&self) -> LevelMode { +        match self { +            Levels::Singular(_) => LevelMode::Singular, +            Levels::Mip { .. } => LevelMode::MipMap, +            Levels::Rip { .. } => LevelMode::RipMap, +        } +    } +} + +impl<Samples> RipMaps<Samples> { + +    /// Flatten the 2D level index to a one dimensional index. +    pub fn get_level_index(&self, level: Vec2<usize>) -> usize { +        level.flat_index_for_size(self.level_count) +    } + +    /// Return a level by level index. Level `0` has the largest resolution. +    pub fn get_by_level(&self, level: Vec2<usize>) -> Option<&Samples> { +        self.map_data.get(self.get_level_index(level)) +    } + +    /// Return a mutable level reference by level index. Level `0` has the largest resolution. +    pub fn get_by_level_mut(&mut self, level: Vec2<usize>) -> Option<&mut Samples> { +        let index = self.get_level_index(level); +        self.map_data.get_mut(index) +    } +} + +impl FlatSamples { + +    /// The number of samples in the image. Should be the width times the height. +    /// Might vary when subsampling is used. +    pub fn len(&self) -> usize { +        match self { +            FlatSamples::F16(vec) => vec.len(), +            FlatSamples::F32(vec) => vec.len(), +            FlatSamples::U32(vec) => vec.len(), +        } +    } + +    /// Views all samples in this storage as f32. +    /// Matches the underlying sample type again for every sample, +    /// match yourself if performance is critical! Does not allocate. +    pub fn values_as_f32<'s>(&'s self) -> impl 's + Iterator<Item = f32> { +        self.values().map(|sample| sample.to_f32()) +    } + +    /// All samples in this storage as iterator. +    /// Matches the underlying sample type again for every sample, +    /// match yourself if performance is critical! Does not allocate. +    pub fn values<'s>(&'s self) -> impl 's + Iterator<Item = Sample> { +        (0..self.len()).map(move |index| self.value_by_flat_index(index)) +    } + +    /// Lookup a single value, by flat index. +    /// The flat index can be obtained using `Vec2::flatten_for_width` +    /// which computes the index in a flattened array of pixel rows. +    pub fn value_by_flat_index(&self, index: usize) -> Sample { +        match self { +            FlatSamples::F16(vec) => Sample::F16(vec[index]), +            FlatSamples::F32(vec) => Sample::F32(vec[index]), +            FlatSamples::U32(vec) => Sample::U32(vec[index]), +        } +    } +} + + +impl<'s, ChannelData:'s> Layer<ChannelData> { + +    /// Create a layer with the specified size, attributes, encoding and channels. +    /// The channels can be either `SpecificChannels` or `AnyChannels`. +    pub fn new( +        dimensions: impl Into<Vec2<usize>>, +        attributes: LayerAttributes, +        encoding: Encoding, +        channels: ChannelData +    ) -> Self +        where ChannelData: WritableChannels<'s> +    { +        Layer { channel_data: channels, attributes, size: dimensions.into(), encoding } +    } + +    // TODO test pls wtf +    /// Panics for images with Scanline encoding. +    pub fn levels_with_resolution<'l, L>(&self, levels: &'l Levels<L>) -> Box<dyn 'l + Iterator<Item=(&'l L, Vec2<usize>)>> { +        match levels { +            Levels::Singular(level) => Box::new(std::iter::once((level, self.size))), + +            Levels::Mip { rounding_mode, level_data } => Box::new(level_data.iter().zip( +                mip_map_levels(*rounding_mode, self.size) +                    .map(|(_index, size)| size) +            )), + +            Levels::Rip { rounding_mode, level_data } => Box::new(level_data.map_data.iter().zip( +                rip_map_levels(*rounding_mode, self.size) +                    .map(|(_index, size)| size) +            )), +        } +    } +} + +impl Encoding { + +    /// No compression. Massive space requirements. +    /// Fast, because it minimizes data shuffling and reallocation. +    pub const UNCOMPRESSED: Encoding = Encoding { +        compression: Compression::Uncompressed, +        blocks: Blocks::ScanLines, // longest lines, faster memcpy +        line_order: LineOrder::Increasing // presumably fastest? +    }; + +    /// Run-length encoding with tiles of 64x64 pixels. This is the recommended default encoding. +    /// Almost as fast as uncompressed data, but optimizes single-colored areas such as mattes and masks. +    pub const FAST_LOSSLESS: Encoding = Encoding { +        compression: Compression::RLE, +        blocks: Blocks::Tiles(Vec2(64, 64)), // optimize for RLE compression +        line_order: LineOrder::Unspecified +    }; + +    /// ZIP compression with blocks of 16 lines. Slow, but produces small files without visible artefacts. +    pub const SMALL_LOSSLESS: Encoding = Encoding { +        compression: Compression::ZIP16, +        blocks: Blocks::ScanLines, // largest possible, but also with high probability of parallel workers +        line_order: LineOrder::Increasing +    }; + +    /// PIZ compression with tiles of 256x256 pixels. Small images, not too slow. +    pub const SMALL_FAST_LOSSLESS: Encoding = Encoding { +        compression: Compression::PIZ, +        blocks: Blocks::Tiles(Vec2(256, 256)), +        line_order: LineOrder::Unspecified +    }; +} + +impl Default for Encoding { +    fn default() -> Self { Encoding::FAST_LOSSLESS } +} + +impl<'s, LayerData: 's> Image<LayerData> where LayerData: WritableLayers<'s> { +    /// Create an image with one or multiple layers. The layer can be a `Layer`, or `Layers` small vector, or `Vec<Layer>` or `&[Layer]`. +    pub fn new(image_attributes: ImageAttributes, layer_data: LayerData) -> Self { +        Image { attributes: image_attributes, layer_data } +    } +} + +// explorable constructor alias +impl<'s, Channels: 's> Image<Layers<Channels>> where Channels: WritableChannels<'s> { +    /// Create an image with multiple layers. The layer can be a `Vec<Layer>` or `Layers` (a small vector). +    pub fn from_layers(image_attributes: ImageAttributes, layer_data: impl Into<Layers<Channels>>) -> Self { +        Self::new(image_attributes, layer_data.into()) +    } +} + + +impl<'s, ChannelData:'s> Image<Layer<ChannelData>> where ChannelData: WritableChannels<'s> { + +    /// Uses the display position and size to the channel position and size of the layer. +    pub fn from_layer(layer: Layer<ChannelData>) -> Self { +        let bounds = IntegerBounds::new(layer.attributes.layer_position, layer.size); +        Self::new(ImageAttributes::new(bounds), layer) +    } + +    /// Uses empty attributes. +    pub fn from_encoded_channels(size: impl Into<Vec2<usize>>, encoding: Encoding, channels: ChannelData) -> Self { +        // layer name is not required for single-layer images +        Self::from_layer(Layer::new(size, LayerAttributes::default(), encoding, channels)) +    } + +    /// Uses empty attributes and fast compression. +    pub fn from_channels(size: impl Into<Vec2<usize>>, channels: ChannelData) -> Self { +        Self::from_encoded_channels(size, Encoding::default(), channels) +    } +} + + +impl Image<NoneMore> { + +    /// Create an empty image, to be filled with layers later on. Add at least one layer to obtain a valid image. +    /// Call `with_layer(another_layer)` for each layer you want to add to this image. +    pub fn empty(attributes: ImageAttributes) -> Self { Self { attributes, layer_data: NoneMore } } +} + +impl<'s, InnerLayers: 's> Image<InnerLayers> where +    InnerLayers: WritableLayers<'s>, +{ +    /// Add another layer to this image. The layer type does +    /// not have to equal the existing layers in this image. +    pub fn with_layer<NewChannels>(self, layer: Layer<NewChannels>) +        -> Image<Recursive<InnerLayers, Layer<NewChannels>>> +        where NewChannels: 's + WritableChannels<'s> +    { +        Image { +            attributes: self.attributes, +            layer_data: Recursive::new(self.layer_data, layer) +        } +    } +} + + +impl<'s, SampleData: 's> AnyChannel<SampleData> { + +    /// Create a new channel without subsampling. +    /// +    /// Automatically flags this channel for specialized compression +    /// if the name is "R", "G", "B", "Y", or "L", +    /// as they typically encode values that are perceived non-linearly. +    /// Construct the value yourself using `AnyChannel { .. }`, if you want to control this flag. +    pub fn new(name: impl Into<Text>, sample_data: SampleData) -> Self where SampleData: WritableSamples<'s> { +        let name: Text = name.into(); + +        AnyChannel { +            quantize_linearly: ChannelDescription::guess_quantization_linearity(&name), +            name, sample_data, +            sampling: Vec2(1, 1), +        } +    } + +    /*/// This is the same as `AnyChannel::new()`, but additionally ensures that the closure type is correct. +    pub fn from_closure<V>(name: Text, sample_data: S) -> Self +        where S: Sync + Fn(Vec2<usize>) -> V, V: InferSampleType + Data +    { +        Self::new(name, sample_data) +    }*/ +} + +impl std::fmt::Debug for FlatSamples { +    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +        if self.len() <= 6 { +            match self { +                FlatSamples::F16(vec) => vec.fmt(formatter), +                FlatSamples::F32(vec) => vec.fmt(formatter), +                FlatSamples::U32(vec) => vec.fmt(formatter), +            } +        } +        else { +            match self { +                FlatSamples::F16(vec) => write!(formatter, "[f16; {}]", vec.len()), +                FlatSamples::F32(vec) => write!(formatter, "[f32; {}]", vec.len()), +                FlatSamples::U32(vec) => write!(formatter, "[u32; {}]", vec.len()), +            } +        } +    } +} + + + +/// Compare the result of a round trip test with the original method. +/// Supports lossy compression methods. +// #[cfg(test)] TODO do not ship this code +pub mod validate_results { +    use crate::prelude::*; +    use smallvec::Array; +    use crate::prelude::recursive::*; +    use crate::image::write::samples::WritableSamples; +    use std::ops::Not; +    use crate::block::samples::IntoNativeSample; + + +    /// Compare two objects, but with a few special quirks. +    /// Intended mainly for unit testing. +    pub trait ValidateResult { + +        /// Compare self with the other. Panics if not equal. +        /// +        /// Exceptional behaviour: +        /// This does not work the other way around! This method is not symmetrical! +        /// Returns whether the result is correct for this image. +        /// For lossy compression methods, uses approximate equality. +        /// Intended for unit testing. +        /// +        /// Warning: If you use `SpecificChannels`, the comparison might be inaccurate +        /// for images with mixed compression methods. This is to be used with `AnyChannels` mainly. +        fn assert_equals_result(&self, result: &Self) { +            self.validate_result(result, ValidationOptions::default(), || String::new()).unwrap(); +        } + +        /// Compare self with the other. +        /// Exceptional behaviour: +        /// - Any two NaN values are considered equal, regardless of bit representation. +        /// - If a `lossy` is specified, any two values that differ only by a small amount will be considered equal. +        /// - If `nan_to_zero` is true, and __self is NaN/Infinite and the other value is zero, they are considered equal__ +        ///   (because some compression methods replace nan with zero) +        /// +        /// This does not work the other way around! This method is not symmetrical! +        fn validate_result( +            &self, lossy_result: &Self, +            options: ValidationOptions, +            // this is a lazy string, because constructing a string is only necessary in the case of an error, +            // but eats up memory and allocation time every time. this was measured. +            context: impl Fn() -> String +        ) -> ValidationResult; +    } + +    /// Whether to do accurate or approximate comparison. +    #[derive(Default, Debug, Eq, PartialEq, Hash, Copy, Clone)] +    pub struct ValidationOptions { +        allow_lossy: bool, +        nan_converted_to_zero: bool, +    } + +    /// If invalid, contains the error message. +    pub type ValidationResult = std::result::Result<(), String>; + + +    impl<C> ValidateResult for Image<C> where C: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self.attributes != other.attributes { Err(location() + "| image > attributes") } +            else { self.layer_data.validate_result(&other.layer_data, options, || location() + "| image > layer data") } +        } +    } + +    impl<S> ValidateResult for Layer<AnyChannels<S>> +        where AnyChannel<S>: ValidateResult, S: for<'a> WritableSamples<'a> +    { +        fn validate_result(&self, other: &Self, _overridden: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            let location = || format!("{} (layer `{:?}`)", location(), self.attributes.layer_name); +            if self.attributes != other.attributes { Err(location() + " > attributes") } +            else if self.encoding != other.encoding { Err(location() + " > encoding") } +            else if self.size != other.size { Err(location() + " > size") } +            else if self.channel_data.list.len() != other.channel_data.list.len() { Err(location() + " > channel count") } +            else { +                for (own_chan, other_chan) in self.channel_data.list.iter().zip(other.channel_data.list.iter()) { +                    own_chan.validate_result( +                        other_chan, + +                        ValidationOptions { +                            // no tolerance for lossless channels +                            allow_lossy: other.encoding.compression +                                .is_lossless_for(other_chan.sample_data.sample_type()).not(), + +                            // consider nan and zero equal if the compression method does not support nan +                            nan_converted_to_zero: other.encoding.compression.supports_nan().not() +                        }, + +                        || format!("{} > channel `{}`", location(), own_chan.name) +                    )?; +                } +                Ok(()) +            } +        } +    } + +    impl<Px, Desc> ValidateResult for Layer<SpecificChannels<Px, Desc>> +        where SpecificChannels<Px, Desc>: ValidateResult +    { +        /// This does an approximate comparison for all channels, +        /// even if some channels can be compressed without loss. +        fn validate_result(&self, other: &Self, _overridden: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            let location = || format!("{} (layer `{:?}`)", location(), self.attributes.layer_name); + +            // TODO dedup with above +            if self.attributes != other.attributes { Err(location() + " > attributes") } +            else if self.encoding != other.encoding { Err(location() + " > encoding") } +            else if self.size != other.size { Err(location() + " > size") } +            else { +                let options = ValidationOptions { +                    // no tolerance for lossless channels +                    // pxr only looses data for f32 values, B44 only for f16, not other any other types +                    allow_lossy: other.encoding.compression.may_loose_data(),// TODO check specific channels sample types + +                    // consider nan and zero equal if the compression method does not support nan +                    nan_converted_to_zero: other.encoding.compression.supports_nan().not() +                }; + +                self.channel_data.validate_result(&other.channel_data, options, || location() + " > channel_data")?; +                Ok(()) +            } +        } +    } + +    impl<S> ValidateResult for AnyChannels<S> where S: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.list.validate_result(&other.list, options, location) +        } +    } + +    impl<S> ValidateResult for AnyChannel<S> where S: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self.name != other.name { Err(location() + " > name") } +            else if self.quantize_linearly != other.quantize_linearly { Err(location() + " > quantize_linearly") } +            else if self.sampling != other.sampling { Err(location() + " > sampling") } +            else { +                self.sample_data.validate_result(&other.sample_data, options, || location() + " > sample_data") +            } +        } +    } + +    impl<Pxs, Chans> ValidateResult for SpecificChannels<Pxs, Chans> where Pxs: ValidateResult, Chans: Eq { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self.channels != other.channels { Err(location() + " > specific channels") } +            else { self.pixels.validate_result(&other.pixels, options, || location() + " > specific pixels") } +        } +    } + +    impl<S> ValidateResult for Levels<S> where S: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.levels_as_slice().validate_result(&other.levels_as_slice(), options, || location() + " > levels") +        } +    } + +    impl ValidateResult for FlatSamples { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            use FlatSamples::*; +            match (self, other) { +                (F16(values), F16(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > f16 samples"), +                (F32(values), F32(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > f32 samples"), +                (U32(values), U32(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > u32 samples"), +                (own, other) => Err(format!("{}: samples type mismatch. expected {:?}, found {:?}", location(), own.sample_type(), other.sample_type())) +            } +        } +    } + +    impl<T> ValidateResult for &[T] where T: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self.len() != other.len() { Err(location() + " count") } +            else { +                for (index, (slf, other)) in self.iter().zip(other.iter()).enumerate() { +                    slf.validate_result(other, options, ||format!("{} element [{}] of {}", location(), index, self.len()))?; +                } +                Ok(()) +            } +        } +    } + +    impl<A: Array> ValidateResult for SmallVec<A> where A::Item: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.as_slice().validate_result(&other.as_slice(), options, location) +        } +    } + +    impl<A> ValidateResult for Vec<A> where A: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.as_slice().validate_result(&other.as_slice(), options, location) +        } +    } + +    impl<A,B,C,D> ValidateResult for (A, B, C, D) where A: Clone+ ValidateResult, B: Clone+ ValidateResult, C: Clone+ ValidateResult, D: Clone+ ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.clone().into_recursive().validate_result(&other.clone().into_recursive(), options, location) +        } +    } + +    impl<A,B,C> ValidateResult for (A, B, C) where A: Clone+ ValidateResult, B: Clone+ ValidateResult, C: Clone+ ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.clone().into_recursive().validate_result(&other.clone().into_recursive(), options, location) +        } +    } + +    // // (low priority because it is only used in the tests) +    /*TODO +    impl<Tuple> SimilarToLossy for Tuple where +        Tuple: Clone + IntoRecursive, +        <Tuple as IntoRecursive>::Recursive: SimilarToLossy, +    { +        fn similar_to_lossy(&self, other: &Self, max_difference: f32) -> bool { +            self.clone().into_recursive().similar_to_lossy(&other.clone().into_recursive(), max_difference) +        } // TODO no clone? +    }*/ + + +    // implement for recursive types +    impl ValidateResult for NoneMore { +        fn validate_result(&self, _: &Self, _: ValidationOptions, _: impl Fn()->String) -> ValidationResult { Ok(()) } +    } + +    impl<Inner, T> ValidateResult for Recursive<Inner, T> where Inner: ValidateResult, T: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            self.value.validate_result(&other.value, options, &location).and_then(|()| +                self.inner.validate_result(&other.inner, options, &location) +            ) +        } +    } + +    impl<S> ValidateResult for Option<S> where S: ValidateResult { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            match (self, other) { +                (None, None) => Ok(()), +                (Some(value), Some(other)) => value.validate_result(other, options, location), +                _ => Err(location() + ": option mismatch") +            } +        } +    } + +    impl ValidateResult for f32 { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self == other || (self.is_nan() && other.is_nan()) || (options.nan_converted_to_zero && !self.is_normal() && *other == 0.0) { +                return Ok(()); +            } + +            if options.allow_lossy { +                let epsilon = 0.06; +                let max_difference = 0.1; + +                let adaptive_threshold = epsilon * (self.abs() + other.abs()); +                let tolerance = adaptive_threshold.max(max_difference); +                let difference = (self - other).abs(); + +                return if difference <= tolerance { Ok(()) } +                else { Err(format!("{}: expected ~{}, found {} (adaptive tolerance {})", location(), self, other, tolerance)) }; +            } + +            Err(format!("{}: expected exactly {}, found {}", location(), self, other)) +        } +    } + +    impl ValidateResult for f16 { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self.to_bits() == other.to_bits() { Ok(()) } else { +                self.to_f32().validate_result(&other.to_f32(), options, location) +            } +        } +    } + +    impl ValidateResult for u32 { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            if self == other { Ok(()) } else { // todo to float conversion resulting in nan/infinity? +                self.to_f32().validate_result(&other.to_f32(), options, location) +            } +        } +    } + +    impl ValidateResult for Sample { +        fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { +            use Sample::*; +            match (self, other) { +                (F16(a), F16(b)) => a.validate_result(b, options, ||location() + " (f16)"), +                (F32(a), F32(b)) => a.validate_result(b, options, ||location() + " (f32)"), +                (U32(a), U32(b)) => a.validate_result(b, options, ||location() + " (u32)"), +                (_,_) => Err(location() + ": sample type mismatch") +            } +        } +    } + + +    #[cfg(test)] +    mod test_value_result { +        use std::f32::consts::*; +        use std::io::Cursor; +        use crate::image::pixel_vec::PixelVec; +        use crate::image::validate_results::{ValidateResult, ValidationOptions}; +        use crate::meta::attribute::LineOrder::Increasing; +        use crate::image::{FlatSamples}; + +        fn expect_valid<T>(original: &T, result: &T, allow_lossy: bool, nan_converted_to_zero: bool) where T: ValidateResult { +            original.validate_result( +                result, +                ValidationOptions { allow_lossy, nan_converted_to_zero }, +                || String::new() +            ).unwrap(); +        } + +        fn expect_invalid<T>(original: &T, result: &T, allow_lossy: bool, nan_converted_to_zero: bool) where T: ValidateResult { +            assert!(original.validate_result( +                result, +                ValidationOptions { allow_lossy, nan_converted_to_zero }, +                || String::new() +            ).is_err()); +        } + +        #[test] +        fn test_f32(){ +            let original:&[f32] = &[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, -20.4, f32::NAN]; +            let lossy:&[f32] = &[0.0, 0.2, 0.2, 0.3, 0.4, 0.5, -20.5, f32::NAN]; + +            expect_valid(&original, &original, true, true); +            expect_valid(&original, &original, true, false); +            expect_valid(&original, &original, false, true); +            expect_valid(&original, &original, false, false); + +            expect_invalid(&original, &lossy, false, false); +            expect_valid(&original, &lossy, true, false); + +            expect_invalid(&original, &&original[..original.len()-2], true, true); + +            // test relative comparison with some large values +            expect_valid(&1_000_f32, &1_001_f32, true, false); +            expect_invalid(&1_000_f32, &1_200_f32, true, false); + +            expect_valid(&10_000_f32, &10_100_f32, true, false); +            expect_invalid(&10_000_f32, &12_000_f32, true, false); + +            expect_valid(&33_120_f32, &30_120_f32, true, false); +            expect_invalid(&33_120_f32, &20_120_f32, true, false); +        } + +        #[test] +        fn test_nan(){ +            let original:&[f32] = &[ 0.0, f32::NAN, f32::NAN ]; +            let lossy:&[f32] = &[ 0.0, f32::NAN, 0.0 ]; + +            expect_valid(&original, &lossy, true, true); +            expect_invalid(&lossy, &original, true, true); + +            expect_valid(&lossy, &lossy, true, true); +            expect_valid(&lossy, &lossy, false, true); +        } + +        #[test] +        fn test_error(){ + +            fn print_error<T: ValidateResult>(original: &T, lossy: &T, allow_lossy: bool){ +                let message = original +                    .validate_result( +                        &lossy, +                        ValidationOptions { allow_lossy, .. Default::default() }, +                        || String::new() // type_name::<T>().to_string() +                    ) +                    .unwrap_err(); + +                println!("message: {}", message); +            } + +            let original:&[f32] = &[ 0.0, f32::NAN, f32::NAN ]; +            let lossy:&[f32] = &[ 0.0, f32::NAN, 0.0 ]; +            print_error(&original, &lossy, false); + +            print_error(&2.0, &1.0, true); +            print_error(&2.0, &1.0, false); + +            print_error(&FlatSamples::F32(vec![0.1,0.1]), &FlatSamples::F32(vec![0.1,0.2]), false); +            print_error(&FlatSamples::U32(vec![0,0]), &FlatSamples::F32(vec![0.1,0.2]), false); + +            { +                let image = crate::prelude::read_all_data_from_file("tests/images/valid/openexr/MultiResolution/Kapaa.exr").unwrap(); + +                let mut mutated = image.clone(); +                let samples = mutated.layer_data.first_mut().unwrap() +                    .channel_data.list.first_mut().unwrap().sample_data.levels_as_slice_mut().first_mut().unwrap(); + +                match samples { +                    FlatSamples::F16(vals) => vals[100] = vals[1], +                    FlatSamples::F32(vals) => vals[100] = vals[1], +                    FlatSamples::U32(vals) => vals[100] = vals[1], +                } + +                print_error(&image, &mutated, false); +            } + +            // TODO check out more nested behaviour! +        } + +        #[test] +        fn test_uncompressed(){ +            use crate::prelude::*; + +            let original_pixels: [(f32,f32,f32); 4] = [ +                (0.0, -1.1, PI), +                (0.0, -1.1, TAU), +                (0.0, -1.1, f32::EPSILON), +                (f32::NAN, 10000.1, -1024.009), +            ]; + +            let mut file_bytes = Vec::new(); +            let original_image = Image::from_encoded_channels( +                (2,2), +                Encoding { +                    compression: Compression::Uncompressed, +                    line_order: Increasing, // FIXME unspecified may be optimized to increasing, which destroys test eq +                    .. Encoding::default() +                }, +                SpecificChannels::rgb(PixelVec::new(Vec2(2,2), original_pixels.to_vec())) +            ); + +            original_image.write().to_buffered(Cursor::new(&mut file_bytes)).unwrap(); + +            let lossy_image = read().no_deep_data().largest_resolution_level() +                .rgb_channels(PixelVec::<(f32,f32,f32)>::constructor, PixelVec::set_pixel) +                .first_valid_layer().all_attributes().from_buffered(Cursor::new(&file_bytes)).unwrap(); + +            original_image.assert_equals_result(&original_image); +            lossy_image.assert_equals_result(&lossy_image); +            original_image.assert_equals_result(&lossy_image); +            lossy_image.assert_equals_result(&original_image); +        } + +        #[test] +        fn test_compiles(){ +            use crate::prelude::*; + +            fn accepts_validatable_value(_: &impl ValidateResult){} + +            let object: Levels<FlatSamples> = Levels::Singular(FlatSamples::F32(Vec::default())); +            accepts_validatable_value(&object); + +            let object: AnyChannels<Levels<FlatSamples>> = AnyChannels::sort(SmallVec::default()); +            accepts_validatable_value(&object); + +            let layer: Layer<AnyChannels<Levels<FlatSamples>>> = Layer::new((0,0), Default::default(), Default::default(), object); +            accepts_validatable_value(&layer); + +            let layers: Layers<AnyChannels<Levels<FlatSamples>>> = Default::default(); +            accepts_validatable_value(&layers); + +            let object: Image<Layer<AnyChannels<Levels<FlatSamples>>>> = Image::from_layer(layer); +            object.assert_equals_result(&object); +        } +    } +} + + diff --git a/vendor/exr/src/image/pixel_vec.rs b/vendor/exr/src/image/pixel_vec.rs new file mode 100644 index 0000000..3447bf2 --- /dev/null +++ b/vendor/exr/src/image/pixel_vec.rs @@ -0,0 +1,97 @@ + +//! Provides a predefined pixel storage. +//! Currently only contains a simple flattened vector storage. +//! Use the functions `create_pixel_vec::<YourPixelTuple>` and +//! `set_pixel_in_vec::<YourPixelTuple>` for reading a predefined pixel vector. +//! Use the function `PixelVec::new` to create a pixel vector which can be written to a file. + +use super::*; + +/// Store all samples in a single array. +/// All samples will be converted to the type `T`. +/// This supports all the sample types, `f16`, `f32`, and `u32`. +/// +/// The flattened vector contains all rows one after another. +/// In each row, for each pixel, its red, green, blue, and then alpha +/// samples are stored one after another. +/// +/// Use `PixelVec.compute_pixel_index(position)` +/// to compute the flat index of a specific pixel. +#[derive(Eq, PartialEq, Clone)] +pub struct PixelVec<T> { + +    /// The resolution of this layer. +    pub resolution: Vec2<usize>, + +    /// The flattened vector contains all rows one after another. +    /// In each row, for each pixel, its red, green, blue, and then alpha +    /// samples are stored one after another. +    /// +    /// Use `Flattened::compute_pixel_index(image, position)` +    /// to compute the flat index of a specific pixel. +    pub pixels: Vec<T>, +} + +impl<Pixel> PixelVec<Pixel> { + +    /// Create a new flattened pixel storage, filled with default pixels. +    /// Accepts a `Channels` parameter, which is not used, so that it can be passed as a function pointer instead of calling it. +    pub fn constructor<Channels>(resolution: Vec2<usize>, _: &Channels) -> Self where Pixel: Default + Clone { +        PixelVec { resolution, pixels: vec![Pixel::default(); resolution.area()] } +    } + +    /// Examine a pixel of a `PixelVec<T>` image. +    /// Can usually be used as a function reference instead of calling it directly. +    #[inline] +    pub fn get_pixel(&self, position: Vec2<usize>) -> &Pixel where Pixel: Sync { +        &self.pixels[self.compute_pixel_index(position)] +    } + +    /// Update a pixel of a `PixelVec<T>` image. +    /// Can usually be used as a function reference instead of calling it directly. +    #[inline] +    pub fn set_pixel(&mut self, position: Vec2<usize>, pixel: Pixel) { +        let index = self.compute_pixel_index(position); +        self.pixels[index] = pixel; +    } + +    /// Create a new flattened pixel storage, checking the length of the provided pixels vector. +    pub fn new(resolution: impl Into<Vec2<usize>>, pixels: Vec<Pixel>) -> Self { +        let size = resolution.into(); +        assert_eq!(size.area(), pixels.len(), "expected {} samples, but vector length is {}", size.area(), pixels.len()); +        Self { resolution: size, pixels } +    } + +    /// Compute the flat index of a specific pixel. Returns a range of either 3 or 4 samples. +    /// The computed index can be used with `PixelVec.samples[index]`. +    /// Panics for invalid sample coordinates. +    #[inline] +    pub fn compute_pixel_index(&self, position: Vec2<usize>) -> usize { +        position.flat_index_for_size(self.resolution) +    } +} + +use crate::image::validate_results::{ValidateResult, ValidationResult}; + +impl<Px> ValidateResult for PixelVec<Px> where Px: ValidateResult { +    fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn() -> String) -> ValidationResult { +        if self.resolution != other.resolution { Err(location() + " > resolution") } +        else { self.pixels.as_slice().validate_result(&other.pixels.as_slice(), options, || location() + " > pixels") } +    } +} + +impl<Px> GetPixel for PixelVec<Px> where Px: Clone + Sync { +    type Pixel = Px; +    fn get_pixel(&self, position: Vec2<usize>) -> Self::Pixel { +        self.get_pixel(position).clone() +    } +} + +use std::fmt::*; + +impl<T> Debug for PixelVec<T> { +    #[inline] fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { +        write!(formatter, "[{}; {}]", std::any::type_name::<T>(), self.pixels.len()) +    } +} + diff --git a/vendor/exr/src/image/read/any_channels.rs b/vendor/exr/src/image/read/any_channels.rs new file mode 100644 index 0000000..054a7c3 --- /dev/null +++ b/vendor/exr/src/image/read/any_channels.rs @@ -0,0 +1,128 @@ +//! How to read arbitrary channels. + +use crate::image::*; +use crate::meta::header::{Header}; +use crate::error::{Result, UnitResult}; +use crate::block::UncompressedBlock; +use crate::block::lines::{LineRef}; +use crate::math::Vec2; +use crate::meta::attribute::{Text, ChannelDescription}; +use crate::image::read::layers::{ReadChannels, ChannelsReader}; +use crate::block::chunk::TileCoordinates; + +/// A template that creates an [AnyChannelsReader] for each layer in the image. +/// This loads all channels for each layer. +/// The `ReadSamples` can, for example, be [ReadFlatSamples] or [ReadAllLevels<ReadFlatSamples>]. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadAnyChannels<ReadSamples> { + +    /// The sample reading specification +    pub read_samples: ReadSamples +} + +/// A template that creates a new [`SampleReader`] for each channel in each layer. +pub trait ReadSamples { + +    /// The type of the temporary samples reader +    type Reader: SamplesReader; + +    /// Create a single reader for a single channel of a layer +    fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader>; +} + +/// Processes pixel blocks from a file and accumulates them into a collection of arbitrary channels. +/// Loads all channels for each layer. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AnyChannelsReader<SamplesReader> { + +    /// Stores a separate sample reader per channel in the layer +    sample_channels_reader: SmallVec<[AnyChannelReader<SamplesReader>; 4]>, +} + +/// Processes pixel blocks from a file and accumulates them into a single arbitrary channel. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AnyChannelReader<SamplesReader> { + +    /// The custom reader that accumulates the pixel data for a single channel +    samples: SamplesReader, + +    /// Temporarily accumulated meta data. +    name: Text, + +    /// Temporarily accumulated meta data. +    sampling_rate: Vec2<usize>, + +    /// Temporarily accumulated meta data. +    quantize_linearly: bool, +} + +/// Processes pixel blocks from a file and accumulates them into a single pixel channel. +/// For example, stores thousands of "Red" pixel values for a single layer. +pub trait SamplesReader { + +    /// The type of resulting sample storage +    type Samples; + +    /// Specify whether a single block of pixels should be loaded from the file +    fn filter_block(&self, tile: TileCoordinates) -> bool; + +    /// Load a single pixel line, which has not been filtered, into the reader, accumulating the sample data +    fn read_line(&mut self, line: LineRef<'_>) -> UnitResult; + +    /// Deliver the final accumulated sample storage for the image +    fn into_samples(self) -> Self::Samples; +} + + +impl<'s, S: 's + ReadSamples> ReadChannels<'s> for ReadAnyChannels<S> { +    type Reader = AnyChannelsReader<S::Reader>; + +    fn create_channels_reader(&self, header: &Header) -> Result<Self::Reader> { +        let samples: Result<_> = header.channels.list.iter() +            .map(|channel: &ChannelDescription| Ok(AnyChannelReader { +                samples: self.read_samples.create_sample_reader(header, channel)?, +                name: channel.name.clone(), +                sampling_rate: channel.sampling, +                quantize_linearly: channel.quantize_linearly +            })) +            .collect(); + +        Ok(AnyChannelsReader { sample_channels_reader: samples? }) +    } +} + +impl<S: SamplesReader> ChannelsReader for AnyChannelsReader<S> { +    type Channels = AnyChannels<S::Samples>; + +    fn filter_block(&self, tile: TileCoordinates) -> bool { +        self.sample_channels_reader.iter().any(|channel| channel.samples.filter_block(tile)) +    } + +    fn read_block(&mut self, header: &Header, decompressed: UncompressedBlock) -> UnitResult { +        /*for (bytes, line) in LineIndex::lines_in_block(decompressed.index, header) { +            let channel = self.sample_channels_reader.get_mut(line.channel).unwrap(); +            channel.samples.read_line(LineSlice { location: line, value: &decompressed.data[bytes] })?; +        } + +        Ok(())*/ +        for line in decompressed.lines(&header.channels) { +            self.sample_channels_reader[line.location.channel].samples.read_line(line)?; +        } + +        Ok(()) +    } + +    fn into_channels(self) -> Self::Channels { +        AnyChannels { // not using `new()` as the channels are already sorted +            list: self.sample_channels_reader.into_iter() +                .map(|channel| AnyChannel { +                    sample_data: channel.samples.into_samples(), + +                    name: channel.name, +                    quantize_linearly: channel.quantize_linearly, +                    sampling: channel.sampling_rate +                }) +                .collect() +        } +    } +} diff --git a/vendor/exr/src/image/read/image.rs b/vendor/exr/src/image/read/image.rs new file mode 100644 index 0000000..fce2f52 --- /dev/null +++ b/vendor/exr/src/image/read/image.rs @@ -0,0 +1,209 @@ +//! The last wrapper of image readers, finally containing the [`from_file(path)`] method. +//! This completes the builder and reads a complete image. + +use crate::image::*; +use crate::meta::header::{Header, ImageAttributes}; +use crate::error::{Result, UnitResult}; +use crate::block::{UncompressedBlock, BlockIndex}; +use crate::block::chunk::TileCoordinates; +use std::path::Path; +use std::io::{Read, BufReader}; +use std::io::Seek; +use crate::meta::MetaData; +use crate::block::reader::ChunksReader; + +/// Specify whether to read the image in parallel, +/// whether to use pedantic error handling, +/// and a callback for the reading progress. +#[derive(Debug, Clone)] +pub struct ReadImage<OnProgress, ReadLayers> { +    on_progress: OnProgress, +    read_layers: ReadLayers, +    pedantic: bool, +    parallel: bool, +} + +impl<F, L> ReadImage<F, L> where F: FnMut(f64) +{ +    /// Uses relaxed error handling and parallel decompression. +    pub fn new(read_layers: L, on_progress: F) -> Self { +        Self { +            on_progress, read_layers, +            pedantic: false, parallel: true, +        } +    } + +    /// Specify that any missing or unusual information should result in an error. +    /// Otherwise, `exrs` will try to compute or ignore missing information. +    /// +    /// If pedantic is true, then an error will be returned as soon as anything is missing in the file, +    /// or two values in the image contradict each other. If pedantic is false, +    /// then only fatal errors will be thrown. By default, reading an image is not pedantic, +    /// which means that slightly invalid files might still be readable. +    /// For example, if some attribute is missing but can be recomputed, this flag decides whether an error is thrown. +    /// Or if the pedantic flag is true and there are still bytes left after the decompression algorithm finished, +    /// an error is thrown, because this should not happen and something might be wrong with the file. +    /// Or if your application is a target of attacks, or if you want to emulate the original C++ library, +    /// you might want to switch to pedantic reading. +    pub fn pedantic(self) -> Self { Self { pedantic: true, ..self } } + +    /// Specify that multiple pixel blocks should never be decompressed using multiple threads at once. +    /// This might be slower but uses less memory and less synchronization. +    pub fn non_parallel(self) -> Self { Self { parallel: false, ..self } } + +    /// Specify a function to be called regularly throughout the loading process. +    /// Replaces all previously specified progress functions in this reader. +    pub fn on_progress<OnProgress>(self, on_progress: OnProgress) -> ReadImage<OnProgress, L> +        where OnProgress: FnMut(f64) +    { +        ReadImage { +            on_progress, +            read_layers: self.read_layers, +            pedantic: self.pedantic, +            parallel: self.parallel +        } +    } + + +    /// Read the exr image from a file. +    /// Use [`ReadImage::read_from_unbuffered`] instead, if you do not have a file. +    #[inline] +    #[must_use] +    pub fn from_file<Layers>(self, path: impl AsRef<Path>) -> Result<Image<Layers>> +        where for<'s> L: ReadLayers<'s, Layers = Layers> +    { +        self.from_unbuffered(std::fs::File::open(path)?) +    } + +    /// Buffer the reader and then read the exr image from it. +    /// Use [`ReadImage::read_from_buffered`] instead, if your reader is an in-memory reader. +    /// Use [`ReadImage::read_from_file`] instead, if you have a file path. +    #[inline] +    #[must_use] +    pub fn from_unbuffered<Layers>(self, unbuffered: impl Read + Seek) -> Result<Image<Layers>> +        where for<'s> L: ReadLayers<'s, Layers = Layers> +    { +        self.from_buffered(BufReader::new(unbuffered)) +    } + +    /// Read the exr image from a buffered reader. +    /// Use [`ReadImage::read_from_file`] instead, if you have a file path. +    /// Use [`ReadImage::read_from_unbuffered`] instead, if this is not an in-memory reader. +    // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required +    #[must_use] +    pub fn from_buffered<Layers>(self, buffered: impl Read + Seek) -> Result<Image<Layers>> +        where for<'s> L: ReadLayers<'s, Layers = Layers> +    { +        let chunks = crate::block::read(buffered, self.pedantic)?; +        self.from_chunks(chunks) +    } + +    /// Read the exr image from an initialized chunks reader +    /// that has already extracted the meta data from the file. +    /// Use [`ReadImage::read_from_file`] instead, if you have a file path. +    /// Use [`ReadImage::read_from_buffered`] instead, if this is an in-memory reader. +    // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required +    #[must_use] +    pub fn from_chunks<Layers>(mut self, chunks_reader: crate::block::reader::Reader<impl Read + Seek>) -> Result<Image<Layers>> +        where for<'s> L: ReadLayers<'s, Layers = Layers> +    { +        let Self { pedantic, parallel, ref mut on_progress, ref mut read_layers } = self; + +        let layers_reader = read_layers.create_layers_reader(chunks_reader.headers())?; +        let mut image_collector = ImageWithAttributesReader::new(chunks_reader.headers(), layers_reader)?; + +        let block_reader = chunks_reader +            .filter_chunks(pedantic, |meta, tile, block| { +                image_collector.filter_block(meta, tile, block) +            })? +            .on_progress(on_progress); + +        // TODO propagate send requirement further upwards +        if parallel { +            block_reader.decompress_parallel(pedantic, |meta_data, block|{ +                image_collector.read_block(&meta_data.headers, block) +            })?; +        } +        else { +            block_reader.decompress_sequential(pedantic, |meta_data, block|{ +                image_collector.read_block(&meta_data.headers, block) +            })?; +        } + +        Ok(image_collector.into_image()) +    } +} + +/// Processes blocks from a file and collects them into a complete `Image`. +#[derive(Debug, Clone, PartialEq)] +pub struct ImageWithAttributesReader<L> { +    image_attributes: ImageAttributes, +    layers_reader: L, +} + +impl<L> ImageWithAttributesReader<L> where L: LayersReader { + +    /// A new image reader with image attributes. +    pub fn new(headers: &[Header], layers_reader: L) -> Result<Self> +    { +        Ok(ImageWithAttributesReader { +            image_attributes: headers.first().as_ref().expect("invalid headers").shared_attributes.clone(), +            layers_reader, +        }) +    } + +    /// Specify whether a single block of pixels should be loaded from the file +    fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool { +        self.layers_reader.filter_block(meta, tile, block) +    } + +    /// Load a single pixel block, which has not been filtered, into the reader, accumulating the image +    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult { +        self.layers_reader.read_block(headers, block) +    } + +    /// Deliver the complete accumulated image +    fn into_image(self) -> Image<L::Layers> { +        Image { +            attributes: self.image_attributes, +            layer_data: self.layers_reader.into_layers() +        } +    } +} + + +/// A template that creates a `LayerReader` for each layer in the file. +pub trait ReadLayers<'s> { + +    /// The type of the resulting Layers +    type Layers; + +    /// The type of the temporary layer reader +    type Reader: LayersReader<Layers = Self::Layers>; + +    /// Create a single reader for a single layer +    fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader>; + +    /// Specify that all attributes should be read from an image. +    /// Use `from_file(path)` on the return value of this method to actually decode an image. +    fn all_attributes(self) -> ReadImage<fn(f64), Self> where Self: Sized { +        ReadImage::new(self, ignore_progress) +    } +} + +/// Processes pixel blocks from a file and accumulates them into a single image layer. +pub trait LayersReader { + +    /// The type of resulting layers +    type Layers; + +    /// Specify whether a single block of pixels should be loaded from the file +    fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool; + +    /// Load a single pixel block, which has not been filtered, into the reader, accumulating the layer +    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult; + +    /// Deliver the final accumulated layers for the image +    fn into_layers(self) -> Self::Layers; +} + diff --git a/vendor/exr/src/image/read/layers.rs b/vendor/exr/src/image/read/layers.rs new file mode 100644 index 0000000..75159c2 --- /dev/null +++ b/vendor/exr/src/image/read/layers.rs @@ -0,0 +1,204 @@ +//! How to read either a single or a list of layers. + +use crate::image::*; +use crate::meta::header::{Header, LayerAttributes}; +use crate::error::{Result, UnitResult, Error}; +use crate::block::{UncompressedBlock, BlockIndex}; +use crate::math::Vec2; +use crate::image::read::image::{ReadLayers, LayersReader}; +use crate::block::chunk::TileCoordinates; +use crate::meta::MetaData; + +/// Specify to read all channels, aborting if any one is invalid. +/// [`ReadRgbaChannels`] or [`ReadAnyChannels<ReadFlatSamples>`]. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadAllLayers<ReadChannels> { + +    /// The channel reading specification +    pub read_channels: ReadChannels, +} + +/// Specify to read only the first layer which meets the previously specified requirements +// FIXME do not throw error on deep data but just skip it! +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadFirstValidLayer<ReadChannels> { + +    /// The channel reading specification +    pub read_channels: ReadChannels, +} + +/// A template that creates a [`ChannelsReader`] once for all channels per layer. +pub trait ReadChannels<'s> { + +    /// The type of the temporary channels reader +    type Reader: ChannelsReader; + +    /// Create a single reader for all channels of a specific layer +    fn create_channels_reader(&'s self, header: &Header) -> Result<Self::Reader>; + + +    /// Read only the first layer which meets the previously specified requirements +    /// For example, skips layers with deep data, if specified earlier. +    /// Aborts if the image contains no layers. +    // TODO test if this filters non-deep layers while ignoring deep data layers! +    fn first_valid_layer(self) -> ReadFirstValidLayer<Self> where Self:Sized { ReadFirstValidLayer { read_channels: self } } + +// FIXME do not throw error on deep data but just skip it! + + +    /// Reads all layers, including an empty list. Aborts if any of the layers are invalid, +    /// even if only one of the layers contains unexpected data. +    fn all_layers(self) -> ReadAllLayers<Self> where Self:Sized { ReadAllLayers { read_channels: self } } + +    // TODO pub fn all_valid_layers(self) -> ReadAllValidLayers<Self> { ReadAllValidLayers { read_channels: self } } +} + + +/// Processes pixel blocks from a file and accumulates them into a list of layers. +/// For example, `ChannelsReader` can be +/// [`SpecificChannelsReader`] or [`AnyChannelsReader<FlatSamplesReader>`]. +#[derive(Debug, Clone, PartialEq)] +pub struct AllLayersReader<ChannelsReader> { +    layer_readers: SmallVec<[LayerReader<ChannelsReader>; 2]>, // TODO unpack struct? +} + +/// Processes pixel blocks from a file and accumulates them into a single layers, using only the first. +/// For example, `ChannelsReader` can be +/// `SpecificChannelsReader` or `AnyChannelsReader<FlatSamplesReader>`. +#[derive(Debug, Clone, PartialEq)] +pub struct FirstValidLayerReader<ChannelsReader> { +    layer_reader: LayerReader<ChannelsReader>, +    layer_index: usize, +} + +/// Processes pixel blocks from a file and accumulates them into a single layers. +/// For example, `ChannelsReader` can be +/// `SpecificChannelsReader` or `AnyChannelsReader<FlatSamplesReader>`. +#[derive(Debug, Clone, PartialEq)] +pub struct LayerReader<ChannelsReader> { +    channels_reader: ChannelsReader, +    attributes: LayerAttributes, +    size: Vec2<usize>, +    encoding: Encoding +} + +/// Processes pixel blocks from a file and accumulates them into multiple channels per layer. +pub trait ChannelsReader { + +    /// The type of the resulting channel collection +    type Channels; + +    /// Specify whether a single block of pixels should be loaded from the file +    fn filter_block(&self, tile: TileCoordinates) -> bool; + +    /// Load a single pixel block, which has not been filtered, into the reader, accumulating the channel data +    fn read_block(&mut self, header: &Header, block: UncompressedBlock) -> UnitResult; + +    /// Deliver the final accumulated channel collection for the image +    fn into_channels(self) -> Self::Channels; +} + + +impl<C> LayerReader<C> { +    fn new(header: &Header, channels_reader: C) -> Result<Self> { +        Ok(LayerReader { +            channels_reader, +            attributes: header.own_attributes.clone(), +            size: header.layer_size, +            encoding: Encoding { +                compression: header.compression, +                line_order: header.line_order, +                blocks: match header.blocks { +                    crate::meta::BlockDescription::ScanLines => Blocks::ScanLines, +                    crate::meta::BlockDescription::Tiles(TileDescription { tile_size, .. }) => Blocks::Tiles(tile_size) +                }, +            }, +        }) +    } +} + +impl<'s, C> ReadLayers<'s> for ReadAllLayers<C> where C: ReadChannels<'s> { +    type Layers = Layers<<C::Reader as ChannelsReader>::Channels>; +    type Reader = AllLayersReader<C::Reader>; + +    fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader> { +        let readers: Result<_> = headers.iter() +            .map(|header| LayerReader::new(header, self.read_channels.create_channels_reader(header)?)) +            .collect(); + +        Ok(AllLayersReader { +            layer_readers: readers? +        }) +    } +} + +impl<C> LayersReader for AllLayersReader<C> where C: ChannelsReader { +    type Layers = Layers<C::Channels>; + +    fn filter_block(&self, _: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool { +        let layer = self.layer_readers.get(block.layer).expect("invalid layer index argument"); +        layer.channels_reader.filter_block(tile) +    } + +    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult { +        self.layer_readers +            .get_mut(block.index.layer).expect("invalid layer index argument") +            .channels_reader.read_block(headers.get(block.index.layer).expect("invalid header index in block"), block) +    } + +    fn into_layers(self) -> Self::Layers { +        self.layer_readers +            .into_iter() +            .map(|layer| Layer { +                channel_data: layer.channels_reader.into_channels(), +                attributes: layer.attributes, +                size: layer.size, +                encoding: layer.encoding +            }) +            .collect() +    } +} + + +impl<'s, C> ReadLayers<'s> for ReadFirstValidLayer<C> where C: ReadChannels<'s> { +    type Layers = Layer<<C::Reader as ChannelsReader>::Channels>; +    type Reader = FirstValidLayerReader<C::Reader>; + +    fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader> { +        headers.iter().enumerate() +            .flat_map(|(index, header)| +                self.read_channels.create_channels_reader(header) +                    .and_then(|reader| Ok(FirstValidLayerReader { +                        layer_reader: LayerReader::new(header, reader)?, +                        layer_index: index +                    })) +                    .ok() +            ) +            .next() +            .ok_or(Error::invalid("no layer in the image matched your specified requirements")) +    } +} + + +impl<C> LayersReader for FirstValidLayerReader<C> where C: ChannelsReader { +    type Layers = Layer<C::Channels>; + +    fn filter_block(&self, _: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool { +        block.layer == self.layer_index && self.layer_reader.channels_reader.filter_block(tile) +    } + +    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult { +        debug_assert_eq!(block.index.layer, self.layer_index, "block should have been filtered out"); +        self.layer_reader.channels_reader.read_block(&headers[self.layer_index], block) +    } + +    fn into_layers(self) -> Self::Layers { +        Layer { +            channel_data: self.layer_reader.channels_reader.into_channels(), +            attributes: self.layer_reader.attributes, +            size: self.layer_reader.size, +            encoding: self.layer_reader.encoding +        } +    } +} + diff --git a/vendor/exr/src/image/read/levels.rs b/vendor/exr/src/image/read/levels.rs new file mode 100644 index 0000000..5705903 --- /dev/null +++ b/vendor/exr/src/image/read/levels.rs @@ -0,0 +1,219 @@ +//! How to read a set of resolution levels. + +use crate::meta::*; +use crate::image::*; +use crate::error::*; +use crate::meta::attribute::*; +use crate::image::read::any_channels::*; +use crate::block::chunk::TileCoordinates; +use crate::image::read::specific_channels::*; +use crate::image::recursive::*; +use crate::math::Vec2; +use crate::block::lines::LineRef; +use crate::block::samples::*; +use crate::meta::header::{Header}; + + +// Note: In the resulting image, the `FlatSamples` are placed +// directly inside the channels, without `LargestLevel<>` indirection +/// Specify to read only the highest resolution level, skipping all smaller variations. +/// The sample storage can be [`ReadFlatSamples`]. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadLargestLevel<DeepOrFlatSamples> { + +    /// The sample reading specification +    pub read_samples: DeepOrFlatSamples +} + + +// FIXME rgba levels??? + +// Read the largest level, directly, without intermediate structs +impl<DeepOrFlatSamples> ReadLargestLevel<DeepOrFlatSamples> { + +    /// Read all arbitrary channels in each layer. +    pub fn all_channels(self) -> ReadAnyChannels<DeepOrFlatSamples> { ReadAnyChannels { read_samples: self.read_samples } } // Instead of Self, the `FlatSamples` are used directly + +    /// Read only layers that contain rgba channels. Skips any other channels in the layer. +    /// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image. +    /// +    /// Using two closures, define how to store the pixels. +    /// The first closure creates an image, and the second closure inserts a single pixel. +    /// The type of the pixel can be defined by the second closure; +    /// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`. +    /// +    /// Throws an error for images with deep data or subsampling. +    /// Use `specific_channels` or `all_channels` if you want to read something other than rgba. +    pub fn rgba_channels<R,G,B,A, Create, Set, Pixels>( +        self, create_pixels: Create, set_pixel: Set +    ) -> CollectPixels< +        ReadOptionalChannel<ReadRequiredChannel<ReadRequiredChannel<ReadRequiredChannel<NoneMore, R>, G>, B>, A>, +        (R, G, B, A), Pixels, Create, Set +    > +        where +            R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, +            Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels, +            Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)), +    { +        self.specific_channels() +            .required("R").required("G").required("B") +            .optional("A", A::from_f32(1.0)) +            .collect_pixels(create_pixels, set_pixel) +    } + +    /// Read only layers that contain rgb channels. Skips any other channels in the layer. +    /// +    /// Using two closures, define how to store the pixels. +    /// The first closure creates an image, and the second closure inserts a single pixel. +    /// The type of the pixel can be defined by the second closure; +    /// it must be a tuple containing three values, each being either `f16`, `f32`, `u32` or `Sample`. +    /// +    /// Throws an error for images with deep data or subsampling. +    /// Use `specific_channels` or `all_channels` if you want to read something other than rgb. +    pub fn rgb_channels<R,G,B, Create, Set, Pixels>( +        self, create_pixels: Create, set_pixel: Set +    ) -> CollectPixels< +        ReadRequiredChannel<ReadRequiredChannel<ReadRequiredChannel<NoneMore, R>, G>, B>, +        (R, G, B), Pixels, Create, Set +    > +        where +            R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, +            Create: Fn(Vec2<usize>, &RgbChannels) -> Pixels, +            Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B)), +    { +        self.specific_channels() +            .required("R").required("G").required("B") +            .collect_pixels(create_pixels, set_pixel) +    } + +    /// Read only layers that contain the specified channels, skipping any other channels in the layer. +    /// Further specify which channels should be included by calling `.required("ChannelName")` +    /// or `.optional("ChannelName", default_value)` on the result of this function. +    /// Call `collect_pixels` afterwards to define the pixel container for your set of channels. +    /// +    /// Throws an error for images with deep data or subsampling. +    pub fn specific_channels(self) -> ReadZeroChannels { +        ReadZeroChannels { } +    } +} + +/// Specify to read all contained resolution levels from the image, if any. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadAllLevels<DeepOrFlatSamples> { + +    /// The sample reading specification +    pub read_samples: DeepOrFlatSamples +} + +impl<ReadDeepOrFlatSamples> ReadAllLevels<ReadDeepOrFlatSamples> { + +    /// Read all arbitrary channels in each layer. +    pub fn all_channels(self) -> ReadAnyChannels<Self> { ReadAnyChannels { read_samples: self } } + +    // TODO specific channels for multiple resolution levels + +} + +/*pub struct ReadLevels<S> { +    read_samples: S, +}*/ + +/// Processes pixel blocks from a file and accumulates them into multiple levels per channel. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AllLevelsReader<SamplesReader> { +    levels: Levels<SamplesReader>, +} + +/// A template that creates a [`SamplesReader`] once for each resolution level. +pub trait ReadSamplesLevel { + +    /// The type of the temporary level reader +    type Reader: SamplesReader; + +    /// Create a single reader for a single resolution level +    fn create_samples_level_reader(&self, header: &Header, channel: &ChannelDescription, level: Vec2<usize>, resolution: Vec2<usize>) -> Result<Self::Reader>; +} + + +impl<S: ReadSamplesLevel> ReadSamples for ReadAllLevels<S> { +    type Reader = AllLevelsReader<S::Reader>; + +    fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader> { +        let data_size = header.layer_size / channel.sampling; + +        let levels = { +            if let crate::meta::BlockDescription::Tiles(tiles) = &header.blocks { +                match tiles.level_mode { +                    LevelMode::Singular => Levels::Singular(self.read_samples.create_samples_level_reader(header, channel, Vec2(0,0), header.layer_size)?), + +                    LevelMode::MipMap => Levels::Mip { +                        rounding_mode: tiles.rounding_mode, +                        level_data: { +                            let round = tiles.rounding_mode; +                            let maps: Result<LevelMaps<S::Reader>> = mip_map_levels(round, data_size) +                                .map(|(index, level_size)| self.read_samples.create_samples_level_reader(header, channel, Vec2(index, index), level_size)) +                                .collect(); + +                            maps? +                        }, +                    }, + +                    // TODO put this into Levels::new(..) ? +                    LevelMode::RipMap => Levels::Rip { +                        rounding_mode: tiles.rounding_mode, +                        level_data: { +                            let round = tiles.rounding_mode; +                            let level_count_x = compute_level_count(round, data_size.width()); +                            let level_count_y = compute_level_count(round, data_size.height()); +                            let maps: Result<LevelMaps<S::Reader>> = rip_map_levels(round, data_size) +                                .map(|(index, level_size)| self.read_samples.create_samples_level_reader(header, channel, index, level_size)) +                                .collect(); + +                            RipMaps { +                                map_data: maps?, +                                level_count: Vec2(level_count_x, level_count_y) +                            } +                        }, +                    }, +                } +            } + +            // scan line blocks never have mip maps +            else { +                Levels::Singular(self.read_samples.create_samples_level_reader(header, channel, Vec2(0, 0), data_size)?) +            } +        }; + +        Ok(AllLevelsReader { levels }) +    } +} + + +impl<S: SamplesReader> SamplesReader for AllLevelsReader<S> { +    type Samples = Levels<S::Samples>; + +    fn filter_block(&self, _: TileCoordinates) -> bool { +        true +    } + +    fn read_line(&mut self, line: LineRef<'_>) -> UnitResult { +        self.levels.get_level_mut(line.location.level)?.read_line(line) +    } + +    fn into_samples(self) -> Self::Samples { +        match self.levels { +            Levels::Singular(level) => Levels::Singular(level.into_samples()), +            Levels::Mip { rounding_mode, level_data } => Levels::Mip { +                rounding_mode, level_data: level_data.into_iter().map(|s| s.into_samples()).collect(), +            }, + +            Levels::Rip { rounding_mode, level_data } => Levels::Rip { +                rounding_mode, +                level_data: RipMaps { +                    level_count: level_data.level_count, +                    map_data: level_data.map_data.into_iter().map(|s| s.into_samples()).collect(), +                } +            }, +        } +    } +} diff --git a/vendor/exr/src/image/read/mod.rs b/vendor/exr/src/image/read/mod.rs new file mode 100644 index 0000000..c03fc90 --- /dev/null +++ b/vendor/exr/src/image/read/mod.rs @@ -0,0 +1,207 @@ + +//! Read an exr image. +//! +//! For great flexibility and customization, use the `read()` function. +//! The return value of the `read()` function must be further customized before reading a file. + +//! +//! For very simple applications, you can alternatively use one of these functions: +//! +//! 1. `read_first_rgba_layer_from_file(path, your_constructor, your_pixel_setter)`: +//!     You specify how to store the pixels. +//!     The first layer containing rgba channels is then loaded from the file. +//!     Fails if no rgba layer can be found. +//! +//! 1. `read_all_rgba_layers_from_file(path, your_constructor, your_pixel_setter)`: +//!     You specify how to store the pixels. +//!     All layers containing rgba channels are then loaded from the file. +//!     Fails if any layer in the image does not contain rgba channels. +//! +//! 1. `read_first_flat_layer_from_file(path)`: +//!     The first layer containing non-deep data with arbitrary channels is loaded from the file. +//!     Fails if no non-deep layer can be found. +//! +//! 1. `read_all_flat_layers_from_file(path)`: +//!     All layers containing non-deep data with arbitrary channels are loaded from the file. +//!     Fails if any layer in the image contains deep data. +//! +//! 1. `read_all_data_from_file(path)`: +//!     All layers with arbitrary channels and all resolution levels are extracted from the file. +//! +//!     Note: Currently does not support deep data, and currently fails +//!     if any layer in the image contains deep data. +//! + +// The following three stages are internally used to read an image. +// 1. `ReadImage` - The specification. Contains everything the user wants to tell us about loading an image. +//    The data in this structure will be instantiated and might be borrowed. +// 2. `ImageReader` - The temporary reader. Based on the specification of the blueprint, +//    a reader is instantiated, once for each layer. +//    This data structure accumulates the image data from the file. +//    It also owns temporary data and references the blueprint. +// 3. `Image` - The clean image. The accumulated data from the Reader +//    is converted to the clean image structure, without temporary data. + +pub mod image; +pub mod layers; +pub mod any_channels; +pub mod levels; +pub mod samples; +pub mod specific_channels; + +use crate::error::{Result}; +use crate::image::read::samples::{ReadFlatSamples}; +use std::path::Path; +use crate::image::{AnyImage, AnyChannels, FlatSamples, Image, Layer, FlatImage, PixelLayersImage, RgbaChannels}; +use crate::image::read::image::ReadLayers; +use crate::image::read::layers::ReadChannels; +use crate::math::Vec2; +use crate::prelude::{PixelImage}; +use crate::block::samples::FromNativeSample; + + +/// All resolution levels, all channels, all layers. +/// Does not support deep data yet. Uses parallel decompression and relaxed error handling. +/// Inspect the source code of this function if you need customization. +pub fn read_all_data_from_file(path: impl AsRef<Path>) -> Result<AnyImage> { +    read() +        .no_deep_data() // TODO deep data +        .all_resolution_levels() +        .all_channels() +        .all_layers() +        .all_attributes() +        .from_file(path) +} + +// FIXME do not throw error on deep data but just skip it! +/// No deep data, no resolution levels, all channels, all layers. +/// Uses parallel decompression and relaxed error handling. +/// Inspect the source code of this function if you need customization. +pub fn read_all_flat_layers_from_file(path: impl AsRef<Path>) -> Result<FlatImage> { +    read() +        .no_deep_data() +        .largest_resolution_level() +        .all_channels() +        .all_layers() +        .all_attributes() +        .from_file(path) +} + +/// No deep data, no resolution levels, all channels, first layer. +/// Uses parallel decompression and relaxed error handling. +/// Inspect the source code of this function if you need customization. +pub fn read_first_flat_layer_from_file(path: impl AsRef<Path>) -> Result<Image<Layer<AnyChannels<FlatSamples>>>> { +    read() +        .no_deep_data() +        .largest_resolution_level() +        .all_channels() +        .first_valid_layer() +        .all_attributes() +        .from_file(path) +} + +/// No deep data, no resolution levels, rgba channels, all layers. +/// If a single layer does not contain rgba data, this method returns an error. +/// Uses parallel decompression and relaxed error handling. +/// `Create` and `Set` can be closures, see the examples for more information. +/// Inspect the source code of this function if you need customization. +/// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image. +/// +/// Using two closures, define how to store the pixels. +/// The first closure creates an image, and the second closure inserts a single pixel. +/// The type of the pixel can be defined by the second closure; +/// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`. +// FIXME Set and Create should not need to be static +pub fn read_all_rgba_layers_from_file<R,G,B,A, Set:'static, Create:'static, Pixels: 'static>( +    path: impl AsRef<Path>, create: Create, set_pixel: Set +) +    -> Result<PixelLayersImage<Pixels, RgbaChannels>> +    where +        R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, +        Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels<Pixels=Pixels>, +        Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)), +{ +    read() +        .no_deep_data() +        .largest_resolution_level() +        .rgba_channels(create, set_pixel) +        .all_layers() +        .all_attributes() +        .from_file(path) +} + +/// No deep data, no resolution levels, rgba channels, choosing the first layer with rgba channels. +/// Uses parallel decompression and relaxed error handling. +/// `Create` and `Set` can be closures, see the examples for more information. +/// Inspect the source code of this function if you need customization. +/// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image. +/// +/// Using two closures, define how to store the pixels. +/// The first closure creates an image, and the second closure inserts a single pixel. +/// The type of the pixel can be defined by the second closure; +/// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`. +// FIXME Set and Create should not need to be static +pub fn read_first_rgba_layer_from_file<R,G,B,A, Set:'static, Create:'static, Pixels: 'static>( +    path: impl AsRef<Path>, create: Create, set_pixel: Set +) +    -> Result<PixelImage<Pixels, RgbaChannels>> +    where +        R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, +        Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels<Pixels=Pixels>, +        Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)), +{ +    read() +        .no_deep_data() +        .largest_resolution_level() +        .rgba_channels(create, set_pixel) +        .first_valid_layer() +        .all_attributes() +        .from_file(path) +} + + +/// Utilizes the builder pattern to configure an image reader. This is the initial struct. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ReadBuilder; + +/// Create a reader which can be used to load an exr image. +/// Allows you to exactly specify how to load the image, for example: +/// +/// ```no_run +///     use exr::prelude::*; +/// +///     // the type of the this image depends on the chosen options +///     let image = read() +///         .no_deep_data() // (currently required) +///         .largest_resolution_level() // or `all_resolution_levels()` +///         .all_channels() // or `rgba_channels(constructor, setter)` +///         .all_layers() // or `first_valid_layer()` +///         .all_attributes() // (currently required) +///         .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) // optional +///         .from_file("image.exr").unwrap(); // or `from_buffered(my_byte_slice)` +/// ``` +/// +/// You can alternatively use one of the following simpler functions: +/// 1. `read_first_flat_layer_from_file` +/// 1. `read_all_rgba_layers_from_file` +/// 1. `read_all_flat_layers_from_file` +/// 1. `read_all_data_from_file` +/// +// TODO not panic but skip deep layers! +pub fn read() -> ReadBuilder { ReadBuilder } + +impl ReadBuilder { + +    /// Specify to handle only one sample per channel, disabling "deep data". +    // TODO not panic but skip deep layers! +    pub fn no_deep_data(self) -> ReadFlatSamples { ReadFlatSamples } + +    // pub fn any_resolution_levels() -> ReadBuilder<> {} + +    // TODO +    // e. g. `let sum = reader.any_channels_with(|sample, sum| sum += sample)` +    // e. g. `let floats = reader.any_channels_with(|sample, f32_samples| f32_samples[index] = sample as f32)` +    // pub fn no_deep_data_with <S> (self, storage: S) -> FlatSamplesWith<S> {  } + +    // pub fn flat_and_deep_data(self) -> ReadAnySamples { ReadAnySamples } +} diff --git a/vendor/exr/src/image/read/samples.rs b/vendor/exr/src/image/read/samples.rs new file mode 100644 index 0000000..e03c3cc --- /dev/null +++ b/vendor/exr/src/image/read/samples.rs @@ -0,0 +1,122 @@ +//! How to read samples (a grid of `f32`, `f16` or `u32` values). + +use crate::image::*; +use crate::meta::header::{Header}; +use crate::error::{Result, UnitResult}; +use crate::block::lines::LineRef; +use crate::math::Vec2; +use crate::meta::attribute::{ChannelDescription, SampleType}; +use crate::image::read::any_channels::{SamplesReader, ReadSamples}; +use crate::image::read::levels::{ReadSamplesLevel, ReadAllLevels, ReadLargestLevel}; +use crate::block::chunk::TileCoordinates; +// use crate::image::read::layers::ReadChannels; + +/// Specify to read only flat samples and no "deep data" +// FIXME do not throw error on deep data but just skip it! +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ReadFlatSamples; +// pub struct ReadAnySamples; + +impl ReadFlatSamples { + +    // TODO +    // e. g. `let sum = reader.any_channels_with(|sample, sum| sum += sample)` +    // pub fn any_channels_with <S> (self, storage: S) -> {  } + +    /// Specify to read only the highest resolution level, skipping all smaller variations. +    pub fn largest_resolution_level(self) -> ReadLargestLevel<Self> { ReadLargestLevel { read_samples: self } } + +    /// Specify to read all contained resolution levels from the image, if any. +    pub fn all_resolution_levels(self) -> ReadAllLevels<Self> { ReadAllLevels { read_samples: self } } + +    // TODO pub fn specific_resolution_level<F: Fn(&[Vec2<usize>])->usize >(self, select_level: F) -> ReadLevelBy<Self> { ReadAllLevels { read_samples: self } } +} + + +/*pub struct AnySamplesReader { TODO +    resolution: Vec2<usize>, +    samples: DeepAndFlatSamples +}*/ + +/// Processes pixel blocks from a file and accumulates them into a grid of samples, for example "Red" or "Alpha". +#[derive(Debug, Clone, PartialEq)] +pub struct FlatSamplesReader { +    level: Vec2<usize>, +    resolution: Vec2<usize>, +    samples: FlatSamples +} + + +// only used when samples is directly inside a channel, without levels +impl ReadSamples for ReadFlatSamples { +    type Reader = FlatSamplesReader; + +    fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader> { +        self.create_samples_level_reader(header, channel, Vec2(0, 0), header.layer_size) +    } +} + +impl ReadSamplesLevel for ReadFlatSamples { +    type Reader = FlatSamplesReader; + +    fn create_samples_level_reader(&self, _header: &Header, channel: &ChannelDescription, level: Vec2<usize>, resolution: Vec2<usize>) -> Result<Self::Reader> { +        Ok(FlatSamplesReader { +            level, resolution, // TODO sampling +            samples: match channel.sample_type { +                SampleType::F16 => FlatSamples::F16(vec![f16::ZERO; resolution.area()]), +                SampleType::F32 => FlatSamples::F32(vec![0.0; resolution.area()]), +                SampleType::U32 => FlatSamples::U32(vec![0; resolution.area()]), +            } +        }) +    } +} + + +impl SamplesReader for FlatSamplesReader { +    type Samples = FlatSamples; + +    fn filter_block(&self, tile: TileCoordinates) -> bool { +        tile.level_index == self.level +    } + +    fn read_line(&mut self, line: LineRef<'_>) -> UnitResult { +        let index = line.location; +        let resolution = self.resolution; + +        // the index is generated by ourselves and must always be correct +        debug_assert_eq!(index.level, self.level, "line should have been filtered"); +        debug_assert!(index.position.x() + index.sample_count <= resolution.width(), "line index calculation bug"); +        debug_assert!(index.position.y() < resolution.height(), "line index calculation bug"); +        debug_assert_ne!(resolution.0, 0, "sample size bug"); + +        let start_index = index.position.y() * resolution.width() + index.position.x(); +        let end_index = start_index + index.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 &mut self.samples { +            FlatSamples::F16(samples) => +                line.read_samples_into_slice(&mut samples[start_index .. end_index]) +                    .expect("writing line bytes failed"), + +            FlatSamples::F32(samples) => +                line.read_samples_into_slice(&mut samples[start_index .. end_index]) +                    .expect("writing line bytes failed"), + +            FlatSamples::U32(samples) => +                line.read_samples_into_slice(&mut samples[start_index .. end_index]) +                    .expect("writing line bytes failed"), +        } + +        Ok(()) +    } + +    fn into_samples(self) -> FlatSamples { +        self.samples +    } +} + diff --git a/vendor/exr/src/image/read/specific_channels.rs b/vendor/exr/src/image/read/specific_channels.rs new file mode 100644 index 0000000..375691c --- /dev/null +++ b/vendor/exr/src/image/read/specific_channels.rs @@ -0,0 +1,463 @@ +//! How to read arbitrary but specific selection of arbitrary channels. +//! This is not a zero-cost abstraction. + +use crate::image::recursive::*; +use crate::block::samples::*; +use crate::image::*; +use crate::math::*; +use crate::meta::header::*; +use crate::error::*; +use crate::block::UncompressedBlock; +use crate::image::read::layers::{ChannelsReader, ReadChannels}; +use crate::block::chunk::TileCoordinates; + +use std::marker::PhantomData; +use crate::io::Read; + + +/// Can be attached one more channel reader. +/// Call `required` or `optional` on this object to declare another channel to be read from the file. +/// Call `collect_pixels` at last to define how the previously declared pixels should be stored. +pub trait ReadSpecificChannel: Sized + CheckDuplicates { + +    /// A separate internal reader for the pixels. Will be of type `Recursive<_, SampleReader<_>>`, +    /// depending on the pixels of the specific channel combination. +    type RecursivePixelReader: RecursivePixelReader; + +    /// Create a separate internal reader for the pixels of the specific channel combination. +    fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader>; + +    /// Plan to read an additional channel from the image, with the specified name. +    /// If the channel cannot be found in the image when the image is read, the image will not be loaded. +    /// The generic parameter can usually be inferred from the closure in `collect_pixels`. +    fn required<Sample>(self, channel_name: impl Into<Text>) -> ReadRequiredChannel<Self, Sample> { +        let channel_name = channel_name.into(); +        assert!(self.already_contains(&channel_name).not(), "a channel with the name `{}` is already defined", channel_name); +        ReadRequiredChannel { channel_name, previous_channels: self, px: Default::default() } +    } + +    /// Plan to read an additional channel from the image, with the specified name. +    /// If the file does not contain this channel, the specified default sample will be returned instead. +    /// You can check whether the channel has been loaded by +    /// checking the presence of the optional channel description before instantiating your own image. +    /// The generic parameter can usually be inferred from the closure in `collect_pixels`. +    fn optional<Sample>(self, channel_name: impl Into<Text>, default_sample: Sample) +        -> ReadOptionalChannel<Self, Sample> +    { +        let channel_name = channel_name.into(); +        assert!(self.already_contains(&channel_name).not(), "a channel with the name `{}` is already defined", channel_name); +        ReadOptionalChannel { channel_name, previous_channels: self, default_sample } +    } + +    /// Using two closures, define how to store the pixels. +    /// The first closure creates an image, and the second closure inserts a single pixel. +    /// The type of the pixel can be defined by the second closure; +    /// it must be a tuple containing `f16`, `f32`, `u32` or `Sample` values. +    /// See the examples for more information. +    fn collect_pixels<Pixel, PixelStorage, CreatePixels, SetPixel>( +        self, create_pixels: CreatePixels, set_pixel: SetPixel +    ) -> CollectPixels<Self, Pixel, PixelStorage, CreatePixels, SetPixel> +        where +            <Self::RecursivePixelReader as RecursivePixelReader>::RecursivePixel: IntoTuple<Pixel>, +            <Self::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions: IntoNonRecursive, +            CreatePixels: Fn( +                Vec2<usize>, +                &<<Self::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive +            ) -> PixelStorage, +            SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel), +    { +        CollectPixels { read_channels: self, set_pixel, create_pixels, px: Default::default() } +    } +} + +/// A reader containing sub-readers for reading the pixel content of an image. +pub trait RecursivePixelReader { + +    /// The channel descriptions from the image. +    /// Will be converted to a tuple before being stored in `SpecificChannels<_, ChannelDescriptions>`. +    type RecursiveChannelDescriptions; + +    /// Returns the channel descriptions based on the channels in the file. +    fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions; + +    /// The pixel type. Will be converted to a tuple at the end of the process. +    type RecursivePixel: Copy + Default + 'static; + +    /// Read the line of pixels. +    fn read_pixels<'s, FullPixel>( +        &self, bytes: &'s[u8], pixels: &mut [FullPixel], +        get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel +    ); +} + +// does not use the generic `Recursive` struct to reduce the number of angle brackets in the public api +/// Used to read another specific channel from an image. +/// Contains the previous `ReadChannels` objects. +#[derive(Clone, Debug)] +pub struct ReadOptionalChannel<ReadChannels, Sample> { +    previous_channels: ReadChannels, +    channel_name: Text, +    default_sample: Sample, +} + +// does not use the generic `Recursive` struct to reduce the number of angle brackets in the public api +/// Used to read another specific channel from an image. +/// Contains the previous `ReadChannels` objects. +#[derive(Clone, Debug)] +pub struct ReadRequiredChannel<ReadChannels, Sample> { +    previous_channels: ReadChannels, +    channel_name: Text, +    px: PhantomData<Sample>, +} + +/// Specifies how to collect all the specified channels into a number of individual pixels. +#[derive(Copy, Clone, Debug)] +pub struct CollectPixels<ReadChannels, Pixel, PixelStorage, CreatePixels, SetPixel> { +    read_channels: ReadChannels, +    create_pixels: CreatePixels, +    set_pixel: SetPixel, +    px: PhantomData<(Pixel, PixelStorage)>, +} + +impl<Inner: CheckDuplicates, Sample> CheckDuplicates for ReadRequiredChannel<Inner, Sample> { +    fn already_contains(&self, name: &Text) -> bool { +        &self.channel_name == name || self.previous_channels.already_contains(name) +    } +} + +impl<Inner: CheckDuplicates, Sample> CheckDuplicates for ReadOptionalChannel<Inner, Sample> { +    fn already_contains(&self, name: &Text) -> bool { +        &self.channel_name == name || self.previous_channels.already_contains(name) +    } +} + +impl<'s, InnerChannels, Pixel, PixelStorage, CreatePixels, SetPixel: 's> +ReadChannels<'s> for CollectPixels<InnerChannels, Pixel, PixelStorage, CreatePixels, SetPixel> +    where +        InnerChannels: ReadSpecificChannel, +        <InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursivePixel: IntoTuple<Pixel>, +        <InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions: IntoNonRecursive, +        CreatePixels: Fn(Vec2<usize>, &<<InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive) -> PixelStorage, +        SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel), +{ +    type Reader = SpecificChannelsReader< +        PixelStorage, &'s SetPixel, +        InnerChannels::RecursivePixelReader, +        Pixel, +    >; + +    fn create_channels_reader(&'s self, header: &Header) -> Result<Self::Reader> { +        if header.deep { return Err(Error::invalid("`SpecificChannels` does not support deep data yet")) } + +        let pixel_reader = self.read_channels.create_recursive_reader(&header.channels)?; +        let channel_descriptions = pixel_reader.get_descriptions().into_non_recursive();// TODO not call this twice + +        let create = &self.create_pixels; +        let pixel_storage = create(header.layer_size, &channel_descriptions); + +        Ok(SpecificChannelsReader { +            set_pixel: &self.set_pixel, +            pixel_storage, +            pixel_reader, +            px: Default::default() +        }) +    } +} + +/// The reader that holds the temporary data that is required to read some specified channels. +#[derive(Copy, Clone, Debug)] +pub struct SpecificChannelsReader<PixelStorage, SetPixel, PixelReader, Pixel> { +    set_pixel: SetPixel, +    pixel_storage: PixelStorage, +    pixel_reader: PixelReader, +    px: PhantomData<Pixel> +} + +impl<PixelStorage, SetPixel, PxReader, Pixel> +ChannelsReader for SpecificChannelsReader<PixelStorage, SetPixel, PxReader, Pixel> +    where PxReader: RecursivePixelReader, +          PxReader::RecursivePixel: IntoTuple<Pixel>, +          PxReader::RecursiveChannelDescriptions: IntoNonRecursive, +          SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel), +{ +    type Channels = SpecificChannels<PixelStorage, <PxReader::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive>; + +    fn filter_block(&self, tile: TileCoordinates) -> bool { tile.is_largest_resolution_level() } // TODO all levels + +    fn read_block(&mut self, header: &Header, block: UncompressedBlock) -> UnitResult { +        let mut pixels = vec![PxReader::RecursivePixel::default(); block.index.pixel_size.width()]; // TODO allocate once in self + +        let byte_lines = block.data.chunks_exact(header.channels.bytes_per_pixel * block.index.pixel_size.width()); +        debug_assert_eq!(byte_lines.len(), block.index.pixel_size.height(), "invalid block lines split"); + +        for (y_offset, line_bytes) in byte_lines.enumerate() { // TODO sampling +            // this two-step copy method should be very cache friendly in theory, and also reduce sample_type lookup count +            self.pixel_reader.read_pixels(line_bytes, &mut pixels, |px| px); + +            for (x_offset, pixel) in pixels.iter().enumerate() { +                let set_pixel = &self.set_pixel; +                set_pixel(&mut self.pixel_storage, block.index.pixel_position + Vec2(x_offset, y_offset), pixel.into_tuple()); +            } +        } + +        Ok(()) +    } + +    fn into_channels(self) -> Self::Channels { +        SpecificChannels { channels: self.pixel_reader.get_descriptions().into_non_recursive(), pixels: self.pixel_storage } +    } +} + + +/// Read zero channels from an image. Call `with_named_channel` on this object +/// to read as many channels as desired. +pub type ReadZeroChannels = NoneMore; + +impl ReadSpecificChannel for NoneMore { +    type RecursivePixelReader = NoneMore; +    fn create_recursive_reader(&self, _: &ChannelList) -> Result<Self::RecursivePixelReader> { Ok(NoneMore) } +} + +impl<DefaultSample, ReadChannels> ReadSpecificChannel for ReadOptionalChannel<ReadChannels, DefaultSample> +    where ReadChannels: ReadSpecificChannel, DefaultSample: FromNativeSample + 'static, +{ +    type RecursivePixelReader = Recursive<ReadChannels::RecursivePixelReader, OptionalSampleReader<DefaultSample>>; + +    fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader> { +        debug_assert!(self.previous_channels.already_contains(&self.channel_name).not(), "duplicate channel name: {}", self.channel_name); + +        let inner_samples_reader = self.previous_channels.create_recursive_reader(channels)?; +        let reader = channels.channels_with_byte_offset() +            .find(|(_, channel)| channel.name == self.channel_name) +            .map(|(channel_byte_offset, channel)| SampleReader { +                channel_byte_offset, channel: channel.clone(), +                px: Default::default() +            }); + +        Ok(Recursive::new(inner_samples_reader, OptionalSampleReader { +            reader, default_sample: self.default_sample, +        })) +    } +} + +impl<Sample, ReadChannels> ReadSpecificChannel for ReadRequiredChannel<ReadChannels, Sample> +    where ReadChannels: ReadSpecificChannel, Sample: FromNativeSample + 'static +{ +    type RecursivePixelReader = Recursive<ReadChannels::RecursivePixelReader, SampleReader<Sample>>; + +    fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader> { +        let previous_samples_reader = self.previous_channels.create_recursive_reader(channels)?; +        let (channel_byte_offset, channel) = channels.channels_with_byte_offset() +                .find(|(_, channel)| channel.name == self.channel_name) +                .ok_or_else(|| Error::invalid(format!( +                    "layer does not contain all of your specified channels (`{}` is missing)", +                    self.channel_name +                )))?; + +        Ok(Recursive::new(previous_samples_reader, SampleReader { channel_byte_offset, channel: channel.clone(), px: Default::default() })) +    } +} + +/// Reader for a single channel. Generic over the concrete sample type (f16, f32, u32). +#[derive(Clone, Debug)] +pub struct SampleReader<Sample> { + +    /// to be multiplied with line width! +    channel_byte_offset: usize, + +    channel: ChannelDescription, +    px: PhantomData<Sample> +} + +/// Reader for a single channel. Generic over the concrete sample type (f16, f32, u32). +/// Can also skip reading a channel if it could not be found in the image. +#[derive(Clone, Debug)] +pub struct OptionalSampleReader<DefaultSample> { +    reader: Option<SampleReader<DefaultSample>>, +    default_sample: DefaultSample, +} + +impl<Sample: FromNativeSample> SampleReader<Sample> { +    fn read_own_samples<'s, FullPixel>( +        &self, bytes: &'s[u8], pixels: &mut [FullPixel], +        get_sample: impl Fn(&mut FullPixel) -> &mut Sample +    ){ +        let start_index = pixels.len() * self.channel_byte_offset; +        let byte_count = pixels.len() * self.channel.sample_type.bytes_per_sample(); +        let mut own_bytes_reader = &mut &bytes[start_index .. start_index + byte_count]; // TODO check block size somewhere +        let mut samples_out = pixels.iter_mut().map(|pixel| get_sample(pixel)); + +        // match the type once for the whole line, not on every single sample +        match self.channel.sample_type { +            SampleType::F16 => read_and_convert_all_samples_batched( +                &mut own_bytes_reader, &mut samples_out, +                Sample::from_f16s +            ), + +            SampleType::F32 => read_and_convert_all_samples_batched( +                &mut own_bytes_reader, &mut samples_out, +                Sample::from_f32s +            ), + +            SampleType::U32 => read_and_convert_all_samples_batched( +                &mut own_bytes_reader, &mut samples_out, +                Sample::from_u32s +            ), +        } + +        debug_assert!(samples_out.next().is_none(), "not all samples have been converted"); +        debug_assert!(own_bytes_reader.is_empty(), "bytes left after reading all samples"); +    } +} + + +/// Does the same as `convert_batch(in_bytes.chunks().map(From::from_bytes))`, but vectorized. +/// Reads the samples for one line, using the sample type specified in the file, +/// and then converts those to the desired sample types. +/// Uses batches to allow vectorization, converting multiple values with one instruction. +fn read_and_convert_all_samples_batched<'t, From, To>( +    mut in_bytes: impl Read, +    out_samples: &mut impl ExactSizeIterator<Item=&'t mut To>, +    convert_batch: fn(&[From], &mut [To]) +) where From: Data + Default + Copy, To: 't + Default + Copy +{ +    // this is not a global! why is this warning triggered? +    #[allow(non_upper_case_globals)] +    const batch_size: usize = 16; + +    let total_sample_count = out_samples.len(); +    let batch_count = total_sample_count / batch_size; +    let remaining_samples_count = total_sample_count % batch_size; + +    let len_error_msg = "sample count was miscalculated"; +    let byte_error_msg = "error when reading from in-memory slice"; + +    // write samples from a given slice to the output iterator. should be inlined. +    let output_n_samples = &mut move |samples: &[To]| { +        for converted_sample in samples { +            *out_samples.next().expect(len_error_msg) = *converted_sample; +        } +    }; + +    // read samples from the byte source into a given slice. should be inlined. +    // todo: use #[inline] when available +    // error[E0658]: attributes on expressions are experimental, +    // see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information +    let read_n_samples = &mut move |samples: &mut [From]| { +        Data::read_slice(&mut in_bytes, samples).expect(byte_error_msg); +    }; + +    // temporary arrays with fixed size, operations should be vectorized within these arrays +    let mut source_samples_batch: [From; batch_size] = Default::default(); +    let mut desired_samples_batch: [To; batch_size] = Default::default(); + +    // first convert all whole batches, size statically known to be 16 element arrays +    for _ in 0 .. batch_count { +        read_n_samples(&mut source_samples_batch); +        convert_batch(source_samples_batch.as_slice(), desired_samples_batch.as_mut_slice()); +        output_n_samples(&desired_samples_batch); +    } + +    // then convert a partial remaining batch, size known only at runtime +    if remaining_samples_count != 0 { +        let source_samples_batch = &mut source_samples_batch[..remaining_samples_count]; +        let desired_samples_batch = &mut desired_samples_batch[..remaining_samples_count]; + +        read_n_samples(source_samples_batch); +        convert_batch(source_samples_batch, desired_samples_batch); +        output_n_samples(desired_samples_batch); +    } +} + +#[cfg(test)] +mod test { +    use super::*; + +    #[test] +    fn equals_naive_f32(){ +        for total_array_size in [3, 7, 30, 41, 120, 10_423] { +            let input_f32s = (0..total_array_size).map(|_| rand::random::<f32>()).collect::<Vec<f32>>(); +            let in_f32s_bytes = input_f32s.iter().cloned().flat_map(f32::to_le_bytes).collect::<Vec<u8>>(); + +            let mut out_f16_samples_batched = vec![ +                f16::from_f32(rand::random::<f32>()); +                total_array_size +            ]; + +            read_and_convert_all_samples_batched( +                &mut in_f32s_bytes.as_slice(), +                &mut out_f16_samples_batched.iter_mut(), +                f16::from_f32s +            ); + +            let out_f16_samples_naive = input_f32s.iter() +                .cloned().map(f16::from_f32); + +            assert!(out_f16_samples_naive.eq(out_f16_samples_batched)); +        } +    } +} + + +impl RecursivePixelReader for NoneMore { +    type RecursiveChannelDescriptions = NoneMore; +    fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { NoneMore } + +    type RecursivePixel = NoneMore; + +    fn read_pixels<'s, FullPixel>( +        &self, _: &'s[u8], _: &mut [FullPixel], +        _: impl Fn(&mut FullPixel) -> &mut NoneMore +    ){} +} + +impl<Sample, InnerReader: RecursivePixelReader> +    RecursivePixelReader +    for Recursive<InnerReader, SampleReader<Sample>> +    where Sample: FromNativeSample + 'static +{ +    type RecursiveChannelDescriptions = Recursive<InnerReader::RecursiveChannelDescriptions, ChannelDescription>; +    fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { Recursive::new(self.inner.get_descriptions(), self.value.channel.clone()) } + +    type RecursivePixel = Recursive<InnerReader::RecursivePixel, Sample>; + +    fn read_pixels<'s, FullPixel>( +        &self, bytes: &'s[u8], pixels: &mut [FullPixel], +        get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel +    ) { +        self.value.read_own_samples(bytes, pixels, |px| &mut get_pixel(px).value); +        self.inner.read_pixels(bytes, pixels, |px| &mut get_pixel(px).inner); +    } +} + +impl<Sample, InnerReader: RecursivePixelReader> +RecursivePixelReader +for Recursive<InnerReader, OptionalSampleReader<Sample>> +    where Sample: FromNativeSample + 'static +{ +    type RecursiveChannelDescriptions = Recursive<InnerReader::RecursiveChannelDescriptions, Option<ChannelDescription>>; +    fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { Recursive::new( +        self.inner.get_descriptions(), self.value.reader.as_ref().map(|reader| reader.channel.clone()) +    ) } + +    type RecursivePixel = Recursive<InnerReader::RecursivePixel, Sample>; + +    fn read_pixels<'s, FullPixel>( +        &self, bytes: &'s[u8], pixels: &mut [FullPixel], +        get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel +    ) { +        if let Some(reader) = &self.value.reader { +            reader.read_own_samples(bytes, pixels, |px| &mut get_pixel(px).value); +        } +        else { +            // if this channel is optional and was not found in the file, fill the default sample +            for pixel in pixels.iter_mut() { +                get_pixel(pixel).value = self.value.default_sample; +            } +        } + +        self.inner.read_pixels(bytes, pixels, |px| &mut get_pixel(px).inner); +    } +} + + diff --git a/vendor/exr/src/image/recursive.rs b/vendor/exr/src/image/recursive.rs new file mode 100644 index 0000000..25a980f --- /dev/null +++ b/vendor/exr/src/image/recursive.rs @@ -0,0 +1,178 @@ +//! A generic wrapper which can be used to represent recursive types. +//! Supports conversion from and to tuples of the same size. + +/// No more recursion. Can be used within any `Recursive<NoneMore, YourValue>` type. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct NoneMore; + +/// A recursive type-level linked list of `Value` entries. +/// Mainly used to represent an arbitrary number of channels. +/// The recursive architecture removes the need to implement traits for many different tuples. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct Recursive<Inner, Value> { +    /// The remaining values of this linked list, +    /// probably either `NoneMore` or another instance of the same `Recursive<Inner - 1, Value>`. +    pub inner: Inner, + +    /// The next item in this linked list. +    pub value: Value, +} + +impl<Inner, Value> Recursive<Inner, Value> { +    /// Create a new recursive type. Equivalent to the manual constructor, but less verbose. +    pub fn new(inner: Inner, value: Value) -> Self { Self { inner, value } } +} + +/// Convert this recursive type into a tuple. +/// This is nice as it will require less typing for the same type. +/// A type might or might not be convertible to the specified `Tuple` type. +pub trait IntoTuple<Tuple> { +    /// Convert this recursive type to a nice tuple. +    fn into_tuple(self) -> Tuple; +} + +/// Convert this recursive type into a tuple. +/// This is nice as it will require less typing for the same type. +/// A type will be converted to the specified `Self::NonRecursive` type. +pub trait IntoNonRecursive { +    /// The resulting tuple type. +    type NonRecursive; + +    /// Convert this recursive type to a nice tuple. +    fn into_non_recursive(self) -> Self::NonRecursive; +} + +/// Create a recursive type from this tuple. +pub trait IntoRecursive { +    /// The recursive type resulting from this tuple. +    type Recursive; + +    /// Create a recursive type from this tuple. +    fn into_recursive(self) -> Self::Recursive; +} + +impl IntoRecursive for NoneMore { +    type Recursive = Self; +    fn into_recursive(self) -> Self::Recursive { self } +} + +impl<Inner: IntoRecursive, Value> IntoRecursive for Recursive<Inner, Value> { +    type Recursive = Recursive<Inner::Recursive, Value>; +    fn into_recursive(self) -> Self::Recursive { Recursive::new(self.inner.into_recursive(), self.value) } +} + +// Automatically implement IntoTuple so we have to generate less code in the macros +impl<I: IntoNonRecursive> IntoTuple<I::NonRecursive> for I { +    fn into_tuple(self) -> <I as IntoNonRecursive>::NonRecursive { +        self.into_non_recursive() +    } +} + +//Implement traits for the empty tuple, the macro doesn't handle that +impl IntoRecursive for () { +    type Recursive = NoneMore; +    fn into_recursive(self) -> Self::Recursive { NoneMore } +} + +impl IntoNonRecursive for NoneMore { +    type NonRecursive = (); + +    fn into_non_recursive(self) -> Self::NonRecursive { +        () +    } +} + +/// Generates the recursive type corresponding to this tuple: +/// ```nocheck +/// gen_recursive_type!(A, B, C) +/// => Recursive<Recursive<Recursive<NoneMore, A>, B>, C> +/// ``` +macro_rules! gen_recursive_type { +    () => { NoneMore }; +    ($last:ident $(,$not_last:ident)*) => { +        Recursive<gen_recursive_type!($($not_last),*), $last> +    }; +} + +/// Generates the recursive value corresponding to the given indices: +/// ```nocheck +/// gen_recursive_value(self; 1, 0) +/// => Recursive { inner: Recursive {  inner: NoneMore, value: self.0 }, value: self.1 } +/// ``` +macro_rules! gen_recursive_value { +    ($self:ident;) => { NoneMore }; +    ($self:ident; $last:tt $(,$not_last:tt)*) => { +        Recursive { inner: gen_recursive_value!($self; $($not_last),*), value: $self.$last } +    }; +} + +/// Generates the into_tuple value corresponding to the given type names: +/// ```nocheck +/// gen_tuple_value(self; A, B, C) +/// => (self.inner.inner.value, self.inner.value, self.value) +/// ``` +macro_rules! gen_tuple_value { +    ($self:ident; $($all:ident),* ) => { +        gen_tuple_value!(@ $self; (); $($all),*  ) +    }; + +    (@ $self:ident; ($($state:expr),*);) => { ($($state .value,)*) }; +    (@ $self:ident; ($($state:expr),*); $last:ident $(,$not_last:ident)* ) => { +        gen_tuple_value!(@ $self; ($($state .inner,)* $self); $($not_last),*  ) +    }; +} + +/// Generate the trait implementations given a sequence of type names in both directions and the indices backwards: +/// ```nocheck +/// generate_single(A, B, C; C, B, A; 2, 1, 0) +/// ``` +macro_rules! generate_single { +    ( $($name_fwd:ident),* ; $($name_back:ident),* ; $($index_back:tt),*) => { +        impl<$($name_fwd),*> IntoNonRecursive for gen_recursive_type!($($name_back),*) { +            type NonRecursive = ($($name_fwd,)*); +            fn into_non_recursive(self) -> Self::NonRecursive { +                gen_tuple_value!(self; $($name_fwd),*) +            } +        } + +        impl<$($name_fwd),*> IntoRecursive for ($($name_fwd,)*) { +            type Recursive = gen_recursive_type!($($name_back),*); +            fn into_recursive(self) -> Self::Recursive { +                gen_recursive_value!(self; $($index_back),*) +            } +        } +    }; +} + +generate_single!(A; A; 0); +generate_single!(A,B; B,A; 1,0); +generate_single!(A,B,C; C,B,A; 2,1,0); +generate_single!(A,B,C,D; D,C,B,A; 3,2,1,0); +generate_single!(A,B,C,D,E; E,D,C,B,A; 4,3,2,1,0); +generate_single!(A,B,C,D,E,F; F,E,D,C,B,A; 5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G; G,F,E,D,C,B,A; 6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H; H,G,F,E,D,C,B,A; 7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I; I,H,G,F,E,D,C,B,A; 8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J; J,I,H,G,F,E,D,C,B,A; 9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K; K,J,I,H,G,F,E,D,C,B,A; 10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L; L,K,J,I,H,G,F,E,D,C,B,A; 11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M; M,L,K,J,I,H,G,F,E,D,C,B,A; 12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N; N,M,L,K,J,I,H,G,F,E,D,C,B,A; 13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O; O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P; P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q; Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R; R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S; S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T; T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U; U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V; V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W; W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X; X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y; Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z; Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1; A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1; B1,A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1; C1,B1,A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1; D1,C1,B1,A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1; E1,D1,C1,B1,A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); +generate_single!(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1; F1,E1,D1,C1,B1,A1,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A; 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); 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) +    } +} | 
