Skip to content

Bring back the split out projects back into main project #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,3 @@ dev_bind.sh
.vscode
.devcontainer
*.code-workspace

# built crates
/openexr-sys
/openexr-rs
2 changes: 2 additions & 0 deletions openexr-rs/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
rustdocflags = [ "--html-in-header", "katex-header.html" ]
4 changes: 4 additions & 0 deletions openexr-rs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
Cargo.lock
/*.exr
.gdb_history
53 changes: 53 additions & 0 deletions openexr-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[package]
name = "openexr"
version = "0.11.0"
authors = ["Anders Langlands <anderslanglands@gmail.com>",
"Luke Titley <from+openexr_rs@luketitley.com>",
"Scott Wilson <scott@propersquid.com>",
"Tiago Carvalho <tiago.carvalho@loquattech.com>"]
edition = "2018"

license="BSD-3-Clause"
description = "High-level bindings to OpenEXR 3.0.5"
documentation = "https://docs.rs/openexr"
repository = "https://github.com/vfx-rs/openexr-rs"
readme = "README.md"
keywords = ["graphics", "image", "vfx", "exr", "openexr"]
categories = ["api-bindings", "computer-vision", "graphics", "multimedia", "rendering"]

[package.metadata.docs.rs]
# docs.rs uses a nightly compiler, so by instructing it to use our `doc-images` feature we
# ensure that it will render any images that we may have in inner attribute documentation.
# and include the markdown docs
features = ["long-form-docs"]
# KateX for math notation
rustdoc-args = [ "--html-in-header", "katex-header.html" ]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
openexr-sys = "^0.10.1"
imath-traits = "^0.4"
cgmath = {version = "^0.18", optional = true}
glam = {version = "^0.17", optional = true}
nalgebra = {version = "^0.27", optional = true}
nalgebra-glm = {version = "^0.12", optional = true}
half = "^1.7"
thiserror = "^1.0"
paste = "^1.0"
bitflags = "^1.2"
embed-doc-image = {version = "0.1.4", optional=true}
cfg-if = "^1.0"

[dev-dependencies]
png = "^0.16"
itertools = "^0.10"
lazy_static = "^1.4"

[features]
default = []
imath_cgmath = ["imath-traits/cgmath", "cgmath"]
imath_glam = ["imath-traits/glam", "glam"]
imath_nalgebra = ["imath-traits/nalgebra", "nalgebra"]
imath_nalgebra-glm = ["imath-traits/nalgebra-glm", "nalgebra-glm"]
long-form-docs = ["embed-doc-image"]
11 changes: 11 additions & 0 deletions openexr-rs/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Copyright (c) Contributors to the OpenEXR Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
166 changes: 166 additions & 0 deletions openexr-rs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# OpenEXR

## Bound version: 3.0.5

The openexr crate provides high-level bindings for the [ASWF OpenEXR library](https://github.com/AcademySoftwareFoundation/openexr),
which allows reading and writing files in the OpenEXR format (EXR standing
for **EX**tended **R**ange). The OpenEXR format is the de-facto standard
image storage format of the motion-picture industry.

The purpose of EXR format is to accurately and efficiently represent high-dynamic-range scene-linear image data and associated metadata, with strong support for multi-part, multi-channel use cases.

OpenEXR is widely used in host application software where accuracy is critical, such as photorealistic rendering, texture access, image compositing, deep compositing, and DI.
OpenEXR is a project of the [Academy Software Foundation](https://www.aswf.io/). The format and library were originally developed by Industrial Light & Magic and first released in 2003. Weta Digital, Walt Disney Animation Studios, Sony Pictures Imageworks, Pixar Animation Studios, DreamWorks, and other studios, companies, and individuals have made contributions to the code base.

OpenEXR is included in the [VFX Reference Platform](https://vfxplatform.com/).

The openexr crate is maintained by [the vfx-rs project](https://github.com/vfx-rs).

# Quick Start

To use the included C++ OpenEXR source:

```bash
cargo add openexr
cargo build
```

While this method is supported and easiest to get starter, it is strongly
recommended that you build and install the C++ library separately and build
the crate against it like so:

```bash
cargo add openexr
CMAKE_PREFIX_PATH=/path/to/cmake/configs cargo build
```

Note that you must take care to ensure that the version of OpenEXR you are
pointing it to is the same as that for this version of the crate, otherwise
you will encounter linker errors since all OpenEXR symbols are versioned.

The [`prelude`](crate::prelude) pulls in the set of types that you
need for basic file I/O of RGBA and arbitrary channel images:

```rust
use openexr::prelude::*;

fn write_rgba1(filename: &str, pixels: &[Rgba], width: i32, height: i32)
-> Result<(), Box<dyn std::error::Error>> {
let header = Header::from_dimensions(width, height);
let mut file = RgbaOutputFile::new(
filename,
&header,
RgbaChannels::WriteRgba,
1,
)?;

file.set_frame_buffer(&pixels, 1, width as usize)?;
file.write_pixels(height)?;

Ok(())
}

fn read_rgba1(path: &str) -> Result<(), Box<dyn std::error::Error>> {
use imath_traits::Zero;

let mut file = RgbaInputFile::new(path, 1).unwrap();
// Note that windows in OpenEXR are ***inclusive*** bounds, so a
// 1920x1080 image has window [0, 0, 1919, 1079].
let data_window: [i32; 4] = *file.header().data_window();
let width = data_window.width() + 1;
let height = data_window.height() + 1;

let mut pixels = vec![Rgba::zero(); (width * height) as usize];
file.set_frame_buffer(&mut pixels, 1, width as usize)?;
file.read_pixels(0, height - 1)?;

Ok(())
}
```

Beyond that, types related to deep images are in the [`deep`](crate::deep)
module, and tiled images are in the [`tiled`](crate::tiled) module.

The [Reading and Writing OpenEXR Image Files](crate::doc::reading_and_writing_image_files)
document is a great place to start to explore the full functionality of the
crate. It contains example usage for nearly everything.

# Math Crate Interoperability
OpenEXR (and much of the rest of the VFX ecosystem) relies on Imath for basic
math primitives like vectors and bounding boxes.

Rust already has several mature crates for linear algebra targetting graphics
such as [cgmath](https://crates.io/crates/cgmath), [nalgebra](https://crates.io/crates/nalgebra), [nalgebra-glm](https://crates.io/crates/nalgebra-glm) and [glam](https://crates.io/crates/glam). Rather than adding yet another
contender to this crowded field, we instead provide a set of traits that allow
any of these crates to be used with openexr in the form of [imath-traits](https://crates.io/crates/imath-traits). By default, these traits are implemented for arrays and slices, so you will find that the examples in this documentation will tend to use e.g. `[i32; 4]` for bounding boxes:

```rust
use openexr::prelude::*;
fn read_rgba1(path: &str) -> Result<(), Box<dyn std::error::Error>> {
use imath_traits::Zero;
let mut file = RgbaInputFile::new(path, 1).unwrap();
let data_window = file.header().data_window::<[i32; 4]>().clone();
let width = data_window.width() + 1;
let height = data_window.height() + 1;
Ok(())
}
```

To use your preffered math crate instead, simply enable the corresponding feature on openexr,
which will be `imath_<name>`, for example:

```bash
cargo build --features=imath_cgmath
```

Now you can use types from that crate together with openexr seamlessly. In
the case that the math crate does not provide a bounding box type, one will
be available as `imath_traits::Box2i` and `imath_traits::Box3i`.

```rust
use openexr::prelude::*;
#[cfg(feature = "imath_cgmath")]
fn read_rgba1(path: &str) -> Result<(), Box<dyn std::error::Error>> {
use imath_traits::Zero;
use imath_traits::Box2i;

let mut file = RgbaInputFile::new(path, 1).unwrap();
let data_window: Box2i = *file.header().data_window();
let width = data_window.width() + 1;
let height = data_window.height() + 1;
Ok(())
}
```

# Safety
Some parts of the OpenEXR API are not easily representable in safe Rust.
Notably, the interaction between [`Slice`](crate::core::frame_buffer::Slice), [`FrameBuffer`](crate::core::frame_buffer::FrameBuffer) and the various
File types essentially creates a chain of (mutable) references between the
memory backing the pixel data and the file.

Representing this with lifetimes such that the Rust compiler could track it
would make working with these types extremely unwieldy, so instead the
methods which actually use the pointers in slices and framebuffers are
marked unsafe. It is the caller's responsibility to make sure that the
pointers inserted into the framebuffer are still valid when the image is
read or written:

```rust
// set_frame_buffer internally stores a slice in a frame buffer on the file
// struct. pixels must live until the write_pixels call below has completed.
file.set_frame_buffer(&pixels, 1, width as usize)?;
unsafe {
file.write_pixels(height)?;
}
```

# Features
* High dynamic range and color precision.
* Support for 16-bit floating-point, 32-bit floating-point, and 32-bit integer pixels.
* Multiple image compression algorithms, both lossless and lossy. Some of the included codecs can achieve 2:1 lossless compression ratios on images with film grain. The lossy codecs have been tuned for visual quality and decoding performance.
* Extensibility. New image attributes (strings, vectors, integers, etc.) can be added to OpenEXR image headers without affecting backward compatibility with existing OpenEXR applications.
* Support for stereoscopic image workflows and a generalization to multi-views.
* Flexible support for deep data: pixels can store a variable-length list of samples and, thus, it is possible to store multiple values at different depths for each pixel. Hard surfaces and volumetric data representations are accommodated.
* Multipart: ability to encode separate, but related, images in one file. This allows for access to individual parts without the need to read other parts in the file.


62 changes: 62 additions & 0 deletions openexr-rs/examples/merge_overlapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
fn merge_overlapping(
a1: f32,
c1: f32, // Opacity and color of first sample
a2: f32,
c2: f32, // Opacity and color of second sample
) -> (f32, f32) {
// This function merges two perfectly overlapping volume or point
// samples. Given the color and opacity of two samples, it returns
// the color and opacity of the merged sample.
//
// The code below is written to avoid very large rounding errors when
// the opacity of one or both samples is very small:
//
// * The merged opacity must not be computed as 1 - (1-a1) * (1-a2).
// If a1 and a2 are less than about half a floating-point epsilon,
// the expressions (1-a1) and (1-a2) evaluate to 1.0 exactly, and the
// merged opacity becomes 0.0. The error is amplified later in the
// calculation of the merged color.
//
// Changing the calculation of the merged opacity to a1 + a2 - a1\*a2
// avoids the excessive rounding error.
//
// * For small x, the logarithm of 1+x is approximately equal to x,
// but log(1+x) returns 0 because 1+x evaluates to 1.0 exactly.
// This can lead to large errors in the calculation of the merged
// color if a1 or a2 is very small.
//
// x.ln_1p() returns the logarithm of 1+x, but without attempting to
// evaluate the expression 1+x when x is very small.
//
let a1 = a1.clamp(0.0, 1.0);
let a2 = a2.clamp(0.0, 1.0);
let am = a1 + a2 - a1 * a2;
if a1 == 1.0 && a2 == 1.0 {
(am, (c1 + c2) / 2.0)
} else if a1 == 1.0 {
(am, c1)
} else if a2 == 1.0 {
(am, c2)
} else {
let u1 = -((-a1).ln_1p());
let v1 = if u1 < a1 * f32::MAX { u1 / a1 } else { 1.0 };
let u2 = -((-a2).ln_1p());
let v2 = if u2 < a2 * f32::MAX { u2 / a2 } else { 1.0 };
let u = u1 + u2;
let w = if u > 1.0 || am < u * f32::MAX {
am / u
} else {
1.0
};
(am, (c1 * v1 + c2 * v2) * w)
}
}

fn main() {
let a1 = 0.5f32;
let c1 = 0.2f32;
let a2 = 0.3f32;
let c2 = 0.4f32;

assert_eq!(merge_overlapping(a1, c1, a2, c2), (0.65, 0.46611378))
}
84 changes: 84 additions & 0 deletions openexr-rs/examples/split_volume.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
fn split_volume_sample(
a: f32,
c: f32, // opacity and colour of original sample
zf: f32,
zb: f32, // front and back of original sample
z: f32, // position of split
) -> ((f32, f32), (f32, f32)) {
// Given a volume sample whose front and back are at depths zf and zb
// respectively, split the sample at depth z. Return the opacities
// and colors of the two parts that result from the split.
//
// The code below is written to avoid excessive rounding errors when
// the opacity of the original sample is very small:
//
// The straightforward computation of the opacity of either part
// requires evaluating an expression of the form
//
// 1.0 - (1.0 - a).pow(x)
//
// However, if a is very small, then 1-a evaluates to 1.0 exactly,
// and the entire expression evaluates to 0.0.
//
// We can avoid this by rewriting the expression as
//
// 1.0 - (x * (1.0 - a).log()).exp()
//
// and replacing the call to log() with a call to the method ln_1p(),
// which computes the logarithm of 1+x without attempting to evaluate
// the expression 1+x when x is very small.
//
// Now we have
//
// 1.0 - (x * (-a).ln_1p()).exp()
//
// However, if a is very small then the call to exp() returns 1.0, and
// the overall expression still evaluates to 0.0. We can avoid that
// by replacing the call to exp() with a call to expm1():
//
// -(x * (-a).ln_1p()).exp_m1()
//
// x.exp_m1() computes exp(x) - 1 in such a way that the result is accurate
// even if x is very small.
//
assert!(zb > zf && z >= zf && z <= zb);

let a = a.clamp(0.0, 1.0);

if a == 1.0f32 {
((1.0f32, c), (1.0f32, c))
} else {
let xf = (z - zf) / (zb - zf);
let xb = (zb - z) / (zb - zf);
if a > f32::MIN_POSITIVE {
//let af = -expm1 (xf * log1p (-a));
let af = -((-a).ln_1p() * xf).exp_m1();
let cf = (af / a) * c;
//ab = -expm1 (xb * log1p (-a));
let ab = -((-a).ln_1p() * xb).exp_m1();
let cb = (ab / a) * c;
((af, cf), (ab, cb))
} else {
((a * xf, c * xf), (a * xb, c * xb))
}
}
}

fn main() {
let a = 0.5f32;
let c = 1.0f32;
let zf = 0.0f32;
let zb = 1.0f32;

assert_eq!(
split_volume_sample(a, c, zf, zb, 0.5),
((0.29289323, 0.58578646), (0.29289323, 0.58578646))
);
assert_eq!(
split_volume_sample(a, c, zf, zb, 1.0e-7),
(
(0.000000069314716, 0.00000013862943),
(0.49999997, 0.99999994)
)
);
}
Binary file added openexr-rs/images/comp_piz.exr
Binary file not shown.
Binary file added openexr-rs/images/custom_attributes.exr
Binary file not shown.
Binary file added openexr-rs/images/deep_cyl.exr
Binary file not shown.
Binary file added openexr-rs/images/deep_plane.exr
Binary file not shown.
Binary file added openexr-rs/images/ferris-multipart.exr
Binary file not shown.
Binary file added openexr-rs/images/ferris-preview.exr
Binary file not shown.
Binary file added openexr-rs/images/ferris-tiled.exr
Binary file not shown.
Binary file added openexr-rs/images/ferris.exr
Binary file not shown.
Binary file added openexr-rs/images/ferris.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added openexr-rs/images/window.exr
Binary file not shown.
Loading