use crate::{
core::{
cppstd::CppString,
error::Error,
header::{Header, HeaderRef},
preview_image::PreviewRgba,
Compression, LineOrder,
},
rgba::rgba::{Rgba, RgbaChannels},
};
use imath_traits::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 RgbaOutputFile(pub(crate) *mut sys::Imf_RgbaOutputFile_t);
impl RgbaOutputFile {
pub fn new<P: AsRef<Path>>(
filename: P,
header: &Header,
channels: RgbaChannels,
num_threads: i32,
) -> Result<RgbaOutputFile> {
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_RgbaOutputFile_ctor(
&mut _inner,
c_filename.as_ptr(),
header.0.as_ref(),
channels.into(),
num_threads,
)
.into_result()?;
}
Ok(RgbaOutputFile(_inner))
}
#[allow(clippy::too_many_arguments)]
pub fn with_dimensions<P: AsRef<Path>, V>(
filename: P,
width: i32,
height: i32,
channels: RgbaChannels,
pixel_aspect_ratio: f32,
screen_window_center: V,
screen_window_width: f32,
line_order: LineOrder,
compression: Compression,
num_threads: i32,
) -> Result<RgbaOutputFile>
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_RgbaOutputFile_with_dimensions(
&mut _inner,
c_filename.as_ptr(),
width,
height,
channels.into(),
pixel_aspect_ratio,
s,
screen_window_width,
line_order.into(),
compression.into(),
num_threads,
)
.into_result()?;
}
Ok(RgbaOutputFile(_inner))
}
pub fn set_frame_buffer(
&mut self,
data: &[Rgba],
x_stride: usize,
y_stride: usize,
) -> Result<()> {
unsafe {
sys::Imf_RgbaOutputFile_setFrameBuffer(
self.0,
data.as_ptr() as *const sys::Imf_Rgba_t,
x_stride,
y_stride,
)
.into_result()?;
}
Ok(())
}
pub unsafe fn write_pixels(&mut self, num_scan_lines: i32) -> Result<()> {
sys::Imf_RgbaOutputFile_writePixels(self.0, num_scan_lines)
.into_result()?;
Ok(())
}
pub fn current_scan_line(&self) -> i32 {
let mut v = 0;
unsafe {
sys::Imf_RgbaOutputFile_currentScanLine(self.0, &mut v);
}
v
}
pub fn header(&self) -> HeaderRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_RgbaOutputFile_header(self.0, &mut ptr);
if ptr.is_null() {
panic!("Received null ptr from sys::Imf_RgbaOutputFile_header");
}
HeaderRef::new(ptr)
}
}
pub fn set_yc_rounding(&mut self, round_y: u32, round_c: u32) {
unsafe {
sys::Imf_RgbaOutputFile_setYCRounding(self.0, round_y, round_c);
}
}
pub fn update_preview_image(
&mut self,
new_pixels: &[PreviewRgba],
) -> Result<()> {
unsafe {
sys::Imf_RgbaOutputFile_updatePreviewImage(
self.0,
new_pixels.as_ptr() as *const sys::Imf_PreviewRgba_t,
)
.into_result()?;
}
Ok(())
}
}
impl Drop for RgbaOutputFile {
fn drop(&mut self) {
unsafe {
sys::Imf_RgbaOutputFile_dtor(self.0);
}
}
}
#[repr(transparent)]
pub struct RgbaInputFile(pub(crate) *mut sys::Imf_RgbaInputFile_t);
impl RgbaInputFile {
pub fn new<P: AsRef<Path>>(
filename: P,
num_threads: i32,
) -> Result<RgbaInputFile> {
let mut inner = std::ptr::null_mut();
let c_filename = CString::new(
filename
.as_ref()
.to_str()
.expect("Invalid bytes in filename"),
)
.expect("Internal null bytes in filename");
unsafe {
sys::Imf_RgbaInputFile_ctor(
&mut inner,
c_filename.as_ptr(),
num_threads,
)
.into_result()?;
}
Ok(RgbaInputFile(inner))
}
pub fn set_frame_buffer(
&mut self,
pixels: &mut [Rgba],
x_stride: usize,
y_stride: usize,
) -> Result<()> {
unsafe {
sys::Imf_RgbaInputFile_setFrameBuffer(
self.0,
pixels.as_mut_ptr() as *mut sys::Imf_Rgba_t,
x_stride,
y_stride,
)
.into_result()?;
}
Ok(())
}
pub fn set_layer_name(&mut self, name: &str) {
unsafe {
let s = CppString::new(name);
sys::Imf_RgbaInputFile_setLayerName(self.0, s.0);
}
}
pub unsafe fn read_pixels(
&mut self,
scanline1: i32,
scanline2: i32,
) -> Result<()> {
sys::Imf_RgbaInputFile_readPixels(self.0, scanline1, scanline2)
.into_result()?;
Ok(())
}
pub fn header(&self) -> HeaderRef {
unsafe {
let mut ptr = std::ptr::null();
sys::Imf_RgbaInputFile_header(self.0, &mut ptr);
if ptr.is_null() {
panic!("Received null ptr from sys::Imf_RgbaInputFile_header");
}
HeaderRef::new(ptr)
}
}
pub fn is_complete(&self) -> bool {
let mut result = false;
unsafe { sys::Imf_RgbaInputFile_isComplete(self.0, &mut result) };
result
}
}
impl Drop for RgbaInputFile {
fn drop(&mut self) {
unsafe {
sys::Imf_RgbaInputFile_dtor(self.0);
}
}
}