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