use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use super::{FilterProcError, FilterProcResult, ObjectInfo, SceneInfo};
#[derive(Debug, Clone, Copy)]
pub struct VideoObjectInfo {
pub width: u32,
pub height: u32,
pub index: u32,
pub num: Option<u32>,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct ObjectImageParam {
pub x: f32,
pub y: f32,
pub z: f32,
pub rx: f32,
pub ry: f32,
pub rz: f32,
pub sx: f32,
pub sy: f32,
pub sz: f32,
pub cx: f32,
pub cy: f32,
pub cz: f32,
pub alpha: f32,
}
impl From<aviutl2_sys::filter2::OBJECT_IMAGE_PARAM> for ObjectImageParam {
fn from(value: aviutl2_sys::filter2::OBJECT_IMAGE_PARAM) -> Self {
(&value).into()
}
}
impl From<&aviutl2_sys::filter2::OBJECT_IMAGE_PARAM> for ObjectImageParam {
fn from(value: &aviutl2_sys::filter2::OBJECT_IMAGE_PARAM) -> Self {
Self {
x: value.x,
y: value.y,
z: value.z,
rx: value.rx,
ry: value.ry,
rz: value.rz,
sx: value.sx,
sy: value.sy,
sz: value.sz,
cx: value.cx,
cy: value.cy,
cz: value.cz,
alpha: value.alpha,
}
}
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
)]
pub struct RgbaPixel {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
#[derive(Debug)]
pub struct FilterProcVideo {
pub scene: SceneInfo,
pub object: ObjectInfo,
pub video_object: VideoObjectInfo,
pub param: ObjectImageParam,
pub(crate) prevent_post_effect: bool,
pub(crate) read_section: crate::generic::ReadSection,
pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_VIDEO,
}
unsafe impl Send for FilterProcVideo {}
unsafe impl Sync for FilterProcVideo {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DrawImageResource {
Object,
TempBuffer,
CacheBuffer(String),
ImageFile(std::path::PathBuf),
}
impl DrawImageResource {
fn to_cw_string(
&self,
) -> Result<Option<crate::common::CWString>, crate::common::NullByteError> {
Ok(match self {
DrawImageResource::Object => None,
DrawImageResource::TempBuffer => Some(crate::common::CWString::new("tempbuffer")?),
DrawImageResource::CacheBuffer(name) => {
Some(crate::common::CWString::new(&format!("cache:{}", name))?)
}
DrawImageResource::ImageFile(path) => Some(crate::common::CWString::new(&format!(
"image:{}",
path.to_string_lossy()
))?),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CreateImageResource {
Object,
TempBuffer,
CacheBuffer(String),
}
impl CreateImageResource {
fn to_cw_string(
&self,
) -> Result<Option<crate::common::CWString>, crate::common::NullByteError> {
Ok(match self {
CreateImageResource::Object => None,
CreateImageResource::TempBuffer => Some(crate::common::CWString::new("tempbuffer")?),
CreateImageResource::CacheBuffer(name) => {
Some(crate::common::CWString::new(&format!("cache:{}", name))?)
}
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct DrawImageParam {
pub x: f32,
pub y: f32,
pub z: f32,
pub rx: f32,
pub ry: f32,
pub rz: f32,
pub sx: f32,
pub sy: f32,
pub sz: f32,
pub alpha: f32,
}
impl Default for DrawImageParam {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
rx: 0.0,
ry: 0.0,
rz: 0.0,
sx: 1.0,
sy: 1.0,
sz: 1.0,
alpha: 1.0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct VertexColor {
pub x: f32,
pub y: f32,
pub z: f32,
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct VertexColorNorm {
pub x: f32,
pub y: f32,
pub z: f32,
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
pub vx: f32,
pub vy: f32,
pub vz: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct VertexTexture {
pub x: f32,
pub y: f32,
pub z: f32,
pub u: f32,
pub v: f32,
pub a: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct VertexTextureNorm {
pub x: f32,
pub y: f32,
pub z: f32,
pub u: f32,
pub v: f32,
pub a: f32,
pub vx: f32,
pub vy: f32,
pub vz: f32,
}
impl From<VertexColor> for aviutl2_sys::filter2::VERTEX_COLOR {
fn from(value: VertexColor) -> Self {
Self {
x: value.x,
y: value.y,
z: value.z,
r: value.r,
g: value.g,
b: value.b,
a: value.a,
}
}
}
impl From<VertexColorNorm> for aviutl2_sys::filter2::VERTEX_COLOR_NORM {
fn from(value: VertexColorNorm) -> Self {
Self {
x: value.x,
y: value.y,
z: value.z,
r: value.r,
g: value.g,
b: value.b,
a: value.a,
vx: value.vx,
vy: value.vy,
vz: value.vz,
}
}
}
impl From<VertexTexture> for aviutl2_sys::filter2::VERTEX_TEXTURE {
fn from(value: VertexTexture) -> Self {
Self {
x: value.x,
y: value.y,
z: value.z,
u: value.u,
v: value.v,
a: value.a,
}
}
}
impl From<VertexTextureNorm> for aviutl2_sys::filter2::VERTEX_TEXTURE_NORM {
fn from(value: VertexTextureNorm) -> Self {
Self {
x: value.x,
y: value.y,
z: value.z,
u: value.u,
v: value.v,
a: value.a,
vx: value.vx,
vy: value.vy,
vz: value.vz,
}
}
}
pub enum VertexList {
TriangleColor(Vec<[VertexColor; 3]>),
TriangleColorNorm(Vec<[VertexColorNorm; 3]>),
TriangleTexture(Vec<[VertexTexture; 3]>),
TriangleTextureNorm(Vec<[VertexTextureNorm; 3]>),
QuadColor(Vec<[VertexColor; 4]>),
QuadColorNorm(Vec<[VertexColorNorm; 4]>),
QuadTexture(Vec<[VertexTexture; 4]>),
QuadTextureNorm(Vec<[VertexTextureNorm; 4]>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum BlendMode {
#[default]
None,
Add,
Sub,
Mul,
Screen,
Overlay,
Light,
Dark,
Brightness,
Chroma,
Shadow,
LightDark,
Diff,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum SamplerMode {
#[default]
Clip,
Clamp,
Loop,
Mirror,
Dot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum BillboardMode {
#[default]
None,
Side,
Direction,
Camera,
}
impl From<BlendMode> for aviutl2_sys::filter2::BLEND_MODE {
fn from(value: BlendMode) -> Self {
match value {
BlendMode::None => Self::NONE,
BlendMode::Add => Self::ADD,
BlendMode::Sub => Self::SUB,
BlendMode::Mul => Self::MUL,
BlendMode::Screen => Self::SCREEN,
BlendMode::Overlay => Self::OVERLAY,
BlendMode::Light => Self::LIGHT,
BlendMode::Dark => Self::DARK,
BlendMode::Brightness => Self::BRIGHTNESS,
BlendMode::Chroma => Self::CHROMA,
BlendMode::Shadow => Self::SHADOW,
BlendMode::LightDark => Self::LIGHT_DARK,
BlendMode::Diff => Self::DIFF,
}
}
}
impl From<SamplerMode> for aviutl2_sys::filter2::SAMPLER_MODE {
fn from(value: SamplerMode) -> Self {
match value {
SamplerMode::Clip => Self::CLIP,
SamplerMode::Clamp => Self::CLAMP,
SamplerMode::Loop => Self::LOOP,
SamplerMode::Mirror => Self::MIRROR,
SamplerMode::Dot => Self::DOT,
}
}
}
impl From<BillboardMode> for aviutl2_sys::filter2::BILLBOARD_MODE {
fn from(value: BillboardMode) -> Self {
match value {
BillboardMode::None => Self::NONE,
BillboardMode::Side => Self::SIDE,
BillboardMode::Direction => Self::DIRECTION,
BillboardMode::Camera => Self::CAMERA,
}
}
}
impl FilterProcVideo {
pub fn get_image_data<T>(&mut self, buffer: &mut [T]) -> usize
where
T: Copy + FromBytes + Immutable,
{
if self.video_object.width == 0 || self.video_object.height == 0 {
tracing::warn!("width or height is 0, perhaps the filter plugin is a custom object");
return 0;
}
assert_eq!(
std::mem::size_of_val(buffer),
(self.video_object.width * self.video_object.height * 4) as usize,
"buffer length as bytes does not match width * height * 4"
);
assert!(
std::mem::align_of::<T>() >= std::mem::align_of::<aviutl2_sys::filter2::PIXEL_RGBA>(),
"buffer alignment is not sufficient"
);
let width = self.video_object.width as usize;
let height = self.video_object.height as usize;
let inner = unsafe { &*self.inner };
unsafe {
(inner.get_image_data)(
buffer.as_mut_ptr() as *mut u8 as *mut aviutl2_sys::filter2::PIXEL_RGBA
)
};
width * height * 4
}
pub fn set_image_data<T: IntoBytes + Immutable>(
&mut self,
data: &[T],
width: u32,
height: u32,
) {
let bytes = &data.as_bytes();
assert_eq!(
bytes.len(),
(width * height * 4) as usize,
"data length does not match width * height * 4"
);
let inner = unsafe { &*self.inner };
unsafe {
(inner.set_image_data)(
bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
width as i32,
height as i32,
)
};
}
pub fn get_image_texture2d(&mut self) -> *mut std::ffi::c_void {
let inner = unsafe { &*self.inner };
unsafe { (inner.get_image_texture2d)() }
}
pub fn get_framebuffer_texture2d(&mut self) -> *mut std::ffi::c_void {
let inner = unsafe { &*self.inner };
unsafe { (inner.get_framebuffer_texture2d)() }
}
pub fn read_section(&mut self) -> &crate::generic::ReadSection {
&self.read_section
}
pub fn get_output_image_param(
&mut self,
object: crate::generic::ObjectHandle,
offset: f64,
) -> FilterProcResult<ObjectImageParam> {
let inner = unsafe { &*self.inner };
let mut param = aviutl2_sys::filter2::OBJECT_IMAGE_PARAM {
x: 0.0,
y: 0.0,
z: 0.0,
rx: 0.0,
ry: 0.0,
rz: 0.0,
sx: 1.0,
sy: 1.0,
sz: 1.0,
cx: 0.0,
cy: 0.0,
cz: 0.0,
alpha: 1.0,
};
let ok = unsafe {
(inner.get_output_image_param)(
object.internal,
offset,
&mut param,
std::mem::size_of::<aviutl2_sys::filter2::OBJECT_IMAGE_PARAM>() as i32,
)
};
if ok {
Ok(param.into())
} else {
Err(FilterProcError::ApiCallFailed)
}
}
pub fn get_image_object(
&mut self,
layer: u32,
offset: f64,
) -> Option<crate::generic::ObjectHandle> {
let handle = unsafe { ((*self.inner).get_image_object)(layer as _, offset) };
if handle.is_null() {
None
} else {
Some(crate::generic::ObjectHandle { internal: handle })
}
}
pub fn draw_image(
&mut self,
resource: &DrawImageResource,
param: DrawImageParam,
) -> FilterProcResult<()> {
self.apply_param();
let resource_str = resource.to_cw_string()?;
let inner = unsafe { &*self.inner };
let success = unsafe {
(inner.draw_image)(
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
param.x,
param.y,
param.z,
param.rx,
param.ry,
param.rz,
param.sx,
param.sy,
param.sz,
param.alpha,
)
};
if success {
Ok(())
} else {
Err(FilterProcError::ApiCallFailed)
}
}
pub fn draw_poly(
&mut self,
resource: &DrawImageResource,
vertices: &VertexList,
) -> FilterProcResult<()> {
self.apply_param();
let resource_str = resource.to_cw_string()?;
let inner = unsafe { &*self.inner };
let success = match vertices {
VertexList::TriangleColor(triangles) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR> = triangles
.iter()
.flatten()
.copied()
.map(Into::into)
.collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_COLOR,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::TriangleColorNorm(triangles) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR_NORM> = triangles
.iter()
.flatten()
.copied()
.map(Into::into)
.collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_COLOR_NORM,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::TriangleTexture(triangles) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE> = triangles
.iter()
.flatten()
.copied()
.map(Into::into)
.collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_TEXTURE,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::TriangleTextureNorm(triangles) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE_NORM> = triangles
.iter()
.flatten()
.copied()
.map(Into::into)
.collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_TEXTURE_NORM,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::QuadColor(quads) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR> =
quads.iter().flatten().copied().map(Into::into).collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::QUAD_COLOR,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::QuadColorNorm(quads) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR_NORM> =
quads.iter().flatten().copied().map(Into::into).collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::QUAD_COLOR_NORM,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::QuadTexture(quads) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE> =
quads.iter().flatten().copied().map(Into::into).collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::QUAD_TEXTURE,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
VertexList::QuadTextureNorm(quads) => {
let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE_NORM> =
quads.iter().flatten().copied().map(Into::into).collect();
unsafe {
(inner.draw_poly)(
aviutl2_sys::filter2::VERTEX_TYPE::QUAD_TEXTURE_NORM,
flattened.as_ptr() as *const std::ffi::c_void,
flattened.len() as i32,
resource_str
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr()),
)
}
}
};
if success {
Ok(())
} else {
Err(FilterProcError::ApiCallFailed)
}
}
pub fn set_default_anchor(&mut self, width: u32, height: u32) {
let inner = unsafe { &*self.inner };
self.prevent_post_effect = true;
unsafe {
(inner.set_default_anchor)(width as i32, height as i32);
}
}
pub fn set_blend_mode(&mut self, mode: BlendMode) {
let inner = unsafe { &*self.inner };
unsafe {
(inner.set_blend_mode)(mode.into());
}
}
pub fn set_material_shine(&mut self, shininess: f32) {
let inner = unsafe { &*self.inner };
unsafe {
(inner.set_material_shine)(shininess);
}
}
pub fn set_sampler_mode(&mut self, mode: SamplerMode) {
let inner = unsafe { &*self.inner };
unsafe {
(inner.set_sampler_mode)(mode.into());
}
}
pub fn set_billboard_mode(&mut self, mode: BillboardMode) {
let inner = unsafe { &*self.inner };
unsafe {
(inner.set_billboard_mode)(mode.into());
}
}
pub fn create_image_resource<T: IntoBytes + Immutable>(
&mut self,
resource: &CreateImageResource,
data: &[T],
width: u32,
height: u32,
) -> FilterProcResult<()> {
let bytes = &data.as_bytes();
assert_eq!(
bytes.len(),
(width * height * 4) as usize,
"data length does not match width * height * 4"
);
let inner = unsafe { &*self.inner };
let name_cw = resource.to_cw_string()?;
unsafe {
(inner.create_image_resource)(
name_cw.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
width as i32,
height as i32,
)
};
Ok(())
}
pub fn prevent_post_effect(&mut self) {
self.prevent_post_effect = true;
}
pub(crate) fn apply_param(&mut self) {
let param = unsafe { &mut *((*self.inner).param) };
param.x = self.param.x;
param.y = self.param.y;
param.z = self.param.z;
param.rx = self.param.rx;
param.ry = self.param.ry;
param.rz = self.param.rz;
param.sx = self.param.sx;
param.sy = self.param.sy;
param.sz = self.param.sz;
param.cx = self.param.cx;
param.cy = self.param.cy;
param.cz = self.param.cz;
param.alpha = self.param.alpha;
}
}