pub mod read;
pub mod write;
pub mod crop;
pub mod pixel_vec;
pub mod recursive;
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;
pub(crate) fn ignore_progress(_progress: f64){}
pub type AnyImage = Image<Layers<AnyChannels<Levels<FlatSamples>>>>;
pub type FlatImage = Image<Layers<AnyChannels<FlatSamples>>>;
pub type PixelLayersImage<Storage, Channels> = Image<Layers<SpecificChannels<Storage, Channels>>>;
pub type PixelImage<Storage, Channels> = Image<Layer<SpecificChannels<Storage, Channels>>>;
pub type RgbaLayersImage<Storage> = PixelLayersImage<Storage, RgbaChannels>;
pub type RgbaImage<Storage> = PixelImage<Storage, RgbaChannels>;
pub type RgbaChannels = (ChannelDescription, ChannelDescription, ChannelDescription, Option<ChannelDescription>);
pub type RgbChannels = (ChannelDescription, ChannelDescription, ChannelDescription);
#[derive(Debug, Clone, PartialEq)]
pub struct Image<Layers> {
pub attributes: ImageAttributes,
pub layer_data: Layers,
}
pub type Layers<Channels> = SmallVec<[Layer<Channels>; 2]>;
#[derive(Debug, Clone, PartialEq)]
pub struct Layer<Channels> {
pub channel_data: Channels,
pub attributes: LayerAttributes,
pub size: Vec2<usize>,
pub encoding: Encoding
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Encoding {
pub compression: Compression,
pub blocks: Blocks,
pub line_order: LineOrder,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Blocks {
ScanLines,
Tiles (Vec2<usize>)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpecificChannels<Pixels, ChannelsDescription> {
pub channels: ChannelsDescription,
pub pixels: Pixels, }
#[derive(Debug, Clone, PartialEq)]
pub struct AnyChannels<Samples> {
pub list: SmallVec<[AnyChannel<Samples>; 4]>
}
#[derive(Debug, Clone, PartialEq)]
pub struct AnyChannel<Samples> {
pub name: Text,
pub sample_data: Samples,
pub quantize_linearly: bool,
pub sampling: Vec2<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Levels<Samples> {
Singular(Samples),
Mip
{
rounding_mode: RoundingMode,
level_data: LevelMaps<Samples>
},
Rip
{
rounding_mode: RoundingMode,
level_data: RipMaps<Samples>
},
}
pub type LevelMaps<Samples> = Vec<Samples>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RipMaps<Samples> {
pub map_data: LevelMaps<Samples>,
pub level_count: Vec2<usize>,
}
#[derive(Clone, PartialEq)] pub enum FlatSamples {
F16(Vec<f16>),
F32(Vec<f32>),
U32(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> {
pub fn absolute_bounds(&self) -> IntegerBounds {
IntegerBounds::new(self.attributes.layer_position, self.size)
}
}
impl<SampleStorage, Channels> SpecificChannels<SampleStorage, Channels> {
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 }
}
}
pub trait IntoSample: IntoNativeSample {
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; }
#[derive(Debug)]
pub struct SpecificChannelsBuilder<RecursiveChannels, RecursivePixel> {
channels: RecursiveChannels,
px: PhantomData<RecursivePixel>
}
pub trait CheckDuplicates {
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<(),()>
{
pub fn build() -> SpecificChannelsBuilder<NoneMore, NoneMore> {
SpecificChannelsBuilder { channels: NoneMore, px: Default::default() }
}
}
impl<RecursiveChannels: CheckDuplicates, RecursivePixel> SpecificChannelsBuilder<RecursiveChannels, RecursivePixel>
{
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))
}
pub fn with_channel_details<Sample: Into<Sample>>(self, channel: ChannelDescription)
-> SpecificChannelsBuilder<Recursive<RecursiveChannels, ChannelDescription>, Recursive<RecursivePixel, Sample>>
{
assert!(self.channels.already_contains(&channel.name).not(), "channel name `{}` is duplicate", channel.name);
SpecificChannelsBuilder {
channels: Recursive::new(self.channels, channel),
px: PhantomData::default()
}
}
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
}
}
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)
>
{
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)
>
{
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
}
}
}
pub type FlatSamplesPixel = SmallVec<[Sample; 8]>;
impl Layer<AnyChannels<FlatSamples>> {
pub fn sample_vec_at(&self, position: Vec2<usize>) -> FlatSamplesPixel {
self.samples_at(position).collect()
}
pub fn samples_at(&self, position: Vec2<usize>) -> FlatSampleIterator<'_> {
FlatSampleIterator {
layer: self,
channel_index: 0,
position
}
}
}
#[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 }
}
}
impl<SampleData> AnyChannels<SampleData>{
pub fn sort(mut list: SmallVec<[AnyChannel<SampleData>; 4]>) -> Self {
list.sort_unstable_by_key(|channel| channel.name.clone()); Self { list }
}
}
impl<LevelSamples> Levels<LevelSamples> {
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"))
}
}
}
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"))
}
}
}
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,
}
}
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,
}
}
pub fn level_mode(&self) -> LevelMode {
match self {
Levels::Singular(_) => LevelMode::Singular,
Levels::Mip { .. } => LevelMode::MipMap,
Levels::Rip { .. } => LevelMode::RipMap,
}
}
}
impl<Samples> RipMaps<Samples> {
pub fn get_level_index(&self, level: Vec2<usize>) -> usize {
level.flat_index_for_size(self.level_count)
}
pub fn get_by_level(&self, level: Vec2<usize>) -> Option<&Samples> {
self.map_data.get(self.get_level_index(level))
}
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 {
pub fn len(&self) -> usize {
match self {
FlatSamples::F16(vec) => vec.len(),
FlatSamples::F32(vec) => vec.len(),
FlatSamples::U32(vec) => vec.len(),
}
}
pub fn values_as_f32<'s>(&'s self) -> impl 's + Iterator<Item = f32> {
self.values().map(|sample| sample.to_f32())
}
pub fn values<'s>(&'s self) -> impl 's + Iterator<Item = Sample> {
(0..self.len()).map(move |index| self.value_by_flat_index(index))
}
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> {
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 }
}
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 {
pub const UNCOMPRESSED: Encoding = Encoding {
compression: Compression::Uncompressed,
blocks: Blocks::ScanLines, line_order: LineOrder::Increasing };
pub const FAST_LOSSLESS: Encoding = Encoding {
compression: Compression::RLE,
blocks: Blocks::Tiles(Vec2(64, 64)), line_order: LineOrder::Unspecified
};
pub const SMALL_LOSSLESS: Encoding = Encoding {
compression: Compression::ZIP16,
blocks: Blocks::ScanLines, line_order: LineOrder::Increasing
};
pub const SMALL_FAST_LOSSY: 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> {
pub fn new(image_attributes: ImageAttributes, layer_data: LayerData) -> Self {
Image { attributes: image_attributes, layer_data }
}
}
impl<'s, Channels: 's> Image<Layers<Channels>> where Channels: WritableChannels<'s> {
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> {
pub fn from_layer(layer: Layer<ChannelData>) -> Self {
let bounds = IntegerBounds::new(layer.attributes.layer_position, layer.size);
Self::new(ImageAttributes::new(bounds), layer)
}
pub fn from_encoded_channels(size: impl Into<Vec2<usize>>, encoding: Encoding, channels: ChannelData) -> Self {
Self::from_layer(Layer::new(size, LayerAttributes::default(), encoding, channels))
}
pub fn from_channels(size: impl Into<Vec2<usize>>, channels: ChannelData) -> Self {
Self::from_encoded_channels(size, Encoding::default(), channels)
}
}
impl Image<NoneMore> {
pub fn empty(attributes: ImageAttributes) -> Self { Self { attributes, layer_data: NoneMore } }
}
impl<'s, InnerLayers: 's> Image<InnerLayers> where
InnerLayers: WritableLayers<'s>,
{
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> {
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),
}
}
}
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()),
}
}
}
}
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;
pub trait ValidateResult {
fn assert_equals_result(&self, result: &Self) {
self.validate_result(result, ValidationOptions::default(), String::new()).unwrap();
}
fn validate_result(
&self, lossy_result: &Self,
options: ValidationOptions,
context: String
) -> ValidationResult;
}
#[derive(Default, Debug, Eq, PartialEq, Hash, Copy, Clone)]
pub struct ValidationOptions {
allow_lossy: bool,
nan_converted_to_zero: bool,
}
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: 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: 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 {
allow_lossy: other.encoding.compression
.is_lossless_for(other_chan.sample_data.sample_type()).not(),
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
{
fn validate_result(&self, other: &Self, _overridden: ValidationOptions, location: 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 {
let options = ValidationOptions {
allow_lossy: other.encoding.compression.may_loose_data(),
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: 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: 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: 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: 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: 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: 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: 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: 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: 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: String) -> ValidationResult {
self.clone().into_recursive().validate_result(&other.clone().into_recursive(), options, location)
}
}
impl ValidateResult for NoneMore {
fn validate_result(&self, _: &Self, _: ValidationOptions, _: 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: String) -> ValidationResult {
self.value.validate_result(&other.value, options, location.clone()).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: 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: 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: 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: String) -> ValidationResult {
if self == other { Ok(()) } else { self.to_f32().validate_result(&other.to_f32(), options, location)
}
}
}
impl ValidateResult for Sample {
fn validate_result(&self, other: &Self, options: ValidationOptions, location: 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);
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() )
.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);
}
}
#[test]
fn test_pxr24_f32(){
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::PXR24,
.. 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);
}
#[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, .. 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);
}
}
}