#![cfg_attr(feature = "nightly", doc(cfg(feature = "output")))]
use crate::argument::CallbackPtr;
use core::ops::Index;
use std::{
ffi::CStr,
os::raw::{c_char, c_int, c_void},
};
#[cfg(feature = "jupyter")]
pub mod jupyter;
pub static FERRIS: &str = "ferris";
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, num_enum::IntoPrimitive)]
pub enum Error {
None = ndspy_sys::PtDspyError_PkDspyErrorNone as _,
NoMemory = ndspy_sys::PtDspyError_PkDspyErrorNoMemory as _,
Unsupported = ndspy_sys::PtDspyError_PkDspyErrorUnsupported as _,
BadParameters = ndspy_sys::PtDspyError_PkDspyErrorBadParams as _,
NoResource = ndspy_sys::PtDspyError_PkDspyErrorNoResource as _,
Undefined = ndspy_sys::PtDspyError_PkDspyErrorUndefined as _,
Stop = ndspy_sys::PtDspyError_PkDspyErrorStop as _,
}
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(
&str,
usize,
usize,
PixelFormat,
Vec<f32>,
) -> Error
+ 'a {}
#[doc(hidden)]
impl<'a, T: FnMut(&str, 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: &'a str,
width: usize,
height: usize,
pixel_format: PixelFormat,
pixel_data: Vec<f32>,
fn_open: Option<Box<Box<Box<dyn FnOpen<'a>>>>>,
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>>>>>,
}
impl<'a> DisplayData<'a> {
#[allow(clippy::type_complexity)]
fn boxed_into_tuple(
display_data: Self,
) -> (
&'a str,
usize,
usize,
PixelFormat,
Vec<f32>,
Option<Box<Box<Box<dyn FnFinish<'a>>>>>,
) {
if let Some(fn_open) = display_data.fn_open {
Box::into_raw(fn_open);
}
if let Some(fn_write) = display_data.fn_write {
Box::into_raw(fn_write);
}
if let Some(fn_query) = display_data.fn_query {
Box::into_raw(fn_query);
}
(
display_data.name,
display_data.width,
display_data.height,
display_data.pixel_format,
display_data.pixel_data,
display_data.fn_finish,
)
}
}
#[derive(Debug, Clone, Default)]
pub struct Layer {
name: String,
depth: LayerDepth,
offset: usize,
}
impl Layer {
#[inline]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[inline]
pub fn depth(&self) -> LayerDepth {
self.depth
}
#[inline]
pub fn offset(&self) -> usize {
self.offset
}
#[inline]
pub fn channels(&self) -> usize {
self.depth.channels()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LayerDepth {
OneChannel,
OneChannelAndAlpha,
Color,
ColorAndAlpha,
Vector,
VectorAndAlpha,
FourChannels,
FourChannelsAndAlpha,
}
impl Default for LayerDepth {
fn default() -> Self {
LayerDepth::OneChannel
}
}
impl LayerDepth {
pub fn channels(&self) -> usize {
match self {
LayerDepth::OneChannel => 1,
LayerDepth::OneChannelAndAlpha => 2,
LayerDepth::Color => 3,
LayerDepth::Vector => 3,
LayerDepth::ColorAndAlpha => 4,
LayerDepth::VectorAndAlpha => 4,
LayerDepth::FourChannels => 4,
LayerDepth::FourChannelsAndAlpha => 5,
}
}
}
#[derive(Debug, Default)]
pub struct PixelFormat(Vec<Layer>);
impl PixelFormat {
#[inline]
fn new(format: &[ndspy_sys::PtDspyDevFormat]) -> Self {
let (mut previous_layer_name, mut previous_channel_id) =
Self::split_into_layer_name_and_channel_id(
unsafe { CStr::from_ptr(format[0].name) }.to_str().unwrap(),
);
let mut depth = LayerDepth::OneChannel;
let mut offset = 0;
PixelFormat(
format
.iter()
.enumerate()
.cycle()
.take(format.len() + 1)
.filter_map(|format| {
let name = unsafe { CStr::from_ptr(format.1.name) }.to_str().unwrap();
let (layer_name, channel_id) = Self::split_into_layer_name_and_channel_id(name);
if ["b", "z", "s", "a"].contains(&previous_channel_id)
&& ["r", "x", "s"].contains(&channel_id)
{
let tmp_layer_name = if previous_layer_name.is_empty() {
"Ci"
} else {
previous_layer_name
};
previous_layer_name = layer_name;
previous_channel_id = channel_id;
let tmp_depth = depth;
depth = LayerDepth::OneChannel;
let tmp_offset = offset;
offset = format.0;
Some(Layer {
name: tmp_layer_name.to_string(),
depth: tmp_depth,
offset: tmp_offset,
})
} else {
if layer_name.is_empty() && "a" == channel_id {
depth = match &depth {
LayerDepth::OneChannel => LayerDepth::OneChannelAndAlpha,
LayerDepth::Color => LayerDepth::ColorAndAlpha,
LayerDepth::Vector => LayerDepth::VectorAndAlpha,
LayerDepth::FourChannels => LayerDepth::FourChannelsAndAlpha,
_ => unreachable!(),
};
}
else if layer_name == previous_layer_name {
match channel_id {
"r" | "g" | "b" => depth = LayerDepth::Color,
"x" | "y" | "z" => depth = LayerDepth::Vector,
"a" => {
if layer_name.is_empty() {
depth = match &depth {
LayerDepth::OneChannel => {
LayerDepth::OneChannelAndAlpha
}
LayerDepth::Color => LayerDepth::ColorAndAlpha,
LayerDepth::Vector => LayerDepth::VectorAndAlpha,
_ => unreachable!(),
};
} else {
depth = LayerDepth::FourChannels;
}
}
_ => (),
}
previous_layer_name = layer_name;
} else {
previous_layer_name = layer_name;
}
previous_channel_id = channel_id;
None
}
})
.collect::<Vec<_>>(),
)
}
fn split_into_layer_name_and_channel_id(name: &str) -> (&str, &str) {
let mut split = name.rsplitn(3, '.');
let mut postfix = split.next().unwrap();
if "000" == postfix {
postfix = "s";
split = name.rsplitn(2, '.');
}
if split.next().is_some() {
(split.next().unwrap(), postfix)
} else {
("", postfix)
}
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn channels(&self) -> usize {
self.0
.iter()
.fold(0, |total, layer| total + layer.channels())
}
}
impl Index<usize> for PixelFormat {
type Output = Layer;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
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 { CStr::from_ptr(output_filename).to_str().unwrap() },
width: width as _,
height: height as _,
pixel_format: PixelFormat::default(),
pixel_data: vec![0.0f32; (width * height * format_count) as _],
fn_open: get_parameter_triple_box::<dyn FnOpen>("callback.open", b'p', 1, parameters),
fn_write: get_parameter_triple_box::<dyn FnWrite>("callback.write", b'p', 1, parameters),
fn_query: None,
fn_finish: get_parameter_triple_box::<dyn FnFinish>("callback.finish", b'p', 1, parameters),
});
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(ref mut fn_open) = display_data.fn_open {
fn_open(
display_data.name,
width as _,
height as _,
&display_data.pixel_format,
)
} else {
Error::None
};
unsafe {
*image_handle_ptr = Box::into_raw(display_data) as _;
}
unsafe {
(*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_PkRenderProgress => {
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) };
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;
(&mut 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 display_data = unsafe { Box::from_raw(image_handle_ptr as *mut DisplayData) };
let (name, width, height, pixel_format, pixel_data, fn_finish) =
DisplayData::boxed_into_tuple(*display_data);
if let Some(mut fn_finish) = fn_finish {
let error = fn_finish(name, width, height, pixel_format, pixel_data);
Box::into_raw(fn_finish);
error
} else {
Error::None
}
.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()
}