Coverage Report

Created: 2026-08-13 08:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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)>>>>::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#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::new
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)>>>>::new
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)>>>>::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_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#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>>>::from_layer
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_layer
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_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
}