diff options
Diffstat (limited to 'vendor/exr/GUIDE.md')
-rw-r--r-- | vendor/exr/GUIDE.md | 588 |
1 files changed, 0 insertions, 588 deletions
diff --git a/vendor/exr/GUIDE.md b/vendor/exr/GUIDE.md deleted file mode 100644 index 4f3cfaa..0000000 --- a/vendor/exr/GUIDE.md +++ /dev/null @@ -1,588 +0,0 @@ -# Guide - -This document talks about the capabilities of OpenEXR and outlines the design of this library. -In addition to reading this guide, you should also have a look at the examples. - -Contents: -- Wording -- Why this is complicated -- One-liners for reading and writing simple images -- Reading a complex image -- The Image data structure -- Writing a complex image - -## Wording -Some names in this library differ from the classic OpenEXR conventions. -For example, an OpenEXR "multipart" is called a file with multiple "layers" in this library. -The old OpenEXR "layers" are called "grouped channels" instead. - -- `Image` Contains everything that an `.exr` file can contain. Includes metadata and multiple layers. -- `Layer` A grid of pixels that can be placed anywhere on the two-dimensional canvas -- `Channel` All samples of a single color component, such as red or blue. Also contains metadata. -- `Pixel` The color at an exact location in the image. Contains one sample for each channel. -- `Sample` The value (either f16, f32 or u32) of one channel at an exact location in the image. - Usually a simple number, such as the red value of the bottom left pixel. -- `Grouped Channels` Multiple channels may be grouped my prepending the same prefix to the name. - This behaviour is opt-in; it has to be enabled explicitly: - By default, channels are stored in a plain list, and channel names are unmodified. -- `pedantic: bool` When reading, pedantic being false will generally ignore - invalid information instead of aborting the reading process where possible. - When writing, pedantic being false will generally skip some expensive image validation checks. - -## OpenEXR | Complexity -This image format supports some features that you won't find in other image formats. -As a consequence, an exr file cannot necessarily be converted to other formats, -even when the loss of precision is acceptable. Furthermore, -an arbitrary exr image may include possibly unwanted data. -Supporting deep data, for example, might be unnecessary for some applications. - -To read an image, `exrs` must know which parts of an image you want to end up with, -and which parts of the file should be skipped. That's why you need -a little more code to read an exr file, compared to simpler file formats. - -### Possibly Undesired Features -- Arbitrary Channels: - `CMYK`, `YCbCr`, `LAB`, `XYZ` channels might not be interesting for you, - maybe you only want to accept `RGBA` images -- Deep Data: Multiple colors per pixel might not be interesting for you -- Resolution Levels: Mip Maps or Rip Maps might be unnecessary and can be skipped, - loading only the full resolution image instead -<!-- - TODO: Meta Data: Skip reading meta data --> - -# Simple Reading and Writing -There are a few very simple functions for the most common use cases. -For decoding an image file, use one of these functions -from the `exr::image::read` module (data structure complexity increasing): - -1. `read_first_rgba_layer_from_file(path, your_constructor, your_pixel_setter)` -1. `read_all_rgba_layers_from_file(path, your_constructor, your_pixel_setter)` -1. `read_first_flat_layer_from_file(path)` -1. `read_all_flat_layers_from_file(path)` -1. `read_all_data_from_file(path)` - -If you don't have a file path, or want to load any other channels than `rgba`, -then these simple functions will not suffice. The more complex approaches are -described later in this document. - -For encoding an image file, use one of these functions in the `exr::image::write` module: - -1. `write_rgba_file(path, width, height, |x,y| my_image.get_rgb_at(x,y))` -1. `write_rgb_file(path, width, height, |x,y| my_image.get_rgba_at(x,y))` - -These functions are only syntactic sugar. If you want to customize the data type, -the compression method, or write multiple layers, these simple functions will not suffice. -Again, the more complex approaches are described in the following paragraph. - -# Reading an Image - -Reading an image involves three steps: -1. Specify how to load an image by constructing an image reader. - 1. Start with `read()` - 1. Chain method calls to customize the reader -1. Call `from_file(path)`, `from_buffered(bytes)`, or `from_unbuffered(bytes)` - on the reader to actually load an image -1. Process the resulting image data structure or the error in your application - -The type of the resulting image depends on the reader you constructed. For example, -if you configure the reader to load mip map levels, the resulting image type -will contain an additional vector with the mip map levels. - -### Deep Data -The first choice to be made is whether you want to load deep data or not. -Deep data is where multiple colors are stored in one pixel at the same location. -Currently, deep data is not supported yet, so we always call `no_deep_data()`. - -```rust -fn main(){ - use exr::prelude::*; - let reader = read().no_deep_data(); -} -``` - -### Resolution Levels -Decide whether you want to load the largest resolution level, or all Mip Maps from the file. -Loading only the largest level actually skips portions of the image, which should be faster. - -Calling `largest_resolution_level()` will result in a single image (`FlatSamples`), -whereas calling `all_resolution_levels()` will result in multiple levels `Levels<FlatSamples>`. - -```rust -fn main(){ - use exr::prelude::*; - let reader = read().no_deep_data().largest_resolution_level(); - let reader = read().no_deep_data().all_resolution_levels(); -} -``` - -### Channels -Decide whether you want to load all channels in a dynamic list, or only load a fixed set of channels. - -Calling `all_channels()` will result in a `Vec<Channel<_>>`. - -```rust -fn main(){ - use exr::prelude::*; - let reader = read().no_deep_data().largest_resolution_level().all_channels(); -} -``` - -The alternative, `specific_channels()` allows you to exactly specify which channels should be loaded. -The usage follows the same builder pattern as the rest of the library. - -First, call `specific_channels()`. Then, for each channel you desire, -call either `required(channel_name)` or `optional(channel_name, default_value)`. -At last, call `collect_pixels()` to define how the pixels should be stored in an image. -This additional mechanism will not simply store the pixels in a `Vec<Pixel>`, but instead -works with a closure. This allows you to instantiate your own existing image type with -the pixel data from the file. - -```rust -fn main(){ - use exr::prelude::*; - - let reader = read() - .no_deep_data().largest_resolution_level() - - // load LAB channels, with chroma being optional - .specific_channels().required("L").optional("A", 0.0).optional("B", 0.0).collect_pixels( - - // create our image based on the resolution of the file - |resolution: Vec2<usize>, (l,a,b): &(ChannelDescription, Option<ChannelDescription>, Option<ChannelDescription>)|{ - if a.is_some() && b.is_some() { MyImage::new_lab(resolution) } - else { MyImage::new_luma(resolution) } - }, - - // insert a single pixel into out image - |my_image: &mut MyImage, position: Vec<usize>, (l,a,b): (f32, f16, f16)|{ - my_image.set_pixel_at(position.x(), position.y(), (l, a, b)); - } - - ); -} -``` - -The first closure is the constructor of your image, and the second closure is the setter for a single pixel in your image. -The tuple containing the channel descriptions and the pixel tuple depend on the channels that you defined earlier. -In this example, as we defined to load L,A and B, each pixel has three values. The arguments of the closure -can usually be inferred, so you don't need to declare the type of your image and the `Vec2<usize>`. -However, the type of the pixel needs to be defined. In this example, we define the pixel type to be `(f32, f16, f16)`. -All luma values will be converted to `f32` and all chroma values will be converted to `f16`. -The pixel type can be any combination of `f16`, `f32`, `u32` or `Sample` values, in a tuple with as many entries as there are channels. -The `Sample` type is a dynamic enum over the other types, which allows you to keep the original sample type of each image. - -_Note: Currently, up to 32 channels are supported, which is an implementation problem. -Open an issue if this is not enough for your use case. Alternatively, -you can always use `all_channels()`, which has no limitations._ - -####RGBA Channels -For rgba images, there is a predefined simpler alternative to `specific_channels` called `rgb_channels` and `rgba_channels`. -It works just the same as `specific_channels` and , but you don't need to specify the names of the channels explicitly. - -```rust -fn main(){ - use exr::prelude::*; - - let reader = read() - .no_deep_data().largest_resolution_level() - - // load rgba channels - // with alpha being optional, defaulting to 1.0 - .rgba_channels( - - // create our image based on the resolution of the file - |resolution, &(r,g,b,a)|{ - if a.is_some() { MyImage::new_with_alpha(resolution.x(), resolution.y()) } - else { MyImage::new_without_alpha(resolution.x(), resolution.y()) } - }, - - // insert a single pixel into out image - |my_image, position, (r,g,b,a): (f32, f32, f32, f16)|{ - my_image.set_pixel_at(position.x(), position.y(), (r,g,b,a)); - } - - ); -} -``` - - -### Layers -Use `all_layers()` to load a `Vec<Layer<_>>` or use `first_valid_layer()` to only load -the first `Layer<_>` that matches the previously defined requirements -(for example, the first layer without deep data and cmyk channels). - - -```rust -fn main() { - use exr::prelude::*; - - let image = read() - .no_deep_data().largest_resolution_level() - .all_channels().all_layers(); - - let image = read() - .no_deep_data().largest_resolution_level() - .all_channels().first_valid_layer(); -} -``` - -### Attributes -Currently, the only option is to load all attributes by calling `all_attributes()`. - -### Progress Notification -This library allows you to listen for the file reading progress by calling `on_progress(callback)`. -If you don't need this, you can just omit this call. - -```rust -fn main() { - use exr::prelude::*; - - let image = read().no_deep_data().largest_resolution_level() - .all_channels().first_valid_layer().all_attributes() - .on_progress(|progress: f64| println!("progress: {:.3}", progress)); -} -``` - -### Parallel Decompression -By default, this library uses all the available CPU cores if the pixels are compressed. -You can disable this behaviour by additionally calling `non_parallel()`. - -```rust -fn main() { -use exr::prelude::*; - - let image = read().no_deep_data().largest_resolution_level() - .all_channels().first_valid_layer().all_attributes() - .non_parallel(); -} -``` - -### Byte Sources -Any `std::io::Read` byte source can be used as input. However, this library also offers a simplification for files. -Call `from_file(path)` to load an image from a file. Internally, this wraps the file in a buffered reader. -Alternatively, you can call `from_buffered` or `from_unbuffered` (which wraps your reader in a buffered reader) to read an image. - -```rust -fn main() { -use exr::prelude::*; - - let read = read().no_deep_data().largest_resolution_level() - .all_channels().first_valid_layer().all_attributes(); - - let image = read.clone().from_file("D:/images/file.exr"); // also accepts `Path` and `PathBuf` and `String` - let image = read.clone().from_unbuffered(web_socket); - let image = read.clone().from_buffered(Cursor::new(byte_vec)); -} -``` - -### Results and Errors -The type of image returned depends on the options you picked. -The image is wrapped in a `Result<..., exr::error::Error>`. -This error type allows you to differentiate between three types of errors: -- `Error::Io(std::io::Error)` for file system errors (for example, "file does not exist" or "missing access rights") -- `Error::NotSupported(str)` for files that may be valid but contain features that are not supported yet -- `Error::Invalid(str)` for files that do not contain a valid exr image (files that are not exr or damaged exr) - -## Full Example -Loading all channels from the file: -```rust -fn main() { - use exr::prelude::*; - - // the type of the this image depends on the chosen options - let image = read() - .no_deep_data() // (currently required) - .largest_resolution_level() // or `all_resolution_levels()` - .all_channels() // or `rgba_channels` or `specific_channels() ...` - .all_layers() // or `first_valid_layer()` - .all_attributes() // (currently required) - .on_progress(|progress| println!("progress: {:.1}", progress * 100.0)) // optional - //.non_parallel() // optional. discouraged. just leave this line out - .from_file("image.exr").unwrap(); // or `from_buffered(my_byte_slice)` -} -``` - - -# The `Image` Data Structure - -For great flexibility, this crate does not offer a plain data structure to represent an exr image. -Instead, the `Image` data type has a generic parameter, allowing for different image contents. - -```rust -fn main(){ - // this image contains only a single layer - let single_layer_image: Image<Layer<_>> = Image::from_layer(my_layer); - - // this image contains an arbitrary number of layers (notice the S for plural on `Layers`) - let multi_layer_image: Image<Layers<_>> = Image::new(attributes, smallvec![ layer1, layer2 ]); - - // this image can contain the compile-time specified channels - let single_layer_rgb_image : Image<Layer<SpecificChannels<_, _>>> = Image::from_layer(Layer::new( - dimensions, attributes, encoding, - RgbaChannels::new(sample_types, rgba_pixels) - )); - - // this image can contain all channels from a file, even unexpected ones - let single_layer_image : Image<Layer<AnyChannels<_>>> = Image::from_layer(Layer::new( - dimensions, attributes, encoding, - AnyChannels::sort(smallvec![ channel_x, channel_y, channel_z ]) - )); - -} -``` - -The following pseudo code illustrates the image data structure. -The image should always be constructed using the constructor functions such as `Image::new(...)`, -because these functions watch out for invalid image contents. - -``` -Image { - attributes: ImageAttributes, - - // the layer data can be either a single layer a list of layers - layer_data: Layer | SmallVec<Layer> | Vec<Layer> | &[Layer] (writing only), -} - -Layer { - - // the channel data can either be a fixed set of known channels, or a dynamic list of arbitrary channels - channel_data: SpecificChannels | AnyChannels, - - attributes: LayerAttributes, - size: Vec2<usize>, - encoding: Encoding, -} - -SpecificChannels { - channels: [any tuple containing `ChannelDescription` or `Option<ChannelDescription>`], - - // the storage is usually a closure or a custom type which implements the `GetPixel` trait - storage: impl GetPixel | impl Fn(Vec2<usize>) -> Pixel, - where Pixel = any tuple containing f16 or f32 or u32 values -} - -AnyChannels { - list: SmallVec<AnyChannel> -} - -AnyChannel { - name: Text, - sample_data: FlatSamples | Levels, - quantize_linearly: bool, - sampling: Vec2<usize>, -} - -Levels = Singular(FlatSamples) | Mip(FlatSamples) | Rip(FlatSamples) -FlatSamples = F16(Vec<f16>) | F32(Vec<f32>) | U32(Vec<u32>) -``` - -As a consequence, one of the simpler image types is `Image<Layer<AnyChannels<FlatSamples>>>`. If you -enable loading multiple resolution levels, you will instead get the type `Image<Layer<AnyChannels<Levels<FlatSamples>>>>`. - -While you can put anything inside an image, -it can only be written if the content of the image implements certain traits. -This allows you to potentially write your own channel storage system. - - -# Writing an Image - -Writing an image involves three steps: -1. Construct the image data structure, starting with an `exrs::image::Image` -1. Call `image_data.write()` to obtain an image writer -1. Customize the writer, for example in order to listen for the progress -1. Write the image by calling `to_file(path)`, `to_buffered(bytes)`, or `to_unbuffered(bytes)` on the reader - - -### Image -You will currently need an `Image<_>` at the top level. The type parameter is the type of layer. - -The following variants are recommended: -- `Image::from_channels(resolution, channels)` where the pixel data must be `SpecificChannels` or `AnyChannels`. -- `Image::from_layer(layer)` where the layer data must be one `Layer`. -- `Image::empty(attributes).with_layer(layer1).with_layer(layer2)...` where the two layers can have different types -- `Image::new(image_attributes, layer_data)` where the layer data can be `Layers` or `Layer`. -- `Image::from_layers(image_attributes, layer_vec)` where the layer data can be `Layers`. - -```rust -fn main() { - use exr::prelude::*; - - // single layer constructors - let image = Image::from_layer(layer); - let image = Image::from_channels(resolution, channels); - - // use this if the layers have different types - let image = Image::empty(attributes).with_layer(layer1).with_layer(layer2); - - // use this if the layers have the same type and the above method does not work for you - let image = Image::from_layers(attributes, smallvec![ layer1, layer2 ]); - - // this constructor accepts any layers object if it implements a certain trait, use this for custom layers - let image = Image::new(attributes, layers); - - - // create an image writer - image.write() - - // print progress (optional, you can remove this line) - .on_progress(|progress:f64| println!("progress: {:.3}", progress)) - - // use only a single cpu (optional, you should remove this line) - // .non_parallel() - - // alternatively call to_buffered() or to_unbuffered() - // the file path can be str, String, Path, PathBuf - .to_file(path); -} -``` - -### Layers -The simple way to create layers is to use `Layers<_>` or `Layer<_>`. -The type parameter is the type of channels. - -Use `Layer::new(resolution, attributes, encoding, channels)` to create a layer. -Alternatively, use `smallvec![ layer1, layer2 ]` to create `Layers<_>`, which is a type alias for a list of layers. - -```rust -fn main() { - use exr::prelude::*; - - let layer = Layer::new( - (1024, 800), - LayerAttributes::named("first layer"), // name required, other attributes optional - Encoding::FAST_LOSSLESS, // or Encoding { .. } or Encoding::default() - channels - ); - - let image = Image::from_layer(layer); -} -``` - - -### Channels -You can create either `SpecificChannels` to write a fixed set of channels, or `AnyChannels` for a dynamic list of channels. - -```rust -fn main() { - use exr::prelude::*; - - let channels = AnyChannels::sort(smallvec![ channel1, channel2, channel3 ]); - let image = Image::from_channels((1024, 800), channels); -} -``` - -Alternatively, write specific channels. Start with `SpecificChannels::build()`, -then call `with_channel(name)` as many times as desired, then call `collect_pixels(..)` to define the colors. -You need to provide a closure that defines the content of the channels: Given the pixel location, -return a tuple with one element per channel. The tuple can contain `f16`, `f32` or `u32` values, -which then will be written to the file, without converting any value to a different type. - -```rust -fn main() { - use exr::prelude::*; - - let channels = SpecificChannels::build() - .with_channel("L").with_channel("B") - .with_pixel_fn(|position: Vec2<usize>| { - let (l, b) = my_image.lookup_color_at(position.x(), position.y()); - (l as f32, f16::from_f32(b)) - }); - - let image = Image::from_channels((1024, 800), channels); -} -``` - -#### RGB, RGBA -There is an even simpler alternative for rgba images, namely `SpecificChannels::rgb` and `SpecificChannels::rgba`: -This is mostly the same as the `SpecificChannels::build` option. - -The rgb method works with three channels per pixel, -whereas the rgba method works with four channels per pixel. The default alpha value of `1.0` will be used -if the image does not contain alpha. -```rust -fn main() { - use exr::prelude::*; - - let channels = SpecificChannels::rgba(|_position| - (0.4_f32, 0.2_f32, 0.1_f32, f16::ONE) - ); - - let channels = SpecificChannels::rgb(|_position| - (0.4_f32, 0.2_f32, 0.1_f32) - ); - - let image = Image::from_channels((1024, 800), channels); -} -``` - -### Channel -The type `AnyChannel` can describe every possible channel and contains all its samples for this layer. -Use `AnyChannel::new(channel_name, sample_data)` or `AnyChannel { .. }`. -The samples can currently only be `FlatSamples` or `Levels<FlatSamples>`, and in the future might be `DeepSamples`. - -### Samples -Currently, only flat samples are supported. These do not contain deep data. -Construct flat samples directly using `FlatSamples::F16(samples_vec)`, `FlatSamples::F32(samples_vec)`, or `FlatSamples::U32(samples_vec)`. -The vector contains all samples of the layer, row by row (from top to bottom), from left to right. - -### Levels -Optionally include Mip Maps or Rip Maps. -Construct directly using `Levels::Singular(flat_samples)` or `Levels::Mip { .. }` or `Levels::Rip { .. }`. -Put this into the channel, for example`AnyChannel::new("R", Levels::Singular(FlatSamples::F32(vec)))`. - -## Full example -Writing a flexible list of channels: -```rust -fn main(){ - // construct an image to write - let image = Image::from_layer( - Layer::new( // the only layer in this image - (1920, 1080), // resolution - LayerAttributes::named("main-rgb-layer"), // the layer has a name and other properties - Encoding::FAST_LOSSLESS, // compress slightly - AnyChannels::sort(smallvec![ // the channels contain the actual pixel data - AnyChannel::new("R", FlatSamples::F32(vec![0.6; 1920*1080 ])), // this channel contains all red values - AnyChannel::new("G", FlatSamples::F32(vec![0.7; 1920*1080 ])), // this channel contains all green values - AnyChannel::new("B", FlatSamples::F32(vec![0.9; 1920*1080 ])), // this channel contains all blue values - ]), - ) - ); - - image.write() - .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) // optional - .to_file("image.exr").unwrap(); -} -``` - - -### Pixel Closures -When working with specific channels, the data is not stored directly. -Instead, you provide a closure that stores or loads pixels in your existing image data structure. - -If you really do not want to provide your own storage, you can use the predefined structures from -`exr::image::pixel_vec`, such as `PixelVec<(f32,f32,f16)>` or `create_pixel_vec`. -Use this only if you don't already have a pixel storage. - -```rust -fn main(){ - let read = read() - .no_deep_data().largest_resolution_level() - .rgba_channels( - PixelVec::<(f32,f32,f32,f16)>::constructor, // how to create an image - PixelVec::set_pixel, // how to update a single pixel in the image - )/* ... */; -} -``` - - -## Low Level Operations -The image abstraction builds up on some low level code. -You can use this low level directly, -as shown in the examples `custom_write.rs` and `custom_read.rs`. -This allows you to work with -raw OpenEXR pixel blocks and chunks directly, -or use custom parallelization mechanisms. - -You can find these low level operations in the `exr::block` module. -Start with the `block::read(...)` -and `block::write(...)` functions. - |