/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.0/src/image/mod.rs
Line | Count | Source |
1 | | |
2 | | //! Data structures that represent a complete exr image. |
3 | | //! Contains generic structs that must be nested to obtain a complete image type. |
4 | | //! |
5 | | //! |
6 | | //! For example, an rgba image containing multiple layers |
7 | | //! can be represented using `Image<Layers<SpecificChannels<MyPixelStorage>>>`. |
8 | | //! An image containing a single layer with arbitrary channels and no deep data |
9 | | //! can be represented using `Image<Layer<AnyChannels<FlatSamples>>>`. |
10 | | //! |
11 | | //! |
12 | | //! These and other predefined types are included in this module as |
13 | | //! 1. `PixelImage`: A single layer, fixed set of arbitrary channels. |
14 | | //! 1. `PixelLayersImage`: Multiple layers, fixed set of arbitrary channels. |
15 | | //! 1. `RgbaImage`: A single layer, fixed set of channels: rgb, optional a. |
16 | | //! 1. `RgbaLayersImage`: Multiple layers, fixed set of channels: rgb, optional a. |
17 | | //! 1. `FlatImage`: Multiple layers, any channels, no deep data. |
18 | | //! 1. `AnyImage`: All supported data (multiple layers, arbitrary channels, no deep data yet) |
19 | | //! |
20 | | //! You can also use your own types inside an image, |
21 | | //! for example if you want to use a custom sample storage. |
22 | | //! |
23 | | //! This is the high-level interface for the pixels of an image. |
24 | | //! See `exr::blocks` module for a low-level interface. |
25 | | |
26 | | pub mod read; |
27 | | pub mod write; |
28 | | pub mod crop; |
29 | | pub mod pixel_vec; |
30 | | pub mod recursive; |
31 | | // pub mod channel_groups; |
32 | | |
33 | | |
34 | | use crate::meta::header::{ImageAttributes, LayerAttributes}; |
35 | | use crate::meta::attribute::{Text, LineOrder}; |
36 | | use half::f16; |
37 | | use crate::math::{Vec2, RoundingMode}; |
38 | | use crate::compression::Compression; |
39 | | use smallvec::{SmallVec}; |
40 | | use crate::error::Error; |
41 | | |
42 | | /// Don't do anything |
43 | 718k | pub(crate) fn ignore_progress(_progress: f64){} |
44 | | |
45 | | /// This image type contains all supported exr features and can represent almost any image. |
46 | | /// It currently does not support deep data yet. |
47 | | pub type AnyImage = Image<Layers<AnyChannels<Levels<FlatSamples>>>>; |
48 | | |
49 | | /// This image type contains the most common exr features and can represent almost any plain image. |
50 | | /// Does not contain resolution levels. Does not support deep data. |
51 | | pub type FlatImage = Image<Layers<AnyChannels<FlatSamples>>>; |
52 | | |
53 | | /// This image type contains multiple layers, with each layer containing a user-defined type of pixels. |
54 | | pub type PixelLayersImage<Storage, Channels> = Image<Layers<SpecificChannels<Storage, Channels>>>; |
55 | | |
56 | | /// This image type contains a single layer containing a user-defined type of pixels. |
57 | | pub type PixelImage<Storage, Channels> = Image<Layer<SpecificChannels<Storage, Channels>>>; |
58 | | |
59 | | /// This image type contains multiple layers, with each layer containing a user-defined type of rgba pixels. |
60 | | pub type RgbaLayersImage<Storage> = PixelLayersImage<Storage, RgbaChannels>; |
61 | | |
62 | | /// This image type contains a single layer containing a user-defined type of rgba pixels. |
63 | | pub type RgbaImage<Storage> = PixelImage<Storage, RgbaChannels>; |
64 | | |
65 | | /// Contains information about the channels in an rgba image, in the order `(red, green, blue, alpha)`. |
66 | | /// The alpha channel is not required. May be `None` if the image did not contain an alpha channel. |
67 | | pub type RgbaChannels = (ChannelDescription, ChannelDescription, ChannelDescription, Option<ChannelDescription>); |
68 | | |
69 | | /// Contains information about the channels in an rgb image, in the order `(red, green, blue)`. |
70 | | pub type RgbChannels = (ChannelDescription, ChannelDescription, ChannelDescription); |
71 | | |
72 | | /// The complete exr image. |
73 | | /// `Layers` can be either a single `Layer` or `Layers`. |
74 | | #[derive(Debug, Clone, PartialEq)] |
75 | | pub struct Image<Layers> { |
76 | | |
77 | | /// Attributes that apply to the whole image file. |
78 | | /// These attributes appear in each layer of the file. |
79 | | /// Excludes technical meta data. |
80 | | /// Each layer in this image also has its own attributes. |
81 | | pub attributes: ImageAttributes, |
82 | | |
83 | | /// The layers contained in the image file. |
84 | | /// Can be either a single `Layer` or a list of layers. |
85 | | pub layer_data: Layers, |
86 | | } |
87 | | |
88 | | /// A list of layers. `Channels` can be `SpecificChannels` or `AnyChannels`. |
89 | | pub type Layers<Channels> = SmallVec<[Layer<Channels>; 2]>; |
90 | | |
91 | | /// A single Layer, including fancy attributes and compression settings. |
92 | | /// `Channels` can be either `SpecificChannels` or `AnyChannels` |
93 | | #[derive(Debug, Clone, PartialEq)] |
94 | | pub struct Layer<Channels> { |
95 | | |
96 | | /// The actual pixel data. Either `SpecificChannels` or `AnyChannels` |
97 | | pub channel_data: Channels, |
98 | | |
99 | | /// Attributes that apply to this layer. |
100 | | /// May still contain attributes that should be considered global for an image file. |
101 | | /// Excludes technical meta data: Does not contain data window size, line order, tiling, or compression attributes. |
102 | | /// The image also has attributes, which do not differ per layer. |
103 | | pub attributes: LayerAttributes, |
104 | | |
105 | | /// The pixel resolution of this layer. |
106 | | /// See `layer.attributes` for more attributes, like for example layer position. |
107 | | pub size: Vec2<usize>, |
108 | | |
109 | | /// How the pixels are split up and compressed. |
110 | | pub encoding: Encoding |
111 | | } |
112 | | |
113 | | /// How the pixels are split up and compressed. |
114 | | #[derive(Copy, Clone, Debug, PartialEq)] |
115 | | pub struct Encoding { |
116 | | |
117 | | /// How the pixel data of all channels in this layer is compressed. May be `Compression::Uncompressed`. |
118 | | /// See `layer.attributes` for more attributes. |
119 | | pub compression: Compression, |
120 | | |
121 | | /// Describes how the pixels of this layer are divided into smaller blocks. |
122 | | /// Either splits the image into its scan lines or splits the image into tiles of the specified size. |
123 | | /// A single block can be loaded without processing all bytes of a file. |
124 | | pub blocks: Blocks, |
125 | | |
126 | | /// In what order the tiles of this header occur in the file. |
127 | | /// Does not change any actual image orientation. |
128 | | /// See `layer.attributes` for more attributes. |
129 | | pub line_order: LineOrder, |
130 | | } |
131 | | |
132 | | /// How the image pixels are split up into separate blocks. |
133 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
134 | | pub enum Blocks { |
135 | | |
136 | | /// The image is divided into scan line blocks. |
137 | | /// The number of scan lines in a block depends on the compression method. |
138 | | ScanLines, |
139 | | |
140 | | /// The image is divided into tile blocks. |
141 | | /// Also specifies the size of each tile in the image |
142 | | /// and whether this image contains multiple resolution levels. |
143 | | /// |
144 | | /// The inner `Vec2` describes the size of each tile. |
145 | | /// Stays the same number of pixels across all levels. |
146 | | Tiles (Vec2<usize>) |
147 | | } |
148 | | |
149 | | |
150 | | /// A grid of pixels. The pixels are written to your custom pixel storage. |
151 | | /// `PixelStorage` can be anything, from a flat `Vec<f16>` to `Vec<Vec<AnySample>>`, as desired. |
152 | | /// In order to write this image to a file, your `PixelStorage` must implement [`GetPixel`]. |
153 | | #[derive(Debug, Clone, PartialEq, Eq)] |
154 | | pub struct SpecificChannels<Pixels, ChannelsDescription> { |
155 | | |
156 | | /// A description of the channels in the file, as opposed to the channels in memory. |
157 | | /// Should always be a tuple containing `ChannelDescription`s, one description for each channel. |
158 | | pub channels: ChannelsDescription, // TODO this is awkward. can this be not a type parameter please? maybe vec<option<chan_info>> ?? |
159 | | |
160 | | /// Your custom pixel storage |
161 | | // TODO should also support `Levels<YourStorage>`, where levels are desired! |
162 | | pub pixels: Pixels, // TODO rename to "pixels"? |
163 | | } |
164 | | |
165 | | |
166 | | /// A dynamic list of arbitrary channels. |
167 | | /// `Samples` can currently only be `FlatSamples` or `Levels<FlatSamples>`. |
168 | | #[derive(Debug, Clone, PartialEq)] |
169 | | pub struct AnyChannels<Samples> { |
170 | | |
171 | | /// This list must be sorted alphabetically, by channel name. |
172 | | /// Use `AnyChannels::sorted` for automatic sorting. |
173 | | pub list: SmallVec<[AnyChannel<Samples>; 4]> |
174 | | } |
175 | | |
176 | | /// A single arbitrary channel. |
177 | | /// `Samples` can currently only be `FlatSamples` or `Levels<FlatSamples>` |
178 | | #[derive(Debug, Clone, PartialEq)] |
179 | | pub struct AnyChannel<Samples> { |
180 | | |
181 | | /// One of "R", "G", or "B" most of the time. |
182 | | pub name: Text, |
183 | | |
184 | | /// The actual pixel data. |
185 | | /// Can be `FlatSamples` or `Levels<FlatSamples>`. |
186 | | pub sample_data: Samples, |
187 | | |
188 | | /// This attribute only tells lossy compression methods |
189 | | /// whether this value should be quantized exponentially or linearly. |
190 | | /// |
191 | | /// Should be `false` for red, green, blue and luma channels, as they are not perceived linearly. |
192 | | /// Should be `true` for hue, chroma, saturation, and alpha channels. |
193 | | pub quantize_linearly: bool, |
194 | | |
195 | | /// How many of the samples are skipped compared to the other channels in this layer. |
196 | | /// |
197 | | /// Can be used for chroma subsampling for manual lossy data compression. |
198 | | /// Values other than 1 are allowed only in flat, scan-line based images. |
199 | | /// If an image is deep or tiled, the sampling rates for all of its channels must be 1. |
200 | | pub sampling: Vec2<usize>, |
201 | | } |
202 | | |
203 | | /// One or multiple resolution levels of the same image. |
204 | | /// `Samples` can be `FlatSamples`. |
205 | | #[derive(Debug, Clone, PartialEq, Eq)] |
206 | | pub enum Levels<Samples> { |
207 | | |
208 | | /// A single image without smaller versions of itself. |
209 | | /// If you only want to handle exclusively this case, use `Samples` directly, and not `Levels<Samples>`. |
210 | | Singular(Samples), |
211 | | |
212 | | /// Contains uniformly scaled smaller versions of the original. |
213 | | Mip |
214 | | { |
215 | | /// Whether to round up or down when calculating Mip/Rip levels. |
216 | | rounding_mode: RoundingMode, |
217 | | |
218 | | /// The smaller versions of the original. |
219 | | level_data: LevelMaps<Samples> |
220 | | }, |
221 | | |
222 | | /// Contains any possible combination of smaller versions of the original. |
223 | | Rip |
224 | | { |
225 | | /// Whether to round up or down when calculating Mip/Rip levels. |
226 | | rounding_mode: RoundingMode, |
227 | | |
228 | | /// The smaller versions of the original. |
229 | | level_data: RipMaps<Samples> |
230 | | }, |
231 | | } |
232 | | |
233 | | /// A list of resolution levels. `Samples` can currently only be `FlatSamples`. |
234 | | // or `DeepAndFlatSamples` (not yet implemented). |
235 | | pub type LevelMaps<Samples> = Vec<Samples>; |
236 | | |
237 | | /// In addition to the full resolution image, |
238 | | /// this layer also contains smaller versions, |
239 | | /// and each smaller version has further versions with varying aspect ratios. |
240 | | /// `Samples` can currently only be `FlatSamples`. |
241 | | #[derive(Debug, Clone, PartialEq, Eq)] |
242 | | pub struct RipMaps<Samples> { |
243 | | |
244 | | /// A flattened list containing the individual levels |
245 | | pub map_data: LevelMaps<Samples>, |
246 | | |
247 | | /// The number of levels that were generated along the x-axis and y-axis. |
248 | | pub level_count: Vec2<usize>, |
249 | | } |
250 | | |
251 | | |
252 | | // TODO deep data |
253 | | /*#[derive(Clone, PartialEq)] |
254 | | pub enum DeepAndFlatSamples { |
255 | | Deep(DeepSamples), |
256 | | Flat(FlatSamples) |
257 | | }*/ |
258 | | |
259 | | /// A vector of non-deep values (one value per pixel per channel). |
260 | | /// Stores row after row in a single vector. |
261 | | /// The precision of all values is either `f16`, `f32` or `u32`. |
262 | | /// |
263 | | /// Since this is close to the pixel layout in the byte file, |
264 | | /// this will most likely be the fastest storage. |
265 | | /// Using a different storage, for example `SpecificChannels`, |
266 | | /// will probably be slower. |
267 | | #[derive(Clone, PartialEq)] // debug is implemented manually |
268 | | pub enum FlatSamples { |
269 | | |
270 | | /// A vector of non-deep `f16` values. |
271 | | F16(Vec<f16>), |
272 | | |
273 | | /// A vector of non-deep `f32` values. |
274 | | F32(Vec<f32>), |
275 | | |
276 | | /// A vector of non-deep `u32` values. |
277 | | U32(Vec<u32>), |
278 | | } |
279 | | |
280 | | |
281 | | /*#[derive(Clone, PartialEq)] |
282 | | pub enum DeepSamples { |
283 | | F16(Vec<Vec<f16>>), |
284 | | F32(Vec<Vec<f32>>), |
285 | | U32(Vec<Vec<u32>>), |
286 | | }*/ |
287 | | |
288 | | use crate::block::samples::*; |
289 | | use crate::meta::attribute::*; |
290 | | use crate::error::Result; |
291 | | use crate::block::samples::Sample; |
292 | | use crate::image::write::channels::*; |
293 | | use crate::image::write::layers::WritableLayers; |
294 | | use crate::image::write::samples::{WritableSamples}; |
295 | | use crate::meta::{mip_map_levels, rip_map_levels}; |
296 | | use crate::io::Data; |
297 | | use crate::image::recursive::{NoneMore, Recursive, IntoRecursive}; |
298 | | use std::marker::PhantomData; |
299 | | use std::ops::Not; |
300 | | use crate::image::validate_results::{ValidationOptions}; |
301 | | |
302 | | |
303 | | impl<Channels> Layer<Channels> { |
304 | | /// Sometimes called "data window" |
305 | 0 | pub fn absolute_bounds(&self) -> IntegerBounds { |
306 | 0 | IntegerBounds::new(self.attributes.layer_position, self.size) |
307 | 0 | } |
308 | | } |
309 | | |
310 | | |
311 | | impl<SampleStorage, Channels> SpecificChannels<SampleStorage, Channels> { |
312 | | /// Create some pixels with channel information. |
313 | | /// The `Channels` must be a tuple containing either `ChannelDescription` or `Option<ChannelDescription>`. |
314 | | /// The length of the tuple dictates the number of channels in the sample storage. |
315 | 0 | pub fn new(channels: Channels, source_samples: SampleStorage) -> Self |
316 | 0 | where |
317 | 0 | SampleStorage: GetPixel, |
318 | 0 | SampleStorage::Pixel: IntoRecursive, |
319 | 0 | Channels: Sync + Clone + IntoRecursive, |
320 | 0 | <Channels as IntoRecursive>::Recursive: WritableChannelsDescription<<SampleStorage::Pixel as IntoRecursive>::Recursive>, |
321 | | { |
322 | 0 | SpecificChannels { channels, pixels: source_samples } |
323 | 0 | } |
324 | | } |
325 | | |
326 | | /// Convert this type into one of the known sample types. |
327 | | /// Also specify the preferred native type, which dictates the default sample type in the image. |
328 | | pub trait IntoSample: IntoNativeSample { |
329 | | |
330 | | /// The native sample types that this type should be converted to. |
331 | | const PREFERRED_SAMPLE_TYPE: SampleType; |
332 | | } |
333 | | |
334 | | impl IntoSample for f16 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::F16; } |
335 | | impl IntoSample for f32 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::F32; } |
336 | | impl IntoSample for u32 { const PREFERRED_SAMPLE_TYPE: SampleType = SampleType::U32; } |
337 | | |
338 | | /// Used to construct a `SpecificChannels`. |
339 | | /// Call `with_named_channel` as many times as desired, |
340 | | /// and then call `with_pixels` to define the colors. |
341 | | #[derive(Debug)] |
342 | | pub struct SpecificChannelsBuilder<RecursiveChannels, RecursivePixel> { |
343 | | channels: RecursiveChannels, |
344 | | px: PhantomData<RecursivePixel> |
345 | | } |
346 | | |
347 | | /// This check can be executed at compile time |
348 | | /// if the channel names are `&'static str` and the compiler is smart enough. |
349 | | pub trait CheckDuplicates { |
350 | | |
351 | | /// Check for duplicate channel names. |
352 | | fn already_contains(&self, name: &Text) -> bool; |
353 | | } |
354 | | |
355 | | impl CheckDuplicates for NoneMore { |
356 | 9.07k | fn already_contains(&self, _: &Text) -> bool { false } |
357 | | } |
358 | | |
359 | | impl<Inner: CheckDuplicates> CheckDuplicates for Recursive<Inner, ChannelDescription> { |
360 | 0 | fn already_contains(&self, name: &Text) -> bool { |
361 | 0 | &self.value.name == name || self.inner.already_contains(name) |
362 | 0 | } |
363 | | } |
364 | | |
365 | | impl SpecificChannels<(),()> |
366 | | { |
367 | | /// Start building some specific channels. On the result of this function, |
368 | | /// call `with_named_channel` as many times as desired, |
369 | | /// and then call `with_pixels` to define the colors. |
370 | 0 | pub fn build() -> SpecificChannelsBuilder<NoneMore, NoneMore> { |
371 | 0 | SpecificChannelsBuilder { channels: NoneMore, px: Default::default() } |
372 | 0 | } |
373 | | } |
374 | | |
375 | | impl<RecursiveChannels: CheckDuplicates, RecursivePixel> SpecificChannelsBuilder<RecursiveChannels, RecursivePixel> |
376 | | { |
377 | | /// Add another channel to this image. Does not add the actual pixels, |
378 | | /// but instead only declares the presence of the channel. |
379 | | /// Panics if the name contains unsupported characters. |
380 | | /// Panics if a channel with the same name already exists. |
381 | | /// Use `Text::new_or_none()` to manually handle these cases. |
382 | | /// Use `with_channel_details` instead if you want to specify more options than just the name of the channel. |
383 | | /// The generic parameter can usually be inferred from the closure in `with_pixels`. |
384 | 0 | pub fn with_channel<Sample: IntoSample>(self, name: impl Into<Text>) |
385 | 0 | -> SpecificChannelsBuilder<Recursive<RecursiveChannels, ChannelDescription>, Recursive<RecursivePixel, Sample>> |
386 | | { |
387 | 0 | self.with_channel_details::<Sample>(ChannelDescription::named(name, Sample::PREFERRED_SAMPLE_TYPE)) |
388 | 0 | } |
389 | | |
390 | | /// Add another channel to this image. Does not add the actual pixels, |
391 | | /// but instead only declares the presence of the channel. |
392 | | /// Use `with_channel` instead if you only want to specify the name of the channel. |
393 | | /// Panics if a channel with the same name already exists. |
394 | | /// The generic parameter can usually be inferred from the closure in `with_pixels`. |
395 | 0 | pub fn with_channel_details<Sample: Into<Sample>>(self, channel: ChannelDescription) |
396 | 0 | -> SpecificChannelsBuilder<Recursive<RecursiveChannels, ChannelDescription>, Recursive<RecursivePixel, Sample>> |
397 | | { |
398 | | // duplicate channel names are checked later, but also check now to make sure there are no problems with the `SpecificChannelsWriter` |
399 | 0 | assert!(self.channels.already_contains(&channel.name).not(), "channel name `{}` is duplicate", channel.name); |
400 | | |
401 | 0 | SpecificChannelsBuilder { |
402 | 0 | channels: Recursive::new(self.channels, channel), |
403 | 0 | px: PhantomData::default() |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | /// Specify the actual pixel contents of the image. |
408 | | /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> Pixel`), |
409 | | /// or you can pass your own image if it implements `GetPixel`. |
410 | | /// The pixel type must be a tuple with the correct number of entries, depending on the number of channels. |
411 | | /// The tuple entries can be either `f16`, `f32`, `u32` or `Sample`. |
412 | | /// Use `with_pixel_fn` instead of this function, to get extra type safety for your pixel closure. |
413 | 0 | pub fn with_pixels<Pixels>(self, get_pixel: Pixels) -> SpecificChannels<Pixels, RecursiveChannels> |
414 | 0 | where Pixels: GetPixel, <Pixels as GetPixel>::Pixel: IntoRecursive<Recursive=RecursivePixel>, |
415 | | { |
416 | 0 | SpecificChannels { |
417 | 0 | channels: self.channels, |
418 | 0 | pixels: get_pixel |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | /// Specify the contents of the image. |
423 | | /// The pixel type must be a tuple with the correct number of entries, depending on the number of channels. |
424 | | /// The tuple entries can be either `f16`, `f32`, `u32` or `Sample`. |
425 | | /// Use `with_pixels` instead of this function, if you want to pass an object that is not a closure. |
426 | | /// |
427 | | /// Usually, the compiler can infer the type of the pixel (for example, `f16,f32,f32`) from the closure. |
428 | | /// If that's not possible, you can specify the type of the channels |
429 | | /// when declaring the channel (for example, `with_named_channel::<f32>("R")`). |
430 | 0 | pub fn with_pixel_fn<Pixel, Pixels>(self, get_pixel: Pixels) -> SpecificChannels<Pixels, RecursiveChannels> |
431 | 0 | where Pixels: Sync + Fn(Vec2<usize>) -> Pixel, Pixel: IntoRecursive<Recursive=RecursivePixel>, |
432 | | { |
433 | 0 | SpecificChannels { |
434 | 0 | channels: self.channels, |
435 | 0 | pixels: get_pixel |
436 | 0 | } |
437 | 0 | } |
438 | | } |
439 | | |
440 | | impl<SampleStorage> SpecificChannels< |
441 | | SampleStorage, (ChannelDescription, ChannelDescription, ChannelDescription, ChannelDescription) |
442 | | > |
443 | | { |
444 | | |
445 | | /// Create an image with red, green, blue, and alpha channels. |
446 | | /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> (R,G,B,A)`), |
447 | | /// or you can pass your own image if it implements `GetPixel<Pixel=(R,G,B,A)>`. |
448 | | /// Each of `R`, `G`, `B` and `A` can be either `f16`, `f32`, `u32`, or `Sample`. |
449 | 85 | pub fn rgba<R, G, B, A>(source_samples: SampleStorage) -> Self |
450 | 85 | where R: IntoSample, G: IntoSample, |
451 | 85 | B: IntoSample, A: IntoSample, |
452 | 85 | SampleStorage: GetPixel<Pixel=(R, G, B, A)> |
453 | | { |
454 | 85 | SpecificChannels { |
455 | 85 | channels: ( |
456 | 85 | ChannelDescription::named("R", R::PREFERRED_SAMPLE_TYPE), |
457 | 85 | ChannelDescription::named("G", G::PREFERRED_SAMPLE_TYPE), |
458 | 85 | ChannelDescription::named("B", B::PREFERRED_SAMPLE_TYPE), |
459 | 85 | ChannelDescription::named("A", A::PREFERRED_SAMPLE_TYPE), |
460 | 85 | ), |
461 | 85 | pixels: source_samples |
462 | 85 | } |
463 | 85 | } Unexecuted instantiation: <exr::image::SpecificChannels<_, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgba::<_, _, _, _> Unexecuted instantiation: <exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgba::<f32, f32, f32, f32><exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgba::<f32, f32, f32, f32>Line | Count | Source | 449 | 85 | pub fn rgba<R, G, B, A>(source_samples: SampleStorage) -> Self | 450 | 85 | where R: IntoSample, G: IntoSample, | 451 | 85 | B: IntoSample, A: IntoSample, | 452 | 85 | SampleStorage: GetPixel<Pixel=(R, G, B, A)> | 453 | | { | 454 | 85 | SpecificChannels { | 455 | 85 | channels: ( | 456 | 85 | ChannelDescription::named("R", R::PREFERRED_SAMPLE_TYPE), | 457 | 85 | ChannelDescription::named("G", G::PREFERRED_SAMPLE_TYPE), | 458 | 85 | ChannelDescription::named("B", B::PREFERRED_SAMPLE_TYPE), | 459 | 85 | ChannelDescription::named("A", A::PREFERRED_SAMPLE_TYPE), | 460 | 85 | ), | 461 | 85 | pixels: source_samples | 462 | 85 | } | 463 | 85 | } |
|
464 | | } |
465 | | |
466 | | impl<SampleStorage> SpecificChannels< |
467 | | SampleStorage, (ChannelDescription, ChannelDescription, ChannelDescription) |
468 | | > |
469 | | { |
470 | | |
471 | | /// Create an image with red, green, and blue channels. |
472 | | /// You can pass a closure that returns a color for each pixel (`Fn(Vec2<usize>) -> (R,G,B)`), |
473 | | /// or you can pass your own image if it implements `GetPixel<Pixel=(R,G,B)>`. |
474 | | /// Each of `R`, `G` and `B` can be either `f16`, `f32`, `u32`, or `Sample`. |
475 | 0 | pub fn rgb<R, G, B>(source_samples: SampleStorage) -> Self |
476 | 0 | where R: IntoSample, G: IntoSample, B: IntoSample, |
477 | 0 | SampleStorage: GetPixel<Pixel=(R, G, B)> |
478 | | { |
479 | 0 | SpecificChannels { |
480 | 0 | channels: ( |
481 | 0 | ChannelDescription::named("R", R::PREFERRED_SAMPLE_TYPE), |
482 | 0 | ChannelDescription::named("G", G::PREFERRED_SAMPLE_TYPE), |
483 | 0 | ChannelDescription::named("B", B::PREFERRED_SAMPLE_TYPE), |
484 | 0 | ), |
485 | 0 | pixels: source_samples |
486 | 0 | } |
487 | 0 | } Unexecuted instantiation: <exr::image::SpecificChannels<_, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgb::<_, _, _> Unexecuted instantiation: <exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgb::<f32, f32, f32>Unexecuted instantiation: <exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>::rgb::<f32, f32, f32> |
488 | | } |
489 | | |
490 | | |
491 | | /// A list of samples representing a single pixel. |
492 | | /// Does not heap allocate for images with 8 or fewer channels. |
493 | | pub type FlatSamplesPixel = SmallVec<[Sample; 8]>; |
494 | | |
495 | | // TODO also deep samples? |
496 | | impl Layer<AnyChannels<FlatSamples>> { |
497 | | |
498 | | /// Use `samples_at` if you can borrow from this layer |
499 | 0 | pub fn sample_vec_at(&self, position: Vec2<usize>) -> FlatSamplesPixel { |
500 | 0 | self.samples_at(position).collect() |
501 | 0 | } |
502 | | |
503 | | /// Lookup all channels of a single pixel in the image |
504 | 0 | pub fn samples_at(&self, position: Vec2<usize>) -> FlatSampleIterator<'_> { |
505 | 0 | FlatSampleIterator { |
506 | 0 | layer: self, |
507 | 0 | channel_index: 0, |
508 | 0 | position |
509 | 0 | } |
510 | 0 | } |
511 | | } |
512 | | |
513 | | /// Iterate over all channels of a single pixel in the image |
514 | | #[derive(Debug, Copy, Clone, PartialEq)] |
515 | | pub struct FlatSampleIterator<'s> { |
516 | | layer: &'s Layer<AnyChannels<FlatSamples>>, |
517 | | channel_index: usize, |
518 | | position: Vec2<usize>, |
519 | | } |
520 | | |
521 | | impl Iterator for FlatSampleIterator<'_> { |
522 | | type Item = Sample; |
523 | | |
524 | 0 | fn next(&mut self) -> Option<Self::Item> { |
525 | 0 | if self.channel_index < self.layer.channel_data.list.len() { |
526 | 0 | let channel = &self.layer.channel_data.list[self.channel_index]; |
527 | 0 | let sample = channel.sample_data.value_by_flat_index(self.position.flat_index_for_size(self.layer.size)); |
528 | 0 | self.channel_index += 1; |
529 | 0 | Some(sample) |
530 | | } |
531 | 0 | else { None } |
532 | 0 | } |
533 | | |
534 | 0 | fn nth(&mut self, pos: usize) -> Option<Self::Item> { |
535 | 0 | self.channel_index += pos; |
536 | 0 | self.next() |
537 | 0 | } |
538 | | |
539 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
540 | 0 | let remaining = self.layer.channel_data.list.len().saturating_sub(self.channel_index); |
541 | 0 | (remaining, Some(remaining)) |
542 | 0 | } |
543 | | } |
544 | | |
545 | | impl ExactSizeIterator for FlatSampleIterator<'_> {} |
546 | | |
547 | | impl<SampleData> AnyChannels<SampleData>{ |
548 | | |
549 | | /// A new list of arbitrary channels. Sorts the list to make it alphabetically stable. |
550 | 0 | pub fn sort(mut list: SmallVec<[AnyChannel<SampleData>; 4]>) -> Self { |
551 | 0 | list.sort_unstable_by_key(|channel| channel.name.clone()); // TODO no clone? |
552 | 0 | Self { list } |
553 | 0 | } |
554 | | } |
555 | | |
556 | | // FIXME check content size of layer somewhere??? before writing? |
557 | | impl<LevelSamples> Levels<LevelSamples> { |
558 | | |
559 | | /// Get a resolution level by index, sorted by size, decreasing. |
560 | 0 | pub fn get_level(&self, level: Vec2<usize>) -> Result<&LevelSamples> { |
561 | 0 | match self { |
562 | 0 | Levels::Singular(block) => { |
563 | 0 | debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug"); |
564 | 0 | Ok(block) |
565 | | }, |
566 | | |
567 | 0 | Levels::Mip { level_data, .. } => { |
568 | 0 | debug_assert_eq!(level.x(), level.y(), "mip map levels must be equal on x and y bug"); |
569 | 0 | level_data.get(level.x()).ok_or(Error::invalid("block mip level index")) |
570 | | }, |
571 | | |
572 | 0 | Levels::Rip { level_data, .. } => { |
573 | 0 | level_data.get_by_level(level).ok_or(Error::invalid("block rip level index")) |
574 | | } |
575 | | } |
576 | 0 | } |
577 | | |
578 | | /// Get a resolution level by index, sorted by size, decreasing. |
579 | | // TODO storage order for RIP maps? |
580 | 0 | pub fn get_level_mut(&mut self, level: Vec2<usize>) -> Result<&mut LevelSamples> { |
581 | 0 | match self { |
582 | 0 | Levels::Singular(ref mut block) => { |
583 | 0 | debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug"); |
584 | 0 | Ok(block) |
585 | | }, |
586 | | |
587 | 0 | Levels::Mip { level_data, .. } => { |
588 | 0 | debug_assert_eq!(level.x(), level.y(), "mip map levels must be equal on x and y bug"); |
589 | 0 | level_data.get_mut(level.x()).ok_or(Error::invalid("block mip level index")) |
590 | | }, |
591 | | |
592 | 0 | Levels::Rip { level_data, .. } => { |
593 | 0 | level_data.get_by_level_mut(level).ok_or(Error::invalid("block rip level index")) |
594 | | } |
595 | | } |
596 | 0 | } |
597 | | |
598 | | /// Get a slice of all resolution levels, sorted by size, decreasing. |
599 | 0 | pub fn levels_as_slice(&self) -> &[LevelSamples] { |
600 | 0 | match self { |
601 | 0 | Levels::Singular(data) => std::slice::from_ref(data), |
602 | 0 | Levels::Mip { level_data, .. } => level_data, |
603 | 0 | Levels::Rip { level_data, .. } => &level_data.map_data, |
604 | | } |
605 | 0 | } |
606 | | |
607 | | /// Get a mutable slice of all resolution levels, sorted by size, decreasing. |
608 | 0 | pub fn levels_as_slice_mut(&mut self) -> &mut [LevelSamples] { |
609 | 0 | match self { |
610 | 0 | Levels::Singular(data) => std::slice::from_mut(data), |
611 | 0 | Levels::Mip { level_data, .. } => level_data, |
612 | 0 | Levels::Rip { level_data, .. } => &mut level_data.map_data, |
613 | | } |
614 | 0 | } |
615 | | |
616 | | // TODO simplify working with levels in general! like level_size_by_index and such |
617 | | |
618 | | /*pub fn levels_with_size(&self, rounding: RoundingMode, max_resolution: Vec2<usize>) -> Vec<(Vec2<usize>, &S)> { |
619 | | match self { |
620 | | Levels::Singular(ref data) => vec![ (max_resolution, data) ], |
621 | | Levels::Mip(ref maps) => mip_map_levels(rounding, max_resolution).map(|(_index, size)| size).zip(maps).collect(), |
622 | | Levels::Rip(ref rip_maps) => rip_map_levels(rounding, max_resolution).map(|(_index, size)| size).zip(&rip_maps.map_data).collect(), |
623 | | } |
624 | | }*/ |
625 | | |
626 | | /// Whether this stores multiple resolution levels. |
627 | 0 | pub fn level_mode(&self) -> LevelMode { |
628 | 0 | match self { |
629 | 0 | Levels::Singular(_) => LevelMode::Singular, |
630 | 0 | Levels::Mip { .. } => LevelMode::MipMap, |
631 | 0 | Levels::Rip { .. } => LevelMode::RipMap, |
632 | | } |
633 | 0 | } |
634 | | } |
635 | | |
636 | | impl<Samples> RipMaps<Samples> { |
637 | | |
638 | | /// Flatten the 2D level index to a one dimensional index. |
639 | 0 | pub fn get_level_index(&self, level: Vec2<usize>) -> usize { |
640 | 0 | level.flat_index_for_size(self.level_count) |
641 | 0 | } |
642 | | |
643 | | /// Return a level by level index. Level `0` has the largest resolution. |
644 | 0 | pub fn get_by_level(&self, level: Vec2<usize>) -> Option<&Samples> { |
645 | 0 | self.map_data.get(self.get_level_index(level)) |
646 | 0 | } |
647 | | |
648 | | /// Return a mutable level reference by level index. Level `0` has the largest resolution. |
649 | 0 | pub fn get_by_level_mut(&mut self, level: Vec2<usize>) -> Option<&mut Samples> { |
650 | 0 | let index = self.get_level_index(level); |
651 | 0 | self.map_data.get_mut(index) |
652 | 0 | } |
653 | | } |
654 | | |
655 | | impl FlatSamples { |
656 | | |
657 | | /// The number of samples in the image. Should be the width times the height. |
658 | | /// Might vary when subsampling is used. |
659 | 0 | pub fn len(&self) -> usize { |
660 | 0 | match self { |
661 | 0 | FlatSamples::F16(vec) => vec.len(), |
662 | 0 | FlatSamples::F32(vec) => vec.len(), |
663 | 0 | FlatSamples::U32(vec) => vec.len(), |
664 | | } |
665 | 0 | } |
666 | | |
667 | | /// Views all samples in this storage as f32. |
668 | | /// Matches the underlying sample type again for every sample, |
669 | | /// match yourself if performance is critical! Does not allocate. |
670 | 0 | pub fn values_as_f32<'s>(&'s self) -> impl 's + Iterator<Item = f32> { |
671 | 0 | self.values().map(|sample| sample.to_f32()) |
672 | 0 | } |
673 | | |
674 | | /// All samples in this storage as iterator. |
675 | | /// Matches the underlying sample type again for every sample, |
676 | | /// match yourself if performance is critical! Does not allocate. |
677 | 0 | pub fn values<'s>(&'s self) -> impl 's + Iterator<Item = Sample> { |
678 | 0 | (0..self.len()).map(move |index| self.value_by_flat_index(index)) |
679 | 0 | } |
680 | | |
681 | | /// Lookup a single value, by flat index. |
682 | | /// The flat index can be obtained using `Vec2::flatten_for_width` |
683 | | /// which computes the index in a flattened array of pixel rows. |
684 | 0 | pub fn value_by_flat_index(&self, index: usize) -> Sample { |
685 | 0 | match self { |
686 | 0 | FlatSamples::F16(vec) => Sample::F16(vec[index]), |
687 | 0 | FlatSamples::F32(vec) => Sample::F32(vec[index]), |
688 | 0 | FlatSamples::U32(vec) => Sample::U32(vec[index]), |
689 | | } |
690 | 0 | } |
691 | | } |
692 | | |
693 | | |
694 | | impl<'s, ChannelData:'s> Layer<ChannelData> { |
695 | | |
696 | | /// Create a layer with the specified size, attributes, encoding and channels. |
697 | | /// The channels can be either `SpecificChannels` or `AnyChannels`. |
698 | 85 | pub fn new( |
699 | 85 | dimensions: impl Into<Vec2<usize>>, |
700 | 85 | attributes: LayerAttributes, |
701 | 85 | encoding: Encoding, |
702 | 85 | channels: ChannelData |
703 | 85 | ) -> Self |
704 | 85 | where ChannelData: WritableChannels<'s> |
705 | | { |
706 | 85 | Layer { channel_data: channels, attributes, size: dimensions.into(), encoding } |
707 | 85 | } Unexecuted instantiation: <exr::image::Layer<_>>::new::<_> Unexecuted instantiation: <exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>::new::<(usize, usize)>Unexecuted instantiation: <exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>::new::<(usize, usize)>Unexecuted instantiation: <exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>::new::<(usize, usize)><exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>::new::<(usize, usize)>Line | Count | Source | 698 | 85 | pub fn new( | 699 | 85 | dimensions: impl Into<Vec2<usize>>, | 700 | 85 | attributes: LayerAttributes, | 701 | 85 | encoding: Encoding, | 702 | 85 | channels: ChannelData | 703 | 85 | ) -> Self | 704 | 85 | where ChannelData: WritableChannels<'s> | 705 | | { | 706 | 85 | Layer { channel_data: channels, attributes, size: dimensions.into(), encoding } | 707 | 85 | } |
|
708 | | |
709 | | // TODO test pls wtf |
710 | | /// Panics for images with Scanline encoding. |
711 | 0 | pub fn levels_with_resolution<'l, L>(&self, levels: &'l Levels<L>) -> Box<dyn 'l + Iterator<Item=(&'l L, Vec2<usize>)>> { |
712 | 0 | match levels { |
713 | 0 | Levels::Singular(level) => Box::new(std::iter::once((level, self.size))), |
714 | | |
715 | 0 | Levels::Mip { rounding_mode, level_data } => Box::new(level_data.iter().zip( |
716 | 0 | mip_map_levels(*rounding_mode, self.size) |
717 | 0 | .map(|(_index, size)| size) |
718 | | )), |
719 | | |
720 | 0 | Levels::Rip { rounding_mode, level_data } => Box::new(level_data.map_data.iter().zip( |
721 | 0 | rip_map_levels(*rounding_mode, self.size) |
722 | 0 | .map(|(_index, size)| size) |
723 | | )), |
724 | | } |
725 | 0 | } |
726 | | } |
727 | | |
728 | | impl Encoding { |
729 | | |
730 | | /// No compression. Massive space requirements. |
731 | | /// Fast, because it minimizes data shuffling and reallocation. |
732 | | pub const UNCOMPRESSED: Encoding = Encoding { |
733 | | compression: Compression::Uncompressed, |
734 | | blocks: Blocks::ScanLines, // longest lines, faster memcpy |
735 | | line_order: LineOrder::Increasing // presumably fastest? |
736 | | }; |
737 | | |
738 | | /// Run-length encoding with tiles of 64x64 pixels. This is the recommended default encoding. |
739 | | /// Almost as fast as uncompressed data, but optimizes single-colored areas such as mattes and masks. |
740 | | pub const FAST_LOSSLESS: Encoding = Encoding { |
741 | | compression: Compression::RLE, |
742 | | blocks: Blocks::Tiles(Vec2(64, 64)), // optimize for RLE compression |
743 | | line_order: LineOrder::Unspecified |
744 | | }; |
745 | | |
746 | | /// ZIP compression with blocks of 16 lines. Slow, but produces small files without visible artefacts. |
747 | | pub const SMALL_LOSSLESS: Encoding = Encoding { |
748 | | compression: Compression::ZIP16, |
749 | | blocks: Blocks::ScanLines, // largest possible, but also with high probability of parallel workers |
750 | | line_order: LineOrder::Increasing |
751 | | }; |
752 | | |
753 | | /// PIZ compression with tiles of 256x256 pixels. Small images, not too slow. |
754 | | pub const SMALL_FAST_LOSSLESS: Encoding = Encoding { |
755 | | compression: Compression::PIZ, |
756 | | blocks: Blocks::Tiles(Vec2(256, 256)), |
757 | | line_order: LineOrder::Unspecified |
758 | | }; |
759 | | } |
760 | | |
761 | | impl Default for Encoding { |
762 | 85 | fn default() -> Self { Encoding::FAST_LOSSLESS } |
763 | | } |
764 | | |
765 | | impl<'s, LayerData: 's> Image<LayerData> where LayerData: WritableLayers<'s> { |
766 | | /// Create an image with one or multiple layers. The layer can be a `Layer`, or `Layers` small vector, or `Vec<Layer>` or `&[Layer]`. |
767 | 85 | pub fn new(image_attributes: ImageAttributes, layer_data: LayerData) -> Self { |
768 | 85 | Image { attributes: image_attributes, layer_data } |
769 | 85 | } Unexecuted instantiation: <exr::image::Image<_>>::new Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::newUnexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::newUnexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::new<exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::newLine | Count | Source | 767 | 85 | pub fn new(image_attributes: ImageAttributes, layer_data: LayerData) -> Self { | 768 | 85 | Image { attributes: image_attributes, layer_data } | 769 | 85 | } |
|
770 | | } |
771 | | |
772 | | // explorable constructor alias |
773 | | impl<'s, Channels: 's> Image<Layers<Channels>> where Channels: WritableChannels<'s> { |
774 | | /// Create an image with multiple layers. The layer can be a `Vec<Layer>` or `Layers` (a small vector). |
775 | 0 | pub fn from_layers(image_attributes: ImageAttributes, layer_data: impl Into<Layers<Channels>>) -> Self { |
776 | 0 | Self::new(image_attributes, layer_data.into()) |
777 | 0 | } |
778 | | } |
779 | | |
780 | | |
781 | | impl<'s, ChannelData:'s> Image<Layer<ChannelData>> where ChannelData: WritableChannels<'s> { |
782 | | |
783 | | /// Uses the display position and size to the channel position and size of the layer. |
784 | 85 | pub fn from_layer(layer: Layer<ChannelData>) -> Self { |
785 | 85 | let bounds = IntegerBounds::new(layer.attributes.layer_position, layer.size); |
786 | 85 | Self::new(ImageAttributes::new(bounds), layer) |
787 | 85 | } Unexecuted instantiation: <exr::image::Image<exr::image::Layer<_>>>::from_layer Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_layerUnexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_layerUnexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_layer<exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_layerLine | Count | Source | 784 | 85 | pub fn from_layer(layer: Layer<ChannelData>) -> Self { | 785 | 85 | let bounds = IntegerBounds::new(layer.attributes.layer_position, layer.size); | 786 | 85 | Self::new(ImageAttributes::new(bounds), layer) | 787 | 85 | } |
|
788 | | |
789 | | /// Uses empty attributes. |
790 | 85 | pub fn from_encoded_channels(size: impl Into<Vec2<usize>>, encoding: Encoding, channels: ChannelData) -> Self { |
791 | | // layer name is not required for single-layer images |
792 | 85 | Self::from_layer(Layer::new(size, LayerAttributes::default(), encoding, channels)) |
793 | 85 | } Unexecuted instantiation: <exr::image::Image<exr::image::Layer<_>>>::from_encoded_channels::<_> Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_encoded_channels::<(usize, usize)>Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_encoded_channels::<(usize, usize)>Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_encoded_channels::<(usize, usize)><exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_encoded_channels::<(usize, usize)>Line | Count | Source | 790 | 85 | pub fn from_encoded_channels(size: impl Into<Vec2<usize>>, encoding: Encoding, channels: ChannelData) -> Self { | 791 | | // layer name is not required for single-layer images | 792 | 85 | Self::from_layer(Layer::new(size, LayerAttributes::default(), encoding, channels)) | 793 | 85 | } |
|
794 | | |
795 | | /// Uses empty attributes and fast compression. |
796 | 85 | pub fn from_channels(size: impl Into<Vec2<usize>>, channels: ChannelData) -> Self { |
797 | 85 | Self::from_encoded_channels(size, Encoding::default(), channels) |
798 | 85 | } Unexecuted instantiation: <exr::image::Image<exr::image::Layer<_>>>::from_channels::<_> Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_channels::<(usize, usize)>Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_channels::<(usize, usize)>Unexecuted instantiation: <exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_channels::<(usize, usize)><exr::image::Image<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_channels::<(usize, usize)>Line | Count | Source | 796 | 85 | pub fn from_channels(size: impl Into<Vec2<usize>>, channels: ChannelData) -> Self { | 797 | 85 | Self::from_encoded_channels(size, Encoding::default(), channels) | 798 | 85 | } |
|
799 | | } |
800 | | |
801 | | |
802 | | impl Image<NoneMore> { |
803 | | |
804 | | /// Create an empty image, to be filled with layers later on. Add at least one layer to obtain a valid image. |
805 | | /// Call `with_layer(another_layer)` for each layer you want to add to this image. |
806 | 0 | pub fn empty(attributes: ImageAttributes) -> Self { Self { attributes, layer_data: NoneMore } } |
807 | | } |
808 | | |
809 | | impl<'s, InnerLayers: 's> Image<InnerLayers> where |
810 | | InnerLayers: WritableLayers<'s>, |
811 | | { |
812 | | /// Add another layer to this image. The layer type does |
813 | | /// not have to equal the existing layers in this image. |
814 | 0 | pub fn with_layer<NewChannels>(self, layer: Layer<NewChannels>) |
815 | 0 | -> Image<Recursive<InnerLayers, Layer<NewChannels>>> |
816 | 0 | where NewChannels: 's + WritableChannels<'s> |
817 | | { |
818 | 0 | Image { |
819 | 0 | attributes: self.attributes, |
820 | 0 | layer_data: Recursive::new(self.layer_data, layer) |
821 | 0 | } |
822 | 0 | } |
823 | | } |
824 | | |
825 | | |
826 | | impl<'s, SampleData: 's> AnyChannel<SampleData> { |
827 | | |
828 | | /// Create a new channel without subsampling. |
829 | | /// |
830 | | /// Automatically flags this channel for specialized compression |
831 | | /// if the name is "R", "G", "B", "Y", or "L", |
832 | | /// as they typically encode values that are perceived non-linearly. |
833 | | /// Construct the value yourself using `AnyChannel { .. }`, if you want to control this flag. |
834 | 0 | pub fn new(name: impl Into<Text>, sample_data: SampleData) -> Self where SampleData: WritableSamples<'s> { |
835 | 0 | let name: Text = name.into(); |
836 | | |
837 | 0 | AnyChannel { |
838 | 0 | quantize_linearly: ChannelDescription::guess_quantization_linearity(&name), |
839 | 0 | name, sample_data, |
840 | 0 | sampling: Vec2(1, 1), |
841 | 0 | } |
842 | 0 | } |
843 | | |
844 | | /*/// This is the same as `AnyChannel::new()`, but additionally ensures that the closure type is correct. |
845 | | pub fn from_closure<V>(name: Text, sample_data: S) -> Self |
846 | | where S: Sync + Fn(Vec2<usize>) -> V, V: InferSampleType + Data |
847 | | { |
848 | | Self::new(name, sample_data) |
849 | | }*/ |
850 | | } |
851 | | |
852 | | impl std::fmt::Debug for FlatSamples { |
853 | 0 | fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
854 | 0 | if self.len() <= 6 { |
855 | 0 | match self { |
856 | 0 | FlatSamples::F16(vec) => vec.fmt(formatter), |
857 | 0 | FlatSamples::F32(vec) => vec.fmt(formatter), |
858 | 0 | FlatSamples::U32(vec) => vec.fmt(formatter), |
859 | | } |
860 | | } |
861 | | else { |
862 | 0 | match self { |
863 | 0 | FlatSamples::F16(vec) => write!(formatter, "[f16; {}]", vec.len()), |
864 | 0 | FlatSamples::F32(vec) => write!(formatter, "[f32; {}]", vec.len()), |
865 | 0 | FlatSamples::U32(vec) => write!(formatter, "[u32; {}]", vec.len()), |
866 | | } |
867 | | } |
868 | 0 | } |
869 | | } |
870 | | |
871 | | |
872 | | |
873 | | /// Compare the result of a round trip test with the original method. |
874 | | /// Supports lossy compression methods. |
875 | | // #[cfg(test)] TODO do not ship this code |
876 | | pub mod validate_results { |
877 | | use crate::prelude::*; |
878 | | use smallvec::Array; |
879 | | use crate::prelude::recursive::*; |
880 | | use crate::image::write::samples::WritableSamples; |
881 | | use std::ops::Not; |
882 | | use crate::block::samples::IntoNativeSample; |
883 | | |
884 | | /// Compare two objects, but with a few special quirks. |
885 | | /// Intended mainly for unit testing. |
886 | | pub trait ValidateResult { |
887 | | |
888 | | /// Compare self with the other. Panics if not equal. |
889 | | /// |
890 | | /// Exceptional behaviour: |
891 | | /// This does not work the other way around! This method is not symmetrical! |
892 | | /// Returns whether the result is correct for this image. |
893 | | /// For lossy compression methods, uses approximate equality. |
894 | | /// Intended for unit testing. |
895 | | /// |
896 | | /// Warning: If you use `SpecificChannels`, the comparison might be inaccurate |
897 | | /// for images with mixed compression methods. This is to be used with `AnyChannels` mainly. |
898 | 0 | fn assert_equals_result(&self, result: &Self) { |
899 | 0 | self.validate_result(result, ValidationOptions::default(), || String::new()).unwrap(); |
900 | 0 | } |
901 | | |
902 | | /// Compare self with the other. |
903 | | /// Exceptional behaviour: |
904 | | /// - Any two NaN values are considered equal, regardless of bit representation. |
905 | | /// - If a `lossy` is specified, any two values that differ only by a small amount will be considered equal. |
906 | | /// - If `nan_to_zero` is true, and __self is NaN/Infinite and the other value is zero, they are considered equal__ |
907 | | /// (because some compression methods replace nan with zero) |
908 | | /// |
909 | | /// This does not work the other way around! This method is not symmetrical! |
910 | | fn validate_result( |
911 | | &self, lossy_result: &Self, |
912 | | options: ValidationOptions, |
913 | | // this is a lazy string, because constructing a string is only necessary in the case of an error, |
914 | | // but eats up memory and allocation time every time. this was measured. |
915 | | context: impl Fn() -> String |
916 | | ) -> ValidationResult; |
917 | | } |
918 | | |
919 | | /// Whether to do accurate or approximate comparison. |
920 | | #[derive(Default, Debug, Eq, PartialEq, Hash, Copy, Clone)] |
921 | | pub struct ValidationOptions { |
922 | | allow_lossy: bool, |
923 | | nan_converted_to_zero: bool, |
924 | | } |
925 | | |
926 | | /// If invalid, contains the error message. |
927 | | pub type ValidationResult = std::result::Result<(), String>; |
928 | | |
929 | | |
930 | | impl<C> ValidateResult for Image<C> where C: ValidateResult { |
931 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
932 | 0 | if self.attributes != other.attributes { Err(location() + "| image > attributes") } |
933 | 0 | else { self.layer_data.validate_result(&other.layer_data, options, || location() + "| image > layer data") } |
934 | 0 | } |
935 | | } |
936 | | |
937 | | impl<S> ValidateResult for Layer<AnyChannels<S>> |
938 | | where AnyChannel<S>: ValidateResult, S: for<'a> WritableSamples<'a> |
939 | | { |
940 | 0 | fn validate_result(&self, other: &Self, _overridden: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
941 | 0 | let location = || format!("{} (layer `{:?}`)", location(), self.attributes.layer_name); |
942 | 0 | if self.attributes != other.attributes { Err(location() + " > attributes") } |
943 | 0 | else if self.encoding != other.encoding { Err(location() + " > encoding") } |
944 | 0 | else if self.size != other.size { Err(location() + " > size") } |
945 | 0 | else if self.channel_data.list.len() != other.channel_data.list.len() { Err(location() + " > channel count") } |
946 | | else { |
947 | 0 | for (own_chan, other_chan) in self.channel_data.list.iter().zip(other.channel_data.list.iter()) { |
948 | 0 | own_chan.validate_result( |
949 | 0 | other_chan, |
950 | | |
951 | 0 | ValidationOptions { |
952 | 0 | // no tolerance for lossless channels |
953 | 0 | allow_lossy: other.encoding.compression |
954 | 0 | .is_lossless_for(other_chan.sample_data.sample_type()).not(), |
955 | 0 |
|
956 | 0 | // consider nan and zero equal if the compression method does not support nan |
957 | 0 | nan_converted_to_zero: other.encoding.compression.supports_nan().not() |
958 | 0 | }, |
959 | | |
960 | 0 | || format!("{} > channel `{}`", location(), own_chan.name) |
961 | 0 | )?; |
962 | | } |
963 | 0 | Ok(()) |
964 | | } |
965 | 0 | } |
966 | | } |
967 | | |
968 | | impl<Px, Desc> ValidateResult for Layer<SpecificChannels<Px, Desc>> |
969 | | where SpecificChannels<Px, Desc>: ValidateResult |
970 | | { |
971 | | /// This does an approximate comparison for all channels, |
972 | | /// even if some channels can be compressed without loss. |
973 | 0 | fn validate_result(&self, other: &Self, _overridden: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
974 | 0 | let location = || format!("{} (layer `{:?}`)", location(), self.attributes.layer_name); |
975 | | |
976 | | // TODO dedup with above |
977 | 0 | if self.attributes != other.attributes { Err(location() + " > attributes") } |
978 | 0 | else if self.encoding != other.encoding { Err(location() + " > encoding") } |
979 | 0 | else if self.size != other.size { Err(location() + " > size") } |
980 | | else { |
981 | 0 | let options = ValidationOptions { |
982 | 0 | // no tolerance for lossless channels |
983 | 0 | // pxr only looses data for f32 values, B44 only for f16, not other any other types |
984 | 0 | allow_lossy: other.encoding.compression.may_loose_data(),// TODO check specific channels sample types |
985 | 0 |
|
986 | 0 | // consider nan and zero equal if the compression method does not support nan |
987 | 0 | nan_converted_to_zero: other.encoding.compression.supports_nan().not() |
988 | 0 | }; |
989 | | |
990 | 0 | self.channel_data.validate_result(&other.channel_data, options, || location() + " > channel_data")?; |
991 | 0 | Ok(()) |
992 | | } |
993 | 0 | } |
994 | | } |
995 | | |
996 | | impl<S> ValidateResult for AnyChannels<S> where S: ValidateResult { |
997 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
998 | 0 | self.list.validate_result(&other.list, options, location) |
999 | 0 | } |
1000 | | } |
1001 | | |
1002 | | impl<S> ValidateResult for AnyChannel<S> where S: ValidateResult { |
1003 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1004 | 0 | if self.name != other.name { Err(location() + " > name") } |
1005 | 0 | else if self.quantize_linearly != other.quantize_linearly { Err(location() + " > quantize_linearly") } |
1006 | 0 | else if self.sampling != other.sampling { Err(location() + " > sampling") } |
1007 | | else { |
1008 | 0 | self.sample_data.validate_result(&other.sample_data, options, || location() + " > sample_data") |
1009 | | } |
1010 | 0 | } |
1011 | | } |
1012 | | |
1013 | | impl<Pxs, Chans> ValidateResult for SpecificChannels<Pxs, Chans> where Pxs: ValidateResult, Chans: Eq { |
1014 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1015 | 0 | if self.channels != other.channels { Err(location() + " > specific channels") } |
1016 | 0 | else { self.pixels.validate_result(&other.pixels, options, || location() + " > specific pixels") } |
1017 | 0 | } |
1018 | | } |
1019 | | |
1020 | | impl<S> ValidateResult for Levels<S> where S: ValidateResult { |
1021 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1022 | 0 | self.levels_as_slice().validate_result(&other.levels_as_slice(), options, || location() + " > levels") |
1023 | 0 | } |
1024 | | } |
1025 | | |
1026 | | impl ValidateResult for FlatSamples { |
1027 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1028 | | use FlatSamples::*; |
1029 | 0 | match (self, other) { |
1030 | 0 | (F16(values), F16(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > f16 samples"), |
1031 | 0 | (F32(values), F32(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > f32 samples"), |
1032 | 0 | (U32(values), U32(other_values)) => values.as_slice().validate_result(&other_values.as_slice(), options, ||location() + " > u32 samples"), |
1033 | 0 | (own, other) => Err(format!("{}: samples type mismatch. expected {:?}, found {:?}", location(), own.sample_type(), other.sample_type())) |
1034 | | } |
1035 | 0 | } |
1036 | | } |
1037 | | |
1038 | | impl<T> ValidateResult for &[T] where T: ValidateResult { |
1039 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1040 | 0 | if self.len() != other.len() { Err(location() + " count") } |
1041 | | else { |
1042 | 0 | for (index, (slf, other)) in self.iter().zip(other.iter()).enumerate() { |
1043 | 0 | slf.validate_result(other, options, ||format!("{} element [{}] of {}", location(), index, self.len()))?; |
1044 | | } |
1045 | 0 | Ok(()) |
1046 | | } |
1047 | 0 | } |
1048 | | } |
1049 | | |
1050 | | impl<A: Array> ValidateResult for SmallVec<A> where A::Item: ValidateResult { |
1051 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1052 | 0 | self.as_slice().validate_result(&other.as_slice(), options, location) |
1053 | 0 | } |
1054 | | } |
1055 | | |
1056 | | impl<A> ValidateResult for Vec<A> where A: ValidateResult { |
1057 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1058 | 0 | self.as_slice().validate_result(&other.as_slice(), options, location) |
1059 | 0 | } |
1060 | | } |
1061 | | |
1062 | | impl<A,B,C,D> ValidateResult for (A, B, C, D) where A: Clone+ ValidateResult, B: Clone+ ValidateResult, C: Clone+ ValidateResult, D: Clone+ ValidateResult { |
1063 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1064 | 0 | self.clone().into_recursive().validate_result(&other.clone().into_recursive(), options, location) |
1065 | 0 | } |
1066 | | } |
1067 | | |
1068 | | impl<A,B,C> ValidateResult for (A, B, C) where A: Clone+ ValidateResult, B: Clone+ ValidateResult, C: Clone+ ValidateResult { |
1069 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1070 | 0 | self.clone().into_recursive().validate_result(&other.clone().into_recursive(), options, location) |
1071 | 0 | } |
1072 | | } |
1073 | | |
1074 | | // // (low priority because it is only used in the tests) |
1075 | | /*TODO |
1076 | | impl<Tuple> SimilarToLossy for Tuple where |
1077 | | Tuple: Clone + IntoRecursive, |
1078 | | <Tuple as IntoRecursive>::Recursive: SimilarToLossy, |
1079 | | { |
1080 | | fn similar_to_lossy(&self, other: &Self, max_difference: f32) -> bool { |
1081 | | self.clone().into_recursive().similar_to_lossy(&other.clone().into_recursive(), max_difference) |
1082 | | } // TODO no clone? |
1083 | | }*/ |
1084 | | |
1085 | | |
1086 | | // implement for recursive types |
1087 | | impl ValidateResult for NoneMore { |
1088 | 0 | fn validate_result(&self, _: &Self, _: ValidationOptions, _: impl Fn()->String) -> ValidationResult { Ok(()) } |
1089 | | } |
1090 | | |
1091 | | impl<Inner, T> ValidateResult for Recursive<Inner, T> where Inner: ValidateResult, T: ValidateResult { |
1092 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1093 | 0 | self.value.validate_result(&other.value, options, &location).and_then(|()| |
1094 | 0 | self.inner.validate_result(&other.inner, options, &location) |
1095 | | ) |
1096 | 0 | } |
1097 | | } |
1098 | | |
1099 | | impl<S> ValidateResult for Option<S> where S: ValidateResult { |
1100 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1101 | 0 | match (self, other) { |
1102 | 0 | (None, None) => Ok(()), |
1103 | 0 | (Some(value), Some(other)) => value.validate_result(other, options, location), |
1104 | 0 | _ => Err(location() + ": option mismatch") |
1105 | | } |
1106 | 0 | } |
1107 | | } |
1108 | | |
1109 | | impl ValidateResult for f32 { |
1110 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1111 | 0 | if self == other || (self.is_nan() && other.is_nan()) || (options.nan_converted_to_zero && !self.is_normal() && *other == 0.0) { |
1112 | 0 | return Ok(()); |
1113 | 0 | } |
1114 | | |
1115 | 0 | if options.allow_lossy { |
1116 | 0 | let epsilon = 0.06; |
1117 | 0 | let max_difference = 0.1; |
1118 | | |
1119 | 0 | let adaptive_threshold = epsilon * (self.abs() + other.abs()); |
1120 | 0 | let tolerance = adaptive_threshold.max(max_difference); |
1121 | 0 | let difference = (self - other).abs(); |
1122 | | |
1123 | 0 | return if difference <= tolerance { Ok(()) } |
1124 | 0 | else { Err(format!("{}: expected ~{}, found {} (adaptive tolerance {})", location(), self, other, tolerance)) }; |
1125 | 0 | } |
1126 | | |
1127 | 0 | Err(format!("{}: expected exactly {}, found {}", location(), self, other)) |
1128 | 0 | } |
1129 | | } |
1130 | | |
1131 | | impl ValidateResult for f16 { |
1132 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1133 | 0 | if self.to_bits() == other.to_bits() { Ok(()) } else { |
1134 | 0 | self.to_f32().validate_result(&other.to_f32(), options, location) |
1135 | | } |
1136 | 0 | } |
1137 | | } |
1138 | | |
1139 | | impl ValidateResult for u32 { |
1140 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1141 | 0 | if self == other { Ok(()) } else { // todo to float conversion resulting in nan/infinity? |
1142 | 0 | self.to_f32().validate_result(&other.to_f32(), options, location) |
1143 | | } |
1144 | 0 | } |
1145 | | } |
1146 | | |
1147 | | impl ValidateResult for Sample { |
1148 | 0 | fn validate_result(&self, other: &Self, options: ValidationOptions, location: impl Fn()->String) -> ValidationResult { |
1149 | | use Sample::*; |
1150 | 0 | match (self, other) { |
1151 | 0 | (F16(a), F16(b)) => a.validate_result(b, options, ||location() + " (f16)"), |
1152 | 0 | (F32(a), F32(b)) => a.validate_result(b, options, ||location() + " (f32)"), |
1153 | 0 | (U32(a), U32(b)) => a.validate_result(b, options, ||location() + " (u32)"), |
1154 | 0 | (_,_) => Err(location() + ": sample type mismatch") |
1155 | | } |
1156 | 0 | } |
1157 | | } |
1158 | | |
1159 | | |
1160 | | #[cfg(test)] |
1161 | | mod test_value_result { |
1162 | | use std::f32::consts::*; |
1163 | | use std::io::Cursor; |
1164 | | use crate::image::pixel_vec::PixelVec; |
1165 | | use crate::image::validate_results::{ValidateResult, ValidationOptions}; |
1166 | | use crate::meta::attribute::LineOrder::Increasing; |
1167 | | use crate::image::{FlatSamples}; |
1168 | | |
1169 | | fn expect_valid<T>(original: &T, result: &T, allow_lossy: bool, nan_converted_to_zero: bool) where T: ValidateResult { |
1170 | | original.validate_result( |
1171 | | result, |
1172 | | ValidationOptions { allow_lossy, nan_converted_to_zero }, |
1173 | | || String::new() |
1174 | | ).unwrap(); |
1175 | | } |
1176 | | |
1177 | | fn expect_invalid<T>(original: &T, result: &T, allow_lossy: bool, nan_converted_to_zero: bool) where T: ValidateResult { |
1178 | | assert!(original.validate_result( |
1179 | | result, |
1180 | | ValidationOptions { allow_lossy, nan_converted_to_zero }, |
1181 | | || String::new() |
1182 | | ).is_err()); |
1183 | | } |
1184 | | |
1185 | | #[test] |
1186 | | fn test_f32(){ |
1187 | | let original:&[f32] = &[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, -20.4, f32::NAN]; |
1188 | | let lossy:&[f32] = &[0.0, 0.2, 0.2, 0.3, 0.4, 0.5, -20.5, f32::NAN]; |
1189 | | |
1190 | | expect_valid(&original, &original, true, true); |
1191 | | expect_valid(&original, &original, true, false); |
1192 | | expect_valid(&original, &original, false, true); |
1193 | | expect_valid(&original, &original, false, false); |
1194 | | |
1195 | | expect_invalid(&original, &lossy, false, false); |
1196 | | expect_valid(&original, &lossy, true, false); |
1197 | | |
1198 | | expect_invalid(&original, &&original[..original.len()-2], true, true); |
1199 | | |
1200 | | // test relative comparison with some large values |
1201 | | expect_valid(&1_000_f32, &1_001_f32, true, false); |
1202 | | expect_invalid(&1_000_f32, &1_200_f32, true, false); |
1203 | | |
1204 | | expect_valid(&10_000_f32, &10_100_f32, true, false); |
1205 | | expect_invalid(&10_000_f32, &12_000_f32, true, false); |
1206 | | |
1207 | | expect_valid(&33_120_f32, &30_120_f32, true, false); |
1208 | | expect_invalid(&33_120_f32, &20_120_f32, true, false); |
1209 | | } |
1210 | | |
1211 | | #[test] |
1212 | | fn test_nan(){ |
1213 | | let original:&[f32] = &[ 0.0, f32::NAN, f32::NAN ]; |
1214 | | let lossy:&[f32] = &[ 0.0, f32::NAN, 0.0 ]; |
1215 | | |
1216 | | expect_valid(&original, &lossy, true, true); |
1217 | | expect_invalid(&lossy, &original, true, true); |
1218 | | |
1219 | | expect_valid(&lossy, &lossy, true, true); |
1220 | | expect_valid(&lossy, &lossy, false, true); |
1221 | | } |
1222 | | |
1223 | | #[test] |
1224 | | fn test_error(){ |
1225 | | |
1226 | | fn print_error<T: ValidateResult>(original: &T, lossy: &T, allow_lossy: bool){ |
1227 | | let message = original |
1228 | | .validate_result( |
1229 | | &lossy, |
1230 | | ValidationOptions { allow_lossy, .. Default::default() }, |
1231 | | || String::new() // type_name::<T>().to_string() |
1232 | | ) |
1233 | | .unwrap_err(); |
1234 | | |
1235 | | println!("message: {}", message); |
1236 | | } |
1237 | | |
1238 | | let original:&[f32] = &[ 0.0, f32::NAN, f32::NAN ]; |
1239 | | let lossy:&[f32] = &[ 0.0, f32::NAN, 0.0 ]; |
1240 | | print_error(&original, &lossy, false); |
1241 | | |
1242 | | print_error(&2.0, &1.0, true); |
1243 | | print_error(&2.0, &1.0, false); |
1244 | | |
1245 | | print_error(&FlatSamples::F32(vec![0.1,0.1]), &FlatSamples::F32(vec![0.1,0.2]), false); |
1246 | | print_error(&FlatSamples::U32(vec![0,0]), &FlatSamples::F32(vec![0.1,0.2]), false); |
1247 | | |
1248 | | { |
1249 | | let image = crate::prelude::read_all_data_from_file("tests/images/valid/openexr/MultiResolution/Kapaa.exr").unwrap(); |
1250 | | |
1251 | | let mut mutated = image.clone(); |
1252 | | let samples = mutated.layer_data.first_mut().unwrap() |
1253 | | .channel_data.list.first_mut().unwrap().sample_data.levels_as_slice_mut().first_mut().unwrap(); |
1254 | | |
1255 | | match samples { |
1256 | | FlatSamples::F16(vals) => vals[100] = vals[1], |
1257 | | FlatSamples::F32(vals) => vals[100] = vals[1], |
1258 | | FlatSamples::U32(vals) => vals[100] = vals[1], |
1259 | | } |
1260 | | |
1261 | | print_error(&image, &mutated, false); |
1262 | | } |
1263 | | |
1264 | | // TODO check out more nested behaviour! |
1265 | | } |
1266 | | |
1267 | | #[test] |
1268 | | fn test_uncompressed(){ |
1269 | | use crate::prelude::*; |
1270 | | |
1271 | | let original_pixels: [(f32,f32,f32); 4] = [ |
1272 | | (0.0, -1.1, PI), |
1273 | | (0.0, -1.1, TAU), |
1274 | | (0.0, -1.1, f32::EPSILON), |
1275 | | (f32::NAN, 10000.1, -1024.009), |
1276 | | ]; |
1277 | | |
1278 | | let mut file_bytes = Vec::new(); |
1279 | | let original_image = Image::from_encoded_channels( |
1280 | | (2,2), |
1281 | | Encoding { |
1282 | | compression: Compression::Uncompressed, |
1283 | | line_order: Increasing, // FIXME unspecified may be optimized to increasing, which destroys test eq |
1284 | | .. Encoding::default() |
1285 | | }, |
1286 | | SpecificChannels::rgb(PixelVec::new(Vec2(2,2), original_pixels.to_vec())) |
1287 | | ); |
1288 | | |
1289 | | original_image.write().to_buffered(Cursor::new(&mut file_bytes)).unwrap(); |
1290 | | |
1291 | | let lossy_image = read().no_deep_data().largest_resolution_level() |
1292 | | .rgb_channels(PixelVec::<(f32,f32,f32)>::constructor, PixelVec::set_pixel) |
1293 | | .first_valid_layer().all_attributes().from_buffered(Cursor::new(&file_bytes)).unwrap(); |
1294 | | |
1295 | | original_image.assert_equals_result(&original_image); |
1296 | | lossy_image.assert_equals_result(&lossy_image); |
1297 | | original_image.assert_equals_result(&lossy_image); |
1298 | | lossy_image.assert_equals_result(&original_image); |
1299 | | } |
1300 | | |
1301 | | #[test] |
1302 | | fn test_compiles(){ |
1303 | | use crate::prelude::*; |
1304 | | |
1305 | | fn accepts_validatable_value(_: &impl ValidateResult){} |
1306 | | |
1307 | | let object: Levels<FlatSamples> = Levels::Singular(FlatSamples::F32(Vec::default())); |
1308 | | accepts_validatable_value(&object); |
1309 | | |
1310 | | let object: AnyChannels<Levels<FlatSamples>> = AnyChannels::sort(SmallVec::default()); |
1311 | | accepts_validatable_value(&object); |
1312 | | |
1313 | | let layer: Layer<AnyChannels<Levels<FlatSamples>>> = Layer::new((0,0), Default::default(), Default::default(), object); |
1314 | | accepts_validatable_value(&layer); |
1315 | | |
1316 | | let layers: Layers<AnyChannels<Levels<FlatSamples>>> = Default::default(); |
1317 | | accepts_validatable_value(&layers); |
1318 | | |
1319 | | let object: Image<Layer<AnyChannels<Levels<FlatSamples>>>> = Image::from_layer(layer); |
1320 | | object.assert_equals_result(&object); |
1321 | | } |
1322 | | } |
1323 | | |
1324 | | |
1325 | | #[test] |
1326 | | fn test_nan_compression_attribute(){ |
1327 | | use crate::prelude::*; |
1328 | | use crate::prelude::Compression::*; |
1329 | | use std::io::Cursor; |
1330 | | use crate::image::pixel_vec::PixelVec; |
1331 | | use crate::prelude::LineOrder::Increasing; |
1332 | | |
1333 | | let all_compression_methods = [ |
1334 | | Uncompressed, RLE, ZIP1, ZIP16, PXR24, PIZ, B44, B44A, |
1335 | | ]; |
1336 | | |
1337 | | let original_pixels: [(f32,f32,f16); 4] = [ |
1338 | | (f32::NAN, f32::from_bits(0x7fc01234), f16::from_bits(0x7E01)), |
1339 | | (f32::NAN, f32::from_bits(0xffcabcde), f16::from_bits(0x7FFF)), |
1340 | | (f32::NAN, f32::from_bits(0x7f800001), f16::from_bits(0xFE01)), |
1341 | | (f32::NAN, f32::NAN, f16::NAN), |
1342 | | ]; |
1343 | | |
1344 | | assert!( |
1345 | | original_pixels.iter() |
1346 | | .all(|&(a,b,c)| a.is_nan() && b.is_nan() && c.is_nan()), |
1347 | | "test case has a bug" |
1348 | | ); |
1349 | | |
1350 | | for compression in all_compression_methods { |
1351 | | let mut file_bytes = Vec::new(); |
1352 | | |
1353 | | let original_image = Image::from_encoded_channels( |
1354 | | (2, 2), |
1355 | | Encoding { |
1356 | | compression, |
1357 | | line_order: Increasing, |
1358 | | ..Encoding::default() |
1359 | | }, |
1360 | | SpecificChannels::rgb(PixelVec::new((2, 2), original_pixels.to_vec())) |
1361 | | ); |
1362 | | |
1363 | | let result = original_image.write().to_buffered(Cursor::new(&mut file_bytes)); |
1364 | | if let Err(Error::NotSupported(_)) = result { continue; } |
1365 | | |
1366 | | let reconstructed_image = |
1367 | | read().no_deep_data().largest_resolution_level() |
1368 | | .rgb_channels(PixelVec::<(f32, f32, f16)>::constructor, PixelVec::set_pixel) |
1369 | | .first_valid_layer().all_attributes().from_buffered(Cursor::new(&file_bytes)).unwrap(); |
1370 | | |
1371 | | assert_eq!( |
1372 | | original_image.layer_data.channel_data.pixels.pixels.len(), |
1373 | | reconstructed_image.layer_data.channel_data.pixels.pixels.len() |
1374 | | ); |
1375 | | |
1376 | | let was_nanness_preserved = reconstructed_image.layer_data.channel_data.pixels.pixels |
1377 | | .iter().all(|(r,g,b)| r.is_nan() && g.is_nan() && b.is_nan()); |
1378 | | |
1379 | | assert_eq!( |
1380 | | was_nanness_preserved, compression.supports_nan(), |
1381 | | "{} nanness claims do not match real output", compression |
1382 | | ); |
1383 | | |
1384 | | let was_nan_pattern_preserved = reconstructed_image.layer_data.channel_data.pixels.pixels |
1385 | | .iter().zip(original_pixels.iter()) |
1386 | | .all(|((r2, g2, b2), (r1, g1, b1))| |
1387 | | r2.to_bits() == r1.to_bits() && |
1388 | | g2.to_bits() == g1.to_bits() && |
1389 | | b2.to_bits() == b1.to_bits() |
1390 | | ); |
1391 | | |
1392 | | assert_eq!( |
1393 | | was_nan_pattern_preserved, compression.preserves_nan_bits(), |
1394 | | "{} nan bit claims do not match real output", compression |
1395 | | ); |
1396 | | } |
1397 | | } |
1398 | | } |
1399 | | |
1400 | | |