aboutsummaryrefslogtreecommitdiff
path: root/vendor/exr/src/image/mod.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
committerValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
commita990de90fe41456a23e58bd087d2f107d321f3a1 (patch)
tree15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/exr/src/image/mod.rs
parent3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff)
downloadfparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz
fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip
Deleted vendor folder
Diffstat (limited to 'vendor/exr/src/image/mod.rs')
-rw-r--r--vendor/exr/src/image/mod.rs1326
1 files changed, 0 insertions, 1326 deletions
diff --git a/vendor/exr/src/image/mod.rs b/vendor/exr/src/image/mod.rs
deleted file mode 100644
index db75050..0000000
--- a/vendor/exr/src/image/mod.rs
+++ /dev/null
@@ -1,1326 +0,0 @@
-
-//! 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);
- }
- }
-}
-
-