summaryrefslogtreecommitdiff
path: root/vendor/exr/GUIDE.md
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/exr/GUIDE.md
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/exr/GUIDE.md')
-rw-r--r--vendor/exr/GUIDE.md588
1 files changed, 588 insertions, 0 deletions
diff --git a/vendor/exr/GUIDE.md b/vendor/exr/GUIDE.md
new file mode 100644
index 0000000..4f3cfaa
--- /dev/null
+++ b/vendor/exr/GUIDE.md
@@ -0,0 +1,588 @@
+# 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.
+