use crate::{
core::{
cppstd::CppString,
error::Error,
frame_buffer::FrameBufferRef,
header::{Header, HeaderRef},
preview_image::PreviewRgba,
Compression, LevelMode, LevelRoundingMode, LineOrder,
},
rgba::rgba::{Rgba, RgbaChannels},
};
use imath_traits::{Bound2, Vec2};
use openexr_sys as sys;
use std::ffi::CString;
use std::path::Path;
type Result<T, E = Error> = std::result::Result<T, E>;
#[repr(transparent)]
pub struct TiledRgbaOutputFile(pub(crate) *mut sys::Imf_TiledRgbaOutputFile_t);
impl TiledRgbaOutputFile {
#[allow(clippy::too_many_arguments)]
pub fn new<P: AsRef<Path>>(
filename: P,
header: &Header,
channels: RgbaChannels,
tile_x_size: i32,
tile_y_size: i32,
mode: LevelMode,
rounding_mode: LevelRoundingMode,
num_threads: i32,
) -> Result<TiledRgbaOutputFile> {
let c_filename = CString::new(
filename
.as_ref()
.to_str()
.expect("Invalid bytes in filename"),
)
.expect("Internal null bytes in filename");
let mut _inner = std::ptr::null_mut();
unsafe {
sys::Imf_TiledRgbaOutputFile_ctor(
&mut _inner,
c_filename.as_ptr(),
header.0.as_ref(),
channels.into(),
tile_x_size,
tile_y_size,
mode.into(),
rounding_mode.into(),
num_threads,
)
.into_result()?;
}
Ok(TiledRgbaOutputFile(_inner))
}
#[allow(clippy::too_many_arguments)]
pub fn with_dimensions<P: AsRef<Path>, V>(
filename: P,
width: i32,
height: i32,
channels: RgbaChannels,
tile_x_size: i32,
tile_y_size: i32,
mode: LevelMode,
rounding_mode: LevelRoundingMode,
pixel_aspect_ratio: f32,
screen_window_center: V,
screen_window_width: f32,
line_order: LineOrder,
compression: Compression,
num_threads: i32,
) -> Result<TiledRgbaOutputFile>
where
V: Vec2<f32>,
{
let c_filename = CString::new(
filename
.as_ref()
.to_str()
.expect("Invalid bytes in filename"),
)
.expect("Internal null bytes in filename");
let s = sys::Imath_V2f_t {
x: screen_window_center.as_slice()[0],
y: screen_window_center.as_slice()[1],
};
let mut _inner = std::ptr::null_mut();
unsafe {
sys::Imf_TiledRgbaOutputFile_with_dimensions(
&mut _inner,
c_filename.as_ptr(),
width,
height,
tile_x_size,
tile_y_size,
mode.into(),
rounding_mode.into(),
channels.into(),
pixel_aspect_ratio,
s,
screen_window_width,
line_order.into(),
compression.into(),
num_threads,
)
.into_result()?;
}
Ok(TiledRgbaOutputFile(_inner))
}
pub fn set_frame_buffer(
&mut self,
data: &[Rgba],
x_stride: usize,
y_stride: usize,
) -> Result<()> {
unsafe {
sys::Imf_TiledRgbaOutputFile_setFrameBuffer(
self.0,
data.as_ptr() as *const sys::Imf_Rgba_t,
x_stride,
y_stride,
)
.into_result()?;
}
Ok(())
}
pub fn header(&self) -> HeaderRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_TiledRgbaOutputFile_header(self.0, &mut ptr);
if ptr.is_null() {
panic!("Received null ptr from sys::Imf_TiledRgbaOutputFile_header");
}
HeaderRef::new(ptr)
}
}
pub fn frame_buffer(&self) -> FrameBufferRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_TiledRgbaOutputFile_frameBuffer(self.0, &mut ptr);
if ptr.is_null() {
panic!("Received null ptr from sys::Imf_TiledRgbaOutputFile_frameBuffer");
}
FrameBufferRef::new(ptr)
}
}
pub fn tile_x_size(&self) -> u32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_tileXSize(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaOutputFile_tileXSize");
}
v
}
pub fn tile_y_size(&self) -> u32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_tileYSize(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaOutputFile_tileYSize");
}
v
}
pub fn level_mode(&self) -> LevelMode {
let mut v = sys::Imf_LevelMode(0);
unsafe {
sys::Imf_TiledRgbaOutputFile_levelMode(
self.0,
&mut v,
)
.into_result()
.expect(
"Unexpected exception from Imf_TiledRgbaOutputFile_levelMode",
);
}
v.into()
}
pub fn level_rounding_mode(&self) -> LevelRoundingMode {
let mut v = sys::Imf_LevelRoundingMode(0);
unsafe {
sys::Imf_TiledRgbaOutputFile_levelRoundingMode(
self.0,
&mut v,
)
.into_result()
.expect(
"Unexpected exception from Imf_TiledRgbaOutputFile_levelRoundingMode",
);
}
v.into()
}
pub fn num_levels(&self) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_numLevels(self.0, &mut v)
.into_result()?;
}
Ok(v)
}
pub fn num_x_levels(&self) -> i32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_numXLevels(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaOutputFile_numXLevels");
}
v
}
pub fn num_y_levels(&self) -> i32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_numYLevels(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaOutputFile_numYLevels");
}
v
}
pub fn is_valid_level(&self, lx: i32, ly: i32) -> bool {
let mut v = false;
unsafe {
sys::Imf_TiledRgbaOutputFile_isValidLevel(self.0, &mut v, lx, ly).into_result().expect("Unexpected exception from Imf_TiledRgbaOutputFile_isValidLevel");
}
v
}
pub fn level_width(&self, lx: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_levelWidth(self.0, &mut v, lx)
.into_result()?;
}
Ok(v)
}
pub fn level_height(&self, ly: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_levelHeight(self.0, &mut v, ly)
.into_result()?;
}
Ok(v)
}
pub fn num_x_tiles(&self, lx: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_numXTiles(self.0, &mut v, lx)
.into_result()?;
}
Ok(v)
}
pub fn num_y_tiles(&self, ly: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaOutputFile_numYTiles(self.0, &mut v, ly)
.into_result()?;
}
Ok(v)
}
pub fn data_window_for_level<B: Bound2<i32>>(
&self,
lx: i32,
ly: i32,
) -> Result<B> {
let mut dw = [0i32; 4];
unsafe {
sys::Imf_TiledRgbaOutputFile_dataWindowForLevel(
self.0,
dw.as_mut_ptr() as *mut sys::Imath_Box2i_t,
lx,
ly,
)
.into_result()?
}
Ok(B::from_slice(&dw))
}
pub fn data_window_for_tile<B: Bound2<i32>>(
&self,
dx: i32,
dy: i32,
lx: i32,
ly: i32,
) -> Result<B> {
let mut dw = [0i32; 4];
unsafe {
sys::Imf_TiledRgbaOutputFile_dataWindowForTile(
self.0,
dw.as_mut_ptr() as *mut sys::Imath_Box2i_t,
dx,
dy,
lx,
ly,
)
.into_result()?
}
Ok(B::from_slice(&dw))
}
pub unsafe fn write_tile(
&mut self,
dx: i32,
dy: i32,
lx: i32,
ly: i32,
) -> Result<()> {
sys::Imf_TiledRgbaOutputFile_writeTile(self.0, dx, dy, lx, ly)
.into_result()?;
Ok(())
}
pub unsafe fn write_tiles(
&mut self,
dx1: i32,
dx2: i32,
dy1: i32,
dy2: i32,
lx: i32,
ly: i32,
) -> Result<()> {
sys::Imf_TiledRgbaOutputFile_writeTiles(
self.0, dx1, dx2, dy1, dy2, lx, ly,
)
.into_result()?;
Ok(())
}
pub fn update_preview_image(
&mut self,
new_pixels: &[PreviewRgba],
) -> Result<()> {
unsafe {
sys::Imf_TiledRgbaOutputFile_updatePreviewImage(
self.0,
new_pixels.as_ptr() as *const sys::Imf_PreviewRgba_t,
)
.into_result()?;
}
Ok(())
}
}
impl Drop for TiledRgbaOutputFile {
fn drop(&mut self) {
unsafe {
sys::Imf_TiledRgbaOutputFile_dtor(self.0)
.into_result()
.expect("Unexpected exception in Imf_TiledRgbaOutputFile_dtor");
}
}
}
#[repr(transparent)]
pub struct TiledRgbaInputFile(pub(crate) *mut sys::Imf_TiledRgbaInputFile_t);
impl TiledRgbaInputFile {
pub fn new<P: AsRef<Path>>(
filename: P,
num_threads: i32,
) -> Result<TiledRgbaInputFile> {
let c_filename = CString::new(
filename
.as_ref()
.to_str()
.expect("Invalid bytes in filename"),
)
.expect("Internal null bytes in filename");
let mut inner = std::ptr::null_mut();
unsafe {
sys::Imf_TiledRgbaInputFile_ctor(
&mut inner,
c_filename.as_ptr(),
num_threads,
)
.into_result()?;
}
Ok(TiledRgbaInputFile(inner))
}
pub fn with_layer<P: AsRef<Path>>(
filename: P,
layer_name: &str,
num_threads: i32,
) -> Result<TiledRgbaInputFile> {
let c_filename = CString::new(
filename
.as_ref()
.to_str()
.expect("Invalid bytes in filename"),
)
.expect("Internal null bytes in filename");
let s_layer_name = CppString::new(layer_name);
let mut inner = std::ptr::null_mut();
unsafe {
sys::Imf_TiledRgbaInputFile_with_layer(
&mut inner,
c_filename.as_ptr(),
s_layer_name.0,
num_threads,
)
.into_result()?;
}
Ok(TiledRgbaInputFile(inner))
}
pub fn set_frame_buffer(
&mut self,
pixels: &mut [Rgba],
x_stride: usize,
y_stride: usize,
) -> Result<()> {
unsafe {
sys::Imf_TiledRgbaInputFile_setFrameBuffer(
self.0,
pixels.as_mut_ptr() as *mut Rgba as *mut sys::Imf_Rgba_t,
x_stride,
y_stride,
)
.into_result()?;
}
Ok(())
}
pub fn set_layer_name(&mut self, layer_name: &str) {
let s_layer_name = CppString::new(layer_name);
unsafe {
sys::Imf_TiledRgbaInputFile_setLayerName(self.0, s_layer_name.0).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_setLayerName");
}
}
pub fn header(&self) -> HeaderRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_TiledRgbaInputFile_header(self.0, &mut ptr);
if ptr.is_null() {
panic!(
"Received null ptr from sys::Imf_TiledRgbaInputFile_header"
);
}
HeaderRef::new(ptr)
}
}
pub fn frame_buffer(&self) -> FrameBufferRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_TiledRgbaInputFile_frameBuffer(self.0, &mut ptr);
if ptr.is_null() {
panic!("Received null ptr from sys::Imf_TiledRgbaInputFile_frameBuffer");
}
FrameBufferRef::new(ptr)
}
}
pub fn tile_x_size(&self) -> u32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_tileXSize(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_tileXSize");
}
v
}
pub fn tile_y_size(&self) -> u32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_tileYSize(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_tileYSize");
}
v
}
pub fn level_mode(&self) -> LevelMode {
let mut v = sys::Imf_LevelMode(0);
unsafe {
sys::Imf_TiledRgbaInputFile_levelMode(
self.0,
&mut v,
)
.into_result()
.expect(
"Unexpected exception from Imf_TiledRgbaInputFile_levelMode",
);
}
v.into()
}
pub fn level_rounding_mode(&self) -> LevelRoundingMode {
let mut v = sys::Imf_LevelRoundingMode(0);
unsafe {
sys::Imf_TiledRgbaInputFile_levelRoundingMode(
self.0,
&mut v,
)
.into_result()
.expect(
"Unexpected exception from Imf_TiledRgbaInputFile_levelRoundingMode",
);
}
v.into()
}
pub fn num_levels(&self) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_numLevels(self.0, &mut v)
.into_result()?;
}
Ok(v)
}
pub fn num_x_levels(&self) -> i32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_numXLevels(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_numXLevels");
}
v
}
pub fn num_y_levels(&self) -> i32 {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_numYLevels(self.0, &mut v).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_numYLevels");
}
v
}
pub fn is_valid_level(&self, lx: i32, ly: i32) -> bool {
let mut v = false;
unsafe {
sys::Imf_TiledRgbaInputFile_isValidLevel(self.0, &mut v, lx, ly).into_result().expect("Unexpected exception from Imf_TiledRgbaInputFile_isValidLevel");
}
v
}
pub fn level_width(&self, lx: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_levelWidth(self.0, &mut v, lx)
.into_result()?;
}
Ok(v)
}
pub fn level_height(&self, ly: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_levelHeight(self.0, &mut v, ly)
.into_result()?;
}
Ok(v)
}
pub fn num_x_tiles(&self, lx: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_numXTiles(self.0, &mut v, lx)
.into_result()?;
}
Ok(v)
}
pub fn num_y_tiles(&self, ly: i32) -> Result<i32> {
let mut v = 0;
unsafe {
sys::Imf_TiledRgbaInputFile_numYTiles(self.0, &mut v, ly)
.into_result()?;
}
Ok(v)
}
pub fn data_window_for_level<B: Bound2<i32>>(
&self,
lx: i32,
ly: i32,
) -> Result<B> {
let mut dw = [0i32; 4];
unsafe {
sys::Imf_TiledRgbaInputFile_dataWindowForLevel(
self.0,
dw.as_mut_ptr() as *mut sys::Imath_Box2i_t,
lx,
ly,
)
.into_result()?;
}
Ok(B::from_slice(&dw))
}
pub fn data_window_for_tile<B: Bound2<i32>>(
&self,
dx: i32,
dy: i32,
lx: i32,
ly: i32,
) -> Result<B> {
let mut dw = [0i32; 4];
unsafe {
sys::Imf_TiledRgbaInputFile_dataWindowForTile(
self.0,
dw.as_mut_ptr() as *mut sys::Imath_Box2i_t,
dx,
dy,
lx,
ly,
)
.into_result()?;
}
Ok(B::from_slice(&dw))
}
pub unsafe fn read_tile(
&mut self,
dx: i32,
dy: i32,
lx: i32,
ly: i32,
) -> Result<()> {
sys::Imf_TiledRgbaInputFile_readTile(self.0, dx, dy, lx, ly)
.into_result()?;
Ok(())
}
pub unsafe fn read_tiles(
&mut self,
dx1: i32,
dx2: i32,
dy1: i32,
dy2: i32,
lx: i32,
ly: i32,
) -> Result<()> {
sys::Imf_TiledRgbaInputFile_readTiles(
self.0, dx1, dx2, dy1, dy2, lx, ly,
)
.into_result()?;
Ok(())
}
}
impl Drop for TiledRgbaInputFile {
fn drop(&mut self) {
unsafe {
sys::Imf_TiledRgbaInputFile_dtor(self.0)
.into_result()
.expect("Unexpected exception in Imf_TiledRgbaInputFile_dtor");
}
}
}
#[cfg(test)]
#[test]
fn test_write_tiled_rgba1() -> Result<(), Box<dyn std::error::Error>> {
let (pixels, width, height) = crate::tests::load_ferris();
let header = Header::from_dimensions(width, height);
let mut file = TiledRgbaOutputFile::new(
"write_tiled_rgba1.exr",
&header,
RgbaChannels::WriteRgba,
64,
64,
LevelMode::OneLevel,
LevelRoundingMode::RoundDown,
1,
)?;
file.set_frame_buffer(&pixels, 1, width as usize)?;
unsafe {
file.write_tiles(
0,
file.num_x_tiles(0)? - 1,
0,
file.num_y_tiles(0)? - 1,
0,
0,
)?;
}
Ok(())
}
#[cfg(test)]
#[test]
fn test_read_tiled_rgba1() -> Result<(), Box<dyn std::error::Error>> {
use crate::prelude::*;
use imath_traits::Zero;
use std::path::PathBuf;
let path = PathBuf::from(
std::env::var("CARGO_MANIFEST_DIR")
.expect("CARGO_MANIFEST_DIR not set"),
)
.join("images")
.join("ferris-tiled.exr");
let mut file = TiledRgbaInputFile::new(&path, 1)?;
let data_window = file.header().data_window::<[i32; 4]>().clone();
let width = data_window[2] - data_window[0] + 1;
let height = data_window[3] - data_window[1] + 1;
let mut pixels = vec![Rgba::zero(); (width * height) as usize];
file.set_frame_buffer(&mut pixels, 1, width as usize)?;
unsafe {
file.read_tiles(
0,
file.num_x_tiles(0)? - 1,
0,
file.num_y_tiles(0)? - 1,
0,
0,
)?;
}
let mut ofile = RgbaOutputFile::with_dimensions(
"read_tiled_rgba1.exr",
width,
height,
RgbaChannels::WriteRgba,
1.0f32,
[0.0f32, 0.0f32],
1.0f32,
LineOrder::IncreasingY,
Compression::Piz,
1,
)?;
ofile.set_frame_buffer(&pixels, 1, width as usize)?;
unsafe {
ofile.write_pixels(height)?;
}
Ok(())
}