1use std::ffi::c_void;
2use windows::core::Interface;
3
4use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
5
6use super::{FilterProcError, FilterProcResult, ObjectInfo, SceneInfo};
7
8#[derive(Debug, Clone, Copy)]
10pub struct VideoObjectInfo {
11 pub width: u32,
13 pub height: u32,
15
16 pub index: u32,
18 pub num: Option<u32>,
20}
21
22#[derive(Debug, Clone, Copy)]
24#[non_exhaustive]
25pub struct ObjectImageParam {
26 pub x: f32,
28 pub y: f32,
30 pub z: f32,
32 pub rx: f32,
34 pub ry: f32,
36 pub rz: f32,
38 pub sx: f32,
40 pub sy: f32,
42 pub sz: f32,
44 pub cx: f32,
46 pub cy: f32,
48 pub cz: f32,
50 pub alpha: f32,
52}
53
54impl From<aviutl2_sys::filter2::OBJECT_IMAGE_PARAM> for ObjectImageParam {
55 fn from(value: aviutl2_sys::filter2::OBJECT_IMAGE_PARAM) -> Self {
56 (&value).into()
57 }
58}
59
60impl From<&aviutl2_sys::filter2::OBJECT_IMAGE_PARAM> for ObjectImageParam {
61 fn from(value: &aviutl2_sys::filter2::OBJECT_IMAGE_PARAM) -> Self {
62 Self {
63 x: value.x,
64 y: value.y,
65 z: value.z,
66 rx: value.rx,
67 ry: value.ry,
68 rz: value.rz,
69 sx: value.sx,
70 sy: value.sy,
71 sz: value.sz,
72 cx: value.cx,
73 cy: value.cy,
74 cz: value.cz,
75 alpha: value.alpha,
76 }
77 }
78}
79
80#[derive(
82 Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
83)]
84pub struct RgbaPixel {
85 pub r: u8,
87 pub g: u8,
89 pub b: u8,
91 pub a: u8,
93}
94
95impl From<(u8, u8, u8, u8)> for RgbaPixel {
96 fn from(value: (u8, u8, u8, u8)) -> Self {
97 Self {
98 r: value.0,
99 g: value.1,
100 b: value.2,
101 a: value.3,
102 }
103 }
104}
105impl From<RgbaPixel> for (u8, u8, u8, u8) {
106 fn from(value: RgbaPixel) -> Self {
107 (value.r, value.g, value.b, value.a)
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
113pub enum InputImageResourcePixelFormat {
114 Rgba,
116 Bgra,
118 Bgr,
120 Pa64,
122 Hf64,
124 Yuy2,
126 Yc48,
128}
129
130impl From<InputImageResourcePixelFormat> for aviutl2_sys::filter2::INPUT_PIXEL_FORMAT {
131 fn from(value: InputImageResourcePixelFormat) -> Self {
132 match value {
133 InputImageResourcePixelFormat::Rgba => Self::RGBA,
134 InputImageResourcePixelFormat::Bgra => Self::BGRA,
135 InputImageResourcePixelFormat::Bgr => Self::BGR,
136 InputImageResourcePixelFormat::Pa64 => Self::PA64,
137 InputImageResourcePixelFormat::Hf64 => Self::HF64,
138 InputImageResourcePixelFormat::Yuy2 => Self::YUY2,
139 InputImageResourcePixelFormat::Yc48 => Self::YC48,
140 }
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
146pub enum OutputImageResourcePixelFormat {
147 Rgba,
149 Pa64,
151 Hf64,
153}
154
155impl From<OutputImageResourcePixelFormat> for aviutl2_sys::filter2::OUTPUT_PIXEL_FORMAT {
156 fn from(value: OutputImageResourcePixelFormat) -> Self {
157 match value {
158 OutputImageResourcePixelFormat::Rgba => Self::RGBA,
159 OutputImageResourcePixelFormat::Pa64 => Self::PA64,
160 OutputImageResourcePixelFormat::Hf64 => Self::HF64,
161 }
162 }
163}
164
165#[derive(Debug)]
167pub struct FilterProcVideo<U: super::FilterUserdata> {
168 pub scene: SceneInfo,
170 pub object: ObjectInfo,
172 pub video_object: VideoObjectInfo,
174
175 pub param: ObjectImageParam,
179
180 pub userdata: super::FilterUserdataHandle<U>,
182
183 pub(crate) prevent_post_effect: bool,
184
185 pub(crate) read_section: crate::generic::ReadSection,
186 pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_VIDEO,
187}
188unsafe impl<U: super::FilterUserdata> Send for FilterProcVideo<U> {}
189unsafe impl<U: super::FilterUserdata> Sync for FilterProcVideo<U> {}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
195pub enum ImageResource {
196 Object,
198 Resource(String),
200 Framebuffer,
202 TempBuffer,
204 CacheBuffer(String),
206 ImageFile(std::path::PathBuf),
208 Random,
210 Layer {
212 layer: u32,
214 apply_additional_effects: bool,
216 },
217 PreviousObject,
219}
220
221impl std::fmt::Display for ImageResource {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 match self {
224 ImageResource::Object => write!(f, "object"),
225 ImageResource::Resource(name) => write!(f, "resource:{name}"),
226 ImageResource::Framebuffer => write!(f, "framebuffer"),
227 ImageResource::TempBuffer => write!(f, "tempbuffer"),
228 ImageResource::CacheBuffer(name) => write!(f, "cache:{name}"),
229 ImageResource::ImageFile(path) => write!(f, "image:{}", path.to_string_lossy()),
230 ImageResource::Random => write!(f, "random"),
231 ImageResource::Layer {
232 layer,
233 apply_additional_effects,
234 } => write!(
235 f,
236 "layer:{layer}{}",
237 if *apply_additional_effects { "+" } else { "" }
238 ),
239 ImageResource::PreviousObject => write!(f, "before"),
240 }
241 }
242}
243
244#[derive(Debug, Clone, Copy)]
246pub struct DrawImageParam {
247 pub x: f32,
249 pub y: f32,
251 pub z: f32,
253 pub rx: f32,
255 pub ry: f32,
257 pub rz: f32,
259 pub sx: f32,
261 pub sy: f32,
263 pub sz: f32,
265 pub alpha: f32,
267}
268
269impl Default for DrawImageParam {
270 fn default() -> Self {
271 Self {
272 x: 0.0,
273 y: 0.0,
274 z: 0.0,
275 rx: 0.0,
276 ry: 0.0,
277 rz: 0.0,
278 sx: 1.0,
279 sy: 1.0,
280 sz: 1.0,
281 alpha: 1.0,
282 }
283 }
284}
285
286#[derive(Debug, Clone, Copy)]
288pub struct VertexColor {
289 pub x: f32,
290 pub y: f32,
291 pub z: f32,
292 pub r: f32,
293 pub g: f32,
294 pub b: f32,
295 pub a: f32,
296}
297
298#[derive(Debug, Clone, Copy)]
300pub struct VertexColorNorm {
301 pub x: f32,
302 pub y: f32,
303 pub z: f32,
304 pub r: f32,
305 pub g: f32,
306 pub b: f32,
307 pub a: f32,
308 pub vx: f32,
309 pub vy: f32,
310 pub vz: f32,
311}
312
313#[derive(Debug, Clone, Copy)]
315pub struct VertexTexture {
316 pub x: f32,
317 pub y: f32,
318 pub z: f32,
319 pub u: f32,
320 pub v: f32,
321 pub a: f32,
322}
323
324#[derive(Debug, Clone, Copy)]
326pub struct VertexTextureNorm {
327 pub x: f32,
328 pub y: f32,
329 pub z: f32,
330 pub u: f32,
331 pub v: f32,
332 pub a: f32,
333 pub vx: f32,
334 pub vy: f32,
335 pub vz: f32,
336}
337
338impl From<VertexColor> for aviutl2_sys::filter2::VERTEX_COLOR {
339 fn from(value: VertexColor) -> Self {
340 Self {
341 x: value.x,
342 y: value.y,
343 z: value.z,
344 r: value.r,
345 g: value.g,
346 b: value.b,
347 a: value.a,
348 }
349 }
350}
351
352impl From<VertexColorNorm> for aviutl2_sys::filter2::VERTEX_COLOR_NORM {
353 fn from(value: VertexColorNorm) -> Self {
354 Self {
355 x: value.x,
356 y: value.y,
357 z: value.z,
358 r: value.r,
359 g: value.g,
360 b: value.b,
361 a: value.a,
362 vx: value.vx,
363 vy: value.vy,
364 vz: value.vz,
365 }
366 }
367}
368
369impl From<VertexTexture> for aviutl2_sys::filter2::VERTEX_TEXTURE {
370 fn from(value: VertexTexture) -> Self {
371 Self {
372 x: value.x,
373 y: value.y,
374 z: value.z,
375 u: value.u,
376 v: value.v,
377 a: value.a,
378 }
379 }
380}
381
382impl From<VertexTextureNorm> for aviutl2_sys::filter2::VERTEX_TEXTURE_NORM {
383 fn from(value: VertexTextureNorm) -> Self {
384 Self {
385 x: value.x,
386 y: value.y,
387 z: value.z,
388 u: value.u,
389 v: value.v,
390 a: value.a,
391 vx: value.vx,
392 vy: value.vy,
393 vz: value.vz,
394 }
395 }
396}
397
398pub enum VertexList {
400 TriangleColor(Vec<[VertexColor; 3]>),
401 TriangleColorNorm(Vec<[VertexColorNorm; 3]>),
402 TriangleTexture(Vec<[VertexTexture; 3]>),
403 TriangleTextureNorm(Vec<[VertexTextureNorm; 3]>),
404 QuadColor(Vec<[VertexColor; 4]>),
405 QuadColorNorm(Vec<[VertexColorNorm; 4]>),
406 QuadTexture(Vec<[VertexTexture; 4]>),
407 QuadTextureNorm(Vec<[VertexTextureNorm; 4]>),
408}
409
410#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
412pub enum BlendMode {
413 #[default]
415 None,
416 Add,
418 Sub,
420 Mul,
422 Screen,
424 Overlay,
426 Light,
428 Dark,
430 Brightness,
432 Chroma,
434 Shadow,
436 LightDark,
438 Diff,
440}
441
442#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
444pub enum SamplerMode {
445 #[default]
447 Clip,
448 Clamp,
450 Loop,
452 Mirror,
454 Dot,
456}
457
458#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
460pub enum BillboardMode {
461 #[default]
463 None,
464 Side,
466 Direction,
468 Camera,
470}
471
472#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
474pub enum BlendStateMode {
475 #[default]
477 Copy,
478 Mask,
480 Draw,
482 Add,
484}
485
486impl From<BlendMode> for aviutl2_sys::filter2::BLEND_MODE {
487 fn from(value: BlendMode) -> Self {
488 match value {
489 BlendMode::None => Self::NONE,
490 BlendMode::Add => Self::ADD,
491 BlendMode::Sub => Self::SUB,
492 BlendMode::Mul => Self::MUL,
493 BlendMode::Screen => Self::SCREEN,
494 BlendMode::Overlay => Self::OVERLAY,
495 BlendMode::Light => Self::LIGHT,
496 BlendMode::Dark => Self::DARK,
497 BlendMode::Brightness => Self::BRIGHTNESS,
498 BlendMode::Chroma => Self::CHROMA,
499 BlendMode::Shadow => Self::SHADOW,
500 BlendMode::LightDark => Self::LIGHT_DARK,
501 BlendMode::Diff => Self::DIFF,
502 }
503 }
504}
505impl From<SamplerMode> for aviutl2_sys::filter2::SAMPLER_MODE {
506 fn from(value: SamplerMode) -> Self {
507 match value {
508 SamplerMode::Clip => Self::CLIP,
509 SamplerMode::Clamp => Self::CLAMP,
510 SamplerMode::Loop => Self::LOOP,
511 SamplerMode::Mirror => Self::MIRROR,
512 SamplerMode::Dot => Self::DOT,
513 }
514 }
515}
516impl From<BillboardMode> for aviutl2_sys::filter2::BILLBOARD_MODE {
517 fn from(value: BillboardMode) -> Self {
518 match value {
519 BillboardMode::None => Self::NONE,
520 BillboardMode::Side => Self::SIDE,
521 BillboardMode::Direction => Self::DIRECTION,
522 BillboardMode::Camera => Self::CAMERA,
523 }
524 }
525}
526impl From<BlendStateMode> for aviutl2_sys::filter2::BLEND_STATE_MODE {
527 fn from(value: BlendStateMode) -> Self {
528 match value {
529 BlendStateMode::Copy => Self::COPY,
530 BlendStateMode::Mask => Self::MASK,
531 BlendStateMode::Draw => Self::DRAW,
532 BlendStateMode::Add => Self::ADD,
533 }
534 }
535}
536
537fn vertex_list_as_raw(
538 vertices: &VertexList,
539 f: impl FnOnce(aviutl2_sys::filter2::VERTEX_TYPE, *const c_void, i32) -> bool,
540) -> bool {
541 match vertices {
542 VertexList::TriangleColor(triangles) => {
543 let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR> = triangles
544 .iter()
545 .flatten()
546 .copied()
547 .map(Into::into)
548 .collect();
549 f(
550 aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_COLOR,
551 flattened.as_ptr() as *const c_void,
552 flattened.len() as i32,
553 )
554 }
555 VertexList::TriangleColorNorm(triangles) => {
556 let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR_NORM> = triangles
557 .iter()
558 .flatten()
559 .copied()
560 .map(Into::into)
561 .collect();
562 f(
563 aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_COLOR_NORM,
564 flattened.as_ptr() as *const c_void,
565 flattened.len() as i32,
566 )
567 }
568 VertexList::TriangleTexture(triangles) => {
569 let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE> = triangles
570 .iter()
571 .flatten()
572 .copied()
573 .map(Into::into)
574 .collect();
575 f(
576 aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_TEXTURE,
577 flattened.as_ptr() as *const c_void,
578 flattened.len() as i32,
579 )
580 }
581 VertexList::TriangleTextureNorm(triangles) => {
582 let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE_NORM> = triangles
583 .iter()
584 .flatten()
585 .copied()
586 .map(Into::into)
587 .collect();
588 f(
589 aviutl2_sys::filter2::VERTEX_TYPE::TRIANGLE_TEXTURE_NORM,
590 flattened.as_ptr() as *const c_void,
591 flattened.len() as i32,
592 )
593 }
594 VertexList::QuadColor(quads) => {
595 let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR> =
596 quads.iter().flatten().copied().map(Into::into).collect();
597 f(
598 aviutl2_sys::filter2::VERTEX_TYPE::QUAD_COLOR,
599 flattened.as_ptr() as *const c_void,
600 flattened.len() as i32,
601 )
602 }
603 VertexList::QuadColorNorm(quads) => {
604 let flattened: Vec<aviutl2_sys::filter2::VERTEX_COLOR_NORM> =
605 quads.iter().flatten().copied().map(Into::into).collect();
606 f(
607 aviutl2_sys::filter2::VERTEX_TYPE::QUAD_COLOR_NORM,
608 flattened.as_ptr() as *const c_void,
609 flattened.len() as i32,
610 )
611 }
612 VertexList::QuadTexture(quads) => {
613 let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE> =
614 quads.iter().flatten().copied().map(Into::into).collect();
615 f(
616 aviutl2_sys::filter2::VERTEX_TYPE::QUAD_TEXTURE,
617 flattened.as_ptr() as *const c_void,
618 flattened.len() as i32,
619 )
620 }
621 VertexList::QuadTextureNorm(quads) => {
622 let flattened: Vec<aviutl2_sys::filter2::VERTEX_TEXTURE_NORM> =
623 quads.iter().flatten().copied().map(Into::into).collect();
624 f(
625 aviutl2_sys::filter2::VERTEX_TYPE::QUAD_TEXTURE_NORM,
626 flattened.as_ptr() as *const c_void,
627 flattened.len() as i32,
628 )
629 }
630 }
631}
632
633fn image_resource_ptr_list(
634 resources: &[ImageResource],
635) -> Result<
636 (
637 Vec<crate::common::CWString>,
638 Vec<aviutl2_sys::common::LPCWSTR>,
639 ),
640 FilterProcError,
641> {
642 let strings = resources
643 .iter()
644 .map(|resource| crate::common::CWString::new(&resource.to_string()))
645 .collect::<Result<Vec<_>, _>>()?;
646 let ptrs = strings.iter().map(|s| s.as_ptr()).collect();
647 Ok((strings, ptrs))
648}
649
650fn image_data_byte_len(pitch: u32, height: u32) -> FilterProcResult<usize> {
651 (pitch as usize)
652 .checked_mul(height as usize)
653 .ok_or(FilterProcError::ValueOutOfRange)
654}
655
656impl<U: super::FilterUserdata> FilterProcVideo<U> {
657 pub fn set_filter_config_data_size(
661 &self,
662 data: &mut crate::filter::VariableLengthFilterConfigDataHandle,
663 size: usize,
664 ) -> FilterProcResult<()> {
665 data.try_resize(size, |data, size| unsafe {
666 ((*self.inner).set_filter_item_data_size)(data, size);
667 })
668 }
669
670 pub fn get_image_data<T>(&mut self, buffer: &mut [T]) -> usize
679 where
680 T: Copy + FromBytes + Immutable,
681 {
682 if self.video_object.width == 0 || self.video_object.height == 0 {
683 tracing::warn!("width or height is 0, perhaps the filter plugin is a custom object");
684 return 0;
685 }
686 assert_eq!(
687 std::mem::size_of_val(buffer),
688 (self.video_object.width * self.video_object.height * 4) as usize,
689 "buffer length as bytes does not match width * height * 4"
690 );
691 assert!(
692 std::mem::align_of::<T>() >= std::mem::align_of::<aviutl2_sys::filter2::PIXEL_RGBA>(),
693 "buffer alignment is not sufficient"
694 );
695 let width = self.video_object.width as usize;
696 let height = self.video_object.height as usize;
697 let inner = unsafe { &*self.inner };
698 unsafe {
699 (inner.get_image_data)(
700 buffer.as_mut_ptr() as *mut u8 as *mut aviutl2_sys::filter2::PIXEL_RGBA
701 )
702 };
703
704 width * height * 4
705 }
706
707 pub fn set_image_data<T: IntoBytes + Immutable>(
713 &mut self,
714 data: &[T],
715 width: u32,
716 height: u32,
717 ) {
718 let bytes = &data.as_bytes();
719 assert_eq!(
720 bytes.len(),
721 (width * height * 4) as usize,
722 "data length does not match width * height * 4"
723 );
724 let inner = unsafe { &*self.inner };
725 unsafe {
726 (inner.set_image_data)(
727 bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
728 width as i32,
729 height as i32,
730 )
731 };
732 }
733
734 pub fn get_image_texture2d(
740 &mut self,
741 ) -> Option<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> {
742 let inner = unsafe { &*self.inner };
743 let ptr = unsafe { (inner.get_image_texture2d)() };
744 (!ptr.is_null()).then(|| unsafe {
745 windows::Win32::Graphics::Direct3D11::ID3D11Texture2D::from_raw(ptr)
746 })
747 }
748
749 pub fn get_framebuffer_texture2d(
755 &mut self,
756 ) -> Option<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> {
757 let inner = unsafe { &*self.inner };
758 let ptr = unsafe { (inner.get_framebuffer_texture2d)() };
759 (!ptr.is_null()).then(|| unsafe {
760 windows::Win32::Graphics::Direct3D11::ID3D11Texture2D::from_raw(ptr)
761 })
762 }
763
764 pub fn read_section(&mut self) -> &crate::generic::ReadSection {
766 &self.read_section
767 }
768
769 pub fn get_output_image_param(
776 &mut self,
777 object: Option<crate::generic::ObjectHandle>,
778 offset: f64,
779 ) -> FilterProcResult<ObjectImageParam> {
780 let inner = unsafe { &*self.inner };
781 let mut param = std::mem::MaybeUninit::<aviutl2_sys::filter2::OBJECT_IMAGE_PARAM>::uninit();
782 unsafe {
783 let ok = (inner.get_output_image_param)(
784 object.map_or(std::ptr::null_mut(), |o| o.internal as *mut _),
785 offset,
786 param.as_mut_ptr(),
787 std::mem::size_of::<aviutl2_sys::filter2::OBJECT_IMAGE_PARAM>() as i32,
788 );
789 if ok {
790 Ok(param.assume_init().into())
791 } else {
792 Err(FilterProcError::ApiCallFailed)
793 }
794 }
795 }
796
797 pub fn get_image_object(
799 &mut self,
800 layer: u32,
801 offset: f64,
802 ) -> Option<crate::generic::ObjectHandle> {
803 let handle = unsafe { ((*self.inner).get_image_object)(layer as _, offset) };
804 if handle.is_null() {
805 None
806 } else {
807 Some(crate::generic::ObjectHandle { internal: handle })
808 }
809 }
810
811 pub fn draw_image(
820 &mut self,
821 resource: &ImageResource,
822 param: DrawImageParam,
823 ) -> FilterProcResult<()> {
824 self.apply_param();
825 let resource_str = crate::common::CWString::new(&resource.to_string())?;
826 let inner = unsafe { &*self.inner };
827 let success = unsafe {
828 (inner.draw_image)(
829 resource_str.as_ptr(),
830 param.x,
831 param.y,
832 param.z,
833 param.rx,
834 param.ry,
835 param.rz,
836 param.sx,
837 param.sy,
838 param.sz,
839 param.alpha,
840 )
841 };
842 if success {
843 Ok(())
844 } else {
845 Err(FilterProcError::ApiCallFailed)
846 }
847 }
848
849 pub fn draw_poly(
858 &mut self,
859 vertices: &VertexList,
860 resource: Option<&ImageResource>,
861 ) -> FilterProcResult<()> {
862 self.apply_param();
863
864 let resource_str = crate::common::CWString::new(
865 &resource.map_or_else(|| "tempbuffer".to_string(), |r| r.to_string()),
866 )?;
867 let inner = unsafe { &*self.inner };
868 let success = vertex_list_as_raw(vertices, |vertex_type, vertex_list, vertex_num| unsafe {
869 (inner.draw_poly)(vertex_type, vertex_list, vertex_num, resource_str.as_ptr())
870 });
871 if success {
872 Ok(())
873 } else {
874 Err(FilterProcError::ApiCallFailed)
875 }
876 }
877
878 pub fn set_default_anchor(&mut self, width: u32, height: u32) {
884 let inner = unsafe { &*self.inner };
885 self.prevent_post_effect = true;
886 unsafe {
887 (inner.set_default_anchor)(width as i32, height as i32);
888 }
889 }
890
891 pub fn set_blend_mode(&mut self, mode: BlendMode) {
893 let inner = unsafe { &*self.inner };
894 unsafe {
895 (inner.set_blend_mode)(mode.into());
896 }
897 }
898
899 pub fn set_material_shine(&mut self, shininess: f32) {
902 let inner = unsafe { &*self.inner };
903 unsafe {
904 (inner.set_material_shine)(shininess);
905 }
906 }
907
908 pub fn set_sampler_mode(&mut self, mode: SamplerMode) {
910 let inner = unsafe { &*self.inner };
911 unsafe {
912 (inner.set_sampler_mode)(mode.into());
913 }
914 }
915
916 pub fn set_culling_state(&mut self, culling: bool) {
918 let inner = unsafe { &*self.inner };
919 unsafe {
920 (inner.set_culling_state)(culling);
921 }
922 }
923
924 pub fn set_billboard_mode(&mut self, mode: BillboardMode) {
926 let inner = unsafe { &*self.inner };
927 unsafe {
928 (inner.set_billboard_mode)(mode.into());
929 }
930 }
931
932 pub fn create_image_resource<T: IntoBytes + Immutable>(
949 &mut self,
950 resource: &ImageResource,
951 data: &[T],
952 width: u32,
953 height: u32,
954 ) -> FilterProcResult<()> {
955 let bytes = &data.as_bytes();
956 assert_eq!(
957 bytes.len(),
958 (width * height * 4) as usize,
959 "data length does not match width * height * 4"
960 );
961 let inner = unsafe { &*self.inner };
962 let name_cw = crate::common::CWString::new(&resource.to_string())?;
963 unsafe {
964 (inner.create_image_resource)(
965 name_cw.as_ptr(),
966 bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
967 width as i32,
968 height as i32,
969 )
970 };
971 Ok(())
972 }
973
974 pub fn get_image_resource_texture2d(
983 &mut self,
984 resource: &ImageResource,
985 ) -> FilterProcResult<*mut c_void> {
986 let inner = unsafe { &*self.inner };
987 let resource_cw = crate::common::CWString::new(&resource.to_string())?;
988 let ptr = unsafe { (inner.get_image_resource_texture2d)(resource_cw.as_ptr()) };
989 if ptr.is_null() {
990 Err(FilterProcError::ApiCallFailed)
991 } else {
992 Ok(ptr)
993 }
994 }
995
996 pub fn get_image_resource_size(
1004 &mut self,
1005 resource: &ImageResource,
1006 ) -> FilterProcResult<(u32, u32)> {
1007 let inner = unsafe { &*self.inner };
1008 let resource_cw = crate::common::CWString::new(&resource.to_string())?;
1009 let mut width = 0;
1010 let mut height = 0;
1011 let success = unsafe {
1012 (inner.get_image_resource_size)(resource_cw.as_ptr(), &mut width, &mut height)
1013 };
1014 if success {
1015 Ok((width as u32, height as u32))
1016 } else {
1017 Err(FilterProcError::ApiCallFailed)
1018 }
1019 }
1020
1021 pub fn copy_image_resource(
1034 &mut self,
1035 src_resource: &ImageResource,
1036 dst_resource: &ImageResource,
1037 ) -> FilterProcResult<()> {
1038 let inner = unsafe { &*self.inner };
1039 let dst_resource = crate::common::CWString::new(&dst_resource.to_string())?;
1040 let src_resource = crate::common::CWString::new(&src_resource.to_string())?;
1041 let success =
1042 unsafe { (inner.copy_image_resource)(dst_resource.as_ptr(), src_resource.as_ptr()) };
1043 if success {
1044 Ok(())
1045 } else {
1046 Err(FilterProcError::ApiCallFailed)
1047 }
1048 }
1049
1050 pub fn clear_image_resource(
1058 &mut self,
1059 resource: &ImageResource,
1060 color: RgbaPixel,
1061 ) -> FilterProcResult<()> {
1062 let inner = unsafe { &*self.inner };
1063 let resource = crate::common::CWString::new(&resource.to_string())?;
1064 let color = aviutl2_sys::filter2::PIXEL_RGBA {
1065 r: color.r,
1066 g: color.g,
1067 b: color.b,
1068 a: color.a,
1069 };
1070 let success = unsafe { (inner.clear_image_resource)(resource.as_ptr(), color) };
1071 if success {
1072 Ok(())
1073 } else {
1074 Err(FilterProcError::ApiCallFailed)
1075 }
1076 }
1077
1078 pub fn get_image_resource_data(
1087 &mut self,
1088 resource: &ImageResource,
1089 buffer: &mut [u8],
1090 width: u32,
1091 height: u32,
1092 pitch: u32,
1093 format: OutputImageResourcePixelFormat,
1094 ) -> FilterProcResult<()> {
1095 let min_len = image_data_byte_len(pitch, height)?;
1096 assert!(
1097 buffer.len() >= min_len,
1098 "buffer length must be at least pitch * height"
1099 );
1100 let inner = unsafe { &*self.inner };
1101 let resource = crate::common::CWString::new(&resource.to_string())?;
1102 let success = unsafe {
1103 (inner.get_image_resource_data)(
1104 resource.as_ptr(),
1105 buffer.as_mut_ptr() as *mut c_void,
1106 width as i32,
1107 height as i32,
1108 pitch as i32,
1109 format.into(),
1110 )
1111 };
1112 if success {
1113 Ok(())
1114 } else {
1115 Err(FilterProcError::ApiCallFailed)
1116 }
1117 }
1118
1119 pub fn set_image_resource_data<T: IntoBytes + Immutable>(
1128 &mut self,
1129 resource: &ImageResource,
1130 data: &[T],
1131 width: u32,
1132 height: u32,
1133 pitch: u32,
1134 format: InputImageResourcePixelFormat,
1135 ) -> FilterProcResult<()> {
1136 let bytes = data.as_bytes();
1137 let min_len = image_data_byte_len(pitch, height)?;
1138 assert!(
1139 bytes.len() >= min_len,
1140 "data length must be at least pitch * height"
1141 );
1142 let inner = unsafe { &*self.inner };
1143 let resource = crate::common::CWString::new(&resource.to_string())?;
1144 let success = unsafe {
1145 (inner.set_image_resource_data)(
1146 resource.as_ptr(),
1147 bytes.as_ptr() as *const c_void,
1148 width as i32,
1149 height as i32,
1150 pitch as i32,
1151 format.into(),
1152 )
1153 };
1154 if success {
1155 Ok(())
1156 } else {
1157 Err(FilterProcError::ApiCallFailed)
1158 }
1159 }
1160
1161 pub fn draw_image_to_resource(
1172 &mut self,
1173 src_resource: &ImageResource,
1174 dst_resource: &ImageResource,
1175 param: DrawImageParam,
1176 ) -> FilterProcResult<()> {
1177 self.apply_param();
1178 let inner = unsafe { &*self.inner };
1179 let dst_resource = crate::common::CWString::new(&dst_resource.to_string())?;
1180 let src_resource = crate::common::CWString::new(&src_resource.to_string())?;
1181 let success = unsafe {
1182 (inner.draw_image_to_resource)(
1183 dst_resource.as_ptr(),
1184 src_resource.as_ptr(),
1185 param.x,
1186 param.y,
1187 param.z,
1188 param.rx,
1189 param.ry,
1190 param.rz,
1191 param.sx,
1192 param.sy,
1193 param.sz,
1194 param.alpha,
1195 )
1196 };
1197 if success {
1198 Ok(())
1199 } else {
1200 Err(FilterProcError::ApiCallFailed)
1201 }
1202 }
1203
1204 pub fn draw_poly_to_resource(
1216 &mut self,
1217 dst_resource: &ImageResource,
1218 vertices: &VertexList,
1219 src_resource: Option<&ImageResource>,
1220 ) -> FilterProcResult<()> {
1221 self.apply_param();
1222 let inner = unsafe { &*self.inner };
1223 let dst_resource = crate::common::CWString::new(&dst_resource.to_string())?;
1224 let src_resource = crate::common::CWString::new(
1225 &src_resource.map_or_else(|| "tempbuffer".to_string(), |r| r.to_string()),
1226 )?;
1227 let success = vertex_list_as_raw(vertices, |vertex_type, vertex_list, vertex_num| unsafe {
1228 (inner.draw_poly_to_resource)(
1229 dst_resource.as_ptr(),
1230 vertex_type,
1231 vertex_list as *mut c_void,
1232 vertex_num,
1233 src_resource.as_ptr(),
1234 )
1235 });
1236 if success {
1237 Ok(())
1238 } else {
1239 Err(FilterProcError::ApiCallFailed)
1240 }
1241 }
1242
1243 pub fn exec_pixelshader<T: Copy>(
1258 &mut self,
1259 cso_file: &str,
1260 target: &ImageResource,
1261 resources: &[ImageResource],
1262 constant: T,
1263 blend_state: &windows::Win32::Graphics::Direct3D11::ID3D11BlendState,
1264 sampler_state: &windows::Win32::Graphics::Direct3D11::ID3D11SamplerState,
1265 ) -> FilterProcResult<()> {
1266 self.apply_param();
1267 let inner = unsafe { &*self.inner };
1268 let cso_file = crate::common::CWString::new(cso_file)?;
1269 let target = crate::common::CWString::new(&target.to_string())?;
1270 let (_resource_strings, mut resource_ptrs) = image_resource_ptr_list(resources)?;
1271 let constant_ptr = Box::new(constant);
1272 let success = unsafe {
1273 (inner.exec_pixelshader_file)(
1274 cso_file.as_ptr(),
1275 target.as_ptr(),
1276 if resource_ptrs.is_empty() {
1277 std::ptr::null_mut()
1278 } else {
1279 resource_ptrs.as_mut_ptr()
1280 },
1281 resource_ptrs.len() as i32,
1282 constant_ptr.as_ref() as *const T as *mut c_void,
1283 std::mem::size_of::<T>() as i32,
1284 blend_state.as_raw(),
1285 sampler_state.as_raw(),
1286 )
1287 };
1288 if success {
1289 Ok(())
1290 } else {
1291 Err(FilterProcError::ApiCallFailed)
1292 }
1293 }
1294
1295 pub fn exec_pixelshader_data<T: Copy>(
1310 &mut self,
1311 data: &[u8],
1312 target: &ImageResource,
1313 resources: &[ImageResource],
1314 constant: T,
1315 blend_state: &windows::Win32::Graphics::Direct3D11::ID3D11BlendState,
1316 sampler_state: &windows::Win32::Graphics::Direct3D11::ID3D11SamplerState,
1317 ) -> FilterProcResult<()> {
1318 self.apply_param();
1319 let inner = unsafe { &*self.inner };
1320 let data_size = i32::try_from(data.len()).map_err(|_| FilterProcError::ValueOutOfRange)?;
1321 let target = crate::common::CWString::new(&target.to_string())?;
1322 let (_resource_strings, mut resource_ptrs) = image_resource_ptr_list(resources)?;
1323 let constant_ptr = Box::new(constant);
1324 let success = unsafe {
1325 (inner.exec_pixelshader_data)(
1326 data.as_ptr(),
1327 data_size,
1328 target.as_ptr(),
1329 if resource_ptrs.is_empty() {
1330 std::ptr::null_mut()
1331 } else {
1332 resource_ptrs.as_mut_ptr()
1333 },
1334 resource_ptrs.len() as i32,
1335 constant_ptr.as_ref() as *const T as *mut c_void,
1336 std::mem::size_of::<T>() as i32,
1337 blend_state.as_raw(),
1338 sampler_state.as_raw(),
1339 )
1340 };
1341 if success {
1342 Ok(())
1343 } else {
1344 Err(FilterProcError::ApiCallFailed)
1345 }
1346 }
1347
1348 pub fn exec_computeshader<T: Copy>(
1363 &mut self,
1364 cso_file: &str,
1365 targets: &[ImageResource],
1366 resources: &[ImageResource],
1367 constant: T,
1368 count: [u32; 3],
1369 sampler_state: &windows::Win32::Graphics::Direct3D11::ID3D11SamplerState,
1370 ) -> FilterProcResult<()> {
1371 self.apply_param();
1372 let inner = unsafe { &*self.inner };
1373 let cso_file = crate::common::CWString::new(cso_file)?;
1374 let (_target_strings, mut target_ptrs) = image_resource_ptr_list(targets)?;
1375 let (_resource_strings, mut resource_ptrs) = image_resource_ptr_list(resources)?;
1376 let constant_ptr = Box::new(constant);
1377 let success = unsafe {
1378 (inner.exec_computeshader_file)(
1379 cso_file.as_ptr(),
1380 if target_ptrs.is_empty() {
1381 std::ptr::null_mut()
1382 } else {
1383 target_ptrs.as_mut_ptr()
1384 },
1385 target_ptrs.len() as i32,
1386 if resource_ptrs.is_empty() {
1387 std::ptr::null_mut()
1388 } else {
1389 resource_ptrs.as_mut_ptr()
1390 },
1391 resource_ptrs.len() as i32,
1392 constant_ptr.as_ref() as *const T as *mut c_void,
1393 std::mem::size_of::<T>() as i32,
1394 count[0] as i32,
1395 count[1] as i32,
1396 count[2] as i32,
1397 sampler_state.as_raw(),
1398 )
1399 };
1400 if success {
1401 Ok(())
1402 } else {
1403 Err(FilterProcError::ApiCallFailed)
1404 }
1405 }
1406
1407 pub fn exec_computeshader_data<T: Copy>(
1422 &mut self,
1423 data: &[u8],
1424 targets: &[ImageResource],
1425 resources: &[ImageResource],
1426 constant: T,
1427 count: [u32; 3],
1428 sampler_state: &windows::Win32::Graphics::Direct3D11::ID3D11SamplerState,
1429 ) -> FilterProcResult<()> {
1430 self.apply_param();
1431 let inner = unsafe { &*self.inner };
1432 let data_size = i32::try_from(data.len()).map_err(|_| FilterProcError::ValueOutOfRange)?;
1433 let (_target_strings, mut target_ptrs) = image_resource_ptr_list(targets)?;
1434 let (_resource_strings, mut resource_ptrs) = image_resource_ptr_list(resources)?;
1435 let constant_ptr = Box::new(constant);
1436 let success = unsafe {
1437 (inner.exec_computeshader_data)(
1438 data.as_ptr(),
1439 data_size,
1440 if target_ptrs.is_empty() {
1441 std::ptr::null_mut()
1442 } else {
1443 target_ptrs.as_mut_ptr()
1444 },
1445 target_ptrs.len() as i32,
1446 if resource_ptrs.is_empty() {
1447 std::ptr::null_mut()
1448 } else {
1449 resource_ptrs.as_mut_ptr()
1450 },
1451 resource_ptrs.len() as i32,
1452 constant_ptr.as_ref() as *const T as *mut c_void,
1453 std::mem::size_of::<T>() as i32,
1454 count[0] as i32,
1455 count[1] as i32,
1456 count[2] as i32,
1457 sampler_state.as_raw(),
1458 )
1459 };
1460 if success {
1461 Ok(())
1462 } else {
1463 Err(FilterProcError::ApiCallFailed)
1464 }
1465 }
1466
1467 pub fn release_image_resource(&mut self, resource: &ImageResource) -> FilterProcResult<()> {
1473 let inner = unsafe { &*self.inner };
1474 let resource = crate::common::CWString::new(&resource.to_string())?;
1475 let success = unsafe { (inner.release_image_resource)(resource.as_ptr()) };
1476 if success {
1477 Ok(())
1478 } else {
1479 Err(FilterProcError::ApiCallFailed)
1480 }
1481 }
1482
1483 pub fn exec_effect<'a, C, K, V>(
1494 &mut self,
1495 effect_name: &str,
1496 param: C,
1497 resource: &ImageResource,
1498 ) -> FilterProcResult<()>
1499 where
1500 C: IntoIterator<Item = (&'a K, &'a V)>,
1501 K: AsRef<str> + 'a,
1502 V: AsRef<str> + 'a,
1503 {
1504 let inner = unsafe { &*self.inner };
1505 let effect_name = crate::common::CWString::new(effect_name)?;
1506 let resource = crate::common::CWString::new(&resource.to_string())?;
1507 let mut param_key_cwstrs = vec![];
1508 let mut param_value_cstrs = vec![];
1509 for (key, value) in param {
1510 param_key_cwstrs.push(crate::common::CWString::new(key.as_ref())?);
1511 param_value_cstrs.push(std::ffi::CString::new(value.as_ref())?);
1512 }
1513
1514 let mut params = param_key_cwstrs
1515 .iter()
1516 .zip(param_value_cstrs.iter())
1517 .map(|(key, value)| aviutl2_sys::filter2::EFFECT_ITEM_PARAM {
1518 name: key.as_ptr(),
1519 value: value.as_ptr(),
1520 })
1521 .collect::<Vec<_>>();
1522
1523 let success = unsafe {
1524 (inner.exec_effect)(
1525 effect_name.as_ptr(),
1526 if params.is_empty() {
1527 std::ptr::null_mut()
1528 } else {
1529 params.as_mut_ptr()
1530 },
1531 params.len() as i32,
1532 resource.as_ptr(),
1533 )
1534 };
1535 if success {
1536 Ok(())
1537 } else {
1538 Err(FilterProcError::ApiCallFailed)
1539 }
1540 }
1541
1542 pub fn get_blend_state(
1544 &mut self,
1545 mode: BlendStateMode,
1546 ) -> Option<windows::Win32::Graphics::Direct3D11::ID3D11BlendState> {
1547 let inner = unsafe { &*self.inner };
1548 let ptr = unsafe { (inner.get_blend_state)(mode.into()) };
1549 (!ptr.is_null()).then_some(unsafe {
1550 windows::Win32::Graphics::Direct3D11::ID3D11BlendState::from_raw(ptr)
1551 })
1552 }
1553
1554 pub fn get_sampler_state(
1556 &mut self,
1557 mode: SamplerMode,
1558 ) -> Option<windows::Win32::Graphics::Direct3D11::ID3D11SamplerState> {
1559 let inner = unsafe { &*self.inner };
1560 let ptr = unsafe { (inner.get_sampler_state)(mode.into()) };
1561 (!ptr.is_null()).then_some(unsafe {
1562 windows::Win32::Graphics::Direct3D11::ID3D11SamplerState::from_raw(ptr)
1563 })
1564 }
1565
1566 pub fn prevent_post_effect(&mut self) {
1570 self.prevent_post_effect = true;
1571 }
1572
1573 pub(crate) fn apply_param(&mut self) {
1574 let param = unsafe { &mut *((*self.inner).param) };
1575 param.x = self.param.x;
1576 param.y = self.param.y;
1577 param.z = self.param.z;
1578 param.rx = self.param.rx;
1579 param.ry = self.param.ry;
1580 param.rz = self.param.rz;
1581 param.sx = self.param.sx;
1582 param.sy = self.param.sy;
1583 param.sz = self.param.sz;
1584 param.cx = self.param.cx;
1585 param.cy = self.param.cy;
1586 param.cz = self.param.cz;
1587 param.alpha = self.param.alpha;
1588 }
1589}
1590
1591#[cfg(test)]
1592mod tests {
1593 use super::*;
1594
1595 #[test]
1596 fn formats_image_resources_for_sdk() {
1597 assert_eq!(ImageResource::Object.to_string(), "object");
1598 assert_eq!(
1599 ImageResource::Resource("name".to_string()).to_string(),
1600 "resource:name"
1601 );
1602 assert_eq!(ImageResource::Framebuffer.to_string(), "framebuffer");
1603 assert_eq!(ImageResource::TempBuffer.to_string(), "tempbuffer");
1604 assert_eq!(
1605 ImageResource::CacheBuffer("name".to_string()).to_string(),
1606 "cache:name"
1607 );
1608 assert_eq!(
1609 ImageResource::ImageFile(std::path::PathBuf::from("foo.png")).to_string(),
1610 "image:foo.png"
1611 );
1612 assert_eq!(ImageResource::Random.to_string(), "random");
1613 assert_eq!(
1614 ImageResource::Layer {
1615 layer: 3,
1616 apply_additional_effects: false,
1617 }
1618 .to_string(),
1619 "layer:3"
1620 );
1621 assert_eq!(
1622 ImageResource::Layer {
1623 layer: 3,
1624 apply_additional_effects: true,
1625 }
1626 .to_string(),
1627 "layer:3+"
1628 );
1629 assert_eq!(ImageResource::PreviousObject.to_string(), "before");
1630 }
1631
1632 #[allow(dead_code)]
1633 fn smoke_new_filter2_api(video: &mut FilterProcVideo<()>) -> FilterProcResult<()> {
1634 let writable = ImageResource::Resource("dst".to_string());
1635 let readable = ImageResource::Resource("src".to_string());
1636 let drawable = ImageResource::Resource("src".to_string());
1637 let target = ImageResource::Resource("target".to_string());
1638 let vertices = VertexList::TriangleColor(vec![[
1639 VertexColor {
1640 x: 0.0,
1641 y: 0.0,
1642 z: 0.0,
1643 r: 1.0,
1644 g: 0.0,
1645 b: 0.0,
1646 a: 1.0,
1647 },
1648 VertexColor {
1649 x: 1.0,
1650 y: 0.0,
1651 z: 0.0,
1652 r: 0.0,
1653 g: 1.0,
1654 b: 0.0,
1655 a: 1.0,
1656 },
1657 VertexColor {
1658 x: 0.0,
1659 y: 1.0,
1660 z: 0.0,
1661 r: 0.0,
1662 g: 0.0,
1663 b: 1.0,
1664 a: 1.0,
1665 },
1666 ]]);
1667 let constant = [0u8; 16];
1668
1669 video.set_culling_state(true);
1670 let _ = video.get_image_resource_texture2d(&readable)?;
1671 video.copy_image_resource(&readable, &writable)?;
1672 video.clear_image_resource(&writable, RgbaPixel::default())?;
1673 video.draw_image_to_resource(&drawable, &writable, DrawImageParam::default())?;
1674 video.draw_poly_to_resource(&writable, &vertices, Some(&drawable))?;
1675 let blend_state = video.get_blend_state(BlendStateMode::Draw).unwrap();
1676 let sampler_state = video.get_sampler_state(SamplerMode::Clamp).unwrap();
1677 video.exec_pixelshader(
1678 "shader.cso",
1679 &target,
1680 std::slice::from_ref(&readable),
1681 Some(&constant),
1682 &blend_state,
1683 &sampler_state,
1684 )?;
1685 video.exec_computeshader(
1686 "compute.cso",
1687 std::slice::from_ref(&target),
1688 std::slice::from_ref(&readable),
1689 Some(&constant),
1690 [1, 1, 1],
1691 &sampler_state,
1692 )?;
1693 Ok(())
1694 }
1695}