Skip to main content

aviutl2/
common.rs

1pub use anyhow::Result as AnyResult;
2use zerocopy::{Immutable, IntoBytes};
3
4pub use half::{self, f16};
5pub use num_rational::{self, Rational32};
6pub use raw_window_handle::{self, Win32WindowHandle};
7
8/// AviUtl2の情報。
9#[derive(Debug, Clone)]
10pub struct AviUtl2Info {
11    /// AviUtl2のバージョン。
12    pub version: AviUtl2Version,
13}
14
15/// 対応する最小のAviUtl2バージョン。
16pub const MINIMUM_AVIUTL2_VERSION: AviUtl2Version = AviUtl2Version(2004500);
17
18/// AviUtl2のバージョンがサポート範囲かを確認します。
19pub fn ensure_minimum_aviutl2_version(version: AviUtl2Version) -> AnyResult<()> {
20    anyhow::ensure!(
21        version >= MINIMUM_AVIUTL2_VERSION,
22        "AviUtl2 version {version} is not supported. {MINIMUM_AVIUTL2_VERSION} or higher is required.",
23    );
24    Ok(())
25}
26
27/// AviUtl2のバージョン。
28///
29/// # Note
30///
31/// バージョン番号の形式は公式に定義されていないため、暫定的に以下のように解釈しています。
32/// ```text
33/// 2001500
34/// || | |
35/// || | +-- ビルドバージョン
36/// || +---- ベータバージョン
37/// |+------ マイナーバージョン
38/// +------- メジャーバージョン
39/// ```
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41pub struct AviUtl2Version(pub u32);
42impl From<u32> for AviUtl2Version {
43    fn from(value: u32) -> Self {
44        Self(value)
45    }
46}
47impl From<AviUtl2Version> for u32 {
48    fn from(value: AviUtl2Version) -> Self {
49        value.0
50    }
51}
52impl AviUtl2Version {
53    /// 新しいバージョンを作成します。
54    ///
55    /// # Panics
56    ///
57    /// `minor`、`patch`、`build`がそれぞれ100以上の場合にパニックします。
58    pub fn new(major: u8, minor: u8, patch: u8, build: u8) -> Self {
59        assert!(minor < 100, "minor version must be less than 100");
60        assert!(patch < 100, "patch version must be less than 100");
61        assert!(build < 100, "build version must be less than 100");
62        Self(
63            (major as u32) * 1_000_000
64                + (minor as u32) * 10_000
65                + (patch as u32) * 100
66                + (build as u32),
67        )
68    }
69
70    /// メジャーバージョンを取得します。
71    pub fn major(self) -> u32 {
72        self.0 / 1000000
73    }
74
75    /// マイナーバージョンを取得します。
76    pub fn minor(self) -> u32 {
77        (self.0 / 10000) % 100
78    }
79
80    /// ベータバージョンを取得します。
81    pub fn patch(self) -> u32 {
82        (self.0 / 100) % 100
83    }
84
85    /// ビルドバージョンを取得します。
86    pub fn build(self) -> u32 {
87        self.0 % 100
88    }
89}
90impl std::fmt::Display for AviUtl2Version {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        let build_str = if self.build() > 0 {
93            ('a'..='z')
94                .nth((self.build() - 1) as usize)
95                .map(|c| c.to_string())
96                .unwrap_or_default()
97        } else {
98            String::new()
99        };
100        write!(
101            f,
102            "{}.{:0>2}beta{}{}",
103            self.major(),
104            self.minor(),
105            self.patch(),
106            build_str
107        )
108    }
109}
110#[cfg(feature = "aviutl2-alias")]
111impl aviutl2_alias::FromTableValue for AviUtl2Version {
112    type Err = std::num::ParseIntError;
113
114    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
115        let v: u32 = value.parse()?;
116        Ok(Self::from(v))
117    }
118}
119
120/// ファイル選択ダイアログのフィルタを表す構造体。
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct FileFilter {
123    /// フィルタの名前。
124    pub name: String,
125    /// フィルタが適用される拡張子のリスト。
126    pub extensions: Vec<String>,
127}
128
129/// [`Vec<FileFilter>`]を簡単に作成するためのマクロ。
130///
131/// # Example
132///
133/// ```rust
134/// let filters = aviutl2::file_filters! {
135///     "Image Files" => ["png", "jpg"],
136///     "All Files" => []
137/// };
138/// ```
139#[macro_export]
140macro_rules! file_filters {
141    ($($name:expr => [$($ext:expr),* $(,)?] ),* $(,)?) => {
142        vec![
143            $(
144                $crate::FileFilter {
145                    name: $name.to_string(),
146                    extensions: vec![$($ext.to_string()),*],
147                }
148            ),*
149        ]
150    };
151}
152
153/// YC48のピクセルフォーマットを表す構造体。
154///
155/// # See Also
156/// <https://makiuchi-d.github.io/mksoft/doc/aviutlyc.html>
157#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable)]
158#[repr(C)]
159pub struct Yc48 {
160    /// Y成分の値。
161    /// 0から4096までの値を取ります。
162    pub y: i16,
163    /// Cb成分の値。
164    /// -2048から2048までの値を取ります。
165    pub cb: i16,
166    /// Cr成分の値。
167    /// -2048から2048までの値を取ります。
168    pub cr: i16,
169}
170impl Yc48 {
171    /// YUV 4:2:2(YUY2)からYC48に変換します。
172    pub fn from_yuy2(yuy2: (u8, u8, u8, u8)) -> (Self, Self) {
173        let (y1, u, y2, v) = yuy2;
174        let ny1 = ((y1 as u16 * 1197) >> 6) - 299;
175        let ny2 = ((y2 as u16 * 1197) >> 6) - 299;
176        let ncb = ((u as u16 - 128) * 4681 + 164) >> 8;
177        let ncr = ((v as u16 - 128) * 4681 + 164) >> 8;
178        (
179            Self {
180                y: ny1 as i16,
181                cb: ncb as i16,
182                cr: ncr as i16,
183            },
184            Self {
185                y: ((ny1 + ny2) >> 1) as i16,
186                cb: ncb as i16,
187                cr: ncr as i16,
188            },
189        )
190    }
191
192    /// YC48からYUV 4:2:2(YUY2)に変換します。
193    pub fn to_yuy2(self, other: Yc48) -> (u8, u8, u8, u8) {
194        let y1 = ((self.y * 219 + 383) >> 12) + 16;
195        let y2 = ((other.y * 219 + 383) >> 12) + 16;
196        let u = (((self.cb + 2048) * 7 + 66) >> 7) + 16;
197        let v = (((self.cr + 2048) * 7 + 66) >> 7) + 16;
198        let y1 = y1.min(255) as u8;
199        let y2 = y2.min(255) as u8;
200        let u = u.min(255) as u8;
201        let v = v.min(255) as u8;
202        (y1, u, y2, v)
203    }
204
205    /// RGBからYC48に変換します。
206    pub fn from_rgb(self, rgb: (u8, u8, u8)) -> Self {
207        let (r, g, b) = rgb;
208        let r = i16::from(r);
209        let g = i16::from(g);
210        let b = i16::from(b);
211        let y = ((4918 * r + 354) >> 10) + ((9655 * g + 585) >> 10) + ((1875 * b + 523) >> 10);
212        let cb = ((-2775 * r + 240) >> 10) + ((-5449 * g + 515) >> 10) + ((8224 * b + 256) >> 10);
213        let cr = ((8224 * r + 256) >> 10) + ((-6887 * g + 110) >> 10) + ((-1337 * b + 646) >> 10);
214
215        Yc48 { y, cb, cr }
216    }
217
218    /// YC48からRGBに変換します。
219    pub fn to_rgb(self) -> (u8, u8, u8) {
220        let y = self.y as i32;
221        let cr = self.cr as i32;
222        let cb = self.cr as i32;
223        let r = (255 * y + ((((22881 * cr) >> 16) + 3) << 10)) >> 12;
224        let g = (255 * y + ((((-5616 * cb) >> 16) + ((-11655 * cr) >> 16) + 3) << 10)) >> 12;
225        let b = (255 * y + ((((28919 * cb) >> 16) + 3) << 10)) >> 12;
226
227        let r = r.min(255) as u8;
228        let g = g.min(255) as u8;
229        let b = b.min(255) as u8;
230        (r, g, b)
231    }
232}
233
234pub(crate) fn format_file_filters(file_filters: &[FileFilter]) -> String {
235    let mut file_filter = String::new();
236    for filter in file_filters {
237        let display = format!(
238            "{} ({})",
239            filter.name,
240            if filter.extensions.is_empty() {
241                "*".to_string()
242            } else {
243                filter
244                    .extensions
245                    .iter()
246                    .map(|ext| format!(".{ext}"))
247                    .collect::<Vec<_>>()
248                    .join(", ")
249            }
250        );
251        file_filter.push_str(&display);
252        file_filter.push('\x00');
253        if filter.extensions.is_empty() {
254            file_filter.push('*');
255        } else {
256            file_filter.push_str(
257                &filter
258                    .extensions
259                    .iter()
260                    .map(|ext| format!("*.{ext}"))
261                    .collect::<Vec<_>>()
262                    .join(";"),
263            );
264        }
265        file_filter.push('\x00');
266    }
267
268    file_filter
269}
270
271pub(crate) struct KillablePointer<T> {
272    kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
273    inner: *mut T,
274}
275unsafe impl<T> Send for KillablePointer<T> {}
276unsafe impl<T> Sync for KillablePointer<T> {}
277impl<T> Drop for KillablePointer<T> {
278    fn drop(&mut self) {
279        self.kill_switch
280            .store(true, std::sync::atomic::Ordering::SeqCst);
281    }
282}
283impl<T> KillablePointer<T> {
284    pub fn new(inner: T) -> Self {
285        Self {
286            kill_switch: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
287            inner: Box::into_raw(Box::new(inner)),
288        }
289    }
290
291    pub fn create_child(&self) -> ChildKillablePointer<T> {
292        ChildKillablePointer {
293            kill_switch: std::sync::Arc::clone(&self.kill_switch),
294            inner: self.inner,
295        }
296    }
297}
298
299pub(crate) struct ChildKillablePointer<T> {
300    kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
301    inner: *mut T,
302}
303unsafe impl<T> Send for ChildKillablePointer<T> {}
304unsafe impl<T> Sync for ChildKillablePointer<T> {}
305impl<T> ChildKillablePointer<T> {
306    pub fn is_killed(&self) -> bool {
307        self.kill_switch.load(std::sync::atomic::Ordering::SeqCst)
308    }
309
310    pub unsafe fn as_mut(&mut self) -> &mut T {
311        if self.is_killed() {
312            panic!("parent KillablePointer has been dropped");
313        }
314        unsafe { &mut *self.inner }
315    }
316}
317
318pub(crate) enum LeakType {
319    WideString,
320    ValueVector { len: usize, name: String },
321    Null,
322    Other(String),
323}
324
325pub(crate) struct LeakManager {
326    ptrs: std::sync::Mutex<Vec<(LeakType, usize)>>,
327}
328
329pub(crate) trait IntoLeakedPtr {
330    fn into_leaked_ptr(self) -> (LeakType, usize);
331}
332pub(crate) trait LeakableValue {}
333
334impl LeakManager {
335    pub fn new() -> Self {
336        Self {
337            ptrs: std::sync::Mutex::new(Vec::new()),
338        }
339    }
340
341    pub fn leak<T: IntoLeakedPtr>(&self, value: T) -> *const T {
342        tracing::debug!("Leaking memory for type {}", std::any::type_name::<T>());
343        let mut ptrs = self.ptrs.lock().unwrap();
344        let leaked = value.into_leaked_ptr();
345        let ptr = leaked.1;
346        ptrs.push(leaked);
347        ptr as *const T
348    }
349
350    pub fn leak_as_wide_string(&self, s: &str) -> *const u16 {
351        tracing::debug!("Leaking wide string: {}", s);
352        let mut wide: Vec<u16> = s.encode_utf16().collect();
353        wide.push(0); // Null-terminate the string
354        let boxed = wide.into_boxed_slice();
355        let ptr = Box::into_raw(boxed) as *mut u16 as usize;
356        let mut ptrs = self.ptrs.lock().unwrap();
357        ptrs.push((LeakType::WideString, ptr));
358        ptr as *const u16
359    }
360
361    // pub fn leak_ptr_vec<T: IntoLeakedPtr>(&self, vec: Vec<T>) -> *const *const T {
362    //     tracing::debug!("Leaking vector of type {}", std::any::type_name::<T>());
363    //     let mut raw_ptrs = Vec::with_capacity(vec.len() + 1);
364    //     for item in vec {
365    //         let leaked = item.into_leaked_ptr();
366    //         let ptr = leaked.1;
367    //         raw_ptrs.push(ptr);
368    //         let mut ptrs = self.ptrs.lock().unwrap();
369    //         ptrs.push(leaked);
370    //     }
371    //     self.leak_value_vec(raw_ptrs) as _
372    // }
373
374    pub fn leak_value_vec<T: LeakableValue>(&self, vec: Vec<T>) -> *const T {
375        tracing::debug!(
376            "Leaking value vector of type {}",
377            std::any::type_name::<T>()
378        );
379        let len = vec.len();
380        let boxed = vec.into_boxed_slice();
381        let ptr = Box::into_raw(boxed) as *mut T as usize;
382        let mut ptrs = self.ptrs.lock().unwrap();
383        ptrs.push((
384            LeakType::ValueVector {
385                len,
386                name: std::any::type_name::<T>().to_string(),
387            },
388            ptr,
389        ));
390        ptr as *const T
391    }
392
393    pub fn free_leaked_memory(&self) {
394        let mut ptrs = self.ptrs.lock().unwrap();
395        while let Some((ptr_type, ptr)) = ptrs.pop() {
396            match ptr_type {
397                LeakType::WideString => unsafe {
398                    let _ = Box::from_raw(ptr as *mut u16);
399                },
400                LeakType::ValueVector { len, name } => {
401                    Self::free_leaked_memory_leakable_value(&name, ptr, len);
402                }
403                LeakType::Null => {
404                    assert!(ptr == 0);
405                }
406                LeakType::Other(ref type_name) => {
407                    Self::free_leaked_memory_other_ptr(type_name, ptr);
408                }
409            }
410        }
411    }
412}
413macro_rules! impl_leak_ptr {
414    ($($t:ty),* $(,)?) => {
415        $(
416            impl IntoLeakedPtr for $t {
417                fn into_leaked_ptr(self) -> (LeakType, usize) {
418                    let boxed = Box::new(self);
419                    let ptr = Box::into_raw(boxed) as usize;
420                    (LeakType::Other(std::any::type_name::<$t>().to_string()), ptr)
421                }
422            }
423        )*
424
425        impl LeakManager {
426            fn free_leaked_memory_other_ptr(ptr_type: &str, ptr: usize) {
427                unsafe {
428                    match ptr_type {
429                        $(
430                            t if t == std::any::type_name::<$t>() => {
431                                let _ = Box::from_raw(ptr as *mut $t);
432                            },
433                        )*
434                        _ => {
435                            unreachable!("Unknown leaked pointer type: {}", ptr_type);
436                        }
437                    }
438                }
439            }
440        }
441    };
442}
443macro_rules! impl_leakable_value {
444    ($($t:ty),* $(,)?) => {
445        $(
446            impl LeakableValue for $t {}
447        )*
448        impl LeakManager {
449            fn free_leaked_memory_leakable_value(type_name: &str, ptr: usize, len: usize) {
450                unsafe {
451                    match type_name {
452                        $(
453                            t if t == std::any::type_name::<$t>() => {
454                                let _ = Box::from_raw(std::slice::from_raw_parts_mut(ptr as *mut $t, len));
455                            },
456                        )*
457                        _ => {
458                            unreachable!("Unknown leaked value vector type: {}", type_name);
459                        }
460                    }
461                }
462            }
463        }
464    };
465}
466impl_leak_ptr!(
467    aviutl2_sys::input2::WAVEFORMATEX,
468    aviutl2_sys::output2::BITMAPINFOHEADER,
469    aviutl2_sys::filter2::FILTER_ITEM,
470);
471impl_leakable_value!(
472    aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM,
473    aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION,
474    usize
475);
476
477impl<T: IntoLeakedPtr> IntoLeakedPtr for Option<T> {
478    fn into_leaked_ptr(self) -> (LeakType, usize) {
479        match self {
480            Some(value) => value.into_leaked_ptr(),
481            None => (LeakType::Null, 0),
482        }
483    }
484}
485
486impl Drop for LeakManager {
487    fn drop(&mut self) {
488        self.free_leaked_memory();
489    }
490}
491
492/// # Safety
493///
494/// - `ptr` は有効なLPCWSTRであること。
495/// - `ptr` はNull Terminatedなu16文字列を指していること。
496pub(crate) unsafe fn load_wide_string(ptr: *const u16) -> String {
497    if ptr.is_null() {
498        return String::new();
499    }
500
501    let mut len = 0;
502    while unsafe { *ptr.add(len) } != 0 {
503        len += 1;
504    }
505
506    unsafe { String::from_utf16_lossy(std::slice::from_raw_parts(ptr, len)) }
507}
508
509#[doc(hidden)]
510#[expect(private_bounds)]
511pub fn __output_log_if_error<T: MenuCallbackReturn>(result: T) {
512    if let Some(err_msg) = result.into_optional_error() {
513        let _ = crate::logger::write_error_log(&err_msg);
514    }
515}
516
517#[doc(hidden)]
518#[expect(private_bounds)]
519pub fn __log_and_beep_if_error<T: MenuCallbackReturn>(result: T) {
520    if let Some(err_msg) = result.into_optional_error() {
521        let _ = crate::logger::write_error_log(&err_msg);
522
523        let _ = unsafe {
524            windows::Win32::System::Diagnostics::Debug::MessageBeep(
525                windows::Win32::UI::WindowsAndMessaging::MB_ICONEXCLAMATION,
526            )
527        };
528    }
529}
530
531trait MenuCallbackReturn {
532    fn into_optional_error(self) -> Option<String>;
533}
534impl<E> MenuCallbackReturn for Result<(), E>
535where
536    Box<dyn std::error::Error>: From<E>,
537{
538    fn into_optional_error(self) -> Option<String> {
539        match self {
540            Ok(_) => None,
541            Err(e) => {
542                let boxed: Box<dyn std::error::Error> = e.into();
543                Some(format!("{}", boxed))
544            }
545        }
546    }
547}
548impl MenuCallbackReturn for () {
549    fn into_optional_error(self) -> Option<String> {
550        None
551    }
552}
553
554/// ワイド文字列(LPCWSTR)を所有するRust文字列として扱うためのラッパー。
555#[derive(Debug, Clone, PartialEq, Eq)]
556pub(crate) struct CWString(Vec<u16>);
557
558/// ヌルバイトを含む文字列を作成しようとした際のエラー。
559#[derive(thiserror::Error, Debug)]
560#[error("String contains null byte")]
561pub struct NullByteError {
562    position: usize,
563    u16_seq: Vec<u16>,
564}
565impl NullByteError {
566    pub fn nul_position(&self) -> usize {
567        self.position
568    }
569
570    pub fn into_vec(self) -> Vec<u16> {
571        self.u16_seq
572    }
573}
574
575impl CWString {
576    /// 新しいCWStringを作成します。
577    ///
578    /// # Errors
579    ///
580    /// `string`にヌルバイトが含まれている場合、`NullByteError`を返します。
581    pub fn new(string: &str) -> Result<Self, NullByteError> {
582        let mut wide: Vec<u16> = string.encode_utf16().collect();
583        if let Some((pos, _)) = wide.iter().enumerate().find(|&(_, &c)| c == 0) {
584            return Err(NullByteError {
585                position: pos,
586                u16_seq: wide,
587            });
588        }
589        wide.push(0); // Null-terminate the string
590        Ok(Self(wide))
591    }
592
593    /// 内部のポインタを取得します。
594    ///
595    /// # Warning
596    ///
597    /// <div class="warning">
598    ///
599    /// この`CWString`がDropされた後にこのポインタを使用すると未定義動作を引き起こします。
600    ///
601    /// </div>
602    pub fn as_ptr(&self) -> *const u16 {
603        self.0.as_ptr()
604    }
605}
606impl std::fmt::Display for CWString {
607    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608        let s = String::from_utf16_lossy(&self.0[..self.0.len() - 1]);
609        write!(f, "{}", s)
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use super::*;
616
617    #[test]
618    fn test_format_file_filters() {
619        let filters = vec![
620            FileFilter {
621                name: "Image Files".to_string(),
622                extensions: vec!["png".to_string(), "jpg".to_string()],
623            },
624            FileFilter {
625                name: "All Files".to_string(),
626                extensions: vec![],
627            },
628        ];
629        let formatted = format_file_filters(&filters);
630        let expected = "Image Files (.png, .jpg)\x00*.png;*.jpg\x00All Files (*)\x00*\x00";
631        assert_eq!(formatted, expected);
632    }
633
634    #[test]
635    fn test_file_filters_macro() {
636        let filters = file_filters! {
637            "Image Files" => ["png", "jpg"],
638            "All Files" => []
639        };
640        assert_eq!(filters.len(), 2);
641        assert_eq!(filters[0].name, "Image Files");
642        assert_eq!(
643            filters[0].extensions,
644            vec!["png".to_string(), "jpg".to_string()]
645        );
646        assert_eq!(filters[1].name, "All Files");
647        assert_eq!(filters[1].extensions, Vec::<String>::new());
648    }
649
650    #[test]
651    fn test_aviutl2_version() {
652        let version = AviUtl2Version::new(2, 0, 15, 0);
653        assert_eq!(version.0, 2001500);
654        assert_eq!(version.major(), 2);
655        assert_eq!(version.minor(), 0);
656        assert_eq!(version.patch(), 15);
657        assert_eq!(version.build(), 0);
658
659        let version_from_u32: AviUtl2Version = 2001500u32.into();
660        assert_eq!(version_from_u32, version);
661
662        let u32_from_version: u32 = version.into();
663        assert_eq!(u32_from_version, 2001500);
664    }
665
666    #[test]
667    fn test_cwstring_new() {
668        let s = "Hello, world!";
669        let cwstring = CWString::new(s).unwrap();
670        assert_eq!(unsafe { load_wide_string(cwstring.as_ptr()) }, s);
671
672        let s_with_nul = "Hello\0world!";
673        let err = CWString::new(s_with_nul).unwrap_err();
674        assert_eq!(err.nul_position(), 5);
675    }
676}