#![cfg_attr(feature = "nightly", doc(cfg(feature = "output")))]
use crate::argument::CallbackPtr;
use std::{
ffi::{CStr, CString},
os::raw::{c_char, c_int, c_void},
};
pub mod pixel_format;
pub use pixel_format::*;
pub static FERRIS: &str = "ferris";
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, num_enum::IntoPrimitive)]
pub enum Error {
None = ndspy_sys::PtDspyError::None as _,
NoMemory = ndspy_sys::PtDspyError::NoMemory as _,
Unsupported = ndspy_sys::PtDspyError::Unsupported as _,
BadParameters = ndspy_sys::PtDspyError::BadParams as _,
NoResource = ndspy_sys::PtDspyError::NoResource as _,
Undefined = ndspy_sys::PtDspyError::Undefined as _,
Stop = ndspy_sys::PtDspyError::Stop as _,
}
impl From<Error> for ndspy_sys::PtDspyError {
fn from(item: Error) -> ndspy_sys::PtDspyError {
match item {
Error::None => ndspy_sys::PtDspyError::None,
Error::NoMemory => ndspy_sys::PtDspyError::NoMemory,
Error::Unsupported => ndspy_sys::PtDspyError::Unsupported,
Error::BadParameters => ndspy_sys::PtDspyError::BadParams,
Error::NoResource => ndspy_sys::PtDspyError::NoResource,
Error::Undefined => ndspy_sys::PtDspyError::Undefined,
Error::Stop => ndspy_sys::PtDspyError::Stop,
}
}
}
pub trait FnOpen<'a>: FnMut(
&str,
usize,
usize,
&PixelFormat,
) -> Error
+ 'a {}
#[doc(hidden)]
impl<'a, T: FnMut(&str, usize, usize, &PixelFormat) -> Error + 'a> FnOpen<'a>
for T
{
}
pub trait FnWrite<'a>: FnMut(
&str,
usize,
usize,
usize,
usize,
usize,
usize,
&PixelFormat,
&[f32],
) -> Error
+ 'a {}
#[doc(hidden)]
impl<
'a,
T: FnMut(
&str,
usize,
usize,
usize,
usize,
usize,
usize,
&PixelFormat,
&[f32],
) -> Error
+ 'a,
> FnWrite<'a> for T
{
}
pub trait FnFinish<'a>: FnMut(
String,
usize,
usize,
PixelFormat,
Vec<f32>,
) -> Error
+ 'a {}
#[doc(hidden)]
impl<
'a,
T: FnMut(String, usize, usize, PixelFormat, Vec<f32>) -> Error + 'a,
> FnFinish<'a> for T
{
}
enum Query {}
trait FnQuery<'a>: FnMut(Query) -> Error + 'a {}
impl<'a, T: FnMut(Query) -> Error + 'a> FnQuery<'a> for T {}
pub struct OpenCallback<'a>(Box<Box<Box<dyn FnOpen<'a>>>>);
impl<'a> OpenCallback<'a> {
pub fn new<F>(fn_open: F) -> Self
where
F: FnOpen<'a>,
{
OpenCallback(Box::new(Box::new(Box::new(fn_open))))
}
}
impl CallbackPtr for OpenCallback<'_> {
#[doc(hidden)]
fn to_ptr(self) -> *const core::ffi::c_void {
Box::into_raw(self.0) as *const _ as _
}
}
pub struct WriteCallback<'a>(Box<Box<Box<dyn FnWrite<'a>>>>);
impl<'a> WriteCallback<'a> {
pub fn new<F>(fn_write: F) -> Self
where
F: FnWrite<'a>,
{
WriteCallback(Box::new(Box::new(Box::new(fn_write))))
}
}
impl CallbackPtr for WriteCallback<'_> {
#[doc(hidden)]
fn to_ptr(self) -> *const core::ffi::c_void {
Box::into_raw(self.0) as *const _ as _
}
}
pub struct FinishCallback<'a>(Box<Box<Box<dyn FnFinish<'a>>>>);
impl<'a> FinishCallback<'a> {
pub fn new<F>(fn_finish: F) -> Self
where
F: FnFinish<'a>,
{
FinishCallback(Box::new(Box::new(Box::new(fn_finish))))
}
}
impl CallbackPtr for FinishCallback<'_> {
#[doc(hidden)]
fn to_ptr(self) -> *const core::ffi::c_void {
Box::into_raw(self.0) as *const _ as _
}
}
struct DisplayData<'a> {
name: String,
width: usize,
height: usize,
pixel_format: PixelFormat,
pixel_data: Vec<f32>,
fn_write: Option<Box<Box<Box<dyn FnWrite<'a>>>>>,
fn_finish: Option<Box<Box<Box<dyn FnFinish<'a>>>>>,
fn_query: Option<Box<Box<Box<dyn FnQuery<'a>>>>>,
}
fn get_parameter_triple_box<T: ?Sized>(
name: &str,
type_: u8,
len: usize,
parameters: &mut [ndspy_sys::UserParameter],
) -> Option<Box<Box<Box<T>>>> {
for p in parameters.iter() {
let p_name = unsafe { CStr::from_ptr(p.name) }.to_str().unwrap();
if name == p_name
&& type_ == p.valueType as _
&& len == p.valueCount as _
{
if !p.value.is_null() {
return Some(unsafe {
Box::from_raw(p.value as *mut Box<Box<T>>)
});
} else {
break;
}
}
}
None
}
#[no_mangle]
pub(crate) extern "C" fn image_open(
image_handle_ptr: *mut ndspy_sys::PtDspyImageHandle,
_driver_name: *const c_char,
output_filename: *const c_char,
width: c_int,
height: c_int,
parameters_count: c_int,
parameters: *const ndspy_sys::UserParameter,
format_count: c_int,
format: *mut ndspy_sys::PtDspyDevFormat,
flag_stuff: *mut ndspy_sys::PtFlagStuff,
) -> ndspy_sys::PtDspyError {
if (image_handle_ptr.is_null()) || (output_filename.is_null()) {
return Error::BadParameters.into();
}
let parameters = unsafe {
std::slice::from_raw_parts_mut(
parameters as *mut ndspy_sys::UserParameter,
parameters_count as _,
)
};
let mut display_data = Box::new(DisplayData {
name: unsafe {
CString::from(CStr::from_ptr(output_filename))
.into_string()
.unwrap()
},
width: width as _,
height: height as _,
pixel_format: PixelFormat::default(),
pixel_data: vec![0.0f32; (width * height * format_count) as _],
fn_write: get_parameter_triple_box::<dyn FnWrite>(
"callback.write",
b'p',
1,
parameters,
),
fn_finish: get_parameter_triple_box::<dyn FnFinish>(
"callback.finish",
b'p',
1,
parameters,
),
fn_query: None,
});
let format =
unsafe { std::slice::from_raw_parts_mut(format, format_count as _) };
format
.iter_mut()
.for_each(|format| format.type_ = ndspy_sys::PkDspyFloat32);
display_data.pixel_format = PixelFormat::new(format);
let error = if let Some(mut fn_open) = get_parameter_triple_box::<dyn FnOpen>(
"callback.open",
b'p',
1,
parameters,
) {
let error = fn_open(
&display_data.name,
width as _,
height as _,
&display_data.pixel_format,
);
Box::leak(fn_open);
error
} else {
Error::None
};
unsafe {
*image_handle_ptr = Box::into_raw(display_data) as _;
(*flag_stuff).flags = ndspy_sys::PkDspyFlagsWantsEmptyBuckets as _;
}
error.into()
}
#[no_mangle]
pub(crate) extern "C" fn image_query(
_image_handle_ptr: ndspy_sys::PtDspyImageHandle,
query_type: ndspy_sys::PtDspyQueryType,
data_len: c_int,
data: *mut c_void,
) -> ndspy_sys::PtDspyError {
match query_type {
ndspy_sys::PtDspyQueryType::RenderProgress => {
if (data_len as usize)
< core::mem::size_of::<ndspy_sys::PtDspyRenderProgressFuncPtr>()
{
Error::BadParameters
} else {
*unsafe {
&mut std::mem::transmute::<
_,
ndspy_sys::PtDspyRenderProgressFuncPtr,
>(data)
} = Some(image_progress);
Error::None
}
}
_ => Error::Unsupported,
}
.into()
}
#[no_mangle]
pub(crate) extern "C" fn image_write(
image_handle_ptr: ndspy_sys::PtDspyImageHandle,
x_min: c_int,
x_max_plus_one: c_int,
y_min: c_int,
y_max_plus_one: c_int,
entry_size: c_int,
pixel_data: *const u8,
) -> ndspy_sys::PtDspyError {
let display_data = unsafe { &mut *(image_handle_ptr as *mut DisplayData) };
debug_assert!(entry_size >> 2 == display_data.pixel_format.channels() as _);
let pixel_length = display_data.pixel_format.channels();
let pixel_data = unsafe {
std::slice::from_raw_parts(
pixel_data as *const f32,
pixel_length
* ((x_max_plus_one - x_min) * (y_max_plus_one - y_min))
as usize,
)
};
let bucket_width = pixel_length * (x_max_plus_one - x_min) as usize;
let mut source_index = 0;
for y in y_min as usize..y_max_plus_one as _ {
let dest_index =
(y * display_data.width + x_min as usize) * pixel_length;
display_data.pixel_data[dest_index..dest_index + bucket_width]
.copy_from_slice(
&(pixel_data[source_index..source_index + bucket_width]),
);
source_index += bucket_width;
}
if let Some(ref mut fn_write) = display_data.fn_write {
fn_write(
&display_data.name,
display_data.width,
display_data.height,
x_min as _,
x_max_plus_one as _,
y_min as _,
y_max_plus_one as _,
&display_data.pixel_format,
display_data.pixel_data.as_mut_slice(),
)
} else {
Error::None
}
.into()
}
#[no_mangle]
pub(crate) extern "C" fn image_close(
image_handle_ptr: ndspy_sys::PtDspyImageHandle,
) -> ndspy_sys::PtDspyError {
let mut display_data =
unsafe { Box::from_raw(image_handle_ptr as *mut DisplayData) };
let error = if let Some(ref mut fn_finish) = display_data.fn_finish {
fn_finish(
display_data.name,
display_data.width,
display_data.height,
display_data.pixel_format,
display_data.pixel_data,
)
} else {
Error::None
};
if let Some(fn_write) = display_data.fn_write {
Box::leak(fn_write);
}
if let Some(fn_query) = display_data.fn_query {
Box::leak(fn_query);
}
if let Some(fn_finish) = display_data.fn_finish {
Box::leak(fn_finish);
}
error.into()
}
#[no_mangle]
extern "C" fn image_progress(
_image_handle_ptr: ndspy_sys::PtDspyImageHandle,
progress: f32,
) -> ndspy_sys::PtDspyError {
println!("\rProgress: {}", progress * 100.0);
Error::None.into()
}